[
  {
    "path": ".clang-format",
    "content": "# Copyright 2014 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n{\n  BasedOnStyle: Chromium,\n  AlignTrailingComments: false,\n  BinPackArguments: false,\n}\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Copyright 2019 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n*.S text eol=lf\n*.asm text eol=lf\n*.c text eol=lf\n*.cc text eol=lf\n*.cmx text eol=lf\n*.css text eol=lf\n*.defs text eol=lf\n*.doxy text eol=lf\n*.gn text eol=lf\n*.gni text eol=lf\n*.go text eol=lf\n*.h text eol=lf\n*.m text eol=lf\n*.md text eol=lf\n*.mm text eol=lf\n*.pem text eol=lf\n*.plist text eol=lf\n*.proctype text eol=lf\n*.py text eol=lf\n*.sh text eol=lf\n*.sym text eol=lf\n*.txt text eol=lf\n*.yaml text eol=lf\n.clang-format text eol=lf\n.gitattributes text eol=lf\n.gitignore text eol=lf\n.vpython text eol=lf\n/AUTHORS text eol=lf\n/CONTRIBUTORS text eol=lf\n/LICENSE text eol=lf\n/codereview.settings text eol=lf\nAPPLE_LICENSE text eol=lf\nCOPYING.LIB text eol=lf\nDEPS text eol=lf\nREADME text eol=lf\nREADME.crashpad text eol=lf\n\n*.dat binary\n*.dll binary\n*.ico binary\n*.obj binary\n*.png binary\n*.so binary\n"
  },
  {
    "path": ".gitignore",
    "content": "# Copyright 2014 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Keep sorted\n\n*.Makefile\n*.ninja\n*.pyc\n*.target.mk\n*.xcodeproj\n*~\n.*.sw?\n.DS_Store\n.cache\n.gdb_history\n.gdbinit\n/.vscode/\n/Makefile\n/build/fuchsia\n/out\n/third_party/edo/edo\n/third_party/fuchsia-gn-sdk\n/third_party/fuchsia/.cipd\n/third_party/fuchsia/clang\n/third_party/fuchsia/qemu\n/third_party/fuchsia/sdk\n/third_party/googletest/googletest\n/third_party/libfuzzer\n/third_party/linux/.cipd\n/third_party/linux/clang\n/third_party/linux/sysroot\n/third_party/lss/lss\n/third_party/mini_chromium/mini_chromium\n/third_party/ninja/linux\n/third_party/ninja/mac*\n/third_party/ninja/ninja.exe\n/third_party/windows/clang/win-amd64\n/third_party/zlib/zlib\n/xcodebuild\ntags\n"
  },
  {
    "path": ".gn",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nbuildconfig = \"//build/BUILDCONFIG.gn\"\nscript_executable = \"python3\"\n"
  },
  {
    "path": ".style.yapf",
    "content": "# Copyright 2020 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n[style]\nbased_on_style = google\n"
  },
  {
    "path": ".vpython3",
    "content": "# Copyright 2022 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This is a vpython \"spec\" file.\n#\n# It describes patterns for python wheel dependencies of the python scripts.\n#\n# Read more about `vpython` and how to modify this file here:\n#   https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md\n\n# This is needed for snapshot/win/end_to_end_test.py.\nwheel: <\n  name: \"infra/python/wheels/pywin32/${vpython_platform}\"\n  version: \"version:300\"\n  match_tag: <\n    platform: \"win32\"\n  >\n  match_tag: <\n    platform: \"win_amd64\"\n  >\n>\n"
  },
  {
    "path": "AUTHORS",
    "content": "# This is the official list of Crashpad authors for copyright purposes.\n# This file is distinct from the CONTRIBUTORS files.\n# See the latter for an explanation.\n\n# Names should be added to this file as:\n# Name or Organization <email address>\n# The email address is not required for organizations.\n\nGoogle Inc.\nIntel Corporation\nOpera Software ASA\nVewd Software AS\nLG Electronics, Inc.\nMIPS Technologies, Inc.\nDarshan Sen <raisinten@gmail.com>\nHo Cheung <uioptt24@gmail.com>\n"
  },
  {
    "path": "BUILD.gn",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"build/crashpad_buildconfig.gni\")\nimport(\"build/test.gni\")\nimport(\"util/net/tls.gni\")\n\nconfig(\"crashpad_config\") {\n  include_dirs = [\n    \".\",\n    root_gen_dir,\n  ]\n}\n\nif (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {\n  test(\"crashpad_tests\") {\n    deps = [\n      \"client:client_test\",\n      \"minidump:minidump_test\",\n      \"snapshot:snapshot_test\",\n      \"test:googlemock_main\",\n      \"test:test_test\",\n      \"util:util_test\",\n    ]\n\n    data_deps = []\n\n    if (crashpad_is_in_chromium) {\n      data_deps += [ \"//testing/buildbot/filters:crashpad_tests_filters\" ]\n    }\n\n    if (!crashpad_is_ios && !crashpad_is_fuchsia) {\n      deps += [ \"handler:handler_test\" ]\n    }\n    if (crashpad_is_in_fuchsia) {\n      # TODO(fuchsia:46559): Fix the leaks and remove this.\n      deps += [ \"//build/config/sanitizers:suppress-lsan.DO-NOT-USE-THIS\" ]\n\n      # TODO(fxbug.dev/42059784): Remove this once the underlying issue is\n      # addressed.\n      exclude_toolchain_tags = [ \"hwasan\" ]\n    }\n    if (crashpad_is_android) {\n      use_raw_android_executable = true\n\n      # crbug.com/418874703 - This is a workaround to propagate the data deps to\n      # //:crashpad_tests__dist for Android build.\n      data_deps += [\n        \"snapshot:crashpad_snapshot_test_both_dt_hash_styles\",\n        \"snapshot:crashpad_snapshot_test_module\",\n        \"snapshot:crashpad_snapshot_test_module_large\",\n        \"snapshot:crashpad_snapshot_test_module_small\",\n        \"test:crashpad_test_test_multiprocess_exec_test_child\",\n      ]\n\n      copy(\"crashpad_test_data\") {\n        testonly = true\n        sources = [\n          \"test/test_paths_test_data_root.txt\",\n          \"util/net/testdata/ascii_http_body.txt\",\n          \"util/net/testdata/binary_http_body.dat\",\n        ]\n\n        outputs = [ \"$root_out_dir/crashpad_test_data/{{source}}\" ]\n      }\n\n      deps += [ \":crashpad_test_data\" ]\n\n      extra_dist_files = [\n        \"$root_out_dir/crashpad_handler\",\n        \"$root_out_dir/crashpad_test_test_multiprocess_exec_test_child\",\n        \"$root_out_dir/crashpad_test_data\",\n      ]\n    }\n  }\n\n  if (crashpad_is_in_fuchsia) {\n    import(\"//build/components.gni\")\n    fuchsia_test_component(\"crashpad-test-component\") {\n      manifest = \"test/fuchsia_crashpad_tests.cml\"\n      deps = [\n        \":crashpad-test-resources\",\n        \":crashpad_tests\",\n        \"snapshot:crashpad_snapshot_test_both_dt_hash_styles\",\n        \"snapshot:crashpad_snapshot_test_module\",\n        \"snapshot:crashpad_snapshot_test_module_large\",\n        \"snapshot:crashpad_snapshot_test_module_small\",\n        \"test:crashpad_test_test_multiprocess_exec_test_child\",\n      ]\n    }\n\n    fuchsia_test_package(\"crashpad-test\") {\n      test_components = [ \":crashpad-test-component\" ]\n\n      deps = [\n        \"//src/connectivity/network/dns:component\",\n        \"//src/connectivity/network/netstack:component\",\n      ]\n\n      test_specs = {\n        log_settings = {\n          max_severity = \"FATAL\"\n        }\n      }\n    }\n\n    _resource_files = [\n      \"test/test_paths_test_data_root.txt\",\n      \"util/net/testdata/ascii_http_body.txt\",\n      \"util/net/testdata/binary_http_body.dat\",\n    ]\n    if (crashpad_use_boringssl_for_http_transport_socket) {\n      _resource_files += [\n        \"util/net/testdata/crashpad_util_test_cert.pem\",\n        \"util/net/testdata/crashpad_util_test_key.pem\",\n      ]\n    }\n\n    _resources = []\n    foreach(resource_file, _resource_files) {\n      _resource_file_target = string_replace(resource_file, \"/\", \"_\")\n      resource(\"${_resource_file_target}\") {\n        sources = [ \"${resource_file}\" ]\n        outputs = [ \"data/${resource_file}\" ]\n      }\n      _resources += [ \":${_resource_file_target}\" ]\n    }\n\n    group(\"crashpad-test-resources\") {\n      deps = _resources\n    }\n\n    group(\"tests\") {\n      testonly = true\n\n      deps = [ \":crashpad-test\" ]\n    }\n  }\n} else if (crashpad_is_standalone || crashpad_is_external) {\n  test(\"crashpad_client_test\") {\n    deps = [\n      \"client:client_test\",\n      \"test:googlemock_main\",\n    ]\n  }\n\n  test(\"crashpad_handler_test\") {\n    deps = [\n      \"handler:handler_test\",\n      \"test:googletest_main\",\n    ]\n    if (crashpad_is_ios || crashpad_is_fuchsia) {\n      deps -= [ \"handler:handler_test\" ]\n    }\n  }\n\n  test(\"crashpad_minidump_test\") {\n    deps = [\n      \"minidump:minidump_test\",\n      \"test:googletest_main\",\n    ]\n  }\n\n  test(\"crashpad_snapshot_test\") {\n    deps = [\n      \"snapshot:snapshot_test\",\n      \"test:googlemock_main\",\n    ]\n  }\n\n  test(\"crashpad_test_test\") {\n    deps = [\n      \"test:googlemock_main\",\n      \"test:test_test\",\n    ]\n  }\n\n  test(\"crashpad_util_test\") {\n    deps = [\n      \"test:googlemock_main\",\n      \"util:util_test\",\n    ]\n  }\n}\n\nif (crashpad_is_ios) {\n  group(\"ios_xcuitests\") {\n    testonly = true\n    deps = [ \"test/ios:all_tests\" ]\n  }\n}\n"
  },
  {
    "path": "CONTRIBUTORS",
    "content": "# People who have agreed to one of the CLAs and can contribute patches.\n# The AUTHORS file lists the copyright holders; this file\n# lists people.  For example, Google employees are listed here\n# but not in AUTHORS, because Google holds the copyright.\n#\n# https://developers.google.com/open-source/cla/individual\n# https://developers.google.com/open-source/cla/corporate\n#\n# Names should be added to this file as:\n#     Name <email address>\n\nMark Mentovai <mark@chromium.org>\nRobert Sesek <rsesek@chromium.org>\nScott Graham <scottmg@chromium.org>\nJoshua Peraza <jperaza@chromium.org>\n"
  },
  {
    "path": "DEPS",
    "content": "# Copyright 2014 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nvars = {\n  'chromium_git': 'https://chromium.googlesource.com',\n  'gn_version': 'git_revision:5e19d2fb166fbd4f6f32147fbb2f497091a54ad8',\n  # ninja CIPD package version.\n  # https://chrome-infra-packages.appspot.com/p/infra/3pp/tools/ninja\n  'ninja_version': 'version:2@1.8.2.chromium.3',\n  'pull_linux_clang': False,\n  'pull_win_toolchain': False,\n  # Controls whether crashpad/build/ios/setup-ios-gn.py is run as part of\n  # gclient hooks. It is enabled by default for developer's convenience. It can\n  # be disabled with custom_vars (done automatically on the bots).\n  'run_setup_ios_gn': True,\n}\n\ndeps = {\n  'buildtools':\n      Var('chromium_git') + '/chromium/src/buildtools.git@' +\n      'efa920ce144e4dc1c1841e73179cd7e23b9f0d5e',\n  'buildtools/clang_format/script':\n      Var('chromium_git') +\n      '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' +\n      'c912837e0d82b5ca4b6e790b573b3956d3744c1c',\n  'crashpad/third_party/edo/edo': {\n      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git@' +\n      '38e71ff183d76f702db6966fa7236c98831acd80',\n      'condition': 'checkout_ios',\n  },\n  'crashpad/third_party/googletest/googletest':\n      Var('chromium_git') + '/external/github.com/google/googletest@' +\n      '3983f67e32fb3e9294487b9d4f9586efa6e5d088',\n  'crashpad/third_party/lss/lss':\n      Var('chromium_git') + '/linux-syscall-support.git@' +\n      '9719c1e1e676814c456b55f5f070eabad6709d31',\n  'crashpad/third_party/mini_chromium/mini_chromium':\n      Var('chromium_git') + '/chromium/mini_chromium@' +\n      '706fce5b1a280a6f2eea69040f67847f9acb65ff',\n  'crashpad/third_party/libfuzzer/src':\n      Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +\n      'fda403cf93ecb8792cb1d061564d89a6553ca020',\n  'crashpad/third_party/zlib/zlib':\n      Var('chromium_git') + '/chromium/src/third_party/zlib@' +\n      'fef58692c1d7bec94c4ed3d030a45a1832a9615d',\n\n  # CIPD packages.\n  'buildtools/linux64': {\n    'packages': [\n      {\n        'package': 'gn/gn/linux-${{arch}}',\n        'version': Var('gn_version'),\n      }\n    ],\n    'dep_type': 'cipd',\n    'condition': 'host_os == \"linux\"',\n  },\n  'buildtools/mac': {\n    'packages': [\n      {\n        'package': 'gn/gn/mac-${{arch}}',\n        'version': Var('gn_version'),\n      }\n    ],\n    'dep_type': 'cipd',\n    'condition': 'host_os == \"mac\"',\n  },\n  'buildtools/win': {\n    'packages': [\n      {\n        'package': 'gn/gn/windows-amd64',\n        'version': Var('gn_version'),\n      }\n    ],\n    'dep_type': 'cipd',\n    'condition': 'host_os == \"win\"',\n  },\n  'crashpad/build/fuchsia': {\n     'packages': [\n       {\n        'package': 'chromium/fuchsia/test-scripts',\n        'version': 'latest',\n       }\n     ],\n     'condition': 'checkout_fuchsia',\n     'dep_type': 'cipd',\n  },\n  'crashpad/third_party/linux/clang/linux-amd64': {\n    'packages': [\n      {\n        'package': 'fuchsia/third_party/clang/linux-amd64',\n        'version': 'Tpc85d1ZwSlZ6UKl2d96GRUBGNA5JKholOKe24sRDr0C',\n      },\n    ],\n    'condition': 'checkout_linux and pull_linux_clang',\n    'dep_type': 'cipd'\n  },\n  'crashpad/third_party/fuchsia/clang/mac-amd64': {\n    'packages': [\n      {\n        'package': 'fuchsia/third_party/clang/mac-amd64',\n        'version': 'integration',\n      },\n    ],\n    'condition': 'checkout_fuchsia and host_os == \"mac\"',\n    'dep_type': 'cipd'\n  },\n  'crashpad/third_party/fuchsia/clang/linux-amd64': {\n    'packages': [\n      {\n        'package': 'fuchsia/third_party/clang/linux-amd64',\n        'version': 'integration',\n      },\n    ],\n    'condition': 'checkout_fuchsia and host_os == \"linux\"',\n    'dep_type': 'cipd'\n  },\n  'crashpad/third_party/windows/clang/win-amd64': {\n    'bucket': 'chromium-browser-clang',\n    'objects': [\n      {\n        'object_name': 'Win/clang-llvmorg-20-init-17108-g29ed6000-2.tar.xz',\n        'sha256sum': '1c71efd923a91480480d4f31c2fd5f1369e01e14f15776a9454abbce0bc13548',\n        'size_bytes': 46357580,\n        'generation': 1737590897363452,\n      },\n    ],\n    'condition': 'checkout_win and host_os == \"win\"',\n    'dep_type': 'gcs',\n  },\n  'crashpad/third_party/fuchsia-gn-sdk': {\n    'packages': [\n      {\n        'package': 'chromium/fuchsia/gn-sdk',\n        'version': 'latest'\n      },\n    ],\n    'condition': 'checkout_fuchsia',\n    'dep_type': 'cipd'\n  },\n  'crashpad/third_party/fuchsia/sdk/linux-amd64': {\n    'packages': [\n      {\n        'package': 'fuchsia/sdk/core/linux-amd64',\n        'version': 'latest'\n      },\n    ],\n    'condition': 'checkout_fuchsia and host_os == \"linux\"',\n    'dep_type': 'cipd'\n  },\n  # depot_tools/ninja wrapper calls third_party/ninja/{ninja, ninja.exe}.\n  # crashpad/third_party/ninja/ninja is another wrapper to call linux ninja\n  # or mac ninja.\n  # This allows crashpad developers to work for multiple platforms on the same\n  # machine.\n  'crashpad/third_party/ninja': {\n    'packages': [\n      {\n        'package': 'infra/3pp/tools/ninja/${{platform}}',\n        'version': Var('ninja_version'),\n      }\n    ],\n    'condition': 'host_os == \"win\"',\n    'dep_type': 'cipd',\n  },\n  'crashpad/third_party/ninja/linux': {\n    'packages': [\n      {\n        'package': 'infra/3pp/tools/ninja/${{platform}}',\n        'version': Var('ninja_version'),\n      }\n    ],\n    'condition': 'host_os == \"linux\"',\n    'dep_type': 'cipd',\n  },\n  'crashpad/third_party/ninja/mac-amd64': {\n    'packages': [\n      {\n        'package': 'infra/3pp/tools/ninja/mac-amd64',\n        'version': Var('ninja_version'),\n      }\n    ],\n    'condition': 'host_os == \"mac\" and host_cpu == \"x64\"',\n    'dep_type': 'cipd',\n  },\n  'crashpad/third_party/ninja/mac-arm64': {\n    'packages': [\n      {\n        'package': 'infra/3pp/tools/ninja/mac-arm64',\n        'version': Var('ninja_version'),\n      }\n    ],\n    'condition': 'host_os == \"mac\" and host_cpu == \"arm64\"',\n    'dep_type': 'cipd',\n  },\n  'crashpad/third_party/win/toolchain': {\n    # This package is only updated when the solution in .gclient includes an\n    # entry like:\n    #   \"custom_vars\": { \"pull_win_toolchain\": True }\n    # This is because the contained bits are not redistributable.\n    'packages': [\n      {\n        'package': 'chrome_internal/third_party/sdk/windows',\n        'version': 'uploaded:2021-04-28'\n      },\n    ],\n    'condition': 'checkout_win and pull_win_toolchain',\n    'dep_type': 'cipd'\n  },\n}\n\nhooks = [\n  {\n    # If using a local clang (\"pull_linux_clang\" above), also pull down a\n    # sysroot.\n    'name': 'sysroot_linux',\n    'pattern': '.',\n    'condition': 'checkout_linux and pull_linux_clang',\n    'action': [\n      'crashpad/build/install_linux_sysroot.py',\n    ],\n  },\n  {\n    # Avoid introducing unnecessary PRESUBMIT.py file from build/fuchsia.\n    # Never fail and ignore the error if the file does not exist.\n    'name': 'Remove the PRESUBMIT.py from build/fuchsia',\n    'pattern': '.',\n    'condition': 'checkout_fuchsia',\n    'action': [\n      'rm',\n      '-f',\n      'crashpad/build/fuchsia/PRESUBMIT.py',\n    ],\n  },\n  {\n    'name': 'Generate Fuchsia Build Definitions',\n    'pattern': '.',\n    'condition': 'checkout_fuchsia',\n    'action': [\n      'python3',\n      'crashpad/build/fuchsia_envs.py',\n      'crashpad/build/fuchsia/gen_build_defs.py'\n    ],\n  },\n  {\n    'name': 'setup_gn_ios',\n    'pattern': '.',\n    'condition': 'run_setup_ios_gn and checkout_ios',\n    'action': [\n        'python3',\n        'crashpad/build/ios/setup_ios_gn.py'\n    ],\n  },\n]\n\nrecursedeps = [\n  'buildtools',\n]\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "OWNERS",
    "content": "# Copyright 2025 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\njperaza@chromium.org\njustincohen@chromium.org\nlgrey@chromium.org\nmark@chromium.org\npbos@chromium.org\nwfh@chromium.org\n"
  },
  {
    "path": "README.md",
    "content": "<!--\nCopyright 2015 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# Crashpad\n\n[Crashpad](https://crashpad.chromium.org/) is a crash-reporting system.\n\n## Documentation\n\n * [Project status](doc/status.md)\n * [Developing Crashpad](doc/developing.md): instructions for getting the source\n   code, building, testing, and contributing to the project.\n * [Crashpad interface documentation](https://crashpad.chromium.org/doxygen/)\n * [Crashpad tool man pages](doc/man.md)\n * [Crashpad overview design](doc/overview_design.md)\n\n## Source Code\n\nCrashpad’s source code is hosted in a Git repository at\nhttps://chromium.googlesource.com/crashpad/crashpad.\n\n## Other Links\n\n * Bugs can be reported at the [Crashpad issue\n   tracker](https://crashpad.chromium.org/bug/).\n * The [Crashpad bots](https://ci.chromium.org/p/crashpad/g/main/console)\n   perform automated builds and tests.\n * [crashpad-dev](https://groups.google.com/a/chromium.org/group/crashpad-dev)\n   is the Crashpad developers’ mailing list.\n"
  },
  {
    "path": "build/BUILD.gn",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# When building in Chromium, these configs is used to set #defines that indicate\n# whether code is being built standalone, or in Chromium, or potentially in some\n# other configutation.\n\nimport(\"crashpad_buildconfig.gni\")\n\nconfig(\"crashpad_is_in_chromium\") {\n  if (crashpad_is_in_chromium) {\n    defines = [ \"CRASHPAD_IS_IN_CHROMIUM\" ]\n  }\n}\n\nconfig(\"crashpad_is_in_fuchsia\") {\n  if (crashpad_is_in_fuchsia) {\n    defines = [ \"CRASHPAD_IS_IN_FUCHSIA\" ]\n  }\n}\n\nconfig(\"flock_always_supported_defines\") {\n  defines =\n      [ \"CRASHPAD_FLOCK_ALWAYS_SUPPORTED=$crashpad_flock_always_supported\" ]\n}\n\ngroup(\"default_exe_manifest_win\") {\n  if (crashpad_is_in_chromium) {\n    deps = [ \"//build/win:default_exe_manifest\" ]\n  }\n}\n\nconfig(\"crashpad_fuzzer_flags\") {\n  cflags = [\n    \"-fsanitize=address\",\n    \"-fsanitize-address-use-after-scope\",\n    \"-fsanitize=fuzzer\",\n  ]\n\n  ldflags = [ \"-fsanitize=address\" ]\n}\n\nif (crashpad_is_apple) {\n  group(\"apple_enable_arc\") {\n    # If `crashpad_is_in_chromium`, then because Chromium enables ARC\n    # compilation by default, no special configuration is needed.\n\n    if (crashpad_is_standalone) {\n      public_configs = [ \"//third_party/mini_chromium/mini_chromium/build/config:apple_enable_arc\" ]\n    }\n  }\n}\n\nif (crashpad_is_ios) {\n  group(\"ios_xctest\") {\n    if (crashpad_is_in_chromium) {\n      public_configs = [ \"//build/config/ios:xctest_config\" ]\n    } else if (crashpad_is_standalone) {\n      public_configs = [\n        \"//third_party/mini_chromium/mini_chromium/build/ios:xctest_config\",\n      ]\n    }\n  }\n\n  if (crashpad_is_in_chromium) {\n    import(\"//build/config/ios/ios_sdk.gni\")\n    crashpad_is_ios_app_extension = ios_is_app_extension\n  } else {\n    crashpad_is_ios_app_extension = false\n  }\n\n  config(\"crashpad_is_ios_app_extension\") {\n    if (crashpad_is_ios_app_extension) {\n      defines = [ \"CRASHPAD_IS_IOS_APP_EXTENSION\" ]\n    }\n  }\n}\n"
  },
  {
    "path": "build/BUILDCONFIG.gn",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Intentionally very minimal, so that Crashpad can build in-tree in a variety of\n# other projects, unrelated to the variables that are set in those projects'\n# BUILDCONFIG.gn. Do not add more variables here. Instead, make them available\n# in build/crashpad_buildconfig.gni if they must be globally available.\n\nif (target_os == \"\") {\n  target_os = host_os\n}\n\nif (current_os == \"\") {\n  current_os = target_os\n}\n\nif (target_cpu == \"\") {\n  target_cpu = host_cpu\n}\n\nif (current_cpu == \"\") {\n  current_cpu = target_cpu\n}\n\nimport(\"//build/crashpad_buildconfig.gni\")\n\nif (crashpad_is_standalone) {\n  _mini_chromium_dir = \"//third_party/mini_chromium/mini_chromium\"\n} else if (crashpad_is_external) {\n  _mini_chromium_dir = \"//../../mini_chromium/mini_chromium\"\n}\n\nif (current_os == \"win\") {\n  set_default_toolchain(\n      \"$_mini_chromium_dir/build/config:msvc_toolchain_$current_cpu\")\n} else {\n  set_default_toolchain(\"$_mini_chromium_dir/build/config:gcc_like_toolchain\")\n}\n\ndeclare_args() {\n  # When true, enables the debug configuration, with additional run-time checks\n  # and logging. When false, enables the release configuration, with additional\n  # optimizations.\n  is_debug = false\n\n  # When true, build all code with -fsanitize=fuzzer, and enable various\n  # *_fuzzer targets.\n  crashpad_use_libfuzzer = false\n}\n\n_default_configs = [\n  \"$_mini_chromium_dir/build/config:default\",\n  \"$_mini_chromium_dir/build/config:Wexit_time_destructors\",\n  \"$_mini_chromium_dir/build/config:Wimplicit_fallthrough\",\n]\n\nif (crashpad_use_libfuzzer) {\n  _default_configs += [ \"//build/config:crashpad_fuzzer_flags\" ]\n}\n\nif (current_os == \"fuchsia\") {\n  _default_configs += [\n    \"//third_party/fuchsia-gn-sdk/src/config:compiler\",\n    \"//third_party/fuchsia-gn-sdk/src/config:runtime_library\",\n  ]\n  import(\"//third_party/fuchsia-gn-sdk/src/gn_configs.gni\")\n}\n\n_default_executable_configs = _default_configs + [\n                                \"$_mini_chromium_dir/build/config:executable\",\n                                \"$_mini_chromium_dir/build/config:win_console\",\n                              ]\n\nset_defaults(\"source_set\") {\n  configs = _default_configs\n}\n\nset_defaults(\"static_library\") {\n  configs = _default_configs\n}\n\nset_defaults(\"executable\") {\n  configs = _default_executable_configs\n}\n\nset_defaults(\"loadable_module\") {\n  configs = _default_configs\n}\n\nset_defaults(\"shared_library\") {\n  configs = _default_configs\n}\n\nset_defaults(\"test\") {\n  configs = _default_executable_configs\n}\n"
  },
  {
    "path": "build/config/fuchsia/gn_configs.gni",
    "content": "# Copyright 2024 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This file is copied from\n# https://crsrc.org/c/build/config/fuchsia/gn_configs.gni?q=gn_configs.gni\n# with some local modifications to match the crashpad setup.\n\n# Path to the fuchsia SDK. This is intended for use in other templates &\n# rules to reference the contents of the fuchsia SDK.\nfuchsia_sdk = \"//third_party/fuchsia/sdk/linux-amd64\"\n\ndeclare_args() {\n  # Specify a readelf_exec path to use. If not specified, the host's system\n  # executable will be used. Passed to populate_build_id_dir.py and\n  # prepare_package_inputs.py via the --readelf-exec flag.\n  # Must be a GN path (not an absolute path) since it is adjusted with\n  # rebase_path().\n  if (!defined(fuchsia_sdk_readelf_exec)) {\n    fuchsia_sdk_readelf_exec = \"\"\n  }\n}\n"
  },
  {
    "path": "build/crashpad_buildconfig.gni",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\ndeclare_args() {\n  # Determines various flavors of build configuration, and which concrete\n  # targets to use for dependencies. Valid values are \"standalone\", \"chromium\",\n  # \"fuchsia\", \"dart\" or \"external\".\n  crashpad_dependencies = \"standalone\"\n\n  if (defined(is_fuchsia_tree) && is_fuchsia_tree) {\n    crashpad_dependencies = \"fuchsia\"\n  }\n}\n\nassert(\n    crashpad_dependencies == \"chromium\" || crashpad_dependencies == \"fuchsia\" ||\n    crashpad_dependencies == \"standalone\" ||\n    crashpad_dependencies == \"external\" || crashpad_dependencies == \"dart\")\n\ncrashpad_is_in_chromium = crashpad_dependencies == \"chromium\"\ncrashpad_is_in_fuchsia = crashpad_dependencies == \"fuchsia\"\ncrashpad_is_in_dart = crashpad_dependencies == \"dart\"\ncrashpad_is_external = crashpad_dependencies == \"external\"\ncrashpad_is_standalone = crashpad_dependencies == \"standalone\"\n\n# This is the parent directory that contains the mini_chromium source dir.\n# This variable is not used when crashpad_is_in_chromium.\nif (crashpad_is_in_fuchsia) {\n  import(\"//third_party/crashpad/fuchsia_buildconfig.gni\")\n  mini_chromium_source_parent =\n      fuchsia_crashpad_root + \"/third_party/mini_chromium\"\n} else {\n  mini_chromium_source_parent = \"../third_party/mini_chromium\"\n}\n\n# This is the source directory of mini_chromium (what is checked out).\n_mini_chromium_source_root = \"$mini_chromium_source_parent/mini_chromium\"\n\n# This references the mini_chromium location for importing GN files.\nif (crashpad_is_external || crashpad_is_in_dart) {\n  mini_chromium_import_root = \"../../../$_mini_chromium_source_root\"\n} else if (crashpad_is_in_fuchsia) {\n  mini_chromium_import_root = fuchsia_mini_chromium_root\n} else {\n  mini_chromium_import_root = _mini_chromium_source_root\n}\n\nif (crashpad_is_in_chromium) {\n  if (is_ios) {\n    # For `target_platform`.\n    import(\"//build/config/apple/mobile_config.gni\")\n  }\n\n  crashpad_is_mac = is_mac\n  crashpad_is_ios = is_ios\n  crashpad_is_tvos = is_ios && target_platform == \"tvos\"\n  crashpad_is_apple = is_apple\n  crashpad_is_win = is_win\n  crashpad_is_linux = is_linux || is_chromeos\n  crashpad_is_android = is_android\n  crashpad_is_fuchsia = is_fuchsia\n\n  crashpad_is_posix = is_posix\n\n  crashpad_is_clang = is_clang\n} else {\n  import(\"$mini_chromium_import_root/build/compiler.gni\")\n  import(\"$mini_chromium_import_root/build/platform.gni\")\n\n  if (mini_chromium_is_ios) {\n    # For `target_platform`.\n    import(\"$mini_chromium_import_root/build/ios/ios_sdk.gni\")\n  }\n\n  crashpad_is_mac = mini_chromium_is_mac\n  crashpad_is_ios = mini_chromium_is_ios\n  crashpad_is_tvos = crashpad_is_ios && target_platform == \"tvos\"\n  crashpad_is_apple = mini_chromium_is_apple\n  crashpad_is_win = mini_chromium_is_win\n  crashpad_is_linux = mini_chromium_is_linux\n  crashpad_is_android = mini_chromium_is_android\n  crashpad_is_fuchsia = mini_chromium_is_fuchsia\n\n  crashpad_is_posix = mini_chromium_is_posix\n\n  crashpad_is_clang = mini_chromium_is_clang\n\n  # fuchsia-gn-sdk from chromium uses \"is_fuchsia\" condition.\n  is_fuchsia = crashpad_is_fuchsia\n}\n\ncrashpad_flock_always_supported = !(crashpad_is_android || crashpad_is_fuchsia)\n\ntemplate(\"crashpad_executable\") {\n  executable(target_name) {\n    forward_variables_from(invoker,\n                           \"*\",\n                           [\n                             \"configs\",\n                             \"remove_configs\",\n                           ])\n    if (defined(invoker.remove_configs)) {\n      configs -= invoker.remove_configs\n    }\n\n    if (defined(invoker.configs)) {\n      configs += invoker.configs\n    }\n\n    if (crashpad_is_in_fuchsia) {\n      conversion_config = [ \"//build/config:Wno-conversion\" ]\n      if (configs + conversion_config - conversion_config == configs) {\n        # TODO(https://fxbug.dev/42136089): Decide if these are worth enabling.\n        configs += conversion_config\n      }\n    }\n  }\n}\n\ntemplate(\"crashpad_loadable_module\") {\n  loadable_module(target_name) {\n    forward_variables_from(invoker,\n                           \"*\",\n                           [\n                             \"configs\",\n                             \"remove_configs\",\n                           ])\n    if (defined(invoker.remove_configs)) {\n      configs -= invoker.remove_configs\n    }\n\n    if (defined(invoker.configs)) {\n      configs += invoker.configs\n    }\n\n    if (crashpad_is_in_fuchsia) {\n      conversion_config = [ \"//build/config:Wno-conversion\" ]\n      if (configs + conversion_config - conversion_config == configs) {\n        # TODO(https://fxbug.dev/42136089): Decide if these are worth enabling.\n        configs += conversion_config\n      }\n    }\n  }\n}\n\ntemplate(\"crashpad_static_library\") {\n  static_library(target_name) {\n    forward_variables_from(invoker,\n                           \"*\",\n                           [\n                             \"configs\",\n                             \"remove_configs\",\n                           ])\n    if (defined(invoker.remove_configs)) {\n      configs -= invoker.remove_configs\n    }\n\n    if (defined(invoker.configs)) {\n      configs += invoker.configs\n    }\n\n    if (crashpad_is_in_fuchsia) {\n      conversion_config = [ \"//build/config:Wno-conversion\" ]\n      if (configs + conversion_config - conversion_config == configs) {\n        # TODO(https://fxbug.dev/42136089): Decide if these are worth enabling.\n        configs += conversion_config\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "build/crashpad_fuzzer_test.gni",
    "content": "# Copyright 2018 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"crashpad_buildconfig.gni\")\nimport(\"test.gni\")\nif (crashpad_is_in_chromium) {\n  import(\"//testing/libfuzzer/fuzzer_test.gni\")\n}\n\ntemplate(\"crashpad_fuzzer_test\") {\n  if (crashpad_is_standalone && crashpad_use_libfuzzer) {\n    test(target_name) {\n      forward_variables_from(invoker,\n                             [\n                               \"cflags\",\n                               \"cflags_cc\",\n                               \"check_includes\",\n                               \"defines\",\n                               \"include_dirs\",\n                               \"sources\",\n                             ])\n      configs += [ \"..:crashpad_config\" ]\n      if (defined(invoker.deps)) {\n        deps = invoker.deps\n      }\n      deps += [ \"../third_party/libfuzzer\" ]\n\n      if (!defined(invoker.cflags)) {\n        cflags = []\n      }\n      cflags += [ \"-fsanitize=fuzzer\" ]\n    }\n    if (defined(invoker.seed_corpus)) {\n      not_needed(invoker, [ \"seed_corpus\" ])\n    }\n  } else if (crashpad_is_in_chromium && use_fuzzing_engine) {\n    # Append \"crashpad_\" to the beginning of the fuzzer's name to make it easier\n    # in Chromium to recognize where fuzzer came from.\n    forward_variables_from(invoker, \"*\")\n    fuzzer_test(\"crashpad_\" + target_name) {\n    }\n  } else {\n    not_needed(invoker, \"*\")\n    group(target_name) {\n    }\n  }\n}\n"
  },
  {
    "path": "build/fuchsia_envs.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2024 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport platform\nimport subprocess\nimport sys\n\n\ndef main(args):\n    \"\"\"\n    Executes the test-scripts with required environment variables. It acts like\n    /usr/bin/env, but provides some extra functionality to dynamically set up\n    the environment variables.\n\n    Args:\n        args: the command line arguments without the script name itself.\n    \"\"\"\n    os.environ['SRC_ROOT'] = os.path.abspath(\n        os.path.join(os.path.dirname(__file__), '..'))\n\n    assert platform.system() == 'Linux', 'Unsupported OS ' + platform.system()\n    os.environ['FUCHSIA_SDK_ROOT'] = os.path.join(\n        os.environ['SRC_ROOT'], 'third_party/fuchsia/sdk/linux-amd64/')\n    os.environ['FUCHSIA_GN_SDK_ROOT'] = os.path.join(\n        os.environ['SRC_ROOT'], 'third_party/fuchsia-gn-sdk/src')\n    os.environ['FUCHSIA_READELF'] = os.path.join(os.environ['SRC_ROOT'],\n        'third_party/fuchsia/clang/linux-amd64/bin/llvm-readelf')\n\n    return subprocess.run(args).returncode\n\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv[1:]))\n"
  },
  {
    "path": "build/install_linux_sysroot.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2018 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Various code adapted from:\n# https://cs.chromium.org/chromium/src/build/linux/sysroot_scripts/install-sysroot.py\n\nimport os\nimport shutil\nimport subprocess\nimport sys\nimport urllib.request\n\nSCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))\n\n# Sysroot revision from:\n# https://cs.chromium.org/chromium/src/build/linux/sysroot_scripts/sysroots.json\nSERVER = 'https://commondatastorage.googleapis.com'\nPATH = 'chrome-linux-sysroot/toolchain'\nREVISION = '43a87bbebccad99325fdcf34166295b121ee15c7'\nFILENAME = 'debian_sid_amd64_sysroot.tar.xz'\n\n\ndef main():\n    url = '%s/%s/%s/%s' % (SERVER, PATH, REVISION, FILENAME)\n\n    sysroot = os.path.join(SCRIPT_DIR, os.pardir, 'third_party', 'linux',\n                           'sysroot')\n\n    stamp = os.path.join(sysroot, '.stamp')\n    if os.path.exists(stamp):\n        with open(stamp) as s:\n            if s.read() == url:\n                return\n\n    print('Installing Debian root image from %s' % url)\n\n    if os.path.isdir(sysroot):\n        shutil.rmtree(sysroot)\n    os.mkdir(sysroot)\n    tarball = os.path.join(sysroot, FILENAME)\n    print('Downloading %s' % url)\n\n    for _ in range(3):\n        response = urllib.request.urlopen(url)\n        with open(tarball, 'wb') as f:\n            f.write(response.read())\n        break\n    else:\n        raise Exception('Failed to download %s' % url)\n\n    subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot])\n\n    os.remove(tarball)\n\n    with open(stamp, 'w') as s:\n        s.write(url)\n\n\nif __name__ == '__main__':\n    main()\n    sys.exit(0)\n"
  },
  {
    "path": "build/ios/Unittest-Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleIdentifier</key>\n\t<string>${IOS_BUNDLE_ID_PREFIX}.${GTEST_BUNDLE_ID_SUFFIX:rfc1034identifier}</string>\n\t<key>UIApplicationDelegate</key>\n\t<string>CrashpadUnitTestDelegate</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "build/ios/convert_gn_xcodeproj.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2020 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Convert GN Xcode projects to platform and configuration independent targets.\n\nGN generates Xcode projects that build one configuration only. However, typical\niOS development involves using the Xcode IDE to toggle the platform and\nconfiguration. This script replaces the 'gn' configuration with 'Debug',\n'Release' and 'Profile', and changes the ninja invocation to honor these\nconfigurations.\n\"\"\"\n\nimport argparse\nimport collections\nimport copy\nimport filecmp\nimport functools\nimport hashlib\nimport io\nimport json\nimport os\nimport re\nimport shutil\nimport string\nimport subprocess\nimport sys\nimport tempfile\nimport xml.etree.ElementTree\n\n\nLLDBINIT_PATH = '$(PROJECT_DIR)/.lldbinit'\n\nPYTHON_RE = re.compile('[ /]python[23]?$')\n\nXCTEST_PRODUCT_TYPES = frozenset((\n    'com.apple.product-type.bundle.unit-test',\n    'com.apple.product-type.bundle.ui-testing',\n))\n\nSCHEME_PRODUCT_TYPES = frozenset((\n    'com.apple.product-type.app-extension',\n    'com.apple.product-type.application',\n    'com.apple.product-type.framework'\n))\n\n\nclass Template(string.Template):\n\n  \"\"\"A subclass of string.Template that changes delimiter.\"\"\"\n\n  delimiter = '@'\n\n\n@functools.lru_cache\ndef LoadSchemeTemplate(root, name):\n  \"\"\"Return a string.Template object for scheme file loaded relative to root.\"\"\"\n  path = os.path.join(root, 'build', 'ios', name + '.template')\n  with open(path) as file:\n    return Template(file.read())\n\n\ndef CreateIdentifier(str_id):\n  \"\"\"Return a 24 characters string that can be used as an identifier.\"\"\"\n  return hashlib.sha1(str_id.encode(\"utf-8\")).hexdigest()[:24].upper()\n\n\ndef GenerateSchemeForTarget(root, project, old_project, name, path, is_test):\n  \"\"\"Generates the .xcsheme file for target named |name|.\n\n  The file is generated in the new project schemes directory from a template.\n  If there is an existing previous project, then the old scheme file is copied\n  and the lldbinit setting is set. If lldbinit setting is already correct, the\n  file is not modified, just copied.\n  \"\"\"\n  project_name = os.path.basename(project)\n  relative_path = os.path.join('xcshareddata', 'xcschemes', name + '.xcscheme')\n  identifier = CreateIdentifier('%s %s' % (name, path))\n\n  scheme_path = os.path.join(project, relative_path)\n  if not os.path.isdir(os.path.dirname(scheme_path)):\n    os.makedirs(os.path.dirname(scheme_path))\n\n  substitutions = {\n    'LLDBINIT_PATH': LLDBINIT_PATH,\n    'BLUEPRINT_IDENTIFIER': identifier,\n    'BUILDABLE_NAME': path,\n    'BLUEPRINT_NAME': name,\n    'PROJECT_NAME': project_name\n  }\n\n  if is_test:\n    template = LoadSchemeTemplate(root, 'xcodescheme-testable')\n    substitutions['PATH'] = os.environ['PATH']\n\n  else:\n    template = LoadSchemeTemplate(root, 'xcodescheme')\n\n  old_scheme_path = os.path.join(old_project, relative_path)\n  if os.path.exists(old_scheme_path):\n\n    tree = xml.etree.ElementTree.parse(old_scheme_path)\n    tree_root = tree.getroot()\n\n    for reference in tree_root.findall('.//BuildableReference'):\n      for (attr, value) in (\n          ('BuildableName', path),\n          ('BlueprintName', name),\n          ('BlueprintIdentifier', identifier)):\n        if reference.get(attr) != value:\n          reference.set(attr, value)\n\n    for child in tree_root:\n      if child.tag not in ('TestAction', 'LaunchAction'):\n        continue\n\n      if child.get('customLLDBInitFile') != LLDBINIT_PATH:\n        child.set('customLLDBInitFile', LLDBINIT_PATH)\n\n    if is_test:\n\n      template_tree = xml.etree.ElementTree.parse(\n          io.StringIO(template.substitute(**substitutions)))\n\n      template_tree_root = template_tree.getroot()\n      for child in tree_root:\n        if child.tag != 'BuildAction':\n          continue\n\n        for subchild in list(child):\n          child.remove(subchild)\n\n        for post_action in template_tree_root.findall('.//PostActions'):\n          child.append(post_action)\n\n    tree.write(scheme_path, xml_declaration=True, encoding='UTF-8')\n\n  else:\n\n    with open(scheme_path, 'w') as scheme_file:\n      scheme_file.write(template.substitute(**substitutions))\n\n\nclass XcodeProject(object):\n\n  def __init__(self, objects, counter = 0):\n    self.objects = objects\n    self.counter = 0\n\n  def AddObject(self, parent_name, obj):\n    while True:\n      self.counter += 1\n      str_id = \"%s %s %d\" % (parent_name, obj['isa'], self.counter)\n      new_id = CreateIdentifier(str_id)\n\n      # Make sure ID is unique. It's possible there could be an id conflict\n      # since this is run after GN runs.\n      if new_id not in self.objects:\n        self.objects[new_id] = obj\n        return new_id\n\n  def IterObjectsByIsa(self, isa):\n    \"\"\"Iterates overs objects of the |isa| type.\"\"\"\n    for key, obj in self.objects.items():\n      if obj['isa'] == isa:\n        yield (key, obj)\n\n  def IterNativeTargetByProductType(self, product_types):\n    \"\"\"Iterates over PBXNativeTarget objects of any |product_types| types.\"\"\"\n    for key, obj in self.IterObjectsByIsa('PBXNativeTarget'):\n      if obj['productType'] in product_types:\n        yield (key, obj)\n\n  def UpdateBuildScripts(self):\n    \"\"\"Update build scripts to respect configuration and platforms.\"\"\"\n    for key, obj in self.IterObjectsByIsa('PBXShellScriptBuildPhase'):\n\n      shell_path = obj['shellPath']\n      shell_code = obj['shellScript']\n      if shell_path.endswith('/sh'):\n        shell_code = shell_code.replace(\n            'ninja -C .',\n            'ninja -C \"../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}\"')\n      elif PYTHON_RE.search(shell_path):\n        shell_code = shell_code.replace(\n            '''ninja_params = [ '-C', '.' ]''',\n            '''ninja_params = [ '-C', '../' + os.environ['CONFIGURATION']'''\n            ''' + os.environ['EFFECTIVE_PLATFORM_NAME'] ]''')\n\n      # Replace the build script in the object.\n      obj['shellScript'] = shell_code\n\n\n  def UpdateBuildConfigurations(self, configurations):\n    \"\"\"Add new configurations, using the first one as default.\"\"\"\n\n    # Create a list with all the objects of interest. This is needed\n    # because objects will be added to/removed from the project upon\n    # iterating this list and python dictionaries cannot be mutated\n    # during iteration.\n\n    for key, obj in list(self.IterObjectsByIsa('XCConfigurationList')):\n      # Use the first build configuration as template for creating all the\n      # new build configurations.\n      build_config_template = self.objects[obj['buildConfigurations'][0]]\n      build_config_template['buildSettings']['CONFIGURATION_BUILD_DIR'] = \\\n          '$(PROJECT_DIR)/../$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'\n\n      # Remove the existing build configurations from the project before\n      # creating the new ones.\n      for build_config_id in obj['buildConfigurations']:\n        del self.objects[build_config_id]\n      obj['buildConfigurations'] = []\n\n      for configuration in configurations:\n        build_config = copy.copy(build_config_template)\n        build_config['name'] = configuration\n        build_config_id = self.AddObject('products', build_config)\n        obj['buildConfigurations'].append(build_config_id)\n\n  def GetHostMappingForXCTests(self):\n    \"\"\"Returns a dict from targets to the list of their xctests modules.\"\"\"\n    mapping = collections.defaultdict(list)\n    for key, obj in self.IterNativeTargetByProductType(XCTEST_PRODUCT_TYPES):\n      build_config_lists_id = obj['buildConfigurationList']\n      build_configs = self.objects[build_config_lists_id]['buildConfigurations']\n\n      # Use the first build configuration to get the name of the host target.\n      # This is arbitrary, but since the build configuration are all identical\n      # after UpdateBuildConfiguration, except for their 'name', it is fine.\n      build_config = self.objects[build_configs[0]]\n      if obj['productType'] == 'com.apple.product-type.bundle.unit-test':\n        # The test_host value will look like this:\n        # `$(BUILD_PRODUCTS_DIR)/host_app_name.app/host_app_name`\n        #\n        # Extract the `host_app_name.app` part as key for the output.\n        test_host_path = build_config['buildSettings']['TEST_HOST']\n        test_host_name = os.path.basename(os.path.dirname(test_host_path))\n      else:\n        test_host_name = build_config['buildSettings']['TEST_TARGET_NAME']\n\n      test_name = obj['name']\n      test_path = self.objects[obj['productReference']]['path']\n\n      mapping[test_host_name].append((key, test_name, test_path))\n\n    return dict(mapping)\n\n\ndef check_output(command):\n  \"\"\"Wrapper around subprocess.check_output that decode output as utf-8.\"\"\"\n  return subprocess.check_output(command).decode('utf-8')\n\n\ndef CopyFileIfChanged(source_path, target_path):\n  \"\"\"Copy |source_path| to |target_path| if different.\"\"\"\n  target_dir = os.path.dirname(target_path)\n  if not os.path.isdir(target_dir):\n    os.makedirs(target_dir)\n  if not os.path.exists(target_path) or \\\n      not filecmp.cmp(source_path, target_path):\n    shutil.copyfile(source_path, target_path)\n\n\ndef CopyTreeIfChanged(source, target):\n  \"\"\"Copy |source| to |target| recursively; files are copied iff changed.\"\"\"\n  if os.path.isfile(source):\n    return CopyFileIfChanged(source, target)\n  if not os.path.isdir(target):\n    os.makedirs(target)\n  for name in os.listdir(source):\n    CopyTreeIfChanged(\n        os.path.join(source, name),\n        os.path.join(target, name))\n\n\ndef LoadXcodeProjectAsJSON(project_dir):\n  \"\"\"Return Xcode project at |path| as a JSON string.\"\"\"\n  return check_output([\n      'plutil', '-convert', 'json', '-o', '-',\n      os.path.join(project_dir, 'project.pbxproj')])\n\n\ndef WriteXcodeProject(output_path, json_string):\n  \"\"\"Save Xcode project to |output_path| as XML.\"\"\"\n  with tempfile.NamedTemporaryFile() as temp_file:\n    temp_file.write(json_string.encode(\"utf-8\"))\n    temp_file.flush()\n    subprocess.check_call(['plutil', '-convert', 'xml1', temp_file.name])\n    CopyFileIfChanged(\n        temp_file.name,\n        os.path.join(output_path, 'project.pbxproj'))\n\n\ndef UpdateXcodeProject(project_dir, old_project_dir, configurations, root_dir):\n  \"\"\"Update inplace Xcode project to support multiple configurations.\n\n  Args:\n    project_dir: path to the input Xcode project\n    configurations: list of string corresponding to the configurations that\n      need to be supported by the tweaked Xcode projects, must contains at\n      least one value.\n    root_dir: path to the root directory used to find markdown files\n  \"\"\"\n  json_data = json.loads(LoadXcodeProjectAsJSON(project_dir))\n  project = XcodeProject(json_data['objects'])\n\n  project.UpdateBuildScripts()\n  project.UpdateBuildConfigurations(configurations)\n\n  mapping = project.GetHostMappingForXCTests()\n\n  # Generate schemes for application, extensions and framework targets\n  for key, obj in project.IterNativeTargetByProductType(SCHEME_PRODUCT_TYPES):\n    product = project.objects[obj['productReference']]\n    product_path = product['path']\n\n    # Do not generate scheme for the XCTests and XXCUITests target app.\n    # Instead, a scheme will be generated for each test modules.\n    tests = mapping.get(product_path, []) + mapping.get(obj['name'], [])\n    if not tests:\n      GenerateSchemeForTarget(\n          root_dir, project_dir, old_project_dir,\n          obj['name'], product_path, False)\n\n    else:\n      for (_, test_name, test_path) in tests:\n        GenerateSchemeForTarget(\n          root_dir, project_dir, old_project_dir,\n          test_name, test_path, True)\n\n  root_object = project.objects[json_data['rootObject']]\n  main_group = project.objects[root_object['mainGroup']]\n\n  sources = None\n  for child_key in main_group['children']:\n    child = project.objects[child_key]\n    if child.get('name') == 'Source' or child.get('name') == 'Sources':\n      sources = child\n      break\n\n  if sources is None:\n    sources = main_group\n\n  AddMarkdownToProject(project, root_dir, sources, sources is main_group)\n  SortFileReferencesByName(project, sources, root_object.get('productRefGroup'))\n\n  objects = collections.OrderedDict(sorted(project.objects.items()))\n  # WriteXcodeProject(project_dir, json.dumps(json_data))\n\n\ndef CreateGroup(project, parent_group, group_name, use_relative_paths):\n  group_object = {\n    'children': [],\n    'isa': 'PBXGroup',\n    'sourceTree': '<group>',\n  }\n  if use_relative_paths:\n    group_object['path'] = group_name\n  else:\n    group_object['name'] = group_name\n  parent_group_name = parent_group.get('name', '')\n  group_object_key = project.AddObject(parent_group_name, group_object)\n  parent_group['children'].append(group_object_key)\n  return group_object\n\n\nclass ObjectKey(object):\n\n  \"\"\"Wrapper around PBXFileReference and PBXGroup for sorting.\n\n  A PBXGroup represents a \"directory\" containing a list of files in an\n  Xcode project; it can contain references to a list of directories or\n  files.\n\n  A PBXFileReference represents a \"file\".\n\n  The type is stored in the object \"isa\" property as a string. Since we\n  want to sort all directories before all files, the < and > operators\n  are defined so that if \"isa\" is different, they are sorted in the\n  reverse of alphabetic ordering, otherwise the name (or path) property\n  is checked and compared in alphabetic order.\n  \"\"\"\n\n  def __init__(self, obj, last):\n    self.isa = obj['isa']\n    if 'name' in obj:\n      self.name = obj['name']\n    else:\n      self.name = obj['path']\n    self.last = last\n\n  def __lt__(self, other):\n    if self.last != other.last:\n      return other.last\n    if self.isa != other.isa:\n      return self.isa > other.isa\n    return self.name < other.name\n\n  def __gt__(self, other):\n    if self.last != other.last:\n      return self.last\n    if self.isa != other.isa:\n      return self.isa < other.isa\n    return self.name > other.name\n\n  def __eq__(self, other):\n    return self.isa == other.isa and self.name == other.name\n\n\ndef SortFileReferencesByName(project, group_object, products_group_ref):\n  SortFileReferencesByNameWithSortKey(\n      project, group_object,\n      lambda ref: ObjectKey(project.objects[ref], ref == products_group_ref))\n\n\ndef SortFileReferencesByNameWithSortKey(project, group_object, sort_key):\n  group_object['children'].sort(key=sort_key)\n  for key in group_object['children']:\n    child = project.objects[key]\n    if child['isa'] == 'PBXGroup':\n      SortFileReferencesByNameWithSortKey(project, child, sort_key)\n\n\ndef AddMarkdownToProject(project, root_dir, group_object, use_relative_paths):\n  list_files_cmd = ['git', '-C', root_dir, 'ls-files', '*.md']\n  paths = check_output(list_files_cmd).splitlines()\n  ios_internal_dir = os.path.join(root_dir, 'ios_internal')\n  if os.path.exists(ios_internal_dir):\n    list_files_cmd = ['git', '-C', ios_internal_dir, 'ls-files', '*.md']\n    ios_paths = check_output(list_files_cmd).splitlines()\n    paths.extend([os.path.join(\"ios_internal\", path) for path in ios_paths])\n  for path in paths:\n    new_markdown_entry = {\n      \"fileEncoding\": \"4\",\n      \"isa\": \"PBXFileReference\",\n      \"lastKnownFileType\": \"net.daringfireball.markdown\",\n      \"sourceTree\": \"<group>\"\n    }\n    if use_relative_paths:\n      new_markdown_entry['path'] = os.path.basename(path)\n    else:\n      new_markdown_entry['name'] = os.path.basename(path)\n      new_markdown_entry['path'] = path\n    folder = GetFolderForPath(\n        project, group_object, os.path.dirname(path),\n        use_relative_paths)\n    folder_name = folder.get('name', None)\n    if folder_name is None:\n      folder_name = folder.get('path', 'sources')\n    new_markdown_entry_id = project.AddObject(folder_name, new_markdown_entry)\n    folder['children'].append(new_markdown_entry_id)\n\n\ndef GetFolderForPath(project, group_object, path, use_relative_paths):\n  objects = project.objects\n  if not path:\n    return group_object\n  for folder in path.split('/'):\n    children = group_object['children']\n    new_root = None\n    for child_key in children:\n      child = objects[child_key]\n      if child['isa'] == 'PBXGroup':\n        child_name = child.get('name', None)\n        if child_name is None:\n          child_name = child.get('path')\n        if child_name == folder:\n          new_root = child\n          break\n    if not new_root:\n      # If the folder isn't found we could just cram it into the leaf existing\n      # folder, but that leads to folders with tons of README.md inside.\n      new_root = CreateGroup(project, group_object, folder, use_relative_paths)\n    group_object = new_root\n  return group_object\n\n\ndef ConvertGnXcodeProject(root_dir, proj_name, input_dir, output_dir, configs):\n  '''Tweak the Xcode project generated by gn to support multiple configurations.\n\n  The Xcode projects generated by \"gn gen --ide\" only supports a single\n  platform and configuration (as the platform and configuration are set\n  per output directory). This method takes as input such projects and\n  add support for multiple configurations and platforms (to allow devs\n  to select them in Xcode).\n\n  Args:\n    root_dir: directory that is the root of the project\n    proj_name: name of the Xcode project \"file\" (usually `all.xcodeproj`)\n    input_dir: directory containing the XCode projects created by \"gn gen --ide\"\n    output_dir: directory where the tweaked Xcode projects will be saved\n    configs: list of string corresponding to the configurations that need to be\n        supported by the tweaked Xcode projects, must contains at least one\n        value.\n  '''\n\n  UpdateXcodeProject(\n      os.path.join(input_dir, proj_name),\n      os.path.join(output_dir, proj_name),\n      configs, root_dir)\n\n  CopyTreeIfChanged(os.path.join(input_dir, proj_name),\n                    os.path.join(output_dir, proj_name))\n\n\ndef Main(args):\n  parser = argparse.ArgumentParser(\n      description='Convert GN Xcode projects for iOS.')\n  parser.add_argument(\n      'input',\n      help='directory containing [product|all] Xcode projects.')\n  parser.add_argument(\n      'output',\n      help='directory where to generate the iOS configuration.')\n  parser.add_argument(\n      '--add-config', dest='configurations', default=[], action='append',\n      help='configuration to add to the Xcode project')\n  parser.add_argument(\n      '--root', type=os.path.abspath, required=True,\n      help='root directory of the project')\n  parser.add_argument(\n      '--project-name', default='all.xcodeproj', dest='proj_name',\n      help='name of the Xcode project (default: %(default)s)')\n  args = parser.parse_args(args)\n\n  if not os.path.isdir(args.input):\n    sys.stderr.write('Input directory does not exists.\\n')\n    return 1\n\n  if args.proj_name not in os.listdir(args.input):\n    sys.stderr.write(\n        'Input directory does not contain the Xcode project.\\n')\n    return 1\n\n  if not args.configurations:\n    sys.stderr.write('At least one configuration required, see --add-config.\\n')\n    return 1\n\n  ConvertGnXcodeProject(\n      args.root,\n      args.proj_name,\n      args.input,\n      args.output,\n      args.configurations)\n\nif __name__ == '__main__':\n  sys.exit(Main(sys.argv[1:]))\n"
  },
  {
    "path": "build/ios/setup_ios_gn.config",
    "content": "# Copyright 2020 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n[xcode]\n# Controls settings for the generated Xcode project. If jobs is non-zero\n# it will be passed to the ninja invocation in Xcode project.\njobs = 0\n\n[build]\n# Controls the build output. The only supported values are \"64-bit\", \"32-bit\"\n# and \"fat\" (for a fat binary supporting both \"32-bit\" and \"64-bit\" cpus).\narch = \"64-bit\"\n\n[gn_args]\n# Values in that section will be copied verbatim in the generated args.gn file.\ntarget_os = \"ios\"\n\n[filters]\n# List of target files to pass to --filters argument of gn gen when generating\n# the Xcode project. By default, list all targets from ios/ and ios_internal/\n# and the targets corresponding to the unit tests run on the bots.\n"
  },
  {
    "path": "build/ios/setup_ios_gn.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2020 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport configparser\nimport convert_gn_xcodeproj\nimport errno\nimport io\nimport os\nimport platform\nimport re\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\n\n\nSUPPORTED_TARGETS = ('appletvos', 'appletvsimulator', 'iphoneos',\n                     'iphonesimulator', 'maccatalyst')\nSUPPORTED_CONFIGS = ('Debug', 'Release', 'Profile', 'Official')\n\n# Pattern matching lines from ~/.lldbinit that must not be copied to the\n# generated .lldbinit file. They match what the user were told to add to\n# their global ~/.lldbinit file before setup-gn.py was updated to generate\n# a project specific file and thus must not be copied as they would cause\n# the settings to be overwritten.\nLLDBINIT_SKIP_PATTERNS = (\n    re.compile('^script sys.path\\\\[:0\\\\] = \\\\[\\'.*/src/tools/lldb\\'\\\\]$'),\n    re.compile('^script import lldbinit$'),\n    re.compile('^settings append target.source-map .* /google/src/.*$'),\n)\n\n\ndef HostCpuArch():\n  '''Returns the arch of the host cpu for GN.'''\n  HOST_CPU_ARCH = {\n    'arm64': '\"arm64\"',\n    'x86_64': '\"x64\"',\n  }\n  return HOST_CPU_ARCH[platform.machine()]\n\n\nclass ConfigParserWithStringInterpolation(configparser.ConfigParser):\n\n  '''A .ini file parser that supports strings and environment variables.'''\n\n  ENV_VAR_PATTERN = re.compile(r'\\$([A-Za-z0-9_]+)')\n\n  def values(self, section):\n    return filter(\n        lambda val: val != '',\n        map(lambda kv: self._UnquoteString(self._ExpandEnvVar(kv[1])),\n            configparser.ConfigParser.items(self, section)))\n\n  def getstring(self, section, option, fallback=''):\n    try:\n      raw_value = self.get(section, option)\n    except configparser.NoOptionError:\n      return fallback\n    return self._UnquoteString(self._ExpandEnvVar(raw_value))\n\n  def _UnquoteString(self, string):\n    if not string or string[0] != '\"' or string[-1] != '\"':\n      return string\n    return string[1:-1]\n\n  def _ExpandEnvVar(self, value):\n    match = self.ENV_VAR_PATTERN.search(value)\n    if not match:\n      return value\n    name, (begin, end) = match.group(1), match.span(0)\n    prefix, suffix = value[:begin], self._ExpandEnvVar(value[end:])\n    return prefix + os.environ.get(name, '') + suffix\n\n\nclass GnGenerator(object):\n\n  '''Holds configuration for a build and method to generate gn default files.'''\n\n  FAT_BUILD_DEFAULT_ARCH = '64-bit'\n\n  TARGET_CPU_VALUES = {\n    'appletvos': '\"arm64\"',\n    'appletvsimulator': HostCpuArch(),\n    'iphoneos': '\"arm64\"',\n    'iphonesimulator': HostCpuArch(),\n    'maccatalyst': HostCpuArch(),\n  }\n\n  TARGET_ENVIRONMENT_VALUES = {\n    'appletvos': '\"device\"',\n    'appletvsimulator': '\"simulator\"',\n    'iphoneos': '\"device\"',\n    'iphonesimulator': '\"simulator\"',\n    'maccatalyst': '\"catalyst\"'\n  }\n\n  TARGET_PLATFORM_VALUES = {\n    'appletvos': '\"tvos\"',\n    'appletvsimulator': '\"tvos\"',\n    'iphoneos': '\"iphoneos\"',\n    'iphonesimulator': '\"iphoneos\"',\n    'maccatalyst': '\"iphoneos\"'\n  }\n\n  def __init__(self, settings, config, target):\n    assert target in SUPPORTED_TARGETS\n    assert config in SUPPORTED_CONFIGS\n    self._settings = settings\n    self._config = config\n    self._target = target\n\n  def _GetGnArgs(self):\n    \"\"\"Build the list of arguments to pass to gn.\n\n    Returns:\n      A list of tuple containing gn variable names and variable values (it\n      is not a dictionary as the order needs to be preserved).\n    \"\"\"\n    args = []\n\n    is_debug = self._config == 'Debug'\n    official = self._config == 'Official'\n    is_optim = self._config in ('Profile', 'Official')\n\n    args.append(('target_os', '\"ios\"'))\n    args.append(('is_debug', is_debug))\n\n    if os.environ.get('FORCE_MAC_TOOLCHAIN', '0') == '1':\n      args.append(('use_system_xcode', False))\n\n    args.append(('target_cpu', self.TARGET_CPU_VALUES[self._target]))\n    args.append(\n        ('target_environment', self.TARGET_ENVIRONMENT_VALUES[self._target]))\n    args.append(('target_platform', self.TARGET_PLATFORM_VALUES[self._target]))\n\n    # Add user overrides after the other configurations so that they can\n    # refer to them and override them.\n    args.extend(self._settings.items('gn_args'))\n    return args\n\n\n  def Generate(self, gn_path, proj_name, root_path, build_dir):\n    self.WriteArgsGn(build_dir, xcode_project_name=proj_name)\n    subprocess.check_call(self.GetGnCommand(\n        gn_path, root_path, build_dir, xcode_project_name=proj_name))\n\n  def CreateGnRules(self, gn_path, root_path, build_dir):\n    gn_command = self.GetGnCommand(gn_path, root_path, build_dir)\n    self.WriteArgsGn(build_dir)\n    self.WriteBuildNinja(gn_command, build_dir)\n    self.WriteBuildNinjaDeps(build_dir)\n\n  def WriteArgsGn(self, build_dir, xcode_project_name=None):\n    with open(os.path.join(build_dir, 'args.gn'), 'w') as stream:\n      stream.write('# This file was generated by setup-gn.py. Do not edit\\n')\n      stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\\n')\n      stream.write('# to configure settings.\\n')\n      stream.write('\\n')\n\n      if self._target != 'maccatalyst':\n        if self._settings.has_section('$imports$'):\n          for import_rule in self._settings.values('$imports$'):\n            stream.write('import(\"%s\")\\n' % import_rule)\n          stream.write('\\n')\n\n      gn_args = self._GetGnArgs()\n\n      for name, value in gn_args:\n        if isinstance(value, bool):\n          stream.write('%s = %s\\n' % (name, str(value).lower()))\n        elif isinstance(value, list):\n          stream.write('%s = [%s' % (name, '\\n' if len(value) > 1 else ''))\n          if len(value) == 1:\n            prefix = ' '\n            suffix = ' '\n          else:\n            prefix = '  '\n            suffix = ',\\n'\n          for item in value:\n            if isinstance(item, bool):\n              stream.write('%s%s%s' % (prefix, str(item).lower(), suffix))\n            else:\n              stream.write('%s%s%s' % (prefix, item, suffix))\n          stream.write(']\\n')\n        else:\n          # ConfigParser removes quote around empty string which confuse\n          # `gn gen` so restore them.\n          if not value:\n            value = '\"\"'\n          stream.write('%s = %s\\n' % (name, value))\n\n  def WriteBuildNinja(self, gn_command, build_dir):\n    with open(os.path.join(build_dir, 'build.ninja'), 'w') as stream:\n      stream.write('ninja_required_version = 1.7.2\\n')\n      stream.write('\\n')\n      stream.write('rule gn\\n')\n      stream.write('  command = %s\\n' % NinjaEscapeCommand(gn_command))\n      stream.write('  description = Regenerating ninja files\\n')\n      stream.write('\\n')\n      stream.write('build build.ninja: gn\\n')\n      stream.write('  generator = 1\\n')\n      stream.write('  depfile = build.ninja.d\\n')\n\n  def WriteBuildNinjaDeps(self, build_dir):\n    with open(os.path.join(build_dir, 'build.ninja.d'), 'w') as stream:\n      stream.write('build.ninja: nonexistant_file.gn\\n')\n\n  def GetGnCommand(self, gn_path, src_path, out_path, xcode_project_name=None):\n    gn_command = [ gn_path, '--root=%s' % os.path.realpath(src_path), '-q' ]\n    if xcode_project_name is not None:\n      gn_command.append('--ide=xcode')\n      gn_command.append('--ninja-executable=autoninja')\n      gn_command.append('--xcode-build-system=new')\n      gn_command.append('--xcode-project=%s' % xcode_project_name)\n      gn_command.append('--xcode-additional-files-patterns=*.md')\n      gn_command.append('--xcode-configs=' + ';'.join(SUPPORTED_CONFIGS))\n      gn_command.append('--xcode-config-build-dir='\n                        '//out/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}')\n      if self._settings.has_section('filters'):\n        target_filters = self._settings.values('filters')\n        if target_filters:\n          gn_command.append('--filters=%s' % ';'.join(target_filters))\n    else:\n      gn_command.append('--check')\n    gn_command.append('gen')\n    gn_command.append('//%s' %\n        os.path.relpath(os.path.abspath(out_path), os.path.abspath(src_path)))\n    return gn_command\n\n\ndef NinjaNeedEscape(arg):\n  '''Returns True if |arg| needs to be escaped when written to .ninja file.'''\n  return ':' in arg or '*' in arg or ';' in arg\n\n\ndef NinjaEscapeCommand(command):\n  '''Escapes |command| in order to write it to .ninja file.'''\n  result = []\n  for arg in command:\n    if NinjaNeedEscape(arg):\n      arg = arg.replace(':', '$:')\n      arg = arg.replace(';', '\\\\;')\n      arg = arg.replace('*', '\\\\*')\n    else:\n      result.append(arg)\n  return ' '.join(result)\n\n\ndef FindGn():\n  '''Returns absolute path to gn binary looking at the PATH env variable.'''\n  for path in os.environ['PATH'].split(os.path.pathsep):\n    gn_path = os.path.join(path, 'gn')\n    if os.path.isfile(gn_path) and os.access(gn_path, os.X_OK):\n      return gn_path\n  return None\n\n\ndef GenerateXcodeProject(gn_path, root_dir, proj_name, out_dir, settings):\n  '''Generate Xcode project with Xcode and convert to multi-configurations.'''\n  prefix = os.path.abspath(os.path.join(out_dir, '_temp'))\n  temp_path = tempfile.mkdtemp(prefix=prefix)\n  try:\n    generator = GnGenerator(settings, 'Debug', 'iphonesimulator')\n    generator.Generate(gn_path, proj_name, root_dir, temp_path)\n    convert_gn_xcodeproj.ConvertGnXcodeProject(\n        root_dir,\n        '%s.xcodeproj' % proj_name,\n        os.path.join(temp_path),\n        os.path.join(out_dir, 'build'),\n        SUPPORTED_CONFIGS)\n  finally:\n    if os.path.exists(temp_path):\n      shutil.rmtree(temp_path)\n\ndef CreateLLDBInitFile(root_dir, out_dir, settings):\n  '''\n  Generate an .lldbinit file for the project that load the script that fixes\n  the mapping of source files (see docs/ios/build_instructions.md#debugging).\n  '''\n  with open(os.path.join(out_dir, 'build', '.lldbinit'), 'w') as lldbinit:\n    lldb_script_dir = os.path.join(os.path.abspath(root_dir), 'tools', 'lldb')\n    lldbinit.write('script sys.path[:0] = [\\'%s\\']\\n' % lldb_script_dir)\n    lldbinit.write('script import lldbinit\\n')\n\n    workspace_name = settings.getstring(\n        'gn_args',\n        'ios_internal_citc_workspace_name')\n\n    if workspace_name != '':\n      username = os.environ['USER']\n      for shortname in ('googlemac', 'third_party', 'blaze-out'):\n        lldbinit.write('settings append target.source-map %s %s\\n' % (\n            shortname,\n            '/google/src/cloud/%s/%s/google3/%s' % (\n                username, workspace_name, shortname)))\n\n    # Append the content of //ios/build/tools/lldbinit.defaults if it exists.\n    tools_dir = os.path.join(root_dir, 'ios', 'build', 'tools')\n    defaults_lldbinit_path = os.path.join(tools_dir, 'lldbinit.defaults')\n    if os.path.isfile(defaults_lldbinit_path):\n      with open(defaults_lldbinit_path) as defaults_lldbinit:\n        for line in defaults_lldbinit:\n          lldbinit.write(line)\n\n    # Append the content of ~/.lldbinit if it exists. Line that look like they\n    # are trying to configure source mapping are skipped as they probably date\n    # back from when setup-gn.py was not generating an .lldbinit file.\n    global_lldbinit_path = os.path.join(os.environ['HOME'], '.lldbinit')\n    if os.path.isfile(global_lldbinit_path):\n      with open(global_lldbinit_path) as global_lldbinit:\n        for line in global_lldbinit:\n          if any(pattern.match(line) for pattern in LLDBINIT_SKIP_PATTERNS):\n            continue\n          lldbinit.write(line)\n\n\ndef GenerateGnBuildRules(gn_path, root_dir, out_dir, settings):\n  '''Generates all template configurations for gn.'''\n  for config in SUPPORTED_CONFIGS:\n    for target in SUPPORTED_TARGETS:\n      build_dir = os.path.join(out_dir, '%s-%s' % (config, target))\n      if not os.path.isdir(build_dir):\n        os.makedirs(build_dir)\n\n      generator = GnGenerator(settings, config, target)\n      generator.CreateGnRules(gn_path, root_dir, build_dir)\n\n\ndef Main(args):\n  default_root = os.path.normpath(os.path.join(\n      os.path.dirname(__file__), os.pardir, os.pardir))\n\n  parser = argparse.ArgumentParser(\n      description='Generate build directories for use with gn.')\n  parser.add_argument(\n      'root', default=default_root, nargs='?',\n      help='root directory where to generate multiple out configurations')\n  parser.add_argument(\n      '--import', action='append', dest='import_rules', default=[],\n      help='path to file defining default gn variables')\n  parser.add_argument(\n      '--gn-path', default=None,\n      help='path to gn binary (default: look up in $PATH)')\n  parser.add_argument(\n      '--build-dir', default='out',\n      help='path where the build should be created (default: %(default)s)')\n  parser.add_argument(\n      '--config-path', default=os.path.expanduser('~/.setup-gn'),\n      help='path to the user config file (default: %(default)s)')\n  parser.add_argument(\n      '--system-config-path', default=os.path.splitext(__file__)[0] + '.config',\n      help='path to the default config file (default: %(default)s)')\n  parser.add_argument(\n      '--project-name', default='all', dest='proj_name',\n      help='name of the generated Xcode project (default: %(default)s)')\n  parser.add_argument(\n      '--no-xcode-project', action='store_true', default=False,\n      help='do not generate the build directory with XCode project')\n  args = parser.parse_args(args)\n\n  # Load configuration (first global and then any user overrides).\n  settings = ConfigParserWithStringInterpolation()\n  settings.read([\n      args.system_config_path,\n      args.config_path,\n  ])\n\n  # Add private sections corresponding to --import argument.\n  if args.import_rules:\n    settings.add_section('$imports$')\n    for i, import_rule in enumerate(args.import_rules):\n      if not import_rule.startswith('//'):\n        import_rule = '//%s' % os.path.relpath(\n            os.path.abspath(import_rule), os.path.abspath(args.root))\n      settings.set('$imports$', '$rule%d$' % i, import_rule)\n\n  # Validate settings.\n  if settings.getstring('build', 'arch') not in ('64-bit', '32-bit', 'fat'):\n    sys.stderr.write('ERROR: invalid value for build.arch: %s\\n' %\n        settings.getstring('build', 'arch'))\n    sys.exit(1)\n\n  # Find path to gn binary either from command-line or in PATH.\n  if args.gn_path:\n    gn_path = args.gn_path\n  else:\n    gn_path = FindGn()\n    if gn_path is None:\n      sys.stderr.write('ERROR: cannot find gn in PATH\\n')\n      sys.exit(1)\n\n  out_dir = os.path.join(args.root, args.build_dir)\n  if not os.path.isdir(out_dir):\n    os.makedirs(out_dir)\n\n  if not args.no_xcode_project:\n    GenerateXcodeProject(gn_path, args.root, args.proj_name, out_dir, settings)\n    CreateLLDBInitFile(args.root, out_dir, settings)\n  GenerateGnBuildRules(gn_path, args.root, out_dir, settings)\n\n\nif __name__ == '__main__':\n  sys.exit(Main(sys.argv[1:]))\n"
  },
  {
    "path": "build/ios/xcodescheme-testable.template",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1220\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <PostActions>\n         <ExecutionAction\n            ActionType = \"Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction\">\n            <ActionContent\n               title = \"Resign test runner\"\n               scriptText = \"unset -v XCODE_DEVELOPER_DIR_PATH&#10;&#10;BUILDDIR=&quot;${PROJECT_DIR}/../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}&quot;&#10;&#10;export PATH=&quot;@{PATH}&quot;&#10;&#10;rm -rf &quot;${BUILDDIR}/@{BLUEPRINT_NAME}-Runner.app&quot;&#10;autoninja -C &quot;${BUILDDIR}&quot; &quot;@{BLUEPRINT_NAME}&quot;&#10;\"\n               shellToInvoke = \"/bin/sh\">\n               <EnvironmentBuildable>\n                  <BuildableReference\n                     BuildableIdentifier = \"primary\"\n                     BlueprintIdentifier = \"@{BLUEPRINT_IDENTIFIER}\"\n                     BuildableName = \"@{BUILDABLE_NAME}\"\n                     BlueprintName = \"@{BLUEPRINT_NAME}\"\n                     ReferencedContainer = \"container:@{PROJECT_NAME}\">\n                  </BuildableReference>\n               </EnvironmentBuildable>\n            </ActionContent>\n         </ExecutionAction>\n      </PostActions>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      customLLDBInitFile = \"@{LLDBINIT_PATH}\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"@{BLUEPRINT_IDENTIFIER}\"\n               BuildableName = \"@{BUILDABLE_NAME}\"\n               BlueprintName = \"@{BLUEPRINT_NAME}\"\n               ReferencedContainer = \"container:@{PROJECT_NAME}\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      customLLDBInitFile = \"@{LLDBINIT_PATH}\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Official\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "build/ios/xcodescheme.template",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1220\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"@{BLUEPRINT_IDENTIFIER}\"\n               BuildableName = \"@{BUILDABLE_NAME}\"\n               BlueprintName = \"@{BLUEPRINT_NAME}\"\n               ReferencedContainer = \"container:@{PROJECT_NAME}\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      customLLDBInitFile = \"@{LLDBINIT_PATH}\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      customLLDBInitFile = \"@{LLDBINIT_PATH}\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"@{BLUEPRINT_IDENTIFIER}\"\n            BuildableName = \"@{BUILDABLE_NAME}\"\n            BlueprintName = \"@{BLUEPRINT_NAME}\"\n            ReferencedContainer = \"container:@{PROJECT_NAME}\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"@{BLUEPRINT_IDENTIFIER}\"\n            BuildableName = \"@{BUILDABLE_NAME}\"\n            BlueprintName = \"@{BLUEPRINT_NAME}\"\n            ReferencedContainer = \"container:@{PROJECT_NAME}\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Official\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "build/run_tests.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2014 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport os\nimport posixpath\nimport re\nimport shlex\nimport subprocess\nimport sys\nimport tempfile\nimport uuid\n\nCRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),\n                            os.pardir)\nIS_WINDOWS_HOST = sys.platform.startswith('win')\n\n\ndef _FindGNFromBinaryDir(binary_dir):\n    \"\"\"Attempts to determine the path to a GN binary used to generate the build\n    files in the given binary_dir. This is necessary because `gn` might not be\n    in the path or might be in a non-standard location, particularly on build\n    machines.\"\"\"\n\n    build_ninja = os.path.join(binary_dir, 'build.ninja')\n    if os.path.isfile(build_ninja):\n        with open(build_ninja, 'r') as f:\n            # Look for the always-generated regeneration rule of the form:\n            #\n            # rule gn\n            #   command = <gn binary> ... arguments ...\n            #\n            # to extract the gn binary's full path.\n            found_rule_gn = False\n            for line in f:\n                if line.strip() == 'rule gn':\n                    found_rule_gn = True\n                    continue\n                if found_rule_gn:\n                    if len(line) == 0 or line[0] != ' ':\n                        return None\n                    if line.startswith('  command = '):\n                        gn_command_line_parts = line.strip().split(' ')\n                        if len(gn_command_line_parts) > 2:\n                            return os.path.join(binary_dir,\n                                                gn_command_line_parts[2])\n\n    return None\n\n\ndef _GetGNArgument(argument_name, binary_dir):\n    \"\"\"Returns the value of a given GN argument, or None if it is not\n    explicitly specified.\"\"\"\n\n    gn_path = _FindGNFromBinaryDir(binary_dir)\n    if gn_path:\n        # Look for a GN “target_os”.\n        popen = subprocess.Popen([\n            gn_path, '--root=' + CRASHPAD_DIR, 'args', binary_dir,\n            '--list=%s' % argument_name, '--short'\n        ],\n                                 shell=IS_WINDOWS_HOST,\n                                 stdout=subprocess.PIPE,\n                                 stderr=open(os.devnull),\n                                 text=True)\n        value = popen.communicate()[0]\n        if popen.returncode == 0:\n            match = re.match(r'%s = \"(.*)\"$' % argument_name, value)\n            if match:\n                return match.group(1)\n    return None\n\n\ndef _EnableVTProcessingOnWindowsConsole():\n    \"\"\"Enables virtual terminal processing for ANSI/VT100-style escape sequences\n    on a Windows console attached to standard output. Returns True on success.\n    Returns False if standard output is not a console or if virtual terminal\n    processing is not supported. The feature was introduced in Windows 10.\n    \"\"\"\n\n    import pywintypes\n    import win32console\n    import winerror\n\n    stdout_console = win32console.GetStdHandle(win32console.STD_OUTPUT_HANDLE)\n    try:\n        console_mode = stdout_console.GetConsoleMode()\n    except pywintypes.error as e:\n        if e.winerror == winerror.ERROR_INVALID_HANDLE:\n            # Standard output is not a console.\n            return False\n        raise\n\n    try:\n        # From <wincon.h>. This would be\n        # win32console.ENABLE_VIRTUAL_TERMINAL_PROCESSING, but it’s too new to\n        # be defined there.\n        ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004\n\n        stdout_console.SetConsoleMode(console_mode |\n                                      ENABLE_VIRTUAL_TERMINAL_PROCESSING)\n    except pywintypes.error as e:\n        if e.winerror == winerror.ERROR_INVALID_PARAMETER:\n            # ANSI/VT100-style escape sequence processing isn’t supported before\n            # Windows 10.\n            return False\n        raise\n\n    return True\n\n\ndef _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):\n    local_test_path = os.path.join(binary_dir, test)\n    MAYBE_UNSUPPORTED_TESTS = (\n        'crashpad_client_test',\n        'crashpad_handler_test',\n        'crashpad_minidump_test',\n        'crashpad_snapshot_test',\n    )\n    if not os.path.exists(local_test_path) and test in MAYBE_UNSUPPORTED_TESTS:\n        print('This test is not present and may not be supported, skipping')\n        return\n\n    def _adb(*args):\n        # Flush all of this script’s own buffered stdout output before running\n        # adb, which will likely produce its own output on stdout.\n        sys.stdout.flush()\n\n        adb_command = ['adb', '-s', android_device]\n        adb_command.extend(args)\n        subprocess.check_call(adb_command, shell=IS_WINDOWS_HOST)\n\n    def _adb_push(sources, destination):\n        args = list(sources)\n        args.append(destination)\n        _adb('push', *args)\n\n    def _adb_shell(command_args, env={}):\n        # Build a command to execute via “sh -c” instead of invoking it\n        # directly. Here’s why:\n        #\n        # /system/bin/env isn’t normally present prior to Android 6.0 (M), where\n        # toybox was introduced (Android platform/manifest 9a2c01e8450b).\n        # Instead, set environment variables by using the shell’s internal\n        # “export” command.\n        #\n        # adbd prior to Android 7.0 (N), and the adb client prior to SDK\n        # platform-tools version 24, don’t know how to communicate a shell\n        # command’s exit status. This was added in Android platform/system/core\n        # 606835ae5c4b). With older adb servers and clients, adb will “exit 0”\n        # indicating success even if the command failed on the device. This\n        # makes subprocess.check_call() semantics difficult to implement\n        # directly. As a workaround, have the device send the command’s exit\n        # status over stdout and pick it back up in this function.\n        #\n        # Both workarounds are implemented by giving the device a simple script,\n        # which adbd will run as an “sh -c” argument.\n        adb_command = ['adb', '-s', android_device, 'shell']\n        script_commands = []\n        for k, v in env.items():\n            script_commands.append('export %s=%s' %\n                                   (shlex.quote(k), shlex.quote(v)))\n        script_commands.extend([\n            ' '.join(shlex.quote(x) for x in command_args), 'status=${?}',\n            'echo \"status=${status}\"', 'exit ${status}'\n        ])\n        adb_command.append('; '.join(script_commands))\n        child = subprocess.Popen(adb_command,\n                                 shell=IS_WINDOWS_HOST,\n                                 stdin=open(os.devnull),\n                                 stdout=subprocess.PIPE,\n                                 text=True)\n\n        FINAL_LINE_RE = re.compile(r'status=(\\d+)$')\n        final_line = None\n        while True:\n            # Use readline so that the test output appears “live” when running.\n            data = child.stdout.readline()\n            if data == '':\n                break\n            if final_line is not None:\n                # It wasn’t really the final line.\n                print(final_line, end='')\n                final_line = None\n            if FINAL_LINE_RE.match(data.rstrip()):\n                final_line = data\n            else:\n                print(data, end='')\n\n        if final_line is None:\n            # Maybe there was some stderr output after the end of stdout. Old\n            # versions of adb, prior to when the exit status could be\n            # communicated, smush the two together.\n            raise subprocess.CalledProcessError(-1, adb_command)\n        status = int(FINAL_LINE_RE.match(final_line.rstrip()).group(1))\n        if status != 0:\n            raise subprocess.CalledProcessError(status, adb_command)\n\n        child.wait()\n        if child.returncode != 0:\n            raise subprocess.CalledProcessError(subprocess.returncode,\n                                                adb_command)\n\n    # /system/bin/mktemp isn’t normally present prior to Android 6.0 (M), where\n    # toybox was introduced (Android platform/manifest 9a2c01e8450b). Fake it\n    # with a host-generated name. This won’t retry if the name is in use, but\n    # with 122 bits of randomness, it should be OK. This uses “mkdir” instead of\n    # “mkdir -p”because the latter will not indicate failure if the directory\n    # already exists.\n    device_temp_dir = '/data/local/tmp/%s.%s' % (test, uuid.uuid4().hex)\n    _adb_shell(['mkdir', device_temp_dir])\n\n    try:\n        # Specify test dependencies that must be pushed to the device. This\n        # could be determined automatically in a GN build, following the example\n        # used for Fuchsia. Since nothing like that exists for GYP, hard-code it\n        # for supported tests.\n        test_build_artifacts = [test, 'crashpad_handler']\n        test_data = ['test/test_paths_test_data_root.txt']\n\n        if test == 'crashpad_test_test':\n            test_build_artifacts.append(\n                'crashpad_test_test_multiprocess_exec_test_child')\n        elif test == 'crashpad_util_test':\n            test_data.append('util/net/testdata/')\n\n        # Establish the directory structure on the device.\n        device_out_dir = posixpath.join(device_temp_dir, 'out')\n        device_mkdirs = [device_out_dir]\n        for source_path in test_data:\n            # A trailing slash could reasonably mean to copy an entire\n            # directory, but will interfere with what’s needed from the path\n            # split. All parent directories of any source_path need to be be\n            # represented in device_mkdirs, but it’s important that no\n            # source_path itself wind up in device_mkdirs, even if source_path\n            # names a directory, because that would cause the “adb push” of the\n            # directory below to behave incorrectly.\n            if source_path.endswith(posixpath.sep):\n                source_path = source_path[:-1]\n\n            device_source_path = posixpath.join(device_temp_dir, source_path)\n            device_mkdir = posixpath.split(device_source_path)[0]\n            if device_mkdir not in device_mkdirs:\n                device_mkdirs.append(device_mkdir)\n        adb_mkdir_command = ['mkdir', '-p']\n        adb_mkdir_command.extend(device_mkdirs)\n        _adb_shell(adb_mkdir_command)\n\n        # Push the test binary and any other build output to the device.\n        local_test_build_artifacts = []\n        for artifact in test_build_artifacts:\n            local_test_build_artifacts.append(os.path.join(\n                binary_dir, artifact))\n        _adb_push(local_test_build_artifacts, device_out_dir)\n\n        # Push test data to the device.\n        for source_path in test_data:\n            _adb_push([os.path.join(CRASHPAD_DIR, source_path)],\n                      posixpath.join(device_temp_dir, source_path))\n\n        # Run the test on the device. Pass the test data root in the\n        # environment.\n        #\n        # Because the test will not run with its standard output attached to a\n        # pseudo-terminal device, Google Test will not normally enable colored\n        # output, so mimic Google Test’s own logic for deciding whether to\n        # enable color by checking this script’s own standard output connection.\n        # The list of TERM values comes from Google Test’s\n        # googletest/src/gtest.cc testing::internal::ShouldUseColor().\n        env = {'CRASHPAD_TEST_DATA_ROOT': device_temp_dir}\n        gtest_color = os.environ.get('GTEST_COLOR')\n        if gtest_color in ('auto', None):\n            if (sys.stdout.isatty() and\n                (os.environ.get('TERM')\n                 in ('xterm', 'xterm-color', 'xterm-256color', 'screen',\n                     'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode',\n                     'rxvt-unicode-256color', 'linux', 'cygwin') or\n                 (IS_WINDOWS_HOST and _EnableVTProcessingOnWindowsConsole()))):\n                gtest_color = 'yes'\n            else:\n                gtest_color = 'no'\n        env['GTEST_COLOR'] = gtest_color\n        _adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line,\n                   env)\n    finally:\n        _adb_shell(['rm', '-rf', device_temp_dir])\n\n\ndef _RunOnIOSTarget(binary_dir,\n                    test,\n                    target_platform,\n                    is_xcuitest=False,\n                    gtest_filter=None):\n    \"\"\"Runs the given iOS |test| app on a simulator with the default OS version.\"\"\"\n\n    target_platform = target_platform or 'iphoneos'\n    if target_platform == 'iphoneos':\n        dyld_insert_libraries = (\n            '__PLATFORMS__/iPhoneSimulator.platform/Developer/usr/lib/'\n            'libXCTestBundleInject.dylib')\n        xcodebuild_platform = 'iOS Simulator'\n        xcodebuild_device_name = 'iPhone 17'\n    elif target_platform == 'tvos':\n        dyld_insert_libraries = (\n            '__PLATFORMS__/AppleTVSimulator.platform/Developer/usr/lib/'\n            'libXCTestBundleInject.dylib')\n        xcodebuild_platform = 'tvOS Simulator'\n        xcodebuild_device_name = 'Apple TV 4K (3rd generation)'\n    else:\n        raise ValueError(f'Unexpected target_platform: {target_platform}')\n\n    # E.g. __TESTROOT__/Debug-iphonesimulator.\n    dyld_framework_path = '__TESTROOT__/%s' % os.path.basename(binary_dir)\n\n    def xctest(binary_dir, test, gtest_filter=None):\n        \"\"\"Returns a dict containing the xctestrun data needed to run an\n        XCTest-based test app.\"\"\"\n        test_path = os.path.join(CRASHPAD_DIR, binary_dir)\n        module_data = {\n            'TestBundlePath': os.path.join(test_path, test + '_module.xctest'),\n            'TestHostPath': os.path.join(test_path, test + '.app'),\n            'TestingEnvironmentVariables': {\n                'DYLD_FRAMEWORK_PATH': dyld_framework_path + ':',\n                'DYLD_INSERT_LIBRARIES': dyld_insert_libraries,\n                'DYLD_LIBRARY_PATH': dyld_framework_path,\n                'IDEiPhoneInternalTestBundleName': test + '.app',\n                'XCInjectBundleInto': '__TESTHOST__/' + test,\n            }\n        }\n        if gtest_filter:\n            module_data['CommandLineArguments'] = [\n                '--gtest_filter=' + gtest_filter\n            ]\n        return {test: module_data}\n\n    def xcuitest(binary_dir, test):\n        \"\"\"Returns a dict containing the xctestrun data needed to run an\n        XCUITest-based test app.\"\"\"\n\n        test_path = os.path.join(CRASHPAD_DIR, binary_dir)\n        runner_path = os.path.join(test_path, test + '_module-Runner.app')\n        bundle_path = os.path.join(runner_path, 'PlugIns',\n                                   test + '_module.xctest')\n        target_app_path = os.path.join(test_path, test + '.app')\n        module_data = {\n            'IsUITestBundle': True,\n            'SystemAttachmentLifetime': 'deleteOnSuccess',\n            'IsXCTRunnerHostedTestBundle': True,\n            'TestBundlePath': bundle_path,\n            'TestHostPath': runner_path,\n            'UITargetAppPath': target_app_path,\n            'DependentProductPaths': [\n                bundle_path, runner_path, target_app_path\n            ],\n            'TestingEnvironmentVariables': {\n                'DYLD_FRAMEWORK_PATH': dyld_framework_path + ':',\n                'DYLD_LIBRARY_PATH': dyld_framework_path,\n                'XCInjectBundleInto': '__TESTHOST__/' + test + '_module-Runner',\n            },\n        }\n        return {test: module_data}\n\n    with tempfile.NamedTemporaryFile() as f:\n        import plistlib\n\n        xctestrun_path = f.name + \".xctestrun\"\n        print(xctestrun_path)\n        command = [\n            'xcodebuild',\n            'test-without-building',\n            '-xctestrun',\n            xctestrun_path,\n            '-destination',\n            f'platform={xcodebuild_platform},name={xcodebuild_device_name}',\n        ]\n        with open(xctestrun_path, 'wb') as fp:\n            if is_xcuitest:\n                plistlib.dump(xcuitest(binary_dir, test), fp)\n                if gtest_filter:\n                    command.append('-only-testing:' + test + '/' + gtest_filter)\n            else:\n                plistlib.dump(xctest(binary_dir, test, gtest_filter), fp)\n        subprocess.check_call(command)\n\n\n# This script is primarily used from the waterfall so that the list of tests\n# that are run is maintained in-tree, rather than in a separate infrastructure\n# location in the recipe.\ndef main(args):\n    parser = argparse.ArgumentParser(description='Run Crashpad unittests.')\n    parser.add_argument('binary_dir', help='Root of build dir')\n    parser.add_argument('test', nargs='*', help='Specific test(s) to run.')\n    parser.add_argument(\n        '--gtest_filter',\n        help='Google Test filter applied to Google Test binary runs.')\n    args = parser.parse_args()\n\n    # Tell 64-bit Windows tests where to find 32-bit test executables, for\n    # cross-bitted testing. This relies on the fact that the GYP build by\n    # default uses {Debug,Release} for the 32-bit build and {Debug,Release}_x64\n    # for the 64-bit build. This is not a universally valid assumption, and if\n    # it’s not met, 64-bit tests that require 32-bit build output will disable\n    # themselves dynamically.\n    if (sys.platform == 'win32' and args.binary_dir.endswith('_x64') and\n            'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ):\n        binary_dir_32 = args.binary_dir[:-4]\n        if os.path.isdir(binary_dir_32):\n            os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32\n\n    target_os = _GetGNArgument('target_os', args.binary_dir)\n    is_android = target_os == 'android'\n    is_ios = target_os == 'ios'\n    # |target_platform| is only set for iOS-based platforms.\n    target_platform = _GetGNArgument('target_platform', args.binary_dir)\n\n    tests = [\n        'crashpad_client_test',\n        'crashpad_handler_test',\n        'crashpad_minidump_test',\n        'crashpad_snapshot_test',\n        'crashpad_test_test',\n        'crashpad_util_test',\n    ]\n\n    if is_android:\n        android_device = os.environ.get('ANDROID_DEVICE')\n        if not android_device:\n            adb_devices = subprocess.check_output(['adb', 'devices'],\n                                                  shell=IS_WINDOWS_HOST,\n                                                  text=True)\n            devices = []\n            for line in adb_devices.splitlines():\n                line = line\n                if (line == 'List of devices attached' or\n                        re.match(r'^\\* daemon .+ \\*$', line) or line == ''):\n                    continue\n                (device, ignore) = line.split('\\t')\n                devices.append(device)\n            if len(devices) != 1:\n                print(\"Please set ANDROID_DEVICE to your device's id\",\n                      file=sys.stderr)\n                return 2\n            android_device = devices[0]\n            print('Using autodetected Android device:', android_device)\n    elif is_ios:\n        tests.append('ios_crash_xcuitests')\n    elif IS_WINDOWS_HOST:\n        tests.append('snapshot/win/end_to_end_test.py')\n\n    if args.test:\n        for t in args.test:\n            if t not in tests:\n                print('Unrecognized test:', t, file=sys.stderr)\n                return 3\n        tests = args.test\n\n    for test in tests:\n        print('-' * 80)\n        print(test)\n        print('-' * 80)\n        if test.endswith('.py'):\n            subprocess.check_call([\n                sys.executable,\n                os.path.join(CRASHPAD_DIR, test), args.binary_dir\n            ])\n        else:\n            extra_command_line = []\n            if args.gtest_filter:\n                extra_command_line.append('--gtest_filter=' + args.gtest_filter)\n            if is_android:\n                _RunOnAndroidTarget(args.binary_dir, test, android_device,\n                                    extra_command_line)\n            elif is_ios:\n                _RunOnIOSTarget(args.binary_dir,\n                                test,\n                                target_platform,\n                                is_xcuitest=test.startswith('ios'),\n                                gtest_filter=args.gtest_filter)\n            else:\n                subprocess.check_call([os.path.join(args.binary_dir, test)] +\n                                      extra_command_line)\n\n    return 0\n\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv[1:]))\n"
  },
  {
    "path": "build/test.gni",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"crashpad_buildconfig.gni\")\n\nif (crashpad_is_in_chromium) {\n  import(\"//testing/test.gni\")\n} else {\n  template(\"test\") {\n    if (crashpad_is_ios) {\n      import(\"//third_party/mini_chromium/mini_chromium/build/ios/rules.gni\")\n\n      _launch_image_bundle_target = target_name + \"_launch_image\"\n      bundle_data(_launch_image_bundle_target) {\n        forward_variables_from(invoker, [ \"testonly\" ])\n        sources = [ \"//build/ios/Default.png\" ]\n        outputs = [ \"{{bundle_contents_dir}}/{{source_file_part}}\" ]\n      }\n\n      ios_xctest_test(target_name) {\n        testonly = true\n        xctest_module_target = \"//test/ios:google_test_runner\"\n        info_plist = \"//build/ios/Unittest-Info.plist\"\n        extra_substitutions = [ \"GTEST_BUNDLE_ID_SUFFIX=$target_name\" ]\n        forward_variables_from(invoker, \"*\")\n        if (!defined(deps)) {\n          deps = []\n        }\n        deps += [ \":$_launch_image_bundle_target\" ]\n      }\n    } else {\n      executable(target_name) {\n        testonly = true\n        forward_variables_from(invoker, \"*\")\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "client/BUILD.gn",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../build/crashpad_buildconfig.gni\")\n\ncrashpad_static_library(\"client\") {\n  sources = [\n    \"crashpad_client.h\",\n    \"prune_crash_reports.cc\",\n    \"prune_crash_reports.h\",\n    \"simulate_crash.h\",\n  ]\n\n  if (crashpad_is_mac) {\n    sources += [\n      \"crashpad_client_mac.cc\",\n      \"simulate_crash_mac.cc\",\n      \"simulate_crash_mac.h\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    sources += [\n      \"crash_handler_base_ios.cc\",\n      \"crash_handler_base_ios.h\",\n      \"crashpad_client_ios.cc\",\n      \"ios_handler/exception_processor.h\",\n      \"ios_handler/exception_processor.mm\",\n      \"ios_handler/in_process_handler.cc\",\n      \"ios_handler/in_process_handler.h\",\n      \"ios_handler/in_process_intermediate_dump_handler.cc\",\n      \"ios_handler/in_process_intermediate_dump_handler.h\",\n      \"ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc\",\n      \"ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h\",\n      \"simulate_crash_ios.h\",\n      \"upload_behavior_ios.h\",\n    ]\n\n    if (!crashpad_is_tvos) {\n      sources += [\n        \"crash_handler_ios.cc\",\n        \"crash_handler_ios.h\",\n      ]\n    } else {\n      sources += [\n        \"crash_handler_tvos.cc\",\n        \"crash_handler_tvos.h\",\n      ]\n    }\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    sources += [\n      \"crashpad_client_linux.cc\",\n      \"simulate_crash_linux.h\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {\n    sources += [\n      \"client_argv_handling.cc\",\n      \"client_argv_handling.h\",\n    ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [\n      \"crashpad_client_win.cc\",\n      \"simulate_crash_win.h\",\n    ]\n  }\n\n  if (crashpad_is_fuchsia) {\n    sources += [ \"crashpad_client_fuchsia.cc\" ]\n  }\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  public_deps = [\n    \":common\",\n    \"$mini_chromium_source_parent:base\",\n    \"../util\",\n  ]\n\n  deps = [ \":common\" ]\n\n  if (crashpad_is_win) {\n    libs = [ \"rpcrt4.lib\" ]\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n  }\n\n  if (crashpad_is_apple) {\n    deps += [ \"../build:apple_enable_arc\" ]\n  }\n\n  if (crashpad_is_ios) {\n    deps += [\n      \"../handler:common\",\n      \"../minidump\",\n      \"../snapshot\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    deps += [ \"../third_party/lss\" ]\n  }\n\n  if (crashpad_is_fuchsia) {\n    deps += [ \"../third_party/fuchsia\" ]\n    if (crashpad_is_in_fuchsia) {\n      deps += [ \"//sdk/lib/fdio\" ]\n    }\n  }\n}\n\nstatic_library(\"common\") {\n  sources = [\n    \"annotation.cc\",\n    \"annotation.h\",\n    \"annotation_list.cc\",\n    \"annotation_list.h\",\n    \"crash_report_database.cc\",\n    \"crash_report_database.h\",\n    \"crashpad_info.cc\",\n    \"crashpad_info.h\",\n    \"length_delimited_ring_buffer.h\",\n    \"ring_buffer_annotation.h\",\n    \"settings.cc\",\n    \"settings.h\",\n    \"simple_address_range_bag.h\",\n    \"simple_string_dictionary.h\",\n  ]\n\n  if (crashpad_is_apple) {\n    sources += [ \"crash_report_database_mac.mm\" ]\n  }\n  if (crashpad_is_win) {\n    sources += [ \"crash_report_database_win.cc\" ]\n  }\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {\n    sources += [\n      \"crash_report_database_generic.cc\",\n      \"crashpad_info_note.S\",\n    ]\n  }\n\n  public_configs = [ \"..:crashpad_config\" ]\n  public_deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../util\",\n  ]\n  deps = [ \"../util\" ]\n  configs += [ \"../build:flock_always_supported_defines\" ]\n\n  if (crashpad_is_apple) {\n    deps += [ \"../build:apple_enable_arc\" ]\n  }\n}\n\ncrashpad_executable(\"ring_buffer_annotation_load_test\") {\n  testonly = true\n  sources = [ \"ring_buffer_annotation_load_test_main.cc\" ]\n  deps = [\n    \":client\",\n    \"$mini_chromium_source_parent:base\",\n    \"../tools:tool_support\",\n  ]\n}\n\nsource_set(\"client_test\") {\n  testonly = true\n\n  sources = [\n    \"annotation_list_test.cc\",\n    \"annotation_test.cc\",\n    \"crash_report_database_test.cc\",\n    \"crashpad_info_test.cc\",\n    \"length_delimited_ring_buffer_test.cc\",\n    \"prune_crash_reports_test.cc\",\n    \"ring_buffer_annotation_test.cc\",\n    \"settings_test.cc\",\n    \"simple_address_range_bag_test.cc\",\n    \"simple_string_dictionary_test.cc\",\n  ]\n\n  if (crashpad_is_mac) {\n    sources += [ \"simulate_crash_mac_test.cc\" ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [ \"crashpad_client_win_test.cc\" ]\n  }\n\n  if (crashpad_is_ios) {\n    sources += [\n      \"crashpad_client_ios_test.mm\",\n      \"ios_handler/exception_processor_test.mm\",\n      \"ios_handler/in_process_handler_test.cc\",\n      \"ios_handler/in_process_intermediate_dump_handler_test.cc\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    sources += [ \"crashpad_client_linux_test.cc\" ]\n  }\n\n  deps = [\n    \":client\",\n    \"$mini_chromium_source_parent:base\",\n    \"../compat\",\n    \"../snapshot\",\n    \"../test\",\n    \"../third_party/googletest\",\n    \"../third_party/googletest:googlemock\",\n    \"../util\",\n  ]\n\n  if (!crashpad_is_ios && !crashpad_is_fuchsia) {\n    data_deps = [ \"../handler:crashpad_handler\" ]\n  }\n\n  if (crashpad_is_apple) {\n    deps += [ \"../build:apple_enable_arc\" ]\n  }\n\n  if (crashpad_is_win) {\n    data_deps += [\n      \"../handler:crashpad_handler_console\",\n      \"../handler/win/wer:crashpad_wer_handler\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    deps += [ \"../minidump\" ]\n  }\n}\n\nif (crashpad_is_linux || crashpad_is_android) {\n  source_set(\"pthread_create\") {\n    sources = [ \"pthread_create_linux.cc\" ]\n\n    deps = [ \":client\" ]\n  }\n}\n"
  },
  {
    "path": "client/annotation.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/annotation.h\"\n\n#include <type_traits>\n\n#include \"base/check_op.h\"\n#include \"client/annotation_list.h\"\n\nnamespace crashpad {\n\nstatic_assert(std::is_standard_layout<Annotation>::value,\n              \"Annotation must be POD\");\n\n// static\nconstexpr size_t Annotation::kNameMaxLength;\nconstexpr size_t Annotation::kValueMaxSize;\n\nvoid Annotation::SetSize(ValueSizeType size) {\n  DCHECK_LT(size, kValueMaxSize);\n  size_ = size;\n  // Use Register() instead of Get() in case the calling module has not\n  // explicitly initialized the annotation list, to avoid crashing.\n  AnnotationList::Register()->Add(this);\n}\n\nvoid Annotation::Clear() {\n  size_ = 0;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/annotation.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_ANNOTATION_H_\n#define CRASHPAD_CLIENT_ANNOTATION_H_\n\n#include <stdint.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <atomic>\n#include <optional>\n#include <ostream>\n#include <string_view>\n\n#include \"base/check.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/synchronization/scoped_spin_guard.h\"\n\nnamespace crashpad {\n#if BUILDFLAG(IS_IOS)\nnamespace internal {\nclass InProcessIntermediateDumpHandler;\n}  // namespace internal\n#endif\nclass AnnotationList;\n\n//! \\brief Base class for an annotation, which records a name-value pair of\n//!     arbitrary data when set.\n//!\n//! After an annotation is declared, its `value_ptr_` will not be captured in a\n//! crash report until a call to \\a SetSize() specifies how much data from the\n//! value should be recorded.\n//!\n//! Annotations should be declared with static storage duration.\n//!\n//! An example declaration and usage:\n//!\n//! \\code\n//!   // foo.cc:\n//!\n//!   namespace {\n//!   char g_buffer[1024];\n//!   crashpad::Annotation g_buffer_annotation(\n//!       crashpad::Annotation::Type::kString, \"buffer_head\", g_buffer);\n//!   }  // namespace\n//!\n//!   void OnBufferProduced(size_t n) {\n//!     // Capture the head of the buffer, in case we crash when parsing it.\n//!     g_buffer_annotation.SetSize(std::min(64, n));\n//!\n//!     // Start parsing the header.\n//!     Frobinate(g_buffer, n);\n//!   }\n//! \\endcode\n//!\n//! Annotation objects are not inherently thread-safe. To manipulate them\n//! from multiple threads, external synchronization must be used.\n//!\n//! Annotation objects should never be destroyed. Once they are Set(), they\n//! are permanently referenced by a global object.\nclass Annotation {\n public:\n  //! \\brief The maximum length of an annotation’s name, in bytes.\n  //!    Matches the behavior of Breakpad's SimpleStringDictionary.\n  static constexpr size_t kNameMaxLength = 256;\n\n  //! \\brief The maximum size of an annotation’s value, in bytes.\n  static constexpr size_t kValueMaxSize = 5 * 4096;\n\n  //! \\brief The type used for \\a SetSize().\n  using ValueSizeType = uint32_t;\n\n  //! \\brief The type of data stored in the annotation.\n  enum class Type : uint16_t {\n    //! \\brief An invalid annotation. Reserved for internal use.\n    kInvalid = 0,\n\n    //! \\brief A `NUL`-terminated C-string.\n    kString = 1,\n\n    //! \\brief Clients may declare their own custom types by using values\n    //!     greater than this.\n    kUserDefinedStart = 0x8000,\n  };\n\n  //! \\brief Mode used to guard concurrent reads from writes.\n  enum class ConcurrentAccessGuardMode : bool {\n    //! \\!brief Annotation does not guard reads from concurrent\n    //!     writes. Annotation values can be corrupted if the process crashes\n    //!     mid-write and the handler tries to read from the Annotation while\n    //!     being written to.\n    kUnguarded = false,\n\n    //! \\!brief Annotation guards reads from concurrent writes using\n    //!     ScopedSpinGuard. Clients must use TryCreateScopedSpinGuard()\n    //!     before reading or writing the data in this Annotation.\n    kScopedSpinGuard = true,\n  };\n\n  //! \\brief Creates a user-defined Annotation::Type.\n  //!\n  //! This exists to remove the casting overhead of `enum class`.\n  //!\n  //! \\param[in] value A value used to create a user-defined type.\n  //!\n  //! \\returns The value added to Type::kUserDefinedStart and casted.\n  constexpr static Type UserDefinedType(uint16_t value) {\n    using UnderlyingType = std::underlying_type<Type>::type;\n    // MSVS 2015 doesn't have full C++14 support and complains about local\n    // variables defined in a constexpr function, which is valid. Avoid them\n    // and the also-problematic DCHECK until all the infrastructure is updated:\n    // https://crbug.com/crashpad/201.\n#if !BUILDFLAG(IS_WIN) || (defined(_MSC_VER) && _MSC_VER >= 1910)\n    const UnderlyingType start =\n        static_cast<UnderlyingType>(Type::kUserDefinedStart);\n    const UnderlyingType user_type = start + value;\n    DCHECK(user_type > start) << \"User-defined Type is 0 or overflows\";\n    return static_cast<Type>(user_type);\n#else\n    return static_cast<Type>(\n        static_cast<UnderlyingType>(Type::kUserDefinedStart) + value);\n#endif\n  }\n\n  //! \\brief Constructs a new annotation.\n  //!\n  //! Upon construction, the annotation will not be included in any crash\n  //! reports until \\sa SetSize() is called with a value greater than `0`.\n  //!\n  //! \\param[in] type The data type of the value of the annotation.\n  //! \\param[in] name A `NUL`-terminated C-string name for the annotation. Names\n  //!     do not have to be unique, though not all crash processors may handle\n  //!     Annotations with the same name. Names should be constexpr data with\n  //!     static storage duration.\n  //! \\param[in] value_ptr A pointer to the value for the annotation. The\n  //!     pointer may not be changed once associated with an annotation, but\n  //!     the data may be mutated.\n  constexpr Annotation(Type type, const char name[], void* value_ptr)\n      : Annotation(type,\n                   name,\n                   value_ptr,\n                   ConcurrentAccessGuardMode::kUnguarded) {}\n\n  Annotation(const Annotation&) = delete;\n  Annotation& operator=(const Annotation&) = delete;\n\n  //! \\brief Specifies the number of bytes in \\a value_ptr_ to include when\n  //!     generating a crash report.\n  //!\n  //! A size of `0` indicates that no value should be recorded and is the\n  //! equivalent of calling \\sa Clear().\n  //!\n  //! This method does not mutate the data referenced by the annotation, it\n  //! merely updates the annotation system's bookkeeping.\n  //!\n  //! Subclasses of this base class that provide additional Set methods to\n  //! mutate the value of the annotation must call always call this method.\n  //!\n  //! \\param[in] size The number of bytes.\n  void SetSize(ValueSizeType size);\n\n  //! \\brief Marks the annotation as cleared, indicating the \\a value_ptr_\n  //!     should not be included in a crash report.\n  //!\n  //! This method does not mutate the data referenced by the annotation, it\n  //! merely updates the annotation system's bookkeeping.\n  void Clear();\n\n  //! \\brief Tests whether the annotation has been set.\n  bool is_set() const { return size_ > 0; }\n\n  Type type() const { return type_; }\n  ValueSizeType size() const { return size_; }\n  const char* name() const { return name_; }\n  const void* value() const { return value_ptr_; }\n\n  ConcurrentAccessGuardMode concurrent_access_guard_mode() const {\n    return concurrent_access_guard_mode_;\n  }\n\n  //! \\brief If this Annotation guards concurrent access using ScopedSpinGuard,\n  //!     tries to obtain the spin guard and returns the result.\n  //!\n  //! \\param[in] timeout_ns The timeout in nanoseconds after which to give up\n  //!     trying to obtain the spin guard.\n  //! \\return std::nullopt if the spin guard could not be obtained within\n  //!     timeout_ns, or the obtained spin guard otherwise.\n  std::optional<ScopedSpinGuard> TryCreateScopedSpinGuard(uint64_t timeout_ns) {\n    // This can't use DCHECK_EQ() because ostream doesn't support\n    // operator<<(bool).\n    DCHECK(concurrent_access_guard_mode_ ==\n           ConcurrentAccessGuardMode::kScopedSpinGuard);\n    if (concurrent_access_guard_mode_ ==\n        ConcurrentAccessGuardMode::kUnguarded) {\n      return std::nullopt;\n    }\n    return ScopedSpinGuard::TryCreateScopedSpinGuard(timeout_ns,\n                                                     spin_guard_state_);\n  }\n\n protected:\n  //! \\brief Constructs a new annotation.\n  //!\n  //! Upon construction, the annotation will not be included in any crash\n  //! reports until \\sa SetSize() is called with a value greater than `0`.\n  //!\n  //! \\param[in] type The data type of the value of the annotation.\n  //! \\param[in] name A `NUL`-terminated C-string name for the annotation. Names\n  //!     do not have to be unique, though not all crash processors may handle\n  //!     Annotations with the same name. Names should be constexpr data with\n  //!     static storage duration.\n  //! \\param[in] value_ptr A pointer to the value for the annotation. The\n  //!     pointer may not be changed once associated with an annotation, but\n  //!     the data may be mutated.\n  //! \\param[in] concurrent_access_guard_mode Mode used to guard concurrent\n  //!     reads from writes.\n  constexpr Annotation(Type type,\n                       const char name[],\n                       void* value_ptr,\n                       ConcurrentAccessGuardMode concurrent_access_guard_mode)\n      : link_node_(nullptr),\n        name_(name),\n        value_ptr_(value_ptr),\n        size_(0),\n        type_(type),\n        concurrent_access_guard_mode_(concurrent_access_guard_mode),\n        spin_guard_state_() {}\n\n  friend class AnnotationList;\n#if BUILDFLAG(IS_IOS)\n  friend class internal::InProcessIntermediateDumpHandler;\n#endif\n\n  std::atomic<Annotation*>& link_node() { return link_node_; }\n\n  Annotation* GetLinkNode(std::memory_order order = std::memory_order_seq_cst) {\n    return link_node_.load(order);\n  }\n  const Annotation* GetLinkNode(\n      std::memory_order order = std::memory_order_seq_cst) const {\n    return link_node_.load(order);\n  }\n\n private:\n  //! \\brief Linked list next-node pointer. Accessed only by \\sa AnnotationList.\n  //!\n  //! This will be null until the first call to \\sa SetSize(), after which the\n  //! presence of the pointer will prevent the node from being added to the\n  //! list again.\n  std::atomic<Annotation*> link_node_;\n\n  const char* const name_;\n  void* const value_ptr_;\n  ValueSizeType size_;\n  const Type type_;\n\n  //! \\brief Mode used to guard concurrent reads from writes.\n  const ConcurrentAccessGuardMode concurrent_access_guard_mode_;\n\n  SpinGuardState spin_guard_state_;\n};\n\n//! \\brief An \\sa Annotation that stores a `NUL`-terminated C-string value.\n//!\n//! The storage for the value is allocated by the annotation and the template\n//! parameter \\a MaxSize controls the maxmium length for the value.\n//!\n//! It is expected that the string value be valid UTF-8, although this is not\n//! validated.\ntemplate <Annotation::ValueSizeType MaxSize>\nclass StringAnnotation : public Annotation {\n public:\n  //! \\brief A constructor tag that enables braced initialization in C arrays.\n  //!\n  //! \\sa StringAnnotation()\n  enum class Tag { kArray };\n\n  //! \\brief Constructs a new StringAnnotation with the given \\a name.\n  //!\n  //! \\param[in] name The Annotation name.\n  constexpr explicit StringAnnotation(const char name[])\n      : Annotation(Type::kString, name, value_), value_() {}\n\n  StringAnnotation(const StringAnnotation&) = delete;\n  StringAnnotation& operator=(const StringAnnotation&) = delete;\n\n  //! \\brief Constructs a new StringAnnotation with the given \\a name.\n  //!\n  //! This constructor takes the ArrayInitializerTag for use when\n  //! initializing a C array of annotations. The main constructor is\n  //! explicit and cannot be brace-initialized. As an example:\n  //!\n  //! \\code\n  //!   static crashpad::StringAnnotation<32> annotations[] = {\n  //!     {\"name-1\", crashpad::StringAnnotation<32>::Tag::kArray},\n  //!     {\"name-2\", crashpad::StringAnnotation<32>::Tag::kArray},\n  //!     {\"name-3\", crashpad::StringAnnotation<32>::Tag::kArray},\n  //!   };\n  //! \\endcode\n  //!\n  //! \\param[in] name The Annotation name.\n  //! \\param[in] tag A constructor tag.\n  constexpr StringAnnotation(const char name[], Tag tag)\n      : StringAnnotation(name) {}\n\n  //! \\brief Sets the Annotation's string value.\n  //!\n  //! \\param[in] value The `NUL`-terminated C-string value.\n  void Set(const char* value) {\n    strncpy(value_, value, MaxSize);\n    SetSize(\n        std::min(MaxSize, base::saturated_cast<ValueSizeType>(strlen(value))));\n  }\n\n  //! \\brief Sets the Annotation's string value.\n  //!\n  //! \\param[in] string The string value.\n  void Set(std::string_view string) {\n    Annotation::ValueSizeType size =\n        std::min(MaxSize, base::saturated_cast<ValueSizeType>(string.size()));\n    string = string.substr(0, size);\n    std::copy(string.begin(), string.end(), value_);\n    // Check for no embedded `NUL` characters.\n    DCHECK(string.find('\\0', /*pos=*/0) == std::string_view::npos)\n        << \"embedded NUL\";\n    SetSize(size);\n  }\n\n  const std::string_view value() const {\n    return std::string_view(value_, size());\n  }\n\n private:\n  // This value is not `NUL`-terminated, since the size is stored by the base\n  // annotation.\n  char value_[MaxSize];\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_ANNOTATION_H_\n"
  },
  {
    "path": "client/annotation_list.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/annotation_list.h\"\n\n#include \"base/check_op.h\"\n#include \"client/crashpad_info.h\"\n\nnamespace crashpad {\n\ntemplate <typename T>\nT* AnnotationList::IteratorBase<T>::operator*() const {\n  CHECK_NE(curr_, tail_);\n  return curr_;\n}\n\ntemplate <typename T>\nT* AnnotationList::IteratorBase<T>::operator->() const {\n  CHECK_NE(curr_, tail_);\n  return curr_;\n}\n\ntemplate <typename T>\nAnnotationList::IteratorBase<T>& AnnotationList::IteratorBase<T>::operator++() {\n  CHECK_NE(curr_, tail_);\n  curr_ = curr_->GetLinkNode();\n  return *this;\n}\n\ntemplate <typename T>\nAnnotationList::IteratorBase<T> AnnotationList::IteratorBase<T>::operator++(\n    int) {\n  T* const old_curr = curr_;\n  ++(*this);\n  return IteratorBase(old_curr, tail_);\n}\n\ntemplate <typename T>\nbool AnnotationList::IteratorBase<T>::operator!=(\n    const IteratorBase& other) const {\n  return !(*this == other);\n}\n\ntemplate <typename T>\nAnnotationList::IteratorBase<T>::IteratorBase(T* head, const Annotation* tail)\n    : curr_(head), tail_(tail) {}\n\ntemplate class AnnotationList::IteratorBase<Annotation>;\ntemplate class AnnotationList::IteratorBase<const Annotation>;\n\nAnnotationList::AnnotationList()\n    : tail_pointer_(&tail_),\n      head_(Annotation::Type::kInvalid, nullptr, nullptr),\n      tail_(Annotation::Type::kInvalid, nullptr, nullptr) {\n  head_.link_node().store(&tail_);\n}\n\nAnnotationList::~AnnotationList() {}\n\n// static\nAnnotationList* AnnotationList::Get() {\n  return CrashpadInfo::GetCrashpadInfo()->annotations_list();\n}\n\n// static\nAnnotationList* AnnotationList::Register() {\n  AnnotationList* list = Get();\n  if (!list) {\n    list = new AnnotationList();\n    CrashpadInfo::GetCrashpadInfo()->set_annotations_list(list);\n  }\n  return list;\n}\n\nvoid AnnotationList::Add(Annotation* annotation) {\n  Annotation* null = nullptr;\n  Annotation* head_next = head_.link_node().load(std::memory_order_relaxed);\n  if (!annotation->link_node().compare_exchange_strong(null, head_next)) {\n    // If |annotation|'s link node is not null, then it has been added to the\n    // list already and no work needs to be done.\n    return;\n  }\n\n  // Check that the annotation's name is less than the maximum size. This is\n  // done here, since the Annotation constructor must be constexpr and this\n  // path is taken once per annotation.\n  DCHECK_LT(strlen(annotation->name_), Annotation::kNameMaxLength);\n\n  // Update the head link to point to the new |annotation|.\n  while (!head_.link_node().compare_exchange_weak(head_next, annotation)) {\n    // Another thread has updated the head-next pointer, so try again with the\n    // re-loaded |head_next|.\n    annotation->link_node().store(head_next, std::memory_order_relaxed);\n  }\n}\n\nAnnotationList::Iterator AnnotationList::begin() {\n  return Iterator(head_.GetLinkNode(), tail_pointer_);\n}\n\nAnnotationList::ConstIterator AnnotationList::cbegin() const {\n  return ConstIterator(head_.GetLinkNode(), tail_pointer_);\n}\n\nAnnotationList::Iterator AnnotationList::end() {\n  return Iterator(&tail_, tail_pointer_);\n}\n\nAnnotationList::ConstIterator AnnotationList::cend() const {\n  return ConstIterator(&tail_, tail_pointer_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/annotation_list.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_ANNOTATION_LIST_H_\n#define CRASHPAD_CLIENT_ANNOTATION_LIST_H_\n\n#include <iterator>\n\n#include \"build/build_config.h\"\n#include \"client/annotation.h\"\n\nnamespace crashpad {\n#if BUILDFLAG(IS_IOS)\nnamespace internal {\nclass InProcessIntermediateDumpHandler;\n}  // namespace internal\n#endif\n\n//! \\brief A list that contains all the currently set annotations.\n//!\n//! An instance of this class must be registered on the \\a CrashpadInfo\n//! structure in order to use the annotations system. Once a list object has\n//! been registered on the CrashpadInfo, a different instance should not\n//! be used instead.\nclass AnnotationList {\n public:\n  AnnotationList();\n\n  AnnotationList(const AnnotationList&) = delete;\n  AnnotationList& operator=(const AnnotationList&) = delete;\n\n  ~AnnotationList();\n\n  //! \\brief Returns the instance of the list that has been registered on the\n  //!     CrashapdInfo structure.\n  static AnnotationList* Get();\n\n  //! \\brief Returns the instance of the list, creating and registering\n  //!     it if one is not already set on the CrashapdInfo structure.\n  static AnnotationList* Register();\n\n  //! \\brief Adds \\a annotation to the global list. This method does not need\n  //!     to be called by clients directly. The Annotation object will do so\n  //!     automatically.\n  //!\n  //! Once an annotation is added to the list, it is not removed. This is\n  //! because the AnnotationList avoids the use of locks/mutexes, in case it is\n  //! being manipulated in a compromised context. Instead, an Annotation keeps\n  //! track of when it has been cleared, which excludes it from a crash report.\n  //! This design also avoids linear scans of the list when repeatedly setting\n  //! and/or clearing the value.\n  void Add(Annotation* annotation);\n\n  //! \\brief An InputIterator for the AnnotationList.\n  template <typename T>\n  class IteratorBase {\n   public:\n    using difference_type = signed int;\n    using value_type = T*;\n    using reference = T*;\n    using pointer = void;\n    using iterator_category = std::input_iterator_tag;\n\n    IteratorBase(const IteratorBase& other) = default;\n    IteratorBase(IteratorBase&& other) = default;\n\n    ~IteratorBase() = default;\n\n    IteratorBase& operator=(const IteratorBase& other) = default;\n    IteratorBase& operator=(IteratorBase&& other) = default;\n\n    T* operator*() const;\n    T* operator->() const;\n\n    IteratorBase& operator++();\n    IteratorBase operator++(int);\n\n    bool operator==(const IteratorBase& other) const {\n      return curr_ == other.curr_;\n    }\n\n    bool operator!=(const IteratorBase& other) const;\n\n   private:\n    friend class AnnotationList;\n    IteratorBase(T* head, const Annotation* tail);\n\n    T* curr_ = nullptr;\n    const Annotation* tail_ = nullptr;\n  };\n\n  using Iterator = IteratorBase<Annotation>;\n  using ConstIterator = IteratorBase<const Annotation>;\n\n  //! \\brief Returns an iterator to the first element of the annotation list.\n  Iterator begin();\n  ConstIterator begin() const { return cbegin(); }\n  ConstIterator cbegin() const;\n\n  //! \\brief Returns an iterator past the last element of the annotation list.\n  Iterator end();\n  ConstIterator end() const { return cend(); }\n  ConstIterator cend() const;\n\n protected:\n#if BUILDFLAG(IS_IOS)\n  friend class internal::InProcessIntermediateDumpHandler;\n#endif\n\n  //! \\brief Returns a pointer to the tail node.\n  const Annotation* tail_pointer() const { return tail_pointer_; }\n\n  //! \\brief Returns a pointer to the head element.\n  const Annotation* head() const { return &head_; }\n\n private:\n  // To make it easier for the handler to locate the dummy tail node, store the\n  // pointer. Placed first for packing.\n  const Annotation* const tail_pointer_;\n\n  // Dummy linked-list head and tail elements of \\a Annotation::Type::kInvalid.\n  Annotation head_;\n  Annotation tail_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_ANNOTATION_LIST_H_\n"
  },
  {
    "path": "client/annotation_list_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/annotation.h\"\n\n#include <algorithm>\n#include <iterator>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#include \"base/rand_util.h\"\n#include \"client/crashpad_info.h\"\n#include \"gtest/gtest.h\"\n#include \"util/misc/clock.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n#if (__cplusplus >= 202002L)\ntemplate <typename Iterator>\n  requires std::input_iterator<Iterator>\nvoid VerifyIsInputIterator(Iterator) {}\n#else\ntemplate <typename Iterator>\nstruct IsLegacyIteratorImpl {\n  static constexpr bool value =\n      std::is_copy_constructible_v<Iterator> &&\n      std::is_copy_assignable_v<Iterator> && std::is_destructible_v<Iterator> &&\n      std::is_swappable_v<Iterator> &&\n      // check that std::iterator_traits has the necessary types (check only one\n      // needed as std::iterator is required to define only if all are defined)\n      !std::is_same_v<typename std::iterator_traits<Iterator>::reference,\n                      void> &&\n      std::is_same_v<decltype(++std::declval<Iterator>()), Iterator&> &&\n      !std::is_same_v<decltype(*std::declval<Iterator>()), void>;\n};\n\ntemplate <typename Iterator>\nstruct IsLegacyInputIteratorImpl {\n  static constexpr bool value =\n      IsLegacyIteratorImpl<Iterator>::value &&\n      std::is_base_of_v<\n          std::input_iterator_tag,\n          typename std::iterator_traits<Iterator>::iterator_category> &&\n      std::is_convertible_v<decltype(std::declval<Iterator>() !=\n                                     std::declval<Iterator>()),\n                            bool> &&\n      std::is_convertible_v<decltype(std::declval<Iterator>() ==\n                                     std::declval<Iterator>()),\n                            bool> &&\n      std::is_same_v<decltype(*std::declval<Iterator>()),\n                     typename std::iterator_traits<Iterator>::reference> &&\n      std::is_same_v<decltype(++std::declval<Iterator>()), Iterator&> &&\n      std::is_same_v<decltype(std::declval<Iterator>()++), Iterator> &&\n      std::is_same_v<decltype(*(++std::declval<Iterator>())),\n                     typename std::iterator_traits<Iterator>::reference>;\n};\n\ntemplate <typename Iterator>\nvoid VerifyIsInputIterator(Iterator) {\n  static_assert(IsLegacyInputIteratorImpl<Iterator>::value);\n}\n#endif\n\nTEST(AnnotationListStatic, Register) {\n  ASSERT_FALSE(AnnotationList::Get());\n  EXPECT_TRUE(AnnotationList::Register());\n  EXPECT_TRUE(AnnotationList::Get());\n  EXPECT_EQ(AnnotationList::Get(), AnnotationList::Register());\n\n  // This isn't expected usage of the AnnotationList API, but it is necessary\n  // for testing.\n  AnnotationList* list = AnnotationList::Get();\n  CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr);\n  delete list;\n\n  EXPECT_FALSE(AnnotationList::Get());\n}\n\nclass AnnotationList : public testing::Test {\n public:\n  void SetUp() override {\n    CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_);\n  }\n\n  void TearDown() override {\n    CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr);\n  }\n\n  // NOTE: Annotations should be declared at file-scope, but in order to test\n  // them, they are declared as part of the test. These members are public so\n  // they are accessible from global helpers.\n  crashpad::StringAnnotation<8> one_{\"First\"};\n  crashpad::StringAnnotation<256> two_{\"Second\"};\n  crashpad::StringAnnotation<101> three_{\"First\"};\n\n protected:\n  using AllAnnotations = std::vector<std::pair<std::string, std::string>>;\n\n  AllAnnotations CollectAnnotations() {\n    AllAnnotations annotations;\n\n    for (Annotation* curr : annotations_) {\n      if (!curr->is_set())\n        continue;\n      std::string value(static_cast<const char*>(curr->value()), curr->size());\n      annotations.push_back(std::make_pair(curr->name(), value));\n    }\n\n    return annotations;\n  }\n\n  bool ContainsNameValue(const AllAnnotations& annotations,\n                         const std::string& name,\n                         const std::string& value) {\n    return std::find(annotations.begin(),\n                     annotations.end(),\n                     std::make_pair(name, value)) != annotations.end();\n  }\n\n  crashpad::AnnotationList annotations_;\n};\n\nTEST_F(AnnotationList, SetAndClear) {\n  one_.Set(\"this is a value longer than 8 bytes\");\n  AllAnnotations annotations = CollectAnnotations();\n\n  EXPECT_EQ(1u, annotations.size());\n  EXPECT_TRUE(ContainsNameValue(annotations, \"First\", \"this is \"));\n\n  one_.Clear();\n\n  EXPECT_EQ(0u, CollectAnnotations().size());\n\n  one_.Set(\"short\");\n  two_.Set(std::string(500, 'A').data());\n\n  annotations = CollectAnnotations();\n  EXPECT_EQ(2u, annotations.size());\n\n  EXPECT_EQ(5u, one_.size());\n  EXPECT_EQ(256u, two_.size());\n\n  EXPECT_TRUE(ContainsNameValue(annotations, \"First\", \"short\"));\n  EXPECT_TRUE(ContainsNameValue(annotations, \"Second\", std::string(256, 'A')));\n}\n\nTEST_F(AnnotationList, DuplicateKeys) {\n  ASSERT_EQ(0u, CollectAnnotations().size());\n\n  one_.Set(\"1\");\n  three_.Set(\"2\");\n\n  AllAnnotations annotations = CollectAnnotations();\n  EXPECT_EQ(2u, annotations.size());\n\n  EXPECT_TRUE(ContainsNameValue(annotations, \"First\", \"1\"));\n  EXPECT_TRUE(ContainsNameValue(annotations, \"First\", \"2\"));\n\n  one_.Clear();\n\n  annotations = CollectAnnotations();\n  EXPECT_EQ(1u, annotations.size());\n}\n\nTEST_F(AnnotationList, IteratorSingleAnnotation) {\n  ASSERT_EQ(annotations_.begin(), annotations_.end());\n  ASSERT_EQ(annotations_.cbegin(), annotations_.cend());\n\n  one_.Set(\"1\");\n\n  auto iterator = annotations_.begin();\n  auto const_iterator = annotations_.cbegin();\n\n  ASSERT_NE(iterator, annotations_.end());\n  ASSERT_NE(const_iterator, annotations_.cend());\n\n  EXPECT_EQ(*iterator, &one_);\n  EXPECT_EQ(*const_iterator, &one_);\n\n  ++iterator;\n  ++const_iterator;\n\n  EXPECT_EQ(iterator, annotations_.end());\n  EXPECT_EQ(const_iterator, annotations_.cend());\n}\n\nTEST_F(AnnotationList, IteratorMultipleAnnotationsInserted) {\n  ASSERT_EQ(annotations_.begin(), annotations_.end());\n  ASSERT_EQ(annotations_.cbegin(), annotations_.cend());\n\n  one_.Set(\"1\");\n  two_.Set(\"2\");\n\n  // New annotations are inserted to the beginning of the list. Hence, |two_|\n  // must be the first annotation, followed by |one_|.\n  {\n    auto iterator = annotations_.begin();\n    auto const_iterator = annotations_.cbegin();\n\n    ASSERT_NE(iterator, annotations_.end());\n    ASSERT_NE(const_iterator, annotations_.cend());\n\n    EXPECT_EQ(*iterator, &two_);\n    EXPECT_EQ(*const_iterator, &two_);\n\n    ++iterator;\n    ++const_iterator;\n\n    ASSERT_NE(iterator, annotations_.end());\n    ASSERT_NE(const_iterator, annotations_.cend());\n\n    EXPECT_EQ(*iterator, &one_);\n    EXPECT_EQ(*const_iterator, &one_);\n\n    ++iterator;\n    ++const_iterator;\n\n    EXPECT_EQ(iterator, annotations_.end());\n    EXPECT_EQ(const_iterator, annotations_.cend());\n  }\n}\n\nTEST_F(AnnotationList, IteratorMultipleAnnotationsInsertedAndRemoved) {\n  ASSERT_EQ(annotations_.begin(), annotations_.end());\n  ASSERT_EQ(annotations_.cbegin(), annotations_.cend());\n\n  one_.Set(\"1\");\n  two_.Set(\"2\");\n  one_.Clear();\n  two_.Clear();\n\n  // Even after clearing, Annotations are still inserted in the list and\n  // reachable via the iterators.\n  auto iterator = annotations_.begin();\n  auto const_iterator = annotations_.cbegin();\n\n  ASSERT_NE(iterator, annotations_.end());\n  ASSERT_NE(const_iterator, annotations_.cend());\n\n  EXPECT_EQ(*iterator, &two_);\n  EXPECT_EQ(*const_iterator, &two_);\n\n  ++iterator;\n  ++const_iterator;\n\n  ASSERT_NE(iterator, annotations_.end());\n  ASSERT_NE(const_iterator, annotations_.cend());\n\n  EXPECT_EQ(*iterator, &one_);\n  EXPECT_EQ(*const_iterator, &one_);\n\n  ++iterator;\n  ++const_iterator;\n\n  EXPECT_EQ(iterator, annotations_.end());\n  EXPECT_EQ(const_iterator, annotations_.cend());\n}\n\nTEST_F(AnnotationList, IteratorIsInputIterator) {\n  one_.Set(\"1\");\n  two_.Set(\"2\");\n\n  // Check explicitly that the iterators meet the interface of an input\n  // iterator.\n  VerifyIsInputIterator(annotations_.begin());\n  VerifyIsInputIterator(annotations_.cbegin());\n  VerifyIsInputIterator(annotations_.end());\n  VerifyIsInputIterator(annotations_.cend());\n\n  // Additionally verify that std::distance accepts the iterators. It requires\n  // the iterators to comply to the input iterator interface.\n  EXPECT_EQ(std::distance(annotations_.begin(), annotations_.end()), 2);\n  EXPECT_EQ(std::distance(annotations_.cbegin(), annotations_.cend()), 2);\n}\n\nclass RaceThread : public Thread {\n public:\n  explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {}\n\n private:\n  void ThreadMain() override {\n    for (int i = 0; i <= 50; ++i) {\n      if (i % 2 == 0) {\n        test_->three_.Set(\"three\");\n        test_->two_.Clear();\n      } else {\n        test_->three_.Clear();\n      }\n      SleepNanoseconds(base::RandInt(1, 1000));\n    }\n  }\n\n  test::AnnotationList* test_;\n};\n\nTEST_F(AnnotationList, MultipleThreads) {\n  ASSERT_EQ(0u, CollectAnnotations().size());\n\n  RaceThread other_thread(this);\n  other_thread.Start();\n\n  for (int i = 0; i <= 50; ++i) {\n    if (i % 2 == 0) {\n      one_.Set(\"one\");\n      two_.Set(\"two\");\n    } else {\n      one_.Clear();\n    }\n    SleepNanoseconds(base::RandInt(1, 1000));\n  }\n\n  other_thread.Join();\n\n  AllAnnotations annotations = CollectAnnotations();\n  EXPECT_GE(annotations.size(), 2u);\n  EXPECT_LE(annotations.size(), 3u);\n\n  EXPECT_TRUE(ContainsNameValue(annotations, \"First\", \"one\"));\n  EXPECT_TRUE(ContainsNameValue(annotations, \"First\", \"three\"));\n\n  if (annotations.size() == 3) {\n    EXPECT_TRUE(ContainsNameValue(annotations, \"Second\", \"two\"));\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/annotation_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/annotation.h\"\n\n#include <array>\n#include <string>\n\n#include \"client/annotation_list.h\"\n#include \"client/crashpad_info.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n#include \"util/misc/clock.h\"\n#include \"util/synchronization/scoped_spin_guard.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass SpinGuardAnnotation final : public Annotation {\n public:\n  SpinGuardAnnotation(Annotation::Type type, const char name[])\n      : Annotation(type,\n                   name,\n                   &value_,\n                   ConcurrentAccessGuardMode::kScopedSpinGuard) {}\n\n  bool Set(bool value, uint64_t spin_guard_timeout_ns) {\n    auto guard = TryCreateScopedSpinGuard(spin_guard_timeout_ns);\n    if (!guard) {\n      return false;\n    }\n    value_ = value;\n    SetSize(sizeof(value_));\n    return true;\n  }\n\n private:\n  bool value_;\n};\n\nclass ScopedSpinGuardUnlockThread final : public Thread {\n public:\n  ScopedSpinGuardUnlockThread(ScopedSpinGuard scoped_spin_guard,\n                              uint64_t sleep_time_ns)\n      : scoped_spin_guard_(std::move(scoped_spin_guard)),\n        sleep_time_ns_(sleep_time_ns) {}\n\n private:\n  void ThreadMain() override {\n    SleepNanoseconds(sleep_time_ns_);\n\n    // Move the ScopedSpinGuard member into a local variable which is\n    // destroyed when ThreadMain() returns.\n    ScopedSpinGuard local_scoped_spin_guard(std::move(scoped_spin_guard_));\n\n    // After this point, local_scoped_spin_guard will be destroyed and unlocked.\n  }\n\n  ScopedSpinGuard scoped_spin_guard_;\n  const uint64_t sleep_time_ns_;\n};\n\nclass Annotation : public testing::Test {\n public:\n  void SetUp() override {\n    CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_);\n  }\n\n  void TearDown() override {\n    CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr);\n  }\n\n  size_t AnnotationsCount() {\n    size_t result = 0;\n    for (auto* annotation : annotations_) {\n      if (annotation->is_set())\n        ++result;\n    }\n    return result;\n  }\n\n protected:\n  crashpad::AnnotationList annotations_;\n};\n\nTEST_F(Annotation, Basics) {\n  constexpr crashpad::Annotation::Type kType =\n      crashpad::Annotation::UserDefinedType(1);\n\n  const char kName[] = \"annotation 1\";\n  char buffer[1024];\n  crashpad::Annotation annotation(kType, kName, buffer);\n\n  EXPECT_FALSE(annotation.is_set());\n  EXPECT_EQ(0u, AnnotationsCount());\n\n  EXPECT_EQ(kType, annotation.type());\n  EXPECT_EQ(0u, annotation.size());\n  EXPECT_EQ(std::string(kName), annotation.name());\n  EXPECT_EQ(buffer, annotation.value());\n\n  annotation.SetSize(10);\n\n  EXPECT_TRUE(annotation.is_set());\n  EXPECT_EQ(1u, AnnotationsCount());\n\n  EXPECT_EQ(10u, annotation.size());\n  EXPECT_EQ(&annotation, *annotations_.begin());\n\n  annotation.Clear();\n\n  EXPECT_FALSE(annotation.is_set());\n  EXPECT_EQ(0u, AnnotationsCount());\n\n  EXPECT_EQ(0u, annotation.size());\n}\n\nTEST_F(Annotation, StringType) {\n  crashpad::StringAnnotation<5> annotation(\"name\");\n\n  EXPECT_FALSE(annotation.is_set());\n\n  EXPECT_EQ(crashpad::Annotation::Type::kString, annotation.type());\n  EXPECT_EQ(0u, annotation.size());\n  EXPECT_EQ(std::string(\"name\"), annotation.name());\n  EXPECT_EQ(0u, annotation.value().size());\n\n  annotation.Set(\"test\");\n\n  EXPECT_TRUE(annotation.is_set());\n  EXPECT_EQ(1u, AnnotationsCount());\n\n  EXPECT_EQ(4u, annotation.size());\n  EXPECT_EQ(\"test\", annotation.value());\n\n  annotation.Set(std::string(\"loooooooooooong\"));\n\n  EXPECT_TRUE(annotation.is_set());\n  EXPECT_EQ(1u, AnnotationsCount());\n\n  EXPECT_EQ(5u, annotation.size());\n  EXPECT_EQ(\"loooo\", annotation.value());\n}\n\nTEST_F(Annotation, BaseAnnotationShouldNotSupportSpinGuard) {\n  char buffer[1024];\n  crashpad::Annotation annotation(\n      crashpad::Annotation::Type::kString, \"no-spin-guard\", buffer);\n  EXPECT_EQ(annotation.concurrent_access_guard_mode(),\n            crashpad::Annotation::ConcurrentAccessGuardMode::kUnguarded);\n#if !DCHECK_IS_ON()\n  // This fails a DCHECK() in debug builds, so only test it when DCHECK()\n  // is not on.\n  EXPECT_EQ(std::nullopt, annotation.TryCreateScopedSpinGuard(0));\n#endif\n}\n\nTEST_F(Annotation, CustomAnnotationShouldSupportSpinGuardAndSet) {\n  constexpr crashpad::Annotation::Type kType =\n      crashpad::Annotation::UserDefinedType(1);\n  SpinGuardAnnotation spin_guard_annotation(kType, \"spin-guard\");\n  EXPECT_EQ(spin_guard_annotation.concurrent_access_guard_mode(),\n            crashpad::Annotation::ConcurrentAccessGuardMode::kScopedSpinGuard);\n  EXPECT_TRUE(spin_guard_annotation.Set(true, 0));\n  EXPECT_EQ(1U, spin_guard_annotation.size());\n}\n\nTEST_F(Annotation, CustomAnnotationSetShouldFailIfRunConcurrently) {\n  constexpr crashpad::Annotation::Type kType =\n      crashpad::Annotation::UserDefinedType(1);\n  SpinGuardAnnotation spin_guard_annotation(kType, \"spin-guard\");\n  auto guard = spin_guard_annotation.TryCreateScopedSpinGuard(0);\n  EXPECT_NE(std::nullopt, guard);\n  // This should fail, since the guard is already held and the timeout is 0.\n  EXPECT_FALSE(spin_guard_annotation.Set(true, 0));\n}\n\nTEST_F(Annotation,\n       CustomAnnotationSetShouldSucceedIfSpinGuardUnlockedAsynchronously) {\n  constexpr crashpad::Annotation::Type kType =\n      crashpad::Annotation::UserDefinedType(1);\n  SpinGuardAnnotation spin_guard_annotation(kType, \"spin-guard\");\n  auto guard = spin_guard_annotation.TryCreateScopedSpinGuard(0);\n  EXPECT_NE(std::nullopt, guard);\n  // Pass the guard off to a background thread which unlocks it after 1 ms.\n  constexpr uint64_t kSleepTimeNs = 1000000;  // 1 ms\n  ScopedSpinGuardUnlockThread unlock_thread(std::move(guard.value()),\n                                            kSleepTimeNs);\n  unlock_thread.Start();\n\n  // Try to set the annotation with a 100 ms timeout.\n  constexpr uint64_t kSpinGuardTimeoutNanos = 100000000;  // 100 ms\n\n  // This should succeed after 1 ms, since the timeout is much larger than the\n  // time the background thread holds the guard.\n  EXPECT_TRUE(spin_guard_annotation.Set(true, kSpinGuardTimeoutNanos));\n\n  unlock_thread.Join();\n}\n\nTEST(StringAnnotation, ArrayOfString) {\n  static crashpad::StringAnnotation<4> annotations[] = {\n      {\"test-1\", crashpad::StringAnnotation<4>::Tag::kArray},\n      {\"test-2\", crashpad::StringAnnotation<4>::Tag::kArray},\n      {\"test-3\", crashpad::StringAnnotation<4>::Tag::kArray},\n      {\"test-4\", crashpad::StringAnnotation<4>::Tag::kArray},\n  };\n\n  for (auto& annotation : annotations) {\n    EXPECT_FALSE(annotation.is_set());\n  }\n}\n\n#if DCHECK_IS_ON()\n\nTEST(AnnotationDeathTest, EmbeddedNUL) {\n  crashpad::StringAnnotation<5> annotation(\"name\");\n  EXPECT_DEATH_CHECK(annotation.Set(std::string(\"te\\0st\", 5)), \"embedded NUL\");\n}\n\n#endif\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/client_argv_handling.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/client_argv_handling.h\"\n\n#include \"base/strings/stringprintf.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nstd::string FormatArgumentString(const std::string& name,\n                                 const std::string& value) {\n  return base::StringPrintf(\"--%s=%s\", name.c_str(), value.c_str());\n}\n\n}  // namespace\n\nstd::vector<std::string> BuildHandlerArgvStrings(\n    const base::FilePath& handler,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    const std::vector<base::FilePath>& attachments) {\n  std::vector<std::string> argv_strings(1, handler.value());\n\n  for (const auto& argument : arguments) {\n    argv_strings.push_back(argument);\n  }\n\n  if (!database.empty()) {\n    argv_strings.push_back(FormatArgumentString(\"database\", database.value()));\n  }\n\n  if (!metrics_dir.empty()) {\n    argv_strings.push_back(\n        FormatArgumentString(\"metrics-dir\", metrics_dir.value()));\n  }\n\n  if (!url.empty()) {\n    argv_strings.push_back(FormatArgumentString(\"url\", url));\n  }\n\n  for (const auto& kv : annotations) {\n    argv_strings.push_back(\n        FormatArgumentString(\"annotation\", kv.first + '=' + kv.second));\n  }\n\n  for (const auto& attachment : attachments) {\n    argv_strings.push_back(\n        FormatArgumentString(\"attachment\", attachment.value()));\n  }\n\n  return argv_strings;\n}\n\nvoid StringVectorToCStringVector(const std::vector<std::string>& strings,\n                                 std::vector<const char*>* c_strings) {\n  c_strings->clear();\n  c_strings->reserve(strings.size() + 1);\n  for (const auto& str : strings) {\n    c_strings->push_back(str.c_str());\n  }\n  c_strings->push_back(nullptr);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/client_argv_handling.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_CLIENT_ARGV_HANDLING_H_\n#define CRASHPAD_CLIENT_CLIENT_ARGV_HANDLING_H_\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n\nnamespace crashpad {\n\n//! \\brief Builds a vector of arguments suitable for invoking a handler process\n//!     based on arguments passed to StartHandler-type().\n//!\n//! See StartHandlerAtCrash() for documentation on the input arguments.\n//!\n//! \\return A vector of arguments suitable for starting the handler with.\nstd::vector<std::string> BuildHandlerArgvStrings(\n    const base::FilePath& handler,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    const std::vector<base::FilePath>& attachments = {});\n\n//! \\brief Flattens a string vector into a const char* vector suitable for use\n//!     in an exec() call.\n//!\n//! \\param[in] strings A vector of string data. This vector must remain valid\n//!     for the lifetime of \\a c_strings.\n//! \\param[out] c_strings A vector of pointers to the string data in \\a strings.\nvoid StringVectorToCStringVector(const std::vector<std::string>& strings,\n                                 std::vector<const char*>* c_strings);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_CLIENT_ARGV_HANDLING_H_\n"
  },
  {
    "path": "client/crash_handler_base_ios.cc",
    "content": "// Copyright 2025 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"client/crash_handler_base_ios.h\"\n\n#include \"base/logging.h\"\n#include \"util/posix/signals.h\"\n\nnamespace crashpad {\n\nCrashHandlerBase::CrashHandlerBase() = default;\nCrashHandlerBase::~CrashHandlerBase() = default;\n\nbool CrashHandlerBase::Initialize(\n    const base::FilePath& database,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    internal::InProcessHandler::ProcessPendingReportsObservationCallback\n        callback) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  if (!in_process_handler().Initialize(database, url, annotations, callback)) {\n    LOG(ERROR) << \"Unable to initialize Crashpad.\";\n    return false;\n  }\n  if (!DoInitialize()) {\n    return false;\n  }\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nvoid CrashHandlerBase::ProcessIntermediateDumps(\n    const std::map<std::string, std::string>& annotations,\n    const UserStreamDataSources* user_stream_sources) {\n  in_process_handler_.ProcessIntermediateDumps(annotations,\n                                               user_stream_sources);\n}\n\nvoid CrashHandlerBase::ProcessIntermediateDump(\n    const base::FilePath& file,\n    const std::map<std::string, std::string>& annotations) {\n  in_process_handler_.ProcessIntermediateDump(file, annotations);\n}\n\nvoid CrashHandlerBase::DumpWithoutCrash(NativeCPUContext* context,\n                                        bool process_dump) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  base::FilePath path;\n  if (!in_process_handler_.DumpExceptionFromSimulatedMachException(\n          context, kMachExceptionSimulated, &path)) {\n    return;\n  }\n\n  if (process_dump) {\n    in_process_handler_.ProcessIntermediateDump(path);\n  }\n}\n\nvoid CrashHandlerBase::DumpWithoutCrashAtPath(NativeCPUContext* context,\n                                              const base::FilePath& path) {\n  in_process_handler_.DumpExceptionFromSimulatedMachExceptionAtPath(\n      context, kMachExceptionSimulated, path);\n}\n\nvoid CrashHandlerBase::StartProcessingPendingReports(\n    UploadBehavior upload_behavior) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  in_process_handler_.StartProcessingPendingReports(upload_behavior);\n}\n\nvoid CrashHandlerBase::SetExceptionCallbackForTesting(void (*callback)()) {\n  in_process_handler_.SetExceptionCallbackForTesting(callback);\n}\n\nvoid CrashHandlerBase::HandleAndReraiseSignal(int signo,\n                                              siginfo_t* siginfo,\n                                              ucontext_t* context,\n                                              struct sigaction* old_action) {\n  in_process_handler_.DumpExceptionFromSignal(siginfo, context);\n\n  // Always call system handler.\n  Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action);\n}\n\nvoid CrashHandlerBase::HandleUncaughtNSException(const uint64_t* frames,\n                                                 const size_t num_frames) {\n  in_process_handler_.DumpExceptionFromNSExceptionWithFrames(frames,\n                                                             num_frames);\n  // After uncaught exceptions are reported, the system immediately triggers a\n  // call to std::terminate()/abort(). Remove the abort handler so a second\n  // dump isn't generated.\n  CHECK(Signals::InstallDefaultHandler(SIGABRT));\n}\n\nvoid CrashHandlerBase::HandleUncaughtNSExceptionWithContext(\n    NativeCPUContext* context) {\n  base::FilePath path;\n  in_process_handler_.DumpExceptionFromSimulatedMachException(\n      context, kMachExceptionFromNSException, &path);\n\n  // After uncaught exceptions are reported, the system immediately triggers a\n  // call to std::terminate()/abort(). Remove the abort handler so a second\n  // dump isn't generated.\n  CHECK(Signals::InstallDefaultHandler(SIGABRT));\n}\n\nvoid CrashHandlerBase::HandleUncaughtNSExceptionWithContextAtPath(\n    NativeCPUContext* context,\n    const base::FilePath& path) {\n  in_process_handler_.DumpExceptionFromSimulatedMachExceptionAtPath(\n      context, kMachExceptionFromNSException, path);\n}\n\nbool CrashHandlerBase::MoveIntermediateDumpAtPathToPending(\n    const base::FilePath& path) {\n  if (in_process_handler_.MoveIntermediateDumpAtPathToPending(path)) {\n    // After uncaught exceptions are reported, the system immediately triggers\n    // a call to std::terminate()/abort(). Remove the abort handler so a\n    // second dump isn't generated.\n    CHECK(Signals::InstallDefaultHandler(SIGABRT));\n    return true;\n  }\n  return false;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crash_handler_base_ios.h",
    "content": "// Copyright 2025 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef CRASHPAD_CLIENT_CRASH_HANDLER_BASE_IOS_H_\n#define CRASHPAD_CLIENT_CRASH_HANDLER_BASE_IOS_H_\n\n#include <stdint.h>\n#include <sys/signal.h>\n\n#include <map>\n#include <string>\n\n#include \"base/files/file_path.h\"\n#include \"client/ios_handler/exception_processor.h\"\n#include \"client/ios_handler/in_process_handler.h\"\n#include \"client/upload_behavior_ios.h\"\n#include \"util/misc/capture_context.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\n// Base class shared by the iOS and tvOS CrashHandler implementations.\nclass CrashHandlerBase : public ObjcExceptionDelegate {\n public:\n  CrashHandlerBase(const CrashHandlerBase&) = delete;\n  CrashHandlerBase& operator=(const CrashHandlerBase&) = delete;\n\n  bool Initialize(\n      const base::FilePath& database,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      internal::InProcessHandler::ProcessPendingReportsObservationCallback\n          callback);\n\n  void ProcessIntermediateDumps(\n      const std::map<std::string, std::string>& annotations,\n      const UserStreamDataSources* user_stream_sources);\n\n  void ProcessIntermediateDump(\n      const base::FilePath& file,\n      const std::map<std::string, std::string>& annotations);\n\n  void DumpWithoutCrash(NativeCPUContext* context, bool process_dump);\n\n  void DumpWithoutCrashAtPath(NativeCPUContext* context,\n                              const base::FilePath& path);\n\n  void StartProcessingPendingReports(UploadBehavior upload_behavior);\n\n  void SetExceptionCallbackForTesting(void (*callback)());\n\n protected:\n  CrashHandlerBase();\n  virtual ~CrashHandlerBase();\n\n  // Subclasses are expected to install signal handlers and set up Mach ports in\n  // this function.\n  virtual bool DoInitialize() = 0;\n\n  void HandleAndReraiseSignal(int signo,\n                              siginfo_t* siginfo,\n                              ucontext_t* context,\n                              struct sigaction* old_action);\n\n  internal::InProcessHandler& in_process_handler() {\n    return in_process_handler_;\n  }\n\n private:\n  // ObjcExceptionDelegate overrides:\n  void HandleUncaughtNSException(const uint64_t* frames,\n                                 const size_t num_frames) override;\n\n  void HandleUncaughtNSExceptionWithContext(NativeCPUContext* context) override;\n\n  void HandleUncaughtNSExceptionWithContextAtPath(\n      NativeCPUContext* context,\n      const base::FilePath& path) override;\n\n  bool MoveIntermediateDumpAtPathToPending(const base::FilePath& path) override;\n\n  internal::InProcessHandler in_process_handler_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_CRASH_HANDLER_BASE_IOS_H_\n"
  },
  {
    "path": "client/crash_handler_ios.cc",
    "content": "// Copyright 2025 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"client/crash_handler_ios.h\"\n\n#include <sys/sysctl.h>\n#include <unistd.h>\n\n#include <iterator>\n\n#include \"base/apple/mach_logging.h\"\n#include \"util/ios/raw_logging.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n#include \"util/posix/signals.h\"\n\nnamespace {\n\nbool IsBeingDebugged() {\n  int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};\n  size_t len = sizeof(kinfo_proc);\n  kinfo_proc kern_proc_info;\n  if (sysctl(mib, std::size(mib), &kern_proc_info, &len, nullptr, 0) == 0) {\n    return kern_proc_info.kp_proc.p_flag & P_TRACED;\n  }\n  return false;\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nCrashHandler::ThreadSafeScopedMachPortWithReceiveRight::\n    ThreadSafeScopedMachPortWithReceiveRight()\n    : port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)) {}\n\nCrashHandler::ThreadSafeScopedMachPortWithReceiveRight::\n    ~ThreadSafeScopedMachPortWithReceiveRight() {\n  reset();\n}\n\nmach_port_t CrashHandler::ThreadSafeScopedMachPortWithReceiveRight::get() {\n  return port_.load();\n}\nvoid CrashHandler::ThreadSafeScopedMachPortWithReceiveRight::reset() {\n  mach_port_t old_port = port_.exchange(MACH_PORT_NULL);\n  if (old_port == MACH_PORT_NULL) {\n    // Already reset, nothing to do.\n    return;\n  }\n  kern_return_t kr = mach_port_mod_refs(\n      mach_task_self(), old_port, MACH_PORT_RIGHT_RECEIVE, -1);\n  MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)\n      << \"ThreadSafeScopedMachPortWithReceiveRight mach_port_mod_refs\";\n  kr = mach_port_deallocate(mach_task_self(), old_port);\n  MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)\n      << \"ThreadSafeScopedMachPortWithReceiveRight mach_port_deallocate\";\n}\n\nCrashHandler* CrashHandler::instance_ = nullptr;\n\nCrashHandler::CrashHandler() = default;\n\nCrashHandler::~CrashHandler() {\n  UninstallObjcExceptionPreprocessor();\n  // Reset the SIGPIPE handler only if the current handler is the\n  // one installed by DoInitialize(). In other words, if an\n  // application has set its own SIGPIPE handler after initializing\n  // Crashpad, there is no need to change the signal handler here.\n  struct sigaction sa;\n  if (sigaction(SIGPIPE, nullptr, &sa) == 0 &&\n      sa.sa_sigaction == CatchAndReraiseSignalDefaultAction) {\n    Signals::InstallDefaultHandler(SIGPIPE);\n  }\n  Signals::InstallDefaultHandler(SIGABRT);\n  UninstallMachExceptionHandler();\n}\n\n// static\nCrashHandler* CrashHandler::Get() {\n  if (!instance_)\n    instance_ = new CrashHandler();\n  return instance_;\n}\n\n// static\nvoid CrashHandler::ResetForTesting() {\n  delete instance_;\n  instance_ = nullptr;\n}\n\nbool CrashHandler::DoInitialize() {\n  if (!InstallMachExceptionHandler() ||\n      // xnu turns hardware faults into Mach exceptions, so the only signal\n      // left to register is SIGABRT, which never starts off as a hardware\n      // fault. Installing a handler for other signals would lead to\n      // recording exceptions twice. As a consequence, Crashpad will not\n      // generate intermediate dumps for anything manually calling\n      // raise(SIG*). In practice, this doesn’t actually happen for crash\n      // signals that originate as hardware faults.\n      !Signals::InstallHandler(\n          SIGABRT, CatchAndReraiseSignal, 0, &old_action_)) {\n    return false;\n  }\n\n  // For applications that haven't ignored or set a handler for SIGPIPE:\n  // It’s OK for an application to set its own SIGPIPE handler (including\n  // SIG_IGN) before initializing Crashpad, because Crashpad will discover the\n  // existing handler and not install its own.\n  // It’s OK for Crashpad to install its own SIGPIPE handler and for the\n  // application to subsequently install its own (including SIG_IGN)\n  // afterwards, because its handler will replace Crashpad’s.\n  // This is useful to cover the default situation where nobody installs a\n  // SIGPIPE handler and the disposition is at SIG_DFL, because SIGPIPE is a\n  // “kill” signal (bsd/sys/signalvar.h sigprop). In that case, without\n  // Crashpad, SIGPIPE results in a silent and unreported kill (and not even\n  // ReportCrash will record it), but developers probably want to be alerted to\n  // the condition.\n  struct sigaction sa;\n  if (sigaction(SIGPIPE, nullptr, &sa) == 0 && sa.sa_handler == SIG_DFL) {\n    Signals::InstallHandler(\n        SIGPIPE, CatchAndReraiseSignalDefaultAction, 0, nullptr);\n  }\n\n  InstallObjcExceptionPreprocessor(this);\n  return true;\n}\n\nbool CrashHandler::InstallMachExceptionHandler() {\n  mach_port_t exception_port = exception_port_.get();\n  if (exception_port == MACH_PORT_NULL) {\n    return false;\n  }\n\n  kern_return_t kr = mach_port_insert_right(mach_task_self(),\n                                            exception_port,\n                                            exception_port,\n                                            MACH_MSG_TYPE_MAKE_SEND);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << \"mach_port_insert_right\";\n    return false;\n  }\n\n  // TODO: Use SwapExceptionPort instead and put back EXC_MASK_BREAKPOINT.\n  // Until then, remove |EXC_MASK_BREAKPOINT| while attached to a debugger.\n  exception_mask_t mask =\n      ExcMaskAll() &\n      ~(EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_RPC_ALERT |\n        EXC_MASK_GUARD | (IsBeingDebugged() ? EXC_MASK_BREAKPOINT : 0));\n\n  exception_behavior_t behavior = EXCEPTION_STATE_IDENTITY;\n  if (__builtin_available(iOS 18.0, *)) {\n    behavior = EXCEPTION_STATE_IDENTITY_PROTECTED;\n  }\n\n  ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);\n  if (!exception_ports.GetExceptionPorts(mask, &original_handlers_) ||\n      !exception_ports.SetExceptionPort(mask,\n                                        exception_port,\n                                        behavior | MACH_EXCEPTION_CODES,\n                                        MACHINE_THREAD_STATE)) {\n    return false;\n  }\n\n  mach_handler_running_ = true;\n  Start();\n  return true;\n}\n\nvoid CrashHandler::UninstallMachExceptionHandler() {\n  mach_handler_running_ = false;\n  exception_port_.reset();\n  Join();\n}\n\n// Thread:\n\nvoid CrashHandler::ThreadMain() {\n  UniversalMachExcServer universal_mach_exc_server(this);\n  while (mach_handler_running_) {\n    mach_msg_return_t mr =\n        MachMessageServer::Run(&universal_mach_exc_server,\n                               exception_port_.get(),\n                               MACH_MSG_OPTION_NONE,\n                               MachMessageServer::kPersistent,\n                               MachMessageServer::kReceiveLargeIgnore,\n                               kMachMessageTimeoutWaitIndefinitely);\n    MACH_CHECK(\n        mach_handler_running_\n            ? mr == MACH_SEND_INVALID_DEST  // This shouldn't happen for\n                                            // exception messages that come\n                                            // from the kernel itself, but if\n                                            // something else in-process sends\n                                            // exception messages and breaks,\n                                            // handle that case.\n            : (mr == MACH_RCV_PORT_CHANGED ||  // Port was closed while the\n                                               // thread was listening.\n               mr == MACH_RCV_INVALID_NAME),  // Port was closed before the\n                                              // thread started listening.\n        mr)\n        << \"MachMessageServer::Run\";\n  }\n}\n\n// UniversalMachExcServer::Interface:\n\nkern_return_t CrashHandler::CatchMachException(\n    exception_behavior_t behavior,\n    exception_handler_t exception_port,\n    thread_t thread,\n    task_t task,\n    exception_type_t exception,\n    const mach_exception_data_type_t* code,\n    mach_msg_type_number_t code_count,\n    thread_state_flavor_t* flavor,\n    ConstThreadState old_state,\n    mach_msg_type_number_t old_state_count,\n    thread_state_t new_state,\n    mach_msg_type_number_t* new_state_count,\n    const mach_msg_trailer_t* trailer,\n    bool* destroy_complex_request) {\n  *destroy_complex_request = true;\n\n  // TODO(justincohen): Forward exceptions to original_handlers_ with\n  // UniversalExceptionRaise.\n\n  // iOS shouldn't have any child processes, but just in case, those will\n  // inherit the task exception ports, and this process isn’t prepared to\n  // handle them\n  if (task != mach_task_self()) {\n    CRASHPAD_RAW_LOG(\"MachException task != mach_task_self()\");\n    return KERN_FAILURE;\n  }\n\n  HandleMachException(behavior,\n                      thread,\n                      exception,\n                      code,\n                      code_count,\n                      *flavor,\n                      old_state,\n                      old_state_count);\n\n  // Respond with KERN_FAILURE so the system will continue to handle this\n  // exception. xnu will turn this Mach exception into a signal and take the\n  // default action to terminate the process. However, if sigprocmask is\n  // called before this Mach exception returns (such as by another thread\n  // calling abort, see: Libc-1506.40.4/stdlib/FreeBSD/abort.c), the Mach\n  // exception will be converted into a signal but delivery will be blocked.\n  // Since concurrent exceptions lead to the losing thread sleeping\n  // indefinitely, if the abort thread never returns, the thread that\n  // triggered this Mach exception will repeatedly trap and the process will\n  // never terminate. If the abort thread didn’t have a user-space signal\n  // handler that slept forever, the abort would terminate the process even if\n  // all other signals had been blocked. Instead, unblock all signals\n  // corresponding to all Mach exceptions Crashpad is registered for before\n  // returning KERN_FAILURE. There is still racy behavior possible with this\n  // call to sigprocmask, but the repeated calls to CatchMachException here\n  // will eventually lead to termination.\n  sigset_t unblock_set;\n  sigemptyset(&unblock_set);\n  sigaddset(&unblock_set, SIGILL);  // EXC_BAD_INSTRUCTION\n  sigaddset(&unblock_set, SIGTRAP);  // EXC_BREAKPOINT\n  sigaddset(&unblock_set, SIGFPE);  // EXC_ARITHMETIC\n  sigaddset(&unblock_set, SIGBUS);  // EXC_BAD_ACCESS\n  sigaddset(&unblock_set, SIGSEGV);  // EXC_BAD_ACCESS\n  if (sigprocmask(SIG_UNBLOCK, &unblock_set, nullptr) != 0) {\n    CRASHPAD_RAW_LOG(\"sigprocmask\");\n  }\n  return KERN_FAILURE;\n}\n\nvoid CrashHandler::HandleMachException(exception_behavior_t behavior,\n                                       thread_t thread,\n                                       exception_type_t exception,\n                                       const mach_exception_data_type_t* code,\n                                       mach_msg_type_number_t code_count,\n                                       thread_state_flavor_t flavor,\n                                       ConstThreadState old_state,\n                                       mach_msg_type_number_t old_state_count) {\n  in_process_handler().DumpExceptionFromMachException(behavior,\n                                                      thread,\n                                                      exception,\n                                                      code,\n                                                      code_count,\n                                                      flavor,\n                                                      old_state,\n                                                      old_state_count);\n}\n\n// static\nvoid CrashHandler::CatchAndReraiseSignal(int signo,\n                                         siginfo_t* siginfo,\n                                         void* context) {\n  Get()->HandleAndReraiseSignal(signo,\n                                siginfo,\n                                reinterpret_cast<ucontext_t*>(context),\n                                &(Get()->old_action_));\n}\n\n// static\nvoid CrashHandler::CatchAndReraiseSignalDefaultAction(int signo,\n                                                      siginfo_t* siginfo,\n                                                      void* context) {\n  Get()->HandleAndReraiseSignal(\n      signo, siginfo, reinterpret_cast<ucontext_t*>(context), nullptr);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crash_handler_ios.h",
    "content": "// Copyright 2025 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef CRASHPAD_CLIENT_CRASH_HANDLER_IOS_H_\n#define CRASHPAD_CLIENT_CRASH_HANDLER_IOS_H_\n\n#include <atomic>\n\n#include \"client/crash_handler_base_ios.h\"\n#include \"util/mach/exc_server_variants.h\"\n#include \"util/mach/exception_ports.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\n\n// A base class for signal handler and Mach exception server.\nclass CrashHandler : public Thread,\n                     public UniversalMachExcServer::Interface,\n                     public CrashHandlerBase {\n public:\n  CrashHandler(const CrashHandler&) = delete;\n  CrashHandler& operator=(const CrashHandler&) = delete;\n\n  static CrashHandler* Get();\n\n  static void ResetForTesting();\n\n private:\n  // Thread-safe version of `base::apple::ScopedMachReceiveRight` which\n  // allocates the Mach port upon construction and deallocates it upon\n  // destruction.\n  class ThreadSafeScopedMachPortWithReceiveRight {\n   public:\n    ThreadSafeScopedMachPortWithReceiveRight();\n\n    ThreadSafeScopedMachPortWithReceiveRight(\n        const ThreadSafeScopedMachPortWithReceiveRight&) = delete;\n    ThreadSafeScopedMachPortWithReceiveRight& operator=(\n        const ThreadSafeScopedMachPortWithReceiveRight&) = delete;\n\n    ~ThreadSafeScopedMachPortWithReceiveRight();\n\n    mach_port_t get();\n    void reset();\n\n   private:\n    std::atomic<mach_port_t> port_;\n  };\n\n  CrashHandler();\n\n  ~CrashHandler() override;\n\n  bool DoInitialize() override;\n\n  bool InstallMachExceptionHandler();\n\n  void UninstallMachExceptionHandler();\n\n  // Thread:\n\n  void ThreadMain() override;\n\n  // UniversalMachExcServer::Interface:\n\n  kern_return_t CatchMachException(exception_behavior_t behavior,\n                                   exception_handler_t exception_port,\n                                   thread_t thread,\n                                   task_t task,\n                                   exception_type_t exception,\n                                   const mach_exception_data_type_t* code,\n                                   mach_msg_type_number_t code_count,\n                                   thread_state_flavor_t* flavor,\n                                   ConstThreadState old_state,\n                                   mach_msg_type_number_t old_state_count,\n                                   thread_state_t new_state,\n                                   mach_msg_type_number_t* new_state_count,\n                                   const mach_msg_trailer_t* trailer,\n                                   bool* destroy_complex_request) override;\n\n  void HandleMachException(exception_behavior_t behavior,\n                           thread_t thread,\n                           exception_type_t exception,\n                           const mach_exception_data_type_t* code,\n                           mach_msg_type_number_t code_count,\n                           thread_state_flavor_t flavor,\n                           ConstThreadState old_state,\n                           mach_msg_type_number_t old_state_count);\n\n  // The signal handler installed at OS-level.\n  static void CatchAndReraiseSignal(int signo,\n                                    siginfo_t* siginfo,\n                                    void* context);\n\n  static void CatchAndReraiseSignalDefaultAction(int signo,\n                                                 siginfo_t* siginfo,\n                                                 void* context);\n\n  ThreadSafeScopedMachPortWithReceiveRight exception_port_;\n  ExceptionPorts::ExceptionHandlerVector original_handlers_;\n  struct sigaction old_action_ = {};\n  static CrashHandler* instance_;\n  std::atomic<bool> mach_handler_running_ = false;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_CRASH_HANDLER_IOS_H_\n"
  },
  {
    "path": "client/crash_handler_tvos.cc",
    "content": "// Copyright 2025 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#include \"client/crash_handler_tvos.h\"\n\n#include <sys/signal.h>\n\n#include \"util/posix/signals.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\n\nCrashHandler* CrashHandler::instance_ = nullptr;\n\nCrashHandler::CrashHandler() = default;\n\nCrashHandler::~CrashHandler() {\n  UninstallObjcExceptionPreprocessor();\n  for (int signo = 1; signo < NSIG; ++signo) {\n    if (!Signals::IsCrashSignal(signo)) {\n      Signals::RestoreOrResetHandler(signo,\n                                     old_actions_.ActionForSignal(signo));\n    } else if (signo == SIGPIPE) {\n      // Reset the SIGPIPE handler only if the current handler is the one\n      // installed by DoInitialize(). In other words, if an application has set\n      // its own SIGPIPE handler after initializing Crashpad, there is no need\n      // to change the signal handler here.\n      struct sigaction sa;\n      if (sigaction(SIGPIPE, nullptr, &sa) == 0 &&\n          sa.sa_sigaction == CatchAndReraiseSignal) {\n        Signals::InstallDefaultHandler(SIGPIPE);\n      }\n    }\n  }\n}\n\n// static\nCrashHandler* CrashHandler::Get() {\n  if (!instance_) {\n    instance_ = new CrashHandler;\n  }\n  return instance_;\n}\n\n// static\nvoid CrashHandler::ResetForTesting() {\n  delete instance_;\n  instance_ = nullptr;\n}\n\nuint64_t CrashHandler::GetThreadIdForTesting() {\n  return Thread::GetThreadIdForTesting();\n}\n\nbool CrashHandler::DoInitialize() {\n  if (!Signals::InstallCrashHandlers(CatchAndReraiseSignal,\n                                     /*flags=*/0,\n                                     &old_actions_)) {\n    return false;\n  }\n\n  // For applications that haven't ignored or set a handler for SIGPIPE:\n  // It’s OK for an application to set its own SIGPIPE handler (including\n  // SIG_IGN) before initializing Crashpad, because Crashpad will discover the\n  // existing handler and not install its own.\n  // It’s OK for Crashpad to install its own SIGPIPE handler and for the\n  // application to subsequently install its own (including SIG_IGN)\n  // afterwards, because its handler will replace Crashpad’s.\n  // This is useful to cover the default situation where nobody installs a\n  // SIGPIPE handler and the disposition is at SIG_DFL, because SIGPIPE is a\n  // “kill” signal (bsd/sys/signalvar.h sigprop). In that case, without\n  // Crashpad, SIGPIPE results in a silent and unreported kill (and not even\n  // ReportCrash will record it), but developers probably want to be alerted to\n  // the condition.\n  struct sigaction sa;\n  if (sigaction(SIGPIPE, nullptr, &sa) == 0 && sa.sa_handler == SIG_DFL) {\n    Signals::InstallHandler(SIGPIPE, CatchAndReraiseSignal, 0, nullptr);\n  }\n\n  InstallObjcExceptionPreprocessor(this);\n  return true;\n}\n\n// static\nvoid CrashHandler::CatchAndReraiseSignal(int signo,\n                                         siginfo_t* siginfo,\n                                         void* context) {\n  CrashHandler* self = Get();\n  self->HandleAndReraiseSignal(signo,\n                               siginfo,\n                               reinterpret_cast<ucontext_t*>(context),\n                               self->old_actions_.ActionForSignal(signo));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crash_handler_tvos.h",
    "content": "// Copyright 2025 The Chromium Authors\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\n#ifndef CRASHPAD_CLIENT_CRASH_HANDLER_TVOS_H_\n#define CRASHPAD_CLIENT_CRASH_HANDLER_TVOS_H_\n\n#include \"client/crash_handler_base_ios.h\"\n#include \"util/posix/signals.h\"\n\nnamespace crashpad {\n\n// A crash handler based on POSIX signals.\n// The APIs to handle Mach exceptions are not available to third-party\n// applications on tvOS.\nclass CrashHandler final : public CrashHandlerBase {\n public:\n  CrashHandler(const CrashHandler&) = delete;\n  CrashHandler& operator=(const CrashHandler&) = delete;\n\n  static CrashHandler* Get();\n\n  static void ResetForTesting();\n\n  uint64_t GetThreadIdForTesting();\n\n private:\n  CrashHandler();\n  ~CrashHandler() override;\n\n  bool DoInitialize() override;\n\n  // The signal handler installed at OS-level.\n  static void CatchAndReraiseSignal(int signo,\n                                    siginfo_t* siginfo,\n                                    void* context);\n\n  Signals::OldActions old_actions_ = {};\n  static CrashHandler* instance_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_CRASH_HANDLER_TVOS_H_\n"
  },
  {
    "path": "client/crash_report_database.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crash_report_database.h\"\n\n#include <sys/stat.h>\n\n#include \"base/logging.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/file/filesystem.h\"\n\nnamespace crashpad {\n\nnamespace {\nconstexpr base::FilePath::CharType kAttachmentsDirectory[] =\n    FILE_PATH_LITERAL(\"attachments\");\n\nbool AttachmentNameIsOK(const std::string& name) {\n  for (const char c : name) {\n    if (c != '_' && c != '-' && c != '.' && !isalnum(c))\n      return false;\n  }\n  return true;\n}\n}  // namespace\n\nCrashReportDatabase::Report::Report()\n    : uuid(),\n      file_path(),\n      id(),\n      creation_time(0),\n      uploaded(false),\n      last_upload_attempt_time(0),\n      upload_attempts(0),\n      upload_explicitly_requested(false),\n      total_size(0u) {}\n\nCrashReportDatabase::NewReport::NewReport()\n    : writer_(std::make_unique<FileWriter>()),\n      file_remover_(),\n      attachment_writers_(),\n      attachment_removers_(),\n      uuid_(),\n      database_() {}\n\nCrashReportDatabase::NewReport::~NewReport() = default;\n\nbool CrashReportDatabase::NewReport::Initialize(\n    CrashReportDatabase* database,\n    const base::FilePath& directory,\n    const base::FilePath::StringType& extension) {\n  database_ = database;\n\n  if (!uuid_.InitializeWithNew()) {\n    return false;\n  }\n\n#if BUILDFLAG(IS_WIN)\n  const std::wstring uuid_string = uuid_.ToWString();\n#else\n  const std::string uuid_string = uuid_.ToString();\n#endif\n\n  const base::FilePath path = directory.Append(uuid_string + extension);\n  if (!writer_->Open(\n          path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)) {\n    return false;\n  }\n  file_remover_.reset(path);\n  return true;\n}\n\nFileReaderInterface* CrashReportDatabase::NewReport::Reader() {\n  auto reader = std::make_unique<FileReader>();\n  if (!reader->Open(file_remover_.get())) {\n    return nullptr;\n  }\n  reader_ = std::move(reader);\n  return reader_.get();\n}\n\nFileWriter* CrashReportDatabase::NewReport::AddAttachment(\n    const std::string& name) {\n  if (!AttachmentNameIsOK(name)) {\n    LOG(ERROR) << \"invalid name for attachment \" << name;\n    return nullptr;\n  }\n  base::FilePath report_attachments_dir = database_->AttachmentsPath(uuid_);\n  if (!LoggingCreateDirectory(\n          report_attachments_dir, FilePermissions::kOwnerOnly, true)) {\n    return nullptr;\n  }\n#if BUILDFLAG(IS_WIN)\n  const std::wstring name_string = base::UTF8ToWide(name);\n#else\n  const std::string name_string = name;\n#endif\n  base::FilePath attachment_path = report_attachments_dir.Append(name_string);\n  auto writer = std::make_unique<FileWriter>();\n  if (!writer->Open(attachment_path,\n                    FileWriteMode::kCreateOrFail,\n                    FilePermissions::kOwnerOnly)) {\n    return nullptr;\n  }\n  attachment_writers_.emplace_back(std::move(writer));\n  attachment_removers_.emplace_back(ScopedRemoveFile(attachment_path));\n  return attachment_writers_.back().get();\n}\n\nvoid CrashReportDatabase::UploadReport::InitializeAttachments() {\n  base::FilePath report_attachments_dir = database_->AttachmentsPath(uuid);\n  DirectoryReader dir_reader;\n  if (!dir_reader.Open(report_attachments_dir)) {\n    return;\n  }\n\n  base::FilePath filename;\n  DirectoryReader::Result dir_result;\n  while ((dir_result = dir_reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath filepath(report_attachments_dir.Append(filename));\n    std::unique_ptr<FileReader> file_reader(std::make_unique<FileReader>());\n    if (!file_reader->Open(filepath)) {\n      continue;\n    }\n    attachment_readers_.emplace_back(std::move(file_reader));\n#if BUILDFLAG(IS_WIN)\n    const std::string name_string = base::WideToUTF8(filename.value());\n#else\n    const std::string name_string = filename.value();\n#endif\n    attachment_map_[name_string] = attachment_readers_.back().get();\n  }\n}\n\nCrashReportDatabase::UploadReport::UploadReport()\n    : Report(),\n      reader_(std::make_unique<FileReader>()),\n      database_(nullptr),\n      attachment_readers_(),\n      attachment_map_(),\n      report_metrics_(false) {}\n\nCrashReportDatabase::UploadReport::~UploadReport() {\n  if (database_) {\n    database_->RecordUploadAttempt(this, false, std::string());\n  }\n}\n\nbool CrashReportDatabase::UploadReport::Initialize(const base::FilePath& path,\n                                                   CrashReportDatabase* db) {\n  database_ = db;\n  InitializeAttachments();\n  return reader_->Open(path);\n}\n\nCrashReportDatabase::OperationStatus CrashReportDatabase::RecordUploadComplete(\n    std::unique_ptr<const UploadReport> report_in,\n    const std::string& id) {\n  UploadReport* report = const_cast<UploadReport*>(report_in.get());\n\n  report->database_ = nullptr;\n  return RecordUploadAttempt(report, true, id);\n}\n\nbase::FilePath CrashReportDatabase::AttachmentsPath(const UUID& uuid) {\n#if BUILDFLAG(IS_WIN)\n  const std::wstring uuid_string = uuid.ToWString();\n#else\n  const std::string uuid_string = uuid.ToString();\n#endif\n\n  return DatabasePath().Append(kAttachmentsDirectory).Append(uuid_string);\n}\n\nbase::FilePath CrashReportDatabase::AttachmentsRootPath() {\n  return DatabasePath().Append(kAttachmentsDirectory);\n}\n\nvoid CrashReportDatabase::RemoveAttachmentsByUUID(const UUID& uuid) {\n  base::FilePath report_attachment_dir = AttachmentsPath(uuid);\n  if (!IsDirectory(report_attachment_dir, /*allow_symlinks=*/false)) {\n    return;\n  }\n  DirectoryReader reader;\n  if (!reader.Open(report_attachment_dir)) {\n    return;\n  }\n\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath attachment_path(\n        report_attachment_dir.Append(filename));\n    LoggingRemoveFile(attachment_path);\n  }\n\n  LoggingRemoveDirectory(report_attachment_dir);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crash_report_database.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_\n#define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_\n\n#include <stdint.h>\n#include <time.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/file/scoped_remove_file.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\nclass Settings;\nclass SettingsReader;\n\n//! \\brief An interface for managing a collection of crash report files and\n//!     metadata associated with the crash reports.\n//!\n//! All Report objects that are returned by this class are logically const.\n//! They are snapshots of the database at the time the query was run, and the\n//! data returned is liable to change after the query is executed.\n//!\n//! The lifecycle of a crash report has three stages:\n//!\n//!   1. New: A crash report is created with PrepareNewCrashReport(), the\n//!      the client then writes the report, and then calls\n//!      FinishedWritingCrashReport() to make the report Pending.\n//!   2. Pending: The report has been written but has not been locally\n//!      processed, or it was has been brought back from 'Completed' state by\n//!      user request.\n//!   3. Completed: The report has been locally processed, either by uploading\n//!      it to a collection server and calling RecordUploadComplete(), or by\n//!      calling SkipReportUpload().\nclass CrashReportDatabase {\n public:\n  //! \\brief A crash report record.\n  //!\n  //! This represents the metadata for a crash report, as well as the location\n  //! of the report itself. A CrashReportDatabase maintains at least this\n  //! information.\n  struct Report {\n    Report();\n\n    //! A unique identifier by which this report will always be known to the\n    //! database.\n    UUID uuid;\n\n    //! The current location of the crash report on the client’s filesystem.\n    //! The location of a crash report may change over time, so the UUID should\n    //! be used as the canonical identifier.\n    base::FilePath file_path;\n\n    //! An identifier issued to this crash report by a collection server.\n    std::string id;\n\n    //! The time at which the report was generated.\n    time_t creation_time;\n\n    //! Whether this crash report was successfully uploaded to a collection\n    //! server.\n    bool uploaded;\n\n    //! The last timestamp at which an attempt was made to submit this crash\n    //! report to a collection server. If this is zero, then the report has\n    //! never been uploaded. If #uploaded is true, then this timestamp is the\n    //! time at which the report was uploaded, and no other attempts to upload\n    //! this report will be made.\n    time_t last_upload_attempt_time;\n\n    //! The number of times an attempt was made to submit this report to\n    //! a collection server. If this is more than zero, then\n    //! #last_upload_attempt_time will be set to the timestamp of the most\n    //! recent attempt.\n    int upload_attempts;\n\n    //! Whether this crash report was explicitly requested by user to be\n    //! uploaded. This can be true only if report is in the 'pending' state.\n    bool upload_explicitly_requested;\n\n    //! The total size in bytes taken by the report, including any potential\n    //! attachments.\n    uint64_t total_size;\n  };\n\n  //! \\brief A crash report that is in the process of being written.\n  //!\n  //! An instance of this class should be created via PrepareNewCrashReport().\n  class NewReport {\n   public:\n    NewReport();\n\n    NewReport(const NewReport&) = delete;\n    NewReport& operator=(const NewReport&) = delete;\n\n    ~NewReport();\n\n    //! \\brief An open FileWriter with which to write the report.\n    FileWriter* Writer() const { return writer_.get(); }\n\n    //! \\brief Returns a FileReaderInterface to the report, or `nullptr` with a\n    //!     message logged.\n    FileReaderInterface* Reader();\n\n    //! A unique identifier by which this report will always be known to the\n    //! database.\n    const UUID& ReportID() const { return uuid_; }\n\n    //! \\brief Adds an attachment to the report.\n    //!\n    //! \\param[in] name The key and name for the attachment, which will be\n    //!     included in the http upload. The attachment will not appear in the\n    //!     minidump report. \\a name should only use characters from the set\n    //!     `[a-zA-Z0-9._-]`.\n    //! \\return A FileWriter that the caller should use to write the contents of\n    //!     the attachment, or `nullptr` on failure with an error logged.\n    FileWriter* AddAttachment(const std::string& name);\n\n   private:\n    friend class CrashReportDatabaseGeneric;\n    friend class CrashReportDatabaseMac;\n    friend class CrashReportDatabaseWin;\n\n    bool Initialize(CrashReportDatabase* database,\n                    const base::FilePath& directory,\n                    const base::FilePath::StringType& extension);\n\n    std::unique_ptr<FileWriter> writer_;\n    std::unique_ptr<FileReader> reader_;\n    ScopedRemoveFile file_remover_;\n    std::vector<std::unique_ptr<FileWriter>> attachment_writers_;\n    std::vector<ScopedRemoveFile> attachment_removers_;\n    UUID uuid_;\n    CrashReportDatabase* database_;\n  };\n\n  //! \\brief A crash report that is in the process of being uploaded.\n  //!\n  //! An instance of this class should be created via GetReportForUploading().\n  class UploadReport : public Report {\n   public:\n    UploadReport();\n\n    UploadReport(const UploadReport&) = delete;\n    UploadReport& operator=(const UploadReport&) = delete;\n\n    virtual ~UploadReport();\n\n    //! \\brief An open FileReader with which to read the report.\n    FileReader* Reader() const { return reader_.get(); }\n\n    //! \\brief Obtains a mapping of names to file readers for any attachments\n    //!     for the report.\n    std::map<std::string, FileReader*> GetAttachments() const {\n      return attachment_map_;\n    }\n\n   private:\n    friend class CrashReportDatabase;\n    friend class CrashReportDatabaseGeneric;\n    friend class CrashReportDatabaseMac;\n    friend class CrashReportDatabaseWin;\n\n    bool Initialize(const base::FilePath& path, CrashReportDatabase* database);\n    void InitializeAttachments();\n\n    std::unique_ptr<FileReader> reader_;\n    CrashReportDatabase* database_;\n    std::vector<std::unique_ptr<FileReader>> attachment_readers_;\n    std::map<std::string, FileReader*> attachment_map_;\n    bool report_metrics_;\n  };\n\n  //! \\brief The result code for operations performed on a database.\n  enum OperationStatus {\n    //! \\brief No error occurred.\n    kNoError = 0,\n\n    //! \\brief The report that was requested could not be located.\n    //!\n    //! This may occur when the report is present in the database but not in a\n    //! state appropriate for the requested operation, for example, if\n    //! GetReportForUploading() is called to obtain report that’s already in the\n    //! completed state.\n    kReportNotFound,\n\n    //! \\brief An error occured while performing a file operation on a crash\n    //!     report.\n    //!\n    //! A database is responsible for managing both the metadata about a report\n    //! and the actual crash report itself. This error is returned when an\n    //! error occurred when managing the report file. Additional information\n    //! will be logged.\n    kFileSystemError,\n\n    //! \\brief An error occured while recording metadata for a crash report or\n    //!     database-wide settings.\n    //!\n    //! A database is responsible for managing both the metadata about a report\n    //! and the actual crash report itself. This error is returned when an\n    //! error occurred when managing the metadata about a crash report or\n    //! database-wide settings. Additional information will be logged.\n    kDatabaseError,\n\n    //! \\brief The operation could not be completed because a concurrent\n    //!     operation affecting the report is occurring.\n    kBusyError,\n\n    //! \\brief The report cannot be uploaded by user request as it has already\n    //!     been uploaded.\n    kCannotRequestUpload,\n  };\n\n  CrashReportDatabase(const CrashReportDatabase&) = delete;\n  CrashReportDatabase& operator=(const CrashReportDatabase&) = delete;\n\n  virtual ~CrashReportDatabase() {}\n\n  //! \\brief Opens a database of crash reports, possibly creating it.\n  //!\n  //! \\param[in] path A path to the database to be created or opened. If the\n  //!     database does not yet exist, it will be created if possible. Note that\n  //!     for databases implemented as directory structures, existence refers\n  //!     solely to the outermost directory.\n  //!\n  //! \\return A database object on success, `nullptr` on failure with an error\n  //!     logged.\n  //!\n  //! \\sa InitializeWithoutCreating\n  static std::unique_ptr<CrashReportDatabase> Initialize(\n      const base::FilePath& path);\n\n  //! \\brief Opens an existing database of crash reports.\n  //!\n  //! \\param[in] path A path to the database to be opened. If the database does\n  //!     not yet exist, it will not be created. Note that for databases\n  //!     implemented as directory structures, existence refers solely to the\n  //!     outermost directory. On such databases, as long as the outermost\n  //!     directory is present, this method will create the inner structure.\n  //!\n  //! \\return A database object on success, `nullptr` on failure with an error\n  //!     logged.\n  //!\n  //! \\sa Initialize\n  static std::unique_ptr<CrashReportDatabase> InitializeWithoutCreating(\n      const base::FilePath& path);\n\n  //! \\brief Given a database path, return a read-only view of its settings.\n  //!\n  //! \\param[in] path A path to the database. If the database does not exist, or\n  //!     the settings file does not exist, the returned reader will fail its\n  //!     read methods.\n  //!\n  //! \\return A SettingsReader.\n  static std::unique_ptr<SettingsReader> GetSettingsReaderForDatabasePath(\n      const base::FilePath& path);\n\n  //! \\brief Returns the Settings object for this database.\n  //!\n  //! \\return A weak pointer to the Settings object, which is owned by the\n  //!     database.\n  virtual Settings* GetSettings() = 0;\n\n  //! \\brief Creates a record of a new crash report.\n  //!\n  //! Callers should write the crash report using the FileWriter provided.\n  //! Callers should then call FinishedWritingCrashReport() to complete report\n  //! creation. If an error is encountered while writing the crash report, no\n  //! special action needs to be taken. If FinishedWritingCrashReport() is not\n  //! called, the report will be removed from the database when \\a report is\n  //! destroyed.\n  //!\n  //! \\param[out] report A NewReport object containing a FileWriter with which\n  //!     to write the report data. Only valid if this returns #kNoError.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus PrepareNewCrashReport(\n      std::unique_ptr<NewReport>* report) = 0;\n\n  //! \\brief Informs the database that a crash report has been successfully\n  //!     written.\n  //!\n  //! \\param[in] report A NewReport obtained with PrepareNewCrashReport(). The\n  //!     NewReport object will be invalidated as part of this call.\n  //! \\param[out] uuid The UUID of this crash report.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus FinishedWritingCrashReport(\n      std::unique_ptr<NewReport> report,\n      UUID* uuid) = 0;\n\n  //! \\brief Returns the crash report record for the unique identifier.\n  //!\n  //! \\param[in] uuid The crash report record unique identifier.\n  //! \\param[out] report A crash report record. Only valid if this returns\n  //!     #kNoError.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus LookUpCrashReport(const UUID& uuid,\n                                            Report* report) = 0;\n\n  //! \\brief Returns a list of crash report records that have not been uploaded.\n  //!\n  //! \\param[out] reports A list of crash report record objects. This must be\n  //!     empty on entry. Only valid if this returns #kNoError.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus GetPendingReports(std::vector<Report>* reports) = 0;\n\n  //! \\brief Returns a list of crash report records that have been completed,\n  //!     either by being uploaded or by skipping upload.\n  //!\n  //! \\param[out] reports A list of crash report record objects. This must be\n  //!     empty on entry. Only valid if this returns #kNoError.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus GetCompletedReports(std::vector<Report>* reports) = 0;\n\n  //! \\brief Obtains and locks a report object for uploading to a collection\n  //!     server. On iOS the file lock is released and mutual-exclusion is kept\n  //!     via a file attribute.\n  //!\n  //! Callers should upload the crash report using the FileReader provided.\n  //! Callers should then call RecordUploadComplete() to record a successful\n  //! upload. If RecordUploadComplete() is not called, the upload attempt will\n  //! be recorded as unsuccessful and the report lock released when \\a report is\n  //! destroyed.\n  //!\n  //! On iOS, holding a lock during a slow upload can lead to watchdog kills if\n  //! the app is suspended mid-upload. Instead, if the client can obtain the\n  //! lock, the database sets a lock-time file attribute and releases the lock.\n  //! The attribute is cleared when the upload is completed. The lock-time\n  //! attribute can be used to prevent file access from other processes, or to\n  //! discard reports that likely were terminated mid-upload.\n  //!\n  //! \\param[in] uuid The unique identifier for the crash report record.\n  //! \\param[out] report A crash report record for the report to be uploaded.\n  //!     Only valid if this returns #kNoError.\n  //! \\param[in] report_metrics If `false`, metrics will not be recorded for\n  //!     this upload attempt when RecordUploadComplete() is called or \\a report\n  //!     is destroyed. Metadata for the upload attempt will still be recorded\n  //!     in the database.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus GetReportForUploading(\n      const UUID& uuid,\n      std::unique_ptr<const UploadReport>* report,\n      bool report_metrics = true) = 0;\n\n  //! \\brief Records a successful upload for a report and updates the last\n  //!     upload attempt time as returned by\n  //!     Settings::GetLastUploadAttemptTime().\n  //!\n  //! \\param[in] report A UploadReport object obtained from\n  //!     GetReportForUploading(). The UploadReport object will be invalidated\n  //!     and the report unlocked as part of this call.\n  //! \\param[in] id The possibly empty identifier assigned to this crash report\n  //!     by the collection server.\n  //!\n  //! \\return The operation status code.\n  OperationStatus RecordUploadComplete(\n      std::unique_ptr<const UploadReport> report,\n      const std::string& id);\n\n  //! \\brief Moves a report from the pending state to the completed state, but\n  //!     without the report being uploaded.\n  //!\n  //! This can be used if the user has disabled crash report collection, but\n  //! crash generation is still enabled in the product.\n  //!\n  //! \\param[in] uuid The unique identifier for the crash report record.\n  //! \\param[in] reason The reason the report upload is being skipped for\n  //!     metrics tracking purposes.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus SkipReportUpload(\n      const UUID& uuid,\n      Metrics::CrashSkippedReason reason) = 0;\n\n  //! \\brief Deletes a crash report file and its associated metadata.\n  //!\n  //! \\param[in] uuid The UUID of the report to delete.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus DeleteReport(const UUID& uuid) = 0;\n\n  //! \\brief Marks a crash report as explicitly requested to be uploaded by the\n  //!     user and moves it to 'pending' state.\n  //!\n  //! \\param[in] uuid The unique identifier for the crash report record.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus RequestUpload(const UUID& uuid) = 0;\n\n  //! \\brief Cleans the database of expired lockfiles, metadata without report\n  //!     files, report files without metadata, and attachments without report\n  //!     files.\n  //!\n  //! As the macOS implementation does not use  lock or metadata files, the\n  //! cleaning is limited to attachments without report files.\n  //!\n  //! \\param[in] lockfile_ttl The number of seconds at which lockfiles or new\n  //!     report files are considered expired.\n  //! \\return The number of reports cleaned.\n  virtual int CleanDatabase(time_t lockfile_ttl) { return 0; }\n\n protected:\n  CrashReportDatabase() = default;\n\n  //! \\brief The path to the database passed to Initialize.\n  //!\n  //! \\return The filepath of the database;\n  virtual base::FilePath DatabasePath() = 0;\n\n  //! \\brief Build a filepath for the root attachments directory.\n  //!\n  //! \\return The filepath to the attachments directory.\n  base::FilePath AttachmentsRootPath();\n\n  //! \\brief  Build a filepath for the directory for the report to hold\n  //!     attachments.\n  //!\n  //! \\param[in] uuid The unique identifier for the crash report record.\n  //!\n  //! \\return The filepath to the report attachments directory.\n  base::FilePath AttachmentsPath(const UUID& uuid);\n\n  //! \\brief Attempts to remove any attachments associated with the given\n  //!     report UUID. There may not be any, so failing is not an error.\n  //!\n  //! \\param[in] uuid The unique identifier for the crash report record.\n  void RemoveAttachmentsByUUID(const UUID& uuid);\n\n private:\n  //! \\brief Adjusts a crash report record’s metadata to account for an upload\n  //!     attempt, and updates the last upload attempt time as returned by\n  //!     Settings::GetLastUploadAttemptTime().\n  //!\n  //! \\param[in] report The report object obtained from\n  //!     GetReportForUploading().\n  //! \\param[in] successful Whether the upload attempt was successful.\n  //! \\param[in] id The identifier assigned to this crash report by the\n  //!     collection server. Must be empty if \\a successful is `false`; may be\n  //!     empty if it is `true`.\n  //!\n  //! \\return The operation status code.\n  virtual OperationStatus RecordUploadAttempt(UploadReport* report,\n                                              bool successful,\n                                              const std::string& id) = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_\n"
  },
  {
    "path": "client/crash_report_database_generic.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crash_report_database.h\"\n\n#include <stdint.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <mutex>\n#include <tuple>\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"client/settings.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/memory_sanitizer.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nbase::FilePath ReplaceFinalExtension(\n    const base::FilePath& path,\n    const base::FilePath::StringType extension) {\n  return base::FilePath(path.RemoveFinalExtension().value() + extension);\n}\n\nUUID UUIDFromReportPath(const base::FilePath& path) {\n  UUID uuid;\n  uuid.InitializeFromString(path.RemoveFinalExtension().BaseName().value());\n  return uuid;\n}\n\nusing OperationStatus = CrashReportDatabase::OperationStatus;\n\nconstexpr base::FilePath::CharType kSettings[] =\n    FILE_PATH_LITERAL(\"settings.dat\");\n\nconstexpr base::FilePath::CharType kCrashReportExtension[] =\n    FILE_PATH_LITERAL(\".dmp\");\nconstexpr base::FilePath::CharType kMetadataExtension[] =\n    FILE_PATH_LITERAL(\".meta\");\nconstexpr base::FilePath::CharType kLockExtension[] =\n    FILE_PATH_LITERAL(\".lock\");\n\nconstexpr base::FilePath::CharType kNewDirectory[] = FILE_PATH_LITERAL(\"new\");\nconstexpr base::FilePath::CharType kPendingDirectory[] =\n    FILE_PATH_LITERAL(\"pending\");\nconstexpr base::FilePath::CharType kCompletedDirectory[] =\n    FILE_PATH_LITERAL(\"completed\");\n\nconstexpr const base::FilePath::CharType* kReportDirectories[] = {\n    kNewDirectory,\n    kPendingDirectory,\n    kCompletedDirectory,\n};\n\nenum {\n  //! \\brief Corresponds to uploaded bit of the report state.\n  kAttributeUploaded = 1 << 0,\n\n  //! \\brief Corresponds to upload_explicity_requested bit of the report state.\n  kAttributeUploadExplicitlyRequested = 1 << 1,\n};\n\nstruct ReportMetadata {\n  static constexpr int32_t kVersion = 1;\n\n  int32_t version = kVersion;\n  int32_t upload_attempts = 0;\n  int64_t last_upload_attempt_time = 0;\n  time_t creation_time = 0;\n  uint8_t attributes = 0;\n};\n\n// A lock held while using database resources.\nclass ScopedLockFile {\n public:\n  ScopedLockFile() = default;\n\n  ScopedLockFile(const ScopedLockFile&) = delete;\n  ScopedLockFile& operator=(const ScopedLockFile&) = delete;\n\n  ~ScopedLockFile() = default;\n\n  ScopedLockFile& operator=(ScopedLockFile&& other) {\n    lock_file_.reset(other.lock_file_.release());\n    return *this;\n  }\n\n  // Attempt to acquire a lock for the report at report_path.\n  // Return `true` on success, otherwise `false`.\n  bool ResetAcquire(const base::FilePath& report_path) {\n    lock_file_.reset();\n\n    base::FilePath lock_path(report_path.RemoveFinalExtension().value() +\n                             kLockExtension);\n    ScopedFileHandle lock_fd(LoggingOpenFileForWrite(\n        lock_path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));\n    if (!lock_fd.is_valid()) {\n      return false;\n    }\n    lock_file_.reset(lock_path);\n\n    time_t timestamp = time(nullptr);\n    if (!LoggingWriteFile(lock_fd.get(), &timestamp, sizeof(timestamp))) {\n      return false;\n    }\n\n    return true;\n  }\n\n  // Returns `true` if the lock is held.\n  bool is_valid() const { return lock_file_.is_valid(); }\n\n  // Returns `true` if the lockfile at lock_path has expired.\n  static bool IsExpired(const base::FilePath& lock_path, time_t lockfile_ttl) {\n    time_t now = time(nullptr);\n\n    timespec filetime;\n    if (FileModificationTime(lock_path, &filetime) &&\n        filetime.tv_sec > now + lockfile_ttl) {\n      return false;\n    }\n\n    ScopedFileHandle lock_fd(LoggingOpenFileForReadAndWrite(\n        lock_path, FileWriteMode::kReuseOrFail, FilePermissions::kOwnerOnly));\n    if (!lock_fd.is_valid()) {\n      return false;\n    }\n\n    time_t timestamp;\n    if (!LoggingReadFileExactly(lock_fd.get(), &timestamp, sizeof(timestamp))) {\n      return false;\n    }\n\n    return now >= timestamp + lockfile_ttl;\n  }\n\n private:\n  ScopedRemoveFile lock_file_;\n};\n}  // namespace\n\nclass CrashReportDatabaseGeneric : public CrashReportDatabase {\n public:\n  explicit CrashReportDatabaseGeneric(const base::FilePath& path);\n\n  CrashReportDatabaseGeneric(const CrashReportDatabaseGeneric&) = delete;\n  CrashReportDatabaseGeneric& operator=(const CrashReportDatabaseGeneric&) =\n      delete;\n\n  ~CrashReportDatabaseGeneric() override;\n\n  bool Initialize(bool may_create);\n\n  // CrashReportDatabase:\n  Settings* GetSettings() override;\n  OperationStatus PrepareNewCrashReport(\n      std::unique_ptr<NewReport>* report) override;\n  OperationStatus FinishedWritingCrashReport(std::unique_ptr<NewReport> report,\n                                             UUID* uuid) override;\n  OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override;\n  OperationStatus GetPendingReports(std::vector<Report>* reports) override;\n  OperationStatus GetCompletedReports(std::vector<Report>* reports) override;\n  OperationStatus GetReportForUploading(\n      const UUID& uuid,\n      std::unique_ptr<const UploadReport>* report,\n      bool report_metrics) override;\n  OperationStatus SkipReportUpload(const UUID& uuid,\n                                   Metrics::CrashSkippedReason reason) override;\n  OperationStatus DeleteReport(const UUID& uuid) override;\n  OperationStatus RequestUpload(const UUID& uuid) override;\n  int CleanDatabase(time_t lockfile_ttl) override;\n  base::FilePath DatabasePath() override;\n\n private:\n  struct LockfileUploadReport : public UploadReport {\n    ScopedLockFile lock_file;\n  };\n\n  enum ReportState : int32_t {\n    kUninitialized = -1,\n\n    // Being created by a caller of PrepareNewCrashReport().\n    kNew,\n\n    // Created by FinishedWritingCrashReport(), but not yet uploaded.\n    kPending,\n\n    // Upload completed or skipped.\n    kCompleted,\n\n    // Specifies either kPending or kCompleted.\n    kSearchable,\n  };\n\n  // CrashReportDatabase:\n  OperationStatus RecordUploadAttempt(UploadReport* report,\n                                      bool successful,\n                                      const std::string& id) override;\n\n  // Builds a filepath for the report with the specified uuid and state.\n  base::FilePath ReportPath(const UUID& uuid, ReportState state);\n\n  // Locates the report with id uuid and returns its file path in path and a\n  // lock for the report in lock_file. This method succeeds as long as the\n  // report file exists and the lock can be acquired. No validation is done on\n  // the existence or content of the metadata file.\n  OperationStatus LocateAndLockReport(const UUID& uuid,\n                                      ReportState state,\n                                      base::FilePath* path,\n                                      ScopedLockFile* lock_file);\n\n  // Locates, locks, and reads the metadata for the report with the specified\n  // uuid and state. This method will fail and may remove reports if invalid\n  // metadata is detected. state may be kPending, kCompleted, or kSearchable.\n  OperationStatus CheckoutReport(const UUID& uuid,\n                                 ReportState state,\n                                 base::FilePath* path,\n                                 ScopedLockFile* lock_file,\n                                 Report* report);\n\n  // Reads metadata for all reports in state and returns it in reports.\n  OperationStatus ReportsInState(ReportState state,\n                                 std::vector<Report>* reports);\n\n  // Cleans lone metadata, reports, or expired locks in a particular state.\n  int CleanReportsInState(ReportState state, time_t lockfile_ttl);\n\n  // Cleans any attachments that have no associated report in any state.\n  void CleanOrphanedAttachments();\n\n  // Reads the metadata for a report from path and returns it in report.\n  bool ReadMetadata(const base::FilePath& path, Report* report);\n\n  // Wraps ReadMetadata and removes the report from the database on failure.\n  bool CleaningReadMetadata(const base::FilePath& path, Report* report);\n\n  // Writes metadata for a new report to the filesystem at path.\n  static bool WriteNewMetadata(const base::FilePath& path);\n\n  // Writes the metadata for report to the filesystem at path.\n  static bool WriteMetadata(const base::FilePath& path, const Report& report);\n\n  Settings& SettingsInternal() {\n    std::call_once(settings_init_, [this]() { settings_.Initialize(); });\n    return settings_;\n  }\n\n  const base::FilePath base_dir_;\n  Settings settings_;\n  std::once_flag settings_init_;\n  InitializationStateDcheck initialized_;\n};\n\nCrashReportDatabaseGeneric::CrashReportDatabaseGeneric(\n    const base::FilePath& path)\n    : base_dir_(path), settings_(path.Append(kSettings)) {}\n\nCrashReportDatabaseGeneric::~CrashReportDatabaseGeneric() = default;\n\nbool CrashReportDatabaseGeneric::Initialize(bool may_create) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!IsDirectory(base_dir_, true) &&\n      !(may_create &&\n        LoggingCreateDirectory(base_dir_, FilePermissions::kOwnerOnly, true))) {\n    return false;\n  }\n\n  for (const base::FilePath::CharType* subdir : kReportDirectories) {\n    if (!LoggingCreateDirectory(\n            base_dir_.Append(subdir), FilePermissions::kOwnerOnly, true)) {\n      return false;\n    }\n  }\n\n  if (!LoggingCreateDirectory(\n          AttachmentsRootPath(), FilePermissions::kOwnerOnly, true)) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbase::FilePath CrashReportDatabaseGeneric::DatabasePath() {\n  return base_dir_;\n}\n\nSettings* CrashReportDatabaseGeneric::GetSettings() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &SettingsInternal();\n}\n\nOperationStatus CrashReportDatabaseGeneric::PrepareNewCrashReport(\n    std::unique_ptr<NewReport>* report) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  auto new_report = std::make_unique<NewReport>();\n  if (!new_report->Initialize(\n          this, base_dir_.Append(kNewDirectory), kCrashReportExtension)) {\n    return kFileSystemError;\n  }\n\n  report->reset(new_report.release());\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseGeneric::FinishedWritingCrashReport(\n    std::unique_ptr<NewReport> report,\n    UUID* uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  base::FilePath path = ReportPath(report->ReportID(), kPending);\n  ScopedLockFile lock_file;\n  if (!lock_file.ResetAcquire(path)) {\n    return kBusyError;\n  }\n\n  if (!WriteNewMetadata(ReplaceFinalExtension(path, kMetadataExtension))) {\n    return kDatabaseError;\n  }\n\n  FileOffset size = report->Writer()->Seek(0, SEEK_END);\n\n  report->Writer()->Close();\n  if (!MoveFileOrDirectory(report->file_remover_.get(), path)) {\n    return kFileSystemError;\n  }\n  // We've moved the report to pending, so it no longer needs to be removed.\n  std::ignore = report->file_remover_.release();\n\n  // Close all the attachments and disarm their removers too.\n  for (auto& writer : report->attachment_writers_) {\n    writer->Close();\n  }\n  for (auto& remover : report->attachment_removers_) {\n    std::ignore = remover.release();\n  }\n\n  *uuid = report->ReportID();\n\n  Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);\n  Metrics::CrashReportSize(size);\n\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseGeneric::LookUpCrashReport(const UUID& uuid,\n                                                              Report* report) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  ScopedLockFile lock_file;\n  base::FilePath path;\n  return CheckoutReport(uuid, kSearchable, &path, &lock_file, report);\n}\n\nOperationStatus CrashReportDatabaseGeneric::GetPendingReports(\n    std::vector<Report>* reports) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ReportsInState(kPending, reports);\n}\n\nOperationStatus CrashReportDatabaseGeneric::GetCompletedReports(\n    std::vector<Report>* reports) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ReportsInState(kCompleted, reports);\n}\n\nOperationStatus CrashReportDatabaseGeneric::GetReportForUploading(\n    const UUID& uuid,\n    std::unique_ptr<const UploadReport>* report,\n    bool report_metrics) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  auto upload_report = std::make_unique<LockfileUploadReport>();\n\n  base::FilePath path;\n  OperationStatus os = CheckoutReport(\n      uuid, kPending, &path, &upload_report->lock_file, upload_report.get());\n  if (os != kNoError) {\n    return os;\n  }\n\n  if (!upload_report->Initialize(path, this)) {\n    return kFileSystemError;\n  }\n  upload_report->report_metrics_ = report_metrics;\n\n  report->reset(upload_report.release());\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseGeneric::SkipReportUpload(\n    const UUID& uuid,\n    Metrics::CrashSkippedReason reason) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  Metrics::CrashUploadSkipped(reason);\n\n  base::FilePath path;\n  ScopedLockFile lock_file;\n  Report report;\n  OperationStatus os =\n      CheckoutReport(uuid, kPending, &path, &lock_file, &report);\n  if (os != kNoError) {\n    return os;\n  }\n\n  base::FilePath completed_path(ReportPath(uuid, kCompleted));\n  ScopedLockFile completed_lock_file;\n  if (!completed_lock_file.ResetAcquire(completed_path)) {\n    return kBusyError;\n  }\n\n  report.upload_explicitly_requested = false;\n  if (!WriteMetadata(completed_path, report)) {\n    return kDatabaseError;\n  }\n\n  if (!MoveFileOrDirectory(path, completed_path)) {\n    return kFileSystemError;\n  }\n\n  if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) {\n    return kDatabaseError;\n  }\n\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseGeneric::DeleteReport(const UUID& uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  base::FilePath path;\n  ScopedLockFile lock_file;\n  OperationStatus os =\n      LocateAndLockReport(uuid, kSearchable, &path, &lock_file);\n  if (os != kNoError) {\n    return os;\n  }\n\n  if (!LoggingRemoveFile(path)) {\n    return kFileSystemError;\n  }\n\n  if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) {\n    return kDatabaseError;\n  }\n\n  RemoveAttachmentsByUUID(uuid);\n\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseGeneric::RequestUpload(const UUID& uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  base::FilePath path;\n  ScopedLockFile lock_file;\n  Report report;\n  OperationStatus os =\n      CheckoutReport(uuid, kSearchable, &path, &lock_file, &report);\n  if (os != kNoError) {\n    return os;\n  }\n\n  if (report.uploaded) {\n    return kCannotRequestUpload;\n  }\n\n  report.upload_explicitly_requested = true;\n  base::FilePath pending_path = ReportPath(uuid, kPending);\n  if (!MoveFileOrDirectory(path, pending_path)) {\n    return kFileSystemError;\n  }\n\n  if (!WriteMetadata(pending_path, report)) {\n    return kDatabaseError;\n  }\n\n  if (pending_path != path) {\n    if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) {\n      return kDatabaseError;\n    }\n  }\n\n  Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated);\n  return kNoError;\n}\n\nint CrashReportDatabaseGeneric::CleanDatabase(time_t lockfile_ttl) {\n  int removed = 0;\n  time_t now = time(nullptr);\n\n  DirectoryReader reader;\n  const base::FilePath new_dir(base_dir_.Append(kNewDirectory));\n  if (reader.Open(new_dir)) {\n    base::FilePath filename;\n    DirectoryReader::Result result;\n    while ((result = reader.NextFile(&filename)) ==\n           DirectoryReader::Result::kSuccess) {\n      const base::FilePath filepath(new_dir.Append(filename));\n      timespec filetime;\n      if (!FileModificationTime(filepath, &filetime)) {\n        continue;\n      }\n      if (filetime.tv_sec <= now - lockfile_ttl) {\n        if (LoggingRemoveFile(filepath)) {\n          ++removed;\n        }\n      }\n    }\n  }\n\n  removed += CleanReportsInState(kPending, lockfile_ttl);\n  removed += CleanReportsInState(kCompleted, lockfile_ttl);\n  CleanOrphanedAttachments();\n#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n  base::FilePath settings_path(kSettings);\n  if (Settings::IsLockExpired(settings_path, lockfile_ttl)) {\n    base::FilePath lockfile_path(settings_path.value() +\n                                 Settings::kLockfileExtension);\n    if (LoggingRemoveFile(lockfile_path)) {\n      ++removed;\n    }\n  }\n#endif  // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n  return removed;\n}\n\nOperationStatus CrashReportDatabaseGeneric::RecordUploadAttempt(\n    UploadReport* report,\n    bool successful,\n    const std::string& id) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (report->report_metrics_) {\n    Metrics::CrashUploadAttempted(successful);\n  }\n  time_t now = time(nullptr);\n\n  report->id = id;\n  report->uploaded = successful;\n  report->last_upload_attempt_time = now;\n  ++report->upload_attempts;\n\n  base::FilePath report_path(report->file_path);\n\n  ScopedLockFile lock_file;\n  if (successful) {\n    report->upload_explicitly_requested = false;\n\n    base::FilePath completed_report_path = ReportPath(report->uuid, kCompleted);\n\n    if (!lock_file.ResetAcquire(completed_report_path)) {\n      return kBusyError;\n    }\n\n    report->Reader()->Close();\n    if (!MoveFileOrDirectory(report_path, completed_report_path)) {\n      return kFileSystemError;\n    }\n\n    LoggingRemoveFile(ReplaceFinalExtension(report_path, kMetadataExtension));\n    report_path = completed_report_path;\n  }\n\n  if (!WriteMetadata(report_path, *report)) {\n    return kDatabaseError;\n  }\n\n  if (!SettingsInternal().SetLastUploadAttemptTime(now)) {\n    return kDatabaseError;\n  }\n\n  return kNoError;\n}\n\nbase::FilePath CrashReportDatabaseGeneric::ReportPath(const UUID& uuid,\n                                                      ReportState state) {\n  DCHECK_NE(state, kUninitialized);\n  DCHECK_NE(state, kSearchable);\n\n#if BUILDFLAG(IS_WIN)\n  const std::wstring uuid_string = uuid.ToWString();\n#else\n  const std::string uuid_string = uuid.ToString();\n#endif\n\n  return base_dir_.Append(kReportDirectories[state])\n      .Append(uuid_string + kCrashReportExtension);\n}\n\nOperationStatus CrashReportDatabaseGeneric::LocateAndLockReport(\n    const UUID& uuid,\n    ReportState desired_state,\n    base::FilePath* path,\n    ScopedLockFile* lock_file) {\n  std::vector<ReportState> searchable_states;\n  if (desired_state == kSearchable) {\n    searchable_states.push_back(kPending);\n    searchable_states.push_back(kCompleted);\n  } else {\n    DCHECK(desired_state == kPending || desired_state == kCompleted);\n    searchable_states.push_back(desired_state);\n  }\n\n  for (const ReportState state : searchable_states) {\n    base::FilePath local_path(ReportPath(uuid, state));\n    ScopedLockFile local_lock;\n    if (!local_lock.ResetAcquire(local_path)) {\n      return kBusyError;\n    }\n\n    if (!IsRegularFile(local_path)) {\n      continue;\n    }\n\n    *path = local_path;\n    *lock_file = std::move(local_lock);\n    return kNoError;\n  }\n\n  return kReportNotFound;\n}\n\nOperationStatus CrashReportDatabaseGeneric::CheckoutReport(\n    const UUID& uuid,\n    ReportState state,\n    base::FilePath* path,\n    ScopedLockFile* lock_file,\n    Report* report) {\n  ScopedLockFile local_lock;\n  base::FilePath local_path;\n  OperationStatus os =\n      LocateAndLockReport(uuid, state, &local_path, &local_lock);\n  if (os != kNoError) {\n    return os;\n  }\n\n  if (!CleaningReadMetadata(local_path, report)) {\n    return kDatabaseError;\n  }\n\n  *path = local_path;\n  *lock_file = std::move(local_lock);\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseGeneric::ReportsInState(\n    ReportState state,\n    std::vector<Report>* reports) {\n  DCHECK(reports->empty());\n  DCHECK_NE(state, kUninitialized);\n  DCHECK_NE(state, kSearchable);\n  DCHECK_NE(state, kNew);\n\n  const base::FilePath dir_path(base_dir_.Append(kReportDirectories[state]));\n  DirectoryReader reader;\n  if (!reader.Open(dir_path)) {\n    return kDatabaseError;\n  }\n\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath::StringType extension(filename.FinalExtension());\n    if (extension.compare(kCrashReportExtension) != 0) {\n      continue;\n    }\n\n    const base::FilePath filepath(dir_path.Append(filename));\n    ScopedLockFile lock_file;\n    if (!lock_file.ResetAcquire(filepath)) {\n      continue;\n    }\n\n    Report report;\n    if (!CleaningReadMetadata(filepath, &report)) {\n      continue;\n    }\n    reports->push_back(report);\n    reports->back().file_path = filepath;\n  }\n  return kNoError;\n}\n\nint CrashReportDatabaseGeneric::CleanReportsInState(ReportState state,\n                                                    time_t lockfile_ttl) {\n  const base::FilePath dir_path(base_dir_.Append(kReportDirectories[state]));\n  DirectoryReader reader;\n  if (!reader.Open(dir_path)) {\n    return 0;\n  }\n\n  int removed = 0;\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath::StringType extension(filename.FinalExtension());\n    const base::FilePath filepath(dir_path.Append(filename));\n\n    // Remove any report files without metadata.\n    if (extension.compare(kCrashReportExtension) == 0) {\n      const base::FilePath metadata_path(\n          ReplaceFinalExtension(filepath, kMetadataExtension));\n      ScopedLockFile report_lock;\n      if (report_lock.ResetAcquire(filepath) && !IsRegularFile(metadata_path) &&\n          LoggingRemoveFile(filepath)) {\n        ++removed;\n        RemoveAttachmentsByUUID(UUIDFromReportPath(filepath));\n      }\n      continue;\n    }\n\n    // Remove any metadata files without report files.\n    if (extension.compare(kMetadataExtension) == 0) {\n      const base::FilePath report_path(\n          ReplaceFinalExtension(filepath, kCrashReportExtension));\n      ScopedLockFile report_lock;\n      if (report_lock.ResetAcquire(report_path) &&\n          !IsRegularFile(report_path) && LoggingRemoveFile(filepath)) {\n        ++removed;\n        RemoveAttachmentsByUUID(UUIDFromReportPath(filepath));\n      }\n      continue;\n    }\n\n    // Remove any expired locks only if we can remove the report and metadata.\n    if (extension.compare(kLockExtension) == 0 &&\n        ScopedLockFile::IsExpired(filepath, lockfile_ttl)) {\n      const base::FilePath no_ext(filepath.RemoveFinalExtension());\n      const base::FilePath report_path(no_ext.value() + kCrashReportExtension);\n      const base::FilePath metadata_path(no_ext.value() + kMetadataExtension);\n      if ((IsRegularFile(report_path) && !LoggingRemoveFile(report_path)) ||\n          (IsRegularFile(metadata_path) && !LoggingRemoveFile(metadata_path))) {\n        continue;\n      }\n\n      if (LoggingRemoveFile(filepath)) {\n        ++removed;\n        RemoveAttachmentsByUUID(UUIDFromReportPath(filepath));\n      }\n      continue;\n    }\n  }\n\n  return removed;\n}\n\nvoid CrashReportDatabaseGeneric::CleanOrphanedAttachments() {\n  base::FilePath root_attachments_dir(AttachmentsRootPath());\n  DirectoryReader reader;\n  if (!reader.Open(root_attachments_dir)) {\n    return;\n  }\n\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath report_attachment_dir(\n        root_attachments_dir.Append(filename));\n    if (IsDirectory(report_attachment_dir, false)) {\n      UUID uuid;\n      if (!uuid.InitializeFromString(filename.value())) {\n        LOG(ERROR) << \"unexpected attachment dir name \" << filename.value();\n        continue;\n      }\n\n      // Check to see if the report is being created in \"new\".\n      base::FilePath new_dir_path =\n          base_dir_.Append(kNewDirectory)\n              .Append(uuid.ToString() + kCrashReportExtension);\n      if (IsRegularFile(new_dir_path)) {\n        continue;\n      }\n\n      // Check to see if the report is in \"pending\" or \"completed\".\n      ScopedLockFile local_lock;\n      base::FilePath local_path;\n      OperationStatus os =\n          LocateAndLockReport(uuid, kSearchable, &local_path, &local_lock);\n      if (os != kReportNotFound) {\n        continue;\n      }\n\n      // Couldn't find a report, assume these attachments are orphaned.\n      RemoveAttachmentsByUUID(uuid);\n    }\n  }\n}\n\nbool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,\n                                              Report* report) {\n  const base::FilePath metadata_path(\n      ReplaceFinalExtension(path, kMetadataExtension));\n\n  ScopedFileHandle handle(LoggingOpenFileForRead(metadata_path));\n  if (!handle.is_valid()) {\n    return false;\n  }\n\n  UUID uuid;\n  if (!uuid.InitializeFromString(\n          path.BaseName().RemoveFinalExtension().value())) {\n    LOG(ERROR) << \"Couldn't interpret report uuid\";\n    return false;\n  }\n\n  ReportMetadata metadata;\n  if (!LoggingReadFileExactly(handle.get(), &metadata, sizeof(metadata))) {\n    return false;\n  }\n\n  if (metadata.version != ReportMetadata::kVersion) {\n    LOG(ERROR) << \"metadata version mismatch\";\n    return false;\n  }\n\n  if (!LoggingReadToEOF(handle.get(), &report->id)) {\n    return false;\n  }\n\n  // Seed the total size with the main report size and then add the sizes of any\n  // potential attachments.\n  uint64_t total_size = GetFileSize(path);\n  total_size += GetDirectorySize(AttachmentsPath(uuid));\n\n  report->uuid = uuid;\n  report->upload_attempts = metadata.upload_attempts;\n  report->last_upload_attempt_time = metadata.last_upload_attempt_time;\n  report->creation_time = metadata.creation_time;\n  report->uploaded = (metadata.attributes & kAttributeUploaded) != 0;\n  report->upload_explicitly_requested =\n      (metadata.attributes & kAttributeUploadExplicitlyRequested) != 0;\n  report->file_path = path;\n  report->total_size = total_size;\n  return true;\n}\n\nbool CrashReportDatabaseGeneric::CleaningReadMetadata(\n    const base::FilePath& path,\n    Report* report) {\n  if (ReadMetadata(path, report)) {\n    return true;\n  }\n\n  LoggingRemoveFile(path);\n  LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension));\n  RemoveAttachmentsByUUID(report->uuid);\n  return false;\n}\n\n// static\nbool CrashReportDatabaseGeneric::WriteNewMetadata(const base::FilePath& path) {\n  const base::FilePath metadata_path(\n      ReplaceFinalExtension(path, kMetadataExtension));\n\n  ScopedFileHandle handle(LoggingOpenFileForWrite(metadata_path,\n                                                  FileWriteMode::kCreateOrFail,\n                                                  FilePermissions::kOwnerOnly));\n  if (!handle.is_valid()) {\n    return false;\n  }\n\n  ReportMetadata metadata;\n#if defined(MEMORY_SANITIZER)\n  // memset() + re-initialization is required to zero padding bytes for MSan.\n  memset(&metadata, 0, sizeof(metadata));\n#endif  // defined(MEMORY_SANITIZER)\n  metadata = {};\n  metadata.creation_time = time(nullptr);\n\n  return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata));\n}\n\n// static\nbool CrashReportDatabaseGeneric::WriteMetadata(const base::FilePath& path,\n                                               const Report& report) {\n  const base::FilePath metadata_path(\n      ReplaceFinalExtension(path, kMetadataExtension));\n\n  ScopedFileHandle handle(\n      LoggingOpenFileForWrite(metadata_path,\n                              FileWriteMode::kTruncateOrCreate,\n                              FilePermissions::kOwnerOnly));\n  if (!handle.is_valid()) {\n    return false;\n  }\n\n  ReportMetadata metadata;\n#if defined(MEMORY_SANITIZER)\n  // memset() + re-initialization is required to zero padding bytes for MSan.\n  memset(&metadata, 0, sizeof(metadata));\n#endif  // defined(MEMORY_SANITIZER)\n  metadata = {};\n  metadata.creation_time = report.creation_time;\n  metadata.last_upload_attempt_time = report.last_upload_attempt_time;\n  metadata.upload_attempts = report.upload_attempts;\n  metadata.attributes =\n      (report.uploaded ? kAttributeUploaded : 0) |\n      (report.upload_explicitly_requested ? kAttributeUploadExplicitlyRequested\n                                          : 0);\n\n  return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata)) &&\n         LoggingWriteFile(handle.get(), report.id.c_str(), report.id.size());\n}\n\n// static\nstd::unique_ptr<CrashReportDatabase> CrashReportDatabase::Initialize(\n    const base::FilePath& path) {\n  auto database = std::make_unique<CrashReportDatabaseGeneric>(path);\n  return database->Initialize(true) ? std::move(database) : nullptr;\n}\n\n// static\nstd::unique_ptr<CrashReportDatabase>\nCrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) {\n  auto database = std::make_unique<CrashReportDatabaseGeneric>(path);\n  return database->Initialize(false) ? std::move(database) : nullptr;\n}\n\n// static\nstd::unique_ptr<SettingsReader>\nCrashReportDatabase::GetSettingsReaderForDatabasePath(\n    const base::FilePath& path) {\n  return std::make_unique<SettingsReader>(path.Append(kSettings));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crash_report_database_mac.mm",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crash_report_database.h\"\n\n#import <Foundation/Foundation.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <time.h>\n#include <unistd.h>\n#include <uuid/uuid.h>\n\n#include <array>\n#include <iterator>\n#include <mutex>\n#include <string_view>\n#include <tuple>\n\n#include \"base/apple/scoped_nsautorelease_pool.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/scoped_generic.h\"\n#include \"base/strings/strcat.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"client/settings.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/mac/xattr.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/metrics.h\"\n\n#if BUILDFLAG(IS_IOS)\n#include \"util/ios/scoped_background_task.h\"\n#endif  // BUILDFLAG(IS_IOS)\n\nnamespace crashpad {\n\nnamespace {\n\nconstexpr char kWriteDirectory[] = \"new\";\nconstexpr char kUploadPendingDirectory[] = \"pending\";\nconstexpr char kCompletedDirectory[] = \"completed\";\n\nconstexpr char kSettings[] = \"settings.dat\";\n\nconstexpr std::array<const char*, 3> kReportDirectories = {\n    kWriteDirectory,\n    kUploadPendingDirectory,\n    kCompletedDirectory,\n};\n\nconstexpr char kCrashReportFileExtension[] = \"dmp\";\n\nconstexpr char kXattrUUID[] = \"uuid\";\nconstexpr char kXattrCollectorID[] = \"id\";\nconstexpr char kXattrCreationTime[] = \"creation_time\";\nconstexpr char kXattrIsUploaded[] = \"uploaded\";\n#if BUILDFLAG(IS_IOS)\nconstexpr char kXattrUploadStartTime[] = \"upload_start_time\";\n#endif\nconstexpr char kXattrLastUploadTime[] = \"last_upload_time\";\nconstexpr char kXattrUploadAttemptCount[] = \"upload_count\";\nconstexpr char kXattrIsUploadExplicitlyRequested[] =\n    \"upload_explicitly_requested\";\n\nconstexpr char kXattrDatabaseInitialized[] = \"initialized\";\n\n// Ensures that the node at |path| is a directory. If the |path| refers to a\n// file, rather than a directory, returns false. Otherwise, returns true,\n// indicating that |path| already was a directory.\nbool EnsureDirectoryExists(const base::FilePath& path) {\n  struct stat st;\n  if (stat(path.value().c_str(), &st) != 0) {\n    PLOG(ERROR) << \"stat \" << path.value();\n    return false;\n  }\n  if (!S_ISDIR(st.st_mode)) {\n    LOG(ERROR) << \"stat \" << path.value() << \": not a directory\";\n    return false;\n  }\n  return true;\n}\n\n// Ensures that the node at |path| is a directory, and creates it if it does\n// not exist. If the |path| refers to a file, rather than a directory, or the\n// directory could not be created, returns false. Otherwise, returns true,\n// indicating that |path| already was or now is a directory.\nbool CreateOrEnsureDirectoryExists(const base::FilePath& path) {\n  if (mkdir(path.value().c_str(), 0755) == 0) {\n    return true;\n  }\n  if (errno != EEXIST) {\n    PLOG(ERROR) << \"mkdir \" << path.value();\n    return false;\n  }\n  return EnsureDirectoryExists(path);\n}\n\n// Creates a long database xattr name from the short constant name. These names\n// have changed, and new_name determines whether the returned xattr name will be\n// the old name or its new equivalent.\nstd::string XattrNameInternal(std::string_view name, bool new_name) {\n  return base::StrCat({new_name ? \"org.chromium.crashpad.database.\"\n                                : \"com.googlecode.crashpad.\",\n                       name});\n}\n\n}  // namespace\n\n//! \\brief A CrashReportDatabase that uses HFS+ extended attributes to store\n//!     report metadata.\n//!\n//! The database maintains three directories of reports: `\"new\"` to hold crash\n//! reports that are in the process of being written, `\"completed\"` to hold\n//! reports that have been written and are awaiting upload, and `\"uploaded\"` to\n//! hold reports successfully uploaded to a collection server. If the user has\n//! opted out of report collection, reports will still be written and moved\n//! to the completed directory, but they just will not be uploaded.\n//!\n//! The database stores its metadata in extended filesystem attributes. To\n//! ensure safe access, the report file is locked using `O_EXLOCK` during all\n//! extended attribute operations. The lock should be obtained using\n//! ObtainReportLock().\nclass CrashReportDatabaseMac : public CrashReportDatabase {\n public:\n  explicit CrashReportDatabaseMac(const base::FilePath& path);\n\n  CrashReportDatabaseMac(const CrashReportDatabaseMac&) = delete;\n  CrashReportDatabaseMac& operator=(const CrashReportDatabaseMac&) = delete;\n\n  virtual ~CrashReportDatabaseMac();\n\n  bool Initialize(bool may_create);\n\n  // CrashReportDatabase:\n  Settings* GetSettings() override;\n  OperationStatus PrepareNewCrashReport(\n      std::unique_ptr<NewReport>* report) override;\n  OperationStatus FinishedWritingCrashReport(std::unique_ptr<NewReport> report,\n                                             UUID* uuid) override;\n  OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override;\n  OperationStatus GetPendingReports(std::vector<Report>* reports) override;\n  OperationStatus GetCompletedReports(std::vector<Report>* reports) override;\n  OperationStatus GetReportForUploading(\n      const UUID& uuid,\n      std::unique_ptr<const UploadReport>* report,\n      bool report_metrics) override;\n  OperationStatus SkipReportUpload(const UUID& uuid,\n                                   Metrics::CrashSkippedReason reason) override;\n  OperationStatus DeleteReport(const UUID& uuid) override;\n  OperationStatus RequestUpload(const UUID& uuid) override;\n  int CleanDatabase(time_t lockfile_ttl) override;\n  base::FilePath DatabasePath() override;\n\n private:\n  // CrashReportDatabase:\n  OperationStatus RecordUploadAttempt(UploadReport* report,\n                                      bool successful,\n                                      const std::string& id) override;\n\n  //! \\brief Report states for use with LocateCrashReport().\n  //!\n  //! ReportState may be considered to be a bitfield.\n  enum ReportState : uint8_t {\n    kReportStateWrite = 1 << 0,  // in kWriteDirectory\n    kReportStatePending = 1 << 1,  // in kUploadPendingDirectory\n    kReportStateCompleted = 1 << 2,  // in kCompletedDirectory\n    kReportStateAny =\n        kReportStateWrite | kReportStatePending | kReportStateCompleted,\n  };\n\n  //! \\brief A private extension of the Report class that maintains bookkeeping\n  //!    information of the database.\n  struct UploadReportMac : public UploadReport {\n#if BUILDFLAG(IS_IOS)\n    //! \\brief Obtain a background task assertion while a flock is in use.\n    //!     Ensure this is defined first so it is destroyed last.\n    internal::ScopedBackgroundTask ios_background_task{\"UploadReportMac\"};\n#else\n    //! \\brief Stores the flock of the file for the duration of\n    //!     GetReportForUploading() and RecordUploadAttempt().\n    base::ScopedFD lock_fd;\n#endif  // BUILDFLAG(IS_IOS)\n  };\n\n  //! \\brief Locates a crash report in the database by UUID.\n  //!\n  //! \\param[in] uuid The UUID of the crash report to locate.\n  //! \\param[in] desired_state The state of the report to locate, composed of\n  //!     ReportState values.\n  //!\n  //! \\return The full path to the report file, or an empty path if it cannot be\n  //!     found.\n  base::FilePath LocateCrashReport(const UUID& uuid, uint8_t desired_state);\n\n  //! \\brief Obtains an exclusive advisory lock on a file.\n  //!\n  //! The flock is used to prevent cross-process concurrent metadata reads or\n  //! writes. While xattrs do not observe the lock, if the lock-then-mutate\n  //! protocol is observed by all clients of the database, it still enforces\n  //! synchronization.\n  //!\n  //! This does not block, and so callers must ensure that the lock is valid\n  //! after calling.\n  //!\n  //! \\param[in] path The path of the file to lock.\n  //!\n  //! \\return A scoped lock object. If the result is not valid, an error is\n  //!     logged.\n  static base::ScopedFD ObtainReportLock(const base::FilePath& path);\n\n  //! \\brief Reads all the database xattrs from a file into a Report. The file\n  //!     must be locked with ObtainReportLock.\n  //!\n  //! \\param[in] path The path of the report.\n  //! \\param[out] report The object into which data will be read.\n  //!\n  //! \\return `true` if all the metadata was read successfully, `false`\n  //!     otherwise.\n  bool ReadReportMetadataLocked(const base::FilePath& path, Report* report);\n\n  //! \\brief Reads the metadata from all the reports in a database subdirectory.\n  //!      Invalid reports are skipped.\n  //!\n  //! \\param[in] path The database subdirectory path.\n  //! \\param[out] reports An empty vector of reports, which will be filled.\n  //!\n  //! \\return The operation status code.\n  OperationStatus ReportsInDirectory(const base::FilePath& path,\n                                     std::vector<Report>* reports);\n\n  //! \\brief Creates a database xattr name from the short constant name.\n  //!\n  //! \\param[in] name The short name of the extended attribute.\n  //!\n  //! \\return The long name of the extended attribute.\n  std::string XattrName(std::string_view name);\n\n  //! \\brief Marks a report with a given path as completed.\n  //!\n  //! Assumes that the report is locked.\n  //!\n  //! \\param[in] report_path The path of the file to mark completed.\n  //! \\param[out] out_path The path of the new file. This parameter is optional.\n  //!\n  //! \\return The operation status code.\n  CrashReportDatabase::OperationStatus MarkReportCompletedLocked(\n      const base::FilePath& report_path,\n      base::FilePath* out_path);\n\n  // Cleans any attachments that have no associated report in any state.\n  void CleanOrphanedAttachments();\n\n  Settings& SettingsInternal() {\n    std::call_once(settings_init_, [this]() { settings_.Initialize(); });\n    return settings_;\n  }\n\n  const base::FilePath base_dir_;\n  Settings settings_;\n  std::once_flag settings_init_;\n  bool xattr_new_names_;\n  InitializationStateDcheck initialized_;\n};\n\nCrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path)\n    : CrashReportDatabase(),\n      base_dir_(path),\n      settings_(path.Append(kSettings)),\n      settings_init_(),\n      xattr_new_names_(false),\n      initialized_() {}\n\nCrashReportDatabaseMac::~CrashReportDatabaseMac() {}\n\nbool CrashReportDatabaseMac::Initialize(bool may_create) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  // Check if the database already exists.\n  if (may_create) {\n    if (!CreateOrEnsureDirectoryExists(base_dir_)) {\n      return false;\n    }\n  } else if (!EnsureDirectoryExists(base_dir_)) {\n    return false;\n  }\n\n  // Create the three processing directories for the database.\n  for (const auto& dir : kReportDirectories) {\n    if (!CreateOrEnsureDirectoryExists(base_dir_.Append(dir)))\n      return false;\n  }\n\n  if (!CreateOrEnsureDirectoryExists(AttachmentsRootPath())) {\n    return false;\n  }\n\n  // Do an xattr operation as the last step, to ensure the filesystem has\n  // support for them. This xattr also serves as a marker for whether the\n  // database uses old or new xattr names.\n  bool value;\n  if (ReadXattrBool(base_dir_,\n                    XattrNameInternal(kXattrDatabaseInitialized, true),\n                    &value) == XattrStatus::kOK &&\n      value) {\n    xattr_new_names_ = true;\n  } else if (ReadXattrBool(base_dir_,\n                           XattrNameInternal(kXattrDatabaseInitialized, false),\n                           &value) == XattrStatus::kOK &&\n             value) {\n    xattr_new_names_ = false;\n  } else {\n    xattr_new_names_ = true;\n    if (!WriteXattrBool(base_dir_, XattrName(kXattrDatabaseInitialized), true))\n      return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbase::FilePath CrashReportDatabaseMac::DatabasePath() {\n  return base_dir_;\n}\n\nSettings* CrashReportDatabaseMac::GetSettings() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &SettingsInternal();\n}\n\nCrashReportDatabase::OperationStatus\nCrashReportDatabaseMac::PrepareNewCrashReport(\n    std::unique_ptr<NewReport>* out_report) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<NewReport> report(new NewReport());\n  if (!report->Initialize(this,\n                          base_dir_.Append(kWriteDirectory),\n                          std::string(\".\") + kCrashReportFileExtension)) {\n    return kFileSystemError;\n  }\n\n  // TODO(rsesek): Potentially use an fsetxattr() here instead.\n  if (!WriteXattr(report->file_remover_.get(),\n                  XattrName(kXattrUUID),\n                  report->ReportID().ToString())) {\n    return kDatabaseError;\n  }\n\n  out_report->reset(report.release());\n  return kNoError;\n}\n\nCrashReportDatabase::OperationStatus\nCrashReportDatabaseMac::FinishedWritingCrashReport(\n    std::unique_ptr<NewReport> report,\n    UUID* uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  const base::FilePath& path = report->file_remover_.get();\n\n  // Get the report's UUID to return.\n  std::string uuid_string;\n  if (ReadXattr(path, XattrName(kXattrUUID), &uuid_string) !=\n          XattrStatus::kOK ||\n      !uuid->InitializeFromString(uuid_string)) {\n    LOG(ERROR) << \"Failed to read UUID for crash report \" << path.value();\n    return kDatabaseError;\n  }\n\n  if (*uuid != report->ReportID()) {\n    LOG(ERROR) << \"UUID mismatch for crash report \" << path.value();\n    return kDatabaseError;\n  }\n\n  // Record the creation time of this report.\n  if (!WriteXattrTimeT(path, XattrName(kXattrCreationTime), time(nullptr))) {\n    return kDatabaseError;\n  }\n\n  FileOffset size = report->Writer()->Seek(0, SEEK_END);\n\n  // Move the report to its new location for uploading.\n  base::FilePath new_path =\n      base_dir_.Append(kUploadPendingDirectory).Append(path.BaseName());\n  if (rename(path.value().c_str(), new_path.value().c_str()) != 0) {\n    PLOG(ERROR) << \"rename \" << path.value() << \" to \" << new_path.value();\n    return kFileSystemError;\n  }\n  std::ignore = report->file_remover_.release();\n\n  // Close all the attachments and disarm their removers too.\n  for (auto& writer : report->attachment_writers_) {\n    writer->Close();\n  }\n  for (auto& remover : report->attachment_removers_) {\n    std::ignore = remover.release();\n  }\n\n  Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);\n  Metrics::CrashReportSize(size);\n\n  return kNoError;\n}\n\nCrashReportDatabase::OperationStatus\nCrashReportDatabaseMac::LookUpCrashReport(const UUID& uuid,\n                                          CrashReportDatabase::Report* report) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  base::FilePath path = LocateCrashReport(uuid, kReportStateAny);\n  if (path.empty())\n    return kReportNotFound;\n\n  base::ScopedFD lock(ObtainReportLock(path));\n  if (!lock.is_valid())\n    return kBusyError;\n\n  *report = Report();\n  report->file_path = path;\n  if (!ReadReportMetadataLocked(path, report))\n    return kDatabaseError;\n\n  return kNoError;\n}\n\nCrashReportDatabase::OperationStatus\nCrashReportDatabaseMac::GetPendingReports(\n    std::vector<CrashReportDatabase::Report>* reports) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  return ReportsInDirectory(base_dir_.Append(kUploadPendingDirectory), reports);\n}\n\nCrashReportDatabase::OperationStatus\nCrashReportDatabaseMac::GetCompletedReports(\n    std::vector<CrashReportDatabase::Report>* reports) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  return ReportsInDirectory(base_dir_.Append(kCompletedDirectory), reports);\n}\n\nCrashReportDatabase::OperationStatus\nCrashReportDatabaseMac::GetReportForUploading(\n    const UUID& uuid,\n    std::unique_ptr<const UploadReport>* report,\n    bool report_metrics) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  auto upload_report = std::make_unique<UploadReportMac>();\n\n  upload_report->file_path = LocateCrashReport(uuid, kReportStatePending);\n  if (upload_report->file_path.empty())\n    return kReportNotFound;\n\n  base::ScopedFD lock(ObtainReportLock(upload_report->file_path));\n  if (!lock.is_valid())\n    return kBusyError;\n\n  if (!ReadReportMetadataLocked(upload_report->file_path, upload_report.get()))\n    return kDatabaseError;\n\n#if BUILDFLAG(IS_IOS)\n  time_t upload_start_time = 0;\n  if (ReadXattrTimeT(upload_report->file_path,\n                     XattrName(kXattrUploadStartTime),\n                     &upload_start_time) == XattrStatus::kOtherError) {\n    return kDatabaseError;\n  }\n\n  time_t now = time(nullptr);\n  if (upload_start_time) {\n    // If we were able to ObtainReportLock but kXattrUploadStartTime is set,\n    // either another client is uploading this report or a client was terminated\n    // during an upload. CrashReportUploadThread sets the timeout to 20 seconds\n    // for iOS. If kXattrUploadStartTime is less than  5 minutes ago, consider\n    // the report locked and return kBusyError. Otherwise, consider the upload a\n    // failure and skip the report.\n    if (upload_start_time > now - 15 * internal::kUploadReportTimeoutSeconds) {\n      return kBusyError;\n    } else {\n      // SkipReportUpload expects an unlocked report.\n      lock.reset();\n      CrashReportDatabase::OperationStatus os = SkipReportUpload(\n          upload_report->uuid, Metrics::CrashSkippedReason::kUploadFailed);\n      if (os != kNoError) {\n        return kDatabaseError;\n      }\n      return kReportNotFound;\n    }\n  }\n\n  if (!WriteXattrTimeT(\n          upload_report->file_path, XattrName(kXattrUploadStartTime), now)) {\n    return kDatabaseError;\n  }\n#endif\n\n  if (!upload_report->Initialize(upload_report->file_path, this)) {\n    return kFileSystemError;\n  }\n\n  upload_report->database_ = this;\n#if !BUILDFLAG(IS_IOS)\n  upload_report->lock_fd.reset(lock.release());\n#endif\n  upload_report->report_metrics_ = report_metrics;\n  report->reset(upload_report.release());\n  return kNoError;\n}\n\nCrashReportDatabase::OperationStatus\nCrashReportDatabaseMac::RecordUploadAttempt(UploadReport* report,\n                                            bool successful,\n                                            const std::string& id) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (report->report_metrics_) {\n    Metrics::CrashUploadAttempted(successful);\n  }\n\n  DCHECK(report);\n  DCHECK(successful || id.empty());\n\n  base::FilePath report_path =\n      LocateCrashReport(report->uuid, kReportStatePending);\n  if (report_path.empty())\n    return kReportNotFound;\n\n  if (successful) {\n    CrashReportDatabase::OperationStatus os =\n        MarkReportCompletedLocked(report_path, &report_path);\n    if (os != kNoError)\n      return os;\n  }\n\n#if BUILDFLAG(IS_IOS)\n  if (RemoveXattr(report_path, XattrName(kXattrUploadStartTime)) ==\n      XattrStatus::kOtherError) {\n    return kDatabaseError;\n  }\n#endif\n\n  if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) {\n    return kDatabaseError;\n  }\n  if (!WriteXattr(report_path, XattrName(kXattrCollectorID), id)) {\n    return kDatabaseError;\n  }\n\n  time_t now = time(nullptr);\n  if (!WriteXattrTimeT(report_path, XattrName(kXattrLastUploadTime), now)) {\n    return kDatabaseError;\n  }\n\n  int upload_attempts = 0;\n  std::string name = XattrName(kXattrUploadAttemptCount);\n  if (ReadXattrInt(report_path, name, &upload_attempts) ==\n          XattrStatus::kOtherError) {\n    return kDatabaseError;\n  }\n  if (!WriteXattrInt(report_path, name, ++upload_attempts)) {\n    return kDatabaseError;\n  }\n\n  if (!SettingsInternal().SetLastUploadAttemptTime(now)) {\n    return kDatabaseError;\n  }\n\n  return kNoError;\n}\n\nCrashReportDatabase::OperationStatus CrashReportDatabaseMac::SkipReportUpload(\n    const UUID& uuid,\n    Metrics::CrashSkippedReason reason) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  Metrics::CrashUploadSkipped(reason);\n\n  base::FilePath report_path = LocateCrashReport(uuid, kReportStatePending);\n  if (report_path.empty())\n    return kReportNotFound;\n\n  base::ScopedFD lock(ObtainReportLock(report_path));\n  if (!lock.is_valid())\n    return kBusyError;\n\n#if BUILDFLAG(IS_IOS)\n  if (RemoveXattr(report_path, XattrName(kXattrUploadStartTime)) ==\n      XattrStatus::kOtherError) {\n    return kDatabaseError;\n  }\n#endif\n\n  return MarkReportCompletedLocked(report_path, nullptr);\n}\n\nCrashReportDatabase::OperationStatus CrashReportDatabaseMac::DeleteReport(\n    const UUID& uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  base::FilePath report_path = LocateCrashReport(uuid, kReportStateAny);\n  if (report_path.empty())\n    return kReportNotFound;\n\n  base::ScopedFD lock(ObtainReportLock(report_path));\n  if (!lock.is_valid())\n    return kBusyError;\n\n  if (unlink(report_path.value().c_str()) != 0) {\n    PLOG(ERROR) << \"unlink \" << report_path.value();\n    return kFileSystemError;\n  }\n\n  RemoveAttachmentsByUUID(uuid);\n\n  return kNoError;\n}\n\nbase::FilePath CrashReportDatabaseMac::LocateCrashReport(\n    const UUID& uuid,\n    uint8_t desired_state) {\n  const std::string target_uuid = uuid.ToString();\n\n  std::vector<std::string> report_directories;\n  if (desired_state & kReportStateWrite) {\n    report_directories.push_back(kWriteDirectory);\n  }\n  if (desired_state & kReportStatePending) {\n    report_directories.push_back(kUploadPendingDirectory);\n  }\n  if (desired_state & kReportStateCompleted) {\n    report_directories.push_back(kCompletedDirectory);\n  }\n\n  for (const std::string& report_directory : report_directories) {\n    base::FilePath path =\n        base_dir_.Append(report_directory)\n                 .Append(target_uuid + \".\" + kCrashReportFileExtension);\n\n    // Test if the path exists.\n    struct stat st;\n    if (lstat(path.value().c_str(), &st)) {\n      continue;\n    }\n\n    // Check that the UUID of the report matches.\n    std::string uuid_string;\n    if (ReadXattr(path, XattrName(kXattrUUID),\n                  &uuid_string) == XattrStatus::kOK &&\n        uuid_string == target_uuid) {\n      return path;\n    }\n  }\n\n  return base::FilePath();\n}\n\nCrashReportDatabase::OperationStatus CrashReportDatabaseMac::RequestUpload(\n    const UUID& uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  base::FilePath report_path =\n      LocateCrashReport(uuid, kReportStatePending | kReportStateCompleted);\n  if (report_path.empty())\n    return kReportNotFound;\n\n  base::ScopedFD lock(ObtainReportLock(report_path));\n  if (!lock.is_valid())\n    return kBusyError;\n\n  // If the crash report has already been uploaded, don't request new upload.\n  bool uploaded = false;\n  XattrStatus status =\n      ReadXattrBool(report_path, XattrName(kXattrIsUploaded), &uploaded);\n  if (status == XattrStatus::kOtherError)\n    return kDatabaseError;\n  if (uploaded)\n    return kCannotRequestUpload;\n\n  // Mark the crash report as having upload explicitly requested by the user,\n  // and move it to the pending state.\n  if (!WriteXattrBool(\n          report_path, XattrName(kXattrIsUploadExplicitlyRequested), true)) {\n    return kDatabaseError;\n  }\n\n  base::FilePath new_path =\n      base_dir_.Append(kUploadPendingDirectory).Append(report_path.BaseName());\n  if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {\n    PLOG(ERROR) << \"rename \" << report_path.value() << \" to \"\n                << new_path.value();\n    return kFileSystemError;\n  }\n\n  Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated);\n\n  return kNoError;\n}\n\nint CrashReportDatabaseMac::CleanDatabase(time_t lockfile_ttl) {\n  int removed = 0;\n  time_t now = time(nullptr);\n\n  DirectoryReader reader;\n  const base::FilePath new_dir(base_dir_.Append(kWriteDirectory));\n  if (reader.Open(new_dir)) {\n    base::FilePath filename;\n    DirectoryReader::Result result;\n    while ((result = reader.NextFile(&filename)) ==\n           DirectoryReader::Result::kSuccess) {\n      const base::FilePath filepath(new_dir.Append(filename));\n      timespec filetime;\n      if (!FileModificationTime(filepath, &filetime)) {\n        continue;\n      }\n      if (filetime.tv_sec <= now - lockfile_ttl) {\n        if (LoggingRemoveFile(filepath)) {\n          ++removed;\n        }\n      }\n    }\n  }\n\n  CleanOrphanedAttachments();\n  return removed;\n}\n\n// static\nbase::ScopedFD CrashReportDatabaseMac::ObtainReportLock(\n    const base::FilePath& path) {\n  int fd = HANDLE_EINTR(\n      open(path.value().c_str(),\n           O_RDONLY | O_NONBLOCK | O_EXLOCK | O_NOCTTY | O_CLOEXEC));\n  PLOG_IF(ERROR, fd < 0) << \"open lock \" << path.value();\n  return base::ScopedFD(fd);\n}\n\nbool CrashReportDatabaseMac::ReadReportMetadataLocked(\n    const base::FilePath& path, Report* report) {\n  std::string uuid_string;\n  if (ReadXattr(path, XattrName(kXattrUUID),\n                &uuid_string) != XattrStatus::kOK ||\n      !report->uuid.InitializeFromString(uuid_string)) {\n    return false;\n  }\n\n  if (ReadXattrTimeT(path, XattrName(kXattrCreationTime),\n                     &report->creation_time) != XattrStatus::kOK) {\n    return false;\n  }\n\n  report->id = std::string();\n  if (ReadXattr(path, XattrName(kXattrCollectorID),\n                &report->id) == XattrStatus::kOtherError) {\n    return false;\n  }\n\n  report->uploaded = false;\n  if (ReadXattrBool(path, XattrName(kXattrIsUploaded),\n                    &report->uploaded) == XattrStatus::kOtherError) {\n    return false;\n  }\n\n  report->last_upload_attempt_time = 0;\n  if (ReadXattrTimeT(path, XattrName(kXattrLastUploadTime),\n                     &report->last_upload_attempt_time) ==\n          XattrStatus::kOtherError) {\n    return false;\n  }\n\n  report->upload_attempts = 0;\n  if (ReadXattrInt(path, XattrName(kXattrUploadAttemptCount),\n                   &report->upload_attempts) == XattrStatus::kOtherError) {\n    return false;\n  }\n\n  report->upload_explicitly_requested = false;\n  if (ReadXattrBool(path,\n                    XattrName(kXattrIsUploadExplicitlyRequested),\n                    &report->upload_explicitly_requested) ==\n      XattrStatus::kOtherError) {\n    return false;\n  }\n\n  // Seed the total size with the main report size and then add the sizes of any\n  // potential attachments.\n  uint64_t total_size = GetFileSize(path);\n  total_size += GetDirectorySize(AttachmentsPath(report->uuid));\n  report->total_size = total_size;\n\n  return true;\n}\n\nCrashReportDatabase::OperationStatus CrashReportDatabaseMac::ReportsInDirectory(\n    const base::FilePath& path,\n    std::vector<CrashReportDatabase::Report>* reports) {\n  base::apple::ScopedNSAutoreleasePool pool;\n\n  DCHECK(reports->empty());\n\n  NSError* error = nil;\n  NSArray* paths = [[NSFileManager defaultManager]\n      contentsOfDirectoryAtPath:base::SysUTF8ToNSString(path.value())\n                          error:&error];\n  if (error) {\n    LOG(ERROR) << \"Failed to enumerate reports in directory \" << path.value()\n               << \": \" << [[error description] UTF8String];\n    return kFileSystemError;\n  }\n\n  reports->reserve([paths count]);\n  for (NSString* entry in paths) {\n    Report report;\n    report.file_path = path.Append([entry fileSystemRepresentation]);\n    base::ScopedFD lock(ObtainReportLock(report.file_path));\n    if (!lock.is_valid())\n      continue;\n\n    if (!ReadReportMetadataLocked(report.file_path, &report)) {\n      LOG(WARNING) << \"Failed to read report metadata for \"\n                   << report.file_path.value();\n      continue;\n    }\n    reports->push_back(report);\n  }\n\n  return kNoError;\n}\n\nstd::string CrashReportDatabaseMac::XattrName(std::string_view name) {\n  return XattrNameInternal(name, xattr_new_names_);\n}\n\nCrashReportDatabase::OperationStatus\nCrashReportDatabaseMac::MarkReportCompletedLocked(\n    const base::FilePath& report_path,\n    base::FilePath* out_path) {\n  if (RemoveXattr(report_path, XattrName(kXattrIsUploadExplicitlyRequested)) ==\n      XattrStatus::kOtherError) {\n    return kDatabaseError;\n  }\n\n  base::FilePath new_path =\n      base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName());\n  if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {\n    PLOG(ERROR) << \"rename \" << report_path.value() << \" to \"\n                << new_path.value();\n    return kFileSystemError;\n  }\n\n  if (out_path)\n    *out_path = new_path;\n  return kNoError;\n}\n\nvoid CrashReportDatabaseMac::CleanOrphanedAttachments() {\n  base::FilePath root_attachments_dir(AttachmentsRootPath());\n  DirectoryReader reader;\n  if (!reader.Open(root_attachments_dir)) {\n    return;\n  }\n\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath report_attachment_dir(\n        root_attachments_dir.Append(filename));\n    if (IsDirectory(report_attachment_dir, false)) {\n      UUID uuid;\n      if (!uuid.InitializeFromString(filename.value())) {\n        LOG(ERROR) << \"unexpected attachment dir name \" << filename.value();\n        continue;\n      }\n\n      // Check to see if the report is being created in \"new\".\n      base::FilePath new_dir_path =\n          base_dir_.Append(kWriteDirectory)\n              .Append(uuid.ToString() + \".\" + kCrashReportFileExtension);\n      if (IsRegularFile(new_dir_path)) {\n        continue;\n      }\n\n      // Check to see if the report is in \"pending\" or \"completed\".\n      base::FilePath local_path =\n          LocateCrashReport(uuid, kReportStatePending | kReportStateCompleted);\n      if (!local_path.empty()) {\n        continue;\n      }\n\n      // Couldn't find a report, assume these attachments are orphaned.\n      RemoveAttachmentsByUUID(uuid);\n    }\n  }\n}\n\nnamespace {\n\nstd::unique_ptr<CrashReportDatabase> InitializeInternal(\n    const base::FilePath& path,\n    bool may_create) {\n  std::unique_ptr<CrashReportDatabaseMac> database_mac(\n      new CrashReportDatabaseMac(path));\n  if (!database_mac->Initialize(may_create))\n    database_mac.reset();\n\n  return std::unique_ptr<CrashReportDatabase>(database_mac.release());\n}\n\n}  // namespace\n\n// static\nstd::unique_ptr<CrashReportDatabase> CrashReportDatabase::Initialize(\n    const base::FilePath& path) {\n  return InitializeInternal(path, true);\n}\n\n// static\nstd::unique_ptr<CrashReportDatabase>\nCrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) {\n  return InitializeInternal(path, false);\n}\n\n// static\nstd::unique_ptr<SettingsReader>\nCrashReportDatabase::GetSettingsReaderForDatabasePath(\n    const base::FilePath& path) {\n  return std::make_unique<SettingsReader>(path.Append(kSettings));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crash_report_database_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crash_report_database.h\"\n\n#include \"build/build_config.h\"\n#include \"client/settings.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/file.h\"\n#include \"test/filesystem.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n\n#if BUILDFLAG(IS_IOS)\n#include \"util/mac/xattr.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass CrashReportDatabaseTest : public testing::Test {\n public:\n  CrashReportDatabaseTest() {}\n\n  CrashReportDatabaseTest(const CrashReportDatabaseTest&) = delete;\n  CrashReportDatabaseTest& operator=(const CrashReportDatabaseTest&) = delete;\n\n protected:\n  // testing::Test:\n  void SetUp() override {\n    db_ = CrashReportDatabase::Initialize(path());\n    ASSERT_TRUE(db_);\n  }\n\n  void ResetDatabase() { db_.reset(); }\n\n  CrashReportDatabase* db() { return db_.get(); }\n  base::FilePath path() const {\n    return temp_dir_.path().Append(FILE_PATH_LITERAL(\"crashpad_test_database\"));\n  }\n\n  void CreateCrashReport(CrashReportDatabase::Report* report) {\n    std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n    ASSERT_EQ(db_->PrepareNewCrashReport(&new_report),\n              CrashReportDatabase::kNoError);\n    static constexpr char kTest[] = \"test\";\n    ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest)));\n\n    char contents[sizeof(kTest)];\n    FileReaderInterface* reader = new_report->Reader();\n    ASSERT_TRUE(reader->ReadExactly(contents, sizeof(contents)));\n    EXPECT_EQ(memcmp(contents, kTest, sizeof(contents)), 0);\n    EXPECT_EQ(reader->ReadExactly(contents, 1), 0);\n\n    UUID uuid;\n    EXPECT_EQ(db_->FinishedWritingCrashReport(std::move(new_report), &uuid),\n              CrashReportDatabase::kNoError);\n\n    EXPECT_EQ(db_->LookUpCrashReport(uuid, report),\n              CrashReportDatabase::kNoError);\n    ExpectPreparedCrashReport(*report);\n  }\n\n  void UploadReport(const UUID& uuid, bool successful, const std::string& id) {\n    Settings* const settings = db_->GetSettings();\n    ASSERT_TRUE(settings);\n    time_t times[2];\n    ASSERT_TRUE(settings->GetLastUploadAttemptTime(&times[0]));\n\n    std::unique_ptr<const CrashReportDatabase::UploadReport> report;\n    ASSERT_EQ(db_->GetReportForUploading(uuid, &report),\n              CrashReportDatabase::kNoError);\n    EXPECT_NE(report->uuid, UUID());\n    EXPECT_FALSE(report->file_path.empty());\n    EXPECT_TRUE(FileExists(report->file_path)) << report->file_path.value();\n    EXPECT_GT(report->creation_time, 0);\n    if (successful) {\n      EXPECT_EQ(db_->RecordUploadComplete(std::move(report), id),\n                CrashReportDatabase::kNoError);\n    } else {\n      report.reset();\n    }\n\n    ASSERT_TRUE(settings->GetLastUploadAttemptTime(&times[1]));\n    EXPECT_NE(times[1], 0);\n    EXPECT_GE(times[1], times[0]);\n  }\n\n  void ExpectPreparedCrashReport(const CrashReportDatabase::Report& report) {\n    EXPECT_NE(report.uuid, UUID());\n    EXPECT_FALSE(report.file_path.empty());\n    EXPECT_TRUE(FileExists(report.file_path)) << report.file_path.value();\n    EXPECT_TRUE(report.id.empty());\n    EXPECT_GT(report.creation_time, 0);\n    EXPECT_FALSE(report.uploaded);\n    EXPECT_EQ(report.last_upload_attempt_time, 0);\n    EXPECT_EQ(report.upload_attempts, 0);\n    EXPECT_FALSE(report.upload_explicitly_requested);\n    EXPECT_GE(report.total_size, 0u);\n  }\n\n  void RelocateDatabase() {\n    ResetDatabase();\n    temp_dir_.Rename();\n    SetUp();\n  }\n\n  CrashReportDatabase::OperationStatus RequestUpload(const UUID& uuid) {\n    CrashReportDatabase::OperationStatus os = db()->RequestUpload(uuid);\n\n    CrashReportDatabase::Report report;\n    EXPECT_EQ(db_->LookUpCrashReport(uuid, &report),\n              CrashReportDatabase::kNoError);\n\n    return os;\n  }\n\n private:\n  ScopedTempDir temp_dir_;\n  std::unique_ptr<CrashReportDatabase> db_;\n};\n\nTEST_F(CrashReportDatabaseTest, Initialize) {\n  // Initialize the database for the first time, creating it.\n  ASSERT_TRUE(db());\n\n  Settings* settings = db()->GetSettings();\n\n  UUID client_ids[3];\n  ASSERT_TRUE(settings->GetClientID(&client_ids[0]));\n  EXPECT_NE(client_ids[0], UUID());\n\n  time_t last_upload_attempt_time;\n  ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time));\n  EXPECT_EQ(last_upload_attempt_time, 0);\n\n  // Close and reopen the database at the same path.\n  ResetDatabase();\n  EXPECT_FALSE(db());\n  auto db = CrashReportDatabase::InitializeWithoutCreating(path());\n  ASSERT_TRUE(db);\n\n  settings = db->GetSettings();\n\n  ASSERT_TRUE(settings->GetClientID(&client_ids[1]));\n  EXPECT_EQ(client_ids[1], client_ids[0]);\n\n  ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time));\n  EXPECT_EQ(last_upload_attempt_time, 0);\n\n  // Check that the database can also be opened by the method that is permitted\n  // to create it.\n  db = CrashReportDatabase::Initialize(path());\n  ASSERT_TRUE(db);\n\n  settings = db->GetSettings();\n\n  ASSERT_TRUE(settings->GetClientID(&client_ids[2]));\n  EXPECT_EQ(client_ids[2], client_ids[0]);\n\n  ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time));\n  EXPECT_EQ(last_upload_attempt_time, 0);\n\n  std::vector<CrashReportDatabase::Report> reports;\n  EXPECT_EQ(db->GetPendingReports(&reports), CrashReportDatabase::kNoError);\n  EXPECT_TRUE(reports.empty());\n  reports.clear();\n  EXPECT_EQ(db->GetCompletedReports(&reports), CrashReportDatabase::kNoError);\n  EXPECT_TRUE(reports.empty());\n\n  // InitializeWithoutCreating() shouldn’t create a nonexistent database.\n  base::FilePath non_database_path =\n      path().DirName().Append(FILE_PATH_LITERAL(\"not_a_database\"));\n  db = CrashReportDatabase::InitializeWithoutCreating(non_database_path);\n  EXPECT_FALSE(db);\n}\n\nTEST_F(CrashReportDatabaseTest, Settings) {\n  // Initialize three databases and ensure settings.dat isn't created yet.\n  ASSERT_TRUE(db());\n\n  base::FilePath settings_path =\n      path().Append(FILE_PATH_LITERAL(\"settings.dat\"));\n  EXPECT_FALSE(FileExists(settings_path));\n\n  std::unique_ptr<CrashReportDatabase> db2 =\n      CrashReportDatabase::Initialize(path());\n  ASSERT_TRUE(db2);\n  EXPECT_FALSE(FileExists(settings_path));\n\n  std::unique_ptr<CrashReportDatabase> db3 =\n      CrashReportDatabase::Initialize(path());\n  ASSERT_TRUE(db3);\n  EXPECT_FALSE(FileExists(settings_path));\n\n  // Ensure settings.dat exists after getter.\n  Settings* settings = db3->GetSettings();\n  ASSERT_TRUE(settings);\n  EXPECT_TRUE(FileExists(settings_path));\n\n  time_t last_upload_attempt_time = 42;\n  ASSERT_TRUE(settings->SetLastUploadAttemptTime(last_upload_attempt_time));\n\n  // Ensure the first two databases read the same value.\n  ASSERT_TRUE(\n      db2->GetSettings()->GetLastUploadAttemptTime(&last_upload_attempt_time));\n  EXPECT_EQ(last_upload_attempt_time, 42);\n  ASSERT_TRUE(\n      db()->GetSettings()->GetLastUploadAttemptTime(&last_upload_attempt_time));\n  EXPECT_EQ(last_upload_attempt_time, 42);\n}\n\nTEST_F(CrashReportDatabaseTest, NewCrashReport) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  EXPECT_EQ(db()->PrepareNewCrashReport(&new_report),\n            CrashReportDatabase::kNoError);\n  UUID expect_uuid = new_report->ReportID();\n  UUID uuid;\n  EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(uuid, expect_uuid);\n\n  CrashReportDatabase::Report report;\n  EXPECT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kNoError);\n  ExpectPreparedCrashReport(report);\n\n  std::vector<CrashReportDatabase::Report> reports;\n  EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 1u);\n  EXPECT_EQ(reports[0].uuid, report.uuid);\n\n  reports.clear();\n  EXPECT_EQ(db()->GetCompletedReports(&reports), CrashReportDatabase::kNoError);\n  EXPECT_TRUE(reports.empty());\n}\n\nTEST_F(CrashReportDatabaseTest, LookUpCrashReport) {\n  UUID uuid;\n\n  {\n    CrashReportDatabase::Report report;\n    CreateCrashReport(&report);\n    uuid = report.uuid;\n  }\n\n  {\n    CrashReportDatabase::Report report;\n    EXPECT_EQ(db()->LookUpCrashReport(uuid, &report),\n              CrashReportDatabase::kNoError);\n    EXPECT_EQ(report.uuid, uuid);\n    EXPECT_NE(report.file_path.value().find(path().value()), std::string::npos);\n    EXPECT_EQ(report.id, std::string());\n    EXPECT_FALSE(report.uploaded);\n    EXPECT_EQ(report.last_upload_attempt_time, 0);\n    EXPECT_EQ(report.upload_attempts, 0);\n    EXPECT_FALSE(report.upload_explicitly_requested);\n  }\n\n  UploadReport(uuid, true, \"test\");\n\n  {\n    CrashReportDatabase::Report report;\n    EXPECT_EQ(db()->LookUpCrashReport(uuid, &report),\n              CrashReportDatabase::kNoError);\n    EXPECT_EQ(report.uuid, uuid);\n    EXPECT_NE(report.file_path.value().find(path().value()), std::string::npos);\n    EXPECT_EQ(report.id, \"test\");\n    EXPECT_TRUE(report.uploaded);\n    EXPECT_NE(report.last_upload_attempt_time, 0);\n    EXPECT_EQ(report.upload_attempts, 1);\n    EXPECT_FALSE(report.upload_explicitly_requested);\n  }\n}\n\nTEST_F(CrashReportDatabaseTest, RecordUploadAttempt) {\n  std::vector<CrashReportDatabase::Report> reports(3);\n  CreateCrashReport(&reports[0]);\n  CreateCrashReport(&reports[1]);\n  CreateCrashReport(&reports[2]);\n\n  // Record two attempts: one successful, one not.\n  UploadReport(reports[1].uuid, false, std::string());\n  UploadReport(reports[2].uuid, true, \"abc123\");\n\n  std::vector<CrashReportDatabase::Report> query(3);\n  EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_EQ(query[0].id, std::string());\n  EXPECT_EQ(query[1].id, std::string());\n  EXPECT_EQ(query[2].id, \"abc123\");\n\n  EXPECT_FALSE(query[0].uploaded);\n  EXPECT_FALSE(query[1].uploaded);\n  EXPECT_TRUE(query[2].uploaded);\n\n  EXPECT_EQ(query[0].last_upload_attempt_time, 0);\n  EXPECT_NE(query[1].last_upload_attempt_time, 0);\n  EXPECT_NE(query[2].last_upload_attempt_time, 0);\n\n  EXPECT_EQ(query[0].upload_attempts, 0);\n  EXPECT_EQ(query[1].upload_attempts, 1);\n  EXPECT_EQ(query[2].upload_attempts, 1);\n\n  // Attempt to upload and fail again.\n  UploadReport(reports[1].uuid, false, std::string());\n\n  time_t report_2_upload_time = query[2].last_upload_attempt_time;\n\n  EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_FALSE(query[0].uploaded);\n  EXPECT_FALSE(query[1].uploaded);\n  EXPECT_TRUE(query[2].uploaded);\n\n  EXPECT_EQ(query[0].last_upload_attempt_time, 0);\n  EXPECT_GE(query[1].last_upload_attempt_time, report_2_upload_time);\n  EXPECT_EQ(query[2].last_upload_attempt_time, report_2_upload_time);\n\n  EXPECT_EQ(query[0].upload_attempts, 0);\n  EXPECT_EQ(query[1].upload_attempts, 2);\n  EXPECT_EQ(query[2].upload_attempts, 1);\n\n  // Third time's the charm: upload and succeed.\n  UploadReport(reports[1].uuid, true, \"666hahaha\");\n\n  time_t report_1_upload_time = query[1].last_upload_attempt_time;\n\n  EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_FALSE(query[0].uploaded);\n  EXPECT_TRUE(query[1].uploaded);\n  EXPECT_TRUE(query[2].uploaded);\n\n  EXPECT_EQ(query[0].last_upload_attempt_time, 0);\n  EXPECT_GE(query[1].last_upload_attempt_time, report_1_upload_time);\n  EXPECT_EQ(query[2].last_upload_attempt_time, report_2_upload_time);\n\n  EXPECT_EQ(query[0].upload_attempts, 0);\n  EXPECT_EQ(query[1].upload_attempts, 3);\n  EXPECT_EQ(query[2].upload_attempts, 1);\n}\n\n// This test covers both query functions since they are related.\nTEST_F(CrashReportDatabaseTest, GetCompletedAndNotUploadedReports) {\n  std::vector<CrashReportDatabase::Report> reports(5);\n  CreateCrashReport(&reports[0]);\n  CreateCrashReport(&reports[1]);\n  CreateCrashReport(&reports[2]);\n  CreateCrashReport(&reports[3]);\n  CreateCrashReport(&reports[4]);\n\n  const UUID& report_0_uuid = reports[0].uuid;\n  const UUID& report_1_uuid = reports[1].uuid;\n  const UUID& report_2_uuid = reports[2].uuid;\n  const UUID& report_3_uuid = reports[3].uuid;\n  const UUID& report_4_uuid = reports[4].uuid;\n\n  std::vector<CrashReportDatabase::Report> pending;\n  EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError);\n\n  std::vector<CrashReportDatabase::Report> completed;\n  EXPECT_EQ(db()->GetCompletedReports(&completed),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_EQ(pending.size(), reports.size());\n  EXPECT_EQ(completed.size(), 0u);\n\n  // Upload one report successfully.\n  UploadReport(report_1_uuid, true, \"report1\");\n\n  pending.clear();\n  EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError);\n  completed.clear();\n  EXPECT_EQ(db()->GetCompletedReports(&completed),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_EQ(pending.size(), 4u);\n  ASSERT_EQ(completed.size(), 1u);\n\n  for (const auto& report : pending) {\n    EXPECT_NE(report.uuid, report_1_uuid);\n    EXPECT_FALSE(report.file_path.empty());\n  }\n  EXPECT_EQ(completed[0].uuid, report_1_uuid);\n  EXPECT_EQ(completed[0].id, \"report1\");\n  EXPECT_EQ(completed[0].uploaded, true);\n  EXPECT_GT(completed[0].last_upload_attempt_time, 0);\n  EXPECT_EQ(completed[0].upload_attempts, 1);\n\n  // Fail to upload one report.\n  UploadReport(report_2_uuid, false, std::string());\n\n  pending.clear();\n  EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError);\n  completed.clear();\n  EXPECT_EQ(db()->GetCompletedReports(&completed),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_EQ(pending.size(), 4u);\n  ASSERT_EQ(completed.size(), 1u);\n\n  for (const auto& report : pending) {\n    if (report.upload_attempts != 0) {\n      EXPECT_EQ(report.uuid, report_2_uuid);\n      EXPECT_GT(report.last_upload_attempt_time, 0);\n      EXPECT_FALSE(report.uploaded);\n      EXPECT_TRUE(report.id.empty());\n    }\n    EXPECT_FALSE(report.file_path.empty());\n  }\n\n  // Upload a second report.\n  UploadReport(report_4_uuid, true, \"report_4\");\n\n  pending.clear();\n  EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError);\n  completed.clear();\n  EXPECT_EQ(db()->GetCompletedReports(&completed),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_EQ(pending.size(), 3u);\n  ASSERT_EQ(completed.size(), 2u);\n\n  // Succeed the failed report.\n  UploadReport(report_2_uuid, true, \"report 2\");\n\n  pending.clear();\n  EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError);\n  completed.clear();\n  EXPECT_EQ(db()->GetCompletedReports(&completed),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_EQ(pending.size(), 2u);\n  ASSERT_EQ(completed.size(), 3u);\n\n  for (const auto& report : pending) {\n    EXPECT_TRUE(report.uuid == report_0_uuid || report.uuid == report_3_uuid);\n    EXPECT_FALSE(report.file_path.empty());\n  }\n\n  // Skip upload for one report.\n  EXPECT_EQ(db()->SkipReportUpload(\n                report_3_uuid, Metrics::CrashSkippedReason::kUploadsDisabled),\n            CrashReportDatabase::kNoError);\n\n  pending.clear();\n  EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError);\n  completed.clear();\n  EXPECT_EQ(db()->GetCompletedReports(&completed),\n            CrashReportDatabase::kNoError);\n\n  ASSERT_EQ(pending.size(), 1u);\n  ASSERT_EQ(completed.size(), 4u);\n\n  EXPECT_EQ(pending[0].uuid, report_0_uuid);\n\n  for (const auto& report : completed) {\n    if (report.uuid == report_3_uuid) {\n      EXPECT_FALSE(report.uploaded);\n      EXPECT_EQ(report.upload_attempts, 0);\n      EXPECT_EQ(report.last_upload_attempt_time, 0);\n    } else {\n      EXPECT_TRUE(report.uploaded);\n      EXPECT_GT(report.upload_attempts, 0);\n      EXPECT_GT(report.last_upload_attempt_time, 0);\n    }\n    EXPECT_FALSE(report.file_path.empty());\n  }\n}\n\nTEST_F(CrashReportDatabaseTest, DuelingUploads) {\n  CrashReportDatabase::Report report;\n  CreateCrashReport(&report);\n\n  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;\n  EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report),\n            CrashReportDatabase::kNoError);\n\n  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report_2;\n  EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2),\n            CrashReportDatabase::kBusyError);\n  EXPECT_FALSE(upload_report_2);\n\n  EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()),\n            CrashReportDatabase::kNoError);\n}\n\n#if BUILDFLAG(IS_IOS)\nTEST_F(CrashReportDatabaseTest, InterruptedIOSUploads) {\n  CrashReportDatabase::Report report;\n  CreateCrashReport(&report);\n\n  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;\n  EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report),\n            CrashReportDatabase::kNoError);\n\n  // Set upload_start_time to 10 minutes ago.\n  time_t ten_minutes_ago = time(nullptr) - 10 * 60;\n  ASSERT_TRUE(\n      WriteXattrTimeT(report.file_path,\n                      \"org.chromium.crashpad.database.upload_start_time\",\n                      ten_minutes_ago));\n\n  std::vector<CrashReportDatabase::Report> reports;\n  EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 1u);\n  reports.clear();\n  EXPECT_EQ(db()->GetCompletedReports(&reports), CrashReportDatabase::kNoError);\n  EXPECT_TRUE(reports.empty());\n\n  // Getting a stale report will automatically skip it.\n  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report_2;\n  EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2),\n            CrashReportDatabase::kReportNotFound);\n  EXPECT_FALSE(upload_report_2);\n\n  // Confirm report was moved from pending to completed.\n  EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError);\n  EXPECT_TRUE(reports.empty());\n  EXPECT_EQ(db()->GetCompletedReports(&reports), CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 1u);\n\n  EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()),\n            CrashReportDatabase::kReportNotFound);\n}\n#endif\n\nTEST_F(CrashReportDatabaseTest, UploadAlreadyUploaded) {\n  CrashReportDatabase::Report report;\n  CreateCrashReport(&report);\n\n  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;\n  EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()),\n            CrashReportDatabase::kNoError);\n\n  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report_2;\n  EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2),\n            CrashReportDatabase::kReportNotFound);\n  EXPECT_FALSE(upload_report_2.get());\n}\n\nTEST_F(CrashReportDatabaseTest, MoveDatabase) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  EXPECT_EQ(db()->PrepareNewCrashReport(&new_report),\n            CrashReportDatabase::kNoError);\n  UUID uuid;\n  EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),\n            CrashReportDatabase::kNoError);\n\n  RelocateDatabase();\n\n  CrashReportDatabase::Report report;\n  EXPECT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kNoError);\n  ExpectPreparedCrashReport(report);\n}\n\nTEST_F(CrashReportDatabaseTest, ReportRemoved) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  EXPECT_EQ(db()->PrepareNewCrashReport(&new_report),\n            CrashReportDatabase::kNoError);\n\n  UUID uuid;\n  EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),\n            CrashReportDatabase::kNoError);\n\n  CrashReportDatabase::Report report;\n  EXPECT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_TRUE(LoggingRemoveFile(report.file_path));\n\n  EXPECT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kReportNotFound);\n}\n\nTEST_F(CrashReportDatabaseTest, DeleteReport) {\n  CrashReportDatabase::Report keep_pending;\n  CrashReportDatabase::Report delete_pending;\n  CrashReportDatabase::Report keep_completed;\n  CrashReportDatabase::Report delete_completed;\n\n  CreateCrashReport(&keep_pending);\n  CreateCrashReport(&delete_pending);\n  CreateCrashReport(&keep_completed);\n  CreateCrashReport(&delete_completed);\n\n  EXPECT_TRUE(FileExists(keep_pending.file_path));\n  EXPECT_TRUE(FileExists(delete_pending.file_path));\n  EXPECT_TRUE(FileExists(keep_completed.file_path));\n  EXPECT_TRUE(FileExists(delete_completed.file_path));\n\n  UploadReport(keep_completed.uuid, true, \"1\");\n  UploadReport(delete_completed.uuid, true, \"2\");\n\n  EXPECT_EQ(db()->LookUpCrashReport(keep_completed.uuid, &keep_completed),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->LookUpCrashReport(delete_completed.uuid, &delete_completed),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_TRUE(FileExists(keep_completed.file_path));\n  EXPECT_TRUE(FileExists(delete_completed.file_path));\n\n  EXPECT_EQ(db()->DeleteReport(delete_pending.uuid),\n            CrashReportDatabase::kNoError);\n  EXPECT_FALSE(FileExists(delete_pending.file_path));\n  EXPECT_EQ(db()->LookUpCrashReport(delete_pending.uuid, &delete_pending),\n            CrashReportDatabase::kReportNotFound);\n  EXPECT_EQ(db()->DeleteReport(delete_pending.uuid),\n            CrashReportDatabase::kReportNotFound);\n\n  EXPECT_EQ(db()->DeleteReport(delete_completed.uuid),\n            CrashReportDatabase::kNoError);\n  EXPECT_FALSE(FileExists(delete_completed.file_path));\n  EXPECT_EQ(db()->LookUpCrashReport(delete_completed.uuid, &delete_completed),\n            CrashReportDatabase::kReportNotFound);\n  EXPECT_EQ(db()->DeleteReport(delete_completed.uuid),\n            CrashReportDatabase::kReportNotFound);\n\n  EXPECT_EQ(db()->LookUpCrashReport(keep_pending.uuid, &keep_pending),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(db()->LookUpCrashReport(keep_completed.uuid, &keep_completed),\n            CrashReportDatabase::kNoError);\n}\n\nTEST_F(CrashReportDatabaseTest, DeleteReportEmptyingDatabase) {\n  CrashReportDatabase::Report report;\n  CreateCrashReport(&report);\n\n  EXPECT_TRUE(FileExists(report.file_path));\n\n  UploadReport(report.uuid, true, \"1\");\n\n  EXPECT_EQ(db()->LookUpCrashReport(report.uuid, &report),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_TRUE(FileExists(report.file_path));\n\n  // This causes an empty database to be written, make sure this is handled.\n  EXPECT_EQ(db()->DeleteReport(report.uuid), CrashReportDatabase::kNoError);\n  EXPECT_FALSE(FileExists(report.file_path));\n}\n\nTEST_F(CrashReportDatabaseTest, ReadEmptyDatabase) {\n  CrashReportDatabase::Report report;\n  CreateCrashReport(&report);\n  EXPECT_EQ(db()->DeleteReport(report.uuid), CrashReportDatabase::kNoError);\n\n  // Deleting and the creating another report causes an empty database to be\n  // loaded. Make sure this is handled.\n\n  CrashReportDatabase::Report report2;\n  CreateCrashReport(&report2);\n}\n\nTEST_F(CrashReportDatabaseTest, RequestUpload) {\n  std::vector<CrashReportDatabase::Report> reports(2);\n  CreateCrashReport(&reports[0]);\n  CreateCrashReport(&reports[1]);\n\n  const UUID& report_0_uuid = reports[0].uuid;\n  const UUID& report_1_uuid = reports[1].uuid;\n\n  // Skipped report gets back to pending state after RequestUpload is called.\n  EXPECT_EQ(db()->SkipReportUpload(\n                report_1_uuid, Metrics::CrashSkippedReason::kUploadsDisabled),\n            CrashReportDatabase::kNoError);\n\n  std::vector<CrashReportDatabase::Report> pending_reports;\n  CrashReportDatabase::OperationStatus os =\n      db()->GetPendingReports(&pending_reports);\n  EXPECT_EQ(os, CrashReportDatabase::kNoError);\n  ASSERT_EQ(pending_reports.size(), 1u);\n  EXPECT_EQ(report_0_uuid, pending_reports[0].uuid);\n\n  pending_reports.clear();\n  EXPECT_EQ(RequestUpload(report_1_uuid), CrashReportDatabase::kNoError);\n  os = db()->GetPendingReports(&pending_reports);\n  EXPECT_EQ(os, CrashReportDatabase::kNoError);\n  ASSERT_EQ(pending_reports.size(), 2u);\n\n  // Check individual reports.\n  const CrashReportDatabase::Report* explicitly_requested_report;\n  const CrashReportDatabase::Report* pending_report;\n  if (pending_reports[0].uuid == report_0_uuid) {\n    pending_report = &pending_reports[0];\n    explicitly_requested_report = &pending_reports[1];\n  } else {\n    pending_report = &pending_reports[1];\n    explicitly_requested_report = &pending_reports[0];\n  }\n\n  EXPECT_EQ(pending_report->uuid, report_0_uuid);\n  EXPECT_FALSE(pending_report->upload_explicitly_requested);\n\n  EXPECT_EQ(explicitly_requested_report->uuid, report_1_uuid);\n  EXPECT_TRUE(explicitly_requested_report->upload_explicitly_requested);\n\n  // Explicitly requested reports will not have upload_explicitly_requested bit\n  // after getting skipped.\n  EXPECT_EQ(db()->SkipReportUpload(\n                report_1_uuid, Metrics::CrashSkippedReason::kUploadsDisabled),\n            CrashReportDatabase::kNoError);\n  CrashReportDatabase::Report report;\n  EXPECT_EQ(db()->LookUpCrashReport(report_1_uuid, &report),\n            CrashReportDatabase::kNoError);\n  EXPECT_FALSE(report.upload_explicitly_requested);\n\n  // Pending report gets correctly affected after RequestUpload is called.\n  pending_reports.clear();\n  EXPECT_EQ(RequestUpload(report_0_uuid), CrashReportDatabase::kNoError);\n  os = db()->GetPendingReports(&pending_reports);\n  EXPECT_EQ(os, CrashReportDatabase::kNoError);\n  EXPECT_EQ(pending_reports.size(), 1u);\n  EXPECT_EQ(report_0_uuid, pending_reports[0].uuid);\n  EXPECT_TRUE(pending_reports[0].upload_explicitly_requested);\n\n  // Already uploaded report cannot be requested for the new upload.\n  UploadReport(report_0_uuid, true, \"1\");\n  EXPECT_EQ(RequestUpload(report_0_uuid),\n            CrashReportDatabase::kCannotRequestUpload);\n}\n\nTEST_F(CrashReportDatabaseTest, Attachments) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),\n            CrashReportDatabase::kNoError);\n\n  FileWriter* attach_some_file = new_report->AddAttachment(\"some_file\");\n  ASSERT_NE(attach_some_file, nullptr);\n  static constexpr char test_data[] = \"test data\";\n  attach_some_file->Write(test_data, sizeof(test_data));\n\n  FileWriter* failed_attach = new_report->AddAttachment(\"not/a valid fi!e\");\n  EXPECT_EQ(failed_attach, nullptr);\n\n  UUID expect_uuid = new_report->ReportID();\n  UUID uuid;\n  ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(uuid, expect_uuid);\n\n  CrashReportDatabase::Report report;\n  EXPECT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kNoError);\n  ExpectPreparedCrashReport(report);\n\n  std::vector<CrashReportDatabase::Report> reports;\n  EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 1u);\n  EXPECT_EQ(reports[0].uuid, report.uuid);\n\n  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;\n  ASSERT_EQ(db()->GetReportForUploading(reports[0].uuid, &upload_report),\n            CrashReportDatabase::kNoError);\n  std::map<std::string, FileReader*> result_attachments =\n      upload_report->GetAttachments();\n  EXPECT_EQ(result_attachments.size(), 1u);\n  EXPECT_NE(result_attachments.find(\"some_file\"), result_attachments.end());\n  char result_buffer[sizeof(test_data)];\n  result_attachments[\"some_file\"]->Read(result_buffer, sizeof(result_buffer));\n  EXPECT_EQ(memcmp(test_data, result_buffer, sizeof(test_data)), 0);\n}\n\nTEST_F(CrashReportDatabaseTest, OrphanedAttachments) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),\n            CrashReportDatabase::kNoError);\n\n  FileWriter* file1 = new_report->AddAttachment(\"file1\");\n  ASSERT_NE(file1, nullptr);\n  FileWriter* file2 = new_report->AddAttachment(\"file2\");\n  ASSERT_NE(file2, nullptr);\n\n  UUID expect_uuid = new_report->ReportID();\n  UUID uuid;\n  ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(uuid, expect_uuid);\n\n#if BUILDFLAG(IS_WIN)\n  const std::wstring uuid_string = uuid.ToWString();\n#else\n  const std::string uuid_string = uuid.ToString();\n#endif\n  const base::FilePath report_attachments_dir(\n      path().Append(FILE_PATH_LITERAL(\"attachments\")).Append(uuid_string));\n  const base::FilePath file_path1(\n      report_attachments_dir.Append(FILE_PATH_LITERAL(\"file1\")));\n  const base::FilePath file_path2(\n      report_attachments_dir.Append(FILE_PATH_LITERAL(\"file2\")));\n\n  CrashReportDatabase::Report report;\n  ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kNoError);\n\n  // Cleaning a consistent database does not remove the attachements.\n  EXPECT_EQ(db()->CleanDatabase(0), 0);\n  EXPECT_TRUE(FileExists(file_path1));\n  EXPECT_TRUE(FileExists(file_path1));\n\n  ASSERT_TRUE(LoggingRemoveFile(report.file_path));\n\n#if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN)\n  // CrashReportDatabaseMac stores metadata in xattrs and does not have .meta\n  // files.\n  // CrashReportDatabaseWin stores metadata in a global metadata file and not\n  // per report.\n  ASSERT_TRUE(LoggingRemoveFile(base::FilePath(\n      report.file_path.RemoveFinalExtension().value() + \".meta\")));\n#endif\n\n  ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kReportNotFound);\n\n  EXPECT_TRUE(FileExists(file_path1));\n  EXPECT_TRUE(FileExists(file_path1));\n\n#if BUILDFLAG(IS_WIN)\n  // On Windows, reports removed from metadata are counted, even if the file\n  // is not on the disk.\n  EXPECT_EQ(db()->CleanDatabase(0), 1);\n#else\n  EXPECT_EQ(db()->CleanDatabase(0), 0);\n#endif\n\n  EXPECT_FALSE(FileExists(file_path1));\n  EXPECT_FALSE(FileExists(file_path2));\n  EXPECT_FALSE(FileExists(report_attachments_dir));\n}\n\n// This test uses knowledge of the database format to break it, so it only\n// applies to the unfified database implementation.\n#if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN)\nTEST_F(CrashReportDatabaseTest, CleanBrokenDatabase) {\n  // Remove report files if metadata goes missing.\n  CrashReportDatabase::Report report;\n  ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report));\n\n  const base::FilePath metadata(\n      report.file_path.RemoveFinalExtension().value() +\n      FILE_PATH_LITERAL(\".meta\"));\n  ASSERT_TRUE(PathExists(report.file_path));\n  ASSERT_TRUE(PathExists(metadata));\n\n  ASSERT_TRUE(LoggingRemoveFile(metadata));\n  EXPECT_EQ(db()->CleanDatabase(0), 1);\n\n  EXPECT_FALSE(PathExists(report.file_path));\n  EXPECT_FALSE(PathExists(metadata));\n\n  // Remove metadata files if reports go missing.\n  ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report));\n  const base::FilePath metadata2(\n      report.file_path.RemoveFinalExtension().value() +\n      FILE_PATH_LITERAL(\".meta\"));\n  ASSERT_TRUE(PathExists(report.file_path));\n  ASSERT_TRUE(PathExists(metadata2));\n\n  ASSERT_TRUE(LoggingRemoveFile(report.file_path));\n  EXPECT_EQ(db()->CleanDatabase(0), 1);\n\n  EXPECT_FALSE(PathExists(report.file_path));\n  EXPECT_FALSE(PathExists(metadata2));\n\n  // Remove stale new files.\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  EXPECT_EQ(db()->PrepareNewCrashReport(&new_report),\n            CrashReportDatabase::kNoError);\n  new_report->Writer()->Close();\n  EXPECT_EQ(db()->CleanDatabase(0), 1);\n\n  // Remove stale lock files and their associated reports.\n  ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report));\n  const base::FilePath metadata3(\n      report.file_path.RemoveFinalExtension().value() +\n      FILE_PATH_LITERAL(\".meta\"));\n  ASSERT_TRUE(PathExists(report.file_path));\n  ASSERT_TRUE(PathExists(metadata3));\n\n  const base::FilePath lockpath(\n      report.file_path.RemoveFinalExtension().value() +\n      FILE_PATH_LITERAL(\".lock\"));\n  ScopedFileHandle handle(LoggingOpenFileForWrite(\n      lockpath, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));\n  ASSERT_TRUE(handle.is_valid());\n\n  time_t expired_timestamp = time(nullptr) - 60 * 60 * 24 * 3;\n\n  ASSERT_TRUE(LoggingWriteFile(\n      handle.get(), &expired_timestamp, sizeof(expired_timestamp)));\n  ASSERT_TRUE(LoggingCloseFile(handle.release()));\n\n  EXPECT_EQ(db()->CleanDatabase(0), 1);\n\n  EXPECT_FALSE(PathExists(report.file_path));\n  EXPECT_FALSE(PathExists(metadata3));\n}\n#endif  // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN)\n\nTEST_F(CrashReportDatabaseTest, TotalSize_MainReportOnly) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),\n            CrashReportDatabase::kNoError);\n\n  // Main report.\n  static constexpr char main_report_data[] = \"dlbvandslhb\";\n  ASSERT_TRUE(\n      new_report->Writer()->Write(main_report_data, sizeof(main_report_data)));\n\n  UUID uuid;\n  ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),\n            CrashReportDatabase::kNoError);\n\n  CrashReportDatabase::Report report;\n  ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kNoError);\n\n  EXPECT_EQ(report.total_size, sizeof(main_report_data));\n}\n\nTEST_F(CrashReportDatabaseTest, GetReportSize_RightSizeWithAttachments) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),\n            CrashReportDatabase::kNoError);\n\n  // Main report.\n  static constexpr char main_report_data[] = \"dlbvandslhb\";\n  ASSERT_TRUE(\n      new_report->Writer()->Write(main_report_data, sizeof(main_report_data)));\n\n  // First attachment.\n  FileWriter* attachment_1 = new_report->AddAttachment(\"my_attachment_1\");\n  ASSERT_NE(attachment_1, nullptr);\n  static constexpr char attachment_1_data[] = \"vKDnidhvbiudshoihbvdsoiuh nhh\";\n  attachment_1->Write(attachment_1_data, sizeof(attachment_1_data));\n\n  // Second attachment.\n  FileWriter* attachment_2 = new_report->AddAttachment(\"my_attachment_2\");\n  ASSERT_NE(attachment_2, nullptr);\n  static constexpr char attachment_2_data[] = \"sgvsvgusiyguysigfkhpmo-[\";\n  attachment_2->Write(attachment_2_data, sizeof(attachment_2_data));\n\n  UUID uuid;\n  ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),\n            CrashReportDatabase::kNoError);\n\n  CrashReportDatabase::Report report;\n  ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(report.total_size,\n            sizeof(main_report_data) + sizeof(attachment_1_data) +\n                sizeof(attachment_2_data));\n}\n\nTEST_F(CrashReportDatabaseTest, InitializeFromLargerFileRetainsClientId) {\n  // Initialize the database for the first time, creating it.\n  ASSERT_TRUE(db());\n\n  const base::FilePath settings_path =\n      path().Append(FILE_PATH_LITERAL(\"settings.dat\"));\n  EXPECT_FALSE(FileExists(settings_path));\n\n  Settings* settings = db()->GetSettings();\n  ASSERT_TRUE(settings);\n  EXPECT_TRUE(FileExists(settings_path));\n\n  UUID client_id;\n  ASSERT_TRUE(settings->GetClientID(&client_id));\n  EXPECT_NE(client_id, UUID());\n\n  // Close and reopen the database at the same path.\n  ResetDatabase();\n  EXPECT_FALSE(db());\n  EXPECT_TRUE(FileExists(settings_path));\n\n  // Append some data, to ensure that we can open settings even if a future\n  // version has added additional field to Settings (forwards compatible).\n  FileWriter settings_writer;\n  ASSERT_TRUE(settings_writer.Open(\n      settings_path, FileWriteMode::kReuseOrFail, FilePermissions::kOwnerOnly));\n  ASSERT_NE(settings_writer.Seek(0, SEEK_END), 0);\n  constexpr uint64_t extra_garbage = 0xBADF00D;\n  ASSERT_TRUE(settings_writer.Write(&extra_garbage, sizeof(extra_garbage)));\n  settings_writer.Close();\n\n  auto db = CrashReportDatabase::InitializeWithoutCreating(path());\n  ASSERT_TRUE(db);\n\n  settings = db->GetSettings();\n  ASSERT_TRUE(settings);\n\n  // Make sure that the reopened settings retained the original client id and\n  // wasn't recreated.\n  UUID reopened_client_id;\n  ASSERT_TRUE(settings->GetClientID(&reopened_client_id));\n  EXPECT_EQ(client_id, reopened_client_id);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crash_report_database_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crash_report_database.h\"\n\n#include <windows.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/types.h>\n#include <time.h>\n#include <wchar.h>\n\n#include <mutex>\n#include <tuple>\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_math.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"client/settings.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/metrics.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nconstexpr wchar_t kReportsDirectory[] = L\"reports\";\nconstexpr wchar_t kMetadataFileName[] = L\"metadata\";\n\nconstexpr wchar_t kSettings[] = L\"settings.dat\";\n\nconstexpr wchar_t kCrashReportFileExtension[] = L\"dmp\";\n\nconstexpr uint32_t kMetadataFileHeaderMagic = 'CPAD';\nconstexpr uint32_t kMetadataFileVersion = 1;\n\nusing OperationStatus = CrashReportDatabase::OperationStatus;\n\n// Helpers ---------------------------------------------------------------------\n\n// Adds a string to the string table and returns the byte index where it was\n// added.\nuint32_t AddStringToTable(std::string* string_table, const std::string& str) {\n  uint32_t offset = base::checked_cast<uint32_t>(string_table->size());\n  *string_table += str;\n  *string_table += '\\0';\n  return offset;\n}\n\n// Converts |str| to UTF8, adds the result to the string table and returns the\n// byte index where it was added.\nuint32_t AddStringToTable(std::string* string_table, const std::wstring& str) {\n  return AddStringToTable(string_table, base::WideToUTF8(str));\n}\n\n// Reads from the current file position to EOF and returns as a string of bytes.\nstd::string ReadRestOfFileAsString(FileHandle file) {\n  FileOffset read_from = LoggingSeekFile(file, 0, SEEK_CUR);\n  FileOffset end = LoggingSeekFile(file, 0, SEEK_END);\n  FileOffset original = LoggingSeekFile(file, read_from, SEEK_SET);\n  if (read_from == -1 || end == -1 || original == -1 || read_from == end)\n    return std::string();\n  DCHECK_EQ(read_from, original);\n  DCHECK_GT(end, read_from);\n  size_t data_length = static_cast<size_t>(end - read_from);\n  std::string buffer(data_length, '\\0');\n  return LoggingReadFileExactly(file, &buffer[0], data_length) ? buffer\n                                                               : std::string();\n}\n\nbool UUIDFromReportPath(const base::FilePath& path, UUID* uuid) {\n  return uuid->InitializeFromString(\n      path.RemoveFinalExtension().BaseName().value());\n}\n\n// Helper structures, and conversions ------------------------------------------\n\n// The format of the on disk metadata file is a MetadataFileHeader, followed by\n// a number of fixed size records of MetadataFileReportRecord, followed by a\n// string table in UTF8 format, where each string is \\0 terminated.\nstruct MetadataFileHeader {\n  uint32_t magic;\n  uint32_t version;\n  uint32_t num_records;\n  uint32_t padding;\n};\n\nstruct ReportDisk;\n\nenum class ReportState {\n  //! \\brief Created and filled out by caller, owned by database.\n  kPending,\n  //! \\brief In the process of uploading, owned by caller.\n  kUploading,\n  //! \\brief Upload completed or skipped, owned by database.\n  kCompleted,\n};\n\nenum {\n  //! \\brief Corresponds to uploaded bit of the report state.\n  kAttributeUploaded = 1 << 0,\n\n  //! \\brief Corresponds to upload_explicity_requested bit of the report state.\n  kAttributeUploadExplicitlyRequested = 1 << 1,\n};\n\nstruct MetadataFileReportRecord {\n  // Note that this default constructor does no initialization. It is used only\n  // to create an array of records that are immediately initialized by reading\n  // from disk in Metadata::Read().\n  MetadataFileReportRecord() {}\n\n  // Constructs from a ReportDisk, adding to |string_table| and storing indices\n  // as strings into that table.\n  MetadataFileReportRecord(const ReportDisk& report, std::string* string_table);\n\n  UUID uuid;  // UUID is a 16 byte, standard layout structure.\n  uint32_t file_path_index;  // Index into string table. File name is relative\n                             // to the reports directory when on disk.\n  uint32_t id_index;  // Index into string table.\n  int64_t creation_time;  // Holds a time_t.\n  int64_t last_upload_attempt_time;  // Holds a time_t.\n  int32_t upload_attempts;\n  int32_t state;  // A ReportState.\n  uint8_t attributes;  // Bitfield of kAttribute*.\n  uint8_t padding[7];\n};\n\n//! \\brief A private extension of the Report class that includes additional data\n//!     that's stored on disk in the metadata file.\nstruct ReportDisk : public CrashReportDatabase::Report {\n  ReportDisk(const MetadataFileReportRecord& record,\n             const base::FilePath& report_dir,\n             const std::string& string_table);\n\n  ReportDisk(const UUID& uuid,\n             const base::FilePath& path,\n             time_t creation_tim,\n             ReportState state);\n\n  //! \\brief The current state of the report.\n  ReportState state;\n};\n\nMetadataFileReportRecord::MetadataFileReportRecord(const ReportDisk& report,\n                                                   std::string* string_table)\n    : uuid(report.uuid),\n      file_path_index(\n          AddStringToTable(string_table, report.file_path.BaseName().value())),\n      id_index(AddStringToTable(string_table, report.id)),\n      creation_time(report.creation_time),\n      last_upload_attempt_time(report.last_upload_attempt_time),\n      upload_attempts(report.upload_attempts),\n      state(static_cast<uint32_t>(report.state)),\n      attributes((report.uploaded ? kAttributeUploaded : 0) |\n                 (report.upload_explicitly_requested\n                      ? kAttributeUploadExplicitlyRequested\n                      : 0)) {\n  memset(&padding, 0, sizeof(padding));\n}\n\nReportDisk::ReportDisk(const MetadataFileReportRecord& record,\n                       const base::FilePath& report_dir,\n                       const std::string& string_table)\n    : Report() {\n  uuid = record.uuid;\n  file_path = report_dir.Append(\n      base::UTF8ToWide(&string_table[record.file_path_index]));\n  id = &string_table[record.id_index];\n  creation_time = record.creation_time;\n  last_upload_attempt_time = record.last_upload_attempt_time;\n  upload_attempts = record.upload_attempts;\n  state = static_cast<ReportState>(record.state);\n  uploaded = (record.attributes & kAttributeUploaded) != 0;\n  upload_explicitly_requested =\n      (record.attributes & kAttributeUploadExplicitlyRequested) != 0;\n}\n\nReportDisk::ReportDisk(const UUID& uuid,\n                       const base::FilePath& path,\n                       time_t creation_time,\n                       ReportState state)\n    : Report() {\n  this->uuid = uuid;\n  this->file_path = path;\n  this->creation_time = creation_time;\n  this->state = state;\n}\n\n// Metadata --------------------------------------------------------------------\n\n//! \\brief Manages the metadata for the set of reports, handling serialization\n//!     to disk, and queries.\nclass Metadata {\n public:\n  Metadata(const Metadata&) = delete;\n  Metadata& operator=(const Metadata&) = delete;\n\n  //! \\brief Writes any changes if necessary, unlocks and closes the file\n  //!     handle.\n  ~Metadata();\n\n  static std::unique_ptr<Metadata> Create(\n      const base::FilePath& metadata_file,\n      const base::FilePath& report_dir,\n      const base::FilePath& attachments_dir);\n\n  //! \\brief Adds a new report to the set.\n  //!\n  //! \\param[in] new_report_disk The record to add. The #state field must be set\n  //!     to kPending.\n  void AddNewRecord(const ReportDisk& new_report_disk);\n\n  //! \\brief Finds all reports in a given state. The \\a reports vector is only\n  //!     valid when CrashReportDatabase::kNoError is returned.\n  //!\n  //! \\param[in] desired_state The state to match.\n  //! \\param[out] reports Matching reports, must be empty on entry.\n  OperationStatus FindReports(\n      ReportState desired_state,\n      std::vector<CrashReportDatabase::Report>* reports) const;\n\n  //! \\brief Finds the report matching the given UUID.\n  //!\n  //! The returned report is only valid if CrashReportDatabase::kNoError is\n  //! returned.\n  //!\n  //! \\param[in] uuid The report identifier.\n  //! \\param[out] report_disk The found report, valid only if\n  //!     CrashReportDatabase::kNoError is returned. Ownership is not\n  //!     transferred to the caller, and the report may not be modified.\n  OperationStatus FindSingleReport(const UUID& uuid,\n                                   const ReportDisk** report_disk) const;\n\n  //! \\brief Finds a single report matching the given UUID and in the desired\n  //!     state, and returns a mutable ReportDisk* if found.\n  //!\n  //! This marks the metadata as dirty, and on destruction, changes will be\n  //! written to disk via Write().\n  //!\n  //! \\return #kNoError on success. #kReportNotFound if there was no report with\n  //!     the specified UUID, or if the report was not in the specified state\n  //!     and was not uploading. #kBusyError if the report was not in the\n  //!     specified state and was uploading.\n  OperationStatus FindSingleReportAndMarkDirty(const UUID& uuid,\n                                               ReportState desired_state,\n                                               ReportDisk** report_disk);\n\n  //! \\brief Removes a report from the metadata database, without touching the\n  //!     on-disk file.\n  //!\n  //! The returned report is only valid if CrashReportDatabase::kNoError is\n  //! returned. This will mark the database as dirty. Future metadata\n  //! operations for this report will not succeed.\n  //!\n  //! \\param[in] uuid The report identifier to remove.\n  //! \\param[out] report_path The found report's file_path, valid only if\n  //!     CrashReportDatabase::kNoError is returned.\n  OperationStatus DeleteReport(const UUID& uuid,\n                               base::FilePath* report_path);\n\n  //! \\brief Removes reports from the metadata database, that don't have\n  //!     corresponding report files.\n  //!\n  //! \\return number of metadata entries removed\n  int CleanDatabase();\n\n private:\n  Metadata(FileHandle handle,\n           const base::FilePath& report_dir,\n           const base::FilePath& attachments_dir);\n\n  bool Rewind();\n\n  void Read();\n  void Write();\n\n  //! \\brief Confirms that the corresponding report actually exists on disk\n  //!     (that is, the dump file has not been removed), and that the report is\n  //!     in the given state.\n  static OperationStatus VerifyReport(const ReportDisk& report_disk,\n                                      ReportState desired_state);\n  //! \\brief Confirms that the corresponding report actually exists on disk\n  //!     (that is, the dump file has not been removed).\n  static OperationStatus VerifyReportAnyState(const ReportDisk& report_disk);\n\n  ScopedFileHandle handle_;\n  const base::FilePath report_dir_;\n  const base::FilePath attachments_dir_;\n  bool dirty_;  //! \\brief `true` when a Write() is required on destruction.\n  std::vector<ReportDisk> reports_;\n};\n\nMetadata::~Metadata() {\n  if (dirty_)\n    Write();\n  // Not actually async, UnlockFileEx requires the Offset fields.\n  OVERLAPPED overlapped = {0};\n  if (!UnlockFileEx(handle_.get(), 0, MAXDWORD, MAXDWORD, &overlapped))\n    PLOG(ERROR) << \"UnlockFileEx\";\n}\n\n// static\nstd::unique_ptr<Metadata> Metadata::Create(\n    const base::FilePath& metadata_file,\n    const base::FilePath& report_dir,\n    const base::FilePath& attachments_dir) {\n  // It is important that dwShareMode be non-zero so that concurrent access to\n  // this file results in a successful open. This allows us to get to LockFileEx\n  // which then blocks to guard access.\n  FileHandle handle = CreateFile(metadata_file.value().c_str(),\n                                 GENERIC_READ | GENERIC_WRITE,\n                                 FILE_SHARE_READ | FILE_SHARE_WRITE,\n                                 nullptr,\n                                 OPEN_ALWAYS,\n                                 FILE_ATTRIBUTE_NORMAL,\n                                 nullptr);\n  if (handle == kInvalidFileHandle)\n    return std::unique_ptr<Metadata>();\n  // Not actually async, LockFileEx requires the Offset fields.\n  OVERLAPPED overlapped = {0};\n  if (!LockFileEx(handle,\n                  LOCKFILE_EXCLUSIVE_LOCK,\n                  0,\n                  MAXDWORD,\n                  MAXDWORD,\n                  &overlapped)) {\n    PLOG(ERROR) << \"LockFileEx\";\n    return std::unique_ptr<Metadata>();\n  }\n\n  std::unique_ptr<Metadata> metadata(\n      new Metadata(handle, report_dir, attachments_dir));\n  // If Read() fails, for whatever reason (corruption, etc.) metadata will not\n  // have been modified and will be in a clean empty state. We continue on and\n  // return an empty database to hopefully recover. This means that existing\n  // crash reports have been orphaned.\n  metadata->Read();\n  return metadata;\n}\n\nvoid Metadata::AddNewRecord(const ReportDisk& new_report_disk) {\n  DCHECK(new_report_disk.state == ReportState::kPending);\n  reports_.push_back(new_report_disk);\n  dirty_ = true;\n}\n\nOperationStatus Metadata::FindReports(\n    ReportState desired_state,\n    std::vector<CrashReportDatabase::Report>* reports) const {\n  DCHECK(reports->empty());\n  for (const auto& report : reports_) {\n    if (report.state == desired_state &&\n        VerifyReport(report, desired_state) == CrashReportDatabase::kNoError) {\n      reports->push_back(report);\n    }\n  }\n  return CrashReportDatabase::kNoError;\n}\n\nOperationStatus Metadata::FindSingleReport(\n    const UUID& uuid,\n    const ReportDisk** out_report) const {\n  auto report_iter = std::find_if(\n      reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) {\n        return report.uuid == uuid;\n      });\n  if (report_iter == reports_.end())\n    return CrashReportDatabase::kReportNotFound;\n  OperationStatus os = VerifyReportAnyState(*report_iter);\n  if (os == CrashReportDatabase::kNoError)\n    *out_report = &*report_iter;\n  return os;\n}\n\nOperationStatus Metadata::FindSingleReportAndMarkDirty(\n    const UUID& uuid,\n    ReportState desired_state,\n    ReportDisk** report_disk) {\n  auto report_iter = std::find_if(\n      reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) {\n        return report.uuid == uuid;\n      });\n  if (report_iter == reports_.end())\n    return CrashReportDatabase::kReportNotFound;\n  OperationStatus os = VerifyReport(*report_iter, desired_state);\n  if (os == CrashReportDatabase::kNoError) {\n    dirty_ = true;\n    *report_disk = &*report_iter;\n  }\n  return os;\n}\n\nOperationStatus Metadata::DeleteReport(const UUID& uuid,\n                                       base::FilePath* report_path) {\n  auto report_iter = std::find_if(\n      reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) {\n        return report.uuid == uuid;\n      });\n  if (report_iter == reports_.end())\n    return CrashReportDatabase::kReportNotFound;\n  *report_path = report_iter->file_path;\n  reports_.erase(report_iter);\n  dirty_ = true;\n  return CrashReportDatabase::kNoError;\n}\n\nint Metadata::CleanDatabase() {\n  int removed = 0;\n  for (auto report_iter = reports_.begin(); report_iter != reports_.end();) {\n    if (!IsRegularFile(report_iter->file_path)) {\n      report_iter = reports_.erase(report_iter);\n      ++removed;\n      dirty_ = true;\n    } else {\n      ++report_iter;\n    }\n  }\n  return removed;\n}\n\nMetadata::Metadata(FileHandle handle,\n                   const base::FilePath& report_dir,\n                   const base::FilePath& attachments_dir)\n    : handle_(handle),\n      report_dir_(report_dir),\n      attachments_dir_(attachments_dir),\n      dirty_(false),\n      reports_() {}\n\nbool Metadata::Rewind() {\n  FileOffset result = LoggingSeekFile(handle_.get(), 0, SEEK_SET);\n  DCHECK_EQ(result, 0);\n  return result == 0;\n}\n\nvoid Metadata::Read() {\n  FileOffset length = LoggingSeekFile(handle_.get(), 0, SEEK_END);\n  if (length <= 0)  // Failed, or empty: Abort.\n    return;\n  if (!Rewind()) {\n    LOG(ERROR) << \"failed to rewind to read\";\n    return;\n  }\n\n  MetadataFileHeader header;\n  if (!LoggingReadFileExactly(handle_.get(), &header, sizeof(header))) {\n    LOG(ERROR) << \"failed to read header\";\n    return;\n  }\n  if (header.magic != kMetadataFileHeaderMagic ||\n      header.version != kMetadataFileVersion) {\n    LOG(ERROR) << \"unexpected header\";\n    return;\n  }\n\n  base::CheckedNumeric<uint32_t> records_size =\n      base::CheckedNumeric<uint32_t>(header.num_records) *\n      static_cast<uint32_t>(sizeof(MetadataFileReportRecord));\n  if (!records_size.IsValid()) {\n    LOG(ERROR) << \"record size out of range\";\n    return;\n  }\n\n  std::vector<ReportDisk> reports;\n  if (header.num_records > 0) {\n    std::vector<MetadataFileReportRecord> records(header.num_records);\n    if (!LoggingReadFileExactly(\n            handle_.get(), &records[0], records_size.ValueOrDie())) {\n      LOG(ERROR) << \"failed to read records\";\n      return;\n    }\n\n    std::string string_table = ReadRestOfFileAsString(handle_.get());\n    if (string_table.empty() || string_table.back() != '\\0') {\n      LOG(ERROR) << \"bad string table\";\n      return;\n    }\n\n    for (const auto& record : records) {\n      if (record.file_path_index >= string_table.size() ||\n          record.id_index >= string_table.size()) {\n        LOG(ERROR) << \"invalid string table index\";\n        return;\n      }\n      ReportDisk report_disk(record, report_dir_, string_table);\n\n      report_disk.total_size = GetFileSize(report_disk.file_path);\n      base::FilePath report_attachment_dir =\n          attachments_dir_.Append(report_disk.uuid.ToWString());\n      report_disk.total_size += GetDirectorySize(report_attachment_dir);\n      reports.push_back(report_disk);\n    }\n  }\n  reports_.swap(reports);\n}\n\nvoid Metadata::Write() {\n  if (!Rewind()) {\n    LOG(ERROR) << \"failed to rewind to write\";\n    return;\n  }\n\n  // Truncate to ensure that a partial write doesn't cause a mix of old and new\n  // data causing an incorrect interpretation on read.\n  if (!SetEndOfFile(handle_.get())) {\n    PLOG(ERROR) << \"failed to truncate\";\n    return;\n  }\n\n  size_t num_records = reports_.size();\n\n  // Fill and write out the header.\n  MetadataFileHeader header = {0};\n  header.magic = kMetadataFileHeaderMagic;\n  header.version = kMetadataFileVersion;\n  header.num_records = base::checked_cast<uint32_t>(num_records);\n  if (!LoggingWriteFile(handle_.get(), &header, sizeof(header))) {\n    LOG(ERROR) << \"failed to write header\";\n    return;\n  }\n\n  if (num_records == 0)\n    return;\n\n  // Build the records and string table we're going to write.\n  std::string string_table;\n  std::vector<MetadataFileReportRecord> records;\n  records.reserve(num_records);\n  for (const auto& report : reports_) {\n    const base::FilePath& path = report.file_path;\n    if (path.DirName() != report_dir_) {\n      LOG(ERROR) << path << \" expected to start with \" << report_dir_;\n      return;\n    }\n    records.push_back(MetadataFileReportRecord(report, &string_table));\n  }\n\n  if (!LoggingWriteFile(handle_.get(),\n                        &records[0],\n                        records.size() * sizeof(MetadataFileReportRecord))) {\n    LOG(ERROR) << \"failed to write records\";\n    return;\n  }\n  if (!LoggingWriteFile(\n          handle_.get(), string_table.c_str(), string_table.size())) {\n    LOG(ERROR) << \"failed to write string table\";\n    return;\n  }\n}\n\n// static\nOperationStatus Metadata::VerifyReportAnyState(const ReportDisk& report_disk) {\n  DWORD fileattr = GetFileAttributes(report_disk.file_path.value().c_str());\n  if (fileattr == INVALID_FILE_ATTRIBUTES)\n    return CrashReportDatabase::kReportNotFound;\n  return (fileattr & FILE_ATTRIBUTE_DIRECTORY)\n             ? CrashReportDatabase::kFileSystemError\n             : CrashReportDatabase::kNoError;\n}\n\n// static\nOperationStatus Metadata::VerifyReport(const ReportDisk& report_disk,\n                                       ReportState desired_state) {\n  if (report_disk.state == desired_state) {\n    return VerifyReportAnyState(report_disk);\n  }\n\n  return report_disk.state == ReportState::kUploading\n             ? CrashReportDatabase::kBusyError\n             : CrashReportDatabase::kReportNotFound;\n}\n\nbool EnsureDirectory(const base::FilePath& path) {\n  DWORD fileattr = GetFileAttributes(path.value().c_str());\n  if (fileattr == INVALID_FILE_ATTRIBUTES) {\n    PLOG(ERROR) << \"GetFileAttributes \" << path;\n    return false;\n  }\n  if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {\n    LOG(ERROR) << \"GetFileAttributes \" << path << \": not a directory\";\n    return false;\n  }\n  return true;\n}\n\n//! \\brief Ensures that the node at path is a directory, and creates it if it\n//!     does not exist.\n//!\n//! \\return If the path points to a file, rather than a directory, or the\n//!     directory could not be created, returns `false`. Otherwise, returns\n//!     `true`, indicating that path already was or now is a directory.\nbool CreateDirectoryIfNecessary(const base::FilePath& path) {\n  if (CreateDirectory(path.value().c_str(), nullptr))\n    return true;\n  if (GetLastError() != ERROR_ALREADY_EXISTS) {\n    PLOG(ERROR) << \"CreateDirectory \" << base::WideToUTF8(path.value());\n    return false;\n  }\n  return EnsureDirectory(path);\n}\n\n}  // namespace\n\n// CrashReportDatabaseWin ------------------------------------------------------\n\nclass CrashReportDatabaseWin : public CrashReportDatabase {\n public:\n  explicit CrashReportDatabaseWin(const base::FilePath& path);\n\n  CrashReportDatabaseWin(const CrashReportDatabaseWin&) = delete;\n  CrashReportDatabaseWin& operator=(const CrashReportDatabaseWin&) = delete;\n\n  ~CrashReportDatabaseWin() override;\n\n  bool Initialize(bool may_create);\n\n  // CrashReportDatabase:\n  Settings* GetSettings() override;\n  OperationStatus PrepareNewCrashReport(\n      std::unique_ptr<NewReport>* report) override;\n  OperationStatus FinishedWritingCrashReport(std::unique_ptr<NewReport> report,\n                                             UUID* uuid) override;\n  OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override;\n  OperationStatus GetPendingReports(std::vector<Report>* reports) override;\n  OperationStatus GetCompletedReports(std::vector<Report>* reports) override;\n  OperationStatus GetReportForUploading(\n      const UUID& uuid,\n      std::unique_ptr<const UploadReport>* report,\n      bool report_metrics) override;\n  OperationStatus SkipReportUpload(const UUID& uuid,\n                                   Metrics::CrashSkippedReason reason) override;\n  OperationStatus DeleteReport(const UUID& uuid) override;\n  OperationStatus RequestUpload(const UUID& uuid) override;\n  int CleanDatabase(time_t lockfile_ttl) override;\n  base::FilePath DatabasePath() override;\n\n private:\n  // CrashReportDatabase:\n  OperationStatus RecordUploadAttempt(UploadReport* report,\n                                      bool successful,\n                                      const std::string& id) override;\n\n  // Cleans any attachments that have no associated report.\n  void CleanOrphanedAttachments();\n\n  std::unique_ptr<Metadata> AcquireMetadata();\n\n  Settings& SettingsInternal() {\n    std::call_once(settings_init_, [this]() { settings_.Initialize(); });\n    return settings_;\n  }\n\n  const base::FilePath base_dir_;\n  Settings settings_;\n  std::once_flag settings_init_;\n  InitializationStateDcheck initialized_;\n};\n\nCrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path)\n    : CrashReportDatabase(),\n      base_dir_(path),\n      settings_(path.Append(kSettings)),\n      settings_init_(),\n      initialized_() {}\n\nCrashReportDatabaseWin::~CrashReportDatabaseWin() {\n}\n\nbool CrashReportDatabaseWin::Initialize(bool may_create) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  // Ensure the database directory exists.\n  if (may_create) {\n    if (!CreateDirectoryIfNecessary(base_dir_))\n      return false;\n  } else if (!EnsureDirectory(base_dir_)) {\n    return false;\n  }\n\n  // Ensure that the report subdirectory exists.\n  if (!CreateDirectoryIfNecessary(base_dir_.Append(kReportsDirectory)))\n    return false;\n\n  if (!CreateDirectoryIfNecessary(AttachmentsRootPath()))\n    return false;\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbase::FilePath CrashReportDatabaseWin::DatabasePath() {\n  return base_dir_;\n}\n\nSettings* CrashReportDatabaseWin::GetSettings() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &SettingsInternal();\n}\n\nOperationStatus CrashReportDatabaseWin::PrepareNewCrashReport(\n    std::unique_ptr<NewReport>* report) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<NewReport> new_report(new NewReport());\n  if (!new_report->Initialize(this,\n                              base_dir_.Append(kReportsDirectory),\n                              std::wstring(L\".\") + kCrashReportFileExtension)) {\n    return kFileSystemError;\n  }\n\n  report->reset(new_report.release());\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport(\n    std::unique_ptr<NewReport> report,\n    UUID* uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  if (!metadata)\n    return kDatabaseError;\n  metadata->AddNewRecord(ReportDisk(report->ReportID(),\n                                    report->file_remover_.get(),\n                                    time(nullptr),\n                                    ReportState::kPending));\n\n  std::ignore = report->file_remover_.release();\n\n  // Close all the attachments and disarm their removers too.\n  for (auto& writer : report->attachment_writers_) {\n    writer->Close();\n  }\n  for (auto& remover : report->attachment_removers_) {\n    std::ignore = remover.release();\n  }\n\n  *uuid = report->ReportID();\n\n  Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);\n  Metrics::CrashReportSize(report->Writer()->Seek(0, SEEK_END));\n\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseWin::LookUpCrashReport(const UUID& uuid,\n                                                          Report* report) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  if (!metadata)\n    return kDatabaseError;\n  // Find and return a copy of the matching report.\n  const ReportDisk* report_disk;\n  OperationStatus os = metadata->FindSingleReport(uuid, &report_disk);\n  if (os == kNoError)\n    *report = *report_disk;\n  return os;\n}\n\nOperationStatus CrashReportDatabaseWin::GetPendingReports(\n    std::vector<Report>* reports) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  return metadata ? metadata->FindReports(ReportState::kPending, reports)\n                  : kDatabaseError;\n}\n\nOperationStatus CrashReportDatabaseWin::GetCompletedReports(\n    std::vector<Report>* reports) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  return metadata ? metadata->FindReports(ReportState::kCompleted, reports)\n                  : kDatabaseError;\n}\n\nOperationStatus CrashReportDatabaseWin::GetReportForUploading(\n    const UUID& uuid,\n    std::unique_ptr<const UploadReport>* report,\n    bool report_metrics) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  if (!metadata)\n    return kDatabaseError;\n\n  ReportDisk* report_disk;\n  OperationStatus os = metadata->FindSingleReportAndMarkDirty(\n      uuid, ReportState::kPending, &report_disk);\n  if (os == kNoError) {\n    report_disk->state = ReportState::kUploading;\n    auto upload_report = std::make_unique<UploadReport>();\n    *implicit_cast<Report*>(upload_report.get()) = *report_disk;\n\n    if (!upload_report->Initialize(upload_report->file_path, this)) {\n      return kFileSystemError;\n    }\n    upload_report->report_metrics_ = report_metrics;\n\n    report->reset(upload_report.release());\n  }\n  return os;\n}\n\nOperationStatus CrashReportDatabaseWin::RecordUploadAttempt(\n    UploadReport* report,\n    bool successful,\n    const std::string& id) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (report->report_metrics_) {\n    Metrics::CrashUploadAttempted(successful);\n  }\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  if (!metadata)\n    return kDatabaseError;\n  ReportDisk* report_disk;\n  OperationStatus os = metadata->FindSingleReportAndMarkDirty(\n      report->uuid, ReportState::kUploading, &report_disk);\n  if (os != kNoError)\n    return os;\n\n  time_t now = time(nullptr);\n\n  report_disk->uploaded = successful;\n  report_disk->id = id;\n  report_disk->last_upload_attempt_time = now;\n  report_disk->upload_attempts++;\n  if (successful) {\n    report_disk->state = ReportState::kCompleted;\n    report_disk->upload_explicitly_requested = false;\n  } else {\n    report_disk->state = ReportState::kPending;\n    report_disk->upload_explicitly_requested =\n        report->upload_explicitly_requested;\n  }\n\n  if (!SettingsInternal().SetLastUploadAttemptTime(now))\n    return kDatabaseError;\n\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseWin::DeleteReport(const UUID& uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  if (!metadata)\n    return kDatabaseError;\n\n  base::FilePath report_path;\n  OperationStatus os = metadata->DeleteReport(uuid, &report_path);\n  if (os != kNoError)\n    return os;\n\n  if (!DeleteFile(report_path.value().c_str())) {\n    PLOG(ERROR) << \"DeleteFile \" << report_path;\n    return kFileSystemError;\n  }\n\n  RemoveAttachmentsByUUID(uuid);\n\n  return kNoError;\n}\n\nOperationStatus CrashReportDatabaseWin::SkipReportUpload(\n    const UUID& uuid,\n    Metrics::CrashSkippedReason reason) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  Metrics::CrashUploadSkipped(reason);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  if (!metadata)\n    return kDatabaseError;\n  ReportDisk* report_disk;\n  OperationStatus os = metadata->FindSingleReportAndMarkDirty(\n      uuid, ReportState::kPending, &report_disk);\n  if (os == kNoError) {\n    report_disk->state = ReportState::kCompleted;\n    report_disk->upload_explicitly_requested = false;\n  }\n  return os;\n}\n\nstd::unique_ptr<Metadata> CrashReportDatabaseWin::AcquireMetadata() {\n  base::FilePath metadata_file = base_dir_.Append(kMetadataFileName);\n  return Metadata::Create(metadata_file,\n                          base_dir_.Append(kReportsDirectory),\n                          AttachmentsRootPath());\n}\n\nOperationStatus CrashReportDatabaseWin::RequestUpload(const UUID& uuid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  if (!metadata)\n    return kDatabaseError;\n\n  ReportDisk* report_disk;\n  // TODO(gayane): Search for the report only once regardless of its state.\n  OperationStatus os = metadata->FindSingleReportAndMarkDirty(\n      uuid, ReportState::kCompleted, &report_disk);\n  if (os == kReportNotFound) {\n    os = metadata->FindSingleReportAndMarkDirty(\n        uuid, ReportState::kPending, &report_disk);\n  }\n\n  if (os != kNoError)\n    return os;\n\n  // If the crash report has already been uploaded, don't request new upload.\n  if (report_disk->uploaded)\n    return kCannotRequestUpload;\n\n  // Mark the crash report as having upload explicitly requested by the user,\n  // and move it to the pending state.\n  report_disk->upload_explicitly_requested = true;\n  report_disk->state = ReportState::kPending;\n\n  Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated);\n\n  return kNoError;\n}\n\nint CrashReportDatabaseWin::CleanDatabase(time_t lockfile_ttl) {\n  int removed = 0;\n  const base::FilePath dir_path(base_dir_.Append(kReportsDirectory));\n  DirectoryReader reader;\n  if (!reader.Open(dir_path)) {\n    return removed;\n  }\n\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  time_t now = time(nullptr);\n\n  std::unique_ptr<Metadata> metadata(AcquireMetadata());\n  if (!metadata) {\n    return removed;\n  }\n\n  // Remove old reports without metadata.\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    timespec filetime;\n    const base::FilePath report_path(dir_path.Append(filename));\n    if (!FileModificationTime(report_path, &filetime) ||\n        filetime.tv_sec > now - lockfile_ttl) {\n      continue;\n    }\n\n    const ReportDisk* report_disk;\n    UUID uuid;\n    bool is_uuid = UUIDFromReportPath(report_path, &uuid);\n    // ignore files whose base name is not uuid\n    if (!is_uuid) {\n      continue;\n    }\n    OperationStatus os = metadata->FindSingleReport(uuid, &report_disk);\n\n    if (os == OperationStatus::kReportNotFound) {\n      if (LoggingRemoveFile(report_path)) {\n        ++removed;\n        RemoveAttachmentsByUUID(uuid);\n      }\n      continue;\n    }\n  }\n\n  // Remove any metadata records without report files.\n  removed += metadata->CleanDatabase();\n\n  CleanOrphanedAttachments();\n  return removed;\n}\n\nvoid CrashReportDatabaseWin::CleanOrphanedAttachments() {\n  base::FilePath root_attachments_dir = AttachmentsRootPath();\n  DirectoryReader reader;\n  if (!reader.Open(root_attachments_dir)) {\n    return;\n  }\n\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  base::FilePath reports_dir = base_dir_.Append(kReportsDirectory);\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath path(root_attachments_dir.Append(filename));\n    if (IsDirectory(path, false)) {\n      UUID uuid;\n      if (!uuid.InitializeFromString(filename.value())) {\n        LOG(ERROR) << \"unexpected attachment dir name \" << filename;\n        continue;\n      }\n\n      // Remove attachments if corresponding report doesn't exist.\n      base::FilePath report_path = reports_dir.Append(\n          uuid.ToWString() + L\".\" + kCrashReportFileExtension);\n      if (!IsRegularFile(report_path)) {\n        RemoveAttachmentsByUUID(uuid);\n      }\n    }\n  }\n}\n\nnamespace {\n\nstd::unique_ptr<CrashReportDatabase> InitializeInternal(\n    const base::FilePath& path,\n    bool may_create) {\n  std::unique_ptr<CrashReportDatabaseWin> database_win(\n      new CrashReportDatabaseWin(path));\n  return database_win->Initialize(may_create)\n             ? std::move(database_win)\n             : std::unique_ptr<CrashReportDatabaseWin>();\n}\n\n}  // namespace\n\n// static\nstd::unique_ptr<CrashReportDatabase> CrashReportDatabase::Initialize(\n    const base::FilePath& path) {\n  return InitializeInternal(path, true);\n}\n\n// static\nstd::unique_ptr<CrashReportDatabase>\nCrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) {\n  return InitializeInternal(path, false);\n}\n\n// static\nstd::unique_ptr<SettingsReader>\nCrashReportDatabase::GetSettingsReaderForDatabasePath(\n    const base::FilePath& path) {\n  return std::make_unique<SettingsReader>(path.Append(kSettings));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_client.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_\n#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_\n\n#include <functional>\n#include <map>\n#include <set>\n#include <string>\n#include <vector>\n\n#include <stdint.h>\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"util/file/file_io.h\"\n\n#if !BUILDFLAG(IS_FUCHSIA)\n#include \"util/misc/capture_context.h\"\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\n#if BUILDFLAG(IS_APPLE)\n#include \"base/apple/scoped_mach_port.h\"\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#include \"util/win/scoped_handle.h\"\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include <signal.h>\n#include <ucontext.h>\n#endif\n\n#if BUILDFLAG(IS_IOS)\n#include \"client/upload_behavior_ios.h\"\n#include \"handler/user_stream_data_source.h\"  // nogncheck\n#endif\n\nnamespace crashpad {\n\n//! \\brief The primary interface for an application to have Crashpad monitor\n//!     it for crashes.\nclass CrashpadClient {\n public:\n  CrashpadClient();\n\n  CrashpadClient(const CrashpadClient&) = delete;\n  CrashpadClient& operator=(const CrashpadClient&) = delete;\n\n  ~CrashpadClient();\n\n  //! \\brief Starts a Crashpad handler process, performing any necessary\n  //!     handshake to configure it.\n  //!\n  //! This method directs crashes to the Crashpad handler. On macOS, this is\n  //! applicable to this process and all subsequent child processes. On Windows,\n  //! child processes must also register by using SetHandlerIPCPipe().\n  //!\n  //! On macOS, this method starts a Crashpad handler and obtains a Mach send\n  //! right corresponding to a receive right held by the handler process. The\n  //! handler process runs an exception server on this port. This method sets\n  //! the task’s exception port for `EXC_CRASH`, `EXC_RESOURCE`, and `EXC_GUARD`\n  //! exceptions to the Mach send right obtained. The handler will be installed\n  //! with behavior `EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES` and thread\n  //! state flavor `MACHINE_THREAD_STATE`. Exception ports are inherited, so a\n  //! Crashpad handler started here will remain the handler for any child\n  //! processes created after StartHandler() is called. These child processes do\n  //! not need to call StartHandler() or be aware of Crashpad in any way. The\n  //! Crashpad handler will receive crashes from child processes that have\n  //! inherited it as their exception handler even after the process that called\n  //! StartHandler() exits.\n  //!\n  //! On Windows, if \\a asynchronous_start is `true`, this function will not\n  //! directly call `CreateProcess()`, making it suitable for use in a\n  //! `DllMain()`. In that case, the handler is started from a background\n  //! thread, deferring the handler's startup. Nevertheless, regardless of the\n  //! value of \\a asynchronous_start, after calling this method, the global\n  //! unhandled exception filter is set up, and all crashes will be handled by\n  //! Crashpad. Optionally, use WaitForHandlerStart() to join with the\n  //! background thread and retrieve the status of handler startup.\n  //!\n  //! On Fuchsia, this method binds to the exception port of the current default\n  //! job, and starts a Crashpad handler to monitor that port.\n  //!\n  //! On Linux, this method starts a Crashpad handler, connected to this process\n  //! via an `AF_UNIX` socket pair and installs signal handlers to request crash\n  //! dumps on the client's socket end.\n  //!\n  //! \\param[in] handler The path to a Crashpad handler executable.\n  //! \\param[in] database The path to a Crashpad database. The handler will be\n  //!     started with this path as its `--database` argument.\n  //! \\param[in] metrics_dir The path to an already existing directory where\n  //!     metrics files can be stored. The handler will be started with this\n  //!     path as its `--metrics-dir` argument.\n  //! \\param[in] url The URL of an upload server. The handler will be started\n  //!     with this URL as its `--url` argument.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     The handler will be started with an `--annotation` argument for each\n  //!     element in this map.\n  //! \\param[in] arguments Additional arguments to pass to the Crashpad handler.\n  //!     Arguments passed in other parameters and arguments required to perform\n  //!     the handshake are the responsibility of this method, and must not be\n  //!     specified in this parameter.\n  //! \\param[in] restartable If `true`, the handler will be restarted if it\n  //!     dies, if this behavior is supported. This option is not available on\n  //!     all platforms, and does not function on all OS versions. If it is\n  //!     not supported, it will be ignored.\n  //! \\param[out] asynchronous_start If `true`, the handler will be started from\n  //!     a background thread. Optionally, WaitForHandlerStart() can be used at\n  //!     a suitable time to retreive the result of background startup. This\n  //!     option is only used on Windows.\n  //! \\param[in] attachments Vector that stores file paths that should be\n  //!     captured with each report at the time of the crash.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool StartHandler(const base::FilePath& handler,\n                    const base::FilePath& database,\n                    const base::FilePath& metrics_dir,\n                    const std::string& url,\n                    const std::map<std::string, std::string>& annotations,\n                    const std::vector<std::string>& arguments,\n                    bool restartable,\n                    bool asynchronous_start,\n                    const std::vector<base::FilePath>& attachments = {});\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \\\n    DOXYGEN\n  //! \\brief Retrieve the socket and process ID for the handler.\n  //!\n  //! `StartHandler()` must have successfully been called before calling this\n  //!     method.\n  //!\n  //! \\param[out] sock The socket connected to the handler, if not `nullptr`.\n  //! \\param[out] pid The handler's process ID, if not `nullptr`.\n  //! \\return `true` on success. Otherwise `false` with a message logged.\n  static bool GetHandlerSocket(int* sock, pid_t* pid);\n\n  //! \\brief Sets the socket to a presumably-running Crashpad handler process\n  //!      which was started with StartHandler().\n  //!\n  //! This method installs a signal handler to request crash dumps on \\a sock.\n  //!\n  //! \\param[in] sock A socket connected to a Crashpad handler.\n  //! \\param[in] pid The process ID of the handler, used to set the handler as\n  //!     this process' ptracer. 0 indicates it is not necessary to set the\n  //!     handler as this process' ptracer. -1 indicates that the handler's\n  //!     process ID should be determined by communicating over the socket.\n  bool SetHandlerSocket(ScopedFileHandle sock, pid_t pid);\n\n  //! \\brief Uses `sigaltstack()` to allocate a signal stack for the calling\n  //!     thread.\n  //!\n  //! This method allocates an alternate stack to handle signals delivered to\n  //! the calling thread and should be called early in the lifetime of each\n  //! thread. Installing an alternate stack allows signals to be delivered in\n  //! the event that the call stack's stack pointer points to invalid memory,\n  //! as in the case of stack overflow.\n  //!\n  //! This method is called automatically by SetHandlerSocket() and\n  //! the various StartHandler() methods. It is harmless to call multiple times.\n  //! A new signal stack will be allocated only if there is no existing stack or\n  //! the existing stack is too small. The stack will be automatically freed\n  //! when the thread exits.\n  //!\n  //! An application might choose to diligently call this method from the start\n  //! routine for each thread, call it from a `pthread_create()` wrapper which\n  //! the application provides, or link the provided \"client:pthread_create\"\n  //! target.\n  //!\n  //! \\return `true` on success. Otherwise `false` with a message logged.\n  static bool InitializeSignalStackForThread();\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS) || DOXYGEN\n\n#if BUILDFLAG(IS_ANDROID) || DOXYGEN\n  //! \\brief Installs a signal handler to execute `/system/bin/app_process` and\n  //!     load a Java class in response to a crash.\n  //!\n  //! \\param[in] class_name The fully qualified class name to load, which must\n  //!     define a `main()` method to be invoked by `app_process`. Arguments\n  //!     will be passed to this method as though it were the Crashpad handler.\n  //!     This class is expected to load a native library defining\n  //!     crashpad::HandlerMain() and pass the arguments to it.\n  //! \\param[in] env A vector of environment variables of the form `var=value`\n  //!     defining the environment in which to execute `app_process`. If this\n  //!     value is `nullptr`, the application's environment at the time of the\n  //!     crash will be used.\n  //! \\param[in] database The path to a Crashpad database. The handler will be\n  //!     started with this path as its `--database` argument.\n  //! \\param[in] metrics_dir The path to an already existing directory where\n  //!     metrics files can be stored. The handler will be started with this\n  //!     path as its `--metrics-dir` argument.\n  //! \\param[in] url The URL of an upload server. The handler will be started\n  //!     with this URL as its `--url` argument.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     The handler will be started with an `--annotation` argument for each\n  //!     element in this map.\n  //! \\param[in] arguments Additional arguments to pass to the Crashpad handler.\n  //!     Arguments passed in other parameters and arguments required to perform\n  //!     the handshake are the responsibility of this method, and must not be\n  //!     specified in this parameter.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool StartJavaHandlerAtCrash(\n      const std::string& class_name,\n      const std::vector<std::string>* env,\n      const base::FilePath& database,\n      const base::FilePath& metrics_dir,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      const std::vector<std::string>& arguments);\n\n  //! \\brief Executes `/system/bin/app_process` and loads a Java class.\n  //!\n  //! \\param[in] class_name The fully qualified class name to load, which must\n  //!     define a `main()` method to be invoked by `app_process`. Arguments\n  //!     will be passed to this method as though it were the Crashpad handler.\n  //!     This class is expected to load a native library defining\n  //!     crashpad::HandlerMain() and pass the arguments to it.\n  //! \\param[in] env A vector of environment variables of the form `var=value`\n  //!     defining the environment in which to execute `app_process`. If this\n  //!     value is `nullptr`, the application's current environment will be\n  //!     used.\n  //! \\param[in] database The path to a Crashpad database. The handler will be\n  //!     started with this path as its `--database` argument.\n  //! \\param[in] metrics_dir The path to an already existing directory where\n  //!     metrics files can be stored. The handler will be started with this\n  //!     path as its `--metrics-dir` argument.\n  //! \\param[in] url The URL of an upload server. The handler will be started\n  //!     with this URL as its `--url` argument.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     The handler will be started with an `--annotation` argument for each\n  //!     element in this map.\n  //! \\param[in] arguments Additional arguments to pass to the Crashpad handler.\n  //!     Arguments passed in other parameters and arguments required to perform\n  //!     the handshake are the responsibility of this method, and must not be\n  //!     specified in this parameter.\n  //! \\param[in] socket The server end of a socket pair. The client end should\n  //!     be used with an ExceptionHandlerClient.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  static bool StartJavaHandlerForClient(\n      const std::string& class_name,\n      const std::vector<std::string>* env,\n      const base::FilePath& database,\n      const base::FilePath& metrics_dir,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      const std::vector<std::string>& arguments,\n      int socket);\n\n  //! \\brief Installs a signal handler to start a Crashpad handler process by\n  //!     loading it with `/system/bin/linker`.\n  //!\n  //! This method is only supported by Android Q+.\n  //!\n  //! \\param[in] handler_trampoline The path to a Crashpad handler trampoline\n  //!     executable, possibly located within an apk, e.g.\n  //!     \"/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so\".\n  //! \\param[in] handler_library The name of a library exporting the symbol\n  //!     `CrashpadHandlerMain()`. The path to this library must be present in\n  //!     `LD_LIBRARY_PATH`.\n  //! \\param[in] is_64_bit `true` if \\a handler_trampoline and \\a\n  //!     handler_library are 64-bit objects. They must have the same bitness.\n  //! \\param[in] env A vector of environment variables of the form `var=value`\n  //!     defining the environment in which to execute `app_process`. If this\n  //!     value is `nullptr`, the application's environment at the time of the\n  //!     crash will be used.\n  //! \\param[in] database The path to a Crashpad database. The handler will be\n  //!     started with this path as its `--database` argument.\n  //! \\param[in] metrics_dir The path to an already existing directory where\n  //!     metrics files can be stored. The handler will be started with this\n  //!     path as its `--metrics-dir` argument.\n  //! \\param[in] url The URL of an upload server. The handler will be started\n  //!     with this URL as its `--url` argument.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     The handler will be started with an `--annotation` argument for each\n  //!     element in this map.\n  //! \\param[in] arguments Additional arguments to pass to the Crashpad handler.\n  //!     Arguments passed in other parameters and arguments required to perform\n  //!     the handshake are the responsibility of this method, and must not be\n  //!     specified in this parameter.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool StartHandlerWithLinkerAtCrash(\n      const std::string& handler_trampoline,\n      const std::string& handler_library,\n      bool is_64_bit,\n      const std::vector<std::string>* env,\n      const base::FilePath& database,\n      const base::FilePath& metrics_dir,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      const std::vector<std::string>& arguments);\n\n  //! \\brief Starts a Crashpad handler process with an initial client by loading\n  //!     it with `/system/bin/linker`.\n  //!\n  //! This method is only supported by Android Q+.\n  //!\n  //! \\param[in] handler_trampoline The path to a Crashpad handler trampoline\n  //!     executable, possibly located within an apk, e.g.\n  //!     \"/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so\".\n  //! \\param[in] handler_library The name of a library exporting the symbol\n  //!     `CrashpadHandlerMain()`. The path to this library must be present in\n  //!     `LD_LIBRARY_PATH`.\n  //! \\param[in] is_64_bit `true` if \\a handler_trampoline and \\a\n  //!     handler_library are 64-bit objects. They must have the same bitness.\n  //! \\param[in] env A vector of environment variables of the form `var=value`\n  //!     defining the environment in which to execute `app_process`. If this\n  //!     value is `nullptr`, the application's current environment will be\n  //!     used.\n  //! \\param[in] database The path to a Crashpad database. The handler will be\n  //!     started with this path as its `--database` argument.\n  //! \\param[in] metrics_dir The path to an already existing directory where\n  //!     metrics files can be stored. The handler will be started with this\n  //!     path as its `--metrics-dir` argument.\n  //! \\param[in] url The URL of an upload server. The handler will be started\n  //!     with this URL as its `--url` argument.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     The handler will be started with an `--annotation` argument for each\n  //!     element in this map.\n  //! \\param[in] arguments Additional arguments to pass to the Crashpad handler.\n  //!     Arguments passed in other parameters and arguments required to perform\n  //!     the handshake are the responsibility of this method, and must not be\n  //!     specified in this parameter.\n  //! \\param[in] socket The server end of a socket pair. The client end should\n  //!     be used with an ExceptionHandlerClient.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  static bool StartHandlerWithLinkerForClient(\n      const std::string& handler_trampoline,\n      const std::string& handler_library,\n      bool is_64_bit,\n      const std::vector<std::string>* env,\n      const base::FilePath& database,\n      const base::FilePath& metrics_dir,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      const std::vector<std::string>& arguments,\n      int socket);\n#endif  // BUILDFLAG(IS_ANDROID) || DOXYGEN\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || \\\n    DOXYGEN\n  //! \\brief Installs a signal handler to launch a handler process in reponse to\n  //!     a crash.\n  //!\n  //! The handler process will create a crash dump for this process and exit.\n  //!\n  //! \\param[in] handler The path to a Crashpad handler executable.\n  //! \\param[in] database The path to a Crashpad database. The handler will be\n  //!     started with this path as its `--database` argument.\n  //! \\param[in] metrics_dir The path to an already existing directory where\n  //!     metrics files can be stored. The handler will be started with this\n  //!     path as its `--metrics-dir` argument.\n  //! \\param[in] url The URL of an upload server. The handler will be started\n  //!     with this URL as its `--url` argument.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     The handler will be started with an `--annotation` argument for each\n  //!     element in this map.\n  //! \\param[in] arguments Additional arguments to pass to the Crashpad handler.\n  //!     Arguments passed in other parameters and arguments required to perform\n  //!     the handshake are the responsibility of this method, and must not be\n  //!     specified in this parameter.\n  //! \\param[in] attachments Attachment paths to pass to the Crashpad handler.\n  //!     The handler will be started with an `--attachment` argument for each\n  //!     path in this vector.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool StartHandlerAtCrash(\n      const base::FilePath& handler,\n      const base::FilePath& database,\n      const base::FilePath& metrics_dir,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      const std::vector<std::string>& arguments,\n      const std::vector<base::FilePath>& attachments = {});\n\n  //! \\brief Starts a handler process with an initial client.\n  //!\n  //! This method allows a process to launch the handler process on behalf of\n  //! another process.\n  //!\n  //! \\param[in] handler The path to a Crashpad handler executable.\n  //! \\param[in] database The path to a Crashpad database. The handler will be\n  //!     started with this path as its `--database` argument.\n  //! \\param[in] metrics_dir The path to an already existing directory where\n  //!     metrics files can be stored. The handler will be started with this\n  //!     path as its `--metrics-dir` argument.\n  //! \\param[in] url The URL of an upload server. The handler will be started\n  //!     with this URL as its `--url` argument.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     The handler will be started with an `--annotation` argument for each\n  //!     element in this map.\n  //! \\param[in] arguments Additional arguments to pass to the Crashpad handler.\n  //!     Arguments passed in other parameters and arguments required to perform\n  //!     the handshake are the responsibility of this method, and must not be\n  //!     specified in this parameter.\n  //! \\param[in] socket The server end of a socket pair. The client end should\n  //!     be used with an ExceptionHandlerClient.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  static bool StartHandlerForClient(\n      const base::FilePath& handler,\n      const base::FilePath& database,\n      const base::FilePath& metrics_dir,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      const std::vector<std::string>& arguments,\n      int socket);\n\n  //! \\brief Requests that the handler capture a dump even though there hasn't\n  //!     been a crash.\n  //!\n  //! A handler must have already been installed before calling this method.\n  //!\n  //! TODO(jperaza): Floating point information in the context is zeroed out\n  //! until CaptureContext() supports collecting that information.\n  //!\n  //! \\param[in] context A NativeCPUContext, generally captured by\n  //!     CaptureContext() or similar.\n  static void DumpWithoutCrash(NativeCPUContext* context);\n\n  //! \\brief Disables any installed crash handler, not including any\n  //!     FirstChanceHandler and crashes the current process.\n  //!\n  //! \\param[in] message A message to be logged before crashing.\n  [[noreturn]] static void CrashWithoutDump(const std::string& message);\n\n  //! \\brief The type for custom handlers installed by clients.\n  using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*);\n\n  //! \\brief Installs a custom crash signal handler which runs before the\n  //!     currently installed Crashpad handler.\n  //!\n  //! Handling signals appropriately can be tricky and use of this method\n  //! should be avoided, if possible.\n  //!\n  //! A handler must have already been installed before calling this method.\n  //!\n  //! The custom handler runs in a signal handler context and must be safe for\n  //! that purpose.\n  //!\n  //! If the custom handler returns `true`, the signal is considered handled and\n  //! the signal handler returns. Otherwise, the currently installed Crashpad\n  //! signal handler is run.\n  //!\n  //! \\param[in] handler The custom crash signal handler to install.\n  static void SetFirstChanceExceptionHandler(FirstChanceHandler handler);\n\n  //! \\brief Installs a custom crash signal handler which runs after the\n  //!     currently installed Crashpad handler.\n  //!\n  //! Handling signals appropriately can be tricky and use of this method\n  //! should be avoided, if possible.\n  //!\n  //! A handler must have already been installed before calling this method.\n  //!\n  //! The custom handler runs in a signal handler context and must be safe for\n  //! that purpose.\n  //!\n  //! If the custom handler returns `true`, the signal is not reraised.\n  //!\n  //! \\param[in] handler The custom crash signal handler to install.\n  static void SetLastChanceExceptionHandler(bool (*handler)(int,\n                                                            siginfo_t*,\n                                                            ucontext_t*));\n\n  //! \\brief Configures a set of signals that shouldn't have Crashpad signal\n  //!     handlers installed.\n  //!\n  //! This method should be called before calling StartHandler(),\n  //! SetHandlerSocket(), or other methods that install Crashpad signal\n  //! handlers.\n  //!\n  //! \\param[in] unhandled_signals The set of unhandled signals\n  void SetUnhandledSignals(const std::set<int>& unhandled_signals);\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||\n        // BUILDFLAG(IS_CHROMEOS) || DOXYGEN\n\n#if BUILDFLAG(IS_IOS) || DOXYGEN\n  //! \\brief Observation callback invoked each time this object finishes\n  //!     processing and attempting to upload on-disk crash reports (whether or\n  //!     not the uploads succeeded).\n  //!\n  //! This callback is copied into this object. Any references or pointers\n  //! inside must outlive this object.\n  //!\n  //! The callback might be invoked on a background thread, so clients must\n  //! synchronize appropriately.\n  using ProcessPendingReportsObservationCallback = std::function<void()>;\n\n  //! \\brief Configures the process to direct its crashes to the iOS in-process\n  //! Crashpad handler.\n  //!\n  //! This method is only defined on iOS.\n  //!\n  //! \\param[in] database The path to a Crashpad database.\n  //! \\param[in] url The URL of an upload server.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //! \\param[in] callback Optional callback invoked zero or more times\n  //!     on a background thread each time the handler finishes\n  //!     processing and attempting to upload on-disk crash reports.\n  //!     If this callback is empty, it is not invoked.\n  //! \\return `true` on success, `false` on failure with a message logged.\n  static bool StartCrashpadInProcessHandler(\n      const base::FilePath& database,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      ProcessPendingReportsObservationCallback callback);\n\n  //! \\brief Requests that the handler convert intermediate dumps into\n  //!     minidumps and trigger an upload if possible.\n  //!\n  //! A handler must have already been installed before calling this method.\n  //! This method should be called when an application is ready to start\n  //! processing previously created intermediate dumps. Processing will block,\n  //! so this should not be called on the main UI thread. No intermediate dumps\n  //! will be processed until this method is called.\n  //!\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     Useful when adding crash annotations detected on the next run after a\n  //!     crash but before upload.\n  //! \\param[in] user_stream_sources An optional vector containing the\n  //!     extensibility data sources to call on crash. Each time a minidump is\n  //!     created, the sources are called in turn. Any streams returned are\n  //!     added to the minidump.\n  static void ProcessIntermediateDumps(\n      const std::map<std::string, std::string>& annotations = {},\n      const UserStreamDataSources* user_stream_sources = nullptr);\n\n  //! \\brief Requests that the handler convert a single intermediate dump at \\a\n  //!     file generated by DumpWithoutCrashAndDeferProcessingAtPath into a\n  //!     minidump and trigger an upload if possible.\n  //!\n  //! A handler must have already been installed before calling this method.\n  //! This method should be called when an application is ready to start\n  //! processing previously created intermediate dumps. Processing will block,\n  //! so this should not be called on the main UI thread.\n  //!\n  //! \\param[in] file The intermediate dump to process.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!     Useful when adding crash annotations detected on the next run after a\n  //!     crash but before upload.\n  static void ProcessIntermediateDump(\n      const base::FilePath& file,\n      const std::map<std::string, std::string>& annotations = {});\n\n  //! \\brief Requests that the handler begin in-process uploading of any\n  //!     pending reports.\n  //!\n  //! Once called the handler will start looking for pending reports to upload\n  //! on another thread. This method does not block.\n  //!\n  //! A handler must have already been installed before calling this method.\n  //!\n  //! \\param[in] upload_behavior Controls when the upload thread will run and\n  //!     process pending reports. By default, only uploads pending reports\n  //!     when the application is active.\n  static void StartProcessingPendingReports(\n      UploadBehavior upload_behavior = UploadBehavior::kUploadWhenAppIsActive);\n\n  //! \\brief Requests that the handler capture an intermediate dump even though\n  //!     there hasn't been a crash. The intermediate dump will be converted\n  //!     to a mindump immediately. If StartProcessingPendingReports() has been\n  //!     called, this will also trigger an upload.\n  //!\n  //! For internal use only. Clients should use CRASHPAD_SIMULATE_CRASH().\n  //!\n  //! A handler must have already been installed before calling this method.\n  //!\n  //! \\param[in] context A NativeCPUContext, generally captured by\n  //!     CaptureContext() or similar.\n  static void DumpWithoutCrash(NativeCPUContext* context);\n\n  //! \\brief Requests that the handler capture an intermediate dump even though\n  //!     there hasn't been a crash. The intermediate dump will not be converted\n  //!     to a mindump until ProcessIntermediateDumps() is called.\n  //!\n  //! For internal use only. Clients should use\n  //! CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING().\n  //!\n  //! A handler must have already been installed before calling this method.\n  //!\n  //! \\param[in] context A NativeCPUContext, generally captured by\n  //!     CaptureContext() or similar.\n  static void DumpWithoutCrashAndDeferProcessing(NativeCPUContext* context);\n\n  //! \\brief Requests that the handler capture an intermediate dump and store it\n  //!     in path, even though there hasn't been a crash. The intermediate dump\n  //!     will not be converted to a mindump until ProcessIntermediateDump() is\n  //!     called.\n  //!\n  //! For internal use only. Clients should use\n  //! CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING_AT_PATH().\n  //!\n  //! A handler must have already been installed before calling this method.\n  //!\n  //! \\param[in] context A NativeCPUContext, generally captured by\n  //!     CaptureContext() or similar.\n  //! \\param[in] path The path for writing the intermediate dump.\n  static void DumpWithoutCrashAndDeferProcessingAtPath(\n      NativeCPUContext* context,\n      const base::FilePath path);\n\n  //! \\brief Unregister the Crashpad client. Intended to be used by tests so\n  //!     multiple Crashpad clients can be started and stopped. Not expected to\n  //!     be used in a shipping application.\n  static void ResetForTesting();\n\n  //! \\brief Inject a callback into the Mach exception and signal handling\n  //!     mechanisms. Intended to be used by tests to trigger a reentrant\n  //      exception.\n  static void SetExceptionCallbackForTesting(void (*callback)());\n\n  //! \\brief Returns the thread id of the Mach exception thread, used by tests.\n  static uint64_t GetThreadIdForTesting();\n#endif\n\n#if BUILDFLAG(IS_APPLE) || DOXYGEN\n  //! \\brief Sets the process’ crash handler to a Mach service registered with\n  //!     the bootstrap server.\n  //!\n  //! This method is only defined on macOS.\n  //!\n  //! See StartHandler() for more detail on how the port and handler are\n  //! configured.\n  //!\n  //! \\param[in] service_name The service name of a Crashpad exception handler\n  //!     service previously registered with the bootstrap server.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool SetHandlerMachService(const std::string& service_name);\n\n  //! \\brief Sets the process’ crash handler to a Mach port.\n  //!\n  //! This method is only defined on macOS.\n  //!\n  //! See StartHandler() for more detail on how the port and handler are\n  //! configured.\n  //!\n  //! \\param[in] exception_port An `exception_port_t` corresponding to a\n  //!     Crashpad exception handler service.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool SetHandlerMachPort(base::apple::ScopedMachSendRight exception_port);\n\n  //! \\brief Retrieves a send right to the process’ crash handler Mach port.\n  //!\n  //! This method is only defined on macOS.\n  //!\n  //! This method can be used to obtain the crash handler Mach port when a\n  //! Crashpad client process wishes to provide a send right to this port to\n  //! another process. The IPC mechanism used to convey the right is under the\n  //! application’s control. If the other process wishes to become a client of\n  //! the same crash handler, it can provide the transferred right to\n  //! SetHandlerMachPort().\n  //!\n  //! See StartHandler() for more detail on how the port and handler are\n  //! configured.\n  //!\n  //! \\return The Mach port set by SetHandlerMachPort(), possibly indirectly by\n  //!     a call to another method such as StartHandler() or\n  //!     SetHandlerMachService(). This method must only be called after a\n  //!     successful call to one of those methods. `MACH_PORT_NULL` on failure\n  //!     with a message logged.\n  base::apple::ScopedMachSendRight GetHandlerMachPort() const;\n#endif\n\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n  //! \\brief Sets the IPC pipe of a presumably-running Crashpad handler process\n  //!     which was started with StartHandler() or by other compatible means\n  //!     and does an IPC message exchange to register this process with the\n  //!     handler. Crashes will be serviced once this method returns.\n  //!\n  //! This method is only defined on Windows.\n  //!\n  //! This method sets the unhandled exception handler to a local\n  //! function that when reached will \"signal and wait\" for the crash handler\n  //! process to create the dump.\n  //!\n  //! \\param[in] ipc_pipe The full name of the crash handler IPC pipe. This is\n  //!     a string of the form `&quot;\\\\.\\pipe\\NAME&quot;`.\n  //!\n  //! \\return `true` on success and `false` on failure.\n  bool SetHandlerIPCPipe(const std::wstring& ipc_pipe);\n\n  //! \\brief Retrieves the IPC pipe name used to register with the Crashpad\n  //!     handler.\n  //!\n  //! This method is only defined on Windows.\n  //!\n  //! This method retrieves the IPC pipe name set by SetHandlerIPCPipe(), or a\n  //! suitable IPC pipe name chosen by StartHandler(). It must only be called\n  //! after a successful call to one of those methods. It is intended to be used\n  //! to obtain the IPC pipe name so that it may be passed to other processes,\n  //! so that they may register with an existing Crashpad handler by calling\n  //! SetHandlerIPCPipe().\n  //!\n  //! \\return The full name of the crash handler IPC pipe, a string of the form\n  //!     `&quot;\\\\.\\pipe\\NAME&quot;`.\n  std::wstring GetHandlerIPCPipe() const;\n\n  //! \\brief When `asynchronous_start` is used with StartHandler(), this method\n  //!     can be used to block until the handler launch has been completed to\n  //!     retrieve status information.\n  //!\n  //! This method should not be used unless `asynchronous_start` was `true`.\n  //!\n  //! \\param[in] timeout_ms The number of milliseconds to wait for a result from\n  //!     the background launch, or `0xffffffff` to block indefinitely.\n  //!\n  //! \\return `true` if the hander startup succeeded, `false` otherwise, and an\n  //!     error message will have been logged.\n  bool WaitForHandlerStart(unsigned int timeout_ms);\n\n  //! \\brief Register a DLL using WerRegisterExceptionModule().\n  //!\n  //! This method should only be called after a successful call to\n  //! SetHandlerIPCPipe() or StartHandler(). The registration is valid for the\n  //! lifetime of this object.\n  //!\n  //! \\param[in] full_path The full path to the DLL that will be registered.\n  //!     The DLL path should also be set in an appropriate\n  //!     `Windows Error Reporting` registry key.\n  //!\n  //! \\return `true` if the DLL was registered. Note: Windows just stashes the\n  //!     path somewhere so this returns `true` even if the DLL is not yet\n  //!     set in an appropriate registry key, or does not exist.\n  bool RegisterWerModule(const std::wstring& full_path);\n\n  //! \\brief Requests that the handler capture a dump even though there hasn't\n  //!     been a crash.\n  //!\n  //! \\param[in] context A `CONTEXT`, generally captured by CaptureContext() or\n  //!     similar.\n  static void DumpWithoutCrash(const CONTEXT& context);\n\n  //! \\brief Requests that the handler capture a dump using the given \\a\n  //!     exception_pointers to get the `EXCEPTION_RECORD` and `CONTEXT`.\n  //!\n  //! This function is not necessary in general usage as an unhandled exception\n  //! filter is installed by StartHandler() or SetHandlerIPCPipe().\n  //!\n  //! \\param[in] exception_pointers An `EXCEPTION_POINTERS`, as would generally\n  //!     passed to an unhandled exception filter.\n  static void DumpAndCrash(EXCEPTION_POINTERS* exception_pointers);\n\n  //! \\brief Requests that the handler capture a dump of a different process.\n  //!\n  //! The target process must be an already-registered Crashpad client. An\n  //! exception will be triggered in the target process, and the regular dump\n  //! mechanism used. This function will block until the exception in the target\n  //! process has been handled by the Crashpad handler.\n  //!\n  //! This function is unavailable when running on Windows XP and will return\n  //! `false`.\n  //!\n  //! \\param[in] process A `HANDLE` identifying the process to be dumped.\n  //! \\param[in] blame_thread If non-null, a `HANDLE` valid in the caller's\n  //!     process, referring to a thread in the target process. If this is\n  //!     supplied, instead of the exception referring to the location where the\n  //!     exception was injected, an exception record will be fabricated that\n  //!     refers to the current location of the given thread.\n  //! \\param[in] exception_code If \\a blame_thread is non-null, this will be\n  //!     used as the exception code in the exception record.\n  //!\n  //! \\return `true` if the exception was triggered successfully.\n  static bool DumpAndCrashTargetProcess(HANDLE process,\n                                        HANDLE blame_thread,\n                                        DWORD exception_code);\n#endif\n\n#if BUILDFLAG(IS_APPLE) || DOXYGEN\n  //! \\brief Configures the process to direct its crashes to the default handler\n  //!     for the operating system.\n  //!\n  //! On macOS, this sets the task’s exception port as in SetHandlerMachPort(),\n  //! but the exception handler used is obtained from\n  //! SystemCrashReporterHandler(). If the system’s crash reporter handler\n  //! cannot be determined or set, the task’s exception ports for crash-type\n  //! exceptions are cleared.\n  //!\n  //! Use of this function is strongly discouraged.\n  //!\n  //! \\warning After a call to this function, Crashpad will no longer monitor\n  //!     the process for crashes until a subsequent call to\n  //!     SetHandlerMachPort().\n  //!\n  //! \\note This is provided as a static function to allow it to be used in\n  //!     situations where a CrashpadClient object is not otherwise available.\n  //!     This may be useful when a child process inherits its parent’s Crashpad\n  //!     handler, but wants to sever this tie.\n  static void UseSystemDefaultHandler();\n#endif\n\n#if BUILDFLAG(IS_CHROMEOS)\n  //! \\brief Sets a timestamp on the signal handler to be passed on to\n  //!     crashpad_handler and then eventually Chrome OS's crash_reporter.\n  //!\n  //! \\note This method is used by clients that use `StartHandler()` to start\n  //!     a handler and not by clients that use any other handler starting\n  //!     methods.\n  static void SetCrashLoopBefore(uint64_t crash_loop_before_time);\n#endif\n\n private:\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n  //!  \\brief Registers process handlers for the client.\n  void RegisterHandlers();\n#endif\n\n#if BUILDFLAG(IS_APPLE)\n  base::apple::ScopedMachSendRight exception_port_;\n#elif BUILDFLAG(IS_WIN)\n  std::wstring ipc_pipe_;\n  ScopedKernelHANDLE handler_start_thread_;\n  ScopedVectoredExceptionRegistration vectored_handler_;\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  std::set<int> unhandled_signals_;\n#endif  // BUILDFLAG(IS_APPLE)\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_\n"
  },
  {
    "path": "client/crashpad_client_fuchsia.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#include <lib/fdio/spawn.h>\n#include <lib/zx/channel.h>\n#include <lib/zx/job.h>\n#include <lib/zx/process.h>\n#include <zircon/processargs.h>\n\n#include \"base/check_op.h\"\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"base/logging.h\"\n#include \"client/client_argv_handling.h\"\n\nnamespace crashpad {\n\nCrashpadClient::CrashpadClient() {}\n\nCrashpadClient::~CrashpadClient() {}\n\nbool CrashpadClient::StartHandler(\n    const base::FilePath& handler,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    bool restartable,\n    bool asynchronous_start,\n    const std::vector<base::FilePath>& attachments) {\n  DCHECK(attachments.empty()); // Attachments are not implemented on Fuchsia yet.\n  DCHECK_EQ(restartable, false);  // Not used on Fuchsia.\n  DCHECK_EQ(asynchronous_start, false);  // Not used on Fuchsia.\n\n  std::vector<std::string> argv_strings = BuildHandlerArgvStrings(\n      handler, database, metrics_dir, url, annotations, arguments);\n\n  std::vector<const char*> argv;\n  StringVectorToCStringVector(argv_strings, &argv);\n\n  // Set up handles to send to the spawned process:\n  //   0. PA_USER0 job\n  //   1. PA_USER0 exception channel\n  //\n  // Currently it is assumed that this process's default job handle is the\n  // exception channel that should be monitored. In the future, it might be\n  // useful for this to be configurable by the client.\n  zx::job job;\n  zx_status_t status =\n      zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS, &job);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_handle_duplicate\";\n    return false;\n  }\n\n  zx::channel exception_channel;\n  status = job.create_exception_channel(0, &exception_channel);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_task_create_exception_channel\";\n    return false;\n  }\n\n  constexpr size_t kActionCount = 2;\n  fdio_spawn_action_t actions[] = {\n      {.action = FDIO_SPAWN_ACTION_ADD_HANDLE,\n       .h = {.id = PA_HND(PA_USER0, 0), .handle = job.release()}},\n      {.action = FDIO_SPAWN_ACTION_ADD_HANDLE,\n       .h = {.id = PA_HND(PA_USER0, 1), .handle = exception_channel.release()}},\n  };\n\n  char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];\n  zx::process child;\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196, FDIO_SPAWN_CLONE_ALL\n  // is useful during bringup, but should probably be made minimal for real\n  // usage.\n  status = fdio_spawn_etc(ZX_HANDLE_INVALID,\n                          FDIO_SPAWN_CLONE_ALL,\n                          argv[0],\n                          argv.data(),\n                          nullptr,\n                          kActionCount,\n                          actions,\n                          child.reset_and_get_address(),\n                          error_message);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"fdio_spawn_etc: \" << error_message;\n    return false;\n  }\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_client_ios.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#include \"build/buildflag.h\"\n#include \"client/ios_handler/in_process_handler.h\"\n\n#if !BUILDFLAG(IS_IOS_TVOS)\n#include \"client/crash_handler_ios.h\"\n#else\n#include \"client/crash_handler_tvos.h\"\n#endif\n\nnamespace crashpad {\n\nCrashpadClient::CrashpadClient() {}\n\nCrashpadClient::~CrashpadClient() {}\n\n// static\nbool CrashpadClient::StartCrashpadInProcessHandler(\n    const base::FilePath& database,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    ProcessPendingReportsObservationCallback callback) {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  return crash_handler->Initialize(database, url, annotations, callback);\n}\n\n// static\nvoid CrashpadClient::ProcessIntermediateDumps(\n    const std::map<std::string, std::string>& annotations,\n    const UserStreamDataSources* user_stream_sources) {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  crash_handler->ProcessIntermediateDumps(annotations, user_stream_sources);\n}\n\n// static\nvoid CrashpadClient::ProcessIntermediateDump(\n    const base::FilePath& file,\n    const std::map<std::string, std::string>& annotations) {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  crash_handler->ProcessIntermediateDump(file, annotations);\n}\n\n// static\nvoid CrashpadClient::StartProcessingPendingReports(\n    UploadBehavior upload_behavior) {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  crash_handler->StartProcessingPendingReports(upload_behavior);\n}\n\n// static\nvoid CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  crash_handler->DumpWithoutCrash(context, /*process_dump=*/true);\n}\n\n// static\nvoid CrashpadClient::DumpWithoutCrashAndDeferProcessing(\n    NativeCPUContext* context) {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  crash_handler->DumpWithoutCrash(context, /*process_dump=*/false);\n}\n\n// static\nvoid CrashpadClient::DumpWithoutCrashAndDeferProcessingAtPath(\n    NativeCPUContext* context,\n    const base::FilePath path) {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  crash_handler->DumpWithoutCrashAtPath(context, path);\n}\n\nvoid CrashpadClient::ResetForTesting() {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  crash_handler->ResetForTesting();\n}\n\nvoid CrashpadClient::SetExceptionCallbackForTesting(void (*callback)()) {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  crash_handler->SetExceptionCallbackForTesting(callback);\n}\n\nuint64_t CrashpadClient::GetThreadIdForTesting() {\n  CrashHandler* crash_handler = CrashHandler::Get();\n  DCHECK(crash_handler);\n  return crash_handler->GetThreadIdForTesting();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_client_ios_test.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#import <Foundation/Foundation.h>\n\n#include <utility>\n#include <vector>\n\n#include \"base/strings/sys_string_conversions.h\"\n#include \"client/crash_report_database.h\"\n#include \"client/ios_handler/exception_processor.h\"\n#include \"client/simulate_crash.h\"\n#include \"gtest/gtest.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"testing/platform_test.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass CrashpadIOSClient : public PlatformTest {\n protected:\n  // testing::Test:\n\n  void SetUp() override {\n    ASSERT_TRUE(client_.StartCrashpadInProcessHandler(\n        base::FilePath(database_dir.path()),\n        \"\",\n        {},\n        CrashpadClient::ProcessPendingReportsObservationCallback()));\n    database_ = CrashReportDatabase::Initialize(database_dir.path());\n  }\n\n  void TearDown() override { client_.ResetForTesting(); }\n\n  auto& Client() { return client_; }\n  auto& Database() { return database_; }\n\n private:\n  std::unique_ptr<CrashReportDatabase> database_;\n  CrashpadClient client_;\n  ScopedTempDir database_dir;\n};\n\nTEST_F(CrashpadIOSClient, DumpWithoutCrash) {\n  std::vector<CrashReportDatabase::Report> reports;\n  EXPECT_EQ(Database()->GetPendingReports(&reports),\n            CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 0u);\n  CRASHPAD_SIMULATE_CRASH();\n\n  EXPECT_EQ(Database()->GetPendingReports(&reports),\n            CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 1u);\n}\n\nTEST_F(CrashpadIOSClient, DumpWithoutCrashAndDefer) {\n  std::vector<CrashReportDatabase::Report> reports;\n  CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING();\n  EXPECT_EQ(Database()->GetPendingReports(&reports),\n            CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 0u);\n  Client().ProcessIntermediateDumps();\n  EXPECT_EQ(Database()->GetPendingReports(&reports),\n            CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 1u);\n}\n\nTEST_F(CrashpadIOSClient, DumpWithoutCrashAndDeferAtPath) {\n  std::vector<CrashReportDatabase::Report> reports;\n  ScopedTempDir crash_dir;\n  UUID uuid;\n  uuid.InitializeWithNew();\n  CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING_AT_PATH(\n      crash_dir.path().Append(uuid.ToString()));\n  EXPECT_EQ(Database()->GetPendingReports(&reports),\n            CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 0u);\n\n  NSError* error = nil;\n  NSArray* paths = [[NSFileManager defaultManager]\n      contentsOfDirectoryAtPath:base::SysUTF8ToNSString(\n                                    crash_dir.path().value())\n                          error:&error];\n  ASSERT_EQ([paths count], 1u);\n  Client().ProcessIntermediateDump(\n      crash_dir.path().Append([paths[0] fileSystemRepresentation]));\n  reports.clear();\n  EXPECT_EQ(Database()->GetPendingReports(&reports),\n            CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 1u);\n}\n\nclass RaceThread : public Thread {\n public:\n  explicit RaceThread() : Thread() {}\n\n private:\n  void ThreadMain() override {\n    for (int i = 0; i < 10; ++i) {\n      CRASHPAD_SIMULATE_CRASH();\n    }\n  }\n};\n\nTEST_F(CrashpadIOSClient, MultipleThreadsSimulateCrash) {\n  RaceThread race_threads[2];\n  for (RaceThread& race_thread : race_threads) {\n    race_thread.Start();\n  }\n\n  for (int i = 0; i < 10; ++i) {\n    CRASHPAD_SIMULATE_CRASH();\n  }\n  for (RaceThread& race_thread : race_threads) {\n    race_thread.Join();\n  }\n\n  std::vector<CrashReportDatabase::Report> reports;\n  ASSERT_EQ(Database()->GetPendingReports(&reports),\n            CrashReportDatabase::kNoError);\n  EXPECT_EQ(reports.size(), 30u);\n}\n\n// This test is covered by a similar XCUITest, but for development purposes it's\n// sometimes easier and faster to run in Google Test.  However, there's no way\n// to correctly run this in Google Test. Leave the test here, disabled, for use\n// during development only.\nTEST_F(CrashpadIOSClient, DISABLED_ThrowNSException) {\n  [NSException raise:@\"GoogleTestNSException\" format:@\"ThrowException\"];\n}\n\n// This test is covered by a similar XCUITest, but for development purposes it's\n// sometimes easier and faster to run in Google Test.  However, there's no way\n// to correctly run this in Google Test. Leave the test here, disabled, for use\n// during development only.\nTEST_F(CrashpadIOSClient, DISABLED_ThrowException) {\n  std::vector<int> empty_vector;\n  std::ignore = empty_vector.at(42);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_client_linux.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <linux/futex.h>\n#include <pthread.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <sys/prctl.h>\n#include <sys/socket.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <atomic>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"client/client_argv_handling.h\"\n#include \"third_party/lss/lss.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/linux/exception_handler_client.h\"\n#include \"util/linux/exception_information.h\"\n#include \"util/linux/scoped_pr_set_dumpable.h\"\n#include \"util/linux/scoped_pr_set_ptracer.h\"\n#include \"util/linux/socket.h\"\n#include \"util/misc/address_sanitizer.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/posix/scoped_mmap.h\"\n#include \"util/posix/signals.h\"\n#include \"util/posix/spawn_subprocess.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nstd::string FormatArgumentInt(const std::string& name, int value) {\n  return base::StringPrintf(\"--%s=%d\", name.c_str(), value);\n}\n\nstd::string FormatArgumentAddress(const std::string& name, const void* addr) {\n  return base::StringPrintf(\"--%s=%p\", name.c_str(), addr);\n}\n\n#if BUILDFLAG(IS_ANDROID)\n\nstd::vector<std::string> BuildAppProcessArgs(\n    const std::string& class_name,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    int socket) {\n#if defined(ARCH_CPU_64_BITS)\n  static constexpr char kAppProcess[] = \"/system/bin/app_process64\";\n#else\n  static constexpr char kAppProcess[] = \"/system/bin/app_process32\";\n#endif\n\n  std::vector<std::string> argv;\n  argv.push_back(kAppProcess);\n  argv.push_back(\"/system/bin\");\n  argv.push_back(\"--application\");\n  argv.push_back(class_name);\n\n  std::vector<std::string> handler_argv =\n      BuildHandlerArgvStrings(base::FilePath(kAppProcess),\n                              database,\n                              metrics_dir,\n                              url,\n                              annotations,\n                              arguments);\n\n  if (socket != kInvalidFileHandle) {\n    handler_argv.push_back(FormatArgumentInt(\"initial-client-fd\", socket));\n  }\n\n  argv.insert(argv.end(), handler_argv.begin(), handler_argv.end());\n  return argv;\n}\n\nstd::vector<std::string> BuildArgsToLaunchWithLinker(\n    const std::string& handler_trampoline,\n    const std::string& handler_library,\n    bool is_64_bit,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    int socket) {\n  std::vector<std::string> argv;\n  if (is_64_bit) {\n    argv.push_back(\"/system/bin/linker64\");\n  } else {\n    argv.push_back(\"/system/bin/linker\");\n  }\n  argv.push_back(handler_trampoline);\n  argv.push_back(handler_library);\n\n  std::vector<std::string> handler_argv = BuildHandlerArgvStrings(\n      base::FilePath(), database, metrics_dir, url, annotations, arguments);\n\n  if (socket != kInvalidFileHandle) {\n    handler_argv.push_back(FormatArgumentInt(\"initial-client-fd\", socket));\n  }\n\n  argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end());\n  return argv;\n}\n\n#endif  // BUILDFLAG(IS_ANDROID)\n\nusing LastChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*);\n\n// A base class for Crashpad signal handler implementations.\nclass SignalHandler {\n public:\n  SignalHandler(const SignalHandler&) = delete;\n  SignalHandler& operator=(const SignalHandler&) = delete;\n\n  // Returns the currently installed signal hander. May be `nullptr` if no\n  // handler has been installed.\n  static SignalHandler* Get() { return handler_; }\n\n  // Disables any installed Crashpad signal handler. If a crash signal is\n  // received, any previously installed (non-Crashpad) signal handler will be\n  // restored and the signal reraised.\n  static void Disable() {\n    if (!handler_->disabled_.test_and_set()) {\n      handler_->WakeThreads();\n    }\n  }\n\n  void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {\n    first_chance_handler_ = handler;\n  }\n\n  void SetLastChanceExceptionHandler(LastChanceHandler handler) {\n    last_chance_handler_ = handler;\n  }\n\n  // The base implementation for all signal handlers, suitable for calling\n  // directly to simulate signal delivery.\n  void HandleCrash(int signo, siginfo_t* siginfo, void* context) {\n    exception_information_.siginfo_address =\n        FromPointerCast<decltype(exception_information_.siginfo_address)>(\n            siginfo);\n    exception_information_.context_address =\n        FromPointerCast<decltype(exception_information_.context_address)>(\n            context);\n    exception_information_.thread_id = sys_gettid();\n\n    ScopedPrSetDumpable set_dumpable(false);\n    HandleCrashImpl();\n  }\n\n protected:\n  SignalHandler() = default;\n  ~SignalHandler() = default;\n\n  bool Install(const std::set<int>* unhandled_signals) {\n    bool signal_stack_initialized =\n        CrashpadClient::InitializeSignalStackForThread();\n    DCHECK(signal_stack_initialized);\n\n    DCHECK(!handler_);\n    handler_ = this;\n    return Signals::InstallCrashHandlers(HandleOrReraiseSignal,\n                                         SA_ONSTACK | SA_EXPOSE_TAGBITS,\n                                         &old_actions_,\n                                         unhandled_signals);\n  }\n\n  const ExceptionInformation& GetExceptionInfo() {\n    return exception_information_;\n  }\n\n  virtual void HandleCrashImpl() = 0;\n\n private:\n  static constexpr int32_t kDumpNotDone = 0;\n  static constexpr int32_t kDumpDone = 1;\n\n  // The signal handler installed at OS-level.\n  static void HandleOrReraiseSignal(int signo,\n                                    siginfo_t* siginfo,\n                                    void* context) {\n    if (handler_->first_chance_handler_ &&\n        handler_->first_chance_handler_(\n            signo, siginfo, static_cast<ucontext_t*>(context))) {\n      return;\n    }\n\n    // Only handle the first fatal signal observed. If another thread receives a\n    // crash signal, it waits for the first dump to complete instead of\n    // requesting another.\n    if (!handler_->disabled_.test_and_set()) {\n      handler_->HandleCrash(signo, siginfo, context);\n      handler_->WakeThreads();\n      if (handler_->last_chance_handler_ &&\n          handler_->last_chance_handler_(\n              signo, siginfo, static_cast<ucontext_t*>(context))) {\n        return;\n      }\n    } else {\n      // Processes on Android normally have several chained signal handlers that\n      // co-operate to report crashes. e.g. WebView will have this signal\n      // handler installed, the app embedding WebView may have a signal handler\n      // installed, and Bionic will have a signal handler. Each signal handler\n      // runs in succession, possibly managed by libsigchain. This wait is\n      // intended to avoid ill-effects from multiple signal handlers from\n      // different layers (possibly all trying to use ptrace()) from running\n      // simultaneously. It does not block forever so that in most conditions,\n      // those signal handlers will still have a chance to run and ensures\n      // process termination in case the first crashing thread crashes again in\n      // its signal handler. Though less typical, this situation also occurs on\n      // other Linuxes, e.g. to produce in-process stack traces for debug\n      // builds.\n      handler_->WaitForDumpDone();\n    }\n\n    Signals::RestoreHandlerAndReraiseSignalOnReturn(\n        siginfo, handler_->old_actions_.ActionForSignal(signo));\n  }\n\n  void WaitForDumpDone() {\n    kernel_timespec timeout;\n    timeout.tv_sec = 5;\n    timeout.tv_nsec = 0;\n    sys_futex(&dump_done_futex_,\n              FUTEX_WAIT_PRIVATE,\n              kDumpNotDone,\n              &timeout,\n              nullptr,\n              0);\n  }\n\n  void WakeThreads() {\n    dump_done_futex_ = kDumpDone;\n    sys_futex(\n        &dump_done_futex_, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);\n  }\n\n  Signals::OldActions old_actions_ = {};\n  ExceptionInformation exception_information_ = {};\n  CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;\n  LastChanceHandler last_chance_handler_ = nullptr;\n  int32_t dump_done_futex_ = kDumpNotDone;\n#if !defined(__cpp_lib_atomic_value_initialization) || \\\n    __cpp_lib_atomic_value_initialization < 201911L\n  std::atomic_flag disabled_ = ATOMIC_FLAG_INIT;\n#else\n  std::atomic_flag disabled_;\n#endif\n\n  static SignalHandler* handler_;\n};\nSignalHandler* SignalHandler::handler_ = nullptr;\n\n// Launches a single use handler to snapshot this process.\nclass LaunchAtCrashHandler : public SignalHandler {\n public:\n  LaunchAtCrashHandler(const LaunchAtCrashHandler&) = delete;\n  LaunchAtCrashHandler& operator=(const LaunchAtCrashHandler&) = delete;\n\n  static LaunchAtCrashHandler* Get() {\n    static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();\n    return instance;\n  }\n\n  bool Initialize(std::vector<std::string>* argv_in,\n                  const std::vector<std::string>* envp,\n                  const std::set<int>* unhandled_signals) {\n    argv_strings_.swap(*argv_in);\n\n    if (envp) {\n      envp_strings_ = *envp;\n      StringVectorToCStringVector(envp_strings_, &envp_);\n      set_envp_ = true;\n    }\n\n    argv_strings_.push_back(FormatArgumentAddress(\"trace-parent-with-exception\",\n                                                  &GetExceptionInfo()));\n\n    StringVectorToCStringVector(argv_strings_, &argv_);\n    return Install(unhandled_signals);\n  }\n\n  void HandleCrashImpl() override {\n    ScopedPrSetPtracer set_ptracer(sys_getpid(), /* may_log= */ false);\n\n    pid_t pid = fork();\n    if (pid < 0) {\n      return;\n    }\n    if (pid == 0) {\n      if (set_envp_) {\n        execve(argv_[0],\n               const_cast<char* const*>(argv_.data()),\n               const_cast<char* const*>(envp_.data()));\n      } else {\n        execv(argv_[0], const_cast<char* const*>(argv_.data()));\n      }\n      _exit(EXIT_FAILURE);\n    }\n\n    int status;\n    waitpid(pid, &status, 0);\n  }\n\n private:\n  LaunchAtCrashHandler() = default;\n\n  ~LaunchAtCrashHandler() = delete;\n\n  std::vector<std::string> argv_strings_;\n  std::vector<const char*> argv_;\n  std::vector<std::string> envp_strings_;\n  std::vector<const char*> envp_;\n  bool set_envp_ = false;\n};\n\nclass RequestCrashDumpHandler : public SignalHandler {\n public:\n  RequestCrashDumpHandler(const RequestCrashDumpHandler&) = delete;\n  RequestCrashDumpHandler& operator=(const RequestCrashDumpHandler&) = delete;\n\n  static RequestCrashDumpHandler* Get() {\n    static RequestCrashDumpHandler* instance = new RequestCrashDumpHandler();\n    return instance;\n  }\n\n  // pid < 0 indicates the handler pid should be determined by communicating\n  // over the socket.\n  // pid == 0 indicates it is not necessary to set the handler as this process'\n  // ptracer. e.g. if the handler has CAP_SYS_PTRACE or if this process is in a\n  // user namespace and the handler's uid matches the uid of the process that\n  // created the namespace.\n  // pid > 0 directly indicates what the handler's pid is expected to be, so\n  // retrieving this information from the handler is not necessary.\n  bool Initialize(ScopedFileHandle sock,\n                  pid_t pid,\n                  const std::set<int>* unhandled_signals) {\n    ExceptionHandlerClient client(sock.get(), true);\n    if (pid < 0) {\n      ucred creds;\n      if (!client.GetHandlerCredentials(&creds)) {\n        return false;\n      }\n      pid = creds.pid;\n    }\n    if (pid > 0) {\n      pthread_atfork(nullptr, nullptr, SetPtracerAtFork);\n      if (prctl(PR_SET_PTRACER, pid, 0, 0, 0) != 0) {\n        PLOG(WARNING) << \"prctl\";\n      }\n    }\n    sock_to_handler_.reset(sock.release());\n    handler_pid_ = pid;\n    return Install(unhandled_signals);\n  }\n\n  bool GetHandlerSocket(int* sock, pid_t* pid) {\n    if (!sock_to_handler_.is_valid()) {\n      return false;\n    }\n    if (sock) {\n      *sock = sock_to_handler_.get();\n    }\n    if (pid) {\n      *pid = handler_pid_;\n    }\n    return true;\n  }\n\n  void HandleCrashImpl() override {\n    // Attempt to set the ptracer again, in case a crash occurs after a fork,\n    // before SetPtracerAtFork() has been called. Ignore errors because the\n    // system call may be disallowed if the sandbox is engaged.\n    if (handler_pid_ > 0) {\n      sys_prctl(PR_SET_PTRACER, handler_pid_, 0, 0, 0);\n    }\n\n    ExceptionHandlerProtocol::ClientInformation info = {};\n    info.exception_information_address =\n        FromPointerCast<VMAddress>(&GetExceptionInfo());\n#if BUILDFLAG(IS_CHROMEOS)\n    info.crash_loop_before_time = crash_loop_before_time_;\n#endif\n\n    ExceptionHandlerClient client(sock_to_handler_.get(), true);\n    client.RequestCrashDump(info);\n  }\n\n#if BUILDFLAG(IS_CHROMEOS)\n  void SetCrashLoopBefore(uint64_t crash_loop_before_time) {\n    crash_loop_before_time_ = crash_loop_before_time;\n  }\n#endif\n\n private:\n  RequestCrashDumpHandler() = default;\n\n  ~RequestCrashDumpHandler() = delete;\n\n  static void SetPtracerAtFork() {\n    auto handler = RequestCrashDumpHandler::Get();\n    if (handler->handler_pid_ > 0 &&\n        prctl(PR_SET_PTRACER, handler->handler_pid_, 0, 0, 0) != 0) {\n      PLOG(WARNING) << \"prctl\";\n    }\n  }\n\n  ScopedFileHandle sock_to_handler_;\n  pid_t handler_pid_ = -1;\n\n#if BUILDFLAG(IS_CHROMEOS)\n  // An optional UNIX timestamp passed to us from Chrome.\n  // This will pass to crashpad_handler and then to Chrome OS crash_reporter.\n  // This should really be a time_t, but it's basically an opaque value (we\n  // don't anything with it except pass it along).\n  uint64_t crash_loop_before_time_ = 0;\n#endif\n};\n\n}  // namespace\n\nCrashpadClient::CrashpadClient() {}\n\nCrashpadClient::~CrashpadClient() {}\n\nbool CrashpadClient::StartHandler(\n    const base::FilePath& handler,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    bool restartable,\n    bool asynchronous_start,\n    const std::vector<base::FilePath>& attachments) {\n  DCHECK(!asynchronous_start);\n\n  ScopedFileHandle client_sock, handler_sock;\n  if (!UnixCredentialSocket::CreateCredentialSocketpair(&client_sock,\n                                                        &handler_sock)) {\n    return false;\n  }\n\n  std::vector<std::string> argv = BuildHandlerArgvStrings(\n      handler, database, metrics_dir, url, annotations, arguments, attachments);\n\n  argv.push_back(FormatArgumentInt(\"initial-client-fd\", handler_sock.get()));\n  argv.push_back(\"--shared-client-connection\");\n  if (!SpawnSubprocess(argv, nullptr, handler_sock.get(), false, nullptr)) {\n    return false;\n  }\n  handler_sock.reset();\n\n  pid_t handler_pid = -1;\n  if (!IsRegularFile(base::FilePath(\"/proc/sys/kernel/yama/ptrace_scope\"))) {\n    handler_pid = 0;\n  }\n\n  auto signal_handler = RequestCrashDumpHandler::Get();\n  return signal_handler->Initialize(\n      std::move(client_sock), handler_pid, &unhandled_signals_);\n}\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n// static\nbool CrashpadClient::GetHandlerSocket(int* sock, pid_t* pid) {\n  auto signal_handler = RequestCrashDumpHandler::Get();\n  return signal_handler->GetHandlerSocket(sock, pid);\n}\n\nbool CrashpadClient::SetHandlerSocket(ScopedFileHandle sock, pid_t pid) {\n  auto signal_handler = RequestCrashDumpHandler::Get();\n  return signal_handler->Initialize(std::move(sock), pid, &unhandled_signals_);\n}\n\n// static\nbool CrashpadClient::InitializeSignalStackForThread() {\n  stack_t stack;\n  if (sigaltstack(nullptr, &stack) != 0) {\n    PLOG(ERROR) << \"sigaltstack\";\n    return false;\n  }\n\n  DCHECK_EQ(stack.ss_flags & SS_ONSTACK, 0);\n\n  const size_t page_size = getpagesize();\n#if defined(ADDRESS_SANITIZER)\n  const size_t kStackSize = 2 * ((SIGSTKSZ + page_size - 1) & ~(page_size - 1));\n#else\n  const size_t kStackSize = (SIGSTKSZ + page_size - 1) & ~(page_size - 1);\n#endif  // ADDRESS_SANITIZER\n  if (stack.ss_flags & SS_DISABLE || stack.ss_size < kStackSize) {\n    const size_t kGuardPageSize = page_size;\n    const size_t kStackAllocSize = kStackSize + 2 * kGuardPageSize;\n\n    static void (*stack_destructor)(void*) = [](void* stack_mem) {\n      const size_t page_size = getpagesize();\n      const size_t kGuardPageSize = page_size;\n#if defined(ADDRESS_SANITIZER)\n      const size_t kStackSize =\n          2 * ((SIGSTKSZ + page_size - 1) & ~(page_size - 1));\n#else\n      const size_t kStackSize = (SIGSTKSZ + page_size - 1) & ~(page_size - 1);\n#endif  // ADDRESS_SANITIZER\n      const size_t kStackAllocSize = kStackSize + 2 * kGuardPageSize;\n\n      stack_t stack;\n      stack.ss_flags = SS_DISABLE;\n      if (sigaltstack(&stack, &stack) != 0) {\n        PLOG(ERROR) << \"sigaltstack\";\n      } else if (stack.ss_sp !=\n                 static_cast<char*>(stack_mem) + kGuardPageSize) {\n        PLOG_IF(ERROR, sigaltstack(&stack, nullptr) != 0) << \"sigaltstack\";\n      }\n\n      if (munmap(stack_mem, kStackAllocSize) != 0) {\n        PLOG(ERROR) << \"munmap\";\n      }\n    };\n\n    static pthread_key_t stack_key;\n    static int key_error = []() {\n      errno = pthread_key_create(&stack_key, stack_destructor);\n      PLOG_IF(ERROR, errno) << \"pthread_key_create\";\n      return errno;\n    }();\n    if (key_error) {\n      return false;\n    }\n\n    auto old_stack = static_cast<char*>(pthread_getspecific(stack_key));\n    if (old_stack) {\n      stack.ss_sp = old_stack + kGuardPageSize;\n    } else {\n      ScopedMmap stack_mem;\n      if (!stack_mem.ResetMmap(nullptr,\n                               kStackAllocSize,\n                               PROT_NONE,\n                               MAP_PRIVATE | MAP_ANONYMOUS,\n                               -1,\n                               0)) {\n        return false;\n      }\n\n      if (mprotect(stack_mem.addr_as<char*>() + kGuardPageSize,\n                   kStackSize,\n                   PROT_READ | PROT_WRITE) != 0) {\n        PLOG(ERROR) << \"mprotect\";\n        return false;\n      }\n\n      stack.ss_sp = stack_mem.addr_as<char*>() + kGuardPageSize;\n\n      errno = pthread_setspecific(stack_key, stack_mem.release());\n      PCHECK(errno == 0) << \"pthread_setspecific\";\n    }\n\n    stack.ss_size = kStackSize;\n    stack.ss_flags =\n        (stack.ss_flags & SS_DISABLE) ? 0 : stack.ss_flags & SS_AUTODISARM;\n    if (sigaltstack(&stack, nullptr) != 0) {\n      PLOG(ERROR) << \"sigaltstack\";\n      return false;\n    }\n  }\n  return true;\n}\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n#if BUILDFLAG(IS_ANDROID)\n\nbool CrashpadClient::StartJavaHandlerAtCrash(\n    const std::string& class_name,\n    const std::vector<std::string>* env,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments) {\n  std::vector<std::string> argv = BuildAppProcessArgs(class_name,\n                                                      database,\n                                                      metrics_dir,\n                                                      url,\n                                                      annotations,\n                                                      arguments,\n                                                      kInvalidFileHandle);\n\n  auto signal_handler = LaunchAtCrashHandler::Get();\n  return signal_handler->Initialize(&argv, env, &unhandled_signals_);\n}\n\n// static\nbool CrashpadClient::StartJavaHandlerForClient(\n    const std::string& class_name,\n    const std::vector<std::string>* env,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    int socket) {\n  std::vector<std::string> argv = BuildAppProcessArgs(\n      class_name, database, metrics_dir, url, annotations, arguments, socket);\n  return SpawnSubprocess(argv, env, socket, false, nullptr);\n}\n\nbool CrashpadClient::StartHandlerWithLinkerAtCrash(\n    const std::string& handler_trampoline,\n    const std::string& handler_library,\n    bool is_64_bit,\n    const std::vector<std::string>* env,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments) {\n  std::vector<std::string> argv =\n      BuildArgsToLaunchWithLinker(handler_trampoline,\n                                  handler_library,\n                                  is_64_bit,\n                                  database,\n                                  metrics_dir,\n                                  url,\n                                  annotations,\n                                  arguments,\n                                  kInvalidFileHandle);\n  auto signal_handler = LaunchAtCrashHandler::Get();\n  return signal_handler->Initialize(&argv, env, &unhandled_signals_);\n}\n\n// static\nbool CrashpadClient::StartHandlerWithLinkerForClient(\n    const std::string& handler_trampoline,\n    const std::string& handler_library,\n    bool is_64_bit,\n    const std::vector<std::string>* env,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    int socket) {\n  std::vector<std::string> argv =\n      BuildArgsToLaunchWithLinker(handler_trampoline,\n                                  handler_library,\n                                  is_64_bit,\n                                  database,\n                                  metrics_dir,\n                                  url,\n                                  annotations,\n                                  arguments,\n                                  socket);\n  return SpawnSubprocess(argv, env, socket, false, nullptr);\n}\n\n#endif\n\nbool CrashpadClient::StartHandlerAtCrash(\n    const base::FilePath& handler,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    const std::vector<base::FilePath>& attachments) {\n  std::vector<std::string> argv = BuildHandlerArgvStrings(\n      handler, database, metrics_dir, url, annotations, arguments, attachments);\n\n  auto signal_handler = LaunchAtCrashHandler::Get();\n  return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_);\n}\n\n// static\nbool CrashpadClient::StartHandlerForClient(\n    const base::FilePath& handler,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    int socket) {\n  std::vector<std::string> argv = BuildHandlerArgvStrings(\n      handler, database, metrics_dir, url, annotations, arguments);\n\n  argv.push_back(FormatArgumentInt(\"initial-client-fd\", socket));\n\n  return SpawnSubprocess(argv, nullptr, socket, true, nullptr);\n}\n\n// static\nvoid CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {\n  if (!SignalHandler::Get()) {\n    DLOG(ERROR) << \"Crashpad isn't enabled\";\n    return;\n  }\n\n#if defined(ARCH_CPU_ARMEL)\n  memset(context->uc_regspace, 0, sizeof(context->uc_regspace));\n#elif defined(ARCH_CPU_ARM64)\n  memset(context->uc_mcontext.__reserved,\n         0,\n         sizeof(context->uc_mcontext.__reserved));\n#endif\n\n  siginfo_t siginfo;\n  siginfo.si_signo = Signals::kSimulatedSigno;\n  siginfo.si_errno = 0;\n  siginfo.si_code = 0;\n  SignalHandler::Get()->HandleCrash(\n      siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));\n}\n\n// static\nvoid CrashpadClient::CrashWithoutDump(const std::string& message) {\n  SignalHandler::Disable();\n  LOG(FATAL) << message;\n}\n\n// static\nvoid CrashpadClient::SetFirstChanceExceptionHandler(\n    FirstChanceHandler handler) {\n  DCHECK(SignalHandler::Get());\n  SignalHandler::Get()->SetFirstChanceHandler(handler);\n}\n\n// static\nvoid CrashpadClient::SetLastChanceExceptionHandler(LastChanceHandler handler) {\n  DCHECK(SignalHandler::Get());\n  SignalHandler::Get()->SetLastChanceExceptionHandler(handler);\n}\n\nvoid CrashpadClient::SetUnhandledSignals(const std::set<int>& signals) {\n  DCHECK(!SignalHandler::Get());\n  unhandled_signals_ = signals;\n}\n\n#if BUILDFLAG(IS_CHROMEOS)\n// static\nvoid CrashpadClient::SetCrashLoopBefore(uint64_t crash_loop_before_time) {\n  auto request_crash_dump_handler = RequestCrashDumpHandler::Get();\n  request_crash_dump_handler->SetCrashLoopBefore(crash_loop_before_time);\n}\n#endif\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_client_linux_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#include <dlfcn.h>\n#include <setjmp.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n#include <unistd.h>\n\n#include \"base/check_op.h\"\n#include \"build/build_config.h\"\n#include \"client/annotation.h\"\n#include \"client/annotation_list.h\"\n#include \"client/crash_report_database.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simulate_crash.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/annotation_snapshot.h\"\n#include \"snapshot/minidump/process_snapshot_minidump.h\"\n#include \"snapshot/sanitized/sanitization_information.h\"\n#include \"test/errors.h\"\n#include \"test/multiprocess.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/linux/exception_handler_client.h\"\n#include \"util/linux/exception_information.h\"\n#include \"util/linux/socket.h\"\n#include \"util/misc/address_sanitizer.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/memory_sanitizer.h\"\n#include \"util/posix/scoped_mmap.h\"\n#include \"util/posix/signals.h\"\n#include \"util/thread/thread.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/set_abort_message.h>\n#include \"dlfcn_internal.h\"\n\n// Normally this comes from set_abort_message.h, but only at API level 21.\nextern \"C\" void android_set_abort_message(const char* msg)\n    __attribute__((weak));\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nenum class CrashType : uint32_t {\n  kSimulated,\n  kBuiltinTrap,\n  kInfiniteRecursion,\n  kSegvWithTagBits,\n  // kFakeSegv is meant to simulate a MTE segv error.\n  kFakeSegv,\n};\n\nstruct StartHandlerForSelfTestOptions {\n  bool start_handler_at_crash;\n  bool set_first_chance_handler;\n  bool set_last_chance_handler;\n  bool crash_non_main_thread;\n  bool client_uses_signals;\n  bool gather_indirectly_referenced_memory;\n  CrashType crash_type;\n};\n\nclass StartHandlerForSelfTest\n    : public testing::TestWithParam<\n          std::tuple<bool, bool, bool, bool, bool, bool, CrashType>> {\n public:\n  StartHandlerForSelfTest() = default;\n\n  StartHandlerForSelfTest(const StartHandlerForSelfTest&) = delete;\n  StartHandlerForSelfTest& operator=(const StartHandlerForSelfTest&) = delete;\n\n  ~StartHandlerForSelfTest() = default;\n\n  void SetUp() override {\n    // MSAN requires that padding bytes have been initialized for structs that\n    // are written to files.\n    memset(&options_, 0, sizeof(options_));\n    std::tie(options_.start_handler_at_crash,\n             options_.set_first_chance_handler,\n             options_.set_last_chance_handler,\n             options_.crash_non_main_thread,\n             options_.client_uses_signals,\n             options_.gather_indirectly_referenced_memory,\n             options_.crash_type) = GetParam();\n  }\n\n  const StartHandlerForSelfTestOptions& Options() const { return options_; }\n\n private:\n  StartHandlerForSelfTestOptions options_;\n};\n\nbool InstallHandler(CrashpadClient* client,\n                    bool start_at_crash,\n                    const base::FilePath& handler_path,\n                    const base::FilePath& database_path,\n                    const std::vector<base::FilePath>& attachments) {\n  return start_at_crash\n             ? client->StartHandlerAtCrash(handler_path,\n                                           database_path,\n                                           base::FilePath(),\n                                           \"\",\n                                           std::map<std::string, std::string>(),\n                                           std::vector<std::string>(),\n                                           attachments)\n             : client->StartHandler(handler_path,\n                                    database_path,\n                                    base::FilePath(),\n                                    \"\",\n                                    std::map<std::string, std::string>(),\n                                    std::vector<std::string>(),\n                                    false,\n                                    false,\n                                    attachments);\n}\n\nconstexpr char kTestAnnotationName[] = \"name_of_annotation\";\nconstexpr char kTestAnnotationValue[] = \"value_of_annotation\";\nconstexpr char kTestAttachmentName[] = \"test_attachment\";\nconstexpr char kTestAttachmentContent[] = \"attachment_content\";\n\n#if BUILDFLAG(IS_ANDROID)\nconstexpr char kTestAbortMessage[] = \"test abort message\";\n#endif\n\nvoid ValidateAttachment(const CrashReportDatabase::UploadReport* report) {\n  auto attachments = report->GetAttachments();\n  ASSERT_EQ(attachments.size(), 1u);\n  char buf[sizeof(kTestAttachmentContent)];\n  attachments.at(kTestAttachmentName)->Read(buf, sizeof(buf));\n  ASSERT_EQ(memcmp(kTestAttachmentContent, buf, sizeof(kTestAttachmentContent)),\n            0);\n}\n\nvoid ValidateExtraMemory(const StartHandlerForSelfTestOptions& options,\n                         const ProcessSnapshotMinidump& minidump) {\n  // Verify that if we have an exception, then the code around the instruction\n  // pointer is included in the extra memory.\n  const ExceptionSnapshot* exception = minidump.Exception();\n  if (exception == nullptr)\n    return;\n  uint64_t pc = exception->Context()->InstructionPointer();\n  std::vector<const MemorySnapshot*> snippets = minidump.ExtraMemory();\n  bool pc_found = false;\n  for (const MemorySnapshot* snippet : snippets) {\n    uint64_t start = snippet->Address();\n    uint64_t end = start + snippet->Size();\n    if (pc >= start && pc < end) {\n      pc_found = true;\n      break;\n    }\n  }\n  EXPECT_EQ(pc_found, options.gather_indirectly_referenced_memory);\n\n  if (options.crash_type == CrashType::kSegvWithTagBits) {\n    EXPECT_EQ(exception->ExceptionAddress(), 0xefull << 56);\n  }\n}\n\nvoid ValidateDump(const StartHandlerForSelfTestOptions& options,\n                  const CrashReportDatabase::UploadReport* report) {\n  ProcessSnapshotMinidump minidump_snapshot;\n  ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader()));\n\n#if BUILDFLAG(IS_ANDROID)\n  // This part of the test requires Q. The API level on Q devices will be 28\n  // until the API is finalized, so we can't check API level yet. For now, test\n  // for the presence of a libc symbol which was introduced in Q.\n  if (crashpad::internal::Dlsym(RTLD_DEFAULT, \"android_fdsan_close_with_tag\")) {\n    const auto& annotations = minidump_snapshot.AnnotationsSimpleMap();\n    auto abort_message = annotations.find(\"abort_message\");\n    ASSERT_NE(annotations.end(), abort_message);\n    EXPECT_EQ(kTestAbortMessage, abort_message->second);\n  }\n#endif\n  ValidateAttachment(report);\n\n  ValidateExtraMemory(options, minidump_snapshot);\n\n  for (const ModuleSnapshot* module : minidump_snapshot.Modules()) {\n    for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {\n      if (static_cast<Annotation::Type>(annotation.type) !=\n          Annotation::Type::kString) {\n        continue;\n      }\n\n      if (annotation.name == kTestAnnotationName) {\n        std::string value(\n            reinterpret_cast<const char*>(annotation.value.data()),\n            annotation.value.size());\n        EXPECT_EQ(value, kTestAnnotationValue);\n        return;\n      }\n    }\n  }\n  ADD_FAILURE();\n}\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Winfinite-recursion\"\n// Clang (masquerading as gcc) is too smart, and removes the recursion\n// otherwise. May need to change if either clang or another compiler becomes\n// smarter.\n#if defined(COMPILER_GCC)\n__attribute__((noinline))\n#endif\n#if defined(__clang__)\n__attribute__((optnone))\n#endif\nint RecurseInfinitely(int* ptr) {\n  int buf[1 << 20];\n  return *ptr + RecurseInfinitely(buf);\n}\n#pragma clang diagnostic pop\n\nsigjmp_buf do_crash_sigjmp_env;\n\nbool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) {\n  siglongjmp(do_crash_sigjmp_env, 1);\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunreachable-code-return\"\n  return true;\n#pragma clang diagnostic pop\n}\n\nbool HandleCrashSuccessfullyAfterReporting(int, siginfo_t*, ucontext_t*) {\n  return true;\n}\n\nvoid DoCrash(const StartHandlerForSelfTestOptions& options,\n             CrashpadClient* client) {\n  if (sigsetjmp(do_crash_sigjmp_env, 1) != 0) {\n    return;\n  }\n\n  switch (options.crash_type) {\n    case CrashType::kSimulated: {\n      CRASHPAD_SIMULATE_CRASH();\n      break;\n    }\n\n    case CrashType::kBuiltinTrap: {\n      __builtin_trap();\n    }\n\n    case CrashType::kInfiniteRecursion: {\n      int val = 42;\n      exit(RecurseInfinitely(&val));\n    }\n\n    case CrashType::kSegvWithTagBits: {\n      volatile char* x = nullptr;\n#ifdef __aarch64__\n      x += 0xefull << 56;\n#endif  // __aarch64__\n      *x;\n      break;\n    }\n\n    case CrashType::kFakeSegv: {\n      // With a regular SIGSEGV like null dereference, the signal gets reraised\n      // automatically, causing HandleOrReraiseSignal() to be called a second\n      // time, terminating the process with the signal regardless of the last\n      // chance handler.\n      raise(SIGSEGV);\n      break;\n    }\n  }\n}\n\nclass ScopedAltSignalStack {\n public:\n  ScopedAltSignalStack() = default;\n\n  ScopedAltSignalStack(const ScopedAltSignalStack&) = delete;\n  ScopedAltSignalStack& operator=(const ScopedAltSignalStack&) = delete;\n\n  ~ScopedAltSignalStack() {\n    if (stack_mem_.is_valid()) {\n      stack_t stack;\n      stack.ss_flags = SS_DISABLE;\n      if (sigaltstack(&stack, nullptr) != 0) {\n        ADD_FAILURE() << ErrnoMessage(\"sigaltstack\");\n      }\n    }\n  }\n\n  void Initialize() {\n    ScopedMmap local_stack_mem;\n    const size_t stack_size = MINSIGSTKSZ;\n    ASSERT_TRUE(local_stack_mem.ResetMmap(nullptr,\n                                          stack_size,\n                                          PROT_READ | PROT_WRITE,\n                                          MAP_PRIVATE | MAP_ANONYMOUS,\n                                          -1,\n                                          0));\n\n    stack_t stack;\n    stack.ss_sp = local_stack_mem.addr();\n    stack.ss_size = stack_size;\n    stack.ss_flags = 0;\n    ASSERT_EQ(sigaltstack(&stack, nullptr), 0) << ErrnoMessage(\"sigaltstack\");\n    stack_mem_.ResetAddrLen(local_stack_mem.release(), stack_size);\n  }\n\n private:\n  ScopedMmap stack_mem_;\n};\n\nclass CrashThread : public Thread {\n public:\n  CrashThread(const StartHandlerForSelfTestOptions& options,\n              CrashpadClient* client)\n      : client_signal_stack_(), options_(options), client_(client) {}\n\n  CrashThread(const CrashThread&) = delete;\n  CrashThread& operator=(const CrashThread&) = delete;\n\n private:\n  void ThreadMain() override {\n    // It is only necessary to call InitializeSignalStackForThread() once, but\n    // should be harmless to call multiple times and durable against the client\n    // using sigaltstack() either before or after it is called.\n    CrashpadClient::InitializeSignalStackForThread();\n    if (options_.client_uses_signals) {\n      client_signal_stack_.Initialize();\n    }\n    CrashpadClient::InitializeSignalStackForThread();\n\n    DoCrash(options_, client_);\n  }\n\n  ScopedAltSignalStack client_signal_stack_;\n  const StartHandlerForSelfTestOptions& options_;\n  CrashpadClient* client_;\n};\n\nCRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {\n  FileHandle in = StdioFileHandle(StdioStream::kStandardInput);\n\n  VMSize temp_dir_length;\n  CheckedReadFileExactly(in, &temp_dir_length, sizeof(temp_dir_length));\n\n  std::string temp_dir(temp_dir_length, '\\0');\n  CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length);\n\n  StartHandlerForSelfTestOptions options;\n  CheckedReadFileExactly(in, &options, sizeof(options));\n\n  ScopedAltSignalStack client_signal_stack;\n  if (options.client_uses_signals) {\n    client_signal_stack.Initialize();\n\n    static Signals::OldActions old_actions;\n    static Signals::Handler client_handler =\n        [](int signo, siginfo_t* siginfo, void*) {\n          FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);\n          char c = 0;\n          WriteFile(out, &c, sizeof(c));\n\n          Signals::RestoreHandlerAndReraiseSignalOnReturn(\n              siginfo, old_actions.ActionForSignal(signo));\n        };\n\n    CHECK(Signals::InstallCrashHandlers(\n        client_handler, SA_ONSTACK, &old_actions));\n  }\n\n  if (options.gather_indirectly_referenced_memory) {\n    CrashpadInfo::GetCrashpadInfo()->set_gather_indirectly_referenced_memory(\n        TriState::kEnabled, 1024 * 1024 * 4);\n  }\n\n  base::FilePath handler_path = TestPaths::Executable().DirName().Append(\n      FILE_PATH_LITERAL(\"crashpad_handler\"));\n\n  crashpad::AnnotationList::Register();\n\n  static StringAnnotation<32> test_annotation(kTestAnnotationName);\n  test_annotation.Set(kTestAnnotationValue);\n\n  const std::vector<base::FilePath> attachments = {\n      base::FilePath(temp_dir).Append(kTestAttachmentName)};\n\n  crashpad::CrashpadClient client;\n  if (!InstallHandler(&client,\n                      options.start_handler_at_crash,\n                      handler_path,\n                      base::FilePath(temp_dir),\n                      attachments)) {\n    return EXIT_FAILURE;\n  }\n\n  if (options.set_first_chance_handler) {\n    client.SetFirstChanceExceptionHandler(HandleCrashSuccessfully);\n  }\n\n  if (options.set_last_chance_handler) {\n    client.SetLastChanceExceptionHandler(HandleCrashSuccessfullyAfterReporting);\n  }\n\n#if BUILDFLAG(IS_ANDROID)\n  if (android_set_abort_message) {\n    android_set_abort_message(kTestAbortMessage);\n  }\n#endif\n\n  if (options.crash_non_main_thread) {\n    CrashThread thread(options, &client);\n    thread.Start();\n    thread.Join();\n  } else {\n    DoCrash(options, &client);\n  }\n\n  return EXIT_SUCCESS;\n}\n\nclass StartHandlerForSelfInChildTest : public MultiprocessExec {\n public:\n  StartHandlerForSelfInChildTest(const StartHandlerForSelfTestOptions& options)\n      : MultiprocessExec(), options_(options) {\n    SetChildTestMainFunction(\"StartHandlerForSelfTestChild\");\n    if (!options.set_first_chance_handler) {\n      switch (options.crash_type) {\n        case CrashType::kSimulated:\n          // kTerminationNormal, EXIT_SUCCESS\n          break;\n        case CrashType::kBuiltinTrap:\n          SetExpectedChildTerminationBuiltinTrap();\n          break;\n        case CrashType::kInfiniteRecursion:\n          SetExpectedChildTermination(TerminationReason::kTerminationSignal,\n                                      SIGSEGV);\n          break;\n        case CrashType::kSegvWithTagBits:\n          SetExpectedChildTermination(TerminationReason::kTerminationSignal,\n                                      SIGSEGV);\n          break;\n        case CrashType::kFakeSegv:\n          if (!options.set_last_chance_handler) {\n            SetExpectedChildTermination(TerminationReason::kTerminationSignal,\n                                        SIGSEGV);\n          } else {\n            SetExpectedChildTermination(TerminationReason::kTerminationNormal,\n                                        EXIT_SUCCESS);\n          }\n          break;\n      }\n    }\n  }\n\n  StartHandlerForSelfInChildTest(const StartHandlerForSelfInChildTest&) =\n      delete;\n  StartHandlerForSelfInChildTest& operator=(\n      const StartHandlerForSelfInChildTest&) = delete;\n\n private:\n  void MultiprocessParent() override {\n    ScopedTempDir temp_dir;\n    VMSize temp_dir_length = temp_dir.path().value().size();\n    ASSERT_TRUE(LoggingWriteFile(\n        WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length)));\n    ASSERT_TRUE(LoggingWriteFile(\n        WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length));\n    ASSERT_TRUE(\n        LoggingWriteFile(WritePipeHandle(), &options_, sizeof(options_)));\n\n    FileWriter writer;\n    base::FilePath test_attachment_path =\n        temp_dir.path().Append(kTestAttachmentName);\n    bool is_created = writer.Open(test_attachment_path,\n                                  FileWriteMode::kCreateOrFail,\n                                  FilePermissions::kOwnerOnly);\n    ASSERT_TRUE(is_created);\n    writer.Write(kTestAttachmentContent, sizeof(kTestAttachmentContent));\n    writer.Close();\n\n    if (options_.client_uses_signals && !options_.set_first_chance_handler &&\n        options_.crash_type != CrashType::kSimulated &&\n        // The last chance handler will prevent the client handler from being\n        // called if crash type is kFakeSegv.\n        (!options_.set_last_chance_handler ||\n         options_.crash_type != CrashType::kFakeSegv)) {\n      // Wait for child's client signal handler.\n      char c;\n      EXPECT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));\n    }\n\n    // Wait for child to finish.\n    CheckedReadFileAtEOF(ReadPipeHandle());\n\n    auto database = CrashReportDatabase::Initialize(temp_dir.path());\n    ASSERT_TRUE(database);\n\n    std::vector<CrashReportDatabase::Report> reports;\n    ASSERT_EQ(database->GetCompletedReports(&reports),\n              CrashReportDatabase::kNoError);\n    EXPECT_EQ(reports.size(), 0u);\n\n    reports.clear();\n    ASSERT_EQ(database->GetPendingReports(&reports),\n              CrashReportDatabase::kNoError);\n\n    bool report_expected = !options_.set_first_chance_handler ||\n                           options_.crash_type == CrashType::kSimulated;\n    ASSERT_EQ(reports.size(), report_expected ? 1u : 0u);\n\n    if (!report_expected) {\n      return;\n    }\n\n    std::unique_ptr<const CrashReportDatabase::UploadReport> report;\n    ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report),\n              CrashReportDatabase::kNoError);\n    ValidateDump(options_, report.get());\n  }\n\n  StartHandlerForSelfTestOptions options_;\n};\n\nTEST_P(StartHandlerForSelfTest, StartHandlerInChild) {\n#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \\\n    defined(UNDEFINED_SANITIZER)\n  if (Options().crash_type == CrashType::kInfiniteRecursion) {\n    GTEST_SKIP();\n  }\n#endif  // defined(ADDRESS_SANITIZER)\n\n  // kFakeSegv does raise(SIGSEGV) to simulate a MTE error which is a SEGSEGV\n  // that doesn't get reraised automatically, but this causes the child process\n  // to flakily terminate normally on some bots (e.g. android-nougat-x86-rel)\n  // for some reason so this is skipped.\n  if (!Options().set_last_chance_handler &&\n      Options().crash_type == CrashType::kFakeSegv) {\n    GTEST_SKIP();\n  }\n\n  if (Options().crash_type == CrashType::kSegvWithTagBits) {\n#if !defined(ARCH_CPU_ARM64)\n    GTEST_SKIP() << \"Testing for tag bits only exists on aarch64.\";\n#else\n    struct utsname uname_info;\n    ASSERT_EQ(uname(&uname_info), 0);\n    ASSERT_NE(uname_info.release, nullptr);\n\n    char* release = uname_info.release;\n    unsigned major = strtoul(release, &release, 10);\n    ASSERT_EQ(*release++, '.');\n    unsigned minor = strtoul(release, nullptr, 10);\n\n    if (major < 5 || (major == 5 && minor < 11)) {\n      GTEST_SKIP() << \"Linux kernel v\" << uname_info.release\n                   << \" does not support SA_EXPOSE_TAGBITS\";\n    }\n#endif  // !defined(ARCH_CPU_ARM64)\n  }\n\n  StartHandlerForSelfInChildTest test(Options());\n  test.Run();\n}\n\nINSTANTIATE_TEST_SUITE_P(\n    StartHandlerForSelfTestSuite,\n    StartHandlerForSelfTest,\n    testing::Combine(testing::Bool(),\n                     testing::Bool(),\n                     testing::Bool(),\n                     testing::Bool(),\n                     testing::Bool(),\n                     testing::Bool(),\n                     testing::Values(CrashType::kSimulated,\n                                     CrashType::kBuiltinTrap,\n                                     CrashType::kInfiniteRecursion,\n                                     CrashType::kSegvWithTagBits,\n                                     CrashType::kFakeSegv)));\n\n// Test state for starting the handler for another process.\nclass StartHandlerForClientTest {\n public:\n  StartHandlerForClientTest() = default;\n\n  StartHandlerForClientTest(const StartHandlerForClientTest&) = delete;\n  StartHandlerForClientTest& operator=(const StartHandlerForClientTest&) =\n      delete;\n\n  ~StartHandlerForClientTest() = default;\n\n  bool Initialize(bool sanitize) {\n    sanitize_ = sanitize;\n    return UnixCredentialSocket::CreateCredentialSocketpair(&client_sock_,\n                                                            &server_sock_);\n  }\n\n  bool StartHandlerOnDemand() {\n    char c;\n    if (!LoggingReadFileExactly(server_sock_.get(), &c, sizeof(c))) {\n      ADD_FAILURE();\n      return false;\n    }\n\n    base::FilePath handler_path = TestPaths::Executable().DirName().Append(\n        FILE_PATH_LITERAL(\"crashpad_handler\"));\n\n    CrashpadClient client;\n    if (!client.StartHandlerForClient(handler_path,\n                                        temp_dir_.path(),\n                                        base::FilePath(),\n                                        \"\",\n                                        std::map<std::string, std::string>(),\n                                        std::vector<std::string>(),\n                                        server_sock_.get())) {\n      ADD_FAILURE();\n      return false;\n    }\n\n    return true;\n  }\n\n  void ExpectReport() {\n    auto database =\n        CrashReportDatabase::InitializeWithoutCreating(temp_dir_.path());\n    ASSERT_TRUE(database);\n\n    std::vector<CrashReportDatabase::Report> reports;\n    ASSERT_EQ(database->GetCompletedReports(&reports),\n              CrashReportDatabase::kNoError);\n    EXPECT_EQ(reports.size(), 0u);\n\n    reports.clear();\n    ASSERT_EQ(database->GetPendingReports(&reports),\n              CrashReportDatabase::kNoError);\n    if (sanitize_) {\n      EXPECT_EQ(reports.size(), 0u);\n    } else {\n      EXPECT_EQ(reports.size(), 1u);\n    }\n  }\n\n  bool InstallHandler() {\n    auto signal_handler = SandboxedHandler::Get();\n    return signal_handler->Initialize(client_sock_.get(), sanitize_);\n  }\n\n private:\n  // A signal handler that defers handler process startup to another, presumably\n  // more privileged, process.\n  class SandboxedHandler {\n   public:\n    SandboxedHandler(const SandboxedHandler&) = delete;\n    SandboxedHandler& operator=(const SandboxedHandler&) = delete;\n\n    static SandboxedHandler* Get() {\n      static SandboxedHandler* instance = new SandboxedHandler();\n      return instance;\n    }\n\n    bool Initialize(FileHandle client_sock, bool sanitize) {\n      client_sock_ = client_sock;\n      sanitize_ = sanitize;\n      return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr);\n    }\n\n   private:\n    SandboxedHandler() = default;\n    ~SandboxedHandler() = delete;\n\n    static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {\n      auto state = Get();\n\n      char c = 0;\n      CHECK(LoggingWriteFile(state->client_sock_, &c, sizeof(c)));\n\n      ExceptionInformation exception_information;\n      exception_information.siginfo_address =\n          FromPointerCast<decltype(exception_information.siginfo_address)>(\n              siginfo);\n      exception_information.context_address =\n          FromPointerCast<decltype(exception_information.context_address)>(\n              context);\n      exception_information.thread_id = syscall(SYS_gettid);\n\n      ExceptionHandlerProtocol::ClientInformation info;\n      info.exception_information_address =\n          FromPointerCast<decltype(info.exception_information_address)>(\n              &exception_information);\n\n      SanitizationInformation sanitization_info = {};\n      if (state->sanitize_) {\n        info.sanitization_information_address =\n            FromPointerCast<VMAddress>(&sanitization_info);\n        // Target a non-module address to prevent a crash dump.\n        sanitization_info.target_module_address =\n            FromPointerCast<VMAddress>(&sanitization_info);\n      }\n\n      ExceptionHandlerClient handler_client(state->client_sock_, false);\n      CHECK_EQ(handler_client.RequestCrashDump(info), 0);\n\n      Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);\n    }\n\n    FileHandle client_sock_;\n    bool sanitize_;\n  };\n\n  ScopedTempDir temp_dir_;\n  ScopedFileHandle client_sock_;\n  ScopedFileHandle server_sock_;\n  bool sanitize_;\n};\n\n// Tests starting the handler for a child process.\nclass StartHandlerForChildTest : public Multiprocess {\n public:\n  StartHandlerForChildTest() = default;\n\n  StartHandlerForChildTest(const StartHandlerForChildTest&) = delete;\n  StartHandlerForChildTest& operator=(const StartHandlerForChildTest&) = delete;\n\n  ~StartHandlerForChildTest() = default;\n\n  bool Initialize(bool sanitize) {\n    SetExpectedChildTerminationBuiltinTrap();\n    return test_state_.Initialize(sanitize);\n  }\n\n private:\n  void MultiprocessParent() {\n    ASSERT_TRUE(test_state_.StartHandlerOnDemand());\n\n    // Wait for chlid to finish.\n    CheckedReadFileAtEOF(ReadPipeHandle());\n\n    test_state_.ExpectReport();\n  }\n\n  [[noreturn]] void MultiprocessChild() {\n    CHECK(test_state_.InstallHandler());\n\n    __builtin_trap();\n  }\n\n  StartHandlerForClientTest test_state_;\n};\n\nTEST(CrashpadClient, StartHandlerForChild) {\n  StartHandlerForChildTest test;\n  ASSERT_TRUE(test.Initialize(/* sanitize= */ false));\n  test.Run();\n}\n\nTEST(CrashpadClient, SanitizedChild) {\n  StartHandlerForChildTest test;\n  ASSERT_TRUE(test.Initialize(/* sanitize= */ true));\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_client_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#include <Availability.h>\n#include <errno.h>\n#include <mach/mach.h>\n#include <pthread.h>\n#include <stdint.h>\n\n#include <memory>\n#include <tuple>\n#include <utility>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/mach/bootstrap.h\"\n#include \"util/mach/child_port_handshake.h\"\n#include \"util/mach/exception_ports.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/notify_server.h\"\n#include \"util/misc/clock.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/posix/spawn_subprocess.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nstd::string FormatArgumentString(const std::string& name,\n                                 const std::string& value) {\n  return base::StringPrintf(\"--%s=%s\", name.c_str(), value.c_str());\n}\n\nstd::string FormatArgumentInt(const std::string& name, int value) {\n  return base::StringPrintf(\"--%s=%d\", name.c_str(), value);\n}\n\n// Set the exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD.\n//\n// EXC_CRASH is how most crashes are received. Most other exception types such\n// as EXC_BAD_ACCESS are delivered to a host-level exception handler in the\n// kernel where they are converted to POSIX signals. See 10.9.5\n// xnu-2422.115.4/bsd/uxkern/ux_exception.c catch_mach_exception_raise(). If a\n// core-generating signal (triggered through this hardware mechanism or a\n// software mechanism such as abort() sending SIGABRT) is unhandled and the\n// process exits, or if the process is killed with SIGKILL for code-signing\n// reasons, an EXC_CRASH exception will be sent. See 10.9.5\n// xnu-2422.115.4/bsd/kern/kern_exit.c proc_prepareexit().\n//\n// EXC_RESOURCE and EXC_GUARD (pre-macOS 13) do not become signals or EXC_CRASH\n// exceptions. The host-level exception handler in the kernel does not receive\n// these exception types, and even if it did, it would not map them to signals.\n// Instead, the first Mach service loaded by the root (process ID 1) launchd\n// with a boolean “ExceptionServer” property in its job dictionary (regardless\n// of its value) or with any subdictionary property will become the host-level\n// exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. See 10.9.5\n// launchd-842.92.1/src/core.c job_setup_exception_port(). Normally, this job is\n// com.apple.ReportCrash.Root, the systemwide Apple Crash Reporter. Since it is\n// impossible to receive EXC_RESOURCE and EXC_GUARD exceptions through the\n// EXC_CRASH mechanism, an exception handler must be registered for them by name\n// if it is to receive these exception types. The default task-level handler for\n// these exception types is set by launchd in a similar manner.\n//\n// EXC_MASK_RESOURCE and EXC_MASK_GUARD are not available on all systems, and\n// the kernel will reject attempts to use them if it does not understand them,\n// so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported.\nbool SetCrashExceptionPorts(exception_handler_t exception_handler) {\n  ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);\n\n  exception_mask_t mask = EXC_MASK_CRASH | EXC_MASK_RESOURCE;\n  if (MacOSVersionNumber() < 13'00'00) {\n    // EXC_GUARD is delivered as an EXC_CRASH macOS 13 and later.\n    mask |= EXC_MASK_GUARD;\n  }\n\n  return exception_ports.SetExceptionPort(\n      mask & ExcMaskValid(),\n      exception_handler,\n      EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,\n      MACHINE_THREAD_STATE);\n}\n\nclass ScopedPthreadAttrDestroy {\n public:\n  explicit ScopedPthreadAttrDestroy(pthread_attr_t* pthread_attr)\n      : pthread_attr_(pthread_attr) {\n  }\n\n  ScopedPthreadAttrDestroy(const ScopedPthreadAttrDestroy&) = delete;\n  ScopedPthreadAttrDestroy& operator=(const ScopedPthreadAttrDestroy&) = delete;\n\n  ~ScopedPthreadAttrDestroy() {\n    errno = pthread_attr_destroy(pthread_attr_);\n    PLOG_IF(WARNING, errno != 0) << \"pthread_attr_destroy\";\n  }\n\n private:\n  pthread_attr_t* pthread_attr_;\n};\n\n//! \\brief Starts a Crashpad handler, possibly restarting it if it dies.\nclass HandlerStarter final : public NotifyServer::DefaultInterface {\n public:\n  HandlerStarter(const HandlerStarter&) = delete;\n  HandlerStarter& operator=(const HandlerStarter&) = delete;\n\n  ~HandlerStarter() {}\n\n  //! \\brief Starts a Crashpad handler initially, as opposed to starting it for\n  //!     subsequent restarts.\n  //!\n  //! All parameters are as in CrashpadClient::StartHandler().\n  //!\n  //! \\return On success, a send right to the Crashpad handler that has been\n  //!     started. On failure, `MACH_PORT_NULL` with a message logged.\n  static base::apple::ScopedMachSendRight InitialStart(\n      const base::FilePath& handler,\n      const base::FilePath& database,\n      const base::FilePath& metrics_dir,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      const std::vector<std::string>& arguments,\n      bool restartable,\n      const std::vector<base::FilePath>& attachments) {\n    base::apple::ScopedMachReceiveRight receive_right(\n        NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n    if (!receive_right.is_valid()) {\n      return base::apple::ScopedMachSendRight();\n    }\n\n    mach_port_t port;\n    mach_msg_type_name_t right_type;\n    kern_return_t kr = mach_port_extract_right(mach_task_self(),\n                                               receive_right.get(),\n                                               MACH_MSG_TYPE_MAKE_SEND,\n                                               &port,\n                                               &right_type);\n    if (kr != KERN_SUCCESS) {\n      MACH_LOG(ERROR, kr) << \"mach_port_extract_right\";\n      return base::apple::ScopedMachSendRight();\n    }\n    base::apple::ScopedMachSendRight send_right(port);\n    DCHECK_EQ(port, receive_right.get());\n    DCHECK_EQ(right_type,\n              implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND));\n\n    std::unique_ptr<HandlerStarter> handler_restarter;\n    if (restartable) {\n      handler_restarter.reset(new HandlerStarter());\n      if (!handler_restarter->notify_port_.is_valid()) {\n        // This is an error that NewMachPort() would have logged. Proceed anyway\n        // without the ability to restart.\n        handler_restarter.reset();\n      }\n    }\n\n    if (!CommonStart(handler,\n                     database,\n                     metrics_dir,\n                     url,\n                     annotations,\n                     arguments,\n                     std::move(receive_right),\n                     handler_restarter.get(),\n                     false,\n                     attachments)) {\n      return base::apple::ScopedMachSendRight();\n    }\n\n    if (handler_restarter && handler_restarter->StartRestartThread(\n                                 handler, database, metrics_dir, url,\n                                 annotations, arguments, attachments)) {\n      // The thread owns the object now.\n      std::ignore = handler_restarter.release();\n    }\n\n    // If StartRestartThread() failed, proceed without the ability to restart.\n    // handler_restarter will be released when this function returns.\n\n    return send_right;\n  }\n\n  // NotifyServer::DefaultInterface:\n\n  kern_return_t DoMachNotifyPortDestroyed(notify_port_t notify,\n                                          mach_port_t rights,\n                                          const mach_msg_trailer_t* trailer,\n                                          bool* destroy_request) override {\n    // The receive right corresponding to this process’ crash exception port is\n    // now owned by this process. Any crashes that occur before the receive\n    // right is moved to a new handler process will cause the process to hang in\n    // an unkillable state prior to OS X 10.10.\n\n    if (notify != notify_port_) {\n      LOG(WARNING) << \"notify port mismatch\";\n      return KERN_FAILURE;\n    }\n\n    // If CommonStart() fails, the receive right will die, and this will just\n    // be called again for another try.\n    CommonStart(handler_,\n                database_,\n                metrics_dir_,\n                url_,\n                annotations_,\n                arguments_,\n                base::apple::ScopedMachReceiveRight(rights),\n                this,\n                true,\n                attachments_);\n\n    return KERN_SUCCESS;\n  }\n\n private:\n  HandlerStarter()\n      : NotifyServer::DefaultInterface(),\n        handler_(),\n        database_(),\n        metrics_dir_(),\n        url_(),\n        annotations_(),\n        arguments_(),\n        notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)),\n        last_start_time_(0) {\n  }\n\n  //! \\brief Starts a Crashpad handler.\n  //!\n  //! All parameters are as in CrashpadClient::StartHandler(), with these\n  //! additions:\n  //!\n  //! \\param[in] receive_right The receive right to move to the Crashpad\n  //!     handler. The handler will use this receive right to run its exception\n  //!     server.\n  //! \\param[in] handler_restarter If CrashpadClient::StartHandler() was invoked\n  //!     with \\a restartable set to `true`, this is the restart state object.\n  //!     Otherwise, this is `nullptr`.\n  //! \\param[in] restart If CrashpadClient::StartHandler() was invoked with \\a\n  //!     restartable set to `true` and CommonStart() is being called to restart\n  //!     a previously-started handler, this is `true`. Otherwise, this is\n  //!     `false`.\n  //!\n  //! \\return `true` on success, `false` on failure, with a message logged.\n  //!     Failures include failure to start the handler process and failure to\n  //!     rendezvous with it via ChildPortHandshake.\n  static bool CommonStart(const base::FilePath& handler,\n                          const base::FilePath& database,\n                          const base::FilePath& metrics_dir,\n                          const std::string& url,\n                          const std::map<std::string, std::string>& annotations,\n                          const std::vector<std::string>& arguments,\n                          base::apple::ScopedMachReceiveRight receive_right,\n                          HandlerStarter* handler_restarter,\n                          bool restart,\n                          const std::vector<base::FilePath>& attachments) {\n    DCHECK(!restart || handler_restarter);\n\n    if (handler_restarter) {\n      // The port-destroyed notification must be requested each time. It uses\n      // a send-once right, so once the notification is received, it won’t be\n      // sent again unless re-requested.\n      mach_port_t previous;\n      kern_return_t kr =\n          mach_port_request_notification(mach_task_self(),\n                                         receive_right.get(),\n                                         MACH_NOTIFY_PORT_DESTROYED,\n                                         0,\n                                         handler_restarter->notify_port_.get(),\n                                         MACH_MSG_TYPE_MAKE_SEND_ONCE,\n                                         &previous);\n      if (kr != KERN_SUCCESS) {\n        MACH_LOG(WARNING, kr) << \"mach_port_request_notification\";\n\n        // This will cause the restart thread to terminate after this restart\n        // attempt. There’s no longer any need for it, because no more\n        // port-destroyed notifications can be delivered.\n        handler_restarter->notify_port_.reset();\n      } else {\n        base::apple::ScopedMachSendRight previous_owner(previous);\n        DCHECK(restart || !previous_owner.is_valid());\n      }\n\n      if (restart) {\n        // If the handler was ever started before, don’t restart it too quickly.\n        constexpr uint64_t kNanosecondsPerSecond = 1E9;\n        constexpr uint64_t kMinimumStartInterval = 1 * kNanosecondsPerSecond;\n\n        const uint64_t earliest_next_start_time =\n            handler_restarter->last_start_time_ + kMinimumStartInterval;\n        const uint64_t now_time = ClockMonotonicNanoseconds();\n        if (earliest_next_start_time > now_time) {\n          const uint64_t sleep_time = earliest_next_start_time - now_time;\n          LOG(INFO) << \"restarting handler\"\n                    << base::StringPrintf(\" in %.3fs\",\n                                          static_cast<double>(sleep_time) /\n                                              kNanosecondsPerSecond);\n          SleepNanoseconds(earliest_next_start_time - now_time);\n        } else {\n          LOG(INFO) << \"restarting handler\";\n        }\n      }\n\n      handler_restarter->last_start_time_ = ClockMonotonicNanoseconds();\n    }\n\n    ChildPortHandshake child_port_handshake;\n    base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD();\n\n    // Use handler as argv[0], followed by arguments directed by this method’s\n    // parameters and a --handshake-fd argument. |arguments| are added first so\n    // that if it erroneously contains an argument such as --url, the actual\n    // |url| argument passed to this method will supersede it. In normal\n    // command-line processing, the last parameter wins in the case of a\n    // conflict.\n    std::vector<std::string> argv(1, handler.value());\n    argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1);\n    for (const std::string& argument : arguments) {\n      argv.push_back(argument);\n    }\n    if (!database.value().empty()) {\n      argv.push_back(FormatArgumentString(\"database\", database.value()));\n    }\n    if (!metrics_dir.value().empty()) {\n      argv.push_back(FormatArgumentString(\"metrics-dir\", metrics_dir.value()));\n    }\n    if (!url.empty()) {\n      argv.push_back(FormatArgumentString(\"url\", url));\n    }\n    for (const auto& kv : annotations) {\n      argv.push_back(\n          FormatArgumentString(\"annotation\", kv.first + '=' + kv.second));\n    }\n    for (const auto& attachment : attachments) {\n      argv.push_back(FormatArgumentString(\"attachment\", attachment.value()));\n    }\n    argv.push_back(FormatArgumentInt(\"handshake-fd\", server_write_fd.get()));\n\n    // When restarting, reset the system default crash handler first. Otherwise,\n    // the crash exception port in the handler will have been inherited from\n    // this parent process, which was probably using the exception server now\n    // being restarted. The handler can’t monitor itself for its own crashes via\n    // this interface.\n    if (!SpawnSubprocess(\n            argv,\n            nullptr,\n            server_write_fd.get(),\n            true,\n            restart ? CrashpadClient::UseSystemDefaultHandler : nullptr)) {\n      return false;\n    }\n\n    // Close the write side of the pipe, so that the handler process is the only\n    // process that can write to it.\n    server_write_fd.reset();\n\n    // Rendezvous with the handler running in the grandchild process.\n    if (!child_port_handshake.RunClient(receive_right.get(),\n                                        MACH_MSG_TYPE_MOVE_RECEIVE)) {\n      return false;\n    }\n\n    std::ignore = receive_right.release();\n    return true;\n  }\n\n  bool StartRestartThread(const base::FilePath& handler,\n                          const base::FilePath& database,\n                          const base::FilePath& metrics_dir,\n                          const std::string& url,\n                          const std::map<std::string, std::string>& annotations,\n                          const std::vector<std::string>& arguments,\n                          const std::vector<base::FilePath>& attachments) {\n    handler_ = handler;\n    database_ = database;\n    metrics_dir_ = metrics_dir;\n    url_ = url;\n    annotations_ = annotations;\n    arguments_ = arguments;\n    attachments_ = attachments;\n\n    pthread_attr_t pthread_attr;\n    errno = pthread_attr_init(&pthread_attr);\n    if (errno != 0) {\n      PLOG(WARNING) << \"pthread_attr_init\";\n      return false;\n    }\n    ScopedPthreadAttrDestroy pthread_attr_owner(&pthread_attr);\n\n    errno = pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED);\n    if (errno != 0) {\n      PLOG(WARNING) << \"pthread_attr_setdetachstate\";\n      return false;\n    }\n\n    pthread_t pthread;\n    errno = pthread_create(&pthread, &pthread_attr, RestartThreadMain, this);\n    if (errno != 0) {\n      PLOG(WARNING) << \"pthread_create\";\n      return false;\n    }\n\n    return true;\n  }\n\n  static void* RestartThreadMain(void* argument) {\n    HandlerStarter* self = reinterpret_cast<HandlerStarter*>(argument);\n\n    NotifyServer notify_server(self);\n    mach_msg_return_t mr;\n    do {\n      mr = MachMessageServer::Run(&notify_server,\n                                  self->notify_port_.get(),\n                                  0,\n                                  MachMessageServer::kPersistent,\n                                  MachMessageServer::kReceiveLargeError,\n                                  kMachMessageTimeoutWaitIndefinitely);\n      MACH_LOG_IF(ERROR, mr != MACH_MSG_SUCCESS, mr)\n          << \"MachMessageServer::Run\";\n    } while (self->notify_port_.is_valid() && mr == MACH_MSG_SUCCESS);\n\n    delete self;\n\n    return nullptr;\n  }\n\n  base::FilePath handler_;\n  base::FilePath database_;\n  base::FilePath metrics_dir_;\n  std::string url_;\n  std::map<std::string, std::string> annotations_;\n  std::vector<std::string> arguments_;\n  std::vector<base::FilePath> attachments_;\n  base::apple::ScopedMachReceiveRight notify_port_;\n  uint64_t last_start_time_;\n};\n\n}  // namespace\n\nCrashpadClient::CrashpadClient() : exception_port_(MACH_PORT_NULL) {\n}\n\nCrashpadClient::~CrashpadClient() {\n}\n\nbool CrashpadClient::StartHandler(\n    const base::FilePath& handler,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    bool restartable,\n    bool asynchronous_start,\n    const std::vector<base::FilePath>& attachments) {\n  // The “restartable” behavior can only be selected on OS X 10.10 and later. In\n  // previous OS versions, if the initial client were to crash while attempting\n  // to restart the handler, it would become an unkillable process.\n  base::apple::ScopedMachSendRight exception_port(HandlerStarter::InitialStart(\n      handler,\n      database,\n      metrics_dir,\n      url,\n      annotations,\n      arguments,\n      restartable && (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||\n                      MacOSVersionNumber() >= 10'10'00),\n      attachments));\n  if (!exception_port.is_valid()) {\n    return false;\n  }\n\n  SetHandlerMachPort(std::move(exception_port));\n  return true;\n}\n\nbool CrashpadClient::SetHandlerMachService(const std::string& service_name) {\n  base::apple::ScopedMachSendRight exception_port(\n      BootstrapLookUp(service_name));\n  if (!exception_port.is_valid()) {\n    return false;\n  }\n\n  SetHandlerMachPort(std::move(exception_port));\n  return true;\n}\n\nbool CrashpadClient::SetHandlerMachPort(\n    base::apple::ScopedMachSendRight exception_port) {\n  DCHECK(!exception_port_.is_valid());\n  DCHECK(exception_port.is_valid());\n\n  if (!SetCrashExceptionPorts(exception_port.get())) {\n    return false;\n  }\n\n  exception_port_.swap(exception_port);\n  return true;\n}\n\nbase::apple::ScopedMachSendRight CrashpadClient::GetHandlerMachPort() const {\n  DCHECK(exception_port_.is_valid());\n\n  // For the purposes of this method, only return a port set by\n  // SetHandlerMachPort().\n  //\n  // It would be possible to use task_get_exception_ports() to look up the\n  // EXC_CRASH task exception port, but that’s probably not what users of this\n  // interface really want. If CrashpadClient is asked for the handler Mach\n  // port, it should only return a port that it knows about by virtue of having\n  // set it. It shouldn’t return any EXC_CRASH task exception port in effect if\n  // SetHandlerMachPort() was never called, and it shouldn’t return any\n  // EXC_CRASH task exception port that might be set by other code after\n  // SetHandlerMachPort() is called.\n  //\n  // The caller is accepting its own new ScopedMachSendRight, so increment the\n  // reference count of the underlying right.\n  kern_return_t kr = mach_port_mod_refs(\n      mach_task_self(), exception_port_.get(), MACH_PORT_RIGHT_SEND, 1);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << \"mach_port_mod_refs\";\n    return base::apple::ScopedMachSendRight(MACH_PORT_NULL);\n  }\n\n  return base::apple::ScopedMachSendRight(exception_port_.get());\n}\n\n// static\nvoid CrashpadClient::UseSystemDefaultHandler() {\n  base::apple::ScopedMachSendRight system_crash_reporter_handler(\n      SystemCrashReporterHandler());\n\n  // Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL\n  // to clear the current exception ports.\n  if (!SetCrashExceptionPorts(system_crash_reporter_handler.get())) {\n    SetCrashExceptionPorts(MACH_PORT_NULL);\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_client_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#include <windows.h>\n\n#include <werapi.h>\n\n#include <signal.h>\n#include <stdint.h>\n#include <string.h>\n\n#include <atomic>\n#include <memory>\n#include <string_view>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/scoped_generic.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"base/synchronization/lock.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/capture_context.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/random_string.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/command_line.h\"\n#include \"util/win/context_wrappers.h\"\n#include \"util/win/critical_section_with_debug_info.h\"\n#include \"util/win/exception_codes.h\"\n#include \"util/win/get_function.h\"\n#include \"util/win/handle.h\"\n#include \"util/win/initial_client_data.h\"\n#include \"util/win/loader_lock.h\"\n#include \"util/win/nt_internals.h\"\n#include \"util/win/ntstatus_logging.h\"\n#include \"util/win/process_info.h\"\n#include \"util/win/registration_protocol_win.h\"\n#include \"util/win/safe_terminate_process.h\"\n#include \"util/win/scoped_process_suspend.h\"\n#include \"util/win/termination_codes.h\"\n#include \"util/win/xp_compat.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// This handle is never closed. This is used to signal to the server that a dump\n// should be taken in the event of a crash.\nHANDLE g_signal_exception = INVALID_HANDLE_VALUE;\n\n// Where we store the exception information that the crash handler reads.\nExceptionInformation g_crash_exception_information;\n\n// Guards multiple simultaneous calls to DumpWithoutCrash() in the client.\n// This is leaked.\nbase::Lock* g_non_crash_dump_lock;\n\n// Where we store a pointer to the context information when taking a non-crash\n// dump.\nExceptionInformation g_non_crash_exception_information;\n\n// Context for the out-of-process exception handler module and holds non-crash\n// dump handles. Handles are never closed once created.\nWerRegistration g_wer_registration = {WerRegistration::kWerRegistrationVersion,\n                                      INVALID_HANDLE_VALUE,\n                                      INVALID_HANDLE_VALUE,\n                                      false,\n                                      nullptr,\n                                      {0},\n                                      {0},\n                                      {0}};\n\nenum class StartupState : int {\n  kNotReady = 0,  // This must be value 0 because it is the initial value of a\n                  // global AtomicWord.\n  kSucceeded = 1,  // The CreateProcess() for the handler succeeded.\n  kFailed = 2,  // The handler failed to start.\n};\n\n// This is a tri-state of type StartupState. It starts at 0 == kNotReady, and\n// when the handler is known to have started successfully, or failed to start\n// the value will be updated. The unhandled exception filter will not proceed\n// until one of those two cases happens.\nstd::atomic<StartupState> g_handler_startup_state;\n\n// A CRITICAL_SECTION initialized with\n// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO to force it to be allocated with a\n// valid .DebugInfo field. The address of this critical section is given to the\n// handler. All critical sections with debug info are linked in a doubly-linked\n// list, so this allows the handler to capture all of them.\nCRITICAL_SECTION g_critical_section_with_debug_info;\n\nvoid SetHandlerStartupState(StartupState state) {\n  DCHECK(state == StartupState::kSucceeded || state == StartupState::kFailed);\n  g_handler_startup_state.store(state, std::memory_order_release);\n}\n\nStartupState BlockUntilHandlerStartedOrFailed() {\n  // Wait until we know the handler has either succeeded or failed to start.\n  StartupState startup_state;\n  while ((startup_state = g_handler_startup_state.load(\n              std::memory_order_acquire)) == StartupState::kNotReady) {\n    Sleep(1);\n  }\n\n  return startup_state;\n}\n\n#if defined(ADDRESS_SANITIZER)\nextern \"C\" LONG __asan_unhandled_exception_filter(EXCEPTION_POINTERS* info);\n#endif\n\nLONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {\n#if defined(ADDRESS_SANITIZER)\n  // In ASan builds, delegate to the ASan exception filter.\n  LONG status = __asan_unhandled_exception_filter(exception_pointers);\n  if (status != EXCEPTION_CONTINUE_SEARCH)\n    return status;\n#endif\n\n  if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) {\n    // If we know for certain that the handler has failed to start, then abort\n    // here, rather than trying to signal to a handler that will never arrive,\n    // and then sleeping unnecessarily.\n    LOG(ERROR) << \"crash server failed to launch, self-terminating\";\n    SafeTerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump);\n    return EXCEPTION_CONTINUE_SEARCH;\n  }\n\n  // Otherwise, we know the handler startup has succeeded, and we can continue.\n\n  // Tracks whether a thread has already entered UnhandledExceptionHandler.\n  static std::atomic<bool> have_crashed;\n\n  // This is a per-process handler. While this handler is being invoked, other\n  // threads are still executing as usual, so multiple threads could enter at\n  // the same time. Because we're in a crashing state, we shouldn't be doing\n  // anything that might cause allocations, call into kernel mode, etc. So, we\n  // don't want to take a critical section here to avoid simultaneous access to\n  // the global exception pointers in ExceptionInformation. Because the crash\n  // handler will record all threads, it's fine to simply have the second and\n  // subsequent entrants block here. They will soon be suspended by the crash\n  // handler, and then the entire process will be terminated below. This means\n  // that we won't save the exception pointers from the second and further\n  // crashes, but contention here is very unlikely, and we'll still have a stack\n  // that's blocked at this location.\n  if (have_crashed.exchange(true)) {\n    SleepEx(INFINITE, false);\n  }\n\n  // Otherwise, we're the first thread, so record the exception pointer and\n  // signal the crash handler.\n  g_crash_exception_information.thread_id = GetCurrentThreadId();\n  g_crash_exception_information.exception_pointers =\n      FromPointerCast<WinVMAddress>(exception_pointers);\n\n  // Now signal the crash server, which will take a dump and then terminate us\n  // when it's complete.\n  SetEvent(g_signal_exception);\n\n  // Time to wait for the handler to create a dump.\n  constexpr DWORD kMillisecondsUntilTerminate = 60 * 1000;\n\n  // Sleep for a while to allow it to process us. Eventually, we terminate\n  // ourselves in case the crash server is gone, so that we don't leave zombies\n  // around. This would ideally never happen.\n  Sleep(kMillisecondsUntilTerminate);\n\n  LOG(ERROR) << \"crash server did not respond, self-terminating\";\n\n  SafeTerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump);\n\n  return EXCEPTION_CONTINUE_SEARCH;\n}\n\n#if !defined(ADDRESS_SANITIZER)\nLONG WINAPI HandleHeapCorruption(EXCEPTION_POINTERS* exception_pointers) {\n  if (exception_pointers->ExceptionRecord->ExceptionCode ==\n      STATUS_HEAP_CORRUPTION) {\n    return UnhandledExceptionHandler(exception_pointers);\n  }\n\n  return EXCEPTION_CONTINUE_SEARCH;\n}\n#endif\n\nvoid HandleAbortSignal(int signum) {\n  DCHECK_EQ(signum, SIGABRT);\n\n  CONTEXT context;\n  CaptureContext(&context);\n\n  EXCEPTION_RECORD record = {};\n  record.ExceptionCode = STATUS_FATAL_APP_EXIT;\n  record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;\n  record.ExceptionAddress = ProgramCounterFromCONTEXT(&context);\n\n  EXCEPTION_POINTERS exception_pointers;\n  exception_pointers.ContextRecord = &context;\n  exception_pointers.ExceptionRecord = &record;\n\n  UnhandledExceptionHandler(&exception_pointers);\n}\n\nstd::wstring FormatArgumentString(const std::string& name,\n                                  const std::wstring& value) {\n  return std::wstring(L\"--\") + base::UTF8ToWide(name) + L\"=\" + value;\n}\n\nstruct ScopedProcThreadAttributeListTraits {\n  static PPROC_THREAD_ATTRIBUTE_LIST InvalidValue() { return nullptr; }\n\n  static void Free(PPROC_THREAD_ATTRIBUTE_LIST proc_thread_attribute_list) {\n    // This is able to use GET_FUNCTION_REQUIRED() instead of GET_FUNCTION()\n    // because it will only be called if InitializeProcThreadAttributeList() and\n    // UpdateProcThreadAttribute() are present.\n    static const auto delete_proc_thread_attribute_list =\n        GET_FUNCTION_REQUIRED(L\"kernel32.dll\", ::DeleteProcThreadAttributeList);\n    delete_proc_thread_attribute_list(proc_thread_attribute_list);\n  }\n};\n\nusing ScopedProcThreadAttributeList =\n    base::ScopedGeneric<PPROC_THREAD_ATTRIBUTE_LIST,\n                        ScopedProcThreadAttributeListTraits>;\n\nbool IsInheritableHandle(HANDLE handle) {\n  if (!handle || handle == INVALID_HANDLE_VALUE)\n    return false;\n\n  // File handles (FILE_TYPE_DISK) and pipe handles (FILE_TYPE_PIPE) are known\n  // to be inheritable. Console handles (FILE_TYPE_CHAR) are not inheritable via\n  // PROC_THREAD_ATTRIBUTE_HANDLE_LIST. See\n  // https://crashpad.chromium.org/bug/77.\n  DWORD handle_type = GetFileType(handle);\n  return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;\n}\n\n// Adds |handle| to |handle_list| if it appears valid, and is not already in\n// |handle_list|.\n//\n// Invalid handles (including INVALID_HANDLE_VALUE and null handles) cannot be\n// added to a PPROC_THREAD_ATTRIBUTE_LIST’s PROC_THREAD_ATTRIBUTE_HANDLE_LIST.\n// If INVALID_HANDLE_VALUE appears, CreateProcess() will fail with\n// ERROR_INVALID_PARAMETER. If a null handle appears, the child process will\n// silently not inherit any handles.\n//\n// Use this function to add handles with uncertain validities.\nvoid AddHandleToListIfValidAndInheritable(std::vector<HANDLE>* handle_list,\n                                          HANDLE handle) {\n  // There doesn't seem to be any documentation of this, but if there's a handle\n  // duplicated in this list, CreateProcess() fails with\n  // ERROR_INVALID_PARAMETER.\n  if (IsInheritableHandle(handle) &&\n      std::find(handle_list->begin(), handle_list->end(), handle) ==\n          handle_list->end()) {\n    handle_list->push_back(handle);\n  }\n}\n\nvoid AddUint32(std::vector<unsigned char>* data_vector, uint32_t data) {\n  data_vector->push_back(static_cast<unsigned char>(data & 0xff));\n  data_vector->push_back(static_cast<unsigned char>((data & 0xff00) >> 8));\n  data_vector->push_back(static_cast<unsigned char>((data & 0xff0000) >> 16));\n  data_vector->push_back(static_cast<unsigned char>((data & 0xff000000) >> 24));\n}\n\nvoid AddUint64(std::vector<unsigned char>* data_vector, uint64_t data) {\n  AddUint32(data_vector, static_cast<uint32_t>(data & 0xffffffffULL));\n  AddUint32(data_vector,\n            static_cast<uint32_t>((data & 0xffffffff00000000ULL) >> 32));\n}\n\n//! \\brief Creates a randomized pipe name to listen for client registrations\n//!     on and returns its name.\n//!\n//! \\param[out] pipe_name The pipe name that will be listened on.\n//! \\param[out] pipe_handle The first pipe instance corresponding for the pipe.\nvoid CreatePipe(std::wstring* pipe_name, ScopedFileHANDLE* pipe_instance) {\n  int tries = 5;\n  std::string pipe_name_base = base::StringPrintf(\n#if defined(WINDOWS_UWP)\n      \"\\\\\\\\.\\\\pipe\\\\LOCAL\\\\crashpad_%lu_\",\n#else\n      \"\\\\\\\\.\\\\pipe\\\\crashpad_%lu_\",\n#endif\n      GetCurrentProcessId());\n  do {\n    *pipe_name = base::UTF8ToWide(pipe_name_base + RandomString());\n\n    pipe_instance->reset(CreateNamedPipeInstance(*pipe_name, true));\n\n    // CreateNamedPipe() is documented as setting the error to\n    // ERROR_ACCESS_DENIED if FILE_FLAG_FIRST_PIPE_INSTANCE is specified and the\n    // pipe name is already in use. However it may set the error to other codes\n    // such as ERROR_PIPE_BUSY (if the pipe already exists and has reached its\n    // maximum instance count) or ERROR_INVALID_PARAMETER (if the pipe already\n    // exists and its attributes differ from those specified to\n    // CreateNamedPipe()). Some of these errors may be ambiguous: for example,\n    // ERROR_INVALID_PARAMETER may also occur if CreateNamedPipe() is called\n    // incorrectly even in the absence of an existing pipe by the same name.\n    // Rather than chasing down all of the possible errors that might indicate\n    // that a pipe name is already in use, retry up to a few times on any error.\n  } while (!pipe_instance->is_valid() && --tries);\n\n  PCHECK(pipe_instance->is_valid()) << \"CreateNamedPipe\";\n}\n\nstruct BackgroundHandlerStartThreadData {\n  BackgroundHandlerStartThreadData(\n      const base::FilePath& handler,\n      const base::FilePath& database,\n      const base::FilePath& metrics_dir,\n      const std::string& url,\n      const std::map<std::string, std::string>& annotations,\n      const std::vector<std::string>& arguments,\n      const std::vector<base::FilePath>& attachments,\n      const std::wstring& ipc_pipe,\n      ScopedFileHANDLE ipc_pipe_handle)\n      : handler(handler),\n        database(database),\n        metrics_dir(metrics_dir),\n        url(url),\n        annotations(annotations),\n        arguments(arguments),\n        attachments(attachments),\n        ipc_pipe(ipc_pipe),\n        ipc_pipe_handle(std::move(ipc_pipe_handle)) {}\n\n  base::FilePath handler;\n  base::FilePath database;\n  base::FilePath metrics_dir;\n  std::string url;\n  std::map<std::string, std::string> annotations;\n  std::vector<std::string> arguments;\n  std::vector<base::FilePath> attachments;\n  std::wstring ipc_pipe;\n  ScopedFileHANDLE ipc_pipe_handle;\n};\n\n// Ensures that SetHandlerStartupState() is called on scope exit. Assumes\n// failure, and on success, SetSuccessful() should be called.\nclass ScopedCallSetHandlerStartupState {\n public:\n  ScopedCallSetHandlerStartupState() : successful_(false) {}\n\n  ScopedCallSetHandlerStartupState(const ScopedCallSetHandlerStartupState&) =\n      delete;\n  ScopedCallSetHandlerStartupState& operator=(\n      const ScopedCallSetHandlerStartupState&) = delete;\n\n  ~ScopedCallSetHandlerStartupState() {\n    SetHandlerStartupState(successful_ ? StartupState::kSucceeded\n                                       : StartupState::kFailed);\n  }\n\n  void SetSuccessful() { successful_ = true; }\n\n private:\n  bool successful_;\n};\n\nbool StartHandlerProcess(\n    std::unique_ptr<BackgroundHandlerStartThreadData> data) {\n  CHECK(!IsThreadInLoaderLock());\n\n  ScopedCallSetHandlerStartupState scoped_startup_state_caller;\n\n  std::wstring command_line;\n  AppendCommandLineArgument(data->handler.value(), &command_line);\n  for (const std::string& argument : data->arguments) {\n    AppendCommandLineArgument(base::UTF8ToWide(argument), &command_line);\n  }\n  if (!data->database.value().empty()) {\n    AppendCommandLineArgument(\n        FormatArgumentString(\"database\", data->database.value()),\n        &command_line);\n  }\n  if (!data->metrics_dir.value().empty()) {\n    AppendCommandLineArgument(\n        FormatArgumentString(\"metrics-dir\", data->metrics_dir.value()),\n        &command_line);\n  }\n  if (!data->url.empty()) {\n    AppendCommandLineArgument(\n        FormatArgumentString(\"url\", base::UTF8ToWide(data->url)),\n        &command_line);\n  }\n  for (const auto& kv : data->annotations) {\n    AppendCommandLineArgument(\n        FormatArgumentString(\"annotation\",\n                             base::UTF8ToWide(kv.first + '=' + kv.second)),\n        &command_line);\n  }\n  for (const base::FilePath& attachment : data->attachments) {\n    AppendCommandLineArgument(\n        FormatArgumentString(\"attachment\", attachment.value()),\n        &command_line);\n  }\n\n  ScopedKernelHANDLE this_process(\n      OpenProcess(kXPProcessAllAccess, true, GetCurrentProcessId()));\n  if (!this_process.is_valid()) {\n    PLOG(ERROR) << \"OpenProcess\";\n    return false;\n  }\n\n  InitialClientData initial_client_data(\n      g_signal_exception,\n      g_wer_registration.dump_without_crashing,\n      g_wer_registration.dump_completed,\n      data->ipc_pipe_handle.get(),\n      this_process.get(),\n      FromPointerCast<WinVMAddress>(&g_crash_exception_information),\n      FromPointerCast<WinVMAddress>(&g_non_crash_exception_information),\n      FromPointerCast<WinVMAddress>(&g_critical_section_with_debug_info));\n  AppendCommandLineArgument(\n      base::UTF8ToWide(std::string(\"--initial-client-data=\") +\n                       initial_client_data.StringRepresentation()),\n      &command_line);\n\n  BOOL rv;\n  DWORD creation_flags;\n  STARTUPINFOEX startup_info = {};\n  startup_info.StartupInfo.dwFlags =\n      STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK;\n  startup_info.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);\n  startup_info.StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);\n  startup_info.StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);\n\n  std::vector<HANDLE> handle_list;\n  std::unique_ptr<uint8_t[]> proc_thread_attribute_list_storage;\n  ScopedProcThreadAttributeList proc_thread_attribute_list_owner;\n\n  static const auto initialize_proc_thread_attribute_list =\n      GET_FUNCTION(L\"kernel32.dll\", ::InitializeProcThreadAttributeList);\n  static const auto update_proc_thread_attribute =\n      initialize_proc_thread_attribute_list\n          ? GET_FUNCTION(L\"kernel32.dll\", ::UpdateProcThreadAttribute)\n          : nullptr;\n  if (!initialize_proc_thread_attribute_list || !update_proc_thread_attribute) {\n    // The OS doesn’t allow handle inheritance to be restricted, so the handler\n    // will inherit every inheritable handle.\n    creation_flags = 0;\n    startup_info.StartupInfo.cb = sizeof(startup_info.StartupInfo);\n  } else {\n    // Restrict handle inheritance to just those needed in the handler.\n\n    creation_flags = EXTENDED_STARTUPINFO_PRESENT;\n    startup_info.StartupInfo.cb = sizeof(startup_info);\n    SIZE_T size;\n    rv = initialize_proc_thread_attribute_list(nullptr, 1, 0, &size);\n    if (rv) {\n      LOG(ERROR) << \"InitializeProcThreadAttributeList (size) succeeded, \"\n                    \"expected failure\";\n      return false;\n    } else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {\n      PLOG(ERROR) << \"InitializeProcThreadAttributeList (size)\";\n      return false;\n    }\n\n    proc_thread_attribute_list_storage.reset(new uint8_t[size]);\n    startup_info.lpAttributeList =\n        reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(\n            proc_thread_attribute_list_storage.get());\n    rv = initialize_proc_thread_attribute_list(\n        startup_info.lpAttributeList, 1, 0, &size);\n    if (!rv) {\n      PLOG(ERROR) << \"InitializeProcThreadAttributeList\";\n      return false;\n    }\n    proc_thread_attribute_list_owner.reset(startup_info.lpAttributeList);\n\n    handle_list.reserve(8);\n    handle_list.push_back(g_signal_exception);\n    handle_list.push_back(g_wer_registration.dump_without_crashing);\n    handle_list.push_back(g_wer_registration.dump_completed);\n    handle_list.push_back(data->ipc_pipe_handle.get());\n    handle_list.push_back(this_process.get());\n    AddHandleToListIfValidAndInheritable(&handle_list,\n                                         startup_info.StartupInfo.hStdInput);\n    AddHandleToListIfValidAndInheritable(&handle_list,\n                                         startup_info.StartupInfo.hStdOutput);\n    AddHandleToListIfValidAndInheritable(&handle_list,\n                                         startup_info.StartupInfo.hStdError);\n    rv = update_proc_thread_attribute(\n        startup_info.lpAttributeList,\n        0,\n        PROC_THREAD_ATTRIBUTE_HANDLE_LIST,\n        &handle_list[0],\n        handle_list.size() * sizeof(handle_list[0]),\n        nullptr,\n        nullptr);\n    if (!rv) {\n      PLOG(ERROR) << \"UpdateProcThreadAttribute\";\n      return false;\n    }\n  }\n\n  // If the embedded crashpad handler is being started via an entry point in a\n  // DLL (the handler executable is rundll32.exe), then don't pass\n  // the application name to CreateProcess as this appears to generate an\n  // invalid command line where the first argument needed by rundll32 is not in\n  // the correct format as required in:\n  // https://support.microsoft.com/en-ca/help/164787/info-windows-rundll-and-rundll32-interface\n  const std::wstring_view kRunDll32Exe(L\"rundll32.exe\");\n  bool is_embedded_in_dll = false;\n  if (data->handler.value().size() >= kRunDll32Exe.size() &&\n      _wcsicmp(data->handler.value()\n                   .substr(data->handler.value().size() - kRunDll32Exe.size())\n                   .c_str(),\n               kRunDll32Exe.data()) == 0) {\n    is_embedded_in_dll = true;\n  }\n\n  PROCESS_INFORMATION process_info;\n  rv = CreateProcess(\n      is_embedded_in_dll ? nullptr : data->handler.value().c_str(),\n      &command_line[0],\n      nullptr,\n      nullptr,\n      true,\n      creation_flags,\n      nullptr,\n      nullptr,\n      &startup_info.StartupInfo,\n      &process_info);\n  if (!rv) {\n    PLOG(ERROR) << \"CreateProcess\";\n    return false;\n  }\n\n  rv = CloseHandle(process_info.hThread);\n  PLOG_IF(WARNING, !rv) << \"CloseHandle thread\";\n\n  rv = CloseHandle(process_info.hProcess);\n  PLOG_IF(WARNING, !rv) << \"CloseHandle process\";\n\n  // It is important to close our side of the pipe here before confirming that\n  // we can communicate with the server. By doing so, the only remaining copy of\n  // the server side of the pipe belongs to the exception handler process we\n  // just spawned. Otherwise, the pipe will continue to exist indefinitely, so\n  // the connection loop will not detect that it will never be serviced.\n  data->ipc_pipe_handle.reset();\n\n  // Confirm that the server is waiting for connections before continuing.\n  ClientToServerMessage message = {};\n  message.type = ClientToServerMessage::kPing;\n  ServerToClientMessage response = {};\n  if (!SendToCrashHandlerServer(data->ipc_pipe, message, &response)) {\n    return false;\n  }\n\n  scoped_startup_state_caller.SetSuccessful();\n  return true;\n}\n\nDWORD WINAPI BackgroundHandlerStartThreadProc(void* data) {\n  std::unique_ptr<BackgroundHandlerStartThreadData> data_as_ptr(\n      reinterpret_cast<BackgroundHandlerStartThreadData*>(data));\n  return StartHandlerProcess(std::move(data_as_ptr)) ? 0 : 1;\n}\n\nvoid CommonInProcessInitialization() {\n  // We create this dummy CRITICAL_SECTION with the\n  // RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag set to have an entry point\n  // into the doubly-linked list of RTL_CRITICAL_SECTION_DEBUG objects. This\n  // allows us to walk the list at crash time to gather data for !locks. A\n  // debugger would instead inspect ntdll!RtlCriticalSectionList to get the head\n  // of the list. But that is not an exported symbol, so on an arbitrary client\n  // machine, we don't have a way of getting that pointer.\n  InitializeCriticalSectionWithDebugInfoIfPossible(\n      &g_critical_section_with_debug_info);\n\n  g_non_crash_dump_lock = new base::Lock();\n}\n\n}  // namespace\n\nCrashpadClient::CrashpadClient()\n    : ipc_pipe_(), handler_start_thread_(), vectored_handler_() {}\n\nCrashpadClient::~CrashpadClient() {}\n\nbool CrashpadClient::StartHandler(\n    const base::FilePath& handler,\n    const base::FilePath& database,\n    const base::FilePath& metrics_dir,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    const std::vector<std::string>& arguments,\n    bool restartable,\n    bool asynchronous_start,\n    const std::vector<base::FilePath>& attachments) {\n  DCHECK(ipc_pipe_.empty());\n\n  // Both the pipe and the signalling events have to be created on the main\n  // thread (not the spawning thread) so that they're valid after we return from\n  // this function.\n  ScopedFileHANDLE ipc_pipe_handle;\n  CreatePipe(&ipc_pipe_, &ipc_pipe_handle);\n\n  SECURITY_ATTRIBUTES security_attributes = {0};\n  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\n  security_attributes.bInheritHandle = true;\n\n  g_signal_exception =\n      CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);\n  g_wer_registration.dump_without_crashing =\n      CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);\n  g_wer_registration.dump_completed =\n      CreateEvent(&security_attributes, false /* auto reset */, false, nullptr);\n\n  CommonInProcessInitialization();\n\n  RegisterHandlers();\n\n  auto data = new BackgroundHandlerStartThreadData(handler,\n                                                   database,\n                                                   metrics_dir,\n                                                   url,\n                                                   annotations,\n                                                   arguments,\n                                                   attachments,\n                                                   ipc_pipe_,\n                                                   std::move(ipc_pipe_handle));\n\n  if (asynchronous_start) {\n    // It is important that the current thread not be synchronized with the\n    // thread that is created here. StartHandler() needs to be callable inside a\n    // DllMain(). In that case, the background thread will not start until the\n    // current DllMain() completes, which would cause deadlock if it was waited\n    // upon.\n    handler_start_thread_.reset(CreateThread(nullptr,\n                                             0,\n                                             &BackgroundHandlerStartThreadProc,\n                                             reinterpret_cast<void*>(data),\n                                             0,\n                                             nullptr));\n    if (!handler_start_thread_.is_valid()) {\n      PLOG(ERROR) << \"CreateThread\";\n      SetHandlerStartupState(StartupState::kFailed);\n      return false;\n    }\n\n    // In asynchronous mode, we can't report on the overall success or failure\n    // of initialization at this point.\n    return true;\n  } else {\n    return StartHandlerProcess(\n        std::unique_ptr<BackgroundHandlerStartThreadData>(data));\n  }\n}\n\nvoid CrashpadClient::RegisterHandlers() {\n  SetUnhandledExceptionFilter(&UnhandledExceptionHandler);\n\n  // Windows swallows heap corruption failures but we can intercept them with\n  // a vectored exception handler. Note that a vectored exception handler is\n  // not compatible with or generally helpful in ASAN builds (ASAN inserts a\n  // bad dereference at the beginning of the handler, leading to recursive\n  // invocation of the handler).\n#if !defined(ADDRESS_SANITIZER)\n  PVOID handler = AddVectoredExceptionHandler(true, HandleHeapCorruption);\n  vectored_handler_.reset(handler);\n#endif\n\n  // The Windows CRT's signal.h lists:\n  // - SIGINT\n  // - SIGILL\n  // - SIGFPE\n  // - SIGSEGV\n  // - SIGTERM\n  // - SIGBREAK\n  // - SIGABRT\n  // SIGILL and SIGTERM are documented as not being generated. SIGBREAK and\n  // SIGINT are for Ctrl-Break and Ctrl-C, and aren't something for which\n  // capturing a dump is warranted. SIGFPE and SIGSEGV are captured as regular\n  // exceptions through the unhandled exception filter. This leaves SIGABRT. In\n  // the standard CRT, abort() is implemented as a synchronous call to the\n  // SIGABRT signal handler if installed, but after doing so, the unhandled\n  // exception filter is not triggered (it instead __fastfail()s). So, register\n  // to handle SIGABRT to catch abort() calls, as client code might use this and\n  // expect it to cause a crash dump. This will only work when the abort()\n  // that's called in client code is the same (or has the same behavior) as the\n  // one in use here.\n  void (*rv)(int) = signal(SIGABRT, HandleAbortSignal);\n  DCHECK_NE(rv, SIG_ERR);\n}\n\nbool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) {\n  DCHECK(ipc_pipe_.empty());\n  DCHECK(!ipc_pipe.empty());\n\n  ipc_pipe_ = ipc_pipe;\n\n  DCHECK(!ipc_pipe_.empty());\n  DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE);\n  DCHECK_EQ(g_wer_registration.dump_without_crashing, INVALID_HANDLE_VALUE);\n  DCHECK_EQ(g_wer_registration.dump_completed, INVALID_HANDLE_VALUE);\n  DCHECK(!g_critical_section_with_debug_info.DebugInfo);\n  DCHECK(!g_non_crash_dump_lock);\n\n  ClientToServerMessage message;\n  memset(&message, 0, sizeof(message));\n  message.type = ClientToServerMessage::kRegister;\n  message.registration.version = RegistrationRequest::kMessageVersion;\n  message.registration.client_process_id = GetCurrentProcessId();\n  message.registration.crash_exception_information =\n      FromPointerCast<WinVMAddress>(&g_crash_exception_information);\n  message.registration.non_crash_exception_information =\n      FromPointerCast<WinVMAddress>(&g_non_crash_exception_information);\n\n  CommonInProcessInitialization();\n\n  message.registration.critical_section_address =\n      FromPointerCast<WinVMAddress>(&g_critical_section_with_debug_info);\n\n  ServerToClientMessage response = {};\n\n  if (!SendToCrashHandlerServer(ipc_pipe_, message, &response)) {\n    return false;\n  }\n\n  SetHandlerStartupState(StartupState::kSucceeded);\n\n  RegisterHandlers();\n\n  // The server returns these already duplicated to be valid in this process.\n  g_signal_exception =\n      IntToHandle(response.registration.request_crash_dump_event);\n  g_wer_registration.dump_without_crashing =\n      IntToHandle(response.registration.request_non_crash_dump_event);\n  g_wer_registration.dump_completed =\n      IntToHandle(response.registration.non_crash_dump_completed_event);\n\n  return true;\n}\n\nstd::wstring CrashpadClient::GetHandlerIPCPipe() const {\n  DCHECK(!ipc_pipe_.empty());\n  return ipc_pipe_;\n}\n\nbool CrashpadClient::WaitForHandlerStart(unsigned int timeout_ms) {\n  DCHECK(handler_start_thread_.is_valid());\n  DWORD result = WaitForSingleObject(handler_start_thread_.get(), timeout_ms);\n  if (result == WAIT_TIMEOUT) {\n    LOG(ERROR) << \"WaitForSingleObject timed out\";\n    return false;\n  } else if (result == WAIT_ABANDONED) {\n    LOG(ERROR) << \"WaitForSingleObject abandoned\";\n    return false;\n  } else if (result != WAIT_OBJECT_0) {\n    PLOG(ERROR) << \"WaitForSingleObject\";\n    return false;\n  }\n\n  DWORD exit_code;\n  if (!GetExitCodeThread(handler_start_thread_.get(), &exit_code)) {\n    PLOG(ERROR) << \"GetExitCodeThread\";\n    return false;\n  }\n\n  handler_start_thread_.reset();\n  return exit_code == 0;\n}\n\nbool CrashpadClient::RegisterWerModule(const std::wstring& path) {\n  if (g_wer_registration.dump_completed == INVALID_HANDLE_VALUE ||\n      g_wer_registration.dump_without_crashing == INVALID_HANDLE_VALUE) {\n    LOG(ERROR) << \"not connected\";\n    return false;\n  }\n  // We cannot point (*context).exception_pointers to our pointers yet as it\n  // might get used for other non-crash dumps.\n  g_wer_registration.crashpad_exception_info =\n      &g_non_crash_exception_information;\n  // we can point these as we are the only users.\n  g_wer_registration.pointers.ExceptionRecord = &g_wer_registration.exception;\n  g_wer_registration.pointers.ContextRecord = &g_wer_registration.context;\n\n  HRESULT res =\n      WerRegisterRuntimeExceptionModule(path.c_str(), &g_wer_registration);\n  return res == S_OK;\n}\n\n// static\nvoid CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {\n  if (g_wer_registration.dump_without_crashing == INVALID_HANDLE_VALUE ||\n      g_wer_registration.dump_completed == INVALID_HANDLE_VALUE) {\n    LOG(ERROR) << \"not connected\";\n    return;\n  }\n\n  if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) {\n    // If we know for certain that the handler has failed to start, then abort\n    // here, as we would otherwise wait indefinitely for the\n    // g_wer_registration.dump_completed event that would never be signalled.\n    LOG(ERROR) << \"crash server failed to launch, no dump captured\";\n    return;\n  }\n\n  // In the non-crashing case, we aren't concerned about avoiding calls into\n  // Win32 APIs, so just use regular locking here in case of multiple threads\n  // calling this function. If a crash occurs while we're in here, the worst\n  // that can happen is that the server captures a partial dump for this path\n  // because another thread’s crash processing finished and the process was\n  // terminated before this thread’s non-crash processing could be completed.\n  base::AutoLock lock(*g_non_crash_dump_lock);\n\n  // Create a fake EXCEPTION_POINTERS to give the handler something to work\n  // with.\n  EXCEPTION_POINTERS exception_pointers = {};\n\n  // This is logically const, but EXCEPTION_POINTERS does not declare it as\n  // const, so we have to cast that away from the argument.\n  exception_pointers.ContextRecord = const_cast<CONTEXT*>(&context);\n\n  // We include a fake exception and use a code of '0x517a7ed' (something like\n  // \"simulated\") so that it's relatively obvious in windbg that it's not\n  // actually an exception. Most values in\n  // https://msdn.microsoft.com/library/aa363082.aspx have some of the top\n  // nibble set, so we make sure to pick a value that doesn't, so as to be\n  // unlikely to conflict.\n  constexpr uint32_t kSimulatedExceptionCode = 0x517a7ed;\n  EXCEPTION_RECORD record = {};\n  record.ExceptionCode = kSimulatedExceptionCode;\n  record.ExceptionAddress = ProgramCounterFromCONTEXT(&context);\n\n  exception_pointers.ExceptionRecord = &record;\n\n  g_non_crash_exception_information.thread_id = GetCurrentThreadId();\n  g_non_crash_exception_information.exception_pointers =\n      FromPointerCast<WinVMAddress>(&exception_pointers);\n\n  g_wer_registration.in_dump_without_crashing = true;\n  bool set_event_result = !!SetEvent(g_wer_registration.dump_without_crashing);\n  PLOG_IF(ERROR, !set_event_result) << \"SetEvent\";\n\n  DWORD wfso_result =\n      WaitForSingleObject(g_wer_registration.dump_completed, INFINITE);\n  PLOG_IF(ERROR, wfso_result != WAIT_OBJECT_0) << \"WaitForSingleObject\";\n  g_wer_registration.in_dump_without_crashing = false;\n}\n\n// static\nvoid CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) {\n  if (g_signal_exception == INVALID_HANDLE_VALUE) {\n    LOG(ERROR) << \"not connected\";\n    SafeTerminateProcess(GetCurrentProcess(),\n                         kTerminationCodeNotConnectedToHandler);\n    return;\n  }\n\n  // We don't need to check for handler startup here, as\n  // UnhandledExceptionHandler() necessarily does that.\n\n  UnhandledExceptionHandler(exception_pointers);\n}\n\n// static\nbool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process,\n                                               HANDLE blame_thread,\n                                               DWORD exception_code) {\n  // Confirm we're on Vista or later.\n  const DWORD version = GetVersion();\n  const DWORD major_version = LOBYTE(LOWORD(version));\n  if (major_version < 6) {\n    LOG(ERROR) << \"unavailable before Vista\";\n    return false;\n  }\n\n  // Confirm that our bitness is the same as the process we're crashing.\n  ProcessInfo process_info;\n  if (!process_info.Initialize(process)) {\n    LOG(ERROR) << \"ProcessInfo::Initialize\";\n    return false;\n  }\n#if defined(ARCH_CPU_64_BITS)\n  if (!process_info.Is64Bit()) {\n    LOG(ERROR) << \"DumpAndCrashTargetProcess currently not supported x64->x86\";\n    return false;\n  }\n#endif  // ARCH_CPU_64_BITS\n\n  ScopedProcessSuspend suspend(process);\n\n  // If no thread handle was provided, or the thread has already exited, we pass\n  // 0 to the handler, which indicates no fake exception record to be created.\n  DWORD thread_id = 0;\n  if (blame_thread) {\n    // Now that we've suspended the process, if our thread hasn't exited, we\n    // know we're relatively safe to pass the thread id through.\n    if (WaitForSingleObject(blame_thread, 0) == WAIT_TIMEOUT) {\n      static const auto get_thread_id =\n          GET_FUNCTION_REQUIRED(L\"kernel32.dll\", ::GetThreadId);\n      thread_id = get_thread_id(blame_thread);\n    }\n  }\n\n  constexpr size_t kInjectBufferSize = 4 * 1024;\n  WinVMAddress inject_memory =\n      FromPointerCast<WinVMAddress>(VirtualAllocEx(process,\n                                                   nullptr,\n                                                   kInjectBufferSize,\n                                                   MEM_RESERVE | MEM_COMMIT,\n                                                   PAGE_READWRITE));\n  if (!inject_memory) {\n    PLOG(ERROR) << \"VirtualAllocEx\";\n    return false;\n  }\n\n  // Because we're the same bitness as our target, we can rely kernel32 being\n  // loaded at the same address in our process as the target, and just look up\n  // its address here.\n  WinVMAddress raise_exception_address =\n      FromPointerCast<WinVMAddress>(&RaiseException);\n\n  WinVMAddress code_entry_point = 0;\n  std::vector<unsigned char> data_to_write;\n  if (process_info.Is64Bit()) {\n    // Data written is first, the data for the 4th argument (lpArguments) to\n    // RaiseException(). A two element array:\n    //\n    // DWORD64: thread_id\n    // DWORD64: exception_code\n    //\n    // Following that, code which sets the arguments to RaiseException() and\n    // then calls it:\n    //\n    // mov r9, <data_array_address>\n    // mov r8d, 2  ; nNumberOfArguments\n    // mov edx, 1  ; dwExceptionFlags = EXCEPTION_NONCONTINUABLE\n    // mov ecx, 0xcca11ed  ; dwExceptionCode, interpreted specially by the\n    //                     ;                  handler.\n    // jmp <address_of_RaiseException>\n    //\n    // Note that the first three arguments to RaiseException() are DWORDs even\n    // on x64, so only the 4th argument (a pointer) is a full-width register.\n    //\n    // We also don't need to set up a stack or use call, since the only\n    // registers modified are volatile ones, and we can just jmp straight to\n    // RaiseException().\n\n    // The data array.\n    AddUint64(&data_to_write, thread_id);\n    AddUint64(&data_to_write, exception_code);\n\n    // The thread entry point.\n    code_entry_point = inject_memory + data_to_write.size();\n\n    // r9 = pointer to data.\n    data_to_write.push_back(0x49);\n    data_to_write.push_back(0xb9);\n    AddUint64(&data_to_write, inject_memory);\n\n    // r8d = 2 for nNumberOfArguments.\n    data_to_write.push_back(0x41);\n    data_to_write.push_back(0xb8);\n    AddUint32(&data_to_write, 2);\n\n    // edx = 1 for dwExceptionFlags.\n    data_to_write.push_back(0xba);\n    AddUint32(&data_to_write, 1);\n\n    // ecx = kTriggeredExceptionCode for dwExceptionCode.\n    data_to_write.push_back(0xb9);\n    AddUint32(&data_to_write, ExceptionCodes::kTriggeredExceptionCode);\n\n    // jmp to RaiseException() via rax.\n    data_to_write.push_back(0x48);  // mov rax, imm.\n    data_to_write.push_back(0xb8);\n    AddUint64(&data_to_write, raise_exception_address);\n    data_to_write.push_back(0xff);  // jmp rax.\n    data_to_write.push_back(0xe0);\n  } else {\n    // Data written is first, the data for the 4th argument (lpArguments) to\n    // RaiseException(). A two element array:\n    //\n    // DWORD: thread_id\n    // DWORD: exception_code\n    //\n    // Following that, code which pushes our arguments to RaiseException() and\n    // then calls it:\n    //\n    // push <data_array_address>\n    // push 2  ; nNumberOfArguments\n    // push 1  ; dwExceptionFlags = EXCEPTION_NONCONTINUABLE\n    // push 0xcca11ed  ; dwExceptionCode, interpreted specially by the handler.\n    // call <address_of_RaiseException>\n    // ud2  ; Generate invalid opcode to make sure we still crash if we return\n    //      ; for some reason.\n    //\n    // No need to clean up the stack, as RaiseException() is __stdcall.\n\n    // The data array.\n    AddUint32(&data_to_write, thread_id);\n    AddUint32(&data_to_write, exception_code);\n\n    // The thread entry point.\n    code_entry_point = inject_memory + data_to_write.size();\n\n    // Push data address.\n    data_to_write.push_back(0x68);\n    AddUint32(&data_to_write, static_cast<uint32_t>(inject_memory));\n\n    // Push 2 for nNumberOfArguments.\n    data_to_write.push_back(0x6a);\n    data_to_write.push_back(2);\n\n    // Push 1 for dwExceptionCode.\n    data_to_write.push_back(0x6a);\n    data_to_write.push_back(1);\n\n    // Push dwExceptionFlags.\n    data_to_write.push_back(0x68);\n    AddUint32(&data_to_write, kTriggeredExceptionCode);\n\n    // Relative call to RaiseException().\n    int64_t relative_address_to_raise_exception =\n        raise_exception_address - (inject_memory + data_to_write.size() + 5);\n    data_to_write.push_back(0xe8);\n    AddUint32(&data_to_write,\n              static_cast<uint32_t>(relative_address_to_raise_exception));\n\n    // ud2.\n    data_to_write.push_back(0x0f);\n    data_to_write.push_back(0x0b);\n  }\n\n  DCHECK_LT(data_to_write.size(), kInjectBufferSize);\n\n  SIZE_T bytes_written;\n  if (!WriteProcessMemory(process,\n                          reinterpret_cast<void*>(inject_memory),\n                          data_to_write.data(),\n                          data_to_write.size(),\n                          &bytes_written)) {\n    PLOG(ERROR) << \"WriteProcessMemory\";\n    return false;\n  }\n\n  if (bytes_written != data_to_write.size()) {\n    LOG(ERROR) << \"WriteProcessMemory unexpected number of bytes\";\n    return false;\n  }\n\n  if (!FlushInstructionCache(\n          process, reinterpret_cast<void*>(inject_memory), bytes_written)) {\n    PLOG(ERROR) << \"FlushInstructionCache\";\n    return false;\n  }\n\n  DWORD old_protect;\n  if (!VirtualProtectEx(process,\n                        reinterpret_cast<void*>(inject_memory),\n                        kInjectBufferSize,\n                        PAGE_EXECUTE_READ,\n                        &old_protect)) {\n    PLOG(ERROR) << \"VirtualProtectEx\";\n    return false;\n  }\n\n  // Cause an exception in the target process by creating a thread which calls\n  // RaiseException with our arguments above. Note that we cannot get away with\n  // using DebugBreakProcess() (nothing happens unless a debugger is attached)\n  // and we cannot get away with CreateRemoteThread() because it doesn't work if\n  // the target is hung waiting for the loader lock. We use NtCreateThreadEx()\n  // with the SKIP_THREAD_ATTACH flag, which skips various notifications,\n  // letting this cause an exception, even when the target is stuck in the\n  // loader lock.\n  HANDLE injected_thread;\n\n  // This is what DebugBreakProcess() uses.\n  constexpr size_t kStackSize = 0x4000;\n\n  NTSTATUS status = NtCreateThreadEx(&injected_thread,\n                                     STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,\n                                     nullptr,\n                                     process,\n                                     reinterpret_cast<void*>(code_entry_point),\n                                     nullptr,\n                                     THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH,\n                                     0,\n                                     kStackSize,\n                                     0,\n                                     nullptr);\n  if (!NT_SUCCESS(status)) {\n    NTSTATUS_LOG(ERROR, status) << \"NtCreateThreadEx\";\n    return false;\n  }\n\n  // The injected thread raises an exception and ultimately results in process\n  // termination. The suspension must be made aware that the process may be\n  // terminating, otherwise it’ll log an extraneous error.\n  suspend.TolerateTermination();\n\n  bool result = true;\n  if (WaitForSingleObject(injected_thread, 60 * 1000) != WAIT_OBJECT_0) {\n    PLOG(ERROR) << \"WaitForSingleObject\";\n    result = false;\n  }\n\n  status = NtClose(injected_thread);\n  if (!NT_SUCCESS(status)) {\n    NTSTATUS_LOG(ERROR, status) << \"NtClose\";\n    result = false;\n  }\n\n  return result;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_client_win_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"gtest/gtest.h\"\n#include \"test/test_paths.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"test/win/win_multiprocess.h\"\n#include \"test/win/win_multiprocess_with_temp_dir.h\"\n#include \"util/win/scoped_handle.h\"\n#include \"util/win/termination_codes.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid StartAndUseHandler(const base::FilePath& temp_dir) {\n  base::FilePath handler_path = TestPaths::Executable().DirName().Append(\n      FILE_PATH_LITERAL(\"crashpad_handler.com\"));\n\n  CrashpadClient client;\n  ASSERT_TRUE(client.StartHandler(handler_path,\n                                  temp_dir,\n                                  base::FilePath(),\n                                  \"\",\n                                  std::map<std::string, std::string>(),\n                                  std::vector<std::string>(),\n                                  true,\n                                  true));\n  ASSERT_TRUE(client.WaitForHandlerStart(INFINITE));\n}\n\nclass StartWithInvalidHandles final : public WinMultiprocessWithTempDir {\n public:\n  StartWithInvalidHandles() : WinMultiprocessWithTempDir() {}\n  ~StartWithInvalidHandles() {}\n\n private:\n  void WinMultiprocessParent() override {}\n\n  void WinMultiprocessChild() override {\n    HANDLE original_stdout = GetStdHandle(STD_OUTPUT_HANDLE);\n    HANDLE original_stderr = GetStdHandle(STD_ERROR_HANDLE);\n    SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);\n    SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);\n\n    StartAndUseHandler(GetTempDirPath());\n\n    SetStdHandle(STD_OUTPUT_HANDLE, original_stdout);\n    SetStdHandle(STD_ERROR_HANDLE, original_stderr);\n  }\n};\n\nTEST(CrashpadClient, StartWithInvalidHandles) {\n  WinMultiprocessWithTempDir::Run<StartWithInvalidHandles>();\n}\n\nclass StartWithSameStdoutStderr final : public WinMultiprocessWithTempDir {\n public:\n  StartWithSameStdoutStderr() : WinMultiprocessWithTempDir() {}\n  ~StartWithSameStdoutStderr() {}\n\n private:\n  void WinMultiprocessParent() override {}\n\n  void WinMultiprocessChild() override {\n    HANDLE original_stdout = GetStdHandle(STD_OUTPUT_HANDLE);\n    HANDLE original_stderr = GetStdHandle(STD_ERROR_HANDLE);\n    SetStdHandle(STD_OUTPUT_HANDLE, original_stderr);\n\n    StartAndUseHandler(GetTempDirPath());\n\n    SetStdHandle(STD_OUTPUT_HANDLE, original_stdout);\n  }\n};\n\nTEST(CrashpadClient, StartWithSameStdoutStderr) {\n  WinMultiprocessWithTempDir::Run<StartWithSameStdoutStderr>();\n}\n\nvoid StartAndUseBrokenHandler(CrashpadClient* client) {\n  ScopedTempDir temp_dir;\n  base::FilePath handler_path = TestPaths::Executable().DirName().Append(\n      FILE_PATH_LITERAL(\"fake_handler_that_crashes_at_startup.exe\"));\n  ASSERT_TRUE(client->StartHandler(handler_path,\n                                  temp_dir.path(),\n                                  base::FilePath(),\n                                  \"\",\n                                  std::map<std::string, std::string>(),\n                                  std::vector<std::string>(),\n                                  false,\n                                  true));\n}\n\nclass HandlerLaunchFailureCrash : public WinMultiprocess {\n public:\n  HandlerLaunchFailureCrash() : WinMultiprocess() {}\n\n private:\n  void WinMultiprocessParent() override {\n    SetExpectedChildExitCode(crashpad::kTerminationCodeCrashNoDump);\n  }\n\n  void WinMultiprocessChild() override {\n    CrashpadClient client;\n    StartAndUseBrokenHandler(&client);\n    __debugbreak();\n    exit(0);\n  }\n};\n\n#if defined(ADDRESS_SANITIZER)\n// https://crbug.com/845011\n#define MAYBE_HandlerLaunchFailureCrash DISABLED_HandlerLaunchFailureCrash\n#else\n#define MAYBE_HandlerLaunchFailureCrash HandlerLaunchFailureCrash\n#endif\nTEST(CrashpadClient, MAYBE_HandlerLaunchFailureCrash) {\n  WinMultiprocess::Run<HandlerLaunchFailureCrash>();\n}\n\nclass HandlerLaunchFailureDumpAndCrash : public WinMultiprocess {\n public:\n  HandlerLaunchFailureDumpAndCrash() : WinMultiprocess() {}\n\n private:\n  void WinMultiprocessParent() override {\n    SetExpectedChildExitCode(crashpad::kTerminationCodeCrashNoDump);\n  }\n\n  void WinMultiprocessChild() override {\n    CrashpadClient client;\n    StartAndUseBrokenHandler(&client);\n    // We don't need to fill this out as we're only checking that we're\n    // terminated with the correct failure code.\n    EXCEPTION_POINTERS info = {};\n    client.DumpAndCrash(&info);\n    exit(0);\n  }\n};\n\n#if defined(ADDRESS_SANITIZER)\n// https://crbug.com/845011\n#define MAYBE_HandlerLaunchFailureDumpAndCrash \\\n  DISABLED_HandlerLaunchFailureDumpAndCrash\n#else\n#define MAYBE_HandlerLaunchFailureDumpAndCrash HandlerLaunchFailureDumpAndCrash\n#endif\nTEST(CrashpadClient, MAYBE_HandlerLaunchFailureDumpAndCrash) {\n  WinMultiprocess::Run<HandlerLaunchFailureDumpAndCrash>();\n}\n\nclass HandlerLaunchFailureDumpWithoutCrash : public WinMultiprocess {\n public:\n  HandlerLaunchFailureDumpWithoutCrash() : WinMultiprocess() {}\n\n private:\n  void WinMultiprocessParent() override {\n    // DumpWithoutCrash() normally blocks indefinitely. There's no return value,\n    // but confirm that it exits cleanly because it'll return right away if the\n    // handler didn't start.\n    SetExpectedChildExitCode(0);\n  }\n\n  void WinMultiprocessChild() override {\n    CrashpadClient client;\n    StartAndUseBrokenHandler(&client);\n    // We don't need to fill this out as we're only checking that we're\n    // terminated with the correct failure code.\n    CONTEXT context = {};\n    client.DumpWithoutCrash(context);\n    exit(0);\n  }\n};\n\nTEST(CrashpadClient, HandlerLaunchFailureDumpWithoutCrash) {\n  WinMultiprocess::Run<HandlerLaunchFailureDumpWithoutCrash>();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_info.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_info.h\"\n\n#include <type_traits>\n\n#include \"base/numerics/safe_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/address_sanitizer.h\"\n#include \"util/misc/from_pointer_cast.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <mach-o/loader.h>\n#endif\n\nnamespace {\n\n// Don’t change this when simply adding fields. Readers will size-check the\n// structure and ignore fields they’re aware of when not present, as well as\n// fields they’re not aware of. Only change this when introducing an\n// incompatible layout, with the understanding that existing readers will not\n// understand new versions.\nconstexpr uint32_t kCrashpadInfoVersion = 1;\n\n// Creates a `UserDataMinidumpStreamListEntry` with the given fields, and\n// returns a pointer to it. The caller takes ownership of the returned object.\ncrashpad::internal::UserDataMinidumpStreamListEntry* CreateListEntry(\n    uint64_t next,\n    uint32_t stream_type,\n    const void* data,\n    size_t size) {\n  auto to_be_added = new crashpad::internal::UserDataMinidumpStreamListEntry();\n  to_be_added->next = next;\n  to_be_added->stream_type = stream_type;\n  to_be_added->base_address = crashpad::FromPointerCast<uint64_t>(data);\n  to_be_added->size = base::checked_cast<uint64_t>(size);\n  return to_be_added;\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nstatic_assert(std::is_standard_layout<CrashpadInfo>::value,\n              \"CrashpadInfo must be standard layout\");\n\n// This structure needs to be stored somewhere that is easy to find without\n// external information.\n//\n// It isn’t placed in an unnamed namespace: hopefully, this will catch attempts\n// to place multiple copies of this structure into the same module. If that’s\n// attempted, and the name of the symbol is the same in each translation unit,\n// it will result in a linker error, which is better than having multiple\n// structures show up.\n//\n// This may result in a static module initializer in debug-mode builds, but\n// because it’s POD, no code should need to run to initialize this under\n// release-mode optimization.\n\n#if BUILDFLAG(IS_POSIX)\n__attribute__((\n\n#if BUILDFLAG(IS_APPLE)\n    // Put the structure in a well-known section name where it can be easily\n    // found without having to consult the symbol table.\n    section(SEG_DATA \",crashpad_info\"),\n#endif\n\n#if defined(ADDRESS_SANITIZER)\n    // AddressSanitizer would add a trailing red zone of at least 32 bytes,\n    // which would be reflected in the size of the custom section. This confuses\n    // MachOImageReader::GetCrashpadInfo(), which finds that the section’s size\n    // disagrees with the structure’s size_ field. By specifying an alignment\n    // greater than the red zone size, the red zone will be suppressed.\n    aligned(64),\n#endif  // defined(ADDRESS_SANITIZER)\n\n    // There's no need to expose this as a public symbol from the symbol table.\n    // All accesses from the outside can locate the well-known section name.\n    visibility(\"hidden\"),\n\n    // The “used” attribute prevents the structure from being dead-stripped.\n    used))\n\n#elif BUILDFLAG(IS_WIN)\n\n// Put the struct in a section name CPADinfo where it can be found without the\n// symbol table.\n#pragma section(\"CPADinfo\", read, write)\n__declspec(allocate(\"CPADinfo\"))\n\n#else  // !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_WIN)\n#error Port\n#endif  // !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_WIN)\n\nCrashpadInfo g_crashpad_info;\n\nextern \"C\" int* CRASHPAD_NOTE_REFERENCE;\n\n// static\nCrashpadInfo* CrashpadInfo::GetCrashpadInfo() {\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \\\n    BUILDFLAG(IS_FUCHSIA)\n  // This otherwise-unused reference is used so that any module that\n  // references GetCrashpadInfo() will also include the note in the\n  // .note.crashpad.info section. That note in turn contains the address of\n  // g_crashpad_info. This allows the module reader to find the CrashpadInfo\n  // structure without requiring the use of the dynamic symbol table.\n  static volatile int* pointer_to_note_section = CRASHPAD_NOTE_REFERENCE;\n  (void)pointer_to_note_section;\n#endif\n  return &g_crashpad_info;\n}\n\nCrashpadInfo::CrashpadInfo()\n    : signature_(kSignature),\n      size_(sizeof(*this)),\n      version_(kCrashpadInfoVersion),\n      indirectly_referenced_memory_cap_(0),\n      padding_0_(0),\n      crashpad_handler_behavior_(TriState::kUnset),\n      system_crash_reporter_forwarding_(TriState::kUnset),\n      gather_indirectly_referenced_memory_(TriState::kUnset),\n      padding_1_(0),\n      extra_memory_ranges_(nullptr),\n      simple_annotations_(nullptr),\n      user_data_minidump_stream_head_(nullptr),\n      annotations_list_(nullptr)\n#if BUILDFLAG(IS_IOS)\n      ,\n      intermediate_dump_extra_memory_ranges_(nullptr)\n#endif\n{\n}\n\nUserDataMinidumpStreamHandle* CrashpadInfo::AddUserDataMinidumpStream(\n    uint32_t stream_type,\n    const void* data,\n    size_t size) {\n  user_data_minidump_stream_head_ = CreateListEntry(\n      crashpad::FromPointerCast<uint64_t>(user_data_minidump_stream_head_),\n      stream_type,\n      data,\n      size);\n  return user_data_minidump_stream_head_;\n}\n\nUserDataMinidumpStreamHandle* CrashpadInfo::UpdateUserDataMinidumpStream(\n    UserDataMinidumpStreamHandle* stream_to_update,\n    uint32_t stream_type,\n    const void* data,\n    size_t size) {\n  // Create a new stream that points to the node `stream_to_update` points to.\n  const auto new_stream =\n      CreateListEntry(stream_to_update->next, stream_type, data, size);\n\n  // If `stream_to_update` is head of the list, replace the head with\n  // `new_stream`.\n  if (stream_to_update == user_data_minidump_stream_head_) {\n    user_data_minidump_stream_head_ = new_stream;\n  } else {\n    // Otherwise, find the node before `stream_to_update`, and make it point to\n    // `new_stream` instead.\n    auto current = user_data_minidump_stream_head_;\n    while (current) {\n      auto next = reinterpret_cast<internal::UserDataMinidumpStreamListEntry*>(\n          current->next);\n      if (next == stream_to_update) {\n        current->next = FromPointerCast<uint64_t>(new_stream);\n        break;\n      }\n      current = next;\n    }\n    CHECK(current)\n        << \"Tried to update a UserDataMinidumpStream that doesn't exist\";\n  }\n\n  delete stream_to_update;\n  return new_stream;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/crashpad_info.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_CRASHPAD_INFO_H_\n#define CRASHPAD_CLIENT_CRASHPAD_INFO_H_\n\n#include <stdint.h>\n\n#include \"build/build_config.h\"\n#include \"client/annotation_list.h\"\n#include \"client/simple_address_range_bag.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"util/misc/tri_state.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif  // BUILDFLAG(IS_WIN)\n\nnamespace crashpad {\n\nnamespace internal {\n\n#if BUILDFLAG(IS_IOS)\nclass InProcessIntermediateDumpHandler;\n#endif\n\n//! \\brief A linked list of blocks representing custom streams in the minidump,\n//!     with addresses (and size) stored as uint64_t to simplify reading from\n//!     the handler process.\nstruct UserDataMinidumpStreamListEntry {\n  //! \\brief The address of the next entry in the linked list.\n  uint64_t next;\n\n  //! \\brief The base address of the memory block in the target process' address\n  //!     space that represents the user data stream.\n  uint64_t base_address;\n\n  //! \\brief The size of memory block in the target process' address space that\n  //!     represents the user data stream.\n  uint64_t size;\n\n  //! \\brief The stream type identifier.\n  uint32_t stream_type;\n};\n\n}  // namespace internal\n\nusing UserDataMinidumpStreamHandle = internal::UserDataMinidumpStreamListEntry;\n\n//! \\brief A structure that can be used by a Crashpad-enabled program to\n//!     provide information to the Crashpad crash handler.\n//!\n//! It is possible for one CrashpadInfo structure to appear in each loaded code\n//! module in a process, but from the perspective of the user of the client\n//! interface, there is only one global CrashpadInfo structure, located in the\n//! module that contains the client interface code.\nstruct CrashpadInfo {\n public:\n  //! \\brief Returns the global CrashpadInfo structure.\n  static CrashpadInfo* GetCrashpadInfo();\n\n  CrashpadInfo();\n\n  CrashpadInfo(const CrashpadInfo&) = delete;\n  CrashpadInfo& operator=(const CrashpadInfo&) = delete;\n\n  //! \\brief Sets the bag of extra memory ranges to be included in the snapshot.\n  //!\n  //! Extra memory ranges may exist in \\a address_range_bag at the time that\n  //! this method is called, or they may be added, removed, or modified in \\a\n  //! address_range_bag after this method is called.\n  //!\n  //! TODO(scottmg) This is currently only supported on Windows and iOS.\n  //!\n  //! \\param[in] address_range_bag A bag of address ranges. The CrashpadInfo\n  //!     object does not take ownership of the SimpleAddressRangeBag object.\n  //!     It is the caller’s responsibility to ensure that this pointer remains\n  //!     valid while it is in effect for a CrashpadInfo object.\n  //!\n  //! \\sa extra_memory_ranges()\n  void set_extra_memory_ranges(SimpleAddressRangeBag* address_range_bag) {\n    extra_memory_ranges_ = address_range_bag;\n  }\n\n  //! \\return The simple extra memory ranges SimpleAddressRangeBag object.\n  //!\n  //! \\sa set_extra_memory_ranges()\n  SimpleAddressRangeBag* extra_memory_ranges() const {\n    return extra_memory_ranges_;\n  }\n\n#if BUILDFLAG(IS_IOS)\n  //! \\brief Sets the bag of extra memory ranges to be included in the iOS\n  //! intermediate dump. This memory is not included in the minidump.\n  //!\n  //! Extra memory ranges may exist in \\a address_range_bag at the time that\n  //! this method is called, or they may be added, removed, or modified in \\a\n  //! address_range_bag after this method is called.\n  //!\n  //! This is only supported on iOS.\n  //!\n  //! \\param[in] address_range_bag A bag of address ranges. The CrashpadInfo\n  //!     object does not take ownership of the SimpleAddressRangeBag object.\n  //!     It is the caller’s responsibility to ensure that this pointer remains\n  //!     valid while it is in effect for a CrashpadInfo object.\n  //!\n  //! \\sa extra_memory_ranges()\n  void set_intermediate_dump_extra_memory_ranges(\n      SimpleAddressRangeBag* address_range_bag) {\n    intermediate_dump_extra_memory_ranges_ = address_range_bag;\n  }\n\n  //! \\return The simple extra memory ranges SimpleAddressRangeBag object.\n  //!\n  //! \\sa set_extra_memory_ranges()\n  SimpleAddressRangeBag* intermediate_dump_extra_memory_ranges() const {\n    return intermediate_dump_extra_memory_ranges_;\n  }\n#endif\n\n  //! \\brief Sets the simple annotations dictionary.\n  //!\n  //! Simple annotations set on a CrashpadInfo structure are interpreted by\n  //! Crashpad as module-level annotations.\n  //!\n  //! Annotations may exist in \\a simple_annotations at the time that this\n  //! method is called, or they may be added, removed, or modified in \\a\n  //! simple_annotations after this method is called.\n  //!\n  //! \\param[in] simple_annotations A dictionary that maps string keys to string\n  //!     values. The CrashpadInfo object does not take ownership of the\n  //!     SimpleStringDictionary object. It is the caller’s responsibility to\n  //!     ensure that this pointer remains valid while it is in effect for a\n  //!     CrashpadInfo object.\n  //!\n  //! \\sa simple_annotations()\n  void set_simple_annotations(SimpleStringDictionary* simple_annotations) {\n    simple_annotations_ = simple_annotations;\n  }\n\n  //! \\return The simple annotations dictionary.\n  //!\n  //! \\sa set_simple_annotations()\n  SimpleStringDictionary* simple_annotations() const {\n    return simple_annotations_;\n  }\n\n  //! \\brief Sets the annotations list.\n  //!\n  //! Unlike the \\a simple_annotations structure, the \\a annotations can\n  //! typed data and it is not limited to a dictionary form. Annotations are\n  //! interpreted by Crashpad as module-level annotations.\n  //!\n  //! Annotations may exist in \\a list at the time that this method is called,\n  //! or they may be added, removed, or modified in \\a list after this method is\n  //! called.\n  //!\n  //! \\param[in] list A list of set Annotation objects that maintain arbitrary,\n  //!     typed key-value state. The CrashpadInfo object does not take ownership\n  //!     of the AnnotationsList object. It is the caller’s responsibility to\n  //!     ensure that this pointer remains valid while it is in effect for a\n  //!     CrashpadInfo object.\n  //!\n  //! \\sa annotations_list()\n  //! \\sa AnnotationList::Register()\n  void set_annotations_list(AnnotationList* list) { annotations_list_ = list; }\n\n  //! \\return The annotations list.\n  //!\n  //! \\sa set_annotations_list()\n  //! \\sa AnnotationList::Get()\n  //! \\sa AnnotationList::Register()\n  AnnotationList* annotations_list() const { return annotations_list_; }\n\n  //! \\brief Enables or disables Crashpad handler processing.\n  //!\n  //! When handling an exception, the Crashpad handler will scan all modules in\n  //! a process. The first one that has a CrashpadInfo structure populated with\n  //! a value other than TriState::kUnset for this field will dictate whether\n  //! the handler is functional or not. If all modules with a CrashpadInfo\n  //! structure specify TriState::kUnset, the handler will be enabled. If\n  //! disabled, the Crashpad handler will still run and receive exceptions, but\n  //! will not take any action on an exception on its own behalf, except for the\n  //! action necessary to determine that it has been disabled.\n  //!\n  //! The Crashpad handler should not normally be disabled. More commonly, it is\n  //! appropriate to disable crash report upload by calling\n  //! Settings::SetUploadsEnabled().\n  void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) {\n    crashpad_handler_behavior_ = crashpad_handler_behavior;\n  }\n\n  //! \\brief Enables or disables Crashpad forwarding of exceptions to the\n  //!     system’s crash reporter.\n  //!\n  //! When handling an exception, the Crashpad handler will scan all modules in\n  //! a process. The first one that has a CrashpadInfo structure populated with\n  //! a value other than TriState::kUnset for this field will dictate whether\n  //! the exception is forwarded to the system’s crash reporter. If all modules\n  //! with a CrashpadInfo structure specify TriState::kUnset, forwarding will be\n  //! enabled. Unless disabled, forwarding may still occur if the Crashpad\n  //! handler is disabled by SetCrashpadHandlerState(). Even when forwarding is\n  //! enabled, the Crashpad handler may choose not to forward all exceptions to\n  //! the system’s crash reporter in cases where it has reason to believe that\n  //! the system’s crash reporter would not normally have handled the exception\n  //! in Crashpad’s absence.\n  void set_system_crash_reporter_forwarding(\n      TriState system_crash_reporter_forwarding) {\n    system_crash_reporter_forwarding_ = system_crash_reporter_forwarding;\n  }\n\n  //! \\brief Enables or disables Crashpad capturing indirectly referenced memory\n  //!     in the minidump.\n  //!\n  //! When handling an exception, the Crashpad handler will scan all modules in\n  //! a process. The first one that has a CrashpadInfo structure populated with\n  //! a value other than TriState::kUnset for this field will dictate whether\n  //! the extra memory is captured.\n  //!\n  //! This causes Crashpad to include pages of data referenced by locals or\n  //! other stack memory. Turning this on can increase the size of the minidump\n  //! significantly.\n  //!\n  //! \\param[in] gather_indirectly_referenced_memory Whether extra memory should\n  //!     be gathered.\n  //! \\param[in] limit The amount of memory in bytes after which no more\n  //!     indirectly gathered memory should be captured. This value is only used\n  //!     when \\a gather_indirectly_referenced_memory is TriState::kEnabled.\n  void set_gather_indirectly_referenced_memory(\n      TriState gather_indirectly_referenced_memory,\n      uint32_t limit) {\n    gather_indirectly_referenced_memory_ = gather_indirectly_referenced_memory;\n    indirectly_referenced_memory_cap_ = limit;\n  }\n\n  //! \\brief Adds a custom stream to the minidump.\n  //!\n  //! The memory block referenced by \\a data and \\a size will added to the\n  //! minidump as separate stream with type \\a stream_type. The memory referred\n  //! to by \\a data and \\a size is owned by the caller and must remain valid\n  //! while it is in effect for the CrashpadInfo object.\n  //!\n  //! Note that streams will appear in the minidump in the reverse order to\n  //! which they are added.\n  //!\n  //! TODO(scottmg) This is currently not supported on Mac.\n  //!\n  //! \\param[in] stream_type The stream type identifier to use. This should be\n  //!     normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream`\n  //!     which is `0xffff`.\n  //! \\param[in] data The base pointer of the stream data.\n  //! \\param[in] size The size of the stream data.\n  //! \\return A handle to the added stream, for use in calling\n  //!     UpdateUserDataMinidumpStream() if needed.\n  UserDataMinidumpStreamHandle* AddUserDataMinidumpStream(uint32_t stream_type,\n                                                          const void* data,\n                                                          size_t size);\n\n  //! \\brief Replaces the given stream with an updated stream.\n  //!\n  //! Creates a new memory block referencing the given \\a data and \\a size with\n  //! type \\a stream_type. The memory referred to be \\a data and \\a size is\n  //! owned by the caller and must remain valid while it is in effect for the\n  //! CrashpadInfo object.\n  //!\n  //! Frees \\a stream_to_update and returns a new handle to the updated stream.\n  //!\n  //! \\param[in] stream_to_update A handle to the stream to be updated, received\n  //!     from either AddUserDataMinidumpStream() or previous calls to this\n  //!     function.\n  //! \\param[in] stream_type The stream type identifier to use. This should be\n  //!     normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream`\n  //!     which is `0xffff`.\n  //! \\param[in] data The base pointer of the stream data.\n  //! \\param[in] size The size of the stream data.\n  //! \\return A handle to the new memory block that references the updated data,\n  //!     for use in calling this method again if needed.\n  UserDataMinidumpStreamHandle* UpdateUserDataMinidumpStream(\n      UserDataMinidumpStreamHandle* stream_to_update,\n      uint32_t stream_type,\n      const void* data,\n      size_t size);\n\n  internal::UserDataMinidumpStreamListEntry*\n  GetUserDataMinidumpStreamHeadForTesting() {\n    return user_data_minidump_stream_head_;\n  }\n\n  enum : uint32_t {\n    kSignature = 'CPad',\n  };\n\n protected:\n#if BUILDFLAG(IS_IOS)\n  friend class internal::InProcessIntermediateDumpHandler;\n#endif\n\n  uint32_t signature() const { return signature_; }\n  uint32_t version() const { return version_; }\n  uint32_t size() const { return size_; }\n\n private:\n  // The compiler won’t necessarily see anyone using these fields, but it\n  // shouldn’t warn about that. These fields aren’t intended for use by the\n  // process they’re found in, they’re supposed to be read by the crash\n  // reporting process.\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunused-private-field\"\n#endif\n\n  // Fields present in version 1, subject to a check of the size_ field:\n  uint32_t signature_;  // kSignature\n  uint32_t size_;  // The size of the entire CrashpadInfo structure.\n  uint32_t version_;  // kCrashpadInfoVersion\n  uint32_t indirectly_referenced_memory_cap_;\n  uint32_t padding_0_;\n  TriState crashpad_handler_behavior_;\n  TriState system_crash_reporter_forwarding_;\n  TriState gather_indirectly_referenced_memory_;\n  uint8_t padding_1_;\n  SimpleAddressRangeBag* extra_memory_ranges_;  // weak\n  SimpleStringDictionary* simple_annotations_;  // weak\n  internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_;\n  AnnotationList* annotations_list_;  // weak\n#if BUILDFLAG(IS_IOS)\n  SimpleAddressRangeBag* intermediate_dump_extra_memory_ranges_;  // weak\n#endif\n\n  // It’s generally safe to add new fields without changing\n  // kCrashpadInfoVersion, because readers should check size_ and ignore fields\n  // that aren’t present, as well as unknown fields.\n  //\n  // Adding fields? Consider snapshot/crashpad_info_size_test_module.cc too.\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_CRASHPAD_INFO_H_\n"
  },
  {
    "path": "client/crashpad_info_note.S",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This note section is used on ELF platforms to give ElfImageReader a method\n// of finding the instance of CrashpadInfo g_crashpad_info without requiring\n// that symbol to be in the dynamic symbol table.\n\n#include \"util/misc/elf_note_types.h\"\n#include \"util/misc/arm64_pac_bti.S\"\n\n// namespace crashpad {\n// CrashpadInfo g_crashpad_info;\n// }  // namespace crashpad\n#define CRASHPAD_INFO_SYMBOL _ZN8crashpad15g_crashpad_infoE\n\n#define NOTE_ALIGN 4\n\n  // This section must be \"a\"llocated so that it appears in the final binary at\n  // runtime. The reference to CRASHPAD_INFO_SYMBOL uses an offset relative to\n  // this note to avoid making this note writable, which triggers a bug in GNU\n  // ld, or adding text relocations which require the target system to allow\n  // making text segments writable. https://crbug.com/crashpad/260.\n  .section .note.crashpad.info,\"a\",%note\n  .balign NOTE_ALIGN\nCRASHPAD_NOTE:\n  .long name_end - name  // namesz\n  .long desc_end - desc  // descsz\n  .long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO  // type\nname:\n  .asciz CRASHPAD_ELF_NOTE_NAME\nname_end:\n  .balign NOTE_ALIGN\ndesc:\n#if defined(__LP64__)\n  .quad CRASHPAD_INFO_SYMBOL - desc\n#else\n  .long CRASHPAD_INFO_SYMBOL - desc\n#endif  // __LP64__\ndesc_end:\n  .size CRASHPAD_NOTE, .-CRASHPAD_NOTE\n\n  // CRASHPAD_NOTE can't be referenced directly by GetCrashpadInfo() because the\n  // relocation used to make the reference may require that the address be\n  // 8-byte aligned and notes must have 4-byte alignment.\n  .section .rodata,\"a\",%progbits\n  .balign 8\n  # .globl indicates that it's available to link against other .o files. .hidden\n  # indicates that it will not appear in the executable's symbol table.\n  .globl CRASHPAD_NOTE_REFERENCE\n  .hidden CRASHPAD_NOTE_REFERENCE\n  .type CRASHPAD_NOTE_REFERENCE, %object\nCRASHPAD_NOTE_REFERENCE:\n  // The value of this quad isn't important. It exists to reference\n  // CRASHPAD_NOTE, causing the linker to include the note into the binary\n  // linking Crashpad. The subtraction from |name| is a convenience to allow the\n  // value to be computed statically.\n  .quad name - CRASHPAD_NOTE\n"
  },
  {
    "path": "client/crashpad_info_test.cc",
    "content": "// Copyright 2024 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_info.h\"\n\n#include <string>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr uint32_t kTestStreamType = 0x33333;\n\nclass CrashpadInfoTest : public testing::Test {\n protected:\n  CrashpadInfo& crashpad_info() { return crashpad_info_; }\n\n  // Returns the current head of the list of streams in `crashpad_info_`. Note\n  // that the returned pointer is invalidated if a stream is added or updated.\n  internal::UserDataMinidumpStreamListEntry* GetCurrentHead() {\n    return crashpad_info().GetUserDataMinidumpStreamHeadForTesting();\n  }\n\n  // Returns a pointer to the next node in the list after the given `node`.\n  internal::UserDataMinidumpStreamListEntry* GetNext(\n      internal::UserDataMinidumpStreamListEntry* node) {\n    return reinterpret_cast<internal::UserDataMinidumpStreamListEntry*>(\n        node->next);\n  }\n\n  internal::UserDataMinidumpStreamListEntry* initial_head() {\n    return initial_head_;\n  }\n\n  internal::UserDataMinidumpStreamListEntry* initial_tail() {\n    return initial_tail_;\n  }\n\n private:\n  void SetUp() override {\n    ASSERT_EQ(nullptr, GetCurrentHead());\n\n    // Create a simple test list with the structure\n    // `initial_head_` -> `initial_tail_`.\n    initial_tail_ = AddStream(0x11111, kInitialTailData);\n    initial_head_ = AddStream(0x22222, kInitialHeadData);\n\n    // Validate the list's contents.\n    auto current = GetCurrentHead();\n    ASSERT_EQ(initial_head_, current);\n    ASSERT_EQ(kInitialHeadData, reinterpret_cast<char*>(current->base_address));\n    current = GetNext(current);\n    ASSERT_EQ(initial_tail_, current);\n    ASSERT_EQ(nullptr, GetNext(current));\n  }\n\n  void TearDown() override {\n    // Free the list. The list lives until process exit in production, but must\n    // be freed in tests as multiple tests run in the same process.\n    auto current = GetCurrentHead();\n    while (current) {\n      auto next = GetNext(current);\n      delete current;\n      current = next;\n    }\n  }\n\n  internal::UserDataMinidumpStreamListEntry* AddStream(uint32_t stream_type,\n                                                       const char* data) {\n    return reinterpret_cast<internal::UserDataMinidumpStreamListEntry*>(\n        crashpad_info().AddUserDataMinidumpStream(\n            stream_type, data, strlen(data)));\n  }\n\n  CrashpadInfo crashpad_info_;\n\n  static constexpr char kInitialHeadData[] = \"head\";\n  static constexpr char kInitialTailData[] = \"tail\";\n\n  internal::UserDataMinidumpStreamListEntry* initial_head_ = nullptr;\n  internal::UserDataMinidumpStreamListEntry* initial_tail_ = nullptr;\n};\n\n// Tests that updating the head of the list updates the head pointer, the new\n// head contains the updated data, and the updated node points to the next node.\nTEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamHead) {\n  const std::string new_data = \"this is a new string\";\n  const auto new_entry = crashpad_info().UpdateUserDataMinidumpStream(\n      initial_head(), kTestStreamType, new_data.data(), new_data.size());\n  const auto head = GetCurrentHead();\n  EXPECT_EQ(new_entry, head);\n  EXPECT_EQ(new_data.data(), reinterpret_cast<char*>(head->base_address));\n  EXPECT_EQ(new_data.size(), head->size);\n  EXPECT_EQ(kTestStreamType, head->stream_type);\n  EXPECT_EQ(initial_tail(), GetNext(head));\n}\n\n// Tests that updating the tail of the list results in a tail pointing to\n// nullptr, and that the node before the updated node points to it.\nTEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamTail) {\n  const std::string new_data = \"new\";\n  const auto new_entry = crashpad_info().UpdateUserDataMinidumpStream(\n      initial_tail(), kTestStreamType, new_data.data(), new_data.size());\n  const auto tail = GetNext(GetCurrentHead());\n  EXPECT_EQ(new_entry, tail);\n  EXPECT_EQ(nullptr, GetNext(tail));\n}\n\n// Tests that the handle returned from updating an entry is usable for updating\n// the entry again.\nTEST_F(CrashpadInfoTest, UpdateUserDataMinidumpStreamMultipleTimes) {\n  // Update the entry at the head; the updated entry should become the new head.\n  const std::string new_data = \"new\";\n  const auto new_entry_1 = crashpad_info().UpdateUserDataMinidumpStream(\n      initial_head(), kTestStreamType, new_data.data(), new_data.size());\n  EXPECT_EQ(new_entry_1, GetCurrentHead());\n\n  // Update the updated entry again; another new entry should replace it as\n  // head.\n  const auto new_entry_2 = crashpad_info().UpdateUserDataMinidumpStream(\n      new_entry_1, kTestStreamType, new_data.data(), new_data.size());\n  EXPECT_NE(new_entry_1, new_entry_2);\n  EXPECT_EQ(new_entry_2, GetCurrentHead());\n  EXPECT_EQ(initial_tail(), GetNext(GetCurrentHead()));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/ios_handler/exception_processor.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_\n#define CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_\n\n#include \"base/files/file_path.h\"\n#include \"util/misc/capture_context.h\"\n\nnamespace crashpad {\n\n//! \\brief An interface for notifying the CrashpadClient of NSExceptions.\nclass ObjcExceptionDelegate {\n public:\n  //! \\brief The exception processor detected an exception as it was thrown and\n  //!     captured the cpu context.\n  //!\n  //! \\param context The cpu context of the thread throwing an exception.\n  virtual void HandleUncaughtNSExceptionWithContext(\n      NativeCPUContext* context) = 0;\n\n  //! \\brief The exception processor did not detect the exception as it was\n  //!     thrown, and instead caught the exception via the\n  //!     NSUncaughtExceptionHandler.\n  //!\n  //! \\param frames An array of call stack frame addresses.\n  //! \\param num_frames The number of frames in |frames|.\n  virtual void HandleUncaughtNSException(const uint64_t* frames,\n                                         const size_t num_frames) = 0;\n\n  //! \\brief Generate an intermediate dump from an NSException caught with its\n  //!     associated CPU context. Because the method for intercepting\n  //!     exceptions is imperfect, write the the intermediate dump to a\n  //!     temporary location specified by \\a path. If the NSException matches\n  //!     the one used in the UncaughtExceptionHandler, call\n  //!     MoveIntermediateDumpAtPathToPending to move to the proper Crashpad\n  //!     database pending location.\n  //!\n  //! \\param[in] context The cpu context of the thread throwing an exception.\n  //! \\param[in] path Path to write the intermediate dump.\n  virtual void HandleUncaughtNSExceptionWithContextAtPath(\n      NativeCPUContext* context,\n      const base::FilePath& path) = 0;\n\n  //! \\brief Moves an intermediate dump to the pending directory. This is meant\n  //!     to be used by the UncaughtExceptionHandler, when the NSException\n  //!     caught by the preprocessor matches the UncaughtExceptionHandler.\n  //!\n  //! \\param[in] path Path to the specific intermediate dump.\n  virtual bool MoveIntermediateDumpAtPathToPending(\n      const base::FilePath& path) = 0;\n\n protected:\n  ~ObjcExceptionDelegate() {}\n};\n\n//! \\brief Installs the Objective-C exception preprocessor.\n//!\n//! When code raises an Objective-C exception, unwind the stack looking for\n//! any exception handlers. If an exception handler is encountered, test to\n//! see if it is a function known to be a catch-and-rethrow 'sinkhole' exception\n//! handler. Various routines in UIKit do this, and they obscure the\n//! crashing stack, since the original throw location is no longer present\n//! on the stack (just the re-throw) when Crashpad captures the crash\n//! report. In the case of sinkholes, trigger an immediate exception to\n//! capture the original stack.\n//!\n//! This should be installed at the same time the CrashpadClient installs the\n//! signal handler. It should only be installed once.\nvoid InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate);\n\n//! \\brief Uninstalls the Objective-C exception preprocessor. Expected to be\n//!     used by tests only.\nvoid UninstallObjcExceptionPreprocessor();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_\n"
  },
  {
    "path": "client/ios_handler/exception_processor.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// std::unexpected_handler is deprecated starting in C++11, and removed in\n// C++17.  But macOS versions we run on still ship it. This define makes\n// std::unexpected_handler reappear. If that define ever stops working,\n// we hopefully no longer run on macOS versions that still have it.\n// (...or we'll have to define it in this file instead of getting it from\n// <exception>). This define must before all includes.\n#define _LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS\n\n#include \"client/ios_handler/exception_processor.h\"\n\n#include <Availability.h>\n#import <Foundation/Foundation.h>\n#include <TargetConditionals.h>\n#include <cxxabi.h>\n#include <dlfcn.h>\n#include <libunwind.h>\n#include <mach-o/loader.h>\n#include <objc/message.h>\n#include <objc/objc-exception.h>\n#include <objc/objc.h>\n#include <objc/runtime.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unwind.h>\n\n#include <atomic>\n#include <exception>\n#include <type_traits>\n#include <typeinfo>\n#include <vector>\n\n#include \"base/format_macros.h\"\n#include \"base/logging.h\"\n#include \"base/memory/free_deleter.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"client/annotation.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// From 10.15.0 objc4-779.1/runtime/objc-exception.mm.\nstruct objc_typeinfo {\n  const void* const* vtable;\n  const char* name;\n  Class cls_unremapped;\n};\nstruct objc_exception {\n  id __unsafe_unretained obj;\n  objc_typeinfo tinfo;\n};\n\n// From 10.15.0 objc4-779.1/runtime/objc-abi.h.\nextern \"C\" const void* const objc_ehtype_vtable[];\n\n// https://github.com/llvm/llvm-project/blob/09dc884eb2e4/libcxxabi/src/cxa_exception.h\nstatic const uint64_t kOurExceptionClass = 0x434c4e47432b2b00;\nstruct __cxa_exception {\n#if defined(ARCH_CPU_64_BITS)\n  void* reserve;\n  size_t referenceCount;\n#endif\n  std::type_info* exceptionType;\n  void (*exceptionDestructor)(void*);\n  std::unexpected_handler unexpectedHandler;\n  std::terminate_handler terminateHandler;\n  __cxa_exception* nextException;\n  int handlerCount;\n  int handlerSwitchValue;\n  const unsigned char* actionRecord;\n  const unsigned char* languageSpecificData;\n  void* catchTemp;\n  void* adjustedPtr;\n#if !defined(ARCH_CPU_64_BITS)\n  size_t referenceCount;\n#endif\n  _Unwind_Exception unwindHeader;\n};\n\nint LoggingUnwStep(unw_cursor_t* cursor) {\n  int rv = unw_step(cursor);\n  if (rv < 0) {\n    LOG(ERROR) << \"unw_step: \" << rv;\n  }\n  return rv;\n}\n\nstd::string FormatStackTrace(const std::vector<uint64_t>& addresses,\n                             size_t max_length) {\n  std::string stack_string;\n  for (uint64_t address : addresses) {\n    std::string address_string = base::StringPrintf(\"0x%\" PRIx64, address);\n    if (stack_string.size() + address_string.size() > max_length)\n      break;\n    stack_string += address_string + \" \";\n  }\n\n  if (!stack_string.empty() && stack_string.back() == ' ') {\n    stack_string.resize(stack_string.size() - 1);\n  }\n\n  return stack_string;\n}\n\nstd::string GetTraceString() {\n  std::vector<uint64_t> addresses;\n  unw_context_t context;\n  unw_getcontext(&context);\n  unw_cursor_t cursor;\n  unw_init_local(&cursor, &context);\n  while (LoggingUnwStep(&cursor) > 0) {\n    unw_word_t ip = 0;\n    unw_get_reg(&cursor, UNW_REG_IP, &ip);\n    addresses.push_back(ip);\n  }\n  return FormatStackTrace(addresses, 1024);\n}\n\nstatic void SetNSExceptionAnnotations(NSException* exception,\n                                      std::string& name,\n                                      std::string& reason) {\n  @try {\n    name = base::SysNSStringToUTF8(exception.name);\n    static StringAnnotation<256> nameKey(\"exceptionName\");\n    nameKey.Set(name);\n  } @catch (id name_exception) {\n    LOG(ERROR) << \"Unable to read uncaught Objective-C exception name.\";\n  }\n\n  @try {\n    reason = base::SysNSStringToUTF8(exception.reason);\n    static StringAnnotation<1024> reasonKey(\"exceptionReason\");\n    reasonKey.Set(reason);\n  } @catch (id reason_exception) {\n    LOG(ERROR) << \"Unable to read uncaught Objective-C exception reason.\";\n  }\n\n  @try {\n    if (exception.userInfo) {\n      static StringAnnotation<1024> userInfoKey(\"exceptionUserInfo\");\n      userInfoKey.Set(base::SysNSStringToUTF8(\n          [NSString stringWithFormat:@\"%@\", exception.userInfo]));\n    }\n  } @catch (id user_info_exception) {\n    LOG(ERROR) << \"Unable to read uncaught Objective-C exception user info.\";\n  }\n}\n\n//! \\brief Helper class to own the complex types used by the Objective-C\n//!     exception preprocessor.\nclass ExceptionPreprocessorState {\n public:\n  ExceptionPreprocessorState(const ExceptionPreprocessorState&) = delete;\n  ExceptionPreprocessorState& operator=(const ExceptionPreprocessorState&) =\n      delete;\n\n  static ExceptionPreprocessorState* Get() {\n    static ExceptionPreprocessorState* instance = []() {\n      return new ExceptionPreprocessorState();\n    }();\n    return instance;\n  }\n\n  // Writes an intermediate dumps to a temporary location to be used by the\n  // final UncaughtExceptionHandler and notifies the preprocessor chain.\n  id HandleUncaughtException(NativeCPUContext* cpu_context, id exception) {\n    // If this isn't the first time the preprocessor has detected an uncaught\n    // NSException, note this in the second intermediate dump.\n    objc_exception_preprocessor next_preprocessor = next_preprocessor_;\n    static bool handled_first_exception;\n    if (handled_first_exception) {\n      static StringAnnotation<5> name_key(\"MultipleHandledUncaughtNSException\");\n      name_key.Set(\"true\");\n\n      // Unregister so we stop getting in the way of the exception processor if\n      // we aren't correctly identifying sinkholes. The final uncaught exception\n      // handler is still active.\n      objc_setExceptionPreprocessor(next_preprocessor_);\n      next_preprocessor_ = nullptr;\n    }\n    handled_first_exception = true;\n\n    // Use tmp/ for this intermediate dump path. Normally these dumps are\n    // written to the \"pending-serialized-ios-dump\" folder and are eligable for\n    // the next pass to convert pending intermediate dumps to minidump files.\n    // Since this intermediate dump isn't eligable until the uncaught handler,\n    // use tmp/.\n    base::FilePath path(base::SysNSStringToUTF8([NSTemporaryDirectory()\n        stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]]));\n    exception_delegate_->HandleUncaughtNSExceptionWithContextAtPath(cpu_context,\n                                                                    path);\n    last_handled_intermediate_dump_ = path;\n\n    return next_preprocessor ? next_preprocessor(exception) : exception;\n  }\n\n  // If the PreprocessException already captured this exception via\n  // HANDLE_UNCAUGHT_NSEXCEPTION. Move last_handled_intermediate_dump_ to\n  // the pending intermediate dump directory and return true. Otherwise the\n  // preprocessor didn't catch anything, so pass the frames or just the context\n  // to the exception_delegate.\n  void FinalizeUncaughtNSException(id exception) {\n    if (last_exception() == (__bridge void*)exception &&\n        !last_handled_intermediate_dump_.empty() &&\n        exception_delegate_->MoveIntermediateDumpAtPathToPending(\n            last_handled_intermediate_dump_)) {\n      last_handled_intermediate_dump_ = base::FilePath();\n      return;\n    }\n\n    std::string name, reason;\n    NSArray<NSNumber*>* address_array = nil;\n    if ([exception isKindOfClass:[NSException class]]) {\n      SetNSExceptionAnnotations(exception, name, reason);\n      address_array = [exception callStackReturnAddresses];\n    }\n\n    if ([address_array count] > 0) {\n      static StringAnnotation<256> name_key(\"UncaughtNSException\");\n      name_key.Set(\"true\");\n      std::vector<uint64_t> addresses;\n      for (NSNumber* address in address_array)\n        addresses.push_back([address unsignedLongLongValue]);\n      exception_delegate_->HandleUncaughtNSException(&addresses[0],\n                                                     addresses.size());\n    } else {\n      LOG(WARNING) << \"Uncaught Objective-C exception name: \" << name\n                   << \" reason: \" << reason << \" with no \"\n                   << \" -callStackReturnAddresses.\";\n      NativeCPUContext cpu_context;\n      CaptureContext(&cpu_context);\n      exception_delegate_->HandleUncaughtNSExceptionWithContext(&cpu_context);\n    }\n  }\n\n  id MaybeCallNextPreprocessor(id exception) {\n    return next_preprocessor_ ? next_preprocessor_(exception) : exception;\n  }\n\n  // Register the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler.\n  void Install(ObjcExceptionDelegate* delegate);\n\n  // Restore the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler.\n  void Uninstall();\n\n  void* last_exception() { return last_exception_; }\n  void set_last_exception(void* exception) { last_exception_ = exception; }\n\n private:\n  ExceptionPreprocessorState() = default;\n  ~ExceptionPreprocessorState() = default;\n\n  // Location of the intermediate dump generated after an exception triggered\n  // HANDLE_UNCAUGHT_NSEXCEPTION.\n  base::FilePath last_handled_intermediate_dump_;\n\n  // Recorded last NSException pointer in case the exception is caught and\n  // thrown again (without using objc_exception_rethrow) as an\n  // unsafe_unretained reference. Stored as a void* as the only safe\n  // operation is pointer comparison.\n  std::atomic<void*> last_exception_ = nil;\n\n  ObjcExceptionDelegate* exception_delegate_ = nullptr;\n  objc_exception_preprocessor next_preprocessor_ = nullptr;\n  NSUncaughtExceptionHandler* next_uncaught_exception_handler_ = nullptr;\n};\n\nstatic void ObjcUncaughtExceptionHandler(NSException* exception) {\n  ExceptionPreprocessorState::Get()->FinalizeUncaughtNSException(exception);\n}\n\n// This function is used to make it clear to the crash processor that an\n// uncaught NSException was recorded here.\nstatic __attribute__((noinline)) id HANDLE_UNCAUGHT_NSEXCEPTION(\n    id exception,\n    const char* sinkhole) {\n  std::string name, reason;\n  if ([exception isKindOfClass:[NSException class]]) {\n    SetNSExceptionAnnotations(exception, name, reason);\n  }\n  LOG(WARNING) << \"Handling Objective-C exception name: \" << name\n               << \" reason: \" << reason << \" with sinkhole: \" << sinkhole;\n  NativeCPUContext cpu_context{};\n  CaptureContext(&cpu_context);\n\n  ExceptionPreprocessorState* preprocessor_state =\n      ExceptionPreprocessorState::Get();\n  return preprocessor_state->HandleUncaughtException(&cpu_context, exception);\n}\n\n// Returns true if |path| equals |sinkhole| on device. Simulator paths prepend\n// much of Xcode's internal structure, so check that |path| ends with |sinkhole|\n// for simulator.\nbool ModulePathMatchesSinkhole(const char* path, const char* sinkhole) {\n#if TARGET_OS_SIMULATOR\n  size_t path_length = strlen(path);\n  size_t sinkhole_length = strlen(sinkhole);\n  if (sinkhole_length > path_length)\n    return false;\n  return strncmp(path + path_length - sinkhole_length,\n                 sinkhole,\n                 sinkhole_length) == 0;\n#else\n  return strcmp(path, sinkhole) == 0;\n#endif\n}\n\n//! \\brief Helper to release memory from calls to __cxa_allocate_exception.\nclass ScopedException {\n public:\n  explicit ScopedException(objc_exception* exception) : exception_(exception) {}\n\n  ScopedException(const ScopedException&) = delete;\n  ScopedException& operator=(const ScopedException&) = delete;\n\n  ~ScopedException() { __cxxabiv1::__cxa_free_exception(exception_); }\n\n private:\n  objc_exception* exception_;  // weak\n};\n\nid ObjcExceptionPreprocessor(id exception) {\n  // Some sinkholes don't use objc_exception_rethrow when they should, which\n  // would otherwise prevent the exception_preprocessor from getting called\n  // again. Because of this, track the most recently seen exception and\n  // ignore it.\n  ExceptionPreprocessorState* preprocessor_state =\n      ExceptionPreprocessorState::Get();\n  if (preprocessor_state->last_exception() == (__bridge void*)exception) {\n    return preprocessor_state->MaybeCallNextPreprocessor(exception);\n  }\n  preprocessor_state->set_last_exception((__bridge void*)exception);\n\n  static bool seen_first_exception;\n\n  static StringAnnotation<256> firstexception(\"firstexception\");\n  static StringAnnotation<256> lastexception(\"lastexception\");\n  static StringAnnotation<1024> firstexception_bt(\"firstexception_bt\");\n  static StringAnnotation<1024> lastexception_bt(\"lastexception_bt\");\n  auto* key = seen_first_exception ? &lastexception : &firstexception;\n  auto* bt_key = seen_first_exception ? &lastexception_bt : &firstexception_bt;\n\n  if ([exception isKindOfClass:[NSException class]]) {\n    NSString* value = [NSString\n        stringWithFormat:@\"%@ reason %@\", [exception name], [exception reason]];\n    key->Set(base::SysNSStringToUTF8(value));\n  } else {\n    key->Set(base::SysNSStringToUTF8([exception description]));\n  }\n\n  // This exception preprocessor runs prior to the one in libobjc, which sets\n  // the -[NSException callStackReturnAddresses].\n  bt_key->Set(GetTraceString());\n  seen_first_exception = true;\n\n  // Unwind the stack looking for any exception handlers. If an exception\n  // handler is encountered, test to see if it is a function known to catch-\n  // and-rethrow as a \"top-level\" exception handler. Various routines in\n  // Cocoa/UIKit do this, and it obscures the crashing stack, since the original\n  // throw location is no longer present on the stack (just the re-throw) when\n  // Crashpad captures the crash report.\n  unw_context_t context;\n  unw_getcontext(&context);\n\n  unw_cursor_t cursor;\n  unw_init_local(&cursor, &context);\n\n  static const void* this_base_address = []() -> const void* {\n    Dl_info dl_info;\n    if (!dladdr(reinterpret_cast<const void*>(&ObjcExceptionPreprocessor),\n                &dl_info)) {\n      LOG(ERROR) << \"dladdr: \" << dlerror();\n      return nullptr;\n    }\n    return dl_info.dli_fbase;\n  }();\n\n  // Generate an exception_header for the __personality_routine.\n  // From 10.15.0 objc4-779.1/runtime/objc-exception.mm objc_exception_throw.\n  objc_exception* exception_objc = reinterpret_cast<objc_exception*>(\n      __cxxabiv1::__cxa_allocate_exception(sizeof(objc_exception)));\n  ScopedException exception_objc_owner(exception_objc);\n  exception_objc->obj = exception;\n  exception_objc->tinfo.vtable = objc_ehtype_vtable + 2;\n  exception_objc->tinfo.name = object_getClassName(exception);\n  exception_objc->tinfo.cls_unremapped = object_getClass(exception);\n\n  // https://github.com/llvm/llvm-project/blob/c5d2746fbea7/libcxxabi/src/cxa_exception.cpp\n  // __cxa_throw\n  __cxa_exception* exception_header =\n      reinterpret_cast<__cxa_exception*>(exception_objc) - 1;\n  exception_header->unexpectedHandler = std::get_unexpected();\n  exception_header->terminateHandler = std::get_terminate();\n  exception_header->exceptionType =\n      reinterpret_cast<std::type_info*>(&exception_objc->tinfo);\n  exception_header->unwindHeader.exception_class = kOurExceptionClass;\n\n  bool handler_found = false;\n  while (LoggingUnwStep(&cursor) > 0) {\n    unw_proc_info_t frame_info;\n    if (unw_get_proc_info(&cursor, &frame_info) != UNW_ESUCCESS) {\n      continue;\n    }\n\n    if (frame_info.handler == 0) {\n      continue;\n    }\n\n    // Check to see if the handler is really an exception handler.\n#if defined(__IPHONE_14_5) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_5\n    using personality_routine = _Unwind_Personality_Fn;\n#else\n    using personality_routine = __personality_routine;\n#endif\n    personality_routine p =\n        reinterpret_cast<personality_routine>(frame_info.handler);\n\n    // From 10.15.0 libunwind-35.4/src/UnwindLevel1.c.\n    _Unwind_Reason_Code personalityResult = (*p)(\n        1,\n        _UA_SEARCH_PHASE,\n        exception_header->unwindHeader.exception_class,\n        reinterpret_cast<_Unwind_Exception*>(&exception_header->unwindHeader),\n        reinterpret_cast<_Unwind_Context*>(&cursor));\n    switch (personalityResult) {\n      case _URC_HANDLER_FOUND:\n        break;\n      case _URC_CONTINUE_UNWIND:\n        continue;\n      default:\n        break;\n    }\n\n    char proc_name[512];\n    unw_word_t offset;\n    if (unw_get_proc_name(&cursor, proc_name, sizeof(proc_name), &offset) !=\n        UNW_ESUCCESS) {\n      // The symbol has no name, so see if it belongs to the same image as\n      // this function.\n      Dl_info dl_info;\n      if (dladdr(reinterpret_cast<const void*>(frame_info.start_ip),\n                 &dl_info)) {\n        if (dl_info.dli_fbase == this_base_address) {\n          // This is a handler in our image, so allow it to run.\n          handler_found = true;\n          break;\n        }\n      }\n\n      // This handler does not belong to us, so continue the search.\n      continue;\n    }\n\n    // Check if the function is one that is known to obscure (by way of\n    // catch-and-rethrow) exception stack traces. If it is, sinkhole it\n    // by crashing here at the point of throw.\n    static constexpr const char* kExceptionSymbolNameSinkholes[] = {\n        // The two CF symbol names will also be captured by the CoreFoundation\n        // library path check below, but for completeness they are listed here,\n        // since they appear unredacted.\n        \"CFRunLoopRunSpecific\",\n        \"_CFXNotificationPost\",\n        \"__NSFireDelayedPerform\",\n        // If this exception is going to end up at EHFrame, record the uncaught\n        // exception instead.\n        \"_ZN4base3mac15CallWithEHFrameEU13block_pointerFvvE\",\n    };\n    for (const char* sinkhole : kExceptionSymbolNameSinkholes) {\n      if (strcmp(sinkhole, proc_name) == 0) {\n        return HANDLE_UNCAUGHT_NSEXCEPTION(exception, sinkhole);\n      }\n    }\n\n    // On iOS, function names are often reported as \"<redacted>\", although they\n    // do appear when attached to the debugger.  When this happens, use the path\n    // of the image to determine if the handler is an exception sinkhole.\n    static constexpr const char* kExceptionLibraryPathSinkholes[] = {\n        // Everything in this library is a sinkhole, specifically\n        // _dispatch_client_callout.  Both are needed here depending on whether\n        // the debugger is attached (introspection only appears when a simulator\n        // is attached to a debugger).\n        \"/usr/lib/system/introspection/libdispatch.dylib\",\n        \"/usr/lib/system/libdispatch.dylib\",\n\n        // __CFRunLoopDoTimers and __CFRunLoopRun are sinkholes. Consider also\n        // checking that a few frames up is CFRunLoopRunSpecific().\n        \"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation\",\n    };\n\n    Dl_info dl_info;\n    if (dladdr(reinterpret_cast<const void*>(frame_info.start_ip), &dl_info) !=\n        0) {\n      for (const char* sinkhole : kExceptionLibraryPathSinkholes) {\n        if (ModulePathMatchesSinkhole(dl_info.dli_fname, sinkhole)) {\n          return HANDLE_UNCAUGHT_NSEXCEPTION(exception, sinkhole);\n        }\n      }\n\n      // Another set of iOS redacted sinkholes appear in CoreAutoLayout.\n      // However, this is often called by client code, so it's unsafe to simply\n      // handle an uncaught nsexception here. Instead, skip the frame and\n      // continue searching for either a handler that belongs to us, or another\n      // sinkhole. See:\n      //    -[NSISEngine\n      //    performModifications:withUnsatisfiableConstraintsHandler:]:\n      //    -[NSISEngine withBehaviors:performModifications:]\n      //    +[NSLayoutConstraintParser\n      //    constraintsWithVisualFormat:options:metrics:views:]:\n      static constexpr const char* kCoreAutoLayoutSinkhole =\n          \"/System/Library/PrivateFrameworks/CoreAutoLayout.framework/\"\n          \"CoreAutoLayout\";\n      if (ModulePathMatchesSinkhole(dl_info.dli_fname,\n                                    kCoreAutoLayoutSinkhole)) {\n        continue;\n      }\n    }\n\n    // Some <redacted> sinkholes are harder to find. _UIGestureEnvironmentUpdate\n    // in UIKitCore is an example. UIKitCore can't be added to\n    // kExceptionLibraryPathSinkholes because it uses Objective-C exceptions\n    // internally and also has has non-sinkhole handlers. While all the\n    // calling methods in UIKit are marked <redacted> starting in iOS14, it's\n    // currently true that all callers to _UIGestureEnvironmentUpdate are within\n    // UIWindow sendEvent -> UIGestureEnvironment.  That means a very hacky way\n    // to detect this is to check if the calling (2x) method IMP is within the\n    // range of all UIWindow methods.\n    static constexpr const char kUIKitCorePath[] =\n        \"/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore\";\n    if (ModulePathMatchesSinkhole(dl_info.dli_fname, kUIKitCorePath)) {\n      unw_proc_info_t caller_frame_info;\n      if (LoggingUnwStep(&cursor) > 0 &&\n          unw_get_proc_info(&cursor, &caller_frame_info) == UNW_ESUCCESS &&\n          LoggingUnwStep(&cursor) > 0 &&\n          unw_get_proc_info(&cursor, &caller_frame_info) == UNW_ESUCCESS) {\n        auto uiwindowimp_lambda = [](IMP* max) {\n          IMP min = *max = nullptr;\n          unsigned int method_count = 0;\n          std::unique_ptr<Method[], base::FreeDeleter> method_list(\n              class_copyMethodList(NSClassFromString(@\"UIWindow\"),\n                                   &method_count));\n          if (method_count > 0) {\n            min = *max = method_getImplementation(method_list[0]);\n            for (unsigned int method_index = 1; method_index < method_count;\n                 method_index++) {\n              IMP method_imp =\n                  method_getImplementation(method_list[method_index]);\n              *max = std::max(method_imp, *max);\n              min = std::min(method_imp, min);\n            }\n          }\n          return min;\n        };\n\n        static IMP uiwindow_max_imp;\n        static IMP uiwindow_min_imp = uiwindowimp_lambda(&uiwindow_max_imp);\n\n        if (uiwindow_min_imp && uiwindow_max_imp &&\n            caller_frame_info.start_ip >=\n                reinterpret_cast<unw_word_t>(uiwindow_min_imp) &&\n            caller_frame_info.start_ip <=\n                reinterpret_cast<unw_word_t>(uiwindow_max_imp)) {\n          return HANDLE_UNCAUGHT_NSEXCEPTION(exception,\n                                             \"_UIGestureEnvironmentUpdate\");\n        }\n      }\n    }\n\n    handler_found = true;\n\n    break;\n  }\n\n  // If no handler is found, __cxa_throw would call failed_throw and terminate.\n  // See:\n  // https://github.com/llvm/llvm-project/blob/c5d2746fbea7/libcxxabi/src/cxa_exception.cpp\n  // __cxa_throw. Instead, call HANDLE_UNCAUGHT_NSEXCEPTION so the exception\n  // name and reason are properly recorded.\n  if (!handler_found) {\n    return HANDLE_UNCAUGHT_NSEXCEPTION(exception, \"__cxa_throw\");\n  }\n\n  // Forward to the next preprocessor.\n  return preprocessor_state->MaybeCallNextPreprocessor(exception);\n}\n\nvoid ExceptionPreprocessorState::Install(ObjcExceptionDelegate* delegate) {\n  DCHECK(!next_preprocessor_);\n  exception_delegate_ = delegate;\n\n  // Preprocessor.\n  next_preprocessor_ =\n      objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor);\n\n  // Uncaught processor.\n  next_uncaught_exception_handler_ = NSGetUncaughtExceptionHandler();\n  NSSetUncaughtExceptionHandler(&ObjcUncaughtExceptionHandler);\n}\n\nvoid ExceptionPreprocessorState::Uninstall() {\n  DCHECK(next_preprocessor_);\n  objc_setExceptionPreprocessor(next_preprocessor_);\n  next_preprocessor_ = nullptr;\n\n  NSSetUncaughtExceptionHandler(next_uncaught_exception_handler_);\n  next_uncaught_exception_handler_ = nullptr;\n\n  exception_delegate_ = nullptr;\n}\n\n}  // namespace\n\nvoid InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate) {\n  ExceptionPreprocessorState::Get()->Install(delegate);\n}\n\nvoid UninstallObjcExceptionPreprocessor() {\n  ExceptionPreprocessorState::Get()->Uninstall();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/ios_handler/exception_processor_test.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <Foundation/Foundation.h>\n#include <objc/message.h>\n#include <objc/runtime.h>\n\n#include \"gtest/gtest.h\"\n#include \"testing/platform_test.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing IOSExceptionProcessor = PlatformTest;\n\nTEST_F(IOSExceptionProcessor, SelectorExists) {\n  IMP init_imp =\n      class_getMethodImplementation(NSClassFromString(@\"UIGestureEnvironment\"),\n                                    NSSelectorFromString(@\"init\"));\n\n  IMP destruct_imp =\n      class_getMethodImplementation(NSClassFromString(@\"UIGestureEnvironment\"),\n                                    NSSelectorFromString(@\".cxx_destruct\"));\n\n  // From 10.15.0 objc4-779.1/runtime/objc-class.mm\n  // class_getMethodImplementation returns nil or _objc_msgForward on failure.\n  ASSERT_TRUE(init_imp);\n  EXPECT_NE(init_imp, _objc_msgForward);\n  ASSERT_TRUE(destruct_imp);\n  EXPECT_NE(destruct_imp, _objc_msgForward);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/ios_handler/in_process_handler.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/ios_handler/in_process_handler.h\"\n\n#include <stdio.h>\n#include <sys/stat.h>\n\n#include <algorithm>\n\n#include \"base/logging.h\"\n#include \"client/ios_handler/in_process_intermediate_dump_handler.h\"\n#include \"client/prune_crash_reports.h\"\n#include \"client/settings.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/ios/raw_logging.h\"\n\nnamespace {\n\n// Creates directory at |path|.\nbool CreateDirectory(const base::FilePath& path) {\n  if (mkdir(path.value().c_str(), 0755) == 0) {\n    return true;\n  }\n  if (errno != EEXIST) {\n    PLOG(ERROR) << \"mkdir \" << path.value();\n    return false;\n  }\n  return true;\n}\n\n// The file extension used to indicate a file is locked.\nconstexpr char kLockedExtension[] = \".locked\";\n\n// The seperator used to break the bundle id (e.g. com.chromium.ios) from the\n// uuid in the intermediate dump file name.\nconstexpr char kBundleSeperator[] = \"@\";\n\n// Zero-ed codes used by kMachExceptionFromNSException and\n// kMachExceptionSimulated.\nconstexpr mach_exception_data_type_t kEmulatedMachExceptionCodes[2] = {};\n\n}  // namespace\n\nnamespace crashpad {\nnamespace internal {\n\nInProcessHandler::InProcessHandler() = default;\n\nInProcessHandler::~InProcessHandler() {\n  if (cached_writer_) {\n    cached_writer_->Close();\n  }\n  UpdatePruneAndUploadThreads(false, UploadBehavior::kUploadWhenAppIsActive);\n}\n\nbool InProcessHandler::Initialize(\n    const base::FilePath& database,\n    const std::string& url,\n    const std::map<std::string, std::string>& annotations,\n    ProcessPendingReportsObservationCallback callback) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  annotations_ = annotations;\n  database_ = CrashReportDatabase::Initialize(database);\n  if (!database_) {\n    return false;\n  }\n  bundle_identifier_and_seperator_ =\n      system_data_.BundleIdentifier() + kBundleSeperator;\n\n  if (!url.empty()) {\n    // TODO(scottmg): options.rate_limit should be removed when we have a\n    // configurable database setting to control upload limiting.\n    // See https://crashpad.chromium.org/bug/23.\n    CrashReportUploadThread::Options upload_thread_options;\n    upload_thread_options.rate_limit = false;\n    upload_thread_options.upload_gzip = true;\n    upload_thread_options.watch_pending_reports = true;\n    upload_thread_options.identify_client_via_url = true;\n\n    upload_thread_.reset(new CrashReportUploadThread(\n        database_.get(), url, upload_thread_options, callback));\n  }\n\n  if (!CreateDirectory(database))\n    return false;\n  static constexpr char kPendingSerializediOSDump[] =\n      \"pending-serialized-ios-dump\";\n  base_dir_ = database.Append(kPendingSerializediOSDump);\n  if (!CreateDirectory(base_dir_))\n    return false;\n\n  bool is_app_extension = system_data_.IsExtension();\n  prune_thread_.reset(new PruneIntermediateDumpsAndCrashReportsThread(\n      database_.get(),\n      PruneCondition::GetDefault(),\n      base_dir_,\n      bundle_identifier_and_seperator_,\n      is_app_extension));\n  if (is_app_extension || system_data_.IsApplicationActive())\n    prune_thread_->Start();\n\n  if (!is_app_extension) {\n    system_data_.SetActiveApplicationCallback([this](bool active) {\n      dispatch_async(\n          dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n            UpdatePruneAndUploadThreads(active,\n                                        UploadBehavior::kUploadWhenAppIsActive);\n          });\n    });\n  }\n\n  base::FilePath cached_writer_path = NewLockedFilePath();\n  cached_writer_ = CreateWriterWithPath(cached_writer_path);\n  if (!cached_writer_.get())\n    return false;\n\n  // Cache the locked and unlocked path here so no allocations are needed during\n  // any exceptions.\n  cached_writer_path_ = cached_writer_path.value();\n  cached_writer_unlocked_path_ =\n      cached_writer_path.RemoveFinalExtension().value();\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nvoid InProcessHandler::DumpExceptionFromSignal(siginfo_t* siginfo,\n                                               ucontext_t* context) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  ScopedLockedWriter writer(GetCachedWriter(),\n                            cached_writer_path_.c_str(),\n                            cached_writer_unlocked_path_.c_str());\n  if (!writer.GetWriter()) {\n    CRASHPAD_RAW_LOG(\"Cannot DumpExceptionFromSignal without writer\");\n    return;\n  }\n  if (exception_callback_for_testing_) {\n    exception_callback_for_testing_();\n  }\n  ScopedReport report(writer.GetWriter(), system_data_, annotations_);\n  InProcessIntermediateDumpHandler::WriteExceptionFromSignal(\n      writer.GetWriter(), system_data_, siginfo, context);\n}\n\nvoid InProcessHandler::DumpExceptionFromMachException(\n    exception_behavior_t behavior,\n    thread_t thread,\n    exception_type_t exception,\n    const mach_exception_data_type_t* code,\n    mach_msg_type_number_t code_count,\n    thread_state_flavor_t flavor,\n    ConstThreadState old_state,\n    mach_msg_type_number_t old_state_count) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  ScopedLockedWriter writer(GetCachedWriter(),\n                            cached_writer_path_.c_str(),\n                            cached_writer_unlocked_path_.c_str());\n  if (!writer.GetWriter()) {\n    CRASHPAD_RAW_LOG(\"Cannot DumpExceptionFromMachException without writer\");\n    return;\n  }\n\n  if (exception_callback_for_testing_) {\n    exception_callback_for_testing_();\n  }\n\n  ScopedReport report(writer.GetWriter(), system_data_, annotations_);\n  InProcessIntermediateDumpHandler::WriteExceptionFromMachException(\n      writer.GetWriter(),\n      behavior,\n      thread,\n      exception,\n      code,\n      code_count,\n      flavor,\n      old_state,\n      old_state_count);\n}\n\nvoid InProcessHandler::DumpExceptionFromNSExceptionWithFrames(\n    const uint64_t* frames,\n    const size_t num_frames) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  ScopedLockedWriter writer(GetCachedWriter(),\n                            cached_writer_path_.c_str(),\n                            cached_writer_unlocked_path_.c_str());\n  if (!writer.GetWriter()) {\n    CRASHPAD_RAW_LOG(\n        \"Cannot DumpExceptionFromNSExceptionWithFrames without writer\");\n    return;\n  }\n  ScopedReport report(\n      writer.GetWriter(), system_data_, annotations_, frames, num_frames);\n  InProcessIntermediateDumpHandler::WriteExceptionFromNSException(\n      writer.GetWriter());\n}\n\nbool InProcessHandler::DumpExceptionFromSimulatedMachException(\n    const NativeCPUContext* context,\n    exception_type_t exception,\n    base::FilePath* path) {\n  base::FilePath locked_path = NewLockedFilePath();\n  *path = locked_path.RemoveFinalExtension();\n  return DumpExceptionFromSimulatedMachExceptionAtPath(\n      context, exception, locked_path);\n}\n\nbool InProcessHandler::DumpExceptionFromSimulatedMachExceptionAtPath(\n    const NativeCPUContext* context,\n    exception_type_t exception,\n    const base::FilePath& path) {\n  // This does not use the cached writer. It's expected that simulated\n  // exceptions can be called multiple times and there is no expectation that\n  // the application is in an unsafe state, or will be terminated after this\n  // call.\n  std::unique_ptr<IOSIntermediateDumpWriter> unsafe_writer =\n      CreateWriterWithPath(path);\n  base::FilePath writer_path_unlocked = path.RemoveFinalExtension();\n  ScopedLockedWriter writer(unsafe_writer.get(),\n                            path.value().c_str(),\n                            writer_path_unlocked.value().c_str());\n  if (!writer.GetWriter()) {\n    CRASHPAD_RAW_LOG(\n        \"Cannot DumpExceptionFromSimulatedMachExceptionAtPath without writer\");\n    return false;\n  }\n  ScopedReport report(writer.GetWriter(), system_data_, annotations_);\n  InProcessIntermediateDumpHandler::WriteExceptionFromMachException(\n      writer.GetWriter(),\n      MACH_EXCEPTION_CODES,\n      mach_thread_self(),\n      exception,\n      kEmulatedMachExceptionCodes,\n      std::size(kEmulatedMachExceptionCodes),\n      MACHINE_THREAD_STATE,\n      reinterpret_cast<ConstThreadState>(context),\n      MACHINE_THREAD_STATE_COUNT);\n  return true;\n}\n\nbool InProcessHandler::MoveIntermediateDumpAtPathToPending(\n    const base::FilePath& path) {\n  base::FilePath new_path_unlocked = NewLockedFilePath().RemoveFinalExtension();\n  return MoveFileOrDirectory(path, new_path_unlocked);\n}\nvoid InProcessHandler::ProcessIntermediateDumps(\n    const std::map<std::string, std::string>& annotations,\n    const UserStreamDataSources* user_stream_sources) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  for (auto& file : PendingFiles())\n    ProcessIntermediateDump(file, annotations, user_stream_sources);\n}\n\nvoid InProcessHandler::ProcessIntermediateDump(\n    const base::FilePath& file,\n    const std::map<std::string, std::string>& annotations,\n    const UserStreamDataSources* user_stream_sources) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  if (process_snapshot.InitializeWithFilePath(file, annotations)) {\n    SaveSnapshot(process_snapshot, user_stream_sources);\n  }\n}\n\nvoid InProcessHandler::StartProcessingPendingReports(\n    UploadBehavior upload_behavior) {\n  if (!upload_thread_)\n    return;\n\n  upload_thread_enabled_ = true;\n\n  // This may be a no-op if IsApplicationActive is false, as it is not safe to\n  // start the upload thread when in the background (due to the potential for\n  // flocked files in shared containers).\n  // TODO(crbug.com/crashpad/400): Consider moving prune and upload thread to\n  // BackgroundTasks and/or NSURLSession. This might allow uploads to continue\n  // in the background.\n  UpdatePruneAndUploadThreads(system_data_.IsApplicationActive(),\n                              upload_behavior);\n}\n\nvoid InProcessHandler::UpdatePruneAndUploadThreads(\n    bool active,\n    UploadBehavior upload_behavior) {\n  base::AutoLock lock_owner(prune_and_upload_lock_);\n  // TODO(crbug.com/crashpad/400): Consider moving prune and upload thread to\n  // BackgroundTasks and/or NSURLSession. This might allow uploads to continue\n  // in the background.\n  bool threads_should_run;\n  switch (upload_behavior) {\n    case UploadBehavior::kUploadWhenAppIsActive:\n      threads_should_run = active;\n      break;\n    case UploadBehavior::kUploadImmediately:\n      threads_should_run = true;\n      break;\n  }\n  if (threads_should_run) {\n    if (!prune_thread_->is_running())\n      prune_thread_->Start();\n    if (upload_thread_enabled_ && !upload_thread_->is_running()) {\n      upload_thread_->Start();\n    }\n  } else {\n    if (prune_thread_->is_running())\n      prune_thread_->Stop();\n    if (upload_thread_enabled_ && upload_thread_->is_running())\n      upload_thread_->Stop();\n  }\n}\n\nvoid InProcessHandler::SaveSnapshot(\n    ProcessSnapshotIOSIntermediateDump& process_snapshot,\n    const UserStreamDataSources* user_stream_sources) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  CrashReportDatabase::OperationStatus database_status =\n      database_->PrepareNewCrashReport(&new_report);\n  if (database_status != CrashReportDatabase::kNoError) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kPrepareNewCrashReportFailed);\n    return;\n  }\n  process_snapshot.SetReportID(new_report->ReportID());\n\n  UUID client_id;\n  Settings* const settings = database_->GetSettings();\n  if (settings && settings->GetClientID(&client_id)) {\n    process_snapshot.SetClientID(client_id);\n  }\n\n  MinidumpFileWriter minidump;\n  minidump.InitializeFromSnapshot(&process_snapshot);\n  AddUserExtensionStreams(user_stream_sources, &process_snapshot, &minidump);\n\n  if (!minidump.WriteEverything(new_report->Writer())) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kMinidumpWriteFailed);\n    return;\n  }\n  UUID uuid;\n  database_status =\n      database_->FinishedWritingCrashReport(std::move(new_report), &uuid);\n  if (database_status != CrashReportDatabase::kNoError) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kFinishedWritingCrashReportFailed);\n    return;\n  }\n\n  if (upload_thread_) {\n    upload_thread_->ReportPending(uuid);\n  }\n}\n\nstd::vector<base::FilePath> InProcessHandler::PendingFiles() {\n  DirectoryReader reader;\n  std::vector<base::FilePath> files;\n  if (!reader.Open(base_dir_)) {\n    return files;\n  }\n  base::FilePath file;\n  DirectoryReader::Result result;\n\n  // Because the intermediate dump directory is expected to be shared,\n  // mitigate any spamming by limiting this to |kMaxPendingFiles|.\n  constexpr size_t kMaxPendingFiles = 20;\n\n  // Track other application bundles separately, so they don't spam our\n  // intermediate dumps into never getting processed.\n  std::vector<base::FilePath> other_files;\n\n  base::FilePath cached_writer_path(cached_writer_path_);\n  while ((result = reader.NextFile(&file)) ==\n         DirectoryReader::Result::kSuccess) {\n    // Don't try to process files marked as 'locked' from a different bundle id.\n    bool bundle_match =\n        file.value().compare(0,\n                             bundle_identifier_and_seperator_.size(),\n                             bundle_identifier_and_seperator_) == 0;\n    if (!bundle_match && file.FinalExtension() == kLockedExtension) {\n      continue;\n    }\n\n    // Never process the current cached writer path.\n    file = base_dir_.Append(file);\n    if (file == cached_writer_path)\n      continue;\n\n    // Otherwise, include any other unlocked, or locked files matching\n    // |bundle_identifier_and_seperator_|.\n    if (bundle_match) {\n      files.push_back(file);\n      if (files.size() >= kMaxPendingFiles)\n        return files;\n    } else {\n      other_files.push_back(file);\n    }\n  }\n\n  auto end_iterator =\n      other_files.begin() +\n      std::min(kMaxPendingFiles - files.size(), other_files.size());\n  files.insert(files.end(), other_files.begin(), end_iterator);\n  return files;\n}\n\nIOSIntermediateDumpWriter* InProcessHandler::GetCachedWriter() {\n  static_assert(\n      std::atomic<uint64_t>::is_always_lock_free,\n      \"std::atomic_compare_exchange_strong uint64_t may not be signal-safe\");\n  uint64_t thread_self;\n  // This is only safe when passing pthread_self(), otherwise this can lock.\n  pthread_threadid_np(pthread_self(), &thread_self);\n  uint64_t expected = 0;\n  if (!std::atomic_compare_exchange_strong(\n          &exception_thread_id_, &expected, thread_self)) {\n    if (expected == thread_self) {\n      // Another exception came in from this thread, which means it's likely\n      // that our own handler crashed. We could open up a new intermediate dump\n      // and try to save this dump, but we could end up endlessly writing dumps.\n      // Instead, give up.\n    } else {\n      // Another thread is handling a crash. Sleep forever.\n      while (1) {\n        sleep(std::numeric_limits<unsigned int>::max());\n      }\n    }\n    return nullptr;\n  }\n\n  return cached_writer_.get();\n}\n\nstd::unique_ptr<IOSIntermediateDumpWriter>\nInProcessHandler::CreateWriterWithPath(const base::FilePath& writer_path) {\n  std::unique_ptr<IOSIntermediateDumpWriter> writer =\n      std::make_unique<IOSIntermediateDumpWriter>();\n  if (!writer->Open(writer_path)) {\n    DLOG(ERROR) << \"Unable to open intermediate dump file: \"\n                << writer_path.value();\n    return nullptr;\n  }\n  return writer;\n}\n\nconst base::FilePath InProcessHandler::NewLockedFilePath() {\n  UUID uuid;\n  uuid.InitializeWithNew();\n  const std::string file_string =\n      bundle_identifier_and_seperator_ + uuid.ToString() + kLockedExtension;\n  return base_dir_.Append(file_string);\n}\n\nInProcessHandler::ScopedReport::ScopedReport(\n    IOSIntermediateDumpWriter* writer,\n    const IOSSystemDataCollector& system_data,\n    const std::map<std::string, std::string>& annotations,\n    const uint64_t* frames,\n    const size_t num_frames)\n    : writer_(writer),\n      frames_(frames),\n      num_frames_(num_frames),\n      rootMap_(writer) {\n  DCHECK(writer);\n  // Grab the report creation time before writing the report.\n  uint64_t report_time_nanos = ClockMonotonicNanoseconds();\n  InProcessIntermediateDumpHandler::WriteHeader(writer);\n  InProcessIntermediateDumpHandler::WriteProcessInfo(writer, annotations);\n  InProcessIntermediateDumpHandler::WriteSystemInfo(\n      writer, system_data, report_time_nanos);\n}\n\nInProcessHandler::ScopedReport::~ScopedReport() {\n  // Write threads and modules last (after the exception itself is written by\n  // DumpExceptionFrom*.)\n  InProcessIntermediateDumpHandler::WriteThreadInfo(\n      writer_, frames_, num_frames_);\n  InProcessIntermediateDumpHandler::WriteModuleInfo(writer_);\n}\n\nInProcessHandler::ScopedLockedWriter::ScopedLockedWriter(\n    IOSIntermediateDumpWriter* writer,\n    const char* writer_path,\n    const char* writer_unlocked_path)\n    : writer_path_(writer_path),\n      writer_unlocked_path_(writer_unlocked_path),\n      writer_(writer) {}\n\nInProcessHandler::ScopedLockedWriter::~ScopedLockedWriter() {\n  if (!writer_)\n    return;\n\n  writer_->Close();\n  if (rename(writer_path_, writer_unlocked_path_) != 0) {\n    CRASHPAD_RAW_LOG(\"Could not remove locked extension.\");\n    CRASHPAD_RAW_LOG(writer_path_);\n    CRASHPAD_RAW_LOG(writer_unlocked_path_);\n  }\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "client/ios_handler/in_process_handler.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_IN_PROCESS_HANDLER_H_\n#define CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_IN_PROCESS_HANDLER_H_\n\n#include <mach/mach.h>\n#include <stdint.h>\n\n#include <atomic>\n#include <functional>\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"base/synchronization/lock.h\"\n#include \"client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h\"\n#include \"client/upload_behavior_ios.h\"\n#include \"handler/crash_report_upload_thread.h\"\n#include \"handler/user_stream_data_source.h\"\n#include \"snapshot/ios/process_snapshot_ios_intermediate_dump.h\"\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n#include \"util/ios/ios_system_data_collector.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/capture_context.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Manage intermediate minidump generation, and own the crash report\n//!     upload thread and database.\nclass InProcessHandler {\n public:\n  InProcessHandler();\n  ~InProcessHandler();\n  InProcessHandler(const InProcessHandler&) = delete;\n  InProcessHandler& operator=(const InProcessHandler&) = delete;\n\n  //! \\brief Observation callback invoked each time this object finishes\n  //!     processing and attempting to upload on-disk crash reports (whether or\n  //!     not the uploads succeeded).\n  //!\n  //! This callback is copied into this object. Any references or pointers\n  //! inside must outlive this object.\n  //!\n  //! The callback might be invoked on a background thread, so clients must\n  //! synchronize appropriately.\n  using ProcessPendingReportsObservationCallback = std::function<void()>;\n\n  //! \\brief Initializes the in-process handler.\n  //!\n  //! This method must be called only once, and must be successfully called\n  //! before any other method in this class may be called.\n  //!\n  //! \\param[in] database The path to a Crashpad database.\n  //! \\param[in] url The URL of an upload server.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //! \\param[in] callback Optional callback invoked zero or more times\n  //!     on a background thread each time this object finishes\n  //!     processing and attempting to upload on-disk crash reports.\n  //! \\return `true` if a handler to a pending intermediate dump could be\n  //!     opened.\n  bool Initialize(const base::FilePath& database,\n                  const std::string& url,\n                  const std::map<std::string, std::string>& annotations,\n                  ProcessPendingReportsObservationCallback callback =\n                      ProcessPendingReportsObservationCallback());\n\n  //! \\brief Generate an intermediate dump from a signal handler exception.\n  //!      Writes the dump with the cached writer does not allow concurrent\n  //!      exceptions to be written. It is expected the system will terminate\n  //!      the application after this call.\n  //!\n  //! \\param[in] siginfo A pointer to a `siginfo_t` object received by a signal\n  //!     handler.\n  //! \\param[in] context A pointer to a `ucontext_t` object received by a\n  //!     signal.\n  void DumpExceptionFromSignal(siginfo_t* siginfo, ucontext_t* context);\n\n  //! \\brief Generate an intermediate dump from a mach exception. Writes the\n  //!     dump with the cached writer does not allow concurrent exceptions to be\n  //!     written. It is expected the system will terminate the application\n  //!     after this call.\n  //!\n  //! \\param[in] behavior\n  //! \\param[in] thread\n  //! \\param[in] exception\n  //! \\param[in] code\n  //! \\param[in] code_count\n  //! \\param[in,out] flavor\n  //! \\param[in] old_state\n  //! \\param[in] old_state_count\n  void DumpExceptionFromMachException(exception_behavior_t behavior,\n                                      thread_t thread,\n                                      exception_type_t exception,\n                                      const mach_exception_data_type_t* code,\n                                      mach_msg_type_number_t code_count,\n                                      thread_state_flavor_t flavor,\n                                      ConstThreadState old_state,\n                                      mach_msg_type_number_t old_state_count);\n\n  //! \\brief Generate an intermediate dump from an uncaught NSException.\n  //!\n  //! When the ObjcExceptionPreprocessor does not detect an NSException as it is\n  //! thrown, the last-chance uncaught exception handler passes a list of call\n  //! stack frame addresses.  Record them in the intermediate dump so a minidump\n  //! with a 'fake' call stack is generated.  Writes the dump with the cached\n  //! writer does not allow concurrent exceptions to be written. It is expected\n  //! the system will terminate the application after this call.\n\n  //!\n  //! \\param[in] frames An array of call stack frame addresses.\n  //! \\param[in] num_frames The number of frames in |frames|.\n  void DumpExceptionFromNSExceptionWithFrames(const uint64_t* frames,\n                                              const size_t num_frames);\n\n  //! \\brief Generate a simulated intermediate dump similar to a Mach exception\n  //!     in the same base directory as other exceptions. Does not use the\n  //!     cached writer.\n  //!\n  //! \\param[in] context A pointer to a NativeCPUContext object for this\n  //!     simulated exception.\n  //! \\param[in] exception\n  //! \\param[out] path The path of the intermediate dump generated.\n  //! \\return `true` if the pending intermediate dump could be written.\n  bool DumpExceptionFromSimulatedMachException(const NativeCPUContext* context,\n                                               exception_type_t exception,\n                                               base::FilePath* path);\n\n  //! \\brief Generate a simulated intermediate dump similar to a Mach exception\n  //!     at a specific path. Does not use the cached writer.\n  //!\n  //! \\param[in] context A pointer to a NativeCPUContext object for this\n  //!     simulated exception.\n  //! \\param[in] exception\n  //! \\param[in] path Path to where the intermediate dump should be written.\n  //! \\return `true` if the pending intermediate dump could be written.\n  bool DumpExceptionFromSimulatedMachExceptionAtPath(\n      const NativeCPUContext* context,\n      exception_type_t exception,\n      const base::FilePath& path);\n\n  //! \\brief Moves an intermediate dump to the pending directory. This is meant\n  //!     to be used by the UncaughtExceptionHandler, when NSException caught\n  //!     by the preprocessor matches the UncaughtExceptionHandler.\n  //!\n  //! \\param[in] path Path to the specific intermediate dump.\n  bool MoveIntermediateDumpAtPathToPending(const base::FilePath& path);\n\n  //! \\brief Requests that the handler convert all intermediate dumps into\n  //!     minidumps and trigger an upload if possible.\n  //!\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //! \\param[in] user_stream_sources An optional vector containing the\n  //!     extensibility data sources to call on crash. Each time a minidump is\n  //!     created, the sources are called in turn. Any streams returned are\n  //!     added to the minidump.\n  void ProcessIntermediateDumps(\n      const std::map<std::string, std::string>& annotations,\n      const UserStreamDataSources* user_stream_sources);\n\n  //! \\brief Requests that the handler convert a specific intermediate dump into\n  //!     a minidump and trigger an upload if possible.\n  //!\n  //! \\param[in] path Path to the specific intermediate dump.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //! \\param[in] user_stream_sources An optional vector containing the\n  //!     extensibility data sources to call on crash. Each time a minidump is\n  //!     created, the sources are called in turn. Any streams returned are\n  //!     added to the minidump.\n  void ProcessIntermediateDump(\n      const base::FilePath& path,\n      const std::map<std::string, std::string>& annotations = {},\n      const UserStreamDataSources* user_stream_sources = {});\n\n  //! \\brief Requests that the handler begin in-process uploading of any\n  //!     pending reports.\n  //!\n  //! \\param[in] upload_behavior Controls when the upload thread will run and\n  //!     process pending reports. By default, only uploads pending reports\n  //!     when the application is active.\n  void StartProcessingPendingReports(\n      UploadBehavior upload_behavior = UploadBehavior::kUploadWhenAppIsActive);\n\n  //! \\brief Inject a callback into the Mach exception and signal handling\n  //!     mechanisms. Intended to be used by tests to trigger a reentrant\n  //      exception.\n  void SetExceptionCallbackForTesting(void (*callback)()) {\n    exception_callback_for_testing_ = callback;\n  }\n\n private:\n  //! \\brief Helper to start and end intermediate reports.\n  class ScopedReport {\n   public:\n    ScopedReport(IOSIntermediateDumpWriter* writer,\n                 const IOSSystemDataCollector& system_data,\n                 const std::map<std::string, std::string>& annotations,\n                 const uint64_t* frames = nullptr,\n                 const size_t num_frames = 0);\n    ~ScopedReport();\n    ScopedReport(const ScopedReport&) = delete;\n    ScopedReport& operator=(const ScopedReport&) = delete;\n\n   private:\n    IOSIntermediateDumpWriter* writer_;\n    const uint64_t* frames_;\n    const size_t num_frames_;\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap_;\n  };\n\n  //! \\brief Helper to manage closing the intermediate dump writer and unlocking\n  //!     the dump file (renaming the file) after the report is written.\n  class ScopedLockedWriter {\n   public:\n    ScopedLockedWriter(IOSIntermediateDumpWriter* writer,\n                       const char* writer_path,\n                       const char* writer_unlocked_path);\n\n    //! \\brief Close the writer_ and rename to the file with path without the\n    //!     .locked extension.\n    ~ScopedLockedWriter();\n\n    ScopedLockedWriter(const ScopedLockedWriter&) = delete;\n    ScopedLockedWriter& operator=(const ScopedLockedWriter&) = delete;\n\n    IOSIntermediateDumpWriter* GetWriter() { return writer_; }\n\n   private:\n    const char* writer_path_;\n    const char* writer_unlocked_path_;\n    IOSIntermediateDumpWriter* writer_;\n  };\n\n  //! \\brief Manage the prune and upload thread when the active state changes.\n  //!\n  //! \\param[in] active `true` if the application is actively running in the\n  //!     foreground, `false` otherwise.\n  //! \\param[in] upload_behavior Controls when the upload thread will run and\n  //!     process pending reports.\n  void UpdatePruneAndUploadThreads(bool active, UploadBehavior upload_behavior);\n\n  //! \\brief Writes a minidump to the Crashpad database from the\n  //!     \\a process_snapshot, and triggers the upload_thread_ if started.\n  //! \\param[in] user_stream_sources An optional vector containing the\n  //!     extensibility data sources to call on crash. Each time a minidump is\n  //!     created, the sources are called in turn. Any streams returned are\n  //!     added to the minidump.\n  void SaveSnapshot(ProcessSnapshotIOSIntermediateDump& process_snapshot,\n                    const UserStreamDataSources* user_stream_sources = {});\n\n  //! \\brief Process a maximum of 20 pending intermediate dumps. Dumps named\n  //!     with our bundle id get first priority to prevent spamming.\n  std::vector<base::FilePath> PendingFiles();\n\n  //! \\brief Lock access to the cached intermediate dump writer from\n  //!     concurrent signal, Mach exception and uncaught NSExceptions so that\n  //!     the first exception wins. If the same thread triggers another\n  //!     reentrant exception, ignore it. If a different thread triggers a\n  //!     concurrent exception, sleep indefinitely.\n  IOSIntermediateDumpWriter* GetCachedWriter();\n\n  //! \\brief Open a new intermediate dump writer from \\a writer_path.\n  std::unique_ptr<IOSIntermediateDumpWriter> CreateWriterWithPath(\n      const base::FilePath& writer_path);\n\n  //! \\brief Generates a new file path to be used by an intermediate dump\n  //! writer built from base_dir_,, bundle_identifier_and_seperator_, a new\n  //! UUID, with a .locked extension.\n  const base::FilePath NewLockedFilePath();\n\n  // Intended to be used by tests triggering a reentrant exception. Called\n  // in DumpExceptionFromMachException and DumpExceptionFromSignal after\n  // acquiring the cached_writer_.\n  void (*exception_callback_for_testing_)() = nullptr;\n\n  // Used to synchronize access to UpdatePruneAndUploadThreads().\n  base::Lock prune_and_upload_lock_;\n  std::atomic_bool upload_thread_enabled_ = false;\n  std::map<std::string, std::string> annotations_;\n  base::FilePath base_dir_;\n  std::string cached_writer_path_;\n  std::string cached_writer_unlocked_path_;\n  std::unique_ptr<IOSIntermediateDumpWriter> cached_writer_;\n  std::atomic<uint64_t> exception_thread_id_ = 0;\n  std::unique_ptr<CrashReportUploadThread> upload_thread_;\n  std::unique_ptr<PruneIntermediateDumpsAndCrashReportsThread> prune_thread_;\n  std::unique_ptr<CrashReportDatabase> database_;\n  std::string bundle_identifier_and_seperator_;\n  IOSSystemDataCollector system_data_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_IN_PROCESS_HANDLER_H_\n"
  },
  {
    "path": "client/ios_handler/in_process_handler_test.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/ios_handler/in_process_handler.h\"\n\n#include \"gtest/gtest.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/file/filesystem.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nbool CreateFile(const base::FilePath& file) {\n  ScopedFileHandle fd(LoggingOpenFileForWrite(\n      file, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));\n  EXPECT_TRUE(fd.is_valid());\n  return fd.is_valid();\n}\n\nclass InProcessHandlerTest : public testing::Test {\n protected:\n  // testing::Test:\n\n  void SetUp() override {\n    ASSERT_TRUE(in_process_handler_.Initialize(temp_dir_.path(), \"\", {}));\n    pending_dir_ = temp_dir_.path().Append(\"pending-serialized-ios-dump\");\n    bundle_identifier_and_seperator_ = system_data_.BundleIdentifier() + \"@\";\n  }\n\n  const auto& path() const { return pending_dir_; }\n  auto& handler() { return in_process_handler_; }\n\n  void CreateFiles(int files, int other_files) {\n    base::FilePath::StringType file_prepend =\n        FILE_PATH_LITERAL(bundle_identifier_and_seperator_);\n    base::FilePath::StringType file_name = FILE_PATH_LITERAL(\"file\");\n    for (int i = 0; i < files; i++) {\n      std::string i_str = std::to_string(i);\n      base::FilePath file(file_prepend + file_name + i_str);\n      CreateFile(path().Append(file));\n    }\n\n    for (int i = 0; i < other_files; i++) {\n      std::string i_str = std::to_string(i);\n      base::FilePath file(file_name + i_str);\n      CreateFile(path().Append(file));\n    }\n  }\n\n  void VerifyRemainingFileCount(int expected_files_count,\n                                int expected_other_files_count) {\n    DirectoryReader reader;\n    ASSERT_TRUE(reader.Open(path()));\n    DirectoryReader::Result result;\n    base::FilePath filename;\n    int files_count = 0;\n    int other_files_count = 0;\n    while ((result = reader.NextFile(&filename)) ==\n           DirectoryReader::Result::kSuccess) {\n      bool bundle_match =\n          filename.value().compare(0,\n                                   bundle_identifier_and_seperator_.size(),\n                                   bundle_identifier_and_seperator_) == 0;\n      if (bundle_match) {\n        files_count++;\n      } else {\n        other_files_count++;\n      }\n    }\n    EXPECT_EQ(expected_files_count, files_count);\n    EXPECT_EQ(expected_other_files_count, other_files_count);\n  }\n\n  void ClearFiles() {\n    DirectoryReader reader;\n    ASSERT_TRUE(reader.Open(path()));\n    DirectoryReader::Result result;\n    base::FilePath filename;\n    while ((result = reader.NextFile(&filename)) ==\n           DirectoryReader::Result::kSuccess) {\n      LoggingRemoveFile(path().Append(filename));\n    }\n  }\n\n private:\n  ScopedTempDir temp_dir_;\n  base::FilePath pending_dir_;\n  std::string bundle_identifier_and_seperator_;\n  internal::IOSSystemDataCollector system_data_;\n  internal::InProcessHandler in_process_handler_;\n};\n\nTEST_F(InProcessHandlerTest, TestPendingFileLimit) {\n  // Clear this first to blow away the pending file held by InProcessHandler.\n  ClearFiles();\n\n  // Only process other app files.\n  CreateFiles(0, 20);\n  handler().ProcessIntermediateDumps({}, nullptr);\n  VerifyRemainingFileCount(0, 0);\n  ClearFiles();\n\n  // Only process our app files.\n  CreateFiles(20, 20);\n  handler().ProcessIntermediateDumps({}, nullptr);\n  VerifyRemainingFileCount(0, 20);\n  ClearFiles();\n\n  // Process all of our files and 10   remaining.\n  CreateFiles(10, 30);\n  handler().ProcessIntermediateDumps({}, nullptr);\n  VerifyRemainingFileCount(0, 20);\n  ClearFiles();\n\n  // Process 20 our files, leaving 10 remaining, and all other files remaining.\n  CreateFiles(30, 10);\n  handler().ProcessIntermediateDumps({}, nullptr);\n  VerifyRemainingFileCount(10, 10);\n  ClearFiles();\n\n  CreateFiles(0, 0);\n  handler().ProcessIntermediateDumps({}, nullptr);\n  VerifyRemainingFileCount(0, 0);\n  ClearFiles();\n\n  CreateFiles(10, 0);\n  handler().ProcessIntermediateDumps({}, nullptr);\n  VerifyRemainingFileCount(0, 0);\n  ClearFiles();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/ios_handler/in_process_intermediate_dump_handler.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/ios_handler/in_process_intermediate_dump_handler.h\"\n\n#include <mach-o/dyld_images.h>\n#include <mach-o/nlist.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/sysctl.h>\n#include <time.h>\n\n#include <iterator>\n#include <optional>\n\n#include \"base/check_op.h\"\n#include \"build/build_config.h\"\n#include \"snapshot/snapshot_constants.h\"\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n#include \"util/ios/raw_logging.h\"\n#include \"util/ios/scoped_vm_map.h\"\n#include \"util/ios/scoped_vm_read.h\"\n#include \"util/synchronization/scoped_spin_guard.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\n#if defined(ARCH_CPU_X86_64)\nconst thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64;\nconst thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64;\nconst thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64;\nusing thread_state_type = x86_thread_state64_t;\n#elif defined(ARCH_CPU_ARM64)\nconst thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;\nconst thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;\nconst thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64;\nusing thread_state_type = arm_thread_state64_t;\n#endif\n\n// From snapshot/mac/process_types/crashreporterclient.proctype\nstruct crashreporter_annotations_t {\n  // Version 4\n  uint64_t version;\n  uint64_t message;\n  uint64_t signature_string;\n  uint64_t backtrace;\n  uint64_t message2;\n  uint64_t thread;\n  uint64_t dialog_mode;\n\n  // Version 5\n  uint64_t abort_cause;\n\n  // The structure here is only defined through version 5, although version 7 is\n  // also known, and contains an additional 264 bytes from this point. Version 7\n  // was first used in iOS 26. The version 7 extension is not defined here\n  // because it would cause the reader to attempt to read the full length of a\n  // version 7 structure, even in cases where only a version 5 structure is\n  // present at runtime, as it will be in iOS versions before 26.\n  //\n  // If it ever becomes necessary to read fields beyond version 5 (or whatever\n  // the minimum structure version that would be encountered at runtime is at\n  // that time in the future), the reader will need to take additional care to\n  // consult the `version` field and only read as much of the structure as is\n  // appropriate for the indicated version. The macOS implementation implements\n  // this logic.\n};\n\n//! \\brief Manage memory and ports after calling `task_threads`.\nclass ScopedTaskThreads {\n public:\n  explicit ScopedTaskThreads(thread_act_array_t threads,\n                             mach_msg_type_number_t thread_count)\n      : threads_(threads), thread_count_(thread_count) {}\n\n  ScopedTaskThreads(const ScopedTaskThreads&) = delete;\n  ScopedTaskThreads& operator=(const ScopedTaskThreads&) = delete;\n\n  ~ScopedTaskThreads() {\n    for (uint32_t thread_index = 0; thread_index < thread_count_;\n         ++thread_index) {\n      mach_port_deallocate(mach_task_self(), threads_[thread_index]);\n    }\n    vm_deallocate(mach_task_self(),\n                  reinterpret_cast<vm_address_t>(threads_),\n                  sizeof(thread_t) * thread_count_);\n  }\n\n private:\n  thread_act_array_t threads_;\n  mach_msg_type_number_t thread_count_;\n};\n\n//! \\brief Log \\a key as a string.\nvoid WriteError(IntermediateDumpKey key) {\n  CRASHPAD_RAW_LOG(\"Unable to write key\");\n  switch (key) {\n    // clang-format off\n#define CASE_KEY(Name, Value)       \\\n    case IntermediateDumpKey::Name: \\\n      CRASHPAD_RAW_LOG(#Name);      \\\n      break;\n    INTERMEDIATE_DUMP_KEYS(CASE_KEY)\n#undef CASE_KEY\n    // clang-format on\n  }\n}\n\n//! \\brief Call AddProperty with raw error log.\n//!\n//! \\param[in] writer The dump writer\n//! \\param[in] key The key to write.\n//! \\param[in] value Memory to be written.\n//! \\param[in] count Length of \\a value.\ntemplate <typename T>\nvoid WriteProperty(IOSIntermediateDumpWriter* writer,\n                   IntermediateDumpKey key,\n                   const T* value,\n                   size_t count = 1) {\n  if (!writer->AddProperty(key, value, count))\n    WriteError(key);\n}\n\n//! \\brief Call AddPropertyBytes with raw error log.\n//!\n//! \\param[in] writer The dump writer\n//! \\param[in] key The key to write.\n//! \\param[in] value Memory to be written.\n//! \\param[in] value_length Length of \\a data.\nvoid WritePropertyBytes(IOSIntermediateDumpWriter* writer,\n                        IntermediateDumpKey key,\n                        const void* value,\n                        size_t value_length) {\n  if (!writer->AddPropertyBytes(key, value, value_length))\n    WriteError(key);\n}\n\n//! \\brief Call AddPropertyCString with raw error log.\n//!\n//! \\param[in] writer The dump writer\n//! \\param[in] key The key to write.\n//! \\param[in] max_length The maximum string length.\n//! \\param[in] value Memory to be written.\nvoid WritePropertyCString(IOSIntermediateDumpWriter* writer,\n                          IntermediateDumpKey key,\n                          size_t max_length,\n                          const char* value) {\n  if (!writer->AddPropertyCString(key, max_length, value))\n    WriteError(key);\n}\n\nkern_return_t MachVMRegionRecurseDeepest(task_t task,\n                                         vm_address_t* address,\n                                         vm_size_t* size,\n                                         natural_t* depth,\n                                         vm_prot_t* protection,\n                                         unsigned int* user_tag) {\n  vm_region_submap_short_info_64 submap_info;\n  mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;\n  while (true) {\n    // Note: vm_region_recurse() would be fine here, but it does not provide\n    // VM_REGION_SUBMAP_SHORT_INFO_COUNT.\n    kern_return_t kr = vm_region_recurse_64(\n        task,\n        address,\n        size,\n        depth,\n        reinterpret_cast<vm_region_recurse_info_t>(&submap_info),\n        &count);\n    if (kr != KERN_SUCCESS) {\n      CRASHPAD_RAW_LOG_ERROR(kr, \"vm_region_recurse_64\");\n      return kr;\n    }\n\n    if (!submap_info.is_submap) {\n      *protection = submap_info.protection;\n      *user_tag = submap_info.user_tag;\n      return KERN_SUCCESS;\n    }\n\n    ++*depth;\n  }\n}\n\n//! \\brief Adjusts the region for the red zone, if the ABI requires one.\n//!\n//! This method performs red zone calculation for CalculateStackRegion(). Its\n//! parameters are local variables used within that method, and may be\n//! modified as needed.\n//!\n//! Where a red zone is required, the region of memory captured for a thread’s\n//! stack will be extended to include the red zone below the stack pointer,\n//! provided that such memory is mapped, readable, and has the correct user\n//! tag value. If these conditions cannot be met fully, as much of the red\n//! zone will be captured as is possible while meeting these conditions.\n//!\n//! \\param[in,out] start_address The base address of the region to begin\n//!     capturing stack memory from. On entry, \\a start_address is the stack\n//!     pointer. On return, \\a start_address may be decreased to encompass a\n//!     red zone.\n//! \\param[in,out] region_base The base address of the region that contains\n//!     stack memory. This is distinct from \\a start_address in that \\a\n//!     region_base will be page-aligned. On entry, \\a region_base is the\n//!     base address of a region that contains \\a start_address. On return,\n//!     if \\a start_address is decremented and is outside of the region\n//!     originally described by \\a region_base, \\a region_base will also be\n//!     decremented appropriately.\n//! \\param[in,out] region_size The size of the region that contains stack\n//!     memory. This region begins at \\a region_base. On return, if \\a\n//!     region_base is decremented, \\a region_size will be incremented\n//!     appropriately.\n//! \\param[in] user_tag The Mach VM system’s user tag for the region described\n//!     by the initial values of \\a region_base and \\a region_size. The red\n//!     zone will only be allowed to extend out of the region described by\n//!     these initial values if the user tag is appropriate for stack memory\n//!     and the expanded region has the same user tag value.\nvoid LocateRedZone(vm_address_t* const start_address,\n                   vm_address_t* const region_base,\n                   vm_address_t* const region_size,\n                   const unsigned int user_tag) {\n  // x86_64 has a red zone. See AMD64 ABI 0.99.8,\n  // https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/uploads/01de35b2c8adc7545de52604cc45d942/x86-64-psABI-2021-05-20.pdf#page=23.\n  // section 3.2.2, “The Stack Frame”.\n  // So does ARM64,\n  // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-Stacks-Red-Zone\n  // section \"Respect the Stack’s Red Zone\".\n  constexpr vm_size_t kRedZoneSize = 128;\n  vm_address_t red_zone_base =\n      *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0;\n  bool red_zone_ok = false;\n  if (red_zone_base >= *region_base) {\n    // The red zone is within the region already discovered.\n    red_zone_ok = true;\n  } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) {\n    // Probe to see if there’s a region immediately below the one already\n    // discovered.\n    vm_address_t red_zone_region_base = red_zone_base;\n    vm_size_t red_zone_region_size;\n    natural_t red_zone_depth = 0;\n    vm_prot_t red_zone_protection;\n    unsigned int red_zone_user_tag;\n    kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(),\n                                                  &red_zone_region_base,\n                                                  &red_zone_region_size,\n                                                  &red_zone_depth,\n                                                  &red_zone_protection,\n                                                  &red_zone_user_tag);\n    if (kr != KERN_SUCCESS) {\n      *start_address = *region_base;\n    } else if (red_zone_region_base + red_zone_region_size == *region_base &&\n               (red_zone_protection & VM_PROT_READ) != 0 &&\n               red_zone_user_tag == user_tag) {\n      // The region containing the red zone is immediately below the region\n      // already found, it’s readable (not the guard region), and it has the\n      // same user tag as the region already found, so merge them.\n      red_zone_ok = true;\n      *region_base -= red_zone_region_size;\n      *region_size += red_zone_region_size;\n    }\n  }\n\n  if (red_zone_ok) {\n    // Begin capturing from the base of the red zone (but not the entire\n    // region that encompasses the red zone).\n    *start_address = red_zone_base;\n  } else {\n    // The red zone would go lower into another region in memory, but no\n    // region was found. Memory can only be captured to an address as low as\n    // the base address of the region already found.\n    *start_address = *region_base;\n  }\n}\n\n//! \\brief Calculates the base address and size of the region used as a\n//!     thread’s stack.\n//!\n//! The region returned by this method may be formed by merging multiple\n//! adjacent regions in a process’ memory map if appropriate. The base address\n//! of the returned region may be lower than the \\a stack_pointer passed in\n//! when the ABI mandates a red zone below the stack pointer.\n//!\n//! \\param[in] stack_pointer The stack pointer, referring to the top (lowest\n//!     address) of a thread’s stack.\n//! \\param[out] stack_region_size The size of the memory region used as the\n//!     thread’s stack.\n//!\n//! \\return The base address (lowest address) of the memory region used as the\n//!     thread’s stack.\nvm_address_t CalculateStackRegion(vm_address_t stack_pointer,\n                                  vm_size_t* stack_region_size) {\n  // For pthreads, it may be possible to compute the stack region based on the\n  // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct\n  // for a thread can be located at TSD slot 0, or the known offsets of\n  // stackaddr and stacksize from the TSD area could be used.\n  vm_address_t region_base = stack_pointer;\n  vm_size_t region_size;\n  natural_t depth = 0;\n  vm_prot_t protection;\n  unsigned int user_tag;\n  kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(),\n                                                &region_base,\n                                                &region_size,\n                                                &depth,\n                                                &protection,\n                                                &user_tag);\n  if (kr != KERN_SUCCESS) {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"MachVMRegionRecurseDeepest\");\n    *stack_region_size = 0;\n    return 0;\n  }\n\n  if (region_base > stack_pointer) {\n    // There’s nothing mapped at the stack pointer’s address. Something may have\n    // trashed the stack pointer. Note that this shouldn’t happen for a normal\n    // stack guard region violation because the guard region is mapped but has\n    // VM_PROT_NONE protection.\n    *stack_region_size = 0;\n    return 0;\n  }\n\n  vm_address_t start_address = stack_pointer;\n\n  if ((protection & VM_PROT_READ) == 0) {\n    // If the region isn’t readable, the stack pointer probably points to the\n    // guard region. Don’t include it as part of the stack, and don’t include\n    // anything at any lower memory address. The code below may still possibly\n    // find the real stack region at a memory address higher than this region.\n    start_address = region_base + region_size;\n  } else {\n    // If the ABI requires a red zone, adjust the region to include it if\n    // possible.\n    LocateRedZone(&start_address, &region_base, &region_size, user_tag);\n\n    // Regardless of whether the ABI requires a red zone, capture up to\n    // kExtraCaptureSize additional bytes of stack, but only if present in the\n    // region that was already found.\n    constexpr vm_size_t kExtraCaptureSize = 128;\n    start_address = std::max(start_address >= kExtraCaptureSize\n                                 ? start_address - kExtraCaptureSize\n                                 : start_address,\n                             region_base);\n\n    // Align start_address to a 16-byte boundary, which can help readers by\n    // ensuring that data is aligned properly. This could page-align instead,\n    // but that might be wasteful.\n    constexpr vm_size_t kDesiredAlignment = 16;\n    start_address &= ~(kDesiredAlignment - 1);\n    DCHECK_GE(start_address, region_base);\n  }\n\n  region_size -= (start_address - region_base);\n  region_base = start_address;\n\n  vm_size_t total_region_size = region_size;\n\n  // The stack region may have gotten split up into multiple abutting regions.\n  // Try to coalesce them. This frequently happens for the main thread’s stack\n  // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region\n  // is split up due to an mprotect() or vm_protect() call.\n  //\n  // Stack regions created by the kernel and the pthreads library will be marked\n  // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions\n  // with the same tag should find an entire stack region. Checking that the\n  // protection on individual regions is not VM_PROT_NONE should guarantee that\n  // this algorithm doesn’t collect map entries belonging to another thread’s\n  // stack: well-behaved stacks (such as those created by the kernel and the\n  // pthreads library) have VM_PROT_NONE guard regions at their low-address\n  // ends.\n  //\n  // Other stack regions may not be so well-behaved and thus if user_tag is not\n  // VM_MEMORY_STACK, the single region that was found is used as-is without\n  // trying to merge it with other adjacent regions.\n  if (user_tag == VM_MEMORY_STACK) {\n    vm_address_t try_address = region_base;\n    vm_address_t original_try_address;\n\n    while (try_address += region_size,\n           original_try_address = try_address,\n           (kr = MachVMRegionRecurseDeepest(mach_task_self(),\n                                            &try_address,\n                                            &region_size,\n                                            &depth,\n                                            &protection,\n                                            &user_tag) == KERN_SUCCESS) &&\n               try_address == original_try_address &&\n               (protection & VM_PROT_READ) != 0 &&\n               user_tag == VM_MEMORY_STACK) {\n      total_region_size += region_size;\n    }\n\n    if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) {\n      // Tolerate KERN_INVALID_ADDRESS because it will be returned when there\n      // are no more regions in the map at or above the specified |try_address|.\n      CRASHPAD_RAW_LOG_ERROR(kr, \"MachVMRegionRecurseDeepest\");\n    }\n  }\n\n  *stack_region_size = total_region_size;\n  return region_base;\n}\n\n//! \\brief Write data around \\a address to intermediate dump. Must be called\n//!    from within a ScopedArray.\nvoid MaybeCaptureMemoryAround(IOSIntermediateDumpWriter* writer,\n                              uint64_t address) {\n  constexpr uint64_t non_address_offset = 0x10000;\n  if (address < non_address_offset)\n    return;\n\n  constexpr uint64_t max_address = std::numeric_limits<uint64_t>::max();\n\n  if (address > max_address - non_address_offset)\n    return;\n\n  constexpr uint64_t kRegisterByteOffset = 128;\n  const uint64_t target = address - kRegisterByteOffset;\n  constexpr uint64_t size = 512;\n  static_assert(kRegisterByteOffset <= size / 2, \"negative offset too large\");\n\n  IOSIntermediateDumpWriter::ScopedArrayMap memory_region(writer);\n  WriteProperty(\n      writer, IntermediateDumpKey::kThreadContextMemoryRegionAddress, &target);\n  // Don't use WritePropertyBytes, this one will fail regularly if |target|\n  // cannot be read.\n  writer->AddPropertyBytes(IntermediateDumpKey::kThreadContextMemoryRegionData,\n                           reinterpret_cast<const void*>(target),\n                           size);\n}\n\nvoid CaptureMemoryPointedToByThreadState(IOSIntermediateDumpWriter* writer,\n                                         thread_state_type thread_state) {\n  IOSIntermediateDumpWriter::ScopedArray memory_regions(\n      writer, IntermediateDumpKey::kThreadContextMemoryRegions);\n\n#if defined(ARCH_CPU_X86_64)\n  MaybeCaptureMemoryAround(writer, thread_state.__rax);\n  MaybeCaptureMemoryAround(writer, thread_state.__rbx);\n  MaybeCaptureMemoryAround(writer, thread_state.__rcx);\n  MaybeCaptureMemoryAround(writer, thread_state.__rdx);\n  MaybeCaptureMemoryAround(writer, thread_state.__rdi);\n  MaybeCaptureMemoryAround(writer, thread_state.__rsi);\n  MaybeCaptureMemoryAround(writer, thread_state.__rbp);\n  MaybeCaptureMemoryAround(writer, thread_state.__r8);\n  MaybeCaptureMemoryAround(writer, thread_state.__r9);\n  MaybeCaptureMemoryAround(writer, thread_state.__r10);\n  MaybeCaptureMemoryAround(writer, thread_state.__r11);\n  MaybeCaptureMemoryAround(writer, thread_state.__r12);\n  MaybeCaptureMemoryAround(writer, thread_state.__r13);\n  MaybeCaptureMemoryAround(writer, thread_state.__r14);\n  MaybeCaptureMemoryAround(writer, thread_state.__r15);\n  MaybeCaptureMemoryAround(writer, thread_state.__rip);\n#elif defined(ARCH_CPU_ARM_FAMILY)\n  MaybeCaptureMemoryAround(writer, arm_thread_state64_get_pc(thread_state));\n  for (size_t i = 0; i < std::size(thread_state.__x); ++i) {\n    MaybeCaptureMemoryAround(writer, thread_state.__x[i]);\n  }\n#endif\n}\n\nvoid WriteCrashpadSimpleAnnotationsDictionary(IOSIntermediateDumpWriter* writer,\n                                              CrashpadInfo* crashpad_info) {\n  if (!crashpad_info->simple_annotations())\n    return;\n\n  ScopedVMRead<SimpleStringDictionary> simple_annotations;\n  if (!simple_annotations.Read(crashpad_info->simple_annotations())) {\n    CRASHPAD_RAW_LOG(\"Unable to read simple annotations.\");\n    return;\n  }\n\n  const size_t count = simple_annotations->GetCount();\n  if (!count)\n    return;\n\n  IOSIntermediateDumpWriter::ScopedArray annotations_array(\n      writer, IntermediateDumpKey::kAnnotationsSimpleMap);\n\n  SimpleStringDictionary::Entry* entries =\n      reinterpret_cast<SimpleStringDictionary::Entry*>(\n          simple_annotations.get());\n  for (size_t index = 0; index < count; index++) {\n    IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer);\n    const auto& entry = entries[index];\n    size_t key_length = strnlen(entry.key, sizeof(entry.key));\n    WritePropertyBytes(writer,\n                       IntermediateDumpKey::kAnnotationName,\n                       reinterpret_cast<const void*>(entry.key),\n                       key_length);\n    size_t value_length = strnlen(entry.value, sizeof(entry.value));\n    WritePropertyBytes(writer,\n                       IntermediateDumpKey::kAnnotationValue,\n                       reinterpret_cast<const void*>(entry.value),\n                       value_length);\n  }\n}\n\nvoid WriteAppleCrashReporterAnnotations(\n    IOSIntermediateDumpWriter* writer,\n    crashreporter_annotations_t* crash_info) {\n  // It seems prudent to enforce some limit. Different users of\n  // CRSetCrashLogMessage and CRSetCrashLogMessage2, apparently the private\n  // <CrashReporterClient.h> functions used to set message and message2, use\n  // different buffer lengths. dyld-1231.3 libdyld/dyld_process_info.cpp has\n  // `static char sCrashReporterInfo[4096]`, which seems like a reasonable\n  // limit.\n  constexpr size_t kMaxMessageSize = 4096;\n\n  IOSIntermediateDumpWriter::ScopedMap annotation_map(\n      writer, IntermediateDumpKey::kAnnotationsCrashInfo);\n  if (crash_info->message) {\n    const size_t message_len = strnlen(\n        reinterpret_cast<const char*>(crash_info->message), kMaxMessageSize);\n    WritePropertyBytes(writer,\n                       IntermediateDumpKey::kAnnotationsCrashInfoMessage1,\n                       reinterpret_cast<const void*>(crash_info->message),\n                       message_len);\n  }\n  if (crash_info->message2) {\n    const size_t message_len = strnlen(\n        reinterpret_cast<const char*>(crash_info->message2), kMaxMessageSize);\n    WritePropertyBytes(writer,\n                       IntermediateDumpKey::kAnnotationsCrashInfoMessage2,\n                       reinterpret_cast<const void*>(crash_info->message2),\n                       message_len);\n  }\n}\n\n}  // namespace\n\n// static\nvoid InProcessIntermediateDumpHandler::WriteHeader(\n    IOSIntermediateDumpWriter* writer) {\n  static constexpr uint8_t version = 1;\n  WriteProperty(writer, IntermediateDumpKey::kVersion, &version);\n}\n\n// static\nvoid InProcessIntermediateDumpHandler::WriteProcessInfo(\n    IOSIntermediateDumpWriter* writer,\n    const std::map<std::string, std::string>& annotations) {\n  IOSIntermediateDumpWriter::ScopedMap process_map(\n      writer, IntermediateDumpKey::kProcessInfo);\n\n  timeval snapshot_time;\n  if (gettimeofday(&snapshot_time, nullptr) == 0) {\n    WriteProperty(writer, IntermediateDumpKey::kSnapshotTime, &snapshot_time);\n  } else {\n    CRASHPAD_RAW_LOG(\"gettimeofday\");\n  }\n\n  // Used by pid, parent pid and snapshot time.\n  kinfo_proc kern_proc_info;\n  int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};\n  size_t len = sizeof(kern_proc_info);\n  if (sysctl(mib, std::size(mib), &kern_proc_info, &len, nullptr, 0) == 0) {\n    WriteProperty(\n        writer, IntermediateDumpKey::kPID, &kern_proc_info.kp_proc.p_pid);\n    WriteProperty(writer,\n                  IntermediateDumpKey::kParentPID,\n                  &kern_proc_info.kp_eproc.e_ppid);\n    WriteProperty(writer,\n                  IntermediateDumpKey::kStartTime,\n                  &kern_proc_info.kp_proc.p_starttime);\n  } else {\n    CRASHPAD_RAW_LOG(\"sysctl kern_proc_info\");\n  }\n\n  // Used by user time and system time.\n  mach_task_basic_info task_basic_info;\n  mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT;\n  kern_return_t kr = task_info(mach_task_self(),\n                               MACH_TASK_BASIC_INFO,\n                               reinterpret_cast<task_info_t>(&task_basic_info),\n                               &task_basic_info_count);\n  if (kr == KERN_SUCCESS) {\n    IOSIntermediateDumpWriter::ScopedMap task_info(\n        writer, IntermediateDumpKey::kTaskBasicInfo);\n\n    WriteProperty(\n        writer, IntermediateDumpKey::kUserTime, &task_basic_info.user_time);\n    WriteProperty(\n        writer, IntermediateDumpKey::kSystemTime, &task_basic_info.system_time);\n  } else {\n    CRASHPAD_RAW_LOG(\"task_info task_basic_info\");\n  }\n\n  task_thread_times_info_data_t task_thread_times;\n  mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT;\n  kr = task_info(mach_task_self(),\n                 TASK_THREAD_TIMES_INFO,\n                 reinterpret_cast<task_info_t>(&task_thread_times),\n                 &task_thread_times_count);\n  if (kr == KERN_SUCCESS) {\n    IOSIntermediateDumpWriter::ScopedMap task_thread_times_map(\n        writer, IntermediateDumpKey::kTaskThreadTimes);\n\n    WriteProperty(\n        writer, IntermediateDumpKey::kUserTime, &task_thread_times.user_time);\n    WriteProperty(writer,\n                  IntermediateDumpKey::kSystemTime,\n                  &task_thread_times.system_time);\n  } else {\n    CRASHPAD_RAW_LOG(\"task_info thread_times_info\");\n  }\n\n  if (!annotations.empty()) {\n    IOSIntermediateDumpWriter::ScopedArray simple_annotations_array(\n        writer, IntermediateDumpKey::kAnnotationsSimpleMap);\n    for (const auto& annotation_pair : annotations) {\n      const std::string& key = annotation_pair.first;\n      const std::string& value = annotation_pair.second;\n      IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer);\n      WriteProperty(writer,\n                    IntermediateDumpKey::kAnnotationName,\n                    key.c_str(),\n                    key.length());\n      WriteProperty(writer,\n                    IntermediateDumpKey::kAnnotationValue,\n                    value.c_str(),\n                    value.length());\n    }\n  }\n}\n\n// static\nvoid InProcessIntermediateDumpHandler::WriteSystemInfo(\n    IOSIntermediateDumpWriter* writer,\n    const IOSSystemDataCollector& system_data,\n    uint64_t report_time_nanos) {\n  IOSIntermediateDumpWriter::ScopedMap system_map(\n      writer, IntermediateDumpKey::kSystemInfo);\n\n  const std::string& machine_description = system_data.MachineDescription();\n  WriteProperty(writer,\n                IntermediateDumpKey::kMachineDescription,\n                machine_description.c_str(),\n                machine_description.length());\n  int os_version_major;\n  int os_version_minor;\n  int os_version_bugfix;\n  system_data.OSVersion(\n      &os_version_major, &os_version_minor, &os_version_bugfix);\n  WriteProperty(\n      writer, IntermediateDumpKey::kOSVersionMajor, &os_version_major);\n  WriteProperty(\n      writer, IntermediateDumpKey::kOSVersionMinor, &os_version_minor);\n  WriteProperty(\n      writer, IntermediateDumpKey::kOSVersionBugfix, &os_version_bugfix);\n  const std::string& os_version_build = system_data.Build();\n  WriteProperty(writer,\n                IntermediateDumpKey::kOSVersionBuild,\n                os_version_build.c_str(),\n                os_version_build.length());\n\n  int cpu_count = system_data.ProcessorCount();\n  WriteProperty(writer, IntermediateDumpKey::kCpuCount, &cpu_count);\n  const std::string& cpu_vendor = system_data.CPUVendor();\n  WriteProperty(writer,\n                IntermediateDumpKey::kCpuVendor,\n                cpu_vendor.c_str(),\n                cpu_vendor.length());\n\n  bool has_daylight_saving_time = system_data.HasDaylightSavingTime();\n  WriteProperty(writer,\n                IntermediateDumpKey::kHasDaylightSavingTime,\n                &has_daylight_saving_time);\n  bool is_daylight_saving_time = system_data.IsDaylightSavingTime();\n  WriteProperty(writer,\n                IntermediateDumpKey::kIsDaylightSavingTime,\n                &is_daylight_saving_time);\n  int standard_offset_seconds = system_data.StandardOffsetSeconds();\n  WriteProperty(writer,\n                IntermediateDumpKey::kStandardOffsetSeconds,\n                &standard_offset_seconds);\n  int daylight_offset_seconds = system_data.DaylightOffsetSeconds();\n  WriteProperty(writer,\n                IntermediateDumpKey::kDaylightOffsetSeconds,\n                &daylight_offset_seconds);\n  const std::string& standard_name = system_data.StandardName();\n  WriteProperty(writer,\n                IntermediateDumpKey::kStandardName,\n                standard_name.c_str(),\n                standard_name.length());\n  const std::string& daylight_name = system_data.DaylightName();\n  WriteProperty(writer,\n                IntermediateDumpKey::kDaylightName,\n                daylight_name.c_str(),\n                daylight_name.length());\n  uint64_t address_mask = system_data.AddressMask();\n  WriteProperty(writer, IntermediateDumpKey::kAddressMask, &address_mask);\n\n  vm_size_t page_size;\n  host_page_size(mach_host_self(), &page_size);\n  WriteProperty(writer, IntermediateDumpKey::kPageSize, &page_size);\n\n  mach_msg_type_number_t host_size =\n      sizeof(vm_statistics_data_t) / sizeof(integer_t);\n  vm_statistics_data_t vm_stat;\n  kern_return_t kr = host_statistics(mach_host_self(),\n                                     HOST_VM_INFO,\n                                     reinterpret_cast<host_info_t>(&vm_stat),\n                                     &host_size);\n  if (kr == KERN_SUCCESS) {\n    IOSIntermediateDumpWriter::ScopedMap vm_stat_map(\n        writer, IntermediateDumpKey::kVMStat);\n\n    WriteProperty(writer, IntermediateDumpKey::kActive, &vm_stat.active_count);\n    WriteProperty(\n        writer, IntermediateDumpKey::kInactive, &vm_stat.inactive_count);\n    WriteProperty(writer, IntermediateDumpKey::kWired, &vm_stat.wire_count);\n    WriteProperty(writer, IntermediateDumpKey::kFree, &vm_stat.free_count);\n  } else {\n    CRASHPAD_RAW_LOG(\"host_statistics\");\n  }\n\n  uint64_t crashpad_uptime_nanos =\n      report_time_nanos - system_data.InitializationTime();\n  WriteProperty(\n      writer, IntermediateDumpKey::kCrashpadUptime, &crashpad_uptime_nanos);\n}\n\n// static\nvoid InProcessIntermediateDumpHandler::WriteThreadInfo(\n    IOSIntermediateDumpWriter* writer,\n    const uint64_t* frames,\n    const size_t num_frames) {\n  IOSIntermediateDumpWriter::ScopedArray thread_array(\n      writer, IntermediateDumpKey::kThreads);\n\n  // Exception thread ID.\n#if defined(ARCH_CPU_ARM64)\n  uint64_t exception_thread_id = 0;\n#endif\n  thread_identifier_info identifier_info;\n  mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;\n  kern_return_t kr =\n      thread_info(mach_thread_self(),\n                  THREAD_IDENTIFIER_INFO,\n                  reinterpret_cast<thread_info_t>(&identifier_info),\n                  &count);\n  if (kr == KERN_SUCCESS) {\n#if defined(ARCH_CPU_ARM64)\n    exception_thread_id = identifier_info.thread_id;\n#endif\n  } else {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"thread_info::THREAD_IDENTIFIER_INFO\");\n  }\n\n  mach_msg_type_number_t thread_count = 0;\n  thread_act_array_t threads;\n  kr = task_threads(mach_task_self(), &threads, &thread_count);\n  if (kr != KERN_SUCCESS) {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"task_threads\");\n  }\n  ScopedTaskThreads threads_vm_owner(threads, thread_count);\n\n  for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) {\n    IOSIntermediateDumpWriter::ScopedArrayMap thread_map(writer);\n    thread_t thread = threads[thread_index];\n\n    thread_basic_info basic_info;\n    count = THREAD_BASIC_INFO_COUNT;\n    kr = thread_info(thread,\n                     THREAD_BASIC_INFO,\n                     reinterpret_cast<thread_info_t>(&basic_info),\n                     &count);\n    if (kr == KERN_SUCCESS) {\n      WriteProperty(writer,\n                    IntermediateDumpKey::kSuspendCount,\n                    &basic_info.suspend_count);\n    } else {\n      CRASHPAD_RAW_LOG_ERROR(kr, \"thread_info::THREAD_BASIC_INFO\");\n    }\n\n    thread_extended_info extended_info;\n    count = THREAD_EXTENDED_INFO_COUNT;\n    kr = thread_info(thread,\n                     THREAD_EXTENDED_INFO,\n                     reinterpret_cast<thread_info_t>(&extended_info),\n                     &count);\n    if (kr == KERN_SUCCESS) {\n      WritePropertyBytes(\n          writer,\n          IntermediateDumpKey::kThreadName,\n          reinterpret_cast<const void*>(extended_info.pth_name),\n          strnlen(extended_info.pth_name, sizeof(extended_info.pth_name)));\n    } else {\n      CRASHPAD_RAW_LOG_ERROR(kr, \"thread_info::THREAD_EXTENDED_INFO\");\n    }\n\n    thread_precedence_policy precedence;\n    count = THREAD_PRECEDENCE_POLICY_COUNT;\n    boolean_t get_default = FALSE;\n    kr = thread_policy_get(thread,\n                           THREAD_PRECEDENCE_POLICY,\n                           reinterpret_cast<thread_policy_t>(&precedence),\n                           &count,\n                           &get_default);\n    if (kr == KERN_SUCCESS) {\n      WriteProperty(\n          writer, IntermediateDumpKey::kPriority, &precedence.importance);\n    } else {\n      CRASHPAD_RAW_LOG_ERROR(kr, \"thread_policy_get\");\n    }\n\n    // Thread ID.\n#if defined(ARCH_CPU_ARM64)\n    uint64_t thread_id;\n#endif\n    count = THREAD_IDENTIFIER_INFO_COUNT;\n    kr = thread_info(thread,\n                     THREAD_IDENTIFIER_INFO,\n                     reinterpret_cast<thread_info_t>(&identifier_info),\n                     &count);\n    if (kr == KERN_SUCCESS) {\n#if defined(ARCH_CPU_ARM64)\n      thread_id = identifier_info.thread_id;\n#endif\n      WriteProperty(\n          writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id);\n      WriteProperty(writer,\n                    IntermediateDumpKey::kThreadDataAddress,\n                    &identifier_info.thread_handle);\n    } else {\n      CRASHPAD_RAW_LOG_ERROR(kr, \"thread_info::THREAD_IDENTIFIER_INFO\");\n    }\n\n    // thread_snapshot_ios_intermediate_dump::GenerateStackMemoryFromFrames is\n    // only implemented for arm64, so no x86_64 block here.\n#if defined(ARCH_CPU_ARM64)\n    // For uncaught NSExceptions, use the frames passed from the system rather\n    // than the current thread state.\n    if (num_frames > 0 && exception_thread_id == thread_id) {\n      WriteProperty(writer,\n                    IntermediateDumpKey::kThreadUncaughtNSExceptionFrames,\n                    frames,\n                    num_frames);\n      continue;\n    }\n#endif\n\n#if defined(ARCH_CPU_X86_64)\n    x86_thread_state64_t thread_state;\n    x86_float_state64_t float_state;\n    x86_debug_state64_t debug_state;\n    mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;\n    mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT;\n    mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT;\n#elif defined(ARCH_CPU_ARM64)\n    arm_thread_state64_t thread_state;\n    arm_neon_state64_t float_state;\n    arm_debug_state64_t debug_state;\n    mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;\n    mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;\n    mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT;\n#endif\n\n    kr = thread_get_state(thread,\n                          kThreadStateFlavor,\n                          reinterpret_cast<thread_state_t>(&thread_state),\n                          &thread_state_count);\n    if (kr != KERN_SUCCESS) {\n      CRASHPAD_RAW_LOG_ERROR(kr, \"thread_get_state::kThreadStateFlavor\");\n    }\n    WriteProperty(writer, IntermediateDumpKey::kThreadState, &thread_state);\n\n    kr = thread_get_state(thread,\n                          kFloatStateFlavor,\n                          reinterpret_cast<thread_state_t>(&float_state),\n                          &float_state_count);\n    if (kr != KERN_SUCCESS) {\n      CRASHPAD_RAW_LOG_ERROR(kr, \"thread_get_state::kFloatStateFlavor\");\n    }\n    WriteProperty(writer, IntermediateDumpKey::kFloatState, &float_state);\n\n    kr = thread_get_state(thread,\n                          kDebugStateFlavor,\n                          reinterpret_cast<thread_state_t>(&debug_state),\n                          &debug_state_count);\n    if (kr != KERN_SUCCESS) {\n      CRASHPAD_RAW_LOG_ERROR(kr, \"thread_get_state::kDebugStateFlavor\");\n    }\n    WriteProperty(writer, IntermediateDumpKey::kDebugState, &debug_state);\n\n#if defined(ARCH_CPU_X86_64)\n    vm_address_t stack_pointer = thread_state.__rsp;\n#elif defined(ARCH_CPU_ARM64)\n    vm_address_t stack_pointer = arm_thread_state64_get_sp(thread_state);\n#endif\n\n    vm_size_t stack_region_size;\n    const vm_address_t stack_region_address =\n        CalculateStackRegion(stack_pointer, &stack_region_size);\n    WriteProperty(writer,\n                  IntermediateDumpKey::kStackRegionAddress,\n                  &stack_region_address);\n    WritePropertyBytes(writer,\n                       IntermediateDumpKey::kStackRegionData,\n                       reinterpret_cast<const void*>(stack_region_address),\n                       stack_region_size);\n\n    // Grab extra memory from context.\n    CaptureMemoryPointedToByThreadState(writer, thread_state);\n  }\n}\n\n// static\nvoid InProcessIntermediateDumpHandler::WriteModuleInfo(\n    IOSIntermediateDumpWriter* writer) {\n#ifndef ARCH_CPU_64_BITS\n#error Only 64-bit Mach-O is supported\n#endif\n\n  IOSIntermediateDumpWriter::ScopedArray module_array(\n      writer, IntermediateDumpKey::kModules);\n\n  task_dyld_info_data_t dyld_info;\n  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;\n  kern_return_t kr = task_info(mach_task_self(),\n                               TASK_DYLD_INFO,\n                               reinterpret_cast<task_info_t>(&dyld_info),\n                               &count);\n  if (kr != KERN_SUCCESS) {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"task_info\");\n  }\n\n  ScopedVMRead<dyld_all_image_infos> image_infos;\n  if (!image_infos.Read(dyld_info.all_image_info_addr)) {\n    CRASHPAD_RAW_LOG(\"Unable to dyld_info.all_image_info_addr\");\n    return;\n  }\n\n  uint32_t image_count = image_infos->infoArrayCount;\n  const dyld_image_info* image_array = image_infos->infoArray;\n  for (int32_t image_index = image_count - 1; image_index >= 0; --image_index) {\n    IOSIntermediateDumpWriter::ScopedArrayMap modules(writer);\n    ScopedVMRead<dyld_image_info> image;\n    if (!image.Read(&image_array[image_index])) {\n      CRASHPAD_RAW_LOG(\"Unable to dyld_image_info\");\n      continue;\n    }\n\n    if (image->imageFilePath) {\n      WritePropertyCString(\n          writer, IntermediateDumpKey::kName, PATH_MAX, image->imageFilePath);\n    }\n    uint64_t address = FromPointerCast<uint64_t>(image->imageLoadAddress);\n    WriteProperty(writer, IntermediateDumpKey::kAddress, &address);\n    WriteProperty(\n        writer, IntermediateDumpKey::kTimestamp, &image->imageFileModDate);\n    WriteModuleInfoAtAddress(writer, address, false /*is_dyld=false*/);\n  }\n\n  {\n    IOSIntermediateDumpWriter::ScopedArrayMap modules(writer);\n    if (image_infos->dyldPath) {\n      WritePropertyCString(\n          writer, IntermediateDumpKey::kName, PATH_MAX, image_infos->dyldPath);\n    }\n    uint64_t address =\n        FromPointerCast<uint64_t>(image_infos->dyldImageLoadAddress);\n    WriteProperty(writer, IntermediateDumpKey::kAddress, &address);\n    WriteModuleInfoAtAddress(writer, address, true /*is_dyld=true*/);\n  }\n}\n\n// static\nvoid InProcessIntermediateDumpHandler::WriteExceptionFromSignal(\n    IOSIntermediateDumpWriter* writer,\n    const IOSSystemDataCollector& system_data,\n    siginfo_t* siginfo,\n    ucontext_t* context) {\n  IOSIntermediateDumpWriter::ScopedMap signal_exception_map(\n      writer, IntermediateDumpKey::kSignalException);\n\n  WriteProperty(writer, IntermediateDumpKey::kSignalNumber, &siginfo->si_signo);\n  WriteProperty(writer, IntermediateDumpKey::kSignalCode, &siginfo->si_code);\n  WriteProperty(writer, IntermediateDumpKey::kSignalAddress, &siginfo->si_addr);\n\n#if defined(ARCH_CPU_X86_64)\n  x86_thread_state64_t thread_state = context->uc_mcontext->__ss;\n  x86_float_state64_t float_state = context->uc_mcontext->__fs;\n#elif defined(ARCH_CPU_ARM64)\n  arm_thread_state64_t thread_state = context->uc_mcontext->__ss;\n  arm_neon_state64_t float_state = context->uc_mcontext->__ns;\n#else\n#error Port to your CPU architecture\n#endif\n  WriteProperty(writer, IntermediateDumpKey::kThreadState, &thread_state);\n  WriteProperty(writer, IntermediateDumpKey::kFloatState, &float_state);\n  CaptureMemoryPointedToByThreadState(writer, thread_state);\n\n  // Thread ID.\n  thread_identifier_info identifier_info;\n  mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;\n  kern_return_t kr =\n      thread_info(mach_thread_self(),\n                  THREAD_IDENTIFIER_INFO,\n                  reinterpret_cast<thread_info_t>(&identifier_info),\n                  &count);\n  if (kr == KERN_SUCCESS) {\n    WriteProperty(\n        writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id);\n  } else {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"thread_info::self\");\n  }\n}\n\n// static\nvoid InProcessIntermediateDumpHandler::WriteExceptionFromMachException(\n    IOSIntermediateDumpWriter* writer,\n    exception_behavior_t behavior,\n    thread_t exception_thread,\n    exception_type_t exception,\n    const mach_exception_data_type_t* code,\n    mach_msg_type_number_t code_count,\n    thread_state_flavor_t flavor,\n    ConstThreadState state,\n    mach_msg_type_number_t state_count) {\n  IOSIntermediateDumpWriter::ScopedMap mach_exception_map(\n      writer, IntermediateDumpKey::kMachException);\n\n  WriteProperty(writer, IntermediateDumpKey::kException, &exception);\n  WriteProperty(writer, IntermediateDumpKey::kCodes, code, code_count);\n  WriteProperty(writer, IntermediateDumpKey::kFlavor, &flavor);\n  WritePropertyBytes(writer,\n                     IntermediateDumpKey::kState,\n                     state,\n                     state_count * sizeof(uint32_t));\n\n  thread_identifier_info identifier_info;\n  mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;\n  kern_return_t kr =\n      thread_info(exception_thread,\n                  THREAD_IDENTIFIER_INFO,\n                  reinterpret_cast<thread_info_t>(&identifier_info),\n                  &count);\n  if (kr == KERN_SUCCESS) {\n    WriteProperty(\n        writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id);\n  } else {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"thread_info\");\n  }\n}\n\n// static\nvoid InProcessIntermediateDumpHandler::WriteExceptionFromNSException(\n    IOSIntermediateDumpWriter* writer) {\n  IOSIntermediateDumpWriter::ScopedMap nsexception_map(\n      writer, IntermediateDumpKey::kNSException);\n\n  thread_identifier_info identifier_info;\n  mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;\n  kern_return_t kr =\n      thread_info(mach_thread_self(),\n                  THREAD_IDENTIFIER_INFO,\n                  reinterpret_cast<thread_info_t>(&identifier_info),\n                  &count);\n  if (kr == KERN_SUCCESS) {\n    WriteProperty(\n        writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id);\n  } else {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"thread_info::self\");\n  }\n}\n\nvoid InProcessIntermediateDumpHandler::WriteModuleInfoAtAddress(\n    IOSIntermediateDumpWriter* writer,\n    uint64_t address,\n    bool is_dyld) {\n  ScopedVMRead<mach_header_64> header;\n  if (!header.Read(address) || header->magic != MH_MAGIC_64) {\n    CRASHPAD_RAW_LOG(\"Invalid module header\");\n    return;\n  }\n\n  const load_command* unsafe_command_ptr =\n      reinterpret_cast<const load_command*>(\n          reinterpret_cast<const mach_header_64*>(address) + 1);\n\n  // Rather than using an individual ScopedVMRead for each load_command, load\n  // the entire block of commands at once.\n  ScopedVMRead<char> all_commands;\n  if (!all_commands.Read(unsafe_command_ptr, header->sizeofcmds)) {\n    CRASHPAD_RAW_LOG(\"Unable to read module load_commands.\");\n    return;\n  }\n\n  // All the *_vm_read_ptr variables in the load_command loop below have been\n  // vm_read in `all_commands` above, and may be dereferenced without additional\n  // ScopedVMReads.\n  const load_command* command_vm_read_ptr =\n      reinterpret_cast<const load_command*>(all_commands.get());\n\n  // Make sure that the basic load command structure doesn’t overflow the\n  // space allotted for load commands, as well as iterating through ncmds.\n  vm_size_t slide = 0;\n  for (uint32_t cmd_index = 0, cumulative_cmd_size = 0;\n       cmd_index < header->ncmds && cumulative_cmd_size < header->sizeofcmds;\n       ++cmd_index) {\n    if (command_vm_read_ptr->cmd == LC_SEGMENT_64) {\n      const segment_command_64* segment_vm_read_ptr =\n          reinterpret_cast<const segment_command_64*>(command_vm_read_ptr);\n      if (strcmp(segment_vm_read_ptr->segname, SEG_TEXT) == 0) {\n        WriteProperty(\n            writer, IntermediateDumpKey::kSize, &segment_vm_read_ptr->vmsize);\n        slide = address - segment_vm_read_ptr->vmaddr;\n      } else if (strcmp(segment_vm_read_ptr->segname, SEG_DATA) == 0 ||\n                 // dyld puts __crash_info in __DATA_DIRTY.\n                 strcmp(segment_vm_read_ptr->segname, \"__DATA_DIRTY\") == 0) {\n        WriteDataSegmentAnnotations(writer, segment_vm_read_ptr, slide);\n      }\n    } else if (command_vm_read_ptr->cmd == LC_ID_DYLIB) {\n      const dylib_command* dylib_vm_read_ptr =\n          reinterpret_cast<const dylib_command*>(command_vm_read_ptr);\n      WriteProperty(writer,\n                    IntermediateDumpKey::kDylibCurrentVersion,\n                    &dylib_vm_read_ptr->dylib.current_version);\n    } else if (command_vm_read_ptr->cmd == LC_SOURCE_VERSION) {\n      const source_version_command* source_version_vm_read_ptr =\n          reinterpret_cast<const source_version_command*>(command_vm_read_ptr);\n      WriteProperty(writer,\n                    IntermediateDumpKey::kSourceVersion,\n                    &source_version_vm_read_ptr->version);\n    } else if (command_vm_read_ptr->cmd == LC_UUID) {\n      const uuid_command* uuid_vm_read_ptr =\n          reinterpret_cast<const uuid_command*>(command_vm_read_ptr);\n      WriteProperty(\n          writer, IntermediateDumpKey::kUUID, &uuid_vm_read_ptr->uuid);\n    }\n\n    cumulative_cmd_size += command_vm_read_ptr->cmdsize;\n    command_vm_read_ptr = reinterpret_cast<const load_command*>(\n        reinterpret_cast<const uint8_t*>(command_vm_read_ptr) +\n        command_vm_read_ptr->cmdsize);\n  }\n\n  WriteProperty(writer, IntermediateDumpKey::kFileType, &header->filetype);\n}\n\nvoid InProcessIntermediateDumpHandler::WriteDataSegmentAnnotations(\n    IOSIntermediateDumpWriter* writer,\n    const segment_command_64* segment_vm_read_ptr,\n    vm_size_t slide) {\n  const section_64* section_vm_read_ptr = reinterpret_cast<const section_64*>(\n      reinterpret_cast<uint64_t>(segment_vm_read_ptr) +\n      sizeof(segment_command_64));\n  for (uint32_t sect_index = 0; sect_index <= segment_vm_read_ptr->nsects;\n       ++sect_index) {\n    if (strcmp(section_vm_read_ptr->sectname, \"crashpad_info\") == 0) {\n      if (section_vm_read_ptr->size >= sizeof(CrashpadInfo)) {\n        ScopedVMRead<CrashpadInfo> crashpad_info;\n        if (crashpad_info.Read(section_vm_read_ptr->addr + slide) &&\n            crashpad_info->size() <= section_vm_read_ptr->size &&\n            crashpad_info->size() >= sizeof(CrashpadInfo) &&\n            crashpad_info->signature() == CrashpadInfo::kSignature &&\n            crashpad_info->version() == 1) {\n          WriteCrashpadAnnotationsList(writer, crashpad_info.get());\n          WriteCrashpadSimpleAnnotationsDictionary(writer, crashpad_info.get());\n          WriteCrashpadExtraMemoryRanges(writer, crashpad_info.get());\n          WriteCrashpadIntermediateDumpExtraMemoryRanges(writer,\n                                                         crashpad_info.get());\n        }\n      }\n    } else if (strcmp(section_vm_read_ptr->sectname, \"__crash_info\") == 0) {\n      if (section_vm_read_ptr->size >= sizeof(crashreporter_annotations_t)) {\n        ScopedVMRead<crashreporter_annotations_t> crash_info;\n        if (crash_info.Read(section_vm_read_ptr->addr + slide) &&\n            (crash_info->version == 4 || crash_info->version == 5 ||\n             crash_info->version == 7)) {\n          WriteAppleCrashReporterAnnotations(writer, crash_info.get());\n        }\n      }\n    }\n    section_vm_read_ptr = reinterpret_cast<const section_64*>(\n        reinterpret_cast<uint64_t>(section_vm_read_ptr) + sizeof(section_64));\n  }\n}\n\nvoid InProcessIntermediateDumpHandler::WriteCrashpadAnnotationsList(\n    IOSIntermediateDumpWriter* writer,\n    CrashpadInfo* crashpad_info) {\n  if (!crashpad_info->annotations_list()) {\n    return;\n  }\n  ScopedVMRead<AnnotationList> annotation_list;\n  if (!annotation_list.Read(crashpad_info->annotations_list())) {\n    CRASHPAD_RAW_LOG(\"Unable to read annotations list object\");\n    return;\n  }\n\n  IOSIntermediateDumpWriter::ScopedArray annotations_array(\n      writer, IntermediateDumpKey::kAnnotationObjects);\n  ScopedVMRead<Annotation> current;\n\n  // Use vm_read() to ensure that the linked-list AnnotationList head (which is\n  // a dummy node of type kInvalid) is valid and copy its memory into a\n  // newly-allocated buffer.\n  //\n  // In the case where the pointer has been clobbered or the memory range is not\n  // readable, skip reading all the Annotations.\n  if (!current.Read(annotation_list->head())) {\n    CRASHPAD_RAW_LOG(\"Unable to read annotation\");\n    return;\n  }\n\n  for (size_t index = 0;\n       current->link_node() != annotation_list.get()->tail_pointer() &&\n       index < kMaxNumberOfAnnotations;\n       ++index) {\n    ScopedVMRead<Annotation> node;\n\n    // Like above, use vm_read() to ensure that the node in the linked list is\n    // valid and copy its memory into a newly-allocated buffer.\n    //\n    // In the case where the pointer has been clobbered or the memory range is\n    // not readable, skip reading this and all further Annotations.\n    if (!node.Read(current->link_node())) {\n      CRASHPAD_RAW_LOG(\"Unable to read annotation\");\n      return;\n    }\n    current.Read(current->link_node());\n\n    if (node->size() == 0)\n      continue;\n\n    if (node->size() > Annotation::kValueMaxSize) {\n      CRASHPAD_RAW_LOG(\"Incorrect annotation length\");\n      continue;\n    }\n\n    // For Annotations which support guarding reads from concurrent writes, map\n    // their memory read-write using vm_remap(), then declare a ScopedSpinGuard\n    // which lives for the duration of the read.\n    ScopedVMMap<Annotation> mapped_node;\n    std::optional<ScopedSpinGuard> annotation_guard;\n    if (node->concurrent_access_guard_mode() ==\n        Annotation::ConcurrentAccessGuardMode::kScopedSpinGuard) {\n      constexpr vm_prot_t kDesiredProtection = VM_PROT_WRITE | VM_PROT_READ;\n      if (!mapped_node.Map(node.get()) ||\n          (mapped_node.CurrentProtection() & kDesiredProtection) !=\n              kDesiredProtection) {\n        CRASHPAD_RAW_LOG(\"Unable to map annotation\");\n\n        // Skip this annotation rather than giving up entirely, since the linked\n        // node should still be valid.\n        continue;\n      }\n\n      // TODO(https://crbug.com/crashpad/438): Pass down a `params` object into\n      // this method to optionally enable a timeout here.\n      constexpr uint64_t kTimeoutNanoseconds = 0;\n      annotation_guard =\n          mapped_node->TryCreateScopedSpinGuard(kTimeoutNanoseconds);\n      if (!annotation_guard) {\n        // This is expected if the process is writing to the Annotation, so\n        // don't log here and skip the annotation.\n        continue;\n      }\n    }\n\n    IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer);\n    WritePropertyCString(writer,\n                         IntermediateDumpKey::kAnnotationName,\n                         Annotation::kNameMaxLength,\n                         reinterpret_cast<const char*>(node->name()));\n    WritePropertyBytes(writer,\n                       IntermediateDumpKey::kAnnotationValue,\n                       reinterpret_cast<const void*>(node->value()),\n                       node->size());\n    Annotation::Type type = node->type();\n    WritePropertyBytes(writer,\n                       IntermediateDumpKey::kAnnotationType,\n                       reinterpret_cast<const void*>(&type),\n                       sizeof(type));\n  }\n}\n\nvoid InProcessIntermediateDumpHandler::WriteCrashpadExtraMemoryRanges(\n    IOSIntermediateDumpWriter* writer,\n    CrashpadInfo* crashpad_info) {\n  if (!crashpad_info->extra_memory_ranges()) {\n    return;\n  }\n\n  ScopedVMRead<SimpleAddressRangeBag> extra_memory_ranges;\n  if (!extra_memory_ranges.Read(crashpad_info->extra_memory_ranges())) {\n    CRASHPAD_RAW_LOG(\"Unable to read extra memory ranges object\");\n    return;\n  }\n\n  IOSIntermediateDumpWriter::ScopedArray module_extra_memory_regions_array(\n      writer, IntermediateDumpKey::kModuleExtraMemoryRegions);\n\n  SimpleAddressRangeBag::Iterator iterator(*(extra_memory_ranges.get()));\n  while (const SimpleAddressRangeBag::Entry* entry = iterator.Next()) {\n    const uint64_t& address = entry->base;\n    const uint64_t& size = entry->size;\n    IOSIntermediateDumpWriter::ScopedArrayMap memory_region_map(writer);\n    WriteProperty(\n        writer, IntermediateDumpKey::kModuleExtraMemoryRegionAddress, &address);\n    WritePropertyBytes(writer,\n                       IntermediateDumpKey::kModuleExtraMemoryRegionData,\n                       reinterpret_cast<const void*>(address),\n                       size);\n  }\n}\n\nvoid InProcessIntermediateDumpHandler::\n    WriteCrashpadIntermediateDumpExtraMemoryRanges(\n        IOSIntermediateDumpWriter* writer,\n        CrashpadInfo* crashpad_info) {\n  if (!crashpad_info->intermediate_dump_extra_memory_ranges()) {\n    return;\n  }\n\n  ScopedVMRead<SimpleAddressRangeBag> intermediate_dump_extra_memory;\n  if (!intermediate_dump_extra_memory.Read(\n          crashpad_info->intermediate_dump_extra_memory_ranges())) {\n    CRASHPAD_RAW_LOG(\n        \"Unable to read intermediate dump extra memory ranges object\");\n    return;\n  }\n\n  IOSIntermediateDumpWriter::ScopedArray module_extra_memory_regions_array(\n      writer, IntermediateDumpKey::kModuleIntermediateDumpExtraMemoryRegions);\n\n  SimpleAddressRangeBag::Iterator iterator(\n      *(intermediate_dump_extra_memory.get()));\n  while (const SimpleAddressRangeBag::Entry* entry = iterator.Next()) {\n    const uint64_t& address = entry->base;\n    const uint64_t& size = entry->size;\n    IOSIntermediateDumpWriter::ScopedArrayMap memory_region_map(writer);\n    WriteProperty(\n        writer,\n        IntermediateDumpKey::kModuleIntermediateDumpExtraMemoryRegionAddress,\n        &address);\n    WritePropertyBytes(\n        writer,\n        IntermediateDumpKey::kModuleIntermediateDumpExtraMemoryRegionData,\n        reinterpret_cast<const void*>(address),\n        size);\n  }\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "client/ios_handler/in_process_intermediate_dump_handler.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_\n#define CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_\n\n#include <mach-o/loader.h>\n#include <mach/mach.h>\n#include <sys/types.h>\n\n#include <map>\n\n#include \"client/crashpad_info.h\"\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n#include \"util/ios/ios_system_data_collector.h\"\n#include \"util/mach/mach_extensions.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Dump all in-process data to iOS intermediate dump.\n//! Note: All methods are `RUNS-DURING-CRASH`.\nclass InProcessIntermediateDumpHandler final {\n public:\n  InProcessIntermediateDumpHandler() = delete;\n  InProcessIntermediateDumpHandler(const InProcessIntermediateDumpHandler&) =\n      delete;\n  InProcessIntermediateDumpHandler& operator=(\n      const InProcessIntermediateDumpHandler&) = delete;\n\n  //! \\brief Set kVersion to 1.\n  //!\n  //! \\param[in] writer The dump writer\n  static void WriteHeader(IOSIntermediateDumpWriter* writer);\n\n  //! \\brief Write ProcessSnapshot data to the intermediate dump.\n  //!\n  //! \\param[in] writer The dump writer\n  //! \\param[in] annotations The simple map annotations.\n  static void WriteProcessInfo(\n      IOSIntermediateDumpWriter* writer,\n      const std::map<std::string, std::string>& annotations);\n\n  //! \\brief Write SystemSnapshot data to the intermediate dump.\n  //!\n  //! \\param[in] writer The dump writer\n  //! \\param[in] system_data An object containing various system data points.\n  //! \\param[in] report_time_nanos Report creation time in nanoseconds as\n  //!     returned by ClockMonotonicNanoseconds().\n  static void WriteSystemInfo(IOSIntermediateDumpWriter* writer,\n                              const IOSSystemDataCollector& system_data,\n                              uint64_t report_time_nanos);\n\n  //! \\brief Write ThreadSnapshot data to the intermediate dump.\n  //!\n  //! For uncaught NSExceptions, \\a frames and \\a num_frames will be added to\n  //! the intermediate dump for the exception thread. Otherwise, or for the\n  //! remaining threads, use `thread_get_state`.\n  //!\n  //! \\param[in] writer The dump writer\n  //! \\param[in] frames An array of callstack return addresses.\n  //! \\param[in] num_frames The number of callstack return address in \\a frames.\n  static void WriteThreadInfo(IOSIntermediateDumpWriter* writer,\n                              const uint64_t* frames,\n                              const size_t num_frames);\n\n  //! \\brief Write ModuleSnapshot data to the intermediate dump.\n  //!\n  //! This includes both modules and annotations.\n  //!\n  //! \\param[in] writer The dump writer\n  static void WriteModuleInfo(IOSIntermediateDumpWriter* writer);\n\n  //! \\brief Write an ExceptionSnapshot from a signal to the intermediate dump.\n  //!\n  //!  Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException\n  //!  and WriteExceptionFromNSException should be called per intermediate dump.\n  //!\n  //! \\param[in] writer The dump writer\n  //! \\param[in] system_data An object containing various system data points.\n  //! \\param[in] siginfo A pointer to a `siginfo_t` object received by a signal\n  //!     handler.\n  //! \\param[in] context A pointer to a `ucontext_t` object received by a\n  //!     signal.\n  static void WriteExceptionFromSignal(\n      IOSIntermediateDumpWriter* writer,\n      const IOSSystemDataCollector& system_data,\n      siginfo_t* siginfo,\n      ucontext_t* context);\n\n  //! \\brief Write an ExceptionSnapshot from a mach exception to the\n  //!     intermediate dump.\n  //!\n  //! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException\n  //! and WriteExceptionFromNSException should be called per intermediate dump.\n  //!\n  //! \\param[in] writer The dump writer\n  //! \\param[in] behavior\n  //! \\param[in] thread\n  //! \\param[in] exception\n  //! \\param[in] code\n  //! \\param[in] code_count\n  //! \\param[in] flavor\n  //! \\param[in] old_state\n  //! \\param[in] old_state_count\n  static void WriteExceptionFromMachException(\n      IOSIntermediateDumpWriter* writer,\n      exception_behavior_t behavior,\n      thread_t thread,\n      exception_type_t exception,\n      const mach_exception_data_type_t* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count);\n\n  //! \\brief Write an ExceptionSnapshot from an NSException to the\n  //!     intermediate dump.\n  //!\n  //!  Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException\n  //!  and WriteExceptionFromNSException should be called per intermediate dump.\n  //!\n  //! \\param[in] writer The dump writer\n  static void WriteExceptionFromNSException(IOSIntermediateDumpWriter* writer);\n\n private:\n  //! \\brief Parse and extract module and annotation information from header.\n  static void WriteModuleInfoAtAddress(IOSIntermediateDumpWriter* writer,\n                                       uint64_t address,\n                                       bool is_dyld);\n\n  //! \\brief Extract and write Apple crashreporter_annotations_t data and\n  //!     Crashpad annotations. Note that \\a segment_vm_read_ptr has already\n  //!     been read via vm_read and may be dereferenced without a ScopedVMRead.\n  static void WriteDataSegmentAnnotations(\n      IOSIntermediateDumpWriter* writer,\n      const segment_command_64* segment_vm_read_ptr,\n      vm_size_t slide);\n\n  //! \\brief Write Crashpad annotations list.\n  static void WriteCrashpadAnnotationsList(IOSIntermediateDumpWriter* writer,\n                                           CrashpadInfo* crashpad_info);\n\n  //! \\brief Write Crashpad extra memory data.\n  static void WriteCrashpadExtraMemoryRanges(IOSIntermediateDumpWriter* writer,\n                                             CrashpadInfo* crashpad_info);\n\n  //! \\brief Write Crashpad intermediate dump extra memory data.\n  static void WriteCrashpadIntermediateDumpExtraMemoryRanges(\n      IOSIntermediateDumpWriter* writer,\n      CrashpadInfo* crashpad_info);\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_\n"
  },
  {
    "path": "client/ios_handler/in_process_intermediate_dump_handler_test.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/ios_handler/in_process_intermediate_dump_handler.h\"\n\n#include <sys/utsname.h>\n\n#include <atomic>\n#include <iterator>\n#include <thread>\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"client/annotation.h\"\n#include \"client/annotation_list.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"snapshot/ios/process_snapshot_ios_intermediate_dump.h\"\n#include \"snapshot/minidump/process_snapshot_minidump.h\"\n#include \"test/scoped_set_thread_name.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/misc/capture_context.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing internal::InProcessIntermediateDumpHandler;\n\nclass ReadToString : public crashpad::MemorySnapshot::Delegate {\n public:\n  std::string result;\n\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override {\n    result = std::string(reinterpret_cast<const char*>(data), size);\n    return true;\n  }\n};\n\nclass InProcessIntermediateDumpHandlerTest : public testing::Test {\n protected:\n  // testing::Test:\n\n  void SetUp() override {\n    path_ = temp_dir_.path().Append(\"dump_file\");\n    writer_ = std::make_unique<internal::IOSIntermediateDumpWriter>();\n    EXPECT_TRUE(writer_->Open(path_));\n    ASSERT_TRUE(IsRegularFile(path_));\n  }\n\n  void TearDown() override {\n    EXPECT_TRUE(writer_->Close());\n    writer_.reset();\n    EXPECT_FALSE(IsRegularFile(path_));\n  }\n\n  void WriteReportAndCloseWriter() {\n    {\n      internal::IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer_.get());\n      InProcessIntermediateDumpHandler::WriteHeader(writer_.get());\n      InProcessIntermediateDumpHandler::WriteProcessInfo(\n          writer_.get(), {{\"before_dump\", \"pre\"}});\n      InProcessIntermediateDumpHandler::WriteSystemInfo(\n          writer_.get(), system_data_, ClockMonotonicNanoseconds());\n      InProcessIntermediateDumpHandler::WriteThreadInfo(writer_.get(), 0, 0);\n      InProcessIntermediateDumpHandler::WriteModuleInfo(writer_.get());\n    }\n    EXPECT_TRUE(writer_->Close());\n  }\n\n  void WriteMachException() {\n    crashpad::NativeCPUContext cpu_context;\n    crashpad::CaptureContext(&cpu_context);\n    const mach_exception_data_type_t code[2] = {};\n    static constexpr int kSimulatedException = -1;\n    InProcessIntermediateDumpHandler::WriteExceptionFromMachException(\n        writer_.get(),\n        MACH_EXCEPTION_CODES,\n        mach_thread_self(),\n        kSimulatedException,\n        code,\n        std::size(code),\n        MACHINE_THREAD_STATE,\n        reinterpret_cast<ConstThreadState>(&cpu_context),\n        MACHINE_THREAD_STATE_COUNT);\n  }\n\n  const auto& path() const { return path_; }\n  auto writer() const { return writer_.get(); }\n\n#if TARGET_OS_SIMULATOR\n  // macOS 14.0 is 23A344, macOS 13.6.5 is 22G621, so if the first two\n  // characters in the kern.osversion are > 22, this build will reproduce the\n  // simulator bug in crbug.com/328282286\n  // This now reproduces on macOS 15.4 24E248 as well for iOS17 simulators.\n  bool HasMacOSBrokeDYLDTaskInfo() {\n    if (__builtin_available(iOS 18, *)) {\n      return false;\n    }\n    if (std::stoi(system_data_.Build().substr(0, 2)) >= 24) {\n      return true;\n    }\n    if (__builtin_available(iOS 17, *)) {\n      return false;\n    }\n    return std::stoi(system_data_.Build().substr(0, 2)) > 22;\n  }\n#endif\n\n private:\n  std::unique_ptr<internal::IOSIntermediateDumpWriter> writer_;\n  internal::IOSSystemDataCollector system_data_;\n  ScopedTempDir temp_dir_;\n  base::FilePath path_;\n};\n\nTEST_F(InProcessIntermediateDumpHandlerTest, TestSystem) {\n  WriteReportAndCloseWriter();\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n\n  // Snpahot\n  const SystemSnapshot* system = process_snapshot.System();\n  ASSERT_NE(system, nullptr);\n#if defined(ARCH_CPU_X86_64)\n  EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureX86_64);\n#elif defined(ARCH_CPU_ARM64)\n  EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureARM64);\n#else\n#error Port to your CPU architecture\n#endif\n#if TARGET_OS_SIMULATOR\n  EXPECT_EQ(system->MachineDescription().substr(0, 13),\n            std::string(\"iOS Simulator\"));\n#elif TARGET_OS_IPHONE\n  utsname uts;\n  ASSERT_EQ(uname(&uts), 0);\n  EXPECT_STREQ(system->MachineDescription().c_str(), uts.machine);\n#endif\n\n  EXPECT_EQ(system->GetOperatingSystem(), SystemSnapshot::kOperatingSystemIOS);\n}\n\nTEST_F(InProcessIntermediateDumpHandlerTest, TestAnnotations) {\n#if TARGET_OS_SIMULATOR\n  // This test will fail on <iOS17 simulators when running on macOS >=14.3 or\n  // <iOS18 simulators when running on macOS >=15.4 due to a bug in Simulator.\n  // crbug.com/328282286\n  if (HasMacOSBrokeDYLDTaskInfo()) {\n    // For TearDown.\n    ASSERT_TRUE(LoggingRemoveFile(path()));\n    return;\n  }\n#endif\n  // This is “leaked” to crashpad_info.\n  crashpad::SimpleStringDictionary* simple_annotations =\n      new crashpad::SimpleStringDictionary();\n  simple_annotations->SetKeyValue(\"#TEST# pad\", \"break\");\n  simple_annotations->SetKeyValue(\"#TEST# key\", \"value\");\n  simple_annotations->SetKeyValue(\"#TEST# pad\", \"crash\");\n  simple_annotations->SetKeyValue(\"#TEST# x\", \"y\");\n  simple_annotations->SetKeyValue(\"#TEST# longer\", \"shorter\");\n  simple_annotations->SetKeyValue(\"#TEST# empty_value\", \"\");\n\n  crashpad::CrashpadInfo* crashpad_info =\n      crashpad::CrashpadInfo::GetCrashpadInfo();\n\n  crashpad_info->set_simple_annotations(simple_annotations);\n\n  crashpad::AnnotationList::Register();  // This is “leaked” to crashpad_info.\n\n  static crashpad::StringAnnotation<32> test_annotation_one{\"#TEST# one\"};\n  static crashpad::StringAnnotation<32> test_annotation_two{\"#TEST# two\"};\n  static crashpad::StringAnnotation<32> test_annotation_three{\n      \"#TEST# same-name\"};\n  static crashpad::StringAnnotation<32> test_annotation_four{\n      \"#TEST# same-name\"};\n\n  test_annotation_one.Set(\"moocow\");\n  test_annotation_two.Set(\"this will be cleared\");\n  test_annotation_three.Set(\"same-name 3\");\n  test_annotation_four.Set(\"same-name 4\");\n  test_annotation_two.Clear();\n\n  WriteReportAndCloseWriter();\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(\n      path(), {{\"after_dump\", \"post\"}}));\n\n  auto process_map = process_snapshot.AnnotationsSimpleMap();\n  EXPECT_EQ(process_map.size(), 3u);\n  EXPECT_EQ(process_map[\"before_dump\"], \"pre\");\n  EXPECT_EQ(process_map[\"after_dump\"], \"post\");\n  EXPECT_TRUE(process_map.find(\"crashpad_uptime_ns\") != process_map.end());\n\n  std::map<std::string, std::string> all_annotations_simple_map;\n  std::vector<AnnotationSnapshot> all_annotations;\n  for (const auto* module : process_snapshot.Modules()) {\n    std::map<std::string, std::string> module_annotations_simple_map =\n        module->AnnotationsSimpleMap();\n    all_annotations_simple_map.insert(module_annotations_simple_map.begin(),\n                                      module_annotations_simple_map.end());\n\n    std::vector<AnnotationSnapshot> annotations = module->AnnotationObjects();\n    all_annotations.insert(\n        all_annotations.end(), annotations.begin(), annotations.end());\n  }\n\n  EXPECT_EQ(all_annotations_simple_map.size(), 5u);\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# pad\"], \"crash\");\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# key\"], \"value\");\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# x\"], \"y\");\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# longer\"], \"shorter\");\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# empty_value\"], \"\");\n\n  bool saw_same_name_3 = false, saw_same_name_4 = false;\n  for (const auto& annotation : all_annotations) {\n    EXPECT_EQ(annotation.type,\n              static_cast<uint16_t>(Annotation::Type::kString));\n    std::string value(reinterpret_cast<const char*>(annotation.value.data()),\n                      annotation.value.size());\n    if (annotation.name == \"#TEST# one\") {\n      EXPECT_EQ(value, \"moocow\");\n    } else if (annotation.name == \"#TEST# same-name\") {\n      if (value == \"same-name 3\") {\n        EXPECT_FALSE(saw_same_name_3);\n        saw_same_name_3 = true;\n      } else if (value == \"same-name 4\") {\n        EXPECT_FALSE(saw_same_name_4);\n        saw_same_name_4 = true;\n      } else {\n        ADD_FAILURE() << \"unexpected annotation value \" << value;\n      }\n    } else {\n      ADD_FAILURE() << \"unexpected annotation \" << annotation.name;\n    }\n  }\n}\n\nTEST_F(InProcessIntermediateDumpHandlerTest, TestExtraMemoryRanges) {\n#if TARGET_OS_SIMULATOR\n  // This test will fail on <iOS17 simulators when running on macOS >=14.3 or\n  // <iOS18 simulators when running on macOS >=15.4 due to a bug in Simulator.\n  // crbug.com/328282286\n  if (HasMacOSBrokeDYLDTaskInfo()) {\n    // For TearDown.\n    ASSERT_TRUE(LoggingRemoveFile(path()));\n    return;\n  }\n#endif\n\n  // Put the string on the heap so the memory doesn't coalesce with the stack.\n  std::unique_ptr<std::string> someExtraMemoryString(\n      new std::string(\"extra memory range\"));\n  crashpad::SimpleAddressRangeBag* ios_extra_ranges =\n      new crashpad::SimpleAddressRangeBag();\n  crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(\n      ios_extra_ranges);\n  ios_extra_ranges->Insert((void*)someExtraMemoryString->c_str(), 18);\n  WriteReportAndCloseWriter();\n  crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(nullptr);\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n  ASSERT_EQ(process_snapshot.ExtraMemory().size(), 1LU);\n  auto memory = process_snapshot.ExtraMemory()[0];\n  EXPECT_EQ(memory->Address(),\n            reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()));\n  EXPECT_EQ(memory->Size(), 18LU);\n  ReadToString delegate;\n  ASSERT_TRUE(memory->Read(&delegate));\n  EXPECT_EQ(delegate.result, someExtraMemoryString->c_str());\n\n  StringFile string_file;\n  MinidumpFileWriter minidump_file_writer;\n  minidump_file_writer.InitializeFromSnapshot(&process_snapshot);\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ProcessSnapshotMinidump process_snapshot_minidump;\n  EXPECT_TRUE(process_snapshot_minidump.Initialize(&string_file));\n  bool found;\n  for (auto minidump_memory : process_snapshot_minidump.ExtraMemory()) {\n    if (minidump_memory->Address() ==\n            reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()) &&\n        minidump_memory->Size() == 18LU) {\n      found = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(found);\n}\n\nTEST_F(InProcessIntermediateDumpHandlerTest,\n       TestIntermediateDumpExtraMemoryRanges) {\n#if TARGET_OS_SIMULATOR\n  // This test will fail on <iOS17 simulators when running on macOS >=14.3 or\n  // <iOS18 simulators when running on macOS >=15.4 due to a bug in Simulator.\n  // crbug.com/328282286\n  if (HasMacOSBrokeDYLDTaskInfo()) {\n    // For TearDown.\n    ASSERT_TRUE(LoggingRemoveFile(path()));\n    return;\n  }\n#endif\n\n  // Put the string on the heap so the memory doesn't coalesce with the stack.\n  std::unique_ptr<std::string> someExtraMemoryString(\n      new std::string(\"extra memory range\"));\n  crashpad::SimpleAddressRangeBag* ios_extra_ranges =\n      new crashpad::SimpleAddressRangeBag();\n  crashpad::CrashpadInfo::GetCrashpadInfo()\n      ->set_intermediate_dump_extra_memory_ranges(ios_extra_ranges);\n  ios_extra_ranges->Insert((void*)someExtraMemoryString->c_str(), 18);\n  WriteReportAndCloseWriter();\n  crashpad::CrashpadInfo::GetCrashpadInfo()\n      ->set_intermediate_dump_extra_memory_ranges(nullptr);\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n  ASSERT_EQ(process_snapshot.IntermediateDumpExtraMemory().size(), 1LU);\n  auto memory = process_snapshot.IntermediateDumpExtraMemory()[0];\n  EXPECT_EQ(memory->Address(),\n            reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()));\n  EXPECT_EQ(memory->Size(), 18LU);\n  ReadToString delegate;\n  ASSERT_TRUE(memory->Read(&delegate));\n  EXPECT_EQ(delegate.result, someExtraMemoryString->c_str());\n\n  StringFile string_file;\n  MinidumpFileWriter minidump_file_writer;\n  minidump_file_writer.InitializeFromSnapshot(&process_snapshot);\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ProcessSnapshotMinidump process_snapshot_minidump;\n  EXPECT_TRUE(process_snapshot_minidump.Initialize(&string_file));\n  for (auto minidump_memory : process_snapshot_minidump.ExtraMemory()) {\n    EXPECT_FALSE(\n        minidump_memory->Address() ==\n            reinterpret_cast<uint64_t>(someExtraMemoryString->c_str()) &&\n        minidump_memory->Size() == 18LU);\n  }\n}\n\nTEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) {\n  const ScopedSetThreadName scoped_set_thread_name(\"TestThreads\");\n\n  WriteReportAndCloseWriter();\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n\n  const auto& threads = process_snapshot.Threads();\n  ASSERT_GT(threads.size(), 0u);\n\n  thread_identifier_info identifier_info;\n  mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;\n  ASSERT_EQ(thread_info(mach_thread_self(),\n                        THREAD_IDENTIFIER_INFO,\n                        reinterpret_cast<thread_info_t>(&identifier_info),\n                        &count),\n            0);\n  EXPECT_EQ(threads[0]->ThreadID(), identifier_info.thread_id);\n  EXPECT_EQ(threads[0]->ThreadName(), \"TestThreads\");\n}\n\nTEST_F(InProcessIntermediateDumpHandlerTest, TestProcess) {\n  WriteReportAndCloseWriter();\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n  EXPECT_EQ(process_snapshot.ProcessID(), getpid());\n}\n\nTEST_F(InProcessIntermediateDumpHandlerTest, TestMachException) {\n  WriteReportAndCloseWriter();\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n}\n\nTEST_F(InProcessIntermediateDumpHandlerTest, TestSignalException) {\n  WriteReportAndCloseWriter();\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n}\n\nTEST_F(InProcessIntermediateDumpHandlerTest, TestNSException) {\n  WriteReportAndCloseWriter();\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n}\n\n}  // namespace\n\nTEST_F(InProcessIntermediateDumpHandlerTest,\n       TestCaptureMemoryPointedToByThreadState) {\n  char test_buffer[1024];\n  memset(test_buffer, 'A', sizeof(test_buffer));\n  test_buffer[128] = 'B';\n\n  // Use a std::atomic and a std::thread to simulate an arbitrary thread with a\n  // known register state.\n  std::atomic<bool> wait_for_main_thread(true);\n  std::atomic<bool> thread_started(false);\n\n  std::string thread_name(\"CaptureMemoryThread\");\n  std::thread t([&]() {\n    pthread_setname_np(thread_name.c_str());\n    void* ptr = test_buffer + 128;\n    // Force the compiler to store our pointer in a designated register rather\n    // than on the stack\n#if defined(ARCH_CPU_ARM64)\n    register void* reg_ptr asm(\"x20\") = ptr;\n#elif defined(ARCH_CPU_X86_64)\n    register void* reg_ptr asm(\"r12\") = ptr;\n#endif\n    thread_started = true;\n    while (wait_for_main_thread.load(std::memory_order_relaxed)) {\n      // Dummy operation to prevent the optimizer from discarding our register\n      // assignment.\n      asm volatile(\"\" : : \"r\"(reg_ptr));\n    }\n  });\n\n  while (!thread_started.load(std::memory_order_relaxed)) {\n    std::this_thread::yield();\n  }\n\n  WriteReportAndCloseWriter();\n\n  wait_for_main_thread = false;\n  t.join();\n\n  internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {}));\n\n  bool found_our_buffer = false;\n  for (const auto* thread : process_snapshot.Threads()) {\n    if (thread->ThreadName() == thread_name) {\n      for (const auto* memory : thread->ExtraMemory()) {\n        if (memory->Address() == reinterpret_cast<uint64_t>(test_buffer)) {\n          found_our_buffer = true;\n          EXPECT_EQ(memory->Size(), 512u);\n\n          ReadToString delegate;\n          ASSERT_TRUE(memory->Read(&delegate));\n          EXPECT_EQ(delegate.result.size(), 512u);\n          EXPECT_EQ(delegate.result[128], 'B');\n          EXPECT_EQ(delegate.result[129], 'A');\n        }\n      }\n    }\n  }\n  EXPECT_TRUE(found_our_buffer);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h\"\n\n#include <utility>\n\n#include \"client/prune_crash_reports.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/ios/scoped_background_task.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// The file extension used to indicate a file is locked.\nconstexpr char kLockedExtension[] = \".locked\";\n\n// Prune onces a day.\nconstexpr time_t prune_interval = 60 * 60 * 24;\n\n// If the client finds a locked file matching it's own bundle id, unlock it\n// after 24 hours.\nconstexpr time_t matching_bundle_locked_ttl = 60 * 60 * 24;\n\n// Unlock any locked intermediate dump after 60 days.\nconstexpr time_t max_locked_ttl = 60 * 60 * 24 * 60;\n\n// The initial thread delay for applications.  Delay the thread's file i/o to\n// not interfere with application startup.\nconstexpr double app_delay = 60;\n\n// The initial thread delay for app extensions. Because iOS extensions are often\n// very short lived, do not wait the full |app_delay|, and instead use a shorter\n// time.\nconstexpr double extension_delay = 5;\n\n\n//! \\brief Unlocks old intermediate dumps.\n//!\n//! This function can unlock (remove the .locked extension) intermediate dumps\n//! that are either too old to be useful, or are likely leftover dumps from\n//! clean app exits.\n//!\n//! \\param[in] pending_path The path to any locked intermediate dump files.\n//! \\param[in] bundle_identifier_and_seperator The identifier for this client,\n//!     used to determine when locked files are considered stale.\nvoid UnlockOldIntermediateDumps(base::FilePath pending_path,\n                                std::string bundle_identifier_and_seperator) {\n  DirectoryReader reader;\n  std::vector<base::FilePath> files;\n  if (!reader.Open(pending_path)) {\n    return;\n  }\n  base::FilePath file;\n  DirectoryReader::Result result;\n  while ((result = reader.NextFile(&file)) ==\n         DirectoryReader::Result::kSuccess) {\n    if (file.FinalExtension() != kLockedExtension)\n      continue;\n\n    const base::FilePath file_path(pending_path.Append(file));\n    timespec file_time;\n    time_t now = time(nullptr);\n    if (!FileModificationTime(file_path, &file_time)) {\n      continue;\n    }\n\n    if ((file.value().compare(0,\n                              bundle_identifier_and_seperator.size(),\n                              bundle_identifier_and_seperator) == 0 &&\n         file_time.tv_sec <= now - matching_bundle_locked_ttl) ||\n        (file_time.tv_sec <= now - max_locked_ttl)) {\n      base::FilePath new_path = file_path.RemoveFinalExtension();\n      MoveFileOrDirectory(file_path, new_path);\n      continue;\n    }\n  }\n}\n\n}  // namespace\n\nPruneIntermediateDumpsAndCrashReportsThread::\n    PruneIntermediateDumpsAndCrashReportsThread(\n        CrashReportDatabase* database,\n        std::unique_ptr<PruneCondition> condition,\n        base::FilePath pending_path,\n        std::string bundle_identifier_and_seperator,\n        bool is_extension)\n    : thread_(prune_interval, this),\n      condition_(std::move(condition)),\n      pending_path_(pending_path),\n      bundle_identifier_and_seperator_(bundle_identifier_and_seperator),\n      initial_work_delay_(is_extension ? extension_delay : app_delay),\n      last_start_time_(0),\n      database_(database) {}\n\nPruneIntermediateDumpsAndCrashReportsThread::\n    ~PruneIntermediateDumpsAndCrashReportsThread() {}\n\nvoid PruneIntermediateDumpsAndCrashReportsThread::Start() {\n  thread_.Start(initial_work_delay_);\n}\n\nvoid PruneIntermediateDumpsAndCrashReportsThread::Stop() {\n  thread_.Stop();\n}\n\nvoid PruneIntermediateDumpsAndCrashReportsThread::DoWork(\n    const WorkerThread* thread) {\n  // This thread may be stopped and started a number of times throughout the\n  // lifetime of the process to prevent 0xdead10cc kills (see\n  // crbug.com/crashpad/400), but it should only run once per prune_interval\n  // after initial_work_delay_.\n  time_t now = time(nullptr);\n  if (now - last_start_time_ < prune_interval)\n    return;\n  last_start_time_ = now;\n\n  internal::ScopedBackgroundTask scoper(\"PruneThread\");\n  database_->CleanDatabase(60 * 60 * 24 * 3);\n\n  // Here and below, respect Stop() being called after each task.\n  if (!thread_.is_running())\n    return;\n  PruneCrashReportDatabase(database_, condition_.get());\n\n  if (!thread_.is_running())\n    return;\n  if (!clean_old_intermediate_dumps_) {\n    clean_old_intermediate_dumps_ = true;\n    UnlockOldIntermediateDumps(pending_path_, bundle_identifier_and_seperator_);\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_\n#define CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_\n\n#include <memory>\n\n#include \"base/files/file_path.h\"\n#include \"util/thread/stoppable.h\"\n#include \"util/thread/worker_thread.h\"\n\nnamespace crashpad {\n\nclass CrashReportDatabase;\nclass PruneCondition;\n\n//! \\brief A thread that periodically prunes crash reports from the database\n//!     using the specified condition, and any leftover locked intermediate\n//!     dumps.\n//!\n//! After the thread is started, the database is pruned using the condition\n//! every 24 hours. Upon calling Start(), the thread waits before performing\n//! the initial prune operation.\n//!\n//! Locked intermediate dump files are unlocked only once, not periodically.\n//! Locked dumps that match this bundle id can be unlocked if they are over a\n//! day old. Otherwise, unlock dumps that are over 60 days old.\nclass PruneIntermediateDumpsAndCrashReportsThread\n    : public WorkerThread::Delegate,\n      public Stoppable {\n public:\n  //! \\brief Constructs a new object.\n  //!\n  //! \\param[in] database The database to prune crash reports from.\n  //! \\param[in] condition The condition used to evaluate crash reports for\n  //!     pruning.\n  //! \\param[in] pending_path The path to any locked intermediate dump files.\n  //! \\param[in] bundle_identifier_and_seperator The identifier for this client,\n  //!     used to determine when locked files are considered stale, with a\n  //!     seperator at the end to allow for substring searches.\n  //! \\param[in] is_extension Whether the process is an app extension. If\n  //!     `true`, the inital prune will occur after a 5-second delay. If\n  //!     `false`, the initial prune will occur after a 60-second delay.\n  PruneIntermediateDumpsAndCrashReportsThread(\n      CrashReportDatabase* database,\n      std::unique_ptr<PruneCondition> condition,\n      base::FilePath pending_path,\n      std::string bundle_identifier_and_seperator,\n      bool is_extension);\n\n  PruneIntermediateDumpsAndCrashReportsThread(\n      const PruneIntermediateDumpsAndCrashReportsThread&) = delete;\n  PruneIntermediateDumpsAndCrashReportsThread& operator=(\n      const PruneIntermediateDumpsAndCrashReportsThread&) = delete;\n\n  ~PruneIntermediateDumpsAndCrashReportsThread();\n\n  // Stoppable:\n\n  //! \\brief Starts a dedicated pruning thread.\n  //!\n  //! The thread waits before running the initial prune, so as to not interfere\n  //! with any startup-related IO performed by the client.\n  //!\n  //! This method may only be be called on a newly-constructed object or after\n  //! a call to Stop().\n  void Start() override;\n\n  //! \\brief Stops the pruning thread.\n  //!\n  //! This method must only be called after Start(). If Start() has been called,\n  //! this method must be called before destroying an object of this class.\n  //!\n  //! This method may be called from any thread other than the pruning thread.\n  //! It is expected to only be called from the same thread that called Start().\n  void Stop() override;\n\n  //! \\return `true` if the thread is running, `false` if it is not.\n  bool is_running() const { return thread_.is_running(); }\n\n private:\n  // WorkerThread::Delegate:\n  void DoWork(const WorkerThread* thread) override;\n\n  WorkerThread thread_;\n  std::unique_ptr<PruneCondition> condition_;\n  base::FilePath pending_path_;\n  std::string bundle_identifier_and_seperator_;\n  bool clean_old_intermediate_dumps_;\n  double initial_work_delay_;\n  time_t last_start_time_;\n  CrashReportDatabase* database_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_\n"
  },
  {
    "path": "client/length_delimited_ring_buffer.h",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_LENGTH_DELIMITED_RING_BUFFER_H_\n#define CRASHPAD_CLIENT_LENGTH_DELIMITED_RING_BUFFER_H_\n\n#include <stdint.h>\n#include <string.h>\n\n#include <algorithm>\n#include <array>\n#include <limits>\n#include <optional>\n#include <type_traits>\n#include <vector>\n\n#include \"base/numerics/safe_math.h\"\n\nnamespace crashpad {\n\n//! \\brief Capacity of a `RingBufferData`, in bytes.\nusing RingBufferCapacity = uint32_t;\n\nnamespace internal {\n\n//! \\brief Default capacity of `RingBufferData`, in bytes.\ninline constexpr RingBufferCapacity kDefaultRingBufferDataCapacity = 8192;\n\n//! \\brief A tuple holding the current range of bytes which can be read from or\n//!     have been written to.\nstruct Range final {\n  //! \\brief The offset into a `RingBufferData` at which a `Range` begins.\n  using Offset = uint32_t;\n\n  //! \\brief The length inside a `RingBufferData` of a `Range` of data.\n  using Length = uint32_t;\n\n  Offset offset;\n  Length length;\n};\n\n// This struct is persisted to disk, so its size must not change.\nstatic_assert(sizeof(Range) == 8,\n              \"struct Range is not packed on this platform\");\n\n//! \\brief The number of bits encoded in each byte of a Base 128-encoded varint.\ninline constexpr int kBase128ByteValueBits = 7;\n\n//! \\!brief Calculates the length in bytes of `value` encoded using\n//!    little-endian Base 128 varint encoding.\n//! \\sa https://developers.google.com/protocol-buffers/docs/encoding#varints\n//!\n//! `LengthDelimitedRingBufferWriter` uses varint-encoded delimiters to enable\n//! zero-copy deserialization of the ringbuffer's contents when storing\n//! protobufs inside the ringbuffer, e.g. via\n//! `google::protobuf::util::ParseDelimitedFromZeroCopyStream()` or similar.\n//!\n//! \\sa\n//! https://github.com/protocolbuffers/protobuf/blob/3202b9da88ceb75b65bbabaf4033c95e872f828d/src/google/protobuf/util/delimited_message_util.h#L85\n//! \\sa\n//! https://github.com/protocolbuffers/protobuf/blob/8bd49dea5e167a389d94b71d24c981d8f9fa0c99/src/google/protobuf/io/zero_copy_stream_impl_lite.h#L68\n//! \\sa\n//! https://github.com/protocolbuffers/protobuf/blob/8bd49dea5e167a389d94b71d24c981d8f9fa0c99/src/google/protobuf/io/coded_stream.h#L171\n//!\n//! \\!param[in] value Value to be encoded in Base 128 varint encoding.\n//! \\!return The length in bytes of `value` in Base 128 varint encoding.\ntemplate <typename IntegerType>\nconstexpr Range::Length Base128VarintEncodedLength(IntegerType value) {\n  static_assert(std::is_unsigned<IntegerType>::value);\n\n  Range::Length size = 1;\n  while (value >= 0x80) {\n    value >>= kBase128ByteValueBits;\n    size++;\n  }\n  return size;\n}\n\n// Note that std::array capacity is a size_t, not a RingBufferCapacity.\ntemplate <size_t ArrayCapacity>\nusing RingBufferArray = std::array<uint8_t, ArrayCapacity>;\n\n//! \\return The size of the `RingBufferArray` as a `Range::Length`.\ntemplate <size_t ArrayCapacity>\nconstexpr Range::Length RingBufferArraySize(\n    const RingBufferArray<ArrayCapacity>& ring_buffer_data) {\n  static_assert(ArrayCapacity <= std::numeric_limits<Range::Length>::max());\n  return static_cast<Range::Length>(ring_buffer_data.size());\n}\n\n//! \\brief Reads data from the ring buffer into a target buffer.\n//! \\param[in] ring_buffer_data The ring buffer to read.\n//! \\param[in,out] ring_buffer_read_range The range of the data available\n//!     to read. Upon return, set to the remaining range of data available\n//!     to read, if any.\n//! \\param[in] target_buffer Buffer into which data will be written.\n//! \\param[in] target_buffer_length Number of bytes to write into\n//!     `target_buffer`.\n//!\n//! \\return `true` if the read succeeded, `false` otherwise. On success, updates\n//!     `ring_buffer_read_range` to reflect the bytes consumed.\n//!\n//! The bytes can wrap around the end of the ring buffer, in which case the read\n//! continues at the beginning of the ring buffer (if the ring buffer is long\n//! enough).\ntemplate <typename RingBufferArrayType>\nbool ReadBytesFromRingBuffer(const RingBufferArrayType& ring_buffer_data,\n                             internal::Range& ring_buffer_read_range,\n                             uint8_t* target_buffer,\n                             Range::Length target_buffer_length) {\n  if (target_buffer_length > ring_buffer_read_range.length) {\n    return false;\n  }\n  if (target_buffer_length == 0) {\n    return true;\n  }\n  const Range::Length initial_read_length = std::min(\n      target_buffer_length,\n      RingBufferArraySize(ring_buffer_data) - ring_buffer_read_range.offset);\n  memcpy(target_buffer,\n         &ring_buffer_data[ring_buffer_read_range.offset],\n         initial_read_length);\n  if (initial_read_length < target_buffer_length) {\n    const Range::Length remaining_read_length =\n        target_buffer_length - initial_read_length;\n    memcpy(target_buffer + initial_read_length,\n           &ring_buffer_data[0],\n           remaining_read_length);\n  }\n  ring_buffer_read_range.offset =\n      (ring_buffer_read_range.offset + target_buffer_length) %\n      RingBufferArraySize(ring_buffer_data);\n  ring_buffer_read_range.length -= target_buffer_length;\n  return true;\n}\n\n//! \\brief Reads a single little-endian Base 128 varint-encoded integer from\n//!     the ring buffer.\n//! \\param[in] ring_buffer_data The ring buffer to read.\n//! \\param[in,out] ring_buffer_read_range The range of the data available\n//!     to read. Upon return, set to the remaining range of data available\n//!     to read, if any.\n//! \\param[out] result Upon success, set to the decoded value read from the\n//!     buffer.\n//!\n//! \\return The length in bytes of the varint if the read succeeded,\n//!    `std::nullopt` otherwise. On success, updates `ring_buffer_read_range`\n//!    to reflect the bytes available to read.\n//!\n//! The varint can wrap around the end of the ring buffer, in which case the\n//! read continues at the beginning of the ring buffer (if the ring buffer is\n//! long enough).\ntemplate <typename RingBufferArrayType, typename IntegerType>\nstd::optional<Range::Length> ReadBase128VarintFromRingBuffer(\n    const RingBufferArrayType& ring_buffer_data,\n    internal::Range& ring_buffer_read_range,\n    IntegerType& result) {\n  static_assert(std::is_unsigned<IntegerType>::value);\n\n  result = 0;\n  uint8_t cur_varint_byte = 0;\n  constexpr uint8_t kValueMask = 0x7f;\n  constexpr uint8_t kContinuationMask = 0x80;\n  Range::Length length = 0;\n  do {\n    if (!ReadBytesFromRingBuffer(\n            ring_buffer_data, ring_buffer_read_range, &cur_varint_byte, 1)) {\n      // No capacity remaining in `ring_buffer_read_range` to read the varint.\n      return std::nullopt;\n    }\n    IntegerType cur_varint_value =\n        static_cast<IntegerType>(cur_varint_byte & kValueMask);\n\n    // This is equivalent to:\n    //\n    // result |= (cur_varint_value << (length * kBase128ByteValueBits));\n    //\n    // but checks the result at each step for overflow, which handles two types\n    // of invalid input:\n    //\n    // 1) Too many bytes with kContinuationMask set (e.g., trying to encode 6\n    //    bytes worth of data in a 32-bit value)\n    // 2) Too many bits in the final byte (e.g., the 5th byte for a 32-bit value\n    //    has bits 33 and 34 set)\n    IntegerType next_result_bits;\n    if (!base::CheckLsh(cur_varint_value, length * kBase128ByteValueBits)\n             .AssignIfValid(&next_result_bits)) {\n      return std::nullopt;\n    }\n    result |= next_result_bits;\n    ++length;\n  } while ((cur_varint_byte & kContinuationMask) == kContinuationMask);\n  return length;\n}\n\n//! \\brief Writes data from the source buffer into the ring buffer.\n//! \\param[in] source_buffer Buffer from which data will be read.\n//! \\param[in] source_buffer_length The length in bytes of `source_buffer`.\n//! \\param[in] ring_buffer_data The ring buffer into which data will be read.\n//! \\param[in,out] ring_buffer_write_range The range of the data available\n//!     to write. Upon return, set to the remaining range of data available\n//!     to write, if any.\n//!\n//! \\return `true` if write read succeeded, `false` otherwise. On success,\n//! updates\n//!     `ring_buffer_write_range` to reflect the bytes written.\n//!\n//! The bytes can wrap around the end of the ring buffer, in which case the\n//! write continues at the beginning of the ring buffer (if the ring buffer is\n//! long enough).\ntemplate <typename RingBufferArrayType>\nbool WriteBytesToRingBuffer(const uint8_t* const source_buffer,\n                            Range::Length source_buffer_length,\n                            RingBufferArrayType& ring_buffer_data,\n                            internal::Range& ring_buffer_write_range) {\n  const Range::Length ring_buffer_bytes_remaining =\n      RingBufferArraySize(ring_buffer_data) - ring_buffer_write_range.length;\n  if (source_buffer_length > ring_buffer_bytes_remaining) {\n    return false;\n  }\n  const Range::Length initial_write_length = std::min(\n      source_buffer_length,\n      RingBufferArraySize(ring_buffer_data) - ring_buffer_write_range.offset);\n  memcpy(&ring_buffer_data[ring_buffer_write_range.offset],\n         source_buffer,\n         initial_write_length);\n  if (initial_write_length < source_buffer_length) {\n    const Range::Length remaining_write_length =\n        source_buffer_length - initial_write_length;\n    memcpy(&ring_buffer_data[0],\n           source_buffer + initial_write_length,\n           remaining_write_length);\n  }\n  ring_buffer_write_range.offset =\n      (ring_buffer_write_range.offset + source_buffer_length) %\n      RingBufferArraySize(ring_buffer_data);\n  ring_buffer_write_range.length -= source_buffer_length;\n  return true;\n}\n\n//! \\brief Writes a single Base 128 varint-encoded little-endian unsigned\n//!     integer into the ring buffer.\n//! \\param[in] value The value to encode and write into the ring buffer.\n//! \\param[in] ring_buffer_data The ring buffer into which to write.\n//! \\param[in,out] ring_buffer_write_range The range of the data available\n//!     to write. Upon return, set to the remaining range of data available\n//!     to write, if any.\n//!\n//! \\return The length in bytes of the varint if the write succeeded,\n//!    `std::nullopt` otherwise. On success, updates `write_buffer_read_range`\n//!    to reflect the range available to write, if any.\n//!\n//! The varint can wrap around the end of the ring buffer, in which case the\n//! write continues at the beginning of the ring buffer (if the ring buffer is\n//! long enough).\ntemplate <typename RingBufferArrayType, typename IntegerType>\nstd::optional<int> WriteBase128VarintToRingBuffer(\n    IntegerType value,\n    RingBufferArrayType& ring_buffer_data,\n    internal::Range& ring_buffer_write_range) {\n  static_assert(std::is_unsigned<IntegerType>::value);\n\n  uint8_t cur_varint_byte;\n  constexpr uint8_t kValueMask = 0x7f;\n  constexpr uint8_t kContinuationMask = 0x80;\n\n  // Every varint encodes to at least 1 byte of data.\n  int length = 1;\n\n  while (value > kValueMask) {\n    cur_varint_byte =\n        (static_cast<uint8_t>(value) & kValueMask) | kContinuationMask;\n    if (!WriteBytesToRingBuffer(\n            &cur_varint_byte, 1, ring_buffer_data, ring_buffer_write_range)) {\n      return std::nullopt;\n    }\n    value >>= kBase128ByteValueBits;\n    ++length;\n  }\n  cur_varint_byte = static_cast<uint8_t>(value);\n  if (!WriteBytesToRingBuffer(\n          &cur_varint_byte, 1, ring_buffer_data, ring_buffer_write_range)) {\n    return std::nullopt;\n  }\n  return length;\n}\n\n}  // namespace internal\n\n//! \\brief Storage for a ring buffer which can hold up to\n//! `RingBufferCapacity`\n//!     bytes of Base 128-varint delimited variable-length items.\n//!\n//! This struct contains a header immediately followed by the ring buffer\n//! data. The current read offset and length are stored in `header.data_range`.\n//!\n//! The structure of this object is:\n//!\n//! `|magic|version|data_offset|data_length|ring_buffer_data|`\n//!\n//! To write data to this object, see `LengthDelimitedRingBufferWriter`.\n//! To read data from this object, see `LengthDelimitedRingBufferReader`.\n//!\n//! The bytes of this structure are suitable for direct serialization from\n//! memory to disk, e.g. as a crashpad::Annotation.\ntemplate <RingBufferCapacity Capacity>\nstruct RingBufferData final {\n  RingBufferData() = default;\n  RingBufferData(RingBufferData&) = delete;\n  RingBufferData& operator=(RingBufferData&) = delete;\n\n  //! \\brief The type of the array holding the data in this object.\n  using RingBufferArrayType = internal::RingBufferArray<Capacity>;\n\n  //! \\brief The type of the size in bytes of operations on this object.\n  using SizeType = internal::Range::Length;\n\n  //! \\brief Attempts to overwrite the contents of this object by deserializing\n  //!     the buffer into this object.\n  //! \\param[in] buffer The bytes to deserialize into this object.\n  //! \\param[in] length The length in bytes of `buffer`.\n  //!\n  //! \\return `true` if the buffer was a valid RingBufferData and this object\n  //!     has enough capacity to store its bytes, `false` otherwise.\n  bool DeserializeFromBuffer(const void* buffer, SizeType length) {\n    if (length < sizeof(header) || length > sizeof(header) + sizeof(data)) {\n      return false;\n    }\n    const Header* other_header = reinterpret_cast<const Header*>(buffer);\n    if (other_header->magic != kMagic || other_header->version != kVersion) {\n      return false;\n    }\n    header.data_range = other_header->data_range;\n    const uint8_t* other_ring_buffer_bytes =\n        reinterpret_cast<const uint8_t*>(buffer) + sizeof(*other_header);\n    const SizeType other_ring_buffer_len = length - sizeof(*other_header);\n    memcpy(&data[0], other_ring_buffer_bytes, other_ring_buffer_len);\n    return true;\n  }\n\n  //! \\return The current length in bytes of the data written to the ring\n  //!     buffer.\n  SizeType GetRingBufferLength() const {\n    internal::Range data_range = header.data_range;\n    return sizeof(header) + std::min(internal::RingBufferArraySize(data),\n                                     data_range.offset + data_range.length);\n  }\n\n  //! \\brief Resets the state of the ring buffer (e.g., for testing).\n  void ResetForTesting() { header.data_range = {0, 0}; }\n\n  //! \\brief The magic signature of the ring buffer.\n  static constexpr uint32_t kMagic = 0xcab00d1e;\n  //! \\brief The version of the ring buffer.\n  static constexpr uint32_t kVersion = 1;\n\n  //! \\brief A header containing metadata preceding the ring buffer data.\n  struct Header final {\n    Header() : magic(kMagic), version(kVersion), data_range({0, 0}) {}\n\n    //! \\brief The fixed magic value identifying this as a ring buffer.\n    const uint32_t magic;\n\n    //! \\brief The version of this ring buffer data.\n    const uint32_t version;\n\n    //! \\brief The range of readable data in the ring buffer.\n    internal::Range data_range;\n  };\n\n  //! \\brief The header containing ring buffer metadata.\n  Header header;\n\n  //! \\brief The bytes of the ring buffer data.\n  RingBufferArrayType data;\n\n  // This struct is persisted to disk, so its size must not change.\n  static_assert(sizeof(Header) == 16);\n  static_assert(Capacity <= std::numeric_limits<uint32_t>::max());\n};\n\n// Ensure the ring buffer is packed correctly at its default capacity.\nstatic_assert(\n    sizeof(RingBufferData<internal::kDefaultRingBufferDataCapacity>) ==\n    16 + internal::kDefaultRingBufferDataCapacity);\n\n// Allow just `RingBufferData foo;` to be declared without template arguments\n// using C++17 class template argument deduction.\ntemplate <\n    RingBufferCapacity Capacity = internal::kDefaultRingBufferDataCapacity>\nRingBufferData() -> RingBufferData<Capacity>;\n\n//! \\brief Reads variable-length data buffers from a `RingBufferData`,\n//!     delimited by Base128 varint-encoded length delimiters.\n//!\n//! Holds a reference to a `RingBufferData` with the capacity to hold\n//! `RingBufferDataType::size()` bytes of variable-length buffers each\n//! preceded by its length (encoded as a Base128 length varint).\n//!\n//! Provides reading capabilities via `Pop()`.\ntemplate <typename RingBufferDataType>\nclass LengthDelimitedRingBufferReader final {\n public:\n  //! \\brief Constructs a reader which holds a reference to `ring_buffer`.\n  //! \\param[in] ring_buffer The ring buffer from which data will be read.\n  //!     This object must outlive the lifetime of `ring_buffer`.\n  constexpr explicit LengthDelimitedRingBufferReader(\n      RingBufferDataType& ring_buffer)\n      : ring_buffer_(ring_buffer),\n        data_range_(ring_buffer_.header.data_range) {}\n\n  LengthDelimitedRingBufferReader(const LengthDelimitedRingBufferReader&) =\n      delete;\n  LengthDelimitedRingBufferReader& operator=(\n      const LengthDelimitedRingBufferReader&) = delete;\n\n  //! \\brief Pops off the next buffer from the front of the ring buffer.\n  //!\n  //! \\param[in] target_buffer On success, the buffer into which data will\n  //!     be read.\n  //! \\return On success, returns `true` and advances `ring_buffer.data_range`\n  //!     past the end of the buffer read. Otherwise, returns `false`.\n  bool Pop(std::vector<uint8_t>& target_buffer) {\n    return PopWithRange(target_buffer, data_range_);\n  }\n\n  //! \\brief Resets the state of the reader (e.g., for testing).\n  void ResetForTesting() { data_range_ = {0, 0}; }\n\n private:\n  //! \\brief Pops off the next buffer from the front of the ring buffer.\n  //! \\param[in] target_buffer On success, the buffer into which data will\n  //!     be read.\n  //! \\param[in,out] data_range The range of data available to read.\n  //!     On success, updated to the remaining range avilable to read.\n  //! \\return On success, returns `true` and advances `ring_buffer.data_range`\n  //!     past the end of the buffer read. Otherwise, returns `false`.\n  bool PopWithRange(std::vector<uint8_t>& target_buffer,\n                    internal::Range& data_range) {\n    internal::Range::Length buffer_length;\n    if (!ReadBase128VarintFromRingBuffer(\n            ring_buffer_.data, data_range, buffer_length)) {\n      return false;\n    }\n    if (buffer_length == 0) {\n      // A zero-length buffer means the buffer was truncated in the middle of a\n      // Push().\n      return false;\n    }\n    const auto previous_target_buffer_size = target_buffer.size();\n    target_buffer.resize(previous_target_buffer_size + buffer_length);\n    if (!ReadBytesFromRingBuffer(ring_buffer_.data,\n                                 data_range,\n                                 &target_buffer[previous_target_buffer_size],\n                                 buffer_length)) {\n      return false;\n    }\n    return true;\n  }\n\n  //! \\brief Reference to the ring buffer from which data is read.\n  const RingBufferDataType& ring_buffer_;\n  //! \\brief Range of data currently available to read.\n  internal::Range data_range_;\n};\n\n// Allow just `LengthDelimitedRingBufferReader reader(foo);` to be declared\n// without template arguments using C++17 class template argument deduction.\ntemplate <typename RingBufferDataType>\nLengthDelimitedRingBufferReader(RingBufferDataType&)\n    -> LengthDelimitedRingBufferReader<RingBufferDataType>;\n\n//! \\brief Writes variable-length data buffers to a `RingBufferData`,\n//!     delimited by Base128 varint-encoded length delimiters.\n//!\n//! Holds a reference to a `RingBufferData` with the capacity to hold\n//! `RingBufferDataType::size()` bytes of variable-length buffers each\n//! preceded by its length (encoded as a Base128 length varint).\n//!\n//! Provides writing capabilities via `Push()`.\ntemplate <typename RingBufferDataType>\nclass LengthDelimitedRingBufferWriter final {\n public:\n  //! \\brief Constructs a writer which holds a reference to `ring_buffer`.\n  //! \\param[in] ring_buffer The ring buffer into which data will be written.\n  //!     This object must outlive the lifetime of `ring_buffer`.\n  constexpr explicit LengthDelimitedRingBufferWriter(\n      RingBufferDataType& ring_buffer)\n      : ring_buffer_(ring_buffer), ring_buffer_write_offset_(0) {}\n\n  LengthDelimitedRingBufferWriter(const LengthDelimitedRingBufferWriter&) =\n      delete;\n  LengthDelimitedRingBufferWriter& operator=(\n      const LengthDelimitedRingBufferWriter&) = delete;\n\n  //! \\brief Writes data to the ring buffer.\n  //!\n  //! If there is not enough room remaining in the ring buffer to store the new\n  //! data, old data will be removed from the ring buffer in FIFO order until\n  //! there is room for the new data.\n  //!\n  //! \\param[in] buffer The data to be written.\n  //! \\param[in] buffer_length The lengh of `buffer`, in bytes.\n  //! \\return On success, returns `true`, updates `ring_buffer.data_range`\n  //!     to reflect the remaining data available to read, and updates\n  //!     `ring_buffer_write_offset_` to reflec the current write positionl.\n  //!     Otherwise, returns `false`.\n  bool Push(const void* const buffer,\n            typename RingBufferDataType::SizeType buffer_length) {\n    if (buffer_length == 0) {\n      // Pushing a zero-length buffer is not allowed\n      // (`LengthDelimitedRingBufferWriter` reserves that to represent a\n      // temporarily truncated item below).\n      return false;\n    }\n    const internal::Range::Length buffer_varint_encoded_length =\n        internal::Base128VarintEncodedLength(buffer_length);\n    const internal::Range::Length bytes_needed =\n        buffer_varint_encoded_length + buffer_length;\n    if (bytes_needed > ring_buffer_.data.size()) {\n      return false;\n    }\n    // If needed, move the readable region forward one buffer at a time to make\n    // room for `buffer_length` bytes of new data.\n    auto readable_data_range = ring_buffer_.header.data_range;\n    internal::Range::Length bytes_available =\n        internal::RingBufferArraySize(ring_buffer_.data) -\n        readable_data_range.length;\n    while (bytes_available < bytes_needed) {\n      internal::Range::Length bytes_to_skip;\n      auto varint_length = ReadBase128VarintFromRingBuffer(\n          ring_buffer_.data, readable_data_range, bytes_to_skip);\n      if (!varint_length.has_value()) {\n        return false;\n      }\n      // Skip past the next entry including its prepended varint length.\n      readable_data_range.offset =\n          (readable_data_range.offset + bytes_to_skip) %\n          internal::RingBufferArraySize(ring_buffer_.data);\n      readable_data_range.length -= bytes_to_skip;\n      bytes_available += varint_length.value() + bytes_to_skip;\n    }\n    // Write the varint containing `buffer_length` to the current write\n    // position.\n    internal::Range write_range = {\n        ring_buffer_write_offset_,\n        bytes_needed,\n    };\n\n    internal::WriteBase128VarintToRingBuffer(\n        buffer_length, ring_buffer_.data, write_range);\n    // Next, write the bytes from `buffer`.\n    internal::WriteBytesToRingBuffer(\n        reinterpret_cast<const uint8_t* const>(buffer),\n        buffer_length,\n        ring_buffer_.data,\n        write_range);\n    // Finally, update the write position and read data range taking into\n    // account any items skipped to make room plus the new buffer's varint\n    // length and the new buffer's length.\n    ring_buffer_write_offset_ = write_range.offset;\n    const internal::Range final_data_range = {\n        readable_data_range.offset,\n        readable_data_range.length + bytes_needed,\n    };\n    ring_buffer_.header.data_range = final_data_range;\n    return true;\n  }\n\n  //! \\brief Resets the state of the ring buffer and writer (e.g., for testing).\n  void ResetForTesting() {\n    ring_buffer_.ResetForTesting();\n    ring_buffer_write_offset_ = 0;\n  }\n\n private:\n  //! \\brief Reference to the ring buffer from which data is written.\n  RingBufferDataType& ring_buffer_;\n\n  // \\brief Current write position next time `Push()` is invoked.\n  internal::Range::Offset ring_buffer_write_offset_;\n};\n\n// Allow just `LengthDelimitedRingBufferWriter writer(foo);` to be declared\n// without template arguments using C++17 class template argument deduction.\ntemplate <typename RingBufferDataType>\nLengthDelimitedRingBufferWriter(RingBufferDataType&)\n    -> LengthDelimitedRingBufferWriter<RingBufferDataType>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_LENGTH_DELIMITED_RING_BUFFER_H_\n"
  },
  {
    "path": "client/length_delimited_ring_buffer_test.cc",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/length_delimited_ring_buffer.h\"\n\n#include <stdint.h>\n\n#include <array>\n#include <string>\n#include <vector>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing testing::Eq;\nusing testing::IsFalse;\nusing testing::IsTrue;\n\n// Buffer with magic 0xcab00d1e, version 1, read_pos 0, length 3, and 3 bytes of\n// data (1 varint length, 2 bytes data)\nconstexpr char kValidBufferSize3[] =\n    \"\\x1e\\x0d\\xb0\\xca\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x02\\x42\"\n    \"\\x23\";\nconstexpr size_t kValidBufferSize3Len =\n    sizeof(kValidBufferSize3) - 1;  // Remove trailing NUL.\n\n// Buffer with magic 0xcab00d1e, version 8, read_pos 0, length 3, and 3 bytes of\n// data (1 varint length, 2 bytes data).\nconstexpr char kInvalidVersionBuffer[] =\n    \"\\x1e\\x0d\\xb0\\xca\\x08\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x02\\xab\"\n    \"\\xcd\";\nconstexpr size_t kInvalidVersionBufferLen =\n    sizeof(kInvalidVersionBuffer) - 1;  // Remove trailing NUL.\n\n// Buffer representing process which crashed while in the middle of a Push()\n// operation, with a previously-Push()ed buffer whose length was zeroed out at\n// the start.\nconstexpr char kMidCrashBuffer[] =\n    \"\\x1e\\x0d\\xb0\\xca\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x03\\x00\\x00\\x00\\x00\\x42\"\n    \"\\x23\";\nconstexpr size_t kMidCrashBufferLen =\n    sizeof(kMidCrashBuffer) - 1;  // Remove trailing NUL.\n\nconstexpr uint8_t kHello[] = {0x68, 0x65, 0x6c, 0x6c, 0x6f};\n\n// Invalid buffer containing malformed varint in data payload (Base 128 varint\n// with length 6, which would represent a data length > 32 bits).\nconstexpr char kInvalidBase128VarintBuffer[] =\n    \"\\x1e\\x0d\\xb0\\xca\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x06\\x00\\x00\\x00\\x80\\x80\"\n    \"\\x80\\x80\\x80\\x01\";\nconstexpr size_t kInvalidBase128VarintBufferLen =\n    sizeof(kInvalidBase128VarintBuffer) - 1;  // Remove trailing NUL.\n\n// Invalid buffer containing malformed varint in data payload (Base 128 varint\n// with length 5 but bits 33 and 34 set, which would represent a data length >\n// 32 bits).\nconstexpr char kInvalidBase128VarintBits33And34SetBuffer[] =\n    \"\\x1e\\x0d\\xb0\\xca\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x05\\x00\\x00\\x00\\x80\\x80\"\n    \"\\x80\\x80\\x60\";\nconstexpr size_t kInvalidBase128VarintBits33And34SetBufferLen =\n    sizeof(kInvalidBase128VarintBits33And34SetBuffer) -\n    1;  // Remove trailing NUL.\n\n// Invalid buffer containing too-short data payload (varint length indicates\n// payload length is 4 but payload only contains 3 bytes).\nconstexpr char kInvalidPayloadBufferTooShort[] =\n    \"\\x1e\\x0d\\xb0\\xca\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\x04\"\n    \"\\x42\\x42\\x42\";\nconstexpr size_t kInvalidPayloadBufferTooShortLen =\n    sizeof(kInvalidPayloadBufferTooShort) - 1;  // Remove trailing NUL.\n\nTEST(LengthDelimitedRingBufferTest,\n     RingBufferDataShouldStartWithMagicAndVersion) {\n  RingBufferData ring_buffer;\n  const void* ring_buffer_bytes = static_cast<const void*>(&ring_buffer);\n  EXPECT_THAT(memcmp(ring_buffer_bytes, \"\\x1e\\x0d\\xb0\\xca\\x01\\x00\\x00\\x00\", 8),\n              Eq(0));\n}\n\nTEST(LengthDelimitedRingBufferTest,\n     EmptyBufferSizeShouldIncludeHeaderInRingBufferLength) {\n  RingBufferData ring_buffer;\n  EXPECT_THAT(ring_buffer.GetRingBufferLength(),\n              Eq(16U));  // 4 for uint32 magic, 4 for uint32 version, 4 for\n                         // uint32 read_pos, 4 for uint32 length\n}\n\nTEST(LengthDelimitedRingBufferTest,\n     NonEmptyBufferSizeShouldIncludeHeaderAndData) {\n  RingBufferData ring_buffer;\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  ASSERT_THAT(writer.Push(kHello, sizeof(kHello)), IsTrue());\n  EXPECT_THAT(ring_buffer.GetRingBufferLength(),\n              Eq(22U));  // 16 for header, 1 for varint length, 5 for data\n}\n\nTEST(LengthDelimitedRingBufferTest, PopOnEmptyBufferShouldFail) {\n  RingBufferData ring_buffer;\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> result;\n  EXPECT_THAT(reader.Pop(result), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferTest, PushZeroLengthShouldFail) {\n  RingBufferData ring_buffer;\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  ASSERT_THAT(writer.Push(nullptr, 0), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferTest, PushExactlyBufferSizeThenPopShouldSucceed) {\n  RingBufferData ring_buffer;\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  ASSERT_THAT(writer.Push(kHello, sizeof(kHello)), IsTrue());\n\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> result;\n  EXPECT_THAT(reader.Pop(result), IsTrue());\n  const std::vector<uint8_t> expected_first = {0x68, 0x65, 0x6c, 0x6c, 0x6f};\n  EXPECT_THAT(result, Eq(expected_first));\n}\n\nTEST(LengthDelimitedRingBufferTest, PushLargerThanBufferSizeShouldFail) {\n  RingBufferData<4> ring_buffer;\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  EXPECT_THAT(writer.Push(kHello, sizeof(kHello)), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferTest,\n     PushUntilFullThenPopUntilEmptyShouldReturnInFIFOOrder) {\n  RingBufferData<4> ring_buffer;\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  constexpr uint8_t a = 0x41;\n  EXPECT_THAT(writer.Push(&a, sizeof(a)),\n              IsTrue());  // Writes 2 bytes (1 for length)\n  constexpr uint8_t b = 0x42;\n  EXPECT_THAT(writer.Push(&b, sizeof(b)),\n              IsTrue());  // Writes 2 bytes (1 for length)\n\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> first;\n  EXPECT_THAT(reader.Pop(first), IsTrue());\n  const std::vector<uint8_t> expected_first = {0x41};\n  EXPECT_THAT(first, Eq(expected_first));\n\n  std::vector<uint8_t> second;\n  EXPECT_THAT(reader.Pop(second), IsTrue());\n  const std::vector<uint8_t> expected_second = {0x42};\n  EXPECT_THAT(second, Eq(expected_second));\n\n  std::vector<uint8_t> empty;\n  EXPECT_THAT(reader.Pop(empty), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferTest,\n     PushThenPopBuffersOfDifferingLengthsShouldReturnBuffers) {\n  RingBufferData<5> ring_buffer;\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  constexpr uint8_t ab[2] = {0x41, 0x42};\n  EXPECT_THAT(writer.Push(ab, sizeof(ab)),\n              IsTrue());  // Writes 3 bytes (1 for length)\n  constexpr uint8_t c = 0x43;\n  EXPECT_THAT(writer.Push(&c, sizeof(c)),\n              IsTrue());  // Writes 2 bytes (1 for length)\n\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> first;\n  EXPECT_THAT(reader.Pop(first), IsTrue());\n  const std::vector<uint8_t> expected_first = {0x41, 0x42};\n  EXPECT_THAT(first, Eq(expected_first));\n\n  std::vector<uint8_t> second;\n  EXPECT_THAT(reader.Pop(second), IsTrue());\n  const std::vector<uint8_t> expected_second = {0x43};\n  EXPECT_THAT(second, Eq(expected_second));\n\n  std::vector<uint8_t> empty;\n  EXPECT_THAT(reader.Pop(empty), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferDataTest, PushOnFullBufferShouldOverwriteOldest) {\n  RingBufferData<4> ring_buffer;\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  constexpr uint8_t a = 0x41;\n  EXPECT_THAT(writer.Push(&a, sizeof(a)),\n              IsTrue());  // Writes 2 bytes (1 for length)\n  constexpr uint8_t b = 0x42;\n  EXPECT_THAT(writer.Push(&b, sizeof(b)),\n              IsTrue());  // Writes 2 bytes (1 for length)\n  constexpr uint8_t c = 0x43;\n  EXPECT_THAT(writer.Push(&c, sizeof(c)), IsTrue());  // Should overwrite \"A\"\n\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> first;\n  EXPECT_THAT(reader.Pop(first), IsTrue());\n  const std::vector<uint8_t> expected_first = {uint8_t{0x42}};\n  EXPECT_THAT(first, Eq(expected_first));\n\n  std::vector<uint8_t> second;\n  EXPECT_THAT(reader.Pop(second), IsTrue());\n  const std::vector<uint8_t> expected_second = {uint8_t{0x43}};\n  EXPECT_THAT(second, Eq(expected_second));\n}\n\nTEST(LengthDelimitedRingBufferDataTest,\n     PushOnFullBufferShouldOverwriteMultipleOldest) {\n  RingBufferData<4> ring_buffer;\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  constexpr uint8_t a = 0x41;\n  EXPECT_THAT(writer.Push(&a, sizeof(a)),\n              IsTrue());  // Writes 2 bytes (1 for length)\n  constexpr uint8_t b = 0x42;\n  EXPECT_THAT(writer.Push(&b, sizeof(b)),\n              IsTrue());  // Writes 2 bytes (1 for length)\n  constexpr uint8_t cd[] = {0x43, 0x44};\n  EXPECT_THAT(writer.Push(cd, sizeof(cd)),\n              IsTrue());  // Needs 3 bytes; should overwrite \"A\" and \"B\"\n\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> first;\n  EXPECT_THAT(reader.Pop(first), IsTrue());\n  const std::vector<uint8_t> expected_first = {0x43, 0x44};\n  EXPECT_THAT(first, Eq(expected_first));\n\n  std::vector<uint8_t> empty;\n  EXPECT_THAT(reader.Pop(empty), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferDataTest, PushThenPopWithLengthVarintTwoBytes) {\n  RingBufferData ring_buffer;\n  decltype(ring_buffer)::SizeType size = 150;\n  std::string s(size, 'X');\n  LengthDelimitedRingBufferWriter writer(ring_buffer);\n  ASSERT_THAT(writer.Push(reinterpret_cast<const uint8_t*>(s.c_str()), size),\n              IsTrue());\n\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> first;\n  EXPECT_THAT(reader.Pop(first), IsTrue());\n  std::string result(reinterpret_cast<const char*>(first.data()), first.size());\n  EXPECT_THAT(result, Eq(s));\n}\n\nTEST(LengthDelimitedRingBufferDataTest, DeserializeFromTooShortShouldFail) {\n  RingBufferData<1> ring_buffer;\n  EXPECT_THAT(ring_buffer.DeserializeFromBuffer(nullptr, 0), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferDataTest, DeserializeFromTooLongShouldFail) {\n  RingBufferData<1> ring_buffer;\n  // This buffer is size 3; it won't fit in the template arg (size 1).\n  EXPECT_THAT(ring_buffer.DeserializeFromBuffer(\n                  reinterpret_cast<const uint8_t*>(kValidBufferSize3),\n                  kValidBufferSize3Len),\n              IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferDataTest,\n     DeserializeFromInvalidVersionShouldFail) {\n  RingBufferData<3> ring_buffer;\n  EXPECT_THAT(ring_buffer.DeserializeFromBuffer(\n                  reinterpret_cast<const uint8_t*>(kInvalidVersionBuffer),\n                  kInvalidVersionBufferLen),\n              IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferDataTest,\n     DeserializeFromInvalidVarintLengthShouldSucceedButPopShouldFail) {\n  RingBufferData ring_buffer;\n  EXPECT_THAT(ring_buffer.DeserializeFromBuffer(\n                  reinterpret_cast<const uint8_t*>(kInvalidBase128VarintBuffer),\n                  kInvalidBase128VarintBufferLen),\n              IsTrue());\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> data;\n  EXPECT_THAT(reader.Pop(data), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferDataTest,\n     DeserializeFromInvalidVarintBitsShouldSucceedButPopShouldFail) {\n  RingBufferData ring_buffer;\n  EXPECT_THAT(ring_buffer.DeserializeFromBuffer(\n                  reinterpret_cast<const uint8_t*>(\n                      kInvalidBase128VarintBits33And34SetBuffer),\n                  kInvalidBase128VarintBits33And34SetBufferLen),\n              IsTrue());\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> data;\n  EXPECT_THAT(reader.Pop(data), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferDataTest,\n     DeserializeFromInvalidPayloadBufferTooShortShouldSucceedButPopShouldFail) {\n  RingBufferData ring_buffer;\n  EXPECT_THAT(\n      ring_buffer.DeserializeFromBuffer(\n          reinterpret_cast<const uint8_t*>(kInvalidPayloadBufferTooShort),\n          kInvalidPayloadBufferTooShortLen),\n      IsTrue());\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> data;\n  EXPECT_THAT(reader.Pop(data), IsFalse());\n}\n\nTEST(LengthDelimitedRingBufferDataTest,\n     DeserializeFromFullBufferShouldSucceed) {\n  RingBufferData<3> ring_buffer;\n  EXPECT_THAT(ring_buffer.DeserializeFromBuffer(\n                  reinterpret_cast<const uint8_t*>(kValidBufferSize3),\n                  kValidBufferSize3Len),\n              IsTrue());\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  std::vector<uint8_t> data;\n  EXPECT_THAT(reader.Pop(data), IsTrue());\n  const std::vector<uint8_t> expected = {0x42, 0x23};\n  EXPECT_THAT(data, Eq(expected));\n}\n\nTEST(LengthDelimitedRingBufferDataTest,\n     DeserializeFromMidCrashBufferShouldSucceedButSubsequentPopShouldFail) {\n  RingBufferData ring_buffer;\n  EXPECT_THAT(ring_buffer.DeserializeFromBuffer(\n                  reinterpret_cast<const uint8_t*>(kMidCrashBuffer),\n                  kMidCrashBufferLen),\n              IsTrue());\n  LengthDelimitedRingBufferReader reader(ring_buffer);\n  // Pop should fail since the length was written to be 0.\n  std::vector<uint8_t> data;\n  EXPECT_THAT(reader.Pop(data), IsFalse());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/prune_crash_reports.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/prune_crash_reports.h\"\n\n#include <sys/stat.h>\n#include <stdint.h>\n\n#include <algorithm>\n#include <vector>\n\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\nsize_t PruneCrashReportDatabase(CrashReportDatabase* database,\n                              PruneCondition* condition) {\n  std::vector<CrashReportDatabase::Report> all_reports;\n  CrashReportDatabase::OperationStatus status;\n\n  status = database->GetPendingReports(&all_reports);\n  if (status != CrashReportDatabase::kNoError) {\n    LOG(ERROR) << \"PruneCrashReportDatabase: Failed to get pending reports\";\n    return 0;\n  }\n\n  std::vector<CrashReportDatabase::Report> completed_reports;\n  status = database->GetCompletedReports(&completed_reports);\n  if (status != CrashReportDatabase::kNoError) {\n    LOG(ERROR) << \"PruneCrashReportDatabase: Failed to get completed reports\";\n    return 0;\n  }\n  all_reports.insert(all_reports.end(), completed_reports.begin(),\n                     completed_reports.end());\n\n  std::sort(all_reports.begin(), all_reports.end(),\n      [](const CrashReportDatabase::Report& lhs,\n         const CrashReportDatabase::Report& rhs) {\n        return lhs.creation_time > rhs.creation_time;\n      });\n\n  size_t num_pruned = 0;\n  for (const auto& report : all_reports) {\n    if (condition->ShouldPruneReport(report)) {\n      status = database->DeleteReport(report.uuid);\n      if (status != CrashReportDatabase::kNoError) {\n        LOG(ERROR) << \"Database Pruning: Failed to remove report \"\n                   << report.uuid.ToString();\n      } else {\n        num_pruned++;\n      }\n    }\n  }\n\n  return num_pruned;\n\n  // TODO(rsesek): For databases that do not use a directory structure, it is\n  // possible for the metadata sidecar to become corrupted and thus leave\n  // orphaned crash report files on-disk. https://crashpad.chromium.org/bug/66\n}\n\n// static\nstd::unique_ptr<PruneCondition> PruneCondition::GetDefault() {\n  // DatabaseSizePruneCondition must be the LHS so that it is always evaluated,\n  // due to the short-circuting behavior of BinaryPruneCondition.\n  return std::make_unique<BinaryPruneCondition>(\n      BinaryPruneCondition::OR,\n      new DatabaseSizePruneCondition(1024 * 128),\n      new AgePruneCondition(365));\n}\n\nstatic const time_t kSecondsInDay = 60 * 60 * 24;\n\nAgePruneCondition::AgePruneCondition(int max_age_in_days)\n    : oldest_report_time_(\n        ((time(nullptr) - (max_age_in_days * kSecondsInDay))\n             / kSecondsInDay) * kSecondsInDay) {}\n\nAgePruneCondition::~AgePruneCondition() {}\n\nbool AgePruneCondition::ShouldPruneReport(\n    const CrashReportDatabase::Report& report) {\n  return report.creation_time < oldest_report_time_;\n}\n\nDatabaseSizePruneCondition::DatabaseSizePruneCondition(size_t max_size_in_kb)\n    : max_size_in_kb_(max_size_in_kb), measured_size_in_kb_(0) {}\n\nDatabaseSizePruneCondition::~DatabaseSizePruneCondition() {}\n\nbool DatabaseSizePruneCondition::ShouldPruneReport(\n    const CrashReportDatabase::Report& report) {\n  // Round up fractional KB to the next 1-KB boundary.\n  measured_size_in_kb_ +=\n      static_cast<size_t>((report.total_size + 1023) / 1024);\n  return measured_size_in_kb_ > max_size_in_kb_;\n}\n\nBinaryPruneCondition::BinaryPruneCondition(\n    Operator op, PruneCondition* lhs, PruneCondition* rhs)\n    : op_(op), lhs_(lhs), rhs_(rhs) {}\n\nBinaryPruneCondition::~BinaryPruneCondition() {}\n\nbool BinaryPruneCondition::ShouldPruneReport(\n    const CrashReportDatabase::Report& report) {\n  switch (op_) {\n    case AND:\n      return lhs_->ShouldPruneReport(report) && rhs_->ShouldPruneReport(report);\n    case OR:\n      return lhs_->ShouldPruneReport(report) || rhs_->ShouldPruneReport(report);\n  }\n  NOTREACHED();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/prune_crash_reports.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_\n#define CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_\n\n#include <sys/types.h>\n#include <time.h>\n\n#include <memory>\n\n#include \"client/crash_report_database.h\"\n\nnamespace crashpad {\n\nclass PruneCondition;\n\n//! \\brief Deletes crash reports from \\a database that match \\a condition.\n//!\n//! This function can be used to remove old or large reports from the database.\n//! The \\a condition will be evaluated against each report in the \\a database,\n//! sorted in descending order by CrashReportDatabase::Report::creation_time.\n//! This guarantee allows conditions to be stateful.\n//!\n//! \\param[in] database The database from which crash reports will be deleted.\n//! \\param[in] condition The condition against which all reports in the database\n//!     will be evaluated.\n//!\n//! \\return The number of deleted crash reports.\nsize_t PruneCrashReportDatabase(CrashReportDatabase* database,\n                                PruneCondition* condition);\n\nstd::unique_ptr<PruneCondition> GetDefaultDatabasePruneCondition();\n\n//! \\brief An abstract base class for evaluating crash reports for deletion.\n//!\n//! When passed to PruneCrashReportDatabase(), each crash report in the\n//! database will be evaluated according to ShouldPruneReport(). The reports\n//! are evaluated serially in descending sort order by\n//! CrashReportDatabase::Report::creation_time.\nclass PruneCondition {\n public:\n  //! \\brief Returns a sensible default condition for removing obsolete crash\n  //!     reports.\n  //!\n  //! The default is to keep reports for one year or a maximum database size\n  //! of 128 MB.\n  //!\n  //! \\return A PruneCondition for use with PruneCrashReportDatabase().\n  static std::unique_ptr<PruneCondition> GetDefault();\n\n  virtual ~PruneCondition() {}\n\n  //! \\brief Evaluates a crash report for deletion.\n  //!\n  //! \\param[in] report The crash report to evaluate.\n  //!\n  //! \\return `true` if the crash report should be deleted, `false` if it\n  //!     should be kept.\n  virtual bool ShouldPruneReport(const CrashReportDatabase::Report& report) = 0;\n};\n\n//! \\brief A PruneCondition that deletes reports older than the specified number\n//!     days.\nclass AgePruneCondition final : public PruneCondition {\n public:\n  //! \\brief Creates a PruneCondition based on Report::creation_time.\n  //!\n  //! \\param[in] max_age_in_days Reports created more than this many days ago\n  //!     will be deleted.\n  explicit AgePruneCondition(int max_age_in_days);\n\n  AgePruneCondition(const AgePruneCondition&) = delete;\n  AgePruneCondition& operator=(const AgePruneCondition&) = delete;\n\n  ~AgePruneCondition();\n\n  bool ShouldPruneReport(const CrashReportDatabase::Report& report) override;\n\n private:\n  const time_t oldest_report_time_;\n};\n\n//! \\brief A PruneCondition that deletes older reports to keep the total\n//!     Crashpad database size under the specified limit.\nclass DatabaseSizePruneCondition final : public PruneCondition {\n public:\n  //! \\brief Creates a PruneCondition that will keep newer reports, until the\n  //!     sum of the size of all reports is not smaller than \\a max_size_in_kb.\n  //!     After the limit is reached, older reports will be pruned.\n  //!\n  //! \\param[in] max_size_in_kb The maximum number of kilobytes that all crash\n  //!     reports should consume.\n  explicit DatabaseSizePruneCondition(size_t max_size_in_kb);\n\n  DatabaseSizePruneCondition(const DatabaseSizePruneCondition&) = delete;\n  DatabaseSizePruneCondition& operator=(const DatabaseSizePruneCondition&) =\n      delete;\n\n  ~DatabaseSizePruneCondition();\n\n  bool ShouldPruneReport(const CrashReportDatabase::Report& report) override;\n\n private:\n  const size_t max_size_in_kb_;\n  size_t measured_size_in_kb_;\n};\n\n//! \\brief A PruneCondition that conjoins two other PruneConditions.\nclass BinaryPruneCondition final : public PruneCondition {\n public:\n  enum Operator {\n    AND,\n    OR,\n  };\n\n  //! \\brief Evaluates two sub-conditions according to the specified logical\n  //!     operator.\n  //!\n  //! This implements left-to-right evaluation. For Operator::AND, this means\n  //! if the \\a lhs is `false`, the \\a rhs will not be consulted. Similarly,\n  //! with Operator::OR, if the \\a lhs is `true`, the \\a rhs will not be\n  //! consulted.\n  //!\n  //! \\param[in] op The logical operator to apply on \\a lhs and \\a rhs.\n  //! \\param[in] lhs The left-hand side of \\a op. This class takes ownership.\n  //! \\param[in] rhs The right-hand side of \\a op. This class takes ownership.\n  BinaryPruneCondition(Operator op, PruneCondition* lhs, PruneCondition* rhs);\n\n  BinaryPruneCondition(const BinaryPruneCondition&) = delete;\n  BinaryPruneCondition& operator=(const BinaryPruneCondition&) = delete;\n\n  ~BinaryPruneCondition();\n\n  bool ShouldPruneReport(const CrashReportDatabase::Report& report) override;\n\n private:\n  const Operator op_;\n  std::unique_ptr<PruneCondition> lhs_;\n  std::unique_ptr<PruneCondition> rhs_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_\n"
  },
  {
    "path": "client/prune_crash_reports_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/prune_crash_reports.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n#include <algorithm>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"base/numerics/safe_conversions.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass MockDatabase : public CrashReportDatabase {\n public:\n  // CrashReportDatabase:\n  MOCK_METHOD(Settings*, GetSettings, (), (override));\n  MOCK_METHOD(OperationStatus,\n              PrepareNewCrashReport,\n              (std::unique_ptr<NewReport>*),\n              (override));\n  MOCK_METHOD(OperationStatus,\n              LookUpCrashReport,\n              (const UUID&, Report*),\n              (override));\n  MOCK_METHOD(OperationStatus,\n              GetPendingReports,\n              (std::vector<Report>*),\n              (override));\n  MOCK_METHOD(OperationStatus,\n              GetCompletedReports,\n              (std::vector<Report>*),\n              (override));\n  MOCK_METHOD(OperationStatus,\n              GetReportForUploading,\n              (const UUID&,\n               std::unique_ptr<const UploadReport>*,\n               bool report_metrics),\n              (override));\n  MOCK_METHOD(OperationStatus,\n              RecordUploadAttempt,\n              (UploadReport*, bool, const std::string&),\n              (override));\n  MOCK_METHOD(OperationStatus,\n              SkipReportUpload,\n              (const UUID&, Metrics::CrashSkippedReason),\n              (override));\n  MOCK_METHOD(OperationStatus, DeleteReport, (const UUID&), (override));\n  MOCK_METHOD(OperationStatus, RequestUpload, (const UUID&), (override));\n  MOCK_METHOD(base::FilePath, DatabasePath, (), (override));\n\n  // Google Mock doesn't support mocking methods with non-copyable types such as\n  // unique_ptr.\n  OperationStatus FinishedWritingCrashReport(std::unique_ptr<NewReport> report,\n                                             UUID* uuid) override {\n    return kNoError;\n  }\n};\n\ntime_t NDaysAgo(int num_days) {\n  return time(nullptr) - (num_days * 60 * 60 * 24);\n}\n\nTEST(PruneCrashReports, AgeCondition) {\n  CrashReportDatabase::Report report_80_days;\n  report_80_days.creation_time = NDaysAgo(80);\n\n  CrashReportDatabase::Report report_10_days;\n  report_10_days.creation_time = NDaysAgo(10);\n\n  CrashReportDatabase::Report report_30_days;\n  report_30_days.creation_time = NDaysAgo(30);\n\n  AgePruneCondition condition(30);\n  EXPECT_TRUE(condition.ShouldPruneReport(report_80_days));\n  EXPECT_FALSE(condition.ShouldPruneReport(report_10_days));\n  EXPECT_FALSE(condition.ShouldPruneReport(report_30_days));\n}\n\nTEST(PruneCrashReports, SizeCondition) {\n  CrashReportDatabase::Report report_1k;\n  report_1k.total_size = 1024u;\n  CrashReportDatabase::Report report_3k;\n  report_3k.total_size = 1024u * 3u;\n  CrashReportDatabase::Report report_unset_size;\n\n  {\n    DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1);\n    // |report_1k| should not be pruned as the cumulated size is not past 1kB\n    // yet.\n    EXPECT_FALSE(condition.ShouldPruneReport(report_1k));\n    // |report_3k| should be pruned as the cumulated size is now past 1kB.\n    EXPECT_TRUE(condition.ShouldPruneReport(report_3k));\n  }\n\n  {\n    DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1);\n    // |report_3k| should be pruned as the cumulated size is already past 1kB.\n    EXPECT_TRUE(condition.ShouldPruneReport(report_3k));\n  }\n\n  {\n    DatabaseSizePruneCondition condition(/*max_size_in_kb=*/6);\n    // |report_3k| should not be pruned as the cumulated size is not past 6kB\n    // yet.\n    EXPECT_FALSE(condition.ShouldPruneReport(report_3k));\n    // |report_3k| should not be pruned as the cumulated size is not past 6kB\n    // yet.\n    EXPECT_FALSE(condition.ShouldPruneReport(report_3k));\n    // |report_1k| should be pruned as the cumulated size is now past 6kB.\n    EXPECT_TRUE(condition.ShouldPruneReport(report_1k));\n  }\n\n  {\n    DatabaseSizePruneCondition condition(/*max_size_in_kb=*/0);\n    // |report_unset_size| should not be pruned as its size is 0, regardless of\n    // how many times we try to prune it.\n    EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));\n    EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));\n    EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));\n    EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));\n    EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));\n    // |report_1k| should be pruned as the cumulated size is now past 0kB.\n    EXPECT_TRUE(condition.ShouldPruneReport(report_1k));\n  }\n}\n\nclass StaticCondition final : public PruneCondition {\n public:\n  explicit StaticCondition(bool value) : value_(value), did_execute_(false) {}\n\n  StaticCondition(const StaticCondition&) = delete;\n  StaticCondition& operator=(const StaticCondition&) = delete;\n\n  ~StaticCondition() {}\n\n  bool ShouldPruneReport(const CrashReportDatabase::Report& report) override {\n    did_execute_ = true;\n    return value_;\n  }\n\n  bool did_execute() const { return did_execute_; }\n\n private:\n  const bool value_;\n  bool did_execute_;\n};\n\nTEST(PruneCrashReports, BinaryCondition) {\n  static constexpr struct {\n    const char* name;\n    BinaryPruneCondition::Operator op;\n    bool lhs_value;\n    bool rhs_value;\n    bool cond_result;\n    bool lhs_executed;\n    bool rhs_executed;\n  } kTests[] = {\n      // clang-format off\n    {\"false && false\",\n     BinaryPruneCondition::AND, false, false,\n     false, true, false},\n    {\"false && true\",\n     BinaryPruneCondition::AND, false, true,\n     false, true, false},\n    {\"true && false\",\n     BinaryPruneCondition::AND, true, false,\n     false, true, true},\n    {\"true && true\",\n     BinaryPruneCondition::AND, true, true,\n     true, true, true},\n    {\"false || false\",\n     BinaryPruneCondition::OR, false, false,\n     false, true, true},\n    {\"false || true\",\n     BinaryPruneCondition::OR, false, true,\n     true, true, true},\n    {\"true || false\",\n     BinaryPruneCondition::OR, true, false,\n     true, true, false},\n    {\"true || true\",\n     BinaryPruneCondition::OR, true, true,\n     true, true, false},\n      // clang-format on\n  };\n  for (const auto& test : kTests) {\n    SCOPED_TRACE(test.name);\n    auto lhs = new StaticCondition(test.lhs_value);\n    auto rhs = new StaticCondition(test.rhs_value);\n    BinaryPruneCondition condition(test.op, lhs, rhs);\n    CrashReportDatabase::Report report;\n    EXPECT_EQ(condition.ShouldPruneReport(report), test.cond_result);\n    EXPECT_EQ(lhs->did_execute(), test.lhs_executed);\n    EXPECT_EQ(rhs->did_execute(), test.rhs_executed);\n  }\n}\n\nMATCHER_P(TestUUID, data_1, \"\") {\n  return arg.data_1 == data_1;\n}\n\nTEST(PruneCrashReports, PruneOrder) {\n  using ::testing::_;\n  using ::testing::DoAll;\n  using ::testing::Return;\n  using ::testing::SetArgPointee;\n\n  const size_t kNumReports = 10;\n  std::vector<CrashReportDatabase::Report> reports;\n  for (size_t i = 0; i < kNumReports; ++i) {\n    CrashReportDatabase::Report temp;\n    temp.uuid.data_1 = static_cast<uint32_t>(i);\n    temp.creation_time = NDaysAgo(static_cast<int>(i) * 10);\n    reports.push_back(temp);\n  }\n  std::mt19937 urng(std::random_device{}());\n  std::shuffle(reports.begin(), reports.end(), urng);\n  std::vector<CrashReportDatabase::Report> pending_reports(reports.begin(),\n                                                           reports.begin() + 5);\n  std::vector<CrashReportDatabase::Report> completed_reports(\n      reports.begin() + 5, reports.end());\n\n  MockDatabase db;\n  EXPECT_CALL(db, GetPendingReports(_))\n      .WillOnce(DoAll(SetArgPointee<0>(pending_reports),\n                      Return(CrashReportDatabase::kNoError)));\n  EXPECT_CALL(db, GetCompletedReports(_))\n      .WillOnce(DoAll(SetArgPointee<0>(completed_reports),\n                      Return(CrashReportDatabase::kNoError)));\n  for (size_t i = 0; i < reports.size(); ++i) {\n    EXPECT_CALL(db, DeleteReport(TestUUID(i)))\n        .WillOnce(Return(CrashReportDatabase::kNoError));\n  }\n\n  StaticCondition delete_all(true);\n  EXPECT_EQ(PruneCrashReportDatabase(&db, &delete_all), kNumReports);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/pthread_create_linux.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <dlfcn.h>\n#include <pthread.h>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"client/crashpad_client.h\"\n#include \"util/misc/no_cfi_icall.h\"\n\nnamespace {\n\nusing StartRoutineType = void* (*)(void*);\n\nstruct StartParams {\n  StartRoutineType start_routine;\n  void* arg;\n};\n\nvoid* InitializeSignalStackAndStart(StartParams* params) {\n  crashpad::CrashpadClient::InitializeSignalStackForThread();\n\n  crashpad::NoCfiIcall<StartRoutineType> start_routine(params->start_routine);\n  void* arg = params->arg;\n  delete params;\n\n  return start_routine(arg);\n}\n\n}  // namespace\n\nextern \"C\" {\n\n__attribute__((visibility(\"default\"))) int pthread_create(\n    pthread_t* thread,\n    const pthread_attr_t* attr,\n    StartRoutineType start_routine,\n    void* arg) {\n  static const crashpad::NoCfiIcall<decltype(pthread_create)*>\n      next_pthread_create([]() {\n        const auto next_pthread_create = dlsym(RTLD_NEXT, \"pthread_create\");\n        CHECK(next_pthread_create) << \"dlsym: \" << dlerror();\n        return next_pthread_create;\n      }());\n\n  StartParams* params = new StartParams;\n  params->start_routine = start_routine;\n  params->arg = arg;\n\n  int result = next_pthread_create(\n      thread,\n      attr,\n      reinterpret_cast<StartRoutineType>(InitializeSignalStackAndStart),\n      params);\n  if (result != 0) {\n    delete params;\n  }\n  return result;\n}\n\n}  // extern \"C\"\n"
  },
  {
    "path": "client/ring_buffer_annotation.h",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_RING_BUFFER_ANNOTATION_H_\n#define CRASHPAD_CLIENT_RING_BUFFER_ANNOTATION_H_\n\n#include <stdio.h>\n\n#include \"client/annotation.h\"\n#include \"client/length_delimited_ring_buffer.h\"\n\nnamespace crashpad {\n\n//! \\brief Capacity of `RingBufferAnnotation`, in bytes.\nusing RingBufferAnnotationCapacity = RingBufferCapacity;\n\nnamespace internal {\n\n//! \\brief Default capacity of `RingBufferAnnotation`, in bytes.\ninline constexpr RingBufferAnnotationCapacity\n    kDefaultRingBufferAnnotationCapacity = 8192;\n\n}  // namespace internal\n\n//! \\brief An `Annotation` which wraps a `LengthDelimitedRingBuffer`\n//!     of up to `Capacity` bytes in length.\n//!\n//! Supports writing variable-length data via `Push()`. When the ring buffer is\n//! full, it will drop old data items in FIFO order until enough space is\n//! available for the write.\n//!\n//! Supports guarding concurrent reads from writes via `ScopedSpinGuard`, so\n//! writing to this object is thread-safe.\n//!\n//! Clients which read this `Annotation`'s memory can optionally invoke\n//! `TryCreateScopedSpinGuard()` on this object to ensure any pending write\n//! finishes before the memory is read.\n//!\n//! Each item in this ring buffer is delimited by its length encoded in\n//! little-endian Base 128 varint encoding.\n//!\n//! `RingBufferAnnotation` uses varint-encoded delimiters to enable\n//! zero-copy deserialization of the ringbuffer's contents when storing\n//! protobufs inside the ringbuffer, e.g. via\n//! `google::protobuf::util::ParseDelimitedFromZeroCopyStream()` or similar.\n//!\n//! \\sa\n//! https://github.com/protocolbuffers/protobuf/blob/3202b9da88ceb75b65bbabaf4033c95e872f828d/src/google/protobuf/util/delimited_message_util.h#L85\n//! \\sa\n//! https://github.com/protocolbuffers/protobuf/blob/8bd49dea5e167a389d94b71d24c981d8f9fa0c99/src/google/protobuf/io/zero_copy_stream_impl_lite.h#L68\n//! \\sa\n//! https://github.com/protocolbuffers/protobuf/blob/8bd49dea5e167a389d94b71d24c981d8f9fa0c99/src/google/protobuf/io/coded_stream.h#L171\n//!\n//! To deserialize the items stored in this annotation, use\n//! `LengthDelimitedRingBufferReader`.\ntemplate <RingBufferAnnotationCapacity Capacity =\n              internal::kDefaultRingBufferAnnotationCapacity>\nclass RingBufferAnnotation final : public Annotation {\n public:\n  //! \\brief Constructs a `RingBufferAnnotation`.\n  //! \\param[in] type A unique identifier for the type of data in the ring\n  //!     buffer.\n  //! \\param[in] name The name of the annotation.\n  constexpr RingBufferAnnotation(Annotation::Type type, const char name[])\n      : Annotation(type,\n                   name,\n                   reinterpret_cast<void* const>(&ring_buffer_data_),\n                   ConcurrentAccessGuardMode::kScopedSpinGuard),\n        ring_buffer_data_(),\n        ring_buffer_writer_(ring_buffer_data_) {}\n  RingBufferAnnotation(const RingBufferAnnotation&) = delete;\n  RingBufferAnnotation& operator=(const RingBufferAnnotation&) = delete;\n  RingBufferAnnotation(RingBufferAnnotation&&) = default;\n  RingBufferAnnotation& operator=(RingBufferAnnotation&&) = default;\n\n  //! \\brief Pushes data onto this annotation's ring buffer.\n  //!\n  //! If the ring buffer does not have enough space to store `buffer_length`\n  //! bytes of data, old data items are dropped in FIFO order until\n  //! enough space is available to store the new data.\n  bool Push(const void* const buffer,\n            RingBufferAnnotationCapacity buffer_length) {\n    // Use a zero timeout so the operation immediately fails if another thread\n    // or process is currently reading this Annotation.\n    constexpr uint64_t kSpinGuardTimeoutNanoseconds = 0;\n\n    auto spin_guard = TryCreateScopedSpinGuard(kSpinGuardTimeoutNanoseconds);\n    if (!spin_guard) {\n      return false;\n    }\n    bool success = ring_buffer_writer_.Push(buffer, buffer_length);\n    if (success) {\n      SetSize(ring_buffer_data_.GetRingBufferLength());\n    }\n    return success;\n  }\n\n  //! \\brief Reset the annotation (e.g., for testing).\n  //! This method is not thread-safe.\n  void ResetForTesting() {\n    ring_buffer_data_.ResetForTesting();\n    ring_buffer_writer_.ResetForTesting();\n  }\n\n private:\n  using RingBufferWriter =\n      LengthDelimitedRingBufferWriter<RingBufferData<Capacity>>;\n\n  //! \\brief The ring buffer data stored in this Anotation.\n  RingBufferData<Capacity> ring_buffer_data_;\n\n  //! \\brief The writer which wraps `ring_buffer_data_`.\n  RingBufferWriter ring_buffer_writer_;\n};\n\n// Allow just `RingBufferAnnotation foo;` to be declared without template\n// arguments using C++17 class template argument deduction.\ntemplate <RingBufferAnnotationCapacity Capacity =\n              internal::kDefaultRingBufferAnnotationCapacity>\nRingBufferAnnotation(Annotation::Type type, const char name[])\n    -> RingBufferAnnotation<Capacity>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_RING_BUFFER_ANNOTATION_H_\n"
  },
  {
    "path": "client/ring_buffer_annotation_load_test_main.cc",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <getopt.h>\n#include <inttypes.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <chrono>\n#include <condition_variable>\n#include <functional>\n#include <mutex>\n#include <optional>\n#include <random>\n#include <ratio>\n#include <string>\n#include <string_view>\n\n#include \"base/notreached.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"client/annotation.h\"\n#include \"client/length_delimited_ring_buffer.h\"\n#include \"client/ring_buffer_annotation.h\"\n#include \"tools/tool_support.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n#include \"util/synchronization/scoped_spin_guard.h\"\n#include \"util/thread/thread.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n#else\n#include <signal.h>\n#endif  // BUILDFLAG(IS_WIN)\n\nnamespace crashpad {\n\nnamespace test {\n\nnamespace {\n\nconstexpr Annotation::Type kRingBufferLoadTestType =\n    Annotation::UserDefinedType(0x0042);\nstd::atomic<bool> g_should_exit = false;\n\nstruct RingBufferAnnotationSnapshotParams final {\n  enum class Mode {\n    kUseScopedSpinGuard = 1,\n    kDoNotUseSpinGuard = 2,\n  };\n  Mode mode = Mode::kUseScopedSpinGuard;\n  using Duration = std::chrono::duration<uint64_t, std::nano>;\n  Duration producer_thread_min_run_duration = std::chrono::milliseconds(1);\n  Duration producer_thread_max_run_duration = std::chrono::milliseconds(10);\n  Duration producer_thread_sleep_duration = std::chrono::nanoseconds(10);\n  Duration consumer_thread_min_run_duration = std::chrono::milliseconds(5);\n  Duration consumer_thread_max_run_duration = std::chrono::milliseconds(100);\n  Duration quiesce_timeout = std::chrono::microseconds(500);\n  uint64_t num_loops = std::numeric_limits<uint64_t>::max();\n  std::optional<Duration> main_thread_run_duration = std::nullopt;\n};\n\ntemplate <uint32_t RingBufferCapacity>\nclass RingBufferAnnotationSnapshot final {\n  using RingBufferAnnotationType = RingBufferAnnotation<RingBufferCapacity>;\n\n  struct State final {\n    State()\n        : ring_buffer_annotation(kRingBufferLoadTestType,\n                                 \"ring-buffer-load-test\"),\n          ring_buffer_ready(false),\n          producer_thread_running(false),\n          producer_thread_finished(false),\n          consumer_thread_finished(false),\n          should_exit(false) {}\n\n    State(const State&) = delete;\n    State& operator=(const State&) = delete;\n\n    RingBufferAnnotationType ring_buffer_annotation;\n    bool ring_buffer_ready;\n    bool producer_thread_running;\n    bool producer_thread_finished;\n    bool consumer_thread_finished;\n    bool should_exit;\n  };\n\n  class Thread final : public crashpad::Thread {\n   public:\n    Thread(std::function<void()> thread_main)\n        : thread_main_(std::move(thread_main)) {}\n\n   private:\n    void ThreadMain() override { thread_main_(); }\n\n    const std::function<void()> thread_main_;\n  };\n\n public:\n  RingBufferAnnotationSnapshot(const RingBufferAnnotationSnapshotParams& params)\n      : params_(params),\n        main_loop_thread_([this]() { MainLoopThreadMain(); }),\n        producer_thread_([this]() { ProducerThreadMain(); }),\n        consumer_thread_([this]() { ConsumerThreadMain(); }),\n        mutex_(),\n        state_changed_condition_(),\n        state_() {}\n\n  RingBufferAnnotationSnapshot(const RingBufferAnnotationSnapshot&) = delete;\n  RingBufferAnnotationSnapshot& operator=(const RingBufferAnnotationSnapshot&) =\n      delete;\n\n  void Start() {\n    main_loop_thread_.Start();\n    producer_thread_.Start();\n    consumer_thread_.Start();\n  }\n\n  void Stop() {\n    consumer_thread_.Join();\n    producer_thread_.Join();\n    main_loop_thread_.Join();\n  }\n\n private:\n  void MainLoopThreadMain() {\n    std::chrono::steady_clock::time_point main_thread_end_time;\n    if (params_.main_thread_run_duration) {\n      main_thread_end_time =\n          std::chrono::steady_clock::now() + *params_.main_thread_run_duration;\n    } else {\n      main_thread_end_time = std::chrono::steady_clock::time_point::max();\n    }\n    for (uint64_t i = 0;\n         i < params_.num_loops &&\n         std::chrono::steady_clock::now() < main_thread_end_time;\n         i++) {\n      {\n        std::unique_lock<std::mutex> start_lock(mutex_);\n        state_.ring_buffer_annotation.ResetForTesting();\n        state_.ring_buffer_ready = true;\n        state_changed_condition_.notify_all();\n      }\n\n      {\n        std::unique_lock<std::mutex> lock(mutex_);\n        state_changed_condition_.wait(lock, [this] {\n          return state_.producer_thread_finished &&\n                 state_.consumer_thread_finished;\n        });\n        state_.ring_buffer_ready = false;\n        if (g_should_exit) {\n          printf(\"Exiting on Control-C.\\n\");\n          break;\n        }\n        printf(\".\");\n        fflush(stdout);\n        state_changed_condition_.notify_all();\n      }\n    }\n    state_.should_exit = true;\n    state_changed_condition_.notify_all();\n  }\n\n  void ProducerThreadMain() {\n    while (true) {\n      {\n        std::unique_lock<std::mutex> lock(mutex_);\n        state_changed_condition_.wait(lock, [this] {\n          return state_.should_exit || state_.ring_buffer_ready;\n        });\n        if (state_.should_exit) {\n          return;\n        }\n        state_.producer_thread_running = true;\n        state_.producer_thread_finished = false;\n        state_changed_condition_.notify_all();\n      }\n\n      auto min_run_duration_micros =\n          std::chrono::duration_cast<std::chrono::microseconds>(\n              params_.producer_thread_min_run_duration);\n      auto max_run_duration_micros =\n          std::chrono::duration_cast<std::chrono::microseconds>(\n              params_.producer_thread_max_run_duration);\n      std::uniform_int_distribution<std::chrono::microseconds::rep>\n          run_duration_distribution(min_run_duration_micros.count(),\n                                    max_run_duration_micros.count());\n      static thread_local std::mt19937 random_number_generator;\n      auto run_duration = std::chrono::microseconds(\n          run_duration_distribution(random_number_generator));\n      auto end_time = std::chrono::steady_clock::now() + run_duration;\n      uint64_t next_value = 0;\n      while (std::chrono::steady_clock::now() < end_time) {\n        if (!Produce(next_value++)) {\n          // The consumer thread interrupted this.\n          break;\n        }\n      }\n      {\n        std::unique_lock<std::mutex> lock(mutex_);\n        state_changed_condition_.wait(\n            lock, [this] { return state_.consumer_thread_finished; });\n        state_.producer_thread_running = false;\n        state_.producer_thread_finished = true;\n        state_changed_condition_.notify_all();\n      }\n    }\n  }\n\n  bool Produce(uint64_t value) {\n    std::string hex_value = base::StringPrintf(\"0x%08\" PRIx64, value);\n    if (!state_.ring_buffer_annotation.Push(\n            hex_value.data(), static_cast<uint32_t>(hex_value.size()))) {\n      fprintf(stderr,\n              \"Ignoring failed call to Push(0x%\" PRIx64\n              \") (ScopedSpinGuard was held by snapshot thread)\\n\",\n              value);\n      return false;\n    }\n    return true;\n  }\n\n  void ConsumerThreadMain() {\n    while (true) {\n      {\n        std::unique_lock<std::mutex> lock(mutex_);\n        state_changed_condition_.wait(lock, [this] {\n          return state_.should_exit ||\n                 (state_.ring_buffer_ready && state_.producer_thread_running);\n        });\n        if (state_.should_exit) {\n          return;\n        }\n        state_.consumer_thread_finished = false;\n        state_changed_condition_.notify_all();\n      }\n      auto min_run_duration_micros =\n          std::chrono::duration_cast<std::chrono::microseconds>(\n              params_.consumer_thread_min_run_duration);\n      auto max_run_duration_micros =\n          std::chrono::duration_cast<std::chrono::microseconds>(\n              params_.consumer_thread_max_run_duration);\n      std::uniform_int_distribution<std::chrono::microseconds::rep>\n          run_duration_distribution(min_run_duration_micros.count(),\n                                    max_run_duration_micros.count());\n      static thread_local std::mt19937 random_number_generator;\n      auto run_duration = std::chrono::microseconds(\n          run_duration_distribution(random_number_generator));\n      auto end_time = std::chrono::steady_clock::now() + run_duration;\n      while (std::chrono::steady_clock::now() < end_time) {\n        constexpr uint64_t kSleepTimeNs = 10000;  // 10 us\n        SleepNanoseconds(kSleepTimeNs);\n      }\n      Snapshot();\n      {\n        std::unique_lock<std::mutex> lock(mutex_);\n        state_.consumer_thread_finished = true;\n        state_.ring_buffer_ready = false;\n        state_changed_condition_.notify_all();\n      }\n    }\n  }\n\n  void Snapshot() {\n    int64_t timeout_ns = static_cast<int64_t>(\n        std::chrono::duration_cast<std::chrono::nanoseconds>(\n            params_.quiesce_timeout)\n            .count());\n    uint8_t serialized_ring_buffer[sizeof(state_.ring_buffer_annotation)];\n    Annotation::ValueSizeType ring_buffer_size;\n    {\n      std::optional<ScopedSpinGuard> scoped_spin_guard;\n      if (params_.mode ==\n          RingBufferAnnotationSnapshotParams::Mode::kUseScopedSpinGuard) {\n        scoped_spin_guard =\n            state_.ring_buffer_annotation.TryCreateScopedSpinGuard(timeout_ns);\n      }\n      if (params_.mode ==\n              RingBufferAnnotationSnapshotParams::Mode::kUseScopedSpinGuard &&\n          !scoped_spin_guard) {\n        fprintf(stderr,\n                \"Could not quiesce writes within %\" PRIi64 \" ns\\n\",\n                timeout_ns);\n        abort();\n      }\n      ring_buffer_size = state_.ring_buffer_annotation.size();\n      memcpy(&serialized_ring_buffer[0],\n             state_.ring_buffer_annotation.value(),\n             ring_buffer_size);\n    }\n    RingBufferData ring_buffer;\n    if (!ring_buffer.DeserializeFromBuffer(serialized_ring_buffer,\n                                           ring_buffer_size)) {\n      fprintf(stderr, \"Could not deserialize ring buffer\\n\");\n      abort();\n    }\n    LengthDelimitedRingBufferReader ring_buffer_reader(ring_buffer);\n    int value = std::numeric_limits<int>::max();\n    std::vector<uint8_t> bytes;\n    while (ring_buffer_reader.Pop(bytes)) {\n      int next_value;\n      std::string_view str(reinterpret_cast<const char*>(&bytes[0]),\n                           bytes.size());\n      if (!base::HexStringToInt(str, &next_value)) {\n        fprintf(stderr,\n                \"Couldn't parse value: [%.*s]\\n\",\n                base::checked_cast<int>(bytes.size()),\n                bytes.data());\n        abort();\n      }\n      if (value == std::numeric_limits<int>::max()) {\n        // First value in buffer.\n      } else if (value + 1 != next_value) {\n        fprintf(stderr,\n                \"Expected value 0x%08x, got 0x%08x\\n\",\n                value + 1,\n                next_value);\n        abort();\n      }\n      value = next_value;\n      bytes.clear();\n    }\n  }\n\n  const RingBufferAnnotationSnapshotParams params_;\n  Thread main_loop_thread_;\n  Thread producer_thread_;\n  Thread consumer_thread_;\n  std::mutex mutex_;\n\n  // Fired whenever `state_` changes.\n  std::condition_variable state_changed_condition_;\n\n  // Protected by `mutex_`.\n  State state_;\n};\n\nvoid Usage(const base::FilePath& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %\" PRFilePath \" [OPTION]...\\n\"\n\"Runs a load test for concurrent I/O to RingBufferAnnotation.\\n\"\n\"\\n\"\n\"By default, enables the annotation spin guard and runs indefinitely\\n\"\n\"until interrupted (e.g., with Control-C or SIGINT).\\n\"\n\"\\n\"\n\"  -d,--disable-spin-guard  Disables the annotation spin guard\\n\"\n\"                           (the test is expected to crash in this case)\\n\"\n\"  -n,--num-loops=N         Runs the test for N iterations, not indefinitely\\n\"\n\"  -s,--duration-secs=SECS  Runs the test for SECS seconds, not indefinitely\\n\",\n          me.value().c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nint TestMain(int argc, char** argv) {\n  const base::FilePath argv0(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));\n  const base::FilePath me(argv0.BaseName());\n\n#if BUILDFLAG(IS_WIN)\n  auto handler_routine = [](DWORD type) -> BOOL {\n    if (type == CTRL_C_EVENT) {\n      g_should_exit = true;\n      return TRUE;\n    }\n    return FALSE;\n  };\n  if (!SetConsoleCtrlHandler(handler_routine, /*Add=*/TRUE)) {\n    fprintf(stderr, \"Couldn't set Control-C handler\\n\");\n    return EXIT_FAILURE;\n  }\n#else\n  signal(SIGINT, [](int signal) { g_should_exit = true; });\n#endif  // BUILDFLAG(IS_WIN)\n  RingBufferAnnotationSnapshotParams params;\n  enum OptionFlags {\n    // \"Short\" (single-character) options.\n    kOptionDisableSpinGuard = 'd',\n    kOptionNumLoops = 'n',\n    kOptionDurationSecs = 's',\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n  static constexpr option long_options[] = {\n      {\"disable-spin-guard\", no_argument, nullptr, kOptionDisableSpinGuard},\n      {\"num-loops\", required_argument, nullptr, kOptionNumLoops},\n      {\"duration-secs\", required_argument, nullptr, kOptionDurationSecs},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"dn:s:\", long_options, nullptr)) !=\n         -1) {\n    switch (opt) {\n      case kOptionDisableSpinGuard:\n        printf(\"Disabling spin guard logic (this test will fail!)\\n\");\n        params.mode =\n            RingBufferAnnotationSnapshotParams::Mode::kDoNotUseSpinGuard;\n        break;\n      case kOptionNumLoops: {\n        std::string num_loops(optarg);\n        uint64_t num_loops_value;\n        if (!StringToNumber(num_loops, &num_loops_value)) {\n          ToolSupport::UsageHint(me, \"--num-loops requires integer value\");\n          return EXIT_FAILURE;\n        }\n        params.num_loops = num_loops_value;\n        break;\n      }\n      case kOptionDurationSecs: {\n        std::string duration_secs(optarg);\n        uint64_t duration_secs_value;\n        if (!StringToNumber(duration_secs, &duration_secs_value)) {\n          ToolSupport::UsageHint(me, \"--duration-secs requires integer value\");\n          return EXIT_FAILURE;\n        }\n        params.main_thread_run_duration =\n            std::chrono::seconds(duration_secs_value);\n        break;\n      }\n      case kOptionHelp:\n        Usage(me);\n        return EXIT_SUCCESS;\n      case kOptionVersion:\n        ToolSupport::Version(me);\n        return EXIT_SUCCESS;\n      default:\n        ToolSupport::UsageHint(me, nullptr);\n        return EXIT_FAILURE;\n    }\n  }\n\n  RingBufferAnnotationSnapshot<8192> test_producer_snapshot(params);\n  printf(\"Starting test (Control-C to exit)...\\n\");\n  test_producer_snapshot.Start();\n  test_producer_snapshot.Stop();\n  printf(\"Test finished.\\n\");\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n\n#if BUILDFLAG(IS_POSIX)\n\nint main(int argc, char** argv) {\n  return crashpad::test::TestMain(argc, argv);\n}\n\n#elif BUILDFLAG(IS_WIN)\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::ToolSupport::Wmain(argc, argv, crashpad::test::TestMain);\n}\n\n#endif\n"
  },
  {
    "path": "client/ring_buffer_annotation_test.cc",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/ring_buffer_annotation.h\"\n#include \"client/length_delimited_ring_buffer.h\"\n\n#include <array>\n#include <string>\n\n#include \"client/annotation_list.h\"\n#include \"client/crashpad_info.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr uint32_t kRingBufferHeaderSize = 16;\nconstexpr uint32_t kLengthDelimiter1ByteSize = 1;\n\nclass RingBufferAnnotationTest : public testing::Test {\n public:\n  void SetUp() override {\n    CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_);\n  }\n\n  void TearDown() override {\n    CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr);\n  }\n\n  size_t AnnotationsCount() {\n    size_t result = 0;\n    for (auto* annotation : annotations_) {\n      if (annotation->is_set())\n        ++result;\n    }\n    return result;\n  }\n\n protected:\n  AnnotationList annotations_;\n};\n\nTEST_F(RingBufferAnnotationTest, Basics) {\n  constexpr Annotation::Type kType = Annotation::UserDefinedType(1);\n\n  constexpr char kName[] = \"annotation 1\";\n  RingBufferAnnotation annotation(kType, kName);\n\n  EXPECT_FALSE(annotation.is_set());\n  EXPECT_EQ(0u, AnnotationsCount());\n\n  EXPECT_EQ(kType, annotation.type());\n  EXPECT_EQ(0u, annotation.size());\n  EXPECT_EQ(std::string(kName), annotation.name());\n\n  EXPECT_TRUE(\n      annotation.Push(reinterpret_cast<const uint8_t*>(\"0123456789\"), 10));\n\n  EXPECT_TRUE(annotation.is_set());\n  EXPECT_EQ(1u, AnnotationsCount());\n\n  constexpr Annotation::ValueSizeType kExpectedSize =\n      kRingBufferHeaderSize + kLengthDelimiter1ByteSize + 10u;\n  EXPECT_EQ(kExpectedSize, annotation.size());\n  EXPECT_EQ(&annotation, *annotations_.begin());\n\n  RingBufferData data;\n  EXPECT_TRUE(\n      data.DeserializeFromBuffer(annotation.value(), annotation.size()));\n  EXPECT_EQ(kExpectedSize, data.GetRingBufferLength());\n\n  std::vector<uint8_t> popped_value;\n  LengthDelimitedRingBufferReader reader(data);\n  EXPECT_TRUE(reader.Pop(popped_value));\n\n  const std::vector<uint8_t> expected = {\n      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};\n  EXPECT_EQ(expected, popped_value);\n\n  annotation.Clear();\n\n  EXPECT_FALSE(annotation.is_set());\n  EXPECT_EQ(0u, AnnotationsCount());\n\n  EXPECT_EQ(0u, annotation.size());\n}\n\nTEST_F(RingBufferAnnotationTest, MultiplePushesWithoutWrapping) {\n  constexpr Annotation::Type kType = Annotation::UserDefinedType(1);\n\n  constexpr char kName[] = \"annotation 1\";\n  RingBufferAnnotation annotation(kType, kName);\n\n  EXPECT_TRUE(\n      annotation.Push(reinterpret_cast<const uint8_t*>(\"0123456789\"), 10));\n  EXPECT_TRUE(annotation.Push(reinterpret_cast<const uint8_t*>(\"ABCDEF\"), 6));\n\n  EXPECT_TRUE(annotation.is_set());\n  EXPECT_EQ(1u, AnnotationsCount());\n\n  constexpr Annotation::ValueSizeType kExpectedSize =\n      kRingBufferHeaderSize + kLengthDelimiter1ByteSize + 10u +\n      kLengthDelimiter1ByteSize + 6u;\n  EXPECT_EQ(kExpectedSize, annotation.size());\n  EXPECT_EQ(&annotation, *annotations_.begin());\n\n  RingBufferData data;\n  EXPECT_TRUE(\n      data.DeserializeFromBuffer(annotation.value(), annotation.size()));\n  EXPECT_EQ(kExpectedSize, data.GetRingBufferLength());\n\n  std::vector<uint8_t> popped_value;\n  LengthDelimitedRingBufferReader reader(data);\n  EXPECT_TRUE(reader.Pop(popped_value));\n\n  const std::vector<uint8_t> expected1 = {\n      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};\n  EXPECT_EQ(expected1, popped_value);\n\n  popped_value.clear();\n  EXPECT_TRUE(reader.Pop(popped_value));\n\n  const std::vector<uint8_t> expected2 = {'A', 'B', 'C', 'D', 'E', 'F'};\n  EXPECT_EQ(expected2, popped_value);\n}\n\nTEST_F(RingBufferAnnotationTest,\n       MultiplePushCallsWithWrappingShouldOverwriteInFIFOOrder) {\n  constexpr Annotation::Type kType = Annotation::UserDefinedType(1);\n\n  constexpr char kName[] = \"annotation 1\";\n  RingBufferAnnotation<10> annotation(kType, kName);\n\n  // Each Push() call will push 1 byte for the varint 128-encoded length,\n  // then the number of bytes specified.\n  constexpr char kFirst[] = \"AAA\";\n  constexpr char kSecond[] = \"BBB\";\n  constexpr char kThird[] = \"CCC\";\n\n  // This takes up bytes 0-3 of the 10-byte RingBufferAnnotation.\n  ASSERT_TRUE(annotation.Push(reinterpret_cast<const uint8_t*>(kFirst), 3));\n\n  // This takes up bytes 4-7 of the 10-byte RingBufferAnnotation.\n  ASSERT_TRUE(annotation.Push(reinterpret_cast<const uint8_t*>(kSecond), 3));\n\n  // This should wrap around the end of the array and overwrite kFirst since it\n  // needs 4 bytes but there are only 2 left.\n  ASSERT_TRUE(annotation.Push(reinterpret_cast<const uint8_t*>(kThird), 3));\n\n  // The size of the annotation should include the header and the full 10 bytes\n  // of the ring buffer, since the third write wrapped around the end.\n  ASSERT_EQ(kRingBufferHeaderSize + 10u, annotation.size());\n\n  // This data size needs to match the size in the RingBufferAnnotation above.\n  RingBufferData<10> data;\n  ASSERT_TRUE(\n      data.DeserializeFromBuffer(annotation.value(), annotation.size()));\n\n  std::vector<uint8_t> popped_value;\n  LengthDelimitedRingBufferReader reader(data);\n  ASSERT_TRUE(reader.Pop(popped_value));\n\n  // \"AAA\" has been overwritten, so the first thing popped should be \"BBB\".\n  const std::vector<uint8_t> expected_b = {'B', 'B', 'B'};\n  EXPECT_EQ(expected_b, popped_value);\n\n  popped_value.clear();\n  ASSERT_TRUE(reader.Pop(popped_value));\n  const std::vector<uint8_t> expected_c = {'C', 'C', 'C'};\n  EXPECT_EQ(expected_c, popped_value);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/settings.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/settings.h\"\n\n#include <stdint.h>\n#include <string.h>\n\n#include <limits>\n\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/numeric/in_range_cast.h\"\n\nnamespace crashpad {\n\n#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\nSettings::ScopedLockedFileHandle::ScopedLockedFileHandle()\n    : handle_(kInvalidFileHandle), lockfile_path_() {\n    }\n\nSettings::ScopedLockedFileHandle::ScopedLockedFileHandle(\n    FileHandle handle,\n    const base::FilePath& lockfile_path)\n    : handle_(handle), lockfile_path_(lockfile_path) {\n}\n\nSettings::ScopedLockedFileHandle::ScopedLockedFileHandle(\n    ScopedLockedFileHandle&& other)\n    : handle_(other.handle_), lockfile_path_(other.lockfile_path_) {\n  other.handle_ = kInvalidFileHandle;\n  other.lockfile_path_ = base::FilePath();\n}\n\nSettings::ScopedLockedFileHandle& Settings::ScopedLockedFileHandle::operator=(\n    ScopedLockedFileHandle&& other) {\n  handle_ = other.handle_;\n  lockfile_path_ = other.lockfile_path_;\n\n  other.handle_ = kInvalidFileHandle;\n  other.lockfile_path_ = base::FilePath();\n  return *this;\n}\n\nSettings::ScopedLockedFileHandle::~ScopedLockedFileHandle() {\n  Destroy();\n}\n\nvoid Settings::ScopedLockedFileHandle::Destroy() {\n  if (handle_ != kInvalidFileHandle) {\n    CheckedCloseFile(handle_);\n  }\n  if (!lockfile_path_.empty()) {\n    const bool success = LoggingRemoveFile(lockfile_path_);\n    DCHECK(success);\n  }\n}\n\n#else  // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\n#if BUILDFLAG(IS_IOS)\n\nSettings::ScopedLockedFileHandle::ScopedLockedFileHandle(\n    const FileHandle& value)\n    : ScopedGeneric(value) {\n  ios_background_task_ = std::make_unique<internal::ScopedBackgroundTask>(\n      \"ScopedLockedFileHandle\");\n}\n\nSettings::ScopedLockedFileHandle::ScopedLockedFileHandle(\n    Settings::ScopedLockedFileHandle&& rvalue) {\n  ios_background_task_.reset(rvalue.ios_background_task_.release());\n  reset(rvalue.release());\n}\n\nSettings::ScopedLockedFileHandle& Settings::ScopedLockedFileHandle::operator=(\n    Settings::ScopedLockedFileHandle&& rvalue) {\n  ios_background_task_.reset(rvalue.ios_background_task_.release());\n  reset(rvalue.release());\n  return *this;\n}\n\nSettings::ScopedLockedFileHandle::~ScopedLockedFileHandle() {\n  // Call reset() to ensure the lock is released before the ios_background_task.\n  reset();\n}\n\n#endif  // BUILDFLAG(IS_IOS)\n\nnamespace internal {\n\n// static\nvoid ScopedLockedFileHandleTraits::Free(FileHandle handle) {\n  if (handle != kInvalidFileHandle) {\n    LoggingUnlockFile(handle);\n    CheckedCloseFile(handle);\n  }\n}\n\n}  // namespace internal\n\n#endif  // BUILDFLAG(IS_FUCHSIA)\n\nstruct SettingsReader::Data {\n  static constexpr uint32_t kSettingsMagic = 'CPds';\n\n  // Version number only used for incompatible changes to Data. Do not change\n  // this when adding additional fields at the end. Modifying `kSettingsVersion`\n  // will wipe away the entire struct when reading from other versions.\n  static constexpr uint32_t kSettingsVersion = 1;\n\n  enum Options : uint32_t {\n    kUploadsEnabled = 1 << 0,\n  };\n\n  Data() : magic(kSettingsMagic),\n           version(kSettingsVersion),\n           options(0),\n           padding_0(0),\n           last_upload_attempt_time(0),\n           client_id() {}\n\n  uint32_t magic;\n  uint32_t version;\n  uint32_t options;\n  uint32_t padding_0;\n  int64_t last_upload_attempt_time;  // time_t\n  UUID client_id;\n};\n\nSettingsReader::SettingsReader(const base::FilePath& file_path)\n    : SettingsReader(file_path, InitializationState::kStateValid) {}\n\nSettingsReader::SettingsReader(const base::FilePath& file_path,\n                               InitializationState::State state)\n    : file_path_(file_path), initialized_(state) {}\n\nSettingsReader::~SettingsReader() = default;\n\nbool SettingsReader::GetClientID(UUID* client_id) {\n  DCHECK(initialized().is_valid());\n\n  Data settings;\n  if (!OpenAndReadSettings(&settings))\n    return false;\n\n  *client_id = settings.client_id;\n  return true;\n}\n\nbool SettingsReader::GetUploadsEnabled(bool* enabled) {\n  DCHECK(initialized().is_valid());\n\n  Data settings;\n  if (!OpenAndReadSettings(&settings))\n    return false;\n\n  *enabled = (settings.options & Data::Options::kUploadsEnabled) != 0;\n  return true;\n}\n\nbool SettingsReader::GetLastUploadAttemptTime(time_t* time) {\n  DCHECK(initialized().is_valid());\n\n  Data settings;\n  if (!OpenAndReadSettings(&settings))\n    return false;\n\n  *time = InRangeCast<time_t>(settings.last_upload_attempt_time,\n                              std::numeric_limits<time_t>::max());\n  return true;\n}\n\nbool SettingsReader::OpenAndReadSettings(Data* out_data) {\n  ScopedFileHandle handle(LoggingOpenFileForRead(file_path_));\n  return handle.is_valid() && ReadSettings(handle.get(), out_data, true);\n}\n\nbool SettingsReader::ReadSettings(FileHandle handle,\n                                  Data* out_data,\n                                  bool log_read_error) {\n  if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) {\n    return false;\n  }\n\n  // This clears `out_data` so that any bytes not read from disk are zero\n  // initialized. This is expected when reading from an older settings file with\n  // fewer fields.\n  memset(out_data, 0, sizeof(*out_data));\n\n  const FileOperationResult read_result =\n      log_read_error ? LoggingReadFileUntil(handle, out_data, sizeof(*out_data))\n                     : ReadFileUntil(handle, out_data, sizeof(*out_data));\n\n  if (read_result <= 0) {\n    return false;\n  }\n\n  // Newer versions of crashpad may add fields to Data, but all versions have\n  // the data members up to `client_id`. Do not attempt to understand a smaller\n  // struct read.\n  const size_t min_size =\n      offsetof(Data, client_id) + sizeof(out_data->client_id);\n  if (static_cast<size_t>(read_result) < min_size) {\n    LOG(ERROR) << \"Settings file too small: minimum \" << min_size\n               << \", observed \" << read_result;\n    return false;\n  }\n\n  if (out_data->magic != Data::kSettingsMagic) {\n    LOG(ERROR) << \"Settings magic is not \" << Data::kSettingsMagic;\n    return false;\n  }\n\n  if (out_data->version != Data::kSettingsVersion) {\n    LOG(ERROR) << \"Settings version is not \" << Data::kSettingsVersion;\n    return false;\n  }\n\n  return true;\n}\n\nSettings::Settings(const base::FilePath& path)\n    : SettingsReader(path, InitializationState::kStateUninitialized) {}\n\nSettings::~Settings() = default;\n\nbool Settings::Initialize() {\n  DCHECK(initialized().is_uninitialized());\n  initialized().set_invalid();\n\n  Data settings;\n  if (!OpenForWritingAndReadSettings(&settings).is_valid())\n    return false;\n\n  initialized().set_valid();\n  return true;\n}\n\nbool Settings::SetUploadsEnabled(bool enabled) {\n  DCHECK(initialized().is_valid());\n\n  Data settings;\n  ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);\n  if (!handle.is_valid())\n    return false;\n\n  if (enabled)\n    settings.options |= Data::Options::kUploadsEnabled;\n  else\n    settings.options &= ~Data::Options::kUploadsEnabled;\n\n  return WriteSettings(handle.get(), settings);\n}\n\nbool Settings::SetLastUploadAttemptTime(time_t time) {\n  DCHECK(initialized().is_valid());\n\n  Data settings;\n  ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings);\n  if (!handle.is_valid())\n    return false;\n\n  settings.last_upload_attempt_time = InRangeCast<int64_t>(time, 0);\n\n  return WriteSettings(handle.get(), settings);\n}\n\n#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n// static\nbool Settings::IsLockExpired(const base::FilePath& file_path,\n                             time_t lockfile_ttl) {\n  time_t now = time(nullptr);\n  base::FilePath lock_path(file_path.value() + Settings::kLockfileExtension);\n  ScopedFileHandle lock_fd(LoggingOpenFileForRead(lock_path));\n  time_t lock_timestamp;\n  if (!LoggingReadFileExactly(\n          lock_fd.get(), &lock_timestamp, sizeof(lock_timestamp))) {\n    return false;\n  }\n  return now >= lock_timestamp + lockfile_ttl;\n}\n#endif  // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\n// static\nSettings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle(\n    const internal::MakeScopedLockedFileHandleOptions& options,\n    FileLocking locking,\n    const base::FilePath& file_path) {\n#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n  base::FilePath lockfile_path(file_path.value() +\n                               Settings::kLockfileExtension);\n  ScopedFileHandle lockfile_scoped(\n      LoggingOpenFileForWrite(lockfile_path,\n                              FileWriteMode::kCreateOrFail,\n                              FilePermissions::kWorldReadable));\n  if (!lockfile_scoped.is_valid()) {\n    return ScopedLockedFileHandle();\n  }\n  time_t now = time(nullptr);\n  if (!LoggingWriteFile(lockfile_scoped.get(), &now, sizeof(now))) {\n    return ScopedLockedFileHandle();\n  }\n  ScopedFileHandle scoped(GetHandleFromOptions(file_path, options));\n  if (scoped.is_valid()) {\n    return ScopedLockedFileHandle(scoped.release(), lockfile_path);\n  }\n  bool success = LoggingRemoveFile(lockfile_path);\n  DCHECK(success);\n  return ScopedLockedFileHandle();\n#else\n  ScopedFileHandle scoped(GetHandleFromOptions(file_path, options));\n  // It's important to create the ScopedLockedFileHandle before calling\n  // LoggingLockFile on iOS, so a ScopedBackgroundTask is created *before*\n  // the LoggingLockFile call below.\n  ScopedLockedFileHandle handle(kInvalidFileHandle);\n  if (scoped.is_valid()) {\n    if (LoggingLockFile(\n            scoped.get(), locking, FileLockingBlocking::kBlocking) !=\n        FileLockingResult::kSuccess) {\n      scoped.reset();\n    }\n  }\n  handle.reset(scoped.release());\n  return handle;\n#endif  // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n}\n\n// static\nFileHandle Settings::GetHandleFromOptions(\n    const base::FilePath& file_path,\n    const internal::MakeScopedLockedFileHandleOptions& options) {\n  switch (options.function_enum) {\n    case internal::FileOpenFunction::kLoggingOpenFileForRead:\n      return LoggingOpenFileForRead(file_path);\n    case internal::FileOpenFunction::kLoggingOpenFileForReadAndWrite:\n      return LoggingOpenFileForReadAndWrite(\n          file_path, options.mode, options.permissions);\n    case internal::FileOpenFunction::kOpenFileForReadAndWrite:\n      return OpenFileForReadAndWrite(\n          file_path, options.mode, options.permissions);\n  }\n  NOTREACHED();\n}\n\nSettings::ScopedLockedFileHandle Settings::OpenForReading() const {\n  internal::MakeScopedLockedFileHandleOptions options;\n  options.function_enum = internal::FileOpenFunction::kLoggingOpenFileForRead;\n  return MakeScopedLockedFileHandle(options, FileLocking::kShared, file_path());\n}\n\nSettings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting(\n    FileWriteMode mode, bool log_open_error) {\n  DCHECK(mode != FileWriteMode::kTruncateOrCreate);\n\n  internal::MakeScopedLockedFileHandleOptions options;\n  options.mode = mode;\n  options.permissions = FilePermissions::kOwnerOnly;\n  if (log_open_error) {\n    options.function_enum =\n        internal::FileOpenFunction::kLoggingOpenFileForReadAndWrite;\n  } else {\n    options.function_enum =\n        internal::FileOpenFunction::kOpenFileForReadAndWrite;\n  }\n\n  return MakeScopedLockedFileHandle(\n      options, FileLocking::kExclusive, file_path());\n}\n\nbool Settings::OpenAndReadSettings(Data* out_data) {\n  // Because this implementation distinguishes between a failure to open and a\n  // failure to read the settings file, it cannot simply invoke\n  // SettingsReader::OpenAndReadSettings.\n  ScopedLockedFileHandle handle = OpenForReading();\n  if (!handle.is_valid())\n    return false;\n\n  if (ReadSettings(handle.get(), out_data, true))\n    return true;\n\n  // The settings file is corrupt, so reinitialize it.\n  handle.reset();\n\n  // The settings failed to be read, so re-initialize them.\n  return RecoverSettings(kInvalidFileHandle, out_data);\n}\n\nSettings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings(\n    Data* out_data) {\n  ScopedLockedFileHandle handle;\n  bool created = false;\n  if (!initialized().is_valid()) {\n    // If this object is initializing, it hasn’t seen a settings file already,\n    // so go easy on errors. Creating a new settings file for the first time\n    // shouldn’t spew log messages.\n    //\n    // First, try to use an existing settings file.\n    handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrFail, false);\n\n    if (!handle.is_valid()) {\n      // Create a new settings file if it didn’t already exist.\n      handle = OpenForReadingAndWriting(FileWriteMode::kCreateOrFail, false);\n\n      if (handle.is_valid()) {\n        created = true;\n      }\n\n      // There may have been a race to create the file, and something else may\n      // have won. There will be one more attempt to try to open or create the\n      // file below.\n    }\n  }\n\n  if (!handle.is_valid()) {\n    // Either the object is initialized, meaning it’s already seen a valid\n    // settings file, or the object is initializing and none of the above\n    // attempts to create the settings file succeeded. Either way, this is the\n    // last chance for success, so if this fails, log a message.\n    handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true);\n  }\n\n  if (!handle.is_valid())\n    return ScopedLockedFileHandle();\n\n  // Attempt reading the settings even if the file is known to have just been\n  // created. The file-create and file-lock operations don’t occur atomically,\n  // and something else may have written the settings before this invocation\n  // took the lock. If the settings file was definitely just created, though,\n  // don’t log any read errors. The expected non-race behavior in this case is a\n  // zero-length read, with ReadSettings() failing.\n  if (!ReadSettings(handle.get(), out_data, !created)) {\n    if (!RecoverSettings(handle.get(), out_data))\n      return ScopedLockedFileHandle();\n  }\n\n  return handle;\n}\n\nbool Settings::WriteSettings(FileHandle handle, const Data& data) {\n  if (LoggingSeekFile(handle, 0, SEEK_SET) != 0)\n    return false;\n\n  if (!LoggingTruncateFile(handle))\n    return false;\n\n  return LoggingWriteFile(handle, &data, sizeof(Data));\n}\n\nbool Settings::RecoverSettings(FileHandle handle, Data* out_data) {\n  ScopedLockedFileHandle scoped_handle;\n  if (handle == kInvalidFileHandle) {\n    scoped_handle =\n        OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true);\n    handle = scoped_handle.get();\n\n    // Test if the file has already been recovered now that the exclusive lock\n    // is held.\n    if (ReadSettings(handle, out_data, true))\n      return true;\n  }\n\n  if (handle == kInvalidFileHandle) {\n    LOG(ERROR) << \"Invalid file handle\";\n    return false;\n  }\n\n  if (!InitializeSettings(handle))\n    return false;\n\n  return ReadSettings(handle, out_data, true);\n}\n\nbool Settings::InitializeSettings(FileHandle handle) {\n  Data settings;\n  if (!settings.client_id.InitializeWithNew())\n    return false;\n\n  return WriteSettings(handle, settings);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/settings.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_SETTINGS_H_\n#define CRASHPAD_CLIENT_SETTINGS_H_\n\n#include <time.h>\n\n#include \"base/files/file_path.h\"\n#include \"base/scoped_generic.h\"\n#include \"build/build_config.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/initialization_state.h\"\n#include \"util/misc/uuid.h\"\n\n#if BUILDFLAG(IS_IOS)\n#include \"util/ios/scoped_background_task.h\"\n#endif  // BUILDFLAG(IS_IOS)\n\nnamespace crashpad {\n\nnamespace internal {\n\nstruct ScopedLockedFileHandleTraits {\n  static FileHandle InvalidValue() { return kInvalidFileHandle; }\n  static void Free(FileHandle handle);\n};\n\nenum class FileOpenFunction {\n  kLoggingOpenFileForRead,\n  kLoggingOpenFileForReadAndWrite,\n  kOpenFileForReadAndWrite,\n};\n\nstruct MakeScopedLockedFileHandleOptions {\n  FileOpenFunction function_enum;\n  FileWriteMode mode;\n  FilePermissions permissions;\n};\n\n// TODO(mark): The timeout should be configurable by the client.\n#if BUILDFLAG(IS_IOS)\n// iOS background assertions only last 30 seconds, keep the timeout shorter.\nconstexpr double kUploadReportTimeoutSeconds = 20;\n#else\nconstexpr double kUploadReportTimeoutSeconds = 60;\n#endif\n\n}  // namespace internal\n\n//! \\brief A class for accessing the settings of a CrashReportDatabase.\n//!\n//! SettingsReader does not participate in file locking.\n//!\n//! This class must not be instantiated directly, but rather an instance of it\n//! should be retrieved via\n//! CrashReportDatabase::GetSettingsReaderForDatabasePath.\nclass SettingsReader {\n public:\n  explicit SettingsReader(const base::FilePath& path);\n\n  SettingsReader(const SettingsReader&) = delete;\n  SettingsReader& operator=(const SettingsReader&) = delete;\n\n  virtual ~SettingsReader();\n\n  //! \\brief Retrieves the immutable identifier for this client, which is used\n  //!     on a server to locate all crash reports from a specific Crashpad\n  //!     database.\n  //!\n  //! \\param[out] client_id The unique client identifier.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false` with an\n  //!     error logged.\n  bool GetClientID(UUID* client_id);\n\n  //! \\brief Retrieves the user’s preference for submitting crash reports to a\n  //!     collection server.\n  //!\n  //! The default value is `false`.\n  //!\n  //! \\note\n  //! This setting is ignored if --use-cros-crash-reporter is present\n  //! (which it will be if invoked by Chrome on ChromeOS).\n  //!\n  //! \\param[out] enabled Whether crash reports should be uploaded.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false` with an\n  //!     error logged.\n  bool GetUploadsEnabled(bool* enabled);\n\n  //! \\brief Retrieves the last time at which a report was attempted to be\n  //!     uploaded.\n  //!\n  //! The default value is `0` if it has never been set before.\n  //!\n  //! \\param[out] time The last time at which a report was uploaded.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false` with an\n  //!     error logged.\n  bool GetLastUploadAttemptTime(time_t* time);\n\n protected:\n  struct Data;\n\n  SettingsReader(const base::FilePath& path, InitializationState::State state);\n\n  // Opens the settings file and reads the data. If that fails, an error will\n  // be logged and the function will return false.\n  virtual bool OpenAndReadSettings(Data* out_data);\n\n  // Reads the settings from |handle|. Logs an error and returns false on\n  // failure.\n  //\n  // If |log_read_error| is false, nothing will be logged for a read error, but\n  // this method will still return false. This is intended to be used to\n  // suppress error messages when attempting to read a newly created settings\n  // file.\n  bool ReadSettings(FileHandle handle, Data* out_data, bool log_read_error);\n\n  const base::FilePath& file_path() const { return file_path_; }\n\n  InitializationState& initialized() { return initialized_; }\n\n private:\n  const base::FilePath file_path_;\n  InitializationState initialized_;\n};\n\n//! \\brief An interface for accessing and modifying the settings of a\n//!     CrashReportDatabase.\n//!\n//! This class must not be instantiated directly, but rather an instance of it\n//! should be retrieved via CrashReportDatabase::GetSettings().\n//!\n//! Settings will lock files prior to reading or writing them, and respect\n//! existing locks.\nclass Settings final : public SettingsReader {\n public:\n  static inline constexpr char kLockfileExtension[] = \".__lock__\";\n\n  explicit Settings(const base::FilePath& file_path);\n\n  ~Settings();\n\n  //! \\brief Initializes the settings data store.\n  //!\n  //! This method must be called only once, and must be successfully called\n  //! before any other method in this class may be called.\n  //!\n  //! \\return `true` if the data store was initialized successfully, otherwise\n  //!     `false` with an error logged.\n  bool Initialize();\n\n  //! \\brief Sets the user’s preference for submitting crash reports to a\n  //!     collection server.\n  //!\n  //! \\param[in] enabled Whether crash reports should be uploaded.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false` with an\n  //!     error logged.\n  bool SetUploadsEnabled(bool enabled);\n\n  //! \\brief Sets the last time at which a report was attempted to be uploaded.\n  //!\n  //! This is only meant to be used internally by the CrashReportDatabase.\n  //!\n  //! \\param[in] time The last time at which a report was uploaded.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false` with an\n  //!     error logged.\n  bool SetLastUploadAttemptTime(time_t time);\n\n#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n  //! \\brief Returns whether the lockfile for a file is expired.\n  //!\n  //! This could be part of ScopedLockedFileHandle, but this needs to be\n  //! public while ScopedLockedFileHandle is private to Settings.\n  //!\n  //! \\param[in] file_path The path to the file whose lockfile will be checked.\n  //! \\param[in] lockfile_ttl How long the lockfile has to live before expiring.\n  //!\n  //! \\return `true` if the lock for the file is expired, otherwise `false`.\n  static bool IsLockExpired(const base::FilePath& file_path,\n                            time_t lockfile_ttl);\n#endif  // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\n private:\n  // This must be constructed with MakeScopedLockedFileHandle(). It both unlocks\n  // and closes the file on destruction. Note that on Fuchsia, this handle DOES\n  // NOT offer correct operation, only an attempt to DCHECK if racy behavior is\n  // detected.\n#if !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n  struct ScopedLockedFileHandle {\n   public:\n    ScopedLockedFileHandle();\n    ScopedLockedFileHandle(FileHandle handle,\n                           const base::FilePath& lockfile_path);\n    ScopedLockedFileHandle(ScopedLockedFileHandle&& other);\n    ScopedLockedFileHandle& operator=(ScopedLockedFileHandle&& other);\n\n    ScopedLockedFileHandle(const ScopedLockedFileHandle&) = delete;\n    ScopedLockedFileHandle& operator=(const ScopedLockedFileHandle&) = delete;\n\n    ~ScopedLockedFileHandle();\n\n    // These mirror the non-Fuchsia ScopedLockedFileHandle via ScopedGeneric so\n    // that calling code can pretend this implementation is the same.\n    bool is_valid() const { return handle_ != kInvalidFileHandle; }\n    FileHandle get() { return handle_; }\n    void reset() {\n      Destroy();\n      handle_ = kInvalidFileHandle;\n      lockfile_path_ = base::FilePath();\n    }\n\n   private:\n    void Destroy();\n\n    FileHandle handle_;\n    base::FilePath lockfile_path_;\n  };\n#elif BUILDFLAG(IS_IOS)\n  // iOS needs to use ScopedBackgroundTask anytime a file lock is used.\n  class ScopedLockedFileHandle\n      : public base::ScopedGeneric<FileHandle,\n                                   internal::ScopedLockedFileHandleTraits> {\n   public:\n    using base::ScopedGeneric<\n        FileHandle,\n        internal::ScopedLockedFileHandleTraits>::ScopedGeneric;\n\n    ScopedLockedFileHandle(const FileHandle& value);\n    ScopedLockedFileHandle(ScopedLockedFileHandle&& rvalue);\n    ScopedLockedFileHandle& operator=(ScopedLockedFileHandle&& rvalue);\n\n    ~ScopedLockedFileHandle();\n\n   private:\n    std::unique_ptr<internal::ScopedBackgroundTask> ios_background_task_;\n  };\n#else\n  using ScopedLockedFileHandle =\n      base::ScopedGeneric<FileHandle, internal::ScopedLockedFileHandleTraits>;\n#endif  // !CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n  static ScopedLockedFileHandle MakeScopedLockedFileHandle(\n      const internal::MakeScopedLockedFileHandleOptions& options,\n      FileLocking locking,\n      const base::FilePath& file_path);\n\n  static FileHandle GetHandleFromOptions(\n      const base::FilePath& file_path,\n      const internal::MakeScopedLockedFileHandleOptions& options);\n\n  // Opens the settings file for reading. On error, logs a message and returns\n  // the invalid handle.\n  ScopedLockedFileHandle OpenForReading() const;\n\n  // Opens the settings file for reading and writing. On error, logs a message\n  // and returns the invalid handle. |mode| determines how the file will be\n  // opened. |mode| must not be FileWriteMode::kTruncateOrCreate.\n  //\n  // If |log_open_error| is false, nothing will be logged for an error\n  // encountered when attempting to open the file, but this method will still\n  // return false. This is intended to be used to suppress error messages when\n  // attempting to create a new settings file when multiple attempts are made.\n  ScopedLockedFileHandle OpenForReadingAndWriting(FileWriteMode mode,\n                                                  bool log_open_error);\n\n  // Opens the settings file and reads the data. If that fails, an error will\n  // be logged and the settings will be recovered and re-initialized. If that\n  // also fails, returns false with additional log data from recovery.\n  bool OpenAndReadSettings(Data* out_data) override;\n\n  // Opens the settings file for writing and reads the data. If reading fails,\n  // recovery is attempted. Returns the opened file handle on success, or the\n  // invalid file handle on failure, with an error logged.\n  ScopedLockedFileHandle OpenForWritingAndReadSettings(Data* out_data);\n\n  // Writes the settings to |handle|. Logs an error and returns false on\n  // failure. This does not perform recovery.\n  //\n  // |handle| must be the result of OpenForReadingAndWriting().\n  bool WriteSettings(FileHandle handle, const Data& data);\n\n  // Recovers the settings file by re-initializing the data. If |handle| is the\n  // invalid handle, this will open the file; if it is not, then it must be the\n  // result of OpenForReadingAndWriting(). If the invalid handle is passed, the\n  // caller must not be holding the handle. The new settings data are stored in\n  // |out_data|. Returns true on success and false on failure, with an error\n  // logged.\n  bool RecoverSettings(FileHandle handle, Data* out_data);\n\n  // Initializes a settings file and writes the data to |handle|. Returns true\n  // on success and false on failure, with an error logged.\n  //\n  // |handle| must be the result of OpenForReadingAndWriting().\n  bool InitializeSettings(FileHandle handle);\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_SETTINGS_H_\n"
  },
  {
    "path": "client/settings_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/settings.h\"\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass SettingsTest : public testing::Test {\n public:\n  SettingsTest()\n      : temp_dir_(),\n        settings_path_(temp_dir_.path().Append(FILE_PATH_LITERAL(\"settings\"))),\n        settings_(settings_path_) {}\n\n  SettingsTest(const SettingsTest&) = delete;\n  SettingsTest& operator=(const SettingsTest&) = delete;\n\n  base::FilePath settings_path() { return settings_path_; }\n\n  Settings* settings() { return &settings_; }\n\n  void InitializeBadFile() {\n    ScopedFileHandle handle(\n        LoggingOpenFileForWrite(settings_path(),\n                                FileWriteMode::kTruncateOrCreate,\n                                FilePermissions::kWorldReadable));\n    ASSERT_TRUE(handle.is_valid());\n\n    static constexpr char kBuf[] = \"test bad file\";\n    ASSERT_TRUE(LoggingWriteFile(handle.get(), kBuf, sizeof(kBuf)));\n    handle.reset();\n  }\n\n protected:\n  // testing::Test:\n  void SetUp() override { ASSERT_TRUE(settings()->Initialize()); }\n\n private:\n  const ScopedTempDir temp_dir_;\n  const base::FilePath settings_path_;\n  Settings settings_;\n};\n\nTEST_F(SettingsTest, ClientID) {\n  UUID client_id;\n  EXPECT_TRUE(settings()->GetClientID(&client_id));\n  EXPECT_NE(client_id, UUID());\n\n  Settings local_settings(settings_path());\n  EXPECT_TRUE(local_settings.Initialize());\n  UUID actual;\n  EXPECT_TRUE(local_settings.GetClientID(&actual));\n  EXPECT_EQ(actual, client_id);\n}\n\nTEST_F(SettingsTest, UploadsEnabled) {\n  bool enabled = true;\n  // Default value is false.\n  EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled));\n  EXPECT_FALSE(enabled);\n\n  EXPECT_TRUE(settings()->SetUploadsEnabled(true));\n  EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled));\n  EXPECT_TRUE(enabled);\n\n  Settings local_settings(settings_path());\n  EXPECT_TRUE(local_settings.Initialize());\n  enabled = false;\n  EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled));\n  EXPECT_TRUE(enabled);\n\n  EXPECT_TRUE(settings()->SetUploadsEnabled(false));\n  EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled));\n  EXPECT_FALSE(enabled);\n\n  enabled = true;\n  EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled));\n  EXPECT_FALSE(enabled);\n}\n\nTEST_F(SettingsTest, LastUploadAttemptTime) {\n  time_t actual = -1;\n  EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual));\n  // Default value is 0.\n  EXPECT_EQ(actual, 0);\n\n  const time_t expected = time(nullptr);\n  EXPECT_TRUE(settings()->SetLastUploadAttemptTime(expected));\n  EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual));\n  EXPECT_EQ(actual, expected);\n\n  Settings local_settings(settings_path());\n  EXPECT_TRUE(local_settings.Initialize());\n  actual = -1;\n  EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&actual));\n  EXPECT_EQ(actual, expected);\n}\n\nTEST_F(SettingsTest, ReadOnly) {\n  {  // A SettingsReader can read an existing setting...\n    SettingsReader reader(settings_path());\n    bool enabled = true;\n    // Default value is false.\n    EXPECT_TRUE(reader.GetUploadsEnabled(&enabled));\n    EXPECT_FALSE(enabled);\n\n    settings()->SetUploadsEnabled(true);\n    EXPECT_TRUE(reader.GetUploadsEnabled(&enabled));\n    EXPECT_TRUE(enabled);\n  }\n\n  {  // ...but not one that doesn't exist.\n    base::FilePath bad_path =\n        settings_path().DirName().Append(FILE_PATH_LITERAL(\"does_not_exist\"));\n    SettingsReader reader(bad_path);\n    bool enabled = true;\n    EXPECT_FALSE(reader.GetUploadsEnabled(&enabled));\n  }\n}\n\n// The following tests write a corrupt settings file and test the recovery\n// operation.\n\nTEST_F(SettingsTest, BadFileOnInitialize) {\n  InitializeBadFile();\n\n  Settings settings(settings_path());\n  EXPECT_TRUE(settings.Initialize());\n}\n\nTEST_F(SettingsTest, BadFileOnGet) {\n  InitializeBadFile();\n\n  UUID client_id;\n  EXPECT_TRUE(settings()->GetClientID(&client_id));\n  EXPECT_NE(client_id, UUID());\n\n  Settings local_settings(settings_path());\n  EXPECT_TRUE(local_settings.Initialize());\n  UUID actual;\n  EXPECT_TRUE(local_settings.GetClientID(&actual));\n  EXPECT_EQ(actual, client_id);\n}\n\nTEST_F(SettingsTest, BadFileOnSet) {\n  InitializeBadFile();\n\n  EXPECT_TRUE(settings()->SetUploadsEnabled(true));\n  bool enabled = false;\n  EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled));\n  EXPECT_TRUE(enabled);\n}\n\nTEST_F(SettingsTest, UnlinkFile) {\n  UUID client_id;\n  EXPECT_TRUE(settings()->GetClientID(&client_id));\n  EXPECT_TRUE(settings()->SetUploadsEnabled(true));\n  EXPECT_TRUE(settings()->SetLastUploadAttemptTime(time(nullptr)));\n\n#if BUILDFLAG(IS_WIN)\n  EXPECT_EQ(_wunlink(settings_path().value().c_str()), 0)\n      << ErrnoMessage(\"_wunlink\");\n#else\n  EXPECT_EQ(unlink(settings_path().value().c_str()), 0)\n      << ErrnoMessage(\"unlink\");\n#endif\n\n  Settings local_settings(settings_path());\n  EXPECT_TRUE(local_settings.Initialize());\n  UUID new_client_id;\n  EXPECT_TRUE(local_settings.GetClientID(&new_client_id));\n  EXPECT_NE(new_client_id, client_id);\n\n  // Check that all values are reset.\n  bool enabled = true;\n  EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled));\n  EXPECT_FALSE(enabled);\n\n  time_t time = -1;\n  EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&time));\n  EXPECT_EQ(time, 0);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/simple_address_range_bag.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_\n#define CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_\n\n#include <stdint.h>\n#include <string.h>\n\n#include <type_traits>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\n\n//! \\brief A bag implementation using a fixed amount of storage, so that it does\n//!     not perform any dynamic allocations for its operations.\n//!\n//! The actual bag storage (TSimpleAddressRangeBag::Entry) is POD, so that it\n//! can be transmitted over various IPC mechanisms.\ntemplate <size_t NumEntries = 64>\nclass TSimpleAddressRangeBag {\n public:\n  //! Constant and publicly accessible version of the template parameter.\n  static const size_t num_entries = NumEntries;\n\n  //! \\brief A single entry in the bag.\n  struct Entry {\n    //! \\brief The base address of the range.\n    uint64_t base;\n\n    //! \\brief The size of the range in bytes.\n    uint64_t size;\n\n    //! \\brief Returns the validity of the entry.\n    //!\n    //! If #base and #size are both zero, the entry is considered inactive, and\n    //! this method returns `false`. Otherwise, returns `true`.\n    bool is_active() const {\n      return base != 0 || size != 0;\n    }\n  };\n\n  //! \\brief An iterator to traverse all of the active entries in a\n  //!     TSimpleAddressRangeBag.\n  class Iterator {\n   public:\n    explicit Iterator(const TSimpleAddressRangeBag& bag)\n        : bag_(bag),\n          current_(0) {\n    }\n\n    Iterator(const Iterator&) = delete;\n    Iterator& operator=(const Iterator&) = delete;\n\n    //! \\brief Returns the next entry in the bag, or `nullptr` if at the end of\n    //!     the collection.\n    const Entry* Next() {\n      while (current_ < bag_.num_entries) {\n        const Entry* entry = &bag_.entries_[current_++];\n        if (entry->is_active()) {\n          return entry;\n        }\n      }\n      return nullptr;\n    }\n\n   private:\n    const TSimpleAddressRangeBag& bag_;\n    size_t current_;\n  };\n\n  TSimpleAddressRangeBag()\n      : entries_() {\n  }\n\n  TSimpleAddressRangeBag(const TSimpleAddressRangeBag& other) {\n    *this = other;\n  }\n\n  TSimpleAddressRangeBag& operator=(const TSimpleAddressRangeBag& other) {\n    memcpy(entries_, other.entries_, sizeof(entries_));\n    return *this;\n  }\n\n  //! \\brief Returns the number of active entries. The upper limit for this is\n  //!     \\a NumEntries.\n  size_t GetCount() const {\n    size_t count = 0;\n    for (size_t i = 0; i < num_entries; ++i) {\n      if (entries_[i].is_active()) {\n        ++count;\n      }\n    }\n    return count;\n  }\n\n  //! \\brief Inserts the given range into the bag. Duplicates and overlapping\n  //! ranges are supported and allowed, but not coalesced.\n  //!\n  //! \\param[in] range The range to be inserted. The range must have either a\n  //!     non-zero base address or size.\n  //!\n  //! \\return `true` if there was space to insert the range into the bag,\n  //!     otherwise `false` with an error logged.\n  bool Insert(CheckedRange<uint64_t> range) {\n    DCHECK(range.base() != 0 || range.size() != 0);\n\n    for (size_t i = 0; i < num_entries; ++i) {\n      if (!entries_[i].is_active()) {\n        entries_[i].base = range.base();\n        entries_[i].size = range.size();\n        return true;\n      }\n    }\n\n    LOG(ERROR) << \"no space available to insert range\";\n    return false;\n  }\n\n  //! \\brief Inserts the given range into the bag. Duplicates and overlapping\n  //! ranges are supported and allowed, but not coalesced.\n  //!\n  //! \\param[in] base The base of the range to be inserted. May not be null.\n  //! \\param[in] size The size of the range to be inserted. May not be zero.\n  //!\n  //! \\return `true` if there was space to insert the range into the bag,\n  //!     otherwise `false` with an error logged.\n  bool Insert(void* base, size_t size) {\n    DCHECK(base != nullptr);\n    DCHECK_NE(0u, size);\n    return Insert(CheckedRange<uint64_t>(FromPointerCast<uint64_t>(base),\n                                         base::checked_cast<uint64_t>(size)));\n  }\n\n  //! \\brief Removes the given range from the bag.\n  //!\n  //! \\param[in] range The range to be removed. The range must have either a\n  //!     non-zero base address or size.\n  //!\n  //! \\return `true` if the range was found and removed, otherwise `false` with\n  //!     an error logged.\n  bool Remove(CheckedRange<uint64_t> range) {\n    DCHECK(range.base() != 0 || range.size() != 0);\n\n    for (size_t i = 0; i < num_entries; ++i) {\n      if (entries_[i].base == range.base() &&\n          entries_[i].size == range.size()) {\n        entries_[i].base = entries_[i].size = 0;\n        return true;\n      }\n    }\n\n    LOG(ERROR) << \"did not find range to remove\";\n    return false;\n  }\n\n  //! \\brief Removes the given range from the bag.\n  //!\n  //! \\param[in] base The base of the range to be removed. May not be null.\n  //! \\param[in] size The size of the range to be removed. May not be zero.\n  //!\n  //! \\return `true` if the range was found and removed, otherwise `false` with\n  //! an error logged.\n  bool Remove(void* base, size_t size) {\n    DCHECK(base != nullptr);\n    DCHECK_NE(0u, size);\n    return Remove(CheckedRange<uint64_t>(FromPointerCast<uint64_t>(base),\n                                         base::checked_cast<uint64_t>(size)));\n  }\n\n\n private:\n  Entry entries_[NumEntries];\n};\n\n//! \\brief A TSimpleAddressRangeBag with default template parameters.\nusing SimpleAddressRangeBag = TSimpleAddressRangeBag<64>;\n\nstatic_assert(std::is_standard_layout<SimpleAddressRangeBag>::value,\n              \"SimpleAddressRangeBag must be standard layout\");\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_\n"
  },
  {
    "path": "client/simple_address_range_bag_test.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/simple_address_range_bag.h\"\n\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(SimpleAddressRangeBag, Entry) {\n  using TestBag = TSimpleAddressRangeBag<15>;\n  TestBag bag;\n\n  const TestBag::Entry* entry = TestBag::Iterator(bag).Next();\n  EXPECT_FALSE(entry);\n\n  bag.Insert(reinterpret_cast<void*>(0x1000), 200);\n  entry = TestBag::Iterator(bag).Next();\n  ASSERT_TRUE(entry);\n  EXPECT_EQ(0x1000u, entry->base);\n  EXPECT_EQ(200u, entry->size);\n\n  bag.Remove(reinterpret_cast<void*>(0x1000), 200);\n  EXPECT_FALSE(entry->is_active());\n  EXPECT_EQ(0u, entry->base);\n  EXPECT_EQ(0u, entry->size);\n}\n\nTEST(SimpleAddressRangeBag, SimpleAddressRangeBag) {\n  SimpleAddressRangeBag bag;\n\n  EXPECT_TRUE(bag.Insert(reinterpret_cast<void*>(0x1000), 10));\n  EXPECT_TRUE(bag.Insert(reinterpret_cast<void*>(0x2000), 20));\n  EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(0x3000, 30)));\n\n  EXPECT_EQ(3u, bag.GetCount());\n\n  // Duplicates added too.\n  EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(0x3000, 30)));\n  EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(0x3000, 30)));\n  EXPECT_EQ(5u, bag.GetCount());\n\n  // Can be removed 3 times, but not the 4th time.\n  EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));\n  EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));\n  EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));\n  EXPECT_EQ(2u, bag.GetCount());\n  EXPECT_FALSE(bag.Remove(CheckedRange<uint64_t>(0x3000, 30)));\n  EXPECT_EQ(2u, bag.GetCount());\n\n  EXPECT_TRUE(bag.Remove(reinterpret_cast<void*>(0x1000), 10));\n  EXPECT_TRUE(bag.Remove(reinterpret_cast<void*>(0x2000), 20));\n  EXPECT_EQ(0u, bag.GetCount());\n}\n\nTEST(SimpleAddressRangeBag, CopyAndAssign) {\n  TSimpleAddressRangeBag<10> bag;\n  EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(1, 2)));\n  EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(3, 4)));\n  EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(5, 6)));\n  EXPECT_TRUE(bag.Remove(CheckedRange<uint64_t>(3, 4)));\n  EXPECT_EQ(bag.GetCount(), 2u);\n\n  // Test copy.\n  TSimpleAddressRangeBag<10> bag_copy(bag);\n  EXPECT_EQ(bag_copy.GetCount(), 2u);\n  EXPECT_TRUE(bag_copy.Remove(CheckedRange<uint64_t>(1, 2)));\n  EXPECT_TRUE(bag_copy.Remove(CheckedRange<uint64_t>(5, 6)));\n  EXPECT_EQ(bag_copy.GetCount(), 0u);\n  EXPECT_EQ(bag.GetCount(), 2u);\n\n  // Test assign.\n  TSimpleAddressRangeBag<10> bag_assign;\n  bag_assign = bag;\n  EXPECT_EQ(bag_assign.GetCount(), 2u);\n  EXPECT_TRUE(bag_assign.Remove(CheckedRange<uint64_t>(1, 2)));\n  EXPECT_TRUE(bag_assign.Remove(CheckedRange<uint64_t>(5, 6)));\n  EXPECT_EQ(bag_assign.GetCount(), 0u);\n  EXPECT_EQ(bag.GetCount(), 2u);\n}\n\n// Running out of space shouldn't crash.\nTEST(SimpleAddressRangeBag, OutOfSpace) {\n  TSimpleAddressRangeBag<2> bag;\n  EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(1, 2)));\n  EXPECT_TRUE(bag.Insert(CheckedRange<uint64_t>(3, 4)));\n  EXPECT_FALSE(bag.Insert(CheckedRange<uint64_t>(5, 6)));\n  EXPECT_EQ(bag.GetCount(), 2u);\n  EXPECT_FALSE(bag.Remove(CheckedRange<uint64_t>(5, 6)));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/simple_string_dictionary.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_\n#define CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <string_view>\n#include <type_traits>\n\n#include \"base/check_op.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\n\n//! \\brief A map/dictionary collection implementation using a fixed amount of\n//!     storage, so that it does not perform any dynamic allocations for its\n//!     operations.\n//!\n//! The actual map storage (TSimpleStringDictionary::Entry) is guaranteed to be\n//! POD, so that it can be transmitted over various IPC mechanisms.\n//!\n//! The template parameters control the amount of storage used for the key,\n//! value, and map. The \\a KeySize and \\a ValueSize are measured in bytes, not\n//! glyphs, and include space for a trailing `NUL` byte. This gives space for\n//! `KeySize - 1` and `ValueSize - 1` characters in an entry. \\a NumEntries is\n//! the total number of entries that will fit in the map.\ntemplate <size_t KeySize = 256, size_t ValueSize = 256, size_t NumEntries = 64>\nclass TSimpleStringDictionary {\n public:\n  //! \\brief Constant and publicly accessible versions of the template\n  //!     parameters.\n  //! \\{\n  static const size_t key_size = KeySize;\n  static const size_t value_size = ValueSize;\n  static const size_t num_entries = NumEntries;\n  //! \\}\n\n  //! \\brief A single entry in the map.\n  struct Entry {\n    //! \\brief The entry’s key.\n    //!\n    //! This string is always `NUL`-terminated. If this is a 0-length\n    //! `NUL`-terminated string, the entry is inactive.\n    char key[KeySize];\n\n    //! \\brief The entry’s value.\n    //!\n    //! This string is always `NUL`-terminated.\n    char value[ValueSize];\n\n    //! \\brief Returns the validity of the entry.\n    //!\n    //! If #key is an empty string, the entry is considered inactive, and this\n    //! method returns `false`. Otherwise, returns `true`.\n    bool is_active() const {\n      return key[0] != '\\0';\n    }\n  };\n\n  //! \\brief An iterator to traverse all of the active entries in a\n  //!     TSimpleStringDictionary.\n  class Iterator {\n   public:\n    explicit Iterator(const TSimpleStringDictionary& map)\n        : map_(map),\n          current_(0) {\n    }\n\n    Iterator(const Iterator&) = delete;\n    Iterator& operator=(const Iterator&) = delete;\n\n    //! \\brief Returns the next entry in the map, or `nullptr` if at the end of\n    //!     the collection.\n    const Entry* Next() {\n      while (current_ < map_.num_entries) {\n        const Entry* entry = &map_.entries_[current_++];\n        if (entry->is_active()) {\n          return entry;\n        }\n      }\n      return nullptr;\n    }\n\n   private:\n    const TSimpleStringDictionary& map_;\n    size_t current_;\n  };\n\n  TSimpleStringDictionary()\n      : entries_() {\n  }\n\n  TSimpleStringDictionary(const TSimpleStringDictionary& other) {\n    *this = other;\n  }\n\n  TSimpleStringDictionary& operator=(const TSimpleStringDictionary& other) {\n    memcpy(entries_, other.entries_, sizeof(entries_));\n    return *this;\n  }\n\n  //! \\brief Returns the number of active key/value pairs. The upper limit for\n  //!     this is \\a NumEntries.\n  size_t GetCount() const {\n    size_t count = 0;\n    for (size_t i = 0; i < num_entries; ++i) {\n      if (entries_[i].is_active()) {\n        ++count;\n      }\n    }\n    return count;\n  }\n\n  //! \\brief Given \\a key, returns its corresponding value.\n  //!\n  //! \\param[in] key The key to look up. This must not be `nullptr`, nor an\n  //!     empty string. It must not contain embedded `NUL`s.\n  //!\n  //! \\return The corresponding value for \\a key, or if \\a key is not found,\n  //!     `nullptr`.\n  const char* GetValueForKey(std::string_view key) const {\n    DCHECK(key.data());\n    DCHECK(key.size());\n    DCHECK_EQ(key.find('\\0', 0), std::string_view::npos);\n    if (!key.data() || !key.size()) {\n      return nullptr;\n    }\n\n    const Entry* entry = GetConstEntryForKey(key);\n    if (!entry) {\n      return nullptr;\n    }\n\n    return entry->value;\n  }\n\n  //! \\brief Stores \\a value into \\a key, replacing the existing value if \\a key\n  //!     is already present.\n  //!\n  //! If \\a key is not yet in the map and the map is already full (containing\n  //! \\a NumEntries active entries), this operation silently fails.\n  //!\n  //! \\param[in] key The key to store. This must not be `nullptr`, nor an empty\n  //!     string. It must not contain embedded `NUL`s.\n  //! \\param[in] value The value to store. If `nullptr`, \\a key is removed from\n  //!     the map. Must not contain embedded `NUL`s.\n  void SetKeyValue(std::string_view key, std::string_view value) {\n    if (!value.data()) {\n      RemoveKey(key);\n      return;\n    }\n\n    DCHECK(key.data());\n    DCHECK(key.size());\n    DCHECK_EQ(key.find('\\0', 0), std::string_view::npos);\n    if (!key.data() || !key.size()) {\n      return;\n    }\n\n    // |key| must not be an empty string.\n    DCHECK_NE(key[0], '\\0');\n    if (key[0] == '\\0') {\n      return;\n    }\n\n    // |value| must not contain embedded NULs.\n    DCHECK_EQ(value.find('\\0', 0), std::string_view::npos);\n\n    Entry* entry = GetEntryForKey(key);\n\n    // If it does not yet exist, attempt to insert it.\n    if (!entry) {\n      for (size_t i = 0; i < num_entries; ++i) {\n        if (!entries_[i].is_active()) {\n          entry = &entries_[i];\n          SetFromStringView(key, entry->key, key_size);\n          break;\n        }\n      }\n    }\n\n    // If the map is out of space, |entry| will be nullptr.\n    if (!entry) {\n      return;\n    }\n\n#ifndef NDEBUG\n    // Sanity check that the key only appears once.\n    int count = 0;\n    for (size_t i = 0; i < num_entries; ++i) {\n      if (EntryKeyEquals(key, entries_[i])) {\n        ++count;\n      }\n    }\n    DCHECK_EQ(count, 1);\n#endif\n\n    SetFromStringView(value, entry->value, value_size);\n  }\n\n  //! \\brief Removes \\a key from the map.\n  //!\n  //! If \\a key is not found, this is a no-op.\n  //!\n  //! \\param[in] key The key of the entry to remove. This must not be `nullptr`,\n  //!     nor an empty string. It must not contain embedded `NUL`s.\n  void RemoveKey(std::string_view key) {\n    DCHECK(key.data());\n    DCHECK(key.size());\n    DCHECK_EQ(key.find('\\0', 0), std::string_view::npos);\n    if (!key.data() || !key.size()) {\n      return;\n    }\n\n    Entry* entry = GetEntryForKey(key);\n    if (entry) {\n      entry->key[0] = '\\0';\n      entry->value[0] = '\\0';\n    }\n\n    DCHECK_EQ(GetEntryForKey(key), implicit_cast<Entry*>(nullptr));\n  }\n\n private:\n  static void SetFromStringView(std::string_view src,\n                                char* dst,\n                                size_t dst_size) {\n    size_t copy_len = std::min(dst_size - 1, src.size());\n    src.copy(dst, copy_len);\n    dst[copy_len] = '\\0';\n  }\n\n  static bool EntryKeyEquals(std::string_view key, const Entry& entry) {\n    if (key.size() >= KeySize)\n      return false;\n\n    // Test for a NUL terminator and early out if it's absent.\n    if (entry.key[key.size()] != '\\0')\n      return false;\n\n    // As there's a NUL terminator at the right position in the entries\n    // string, strncmp can do the rest.\n    return strncmp(key.data(), entry.key, key.size()) == 0;\n  }\n\n  const Entry* GetConstEntryForKey(std::string_view key) const {\n    for (size_t i = 0; i < num_entries; ++i) {\n      if (EntryKeyEquals(key, entries_[i])) {\n        return &entries_[i];\n      }\n    }\n    return nullptr;\n  }\n\n  Entry* GetEntryForKey(std::string_view key) {\n    return const_cast<Entry*>(GetConstEntryForKey(key));\n  }\n\n  Entry entries_[NumEntries];\n};\n\n//! \\brief A TSimpleStringDictionary with default template parameters.\n//!\n//! For historical reasons this specialized version is available with the same\n//! size factors as a previous implementation.\nusing SimpleStringDictionary = TSimpleStringDictionary<256, 256, 64>;\n\nstatic_assert(std::is_standard_layout<SimpleStringDictionary>::value,\n              \"SimpleStringDictionary must be standard layout\");\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_\n"
  },
  {
    "path": "client/simple_string_dictionary_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/simple_string_dictionary.h\"\n\n#include <string_view>\n\n#include \"base/check_op.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(SimpleStringDictionary, Entry) {\n  using TestMap = TSimpleStringDictionary<5, 9, 15>;\n  TestMap map;\n\n  const TestMap::Entry* entry = TestMap::Iterator(map).Next();\n  EXPECT_FALSE(entry);\n\n  // Try setting a key/value and then verify.\n  map.SetKeyValue(\"key1\", \"value1\");\n  entry = TestMap::Iterator(map).Next();\n  ASSERT_TRUE(entry);\n  EXPECT_STREQ(entry->key, \"key1\");\n  EXPECT_STREQ(entry->value, \"value1\");\n\n  // Try setting a new value.\n  map.SetKeyValue(\"key1\", \"value3\");\n  EXPECT_STREQ(entry->value, \"value3\");\n\n  // Make sure the key didn't change.\n  EXPECT_STREQ(entry->key, \"key1\");\n\n  // Clear the entry and verify the key and value are empty strings.\n  map.RemoveKey(\"key1\");\n  EXPECT_FALSE(entry->is_active());\n  EXPECT_EQ(0u, strlen(entry->key));\n  EXPECT_EQ(0u, strlen(entry->value));\n}\n\nTEST(SimpleStringDictionary, SimpleStringDictionary) {\n  // Make a new dictionary\n  SimpleStringDictionary dict;\n\n  // Set three distinct values on three keys\n  dict.SetKeyValue(\"key1\", \"value1\");\n  dict.SetKeyValue(\"key2\", \"value2\");\n  dict.SetKeyValue(\"key3\", \"value3\");\n\n  EXPECT_NE(dict.GetValueForKey(\"key1\"), \"value1\");\n  EXPECT_NE(dict.GetValueForKey(\"key2\"), \"value2\");\n  EXPECT_NE(dict.GetValueForKey(\"key3\"), \"value3\");\n  EXPECT_EQ(3u, dict.GetCount());\n  // try an unknown key\n  EXPECT_FALSE(dict.GetValueForKey(\"key4\"));\n\n  // Remove a key\n  dict.RemoveKey(\"key3\");\n\n  // Now make sure it's not there anymore\n  EXPECT_FALSE(dict.GetValueForKey(\"key3\"));\n\n  // Remove by setting value to nullptr\n  dict.SetKeyValue(\"key2\", std::string_view(nullptr, 0));\n\n  // Now make sure it's not there anymore\n  EXPECT_FALSE(dict.GetValueForKey(\"key2\"));\n}\n\nTEST(SimpleStringDictionary, CopyAndAssign) {\n  TSimpleStringDictionary<10, 10, 10> map;\n  map.SetKeyValue(\"one\", \"a\");\n  map.SetKeyValue(\"two\", \"b\");\n  map.SetKeyValue(\"three\", \"c\");\n  map.RemoveKey(\"two\");\n  EXPECT_EQ(map.GetCount(), 2u);\n\n  // Test copy.\n  TSimpleStringDictionary<10, 10, 10> map_copy(map);\n  EXPECT_EQ(map_copy.GetCount(), 2u);\n  EXPECT_STREQ(\"a\", map_copy.GetValueForKey(\"one\"));\n  EXPECT_STREQ(\"c\", map_copy.GetValueForKey(\"three\"));\n  map_copy.SetKeyValue(\"four\", \"d\");\n  EXPECT_STREQ(\"d\", map_copy.GetValueForKey(\"four\"));\n  EXPECT_FALSE(map.GetValueForKey(\"four\"));\n\n  // Test assign.\n  TSimpleStringDictionary<10, 10, 10> map_assign;\n  map_assign = map;\n  EXPECT_EQ(map_assign.GetCount(), 2u);\n  EXPECT_STREQ(\"a\", map_assign.GetValueForKey(\"one\"));\n  EXPECT_STREQ(\"c\", map_assign.GetValueForKey(\"three\"));\n  map_assign.SetKeyValue(\"four\", \"d\");\n  EXPECT_STREQ(\"d\", map_assign.GetValueForKey(\"four\"));\n  EXPECT_FALSE(map.GetValueForKey(\"four\"));\n\n  map.RemoveKey(\"one\");\n  EXPECT_FALSE(map.GetValueForKey(\"one\"));\n  EXPECT_STREQ(\"a\", map_copy.GetValueForKey(\"one\"));\n  EXPECT_STREQ(\"a\", map_assign.GetValueForKey(\"one\"));\n}\n\n// Add a bunch of values to the dictionary, remove some entries in the middle,\n// and then add more.\nTEST(SimpleStringDictionary, Iterator) {\n  SimpleStringDictionary dict;\n\n  char key[SimpleStringDictionary::key_size];\n  char value[SimpleStringDictionary::value_size];\n\n  constexpr int kDictionaryCapacity = SimpleStringDictionary::num_entries;\n  constexpr int kPartitionIndex = kDictionaryCapacity - 5;\n\n  // We assume at least this size in the tests below\n  ASSERT_GE(kDictionaryCapacity, 64);\n\n  // We'll keep track of the number of key/value pairs we think should be in the\n  // dictionary\n  int expected_dictionary_size = 0;\n\n  // Set a bunch of key/value pairs like key0/value0, key1/value1, ...\n  for (int i = 0; i < kPartitionIndex; ++i) {\n    ASSERT_LT(snprintf(key, sizeof(key), \"key%d\", i),\n              static_cast<int>(sizeof(key)));\n    ASSERT_LT(snprintf(value, sizeof(value), \"value%d\", i),\n              static_cast<int>(sizeof(value)));\n    dict.SetKeyValue(key, value);\n  }\n  expected_dictionary_size = kPartitionIndex;\n\n  // set a couple of the keys twice (with the same value) - should be nop\n  dict.SetKeyValue(\"key2\", \"value2\");\n  dict.SetKeyValue(\"key4\", \"value4\");\n  dict.SetKeyValue(\"key15\", \"value15\");\n\n  // Remove some random elements in the middle\n  dict.RemoveKey(\"key7\");\n  dict.RemoveKey(\"key18\");\n  dict.RemoveKey(\"key23\");\n  dict.RemoveKey(\"key31\");\n  expected_dictionary_size -= 4;  // we just removed four key/value pairs\n\n  // Set some more key/value pairs like key59/value59, key60/value60, ...\n  for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) {\n    ASSERT_LT(snprintf(key, sizeof(key), \"key%d\", i),\n              static_cast<int>(sizeof(key)));\n    ASSERT_LT(snprintf(value, sizeof(value), \"value%d\", i),\n              static_cast<int>(sizeof(value)));\n    dict.SetKeyValue(key, value);\n  }\n  expected_dictionary_size += kDictionaryCapacity - kPartitionIndex;\n\n  // Now create an iterator on the dictionary\n  SimpleStringDictionary::Iterator iter(dict);\n\n  // We then verify that it iterates through exactly the number of key/value\n  // pairs we expect, and that they match one-for-one with what we would expect.\n  // The ordering of the iteration does not matter...\n\n  // used to keep track of number of occurrences found for key/value pairs\n  int count[kDictionaryCapacity];\n  memset(count, 0, sizeof(count));\n\n  int total_count = 0;\n\n  for (;;) {\n    const SimpleStringDictionary::Entry* entry = iter.Next();\n    if (!entry)\n      break;\n    total_count++;\n\n    // Extract key_number from a string of the form key<key_number>\n    int key_number;\n    sscanf(entry->key, \"key%d\", &key_number);\n\n    // Extract value_number from a string of the form value<value_number>\n    int value_number;\n    sscanf(entry->value, \"value%d\", &value_number);\n\n    // The value number should equal the key number since that's how we set them\n    EXPECT_EQ(value_number, key_number);\n\n    // Key and value numbers should be in proper range: 0 <= key_number <\n    // kDictionaryCapacity\n    bool key_in_good_range =\n        key_number >= 0 && key_number < kDictionaryCapacity;\n    bool value_in_good_range =\n        value_number >= 0 && value_number < kDictionaryCapacity;\n    EXPECT_TRUE(key_in_good_range);\n    EXPECT_TRUE(value_in_good_range);\n\n    if (key_in_good_range && value_in_good_range) {\n      ++count[key_number];\n    }\n  }\n\n  // Make sure each of the key/value pairs showed up exactly one time, except\n  // for the ones which we removed.\n  for (size_t i = 0; i < kDictionaryCapacity; ++i) {\n    // Skip over key7, key18, key23, and key31, since we removed them\n    if (!(i == 7 || i == 18 || i == 23 || i == 31)) {\n      EXPECT_EQ(1, count[i]);\n    }\n  }\n\n  // Make sure the number of iterations matches the expected dictionary size.\n  EXPECT_EQ(total_count, expected_dictionary_size);\n}\n\nTEST(SimpleStringDictionary, AddRemove) {\n  TSimpleStringDictionary<5, 7, 6> map;\n  map.SetKeyValue(\"rob\", \"ert\");\n  map.SetKeyValue(\"mike\", \"pink\");\n  map.SetKeyValue(\"mark\", \"allays\");\n\n  EXPECT_EQ(map.GetCount(), 3u);\n  EXPECT_STREQ(\"ert\", map.GetValueForKey(\"rob\"));\n  EXPECT_STREQ(\"pink\", map.GetValueForKey(\"mike\"));\n  EXPECT_STREQ(\"allays\", map.GetValueForKey(\"mark\"));\n\n  map.RemoveKey(\"mike\");\n\n  EXPECT_EQ(map.GetCount(), 2u);\n  EXPECT_FALSE(map.GetValueForKey(\"mike\"));\n\n  map.SetKeyValue(\"mark\", \"mal\");\n  EXPECT_EQ(map.GetCount(), 2u);\n  EXPECT_STREQ(\"mal\", map.GetValueForKey(\"mark\"));\n\n  map.RemoveKey(\"mark\");\n  EXPECT_EQ(map.GetCount(), 1u);\n  EXPECT_FALSE(map.GetValueForKey(\"mark\"));\n}\n\n// Running out of space shouldn't crash.\nTEST(SimpleStringDictionary, OutOfSpace) {\n  TSimpleStringDictionary<3, 2, 2> map;\n  map.SetKeyValue(\"a\", \"1\");\n  map.SetKeyValue(\"b\", \"2\");\n  map.SetKeyValue(\"c\", \"3\");\n  EXPECT_EQ(map.GetCount(), 2u);\n  EXPECT_FALSE(map.GetValueForKey(\"c\"));\n}\n\n#if DCHECK_IS_ON()\n\nTEST(SimpleStringDictionaryDeathTest, SetKeyValueWithNullKey) {\n  TSimpleStringDictionary<4, 6, 6> map;\n  ASSERT_DEATH_CHECK(map.SetKeyValue(std::string_view(nullptr, 0), \"hello\"),\n                     \"key\");\n}\n\nTEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithNullKey) {\n  TSimpleStringDictionary<4, 6, 6> map;\n  map.SetKeyValue(\"hi\", \"there\");\n  ASSERT_DEATH_CHECK(map.GetValueForKey(std::string_view(nullptr, 0)), \"key\");\n  EXPECT_STREQ(\"there\", map.GetValueForKey(\"hi\"));\n}\n\n#endif\n\n// The tests above, without DEATH_CHECK assertions.\nTEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithoutNullKey) {\n  TSimpleStringDictionary<4, 6, 6> map;\n\n  map.SetKeyValue(\"hi\", \"there\");\n  EXPECT_STREQ(\"there\", map.GetValueForKey(\"hi\"));\n  map.RemoveKey(\"hi\");\n  EXPECT_EQ(map.GetCount(), 0u);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/simulate_crash.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_H_\n#define CRASHPAD_CLIENT_SIMULATE_CRASH_H_\n\n#include \"build/build_config.h\"\n\n// IWYU pragma: begin_exports\n#if BUILDFLAG(IS_MAC)\n#include \"client/simulate_crash_mac.h\"\n#elif BUILDFLAG(IS_IOS)\n#include \"client/simulate_crash_ios.h\"\n#elif BUILDFLAG(IS_WIN)\n#include \"client/simulate_crash_win.h\"\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include \"client/simulate_crash_linux.h\"\n#endif\n// IWYU pragma: end_exports\n\n#endif  // CRASHPAD_CLIENT_SIMULATE_CRASH_H_\n"
  },
  {
    "path": "client/simulate_crash_ios.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_IOS_H_\n#define CRASHPAD_CLIENT_SIMULATE_CRASH_IOS_H_\n\n#include \"client/crashpad_client.h\"\n#include \"util/misc/capture_context.h\"\n\n//! \\file\n\n//! \\brief Captures the CPU context and creates a minidump dump without an\n//!     exception. The minidump will immediately become eligible for further\n//!     processing, including upload.\n//!\n//! \\sa CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING\n#define CRASHPAD_SIMULATE_CRASH()                             \\\n  do {                                                        \\\n    crashpad::NativeCPUContext cpu_context;                   \\\n    crashpad::CaptureContext(&cpu_context);                   \\\n    crashpad::CrashpadClient::DumpWithoutCrash(&cpu_context); \\\n  } while (false)\n\n//! \\brief Captures the CPU context and captures an intermediate dump without an\n//!     exception. Does not convert the intermediate dump into a minidump.\n//!\n//! Deferring processing is useful when the application may be in an unstable\n//! state, such as during a hang.\n//!\n//! \\sa CRASHPAD_SIMULATE_CRASH\n#define CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING()            \\\n  do {                                                            \\\n    crashpad::NativeCPUContext cpu_context;                       \\\n    crashpad::CaptureContext(&cpu_context);                       \\\n    crashpad::CrashpadClient::DumpWithoutCrashAndDeferProcessing( \\\n        &cpu_context);                                            \\\n  } while (false)\n\n#define CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING_AT_PATH(path)      \\\n  do {                                                                  \\\n    crashpad::NativeCPUContext cpu_context;                             \\\n    crashpad::CaptureContext(&cpu_context);                             \\\n    crashpad::CrashpadClient::DumpWithoutCrashAndDeferProcessingAtPath( \\\n        &cpu_context, path);                                            \\\n  } while (false)\n\n#endif  // CRASHPAD_CLIENT_SIMULATE_CRASH_IOS_H_\n"
  },
  {
    "path": "client/simulate_crash_linux.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_\n#define CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_\n\n#include \"client/crashpad_client.h\"\n#include \"util/misc/capture_context.h\"\n\n//! \\file\n\n//! \\brief Captures the CPU context and simulates an exception without crashing.\n#define CRASHPAD_SIMULATE_CRASH()                                            \\\n  do {                                                                       \\\n    crashpad::NativeCPUContext simulate_crash_cpu_context;                   \\\n    crashpad::CaptureContext(&simulate_crash_cpu_context);                   \\\n    crashpad::CrashpadClient::DumpWithoutCrash(&simulate_crash_cpu_context); \\\n  } while (false)\n\n#endif  // CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_\n"
  },
  {
    "path": "client/simulate_crash_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/simulate_crash_mac.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"util/mach/exc_client_variants.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/exception_ports.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n//! \\brief Sends an exception message to an exception port in accordance with\n//!     the behavior and thread state flavor it’s registered to receive.\n//!\n//! \\param[in] thread, task, exception, code, code_count These parameters will\n//!     be passed to the exception handler as appropriate.\n//! \\param[in] cpu_context The value to use for the thread state, if \\a behavior\n//!     indicates that the handler should receive a thread state and if the\n//!     supplied thread state matches or can be converted to \\a flavor. If \\a\n//!     behavior requires a thread state but this argument cannot be converted\n//!     to match \\a flavor, `thread_get_state()` will be called to obtain a\n//!     suitable thread state value.\n//! \\param[in] handler The Mach exception handler to deliver the exception to.\n//! \\param[in] set_state If `true` and \\a behavior indicates that the handler\n//!     should receive and return a thread state, a new thread state will be set\n//!     by `thread_set_state()` upon successful completion of the exception\n//!     handler. If `false`, this will be suppressed, even when \\a behavior\n//!     indicates that the handler receives and returns a thread state.\n//!\n//! \\return `true` if the exception was delivered to the handler and the handler\n//!     indicated success. `false` otherwise, with a warning message logged.\nbool DeliverException(thread_t thread,\n                      task_t task,\n                      exception_type_t exception,\n                      const mach_exception_data_t code,\n                      mach_msg_type_number_t code_count,\n                      const NativeCPUContext& cpu_context,\n                      const ExceptionPorts::ExceptionHandler& handler,\n                      bool set_state) {\n  kern_return_t kr;\n\n  bool handler_wants_state = ExceptionBehaviorHasState(handler.behavior);\n  if (!handler_wants_state) {\n    // Regardless of the passed-in value of |set_state|, if the handler won’t be\n    // dealing with any state at all, no state should be set.\n    set_state = false;\n  }\n\n  // old_state is only used if the context already captured doesn’t match (or\n  // can’t be converted to) what’s registered for the handler.\n  thread_state_data_t old_state;\n\n  thread_state_flavor_t flavor = handler.flavor;\n  ConstThreadState state;\n  mach_msg_type_number_t state_count;\n  switch (flavor) {\n#if defined(ARCH_CPU_X86_FAMILY)\n    case x86_THREAD_STATE:\n      state = reinterpret_cast<ConstThreadState>(&cpu_context);\n      state_count = x86_THREAD_STATE_COUNT;\n      break;\n#if defined(ARCH_CPU_X86)\n    case x86_THREAD_STATE32:\n      state = reinterpret_cast<ConstThreadState>(&cpu_context.uts.ts32);\n      state_count = cpu_context.tsh.count;\n      break;\n#elif defined(ARCH_CPU_X86_64)\n    case x86_THREAD_STATE64:\n      state = reinterpret_cast<ConstThreadState>(&cpu_context.uts.ts64);\n      state_count = cpu_context.tsh.count;\n      break;\n#endif\n#elif defined(ARCH_CPU_ARM64)\n    case ARM_UNIFIED_THREAD_STATE:\n      state = reinterpret_cast<ConstThreadState>(&cpu_context);\n      state_count = ARM_UNIFIED_THREAD_STATE_COUNT;\n      break;\n    case ARM_THREAD_STATE64:\n      state = reinterpret_cast<ConstThreadState>(&cpu_context.ts_64);\n      state_count = cpu_context.ash.count;\n      break;\n#else\n#error Port to your CPU architecture\n#endif\n\n    case THREAD_STATE_NONE:\n      // This is only acceptable if the handler doesn’t have one of the “state”\n      // behaviors. Otherwise, if the kernel were attempting to send an\n      // exception message to this port, it would call thread_getstatus() (known\n      // outside the kernel as thread_get_state()) which would fail because\n      // THREAD_STATE_NONE is not a valid state to get. See 10.9.5\n      // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver() and\n      // xnu-2422.115.4/osfmk/i386/pcb.c machine_thread_get_state().\n      if (!handler_wants_state) {\n        state = nullptr;\n        state_count = 0;\n        break;\n      }\n\n      LOG(WARNING) << \"exception handler has unexpected state flavor\" << flavor;\n      return false;\n\n    default:\n      if (!handler_wants_state) {\n        // Don’t bother getting any thread state if the handler’s not actually\n        // going to use it.\n        state = nullptr;\n        state_count = 0;\n      } else {\n        state = old_state;\n        state_count = THREAD_STATE_MAX;\n        kr = thread_get_state(thread, flavor, old_state, &state_count);\n        if (kr != KERN_SUCCESS) {\n          MACH_LOG(WARNING, kr) << \"thread_get_state\";\n          return false;\n        }\n      }\n      break;\n  }\n\n  // new_state is supposed to be an out parameter only, but in case the handler\n  // doesn't touch it, make sure it's initialized to a valid thread state.\n  // Otherwise, the thread_set_state() call below would set a garbage thread\n  // state.\n  thread_state_data_t new_state;\n  size_t state_size =\n      sizeof(natural_t) *\n      std::min(state_count, implicit_cast<unsigned int>(THREAD_STATE_MAX));\n  memcpy(new_state, state, state_size);\n  mach_msg_type_number_t new_state_count = THREAD_STATE_MAX;\n\n  kr = UniversalExceptionRaise(handler.behavior,\n                               handler.port,\n                               thread,\n                               task,\n                               exception,\n                               code,\n                               code_count,\n                               &flavor,\n                               state,\n                               state_count,\n                               new_state,\n                               &new_state_count);\n\n  // The kernel treats a return value of MACH_RCV_PORT_DIED as successful,\n  // although it will not set a new thread state in that case. See 10.9.5\n  // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver(), and the more\n  // elaborate comment at util/mach/exc_server_variants.h\n  // ExcServerSuccessfulReturnValue(). Duplicate that behavior.\n  bool success = kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED;\n  MACH_LOG_IF(WARNING, !success, kr) << \"UniversalExceptionRaise\";\n\n  if (kr == KERN_SUCCESS && set_state) {\n    kr = thread_set_state(thread, flavor, new_state, new_state_count);\n    MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << \"thread_set_state\";\n  }\n\n  return success;\n}\n\n}  // namespace\n\nvoid SimulateCrash(const NativeCPUContext& cpu_context) {\n#if defined(ARCH_CPU_X86)\n  DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),\n            implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));\n  DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),\n            x86_THREAD_STATE32_COUNT);\n#elif defined(ARCH_CPU_X86_64)\n  DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.tsh.flavor),\n            implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));\n  DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.tsh.count),\n            x86_THREAD_STATE64_COUNT);\n#elif defined(ARCH_CPU_ARM64)\n  DCHECK_EQ(implicit_cast<thread_state_flavor_t>(cpu_context.ash.flavor),\n            implicit_cast<thread_state_flavor_t>(ARM_THREAD_STATE64));\n  DCHECK_EQ(implicit_cast<mach_msg_type_number_t>(cpu_context.ash.count),\n            ARM_THREAD_STATE64_COUNT);\n#else\n#error Port to your CPU architecture\n#endif\n\n  base::apple::ScopedMachSendRight thread(mach_thread_self());\n  exception_type_t exception = kMachExceptionSimulated;\n  mach_exception_data_type_t codes[] = {0, 0};\n  mach_msg_type_number_t code_count = std::size(codes);\n\n  // Look up the handler for EXC_CRASH exceptions in the same way that the\n  // kernel would: try a thread handler, then a task handler, and finally a host\n  // handler. 10.9.5 xnu-2422.115.4/osfmk/kern/exception.c exception_triage().\n  static constexpr ExceptionPorts::TargetType kTargetTypes[] = {\n      ExceptionPorts::kTargetTypeThread,\n      ExceptionPorts::kTargetTypeTask,\n\n      // This is not expected to succeed, because mach_host_self() doesn’t\n      // return the host_priv port to non-root users, and this is the port\n      // that’s required for host_get_exception_ports().\n      //\n      // See 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c set_security_token(),\n      // xnu-2422.115.4/osfmk/kern/task.c host_security_set_task_token(), and\n      // xnu-2422.115.4/osfmk/kern/ipc_host.c host_get_exception_ports().\n      ExceptionPorts::kTargetTypeHost,\n  };\n\n  bool success = false;\n\n  for (size_t target_type_index = 0;\n       !success && target_type_index < std::size(kTargetTypes);\n       ++target_type_index) {\n    ExceptionPorts::ExceptionHandlerVector handlers;\n    ExceptionPorts exception_ports(kTargetTypes[target_type_index],\n                                   MACH_PORT_NULL);\n    if (exception_ports.GetExceptionPorts(EXC_MASK_CRASH, &handlers)) {\n      DCHECK_LE(handlers.size(), 1u);\n      if (handlers.size() == 1) {\n        DCHECK(handlers[0].mask & EXC_MASK_CRASH);\n        success = DeliverException(thread.get(),\n                                   mach_task_self(),\n                                   exception,\n                                   codes,\n                                   code_count,\n                                   cpu_context,\n                                   handlers[0],\n                                   false);\n      }\n    }\n  }\n\n  LOG_IF(WARNING, !success)\n      << \"SimulateCrash did not find an appropriate exception handler\";\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "client/simulate_crash_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_\n#define CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_\n\n#include <mach/mach.h>\n\n#include \"util/misc/capture_context.h\"\n\n//! \\file\n\nnamespace crashpad {\n\n//! \\brief Simulates a exception without crashing.\n//!\n//! This function searches for an `EXC_CRASH` handler in the same manner that\n//! the kernel does, and sends it an exception message to that handler in the\n//! format that the handler expects, considering the behavior and thread state\n//! flavor that are registered for it. The exception sent to the handler will be\n//! ::kMachExceptionSimulated, not `EXC_CRASH`.\n//!\n//! Typically, the CRASHPAD_SIMULATE_CRASH() macro will be used in preference to\n//! this function, because it combines the context-capture operation with the\n//! raising of a simulated exception.\n//!\n//! This function returns normally after the exception message is processed. If\n//! no valid handler was found, or no handler processed the exception\n//! successfully, a warning will be logged, but these conditions are not\n//! considered fatal.\n//!\n//! \\param[in] cpu_context The thread state to pass to the exception handler as\n//!     the exception context, provided that it is compatible with the thread\n//!     state flavor that the exception handler accepts. If it is not\n//!     compatible, the correct thread state for the handler will be obtained by\n//!     calling `thread_get_state()`.\nvoid SimulateCrash(const NativeCPUContext& cpu_context);\n\n}  // namespace crashpad\n\n//! \\brief Captures the CPU context and simulates an exception without crashing.\n#define CRASHPAD_SIMULATE_CRASH()           \\\n  do {                                      \\\n    crashpad::NativeCPUContext cpu_context; \\\n    crashpad::CaptureContext(&cpu_context); \\\n    crashpad::SimulateCrash(cpu_context);   \\\n  } while (false)\n\n#endif  // CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_\n"
  },
  {
    "path": "client/simulate_crash_mac_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/simulate_crash.h\"\n\n#include <mach/mach.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"test/mac/mach_multiprocess.h\"\n#include \"util/mach/exc_server_variants.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/exception_ports.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n#include \"util/mach/symbolic_constants_mach.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass TestSimulateCrashMac final : public MachMultiprocess,\n                                   public UniversalMachExcServer::Interface {\n public:\n  // Defines which targets the child should set an EXC_CRASH exception handler\n  // for.\n  enum ExceptionPortsTarget {\n    // The child should clear its EXC_CRASH handler for both its task and thread\n    // targets. SimulateCrash() will attempt to deliver the exception to the\n    // host target, which will fail if not running as root. In any case, the\n    // parent should not expect to receive any exception message from the child.\n    kExceptionPortsTargetNone = 0,\n\n    // The child will set an EXC_CRASH handler for its task target, and clear it\n    // for its thread target. The parent runs an exception server to receive\n    // the child’s simulated crash message.\n    kExceptionPortsTargetTask,\n\n    // The child will set an EXC_CRASH handler for its thread target, and clear\n    // it for its task target. The parent runs an exception server to receive\n    // the child’s simulated crash message.\n    kExceptionPortsTargetThread,\n\n    // The child sets an EXC_CRASH handler for both its task and thread targets.\n    // The parent runs an exception server to receive the message expected to be\n    // delivered to the thread target, but returns an error code. The child will\n    // then fall back to trying the server registered for the task target,\n    // sending a second message to the parent. The server in the parent will\n    // handle this one successfully.\n    kExceptionPortsTargetBoth,\n  };\n\n  TestSimulateCrashMac(ExceptionPortsTarget target,\n                       exception_behavior_t behavior,\n                       thread_state_flavor_t flavor)\n      : MachMultiprocess(),\n        UniversalMachExcServer::Interface(),\n        target_(target),\n        behavior_(behavior),\n        flavor_(flavor),\n        succeed_(true) {\n  }\n\n  TestSimulateCrashMac(const TestSimulateCrashMac&) = delete;\n  TestSimulateCrashMac& operator=(const TestSimulateCrashMac&) = delete;\n\n  ~TestSimulateCrashMac() {}\n\n  // UniversalMachExcServer::Interface:\n  kern_return_t CatchMachException(exception_behavior_t behavior,\n                                   exception_handler_t exception_port,\n                                   thread_t thread,\n                                   task_t task,\n                                   exception_type_t exception,\n                                   const mach_exception_data_type_t* code,\n                                   mach_msg_type_number_t code_count,\n                                   thread_state_flavor_t* flavor,\n                                   ConstThreadState old_state,\n                                   mach_msg_type_number_t old_state_count,\n                                   thread_state_t new_state,\n                                   mach_msg_type_number_t* new_state_count,\n                                   const mach_msg_trailer_t* trailer,\n                                   bool* destroy_complex_request) override {\n    *destroy_complex_request = true;\n\n    // Check the entire exception message, because most or all of it was\n    // generated by SimulateCrash() instead of the kernel.\n\n    EXPECT_EQ(behavior, behavior_);\n    EXPECT_EQ(exception_port, LocalPort());\n    if (ExceptionBehaviorHasIdentity(behavior)) {\n      EXPECT_NE(thread, THREAD_NULL);\n      EXPECT_EQ(task, ChildTask());\n    } else {\n      EXPECT_EQ(thread, THREAD_NULL);\n      EXPECT_EQ(task, TASK_NULL);\n    }\n    EXPECT_EQ(exception, kMachExceptionSimulated);\n    EXPECT_EQ(code_count, 2u);\n    if (code_count >= 1) {\n      EXPECT_EQ(code[0], 0);\n    }\n    if (code_count >= 2) {\n      EXPECT_EQ(code[1], 0);\n    }\n    if (!ExceptionBehaviorHasState(behavior)) {\n      EXPECT_EQ(*flavor, THREAD_STATE_NONE);\n    } else {\n      EXPECT_EQ(*flavor, flavor_);\n      switch (*flavor) {\n#if defined(ARCH_CPU_X86_FAMILY)\n        case x86_THREAD_STATE: {\n          EXPECT_EQ(old_state_count, x86_THREAD_STATE_COUNT);\n          const x86_thread_state* state =\n              reinterpret_cast<const x86_thread_state*>(old_state);\n          switch (state->tsh.flavor) {\n            case x86_THREAD_STATE32:\n              EXPECT_EQ(implicit_cast<uint32_t>(state->tsh.count),\n                        implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT));\n              break;\n            case x86_THREAD_STATE64:\n              EXPECT_EQ(implicit_cast<uint32_t>(state->tsh.count),\n                        implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT));\n              break;\n            default:\n              ADD_FAILURE() << \"unexpected tsh.flavor \" << state->tsh.flavor;\n              break;\n          }\n          break;\n        }\n        case x86_FLOAT_STATE: {\n          EXPECT_EQ(old_state_count, x86_FLOAT_STATE_COUNT);\n          const x86_float_state* state =\n              reinterpret_cast<const x86_float_state*>(old_state);\n          switch (state->fsh.flavor) {\n            case x86_FLOAT_STATE32:\n              EXPECT_EQ(implicit_cast<uint32_t>(state->fsh.count),\n                        implicit_cast<uint32_t>(x86_FLOAT_STATE32_COUNT));\n              break;\n            case x86_FLOAT_STATE64:\n              EXPECT_EQ(implicit_cast<uint32_t>(state->fsh.count),\n                        implicit_cast<uint32_t>(x86_FLOAT_STATE64_COUNT));\n              break;\n            default:\n              ADD_FAILURE() << \"unexpected fsh.flavor \" << state->fsh.flavor;\n              break;\n          }\n          break;\n        }\n        case x86_DEBUG_STATE: {\n          EXPECT_EQ(old_state_count, x86_DEBUG_STATE_COUNT);\n          const x86_debug_state* state =\n              reinterpret_cast<const x86_debug_state*>(old_state);\n          switch (state->dsh.flavor) {\n            case x86_DEBUG_STATE32:\n              EXPECT_EQ(implicit_cast<uint32_t>(state->dsh.count),\n                        implicit_cast<uint32_t>(x86_DEBUG_STATE32_COUNT));\n              break;\n            case x86_DEBUG_STATE64:\n              EXPECT_EQ(implicit_cast<uint32_t>(state->dsh.count),\n                        implicit_cast<uint32_t>(x86_DEBUG_STATE64_COUNT));\n              break;\n            default:\n              ADD_FAILURE() << \"unexpected dsh.flavor \" << state->dsh.flavor;\n              break;\n          }\n          break;\n        }\n        case x86_THREAD_STATE32:\n          EXPECT_EQ(old_state_count, x86_THREAD_STATE32_COUNT);\n          break;\n        case x86_FLOAT_STATE32:\n          EXPECT_EQ(old_state_count, x86_FLOAT_STATE32_COUNT);\n          break;\n        case x86_DEBUG_STATE32:\n          EXPECT_EQ(old_state_count, x86_DEBUG_STATE32_COUNT);\n          break;\n        case x86_THREAD_STATE64:\n          EXPECT_EQ(old_state_count, x86_THREAD_STATE64_COUNT);\n          break;\n        case x86_FLOAT_STATE64:\n          EXPECT_EQ(old_state_count, x86_FLOAT_STATE64_COUNT);\n          break;\n        case x86_DEBUG_STATE64:\n          EXPECT_EQ(old_state_count, x86_DEBUG_STATE64_COUNT);\n          break;\n#elif defined(ARCH_CPU_ARM64)\n        case ARM_UNIFIED_THREAD_STATE: {\n          EXPECT_EQ(old_state_count, ARM_UNIFIED_THREAD_STATE_COUNT);\n          const arm_unified_thread_state* state =\n              reinterpret_cast<const arm_unified_thread_state*>(old_state);\n          EXPECT_EQ(state->ash.flavor,\n                    implicit_cast<uint32_t>(ARM_THREAD_STATE64));\n          if (state->ash.flavor == ARM_THREAD_STATE64) {\n            EXPECT_EQ(state->ash.count,\n                      implicit_cast<uint32_t>(ARM_THREAD_STATE64_COUNT));\n          }\n          break;\n        }\n        case ARM_THREAD_STATE64:\n          EXPECT_EQ(old_state_count, ARM_THREAD_STATE64_COUNT);\n          break;\n        case ARM_NEON_STATE64:\n          EXPECT_EQ(old_state_count, ARM_NEON_STATE64_COUNT);\n          break;\n        case ARM_DEBUG_STATE64:\n          EXPECT_EQ(old_state_count, ARM_DEBUG_STATE64_COUNT);\n          break;\n#else\n#error Port to your CPU architecture\n#endif\n        default:\n          ADD_FAILURE() << \"unexpected flavor \" << *flavor;\n          break;\n      }\n\n      // Attempt to set a garbage thread state, which would cause the child to\n      // crash inside SimulateCrash() if it actually succeeded. This tests that\n      // SimulateCrash() ignores new_state instead of attempting to set the\n      // state as the kernel would do. This operates in conjunction with the\n      // |true| argument to ExcServerSuccessfulReturnValue() below.\n      *new_state_count = old_state_count;\n      size_t new_state_size = sizeof(natural_t) * old_state_count;\n      memset(new_state, 0xa5, new_state_size);\n    }\n\n    if (!succeed_) {\n      // The client has registered EXC_CRASH handlers for both its thread and\n      // task targets, and sent a simulated exception message to its\n      // thread-level EXC_CRASH handler. To test that it will fall back to\n      // trying the task-level EXC_CRASH handler, return a failure code, which\n      // should cause SimulateCrash() to try the next target.\n      EXPECT_EQ(target_, kExceptionPortsTargetBoth);\n      return KERN_ABORTED;\n    }\n\n    ExcServerCopyState(\n        behavior, old_state, old_state_count, new_state, new_state_count);\n\n    return ExcServerSuccessfulReturnValue(exception, behavior, true);\n  }\n\n private:\n  // MachMultiprocess:\n\n  void MachMultiprocessParent() override {\n    if (target_ == kExceptionPortsTargetNone) {\n      // The child does not have any EXC_CRASH handlers registered for its\n      // thread or task targets, so no exception message is expected to be\n      // generated. Don’t run the server at all.\n      return;\n    }\n\n    UniversalMachExcServer universal_mach_exc_server(this);\n\n    mach_msg_return_t mr;\n    if (target_ == kExceptionPortsTargetBoth) {\n      // The client has registered EXC_CRASH handlers for both its thread and\n      // task targets. Run a server that will return a failure code when the\n      // exception message is sent to the thread target, which will cause the\n      // client to fall back to the task target and send another message.\n      succeed_ = false;\n      mr = MachMessageServer::Run(&universal_mach_exc_server,\n                                  LocalPort(),\n                                  MACH_MSG_OPTION_NONE,\n                                  MachMessageServer::kOneShot,\n                                  MachMessageServer::kReceiveLargeError,\n                                  kMachMessageTimeoutWaitIndefinitely);\n      EXPECT_EQ(mr, MACH_MSG_SUCCESS)\n          << MachErrorMessage(mr, \"MachMessageServer::Run\");\n    }\n\n    succeed_ = true;\n    mr = MachMessageServer::Run(&universal_mach_exc_server,\n                                LocalPort(),\n                                MACH_MSG_OPTION_NONE,\n                                MachMessageServer::kOneShot,\n                                MachMessageServer::kReceiveLargeError,\n                                kMachMessageTimeoutWaitIndefinitely);\n    EXPECT_EQ(mr, MACH_MSG_SUCCESS)\n        << MachErrorMessage(mr, \"MachMessageServer::Run\");\n  }\n\n  void MachMultiprocessChild() override {\n    bool task_valid = target_ == kExceptionPortsTargetTask ||\n                      target_ == kExceptionPortsTargetBoth;\n    ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask,\n                                        TASK_NULL);\n    ASSERT_TRUE(task_exception_ports.SetExceptionPort(\n        EXC_MASK_CRASH,\n        task_valid ? RemotePort() : MACH_PORT_NULL,\n        behavior_,\n        flavor_));\n\n    bool thread_valid = target_ == kExceptionPortsTargetThread ||\n                        target_ == kExceptionPortsTargetBoth;\n    ExceptionPorts thread_exception_ports(ExceptionPorts::kTargetTypeThread,\n                                          THREAD_NULL);\n    ASSERT_TRUE(thread_exception_ports.SetExceptionPort(\n        EXC_MASK_CRASH,\n        thread_valid ? RemotePort() : MACH_PORT_NULL,\n        behavior_,\n        flavor_));\n\n    CRASHPAD_SIMULATE_CRASH();\n  }\n\n  ExceptionPortsTarget target_;\n  exception_behavior_t behavior_;\n  thread_state_flavor_t flavor_;\n  bool succeed_;\n};\n\nTEST(SimulateCrash, SimulateCrash) {\n  static constexpr TestSimulateCrashMac::ExceptionPortsTarget kTargets[] = {\n      TestSimulateCrashMac::kExceptionPortsTargetNone,\n      TestSimulateCrashMac::kExceptionPortsTargetTask,\n      TestSimulateCrashMac::kExceptionPortsTargetThread,\n      TestSimulateCrashMac::kExceptionPortsTargetBoth,\n  };\n\n  static constexpr exception_behavior_t kBehaviors[] = {\n      EXCEPTION_DEFAULT,\n      EXCEPTION_STATE,\n      EXCEPTION_STATE_IDENTITY,\n      EXCEPTION_DEFAULT | kMachExceptionCodes,\n      EXCEPTION_STATE | kMachExceptionCodes,\n      EXCEPTION_STATE_IDENTITY | kMachExceptionCodes,\n  };\n\n  static constexpr thread_state_flavor_t kFlavors[] = {\n#if defined(ARCH_CPU_X86_FAMILY)\n    x86_THREAD_STATE,\n    x86_FLOAT_STATE,\n    x86_DEBUG_STATE,\n#if defined(ARCH_CPU_X86)\n    x86_THREAD_STATE32,\n    x86_FLOAT_STATE32,\n    x86_DEBUG_STATE32,\n#elif defined(ARCH_CPU_X86_64)\n    x86_THREAD_STATE64,\n    x86_FLOAT_STATE64,\n    x86_DEBUG_STATE64,\n#endif\n#elif defined(ARCH_CPU_ARM64)\n    ARM_UNIFIED_THREAD_STATE,\n    ARM_THREAD_STATE64,\n    ARM_NEON_STATE64,\n    ARM_DEBUG_STATE64,\n#else\n#error Port to your CPU architecture\n#endif\n  };\n\n  for (size_t target_index = 0; target_index < std::size(kTargets);\n       ++target_index) {\n    TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"target_index %zu, target %d\", target_index, target));\n\n    for (size_t behavior_index = 0; behavior_index < std::size(kBehaviors);\n         ++behavior_index) {\n      exception_behavior_t behavior = kBehaviors[behavior_index];\n      SCOPED_TRACE(base::StringPrintf(\n          \"behavior_index %zu, behavior %s\",\n          behavior_index,\n          ExceptionBehaviorToString(behavior, kUseFullName | kUnknownIsNumeric)\n              .c_str()));\n\n      if (!ExceptionBehaviorHasState(behavior)) {\n        TestSimulateCrashMac test_simulate_crash_mac(\n            target, behavior, THREAD_STATE_NONE);\n        test_simulate_crash_mac.Run();\n      } else {\n        for (size_t flavor_index = 0; flavor_index < std::size(kFlavors);\n             ++flavor_index) {\n          thread_state_flavor_t flavor = kFlavors[flavor_index];\n          SCOPED_TRACE(base::StringPrintf(\n              \"flavor_index %zu, flavor %s\",\n              flavor_index,\n              ThreadStateFlavorToString(\n                  flavor, kUseFullName | kUnknownIsNumeric).c_str()));\n\n          TestSimulateCrashMac test_simulate_crash_mac(\n              target, behavior, flavor);\n          test_simulate_crash_mac.Run();\n        }\n      }\n    }\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "client/simulate_crash_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_\n#define CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_\n\n#include <windows.h>\n\n#include \"client/crashpad_client.h\"\n#include \"util/misc/capture_context.h\"\n\n//! \\file\n\n//! \\brief Captures the CPU context and captures a dump without an exception.\n#define CRASHPAD_SIMULATE_CRASH()                                           \\\n  do {                                                                      \\\n    /* Not \"context\" to avoid variable shadowing warnings. */               \\\n    CONTEXT simulate_crash_cpu_context;                                     \\\n    crashpad::CaptureContext(&simulate_crash_cpu_context);                  \\\n    crashpad::CrashpadClient::DumpWithoutCrash(simulate_crash_cpu_context); \\\n  } while (false)\n\n#endif  // CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_\n"
  },
  {
    "path": "client/upload_behavior_ios.h",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_CLIENT_UPLOAD_BEHAVIOR_IOS_H_\n#define CRASHPAD_CLIENT_UPLOAD_BEHAVIOR_IOS_H_\n\nnamespace crashpad {\n\n//! \\brief Enum to control upload behavior when processing pending reports.\nenum class UploadBehavior {\n  //! \\brief Only upload reports while the application is active (e.g., in the\n  //!     foreground).\n  kUploadWhenAppIsActive = 1,\n\n  //! \\brief Upload reports immediately, regardless of whether or not the\n  //!     application is active.\n  kUploadImmediately = 2,\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_CLIENT_UPLOAD_BEHAVIOR_IOS_H_\n"
  },
  {
    "path": "codereview.settings",
    "content": "# Copyright 2014 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nGERRIT_HOST: True\nCODE_REVIEW_SERVER: https://chromium-review.googlesource.com/\nVIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/\nPROJECT: crashpad\n"
  },
  {
    "path": "compat/BUILD.gn",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../build/crashpad_buildconfig.gni\")\n\nconfig(\"compat_config\") {\n  include_dirs = []\n\n  if (crashpad_is_apple) {\n    include_dirs += [ \"mac\" ]\n  }\n\n  if (crashpad_is_ios) {\n    include_dirs += [ \"ios\" ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    include_dirs += [ \"linux\" ]\n  }\n\n  if (crashpad_is_android) {\n    include_dirs += [ \"android\" ]\n  }\n\n  if (crashpad_is_win) {\n    include_dirs += [ \"win\" ]\n  } else {\n    include_dirs += [ \"non_win\" ]\n  }\n}\n\ntemplate(\"compat_target\") {\n  if (crashpad_is_apple) {\n    # There are no sources to compile, which doesn’t mix will with a\n    # static_library.\n    group(target_name) {\n      forward_variables_from(invoker, \"*\")\n      not_needed([ \"configs\" ])\n    }\n  } else {\n    crashpad_static_library(target_name) {\n      forward_variables_from(invoker, \"*\", [ \"configs\" ])\n      if (!defined(configs)) {\n        configs = []\n      }\n      if (defined(invoker.configs)) {\n        configs += invoker.configs\n      }\n    }\n  }\n}\n\ncompat_target(\"compat\") {\n  sources = []\n\n  if (crashpad_is_apple) {\n    sources += [\n      \"mac/Availability.h\",\n      \"mac/AvailabilityVersions.h\",\n      \"mac/kern/exc_resource.h\",\n      \"mac/mach-o/loader.h\",\n      \"mac/mach/i386/thread_state.h\",\n      \"mac/mach/mach.h\",\n      \"mac/sys/resource.h\",\n    ]\n  } else {\n    sources += [ \"non_mac/mach/mach.h\" ]\n  }\n\n  if (crashpad_is_ios) {\n    sources += [\n      \"ios/mach/exc.defs\",\n      \"ios/mach/mach_exc.defs\",\n      \"ios/mach/mach_types.defs\",\n      \"ios/mach/machine/machine_types.defs\",\n      \"ios/mach/std_types.defs\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    sources += [\n      \"linux/signal.h\",\n      \"linux/sys/mman.h\",\n      \"linux/sys/mman_memfd_create.cc\",\n      \"linux/sys/ptrace.h\",\n      \"linux/sys/user.h\",\n    ]\n  }\n\n  if (crashpad_is_android) {\n    sources += [\n      \"android/dlfcn_internal.cc\",\n      \"android/dlfcn_internal.h\",\n      \"android/elf.h\",\n      \"android/linux/elf.h\",\n      \"android/linux/prctl.h\",\n      \"android/linux/ptrace.h\",\n      \"android/sched.h\",\n      \"android/sys/epoll.cc\",\n      \"android/sys/epoll.h\",\n      \"android/sys/mman.h\",\n      \"android/sys/mman_mmap.cc\",\n      \"android/sys/syscall.h\",\n      \"android/sys/user.h\",\n    ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [\n      \"win/getopt.h\",\n      \"win/strings.cc\",\n      \"win/strings.h\",\n      \"win/sys/types.h\",\n      \"win/time.cc\",\n      \"win/time.h\",\n      \"win/winbase.h\",\n      \"win/winnt.h\",\n      \"win/winternl.h\",\n    ]\n  } else {\n    sources += [\n      \"non_win/dbghelp.h\",\n      \"non_win/minwinbase.h\",\n      \"non_win/timezoneapi.h\",\n      \"non_win/verrsrc.h\",\n      \"non_win/windows.h\",\n      \"non_win/winnt.h\",\n    ]\n  }\n\n  public_configs = [\n    \":compat_config\",\n    \"..:crashpad_config\",\n  ]\n\n  if (!crashpad_is_win) {\n    public_deps = [ \"$mini_chromium_source_parent:base\" ]\n  }\n\n  deps = [ \"../util:no_cfi_icall\" ]\n\n  if (crashpad_is_win) {\n    deps += [ \"../third_party/getopt\" ]\n  }\n}\n"
  },
  {
    "path": "compat/android/dlfcn_internal.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"dlfcn_internal.h\"\n\n#include <android/api-level.h>\n#include <dlfcn.h>\n#include <errno.h>\n#include <setjmp.h>\n#include <signal.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/syscall.h>\n#include <sys/system_properties.h>\n#include <unistd.h>\n\n#include <mutex>\n\nnamespace crashpad {\nnamespace internal {\n\n// KitKat supports API levels up to 20.\n#if __ANDROID_API__ < 21\n\nnamespace {\n\nclass ScopedSigactionRestore {\n public:\n  ScopedSigactionRestore() : old_action_(), signo_(-1), valid_(false) {}\n\n  ~ScopedSigactionRestore() { Reset(); }\n\n  bool Reset() {\n    bool result = true;\n    if (valid_) {\n      result = sigaction(signo_, &old_action_, nullptr) == 0;\n      if (!result) {\n        PrintErrmsg(errno);\n      }\n    }\n    valid_ = false;\n    signo_ = -1;\n    return result;\n  }\n\n  bool ResetAndInstallHandler(int signo,\n                              void (*handler)(int, siginfo_t*, void*)) {\n    Reset();\n\n    struct sigaction act;\n    act.sa_sigaction = handler;\n    sigemptyset(&act.sa_mask);\n    act.sa_flags = SA_SIGINFO;\n    if (sigaction(signo, &act, &old_action_) != 0) {\n      PrintErrmsg(errno);\n      return false;\n    }\n    signo_ = signo;\n    valid_ = true;\n    return true;\n  }\n\n private:\n  void PrintErrmsg(int err) {\n    char errmsg[256];\n\n    if (strerror_r(err, errmsg, sizeof(errmsg)) != 0) {\n      snprintf(errmsg,\n               sizeof(errmsg),\n               \"%s:%d: Couldn't set errmsg for %d: %d\",\n               __FILE__,\n               __LINE__,\n               err,\n               errno);\n      return;\n    }\n\n    fprintf(stderr, \"%s:%d: sigaction: %s\", __FILE__, __LINE__, errmsg);\n  }\n\n  struct sigaction old_action_;\n  int signo_;\n  bool valid_;\n};\n\nbool IsKitKat() {\n  char prop_buf[PROP_VALUE_MAX];\n  int length = __system_property_get(\"ro.build.version.sdk\", prop_buf);\n  if (length <= 0) {\n    fprintf(stderr, \"%s:%d: Couldn't get version\", __FILE__, __LINE__);\n    // It's safer to assume this is KitKat and execute dlsym with a signal\n    // handler installed.\n    return true;\n  }\n  if (strcmp(prop_buf, \"19\") == 0 || strcmp(prop_buf, \"20\") == 0) {\n    return true;\n  }\n  return false;\n}\n\nclass ScopedSetTID {\n public:\n  explicit ScopedSetTID(pid_t* tid) : tid_(tid) { *tid_ = syscall(SYS_gettid); }\n\n  ~ScopedSetTID() { *tid_ = -1; }\n\n private:\n  pid_t* tid_;\n};\n\nsigjmp_buf dlsym_sigjmp_env;\n\npid_t dlsym_tid = -1;\n\nvoid HandleSIGFPE(int signo, siginfo_t* siginfo, void* context) {\n  if (siginfo->si_code != FPE_INTDIV || syscall(SYS_gettid) != dlsym_tid) {\n    return;\n  }\n  siglongjmp(dlsym_sigjmp_env, 1);\n}\n\n}  // namespace\n\nvoid* Dlsym(void* handle, const char* symbol) {\n  if (!IsKitKat()) {\n    return dlsym(handle, symbol);\n  }\n\n  static std::mutex* signal_handler_mutex = new std::mutex();\n  std::lock_guard<std::mutex> lock(*signal_handler_mutex);\n\n  ScopedSetTID set_tid(&dlsym_tid);\n\n  ScopedSigactionRestore sig_restore;\n  if (!sig_restore.ResetAndInstallHandler(SIGFPE, HandleSIGFPE)) {\n    return nullptr;\n  }\n\n  if (sigsetjmp(dlsym_sigjmp_env, 1) != 0) {\n    return nullptr;\n  }\n\n  return dlsym(handle, symbol);\n}\n\n#else\n\nvoid* Dlsym(void* handle, const char* symbol) {\n  return dlsym(handle, symbol);\n}\n\n#endif  // __ANDROID_API__ < 21\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "compat/android/dlfcn_internal.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_\n#define CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Provide a wrapper for `dlsym`.\n//!\n//! dlsym on Android KitKat (4.4.*) raises SIGFPE when searching for a\n//! non-existent symbol. This wrapper avoids crashing in this circumstance.\n//! https://code.google.com/p/android/issues/detail?id=61799\n//!\n//! The parameters and return value for this function are the same as for\n//! `dlsym`, but a return value for `dlerror` may not be set in the event of an\n//! error.\nvoid* Dlsym(void* handle, const char* symbol);\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_\n"
  },
  {
    "path": "compat/android/elf.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_ELF_H_\n#define CRASHPAD_COMPAT_ANDROID_ELF_H_\n\n#include_next <elf.h>\n\n#include <android/api-level.h>\n\n#if !defined(ELF32_ST_VISIBILITY)\n#define ELF32_ST_VISIBILITY(other) ((other) & 0x3)\n#endif\n\n#if !defined(ELF64_ST_VISIBILITY)\n#define ELF64_ST_VISIBILITY(other) ELF32_ST_VISIBILITY(other)\n#endif\n\n// Android 5.0.0 (API 21) NDK\n\n#if !defined(STT_COMMON)\n#define STT_COMMON 5\n#endif\n\n#if !defined(STT_TLS)\n#define STT_TLS 6\n#endif\n\n// ELF note header types are normally provided by <linux/elf.h>. While unified\n// headers include <linux/elf.h> in <elf.h>, traditional headers do not, prior\n// to API 21. <elf.h> and <linux/elf.h> can't both be included in the same\n// translation unit due to collisions, so we provide these types here.\n#if __ANDROID_API__ < 21 && !defined(__ANDROID_API_N__)\ntypedef struct {\n  Elf32_Word n_namesz;\n  Elf32_Word n_descsz;\n  Elf32_Word n_type;\n} Elf32_Nhdr;\n\ntypedef struct {\n  Elf64_Word n_namesz;\n  Elf64_Word n_descsz;\n  Elf64_Word n_type;\n} Elf64_Nhdr;\n#endif  // __ANDROID_API__ < 21 && !defined(NT_PRSTATUS)\n\n#endif  // CRASHPAD_COMPAT_ANDROID_ELF_H_\n"
  },
  {
    "path": "compat/android/linux/elf.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_\n#define CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_\n\n#include_next <linux/elf.h>\n\n// Android 5.0.0 (API 21) NDK\n\n#if defined(__i386__) || defined(__x86_64__)\n#if !defined(NT_386_TLS)\n#define NT_386_TLS 0x200\n#endif\n#endif  // __i386__ || __x86_64__\n\n#if defined(__ARMEL__) || defined(__aarch64__)\n#if !defined(NT_ARM_VFP)\n#define NT_ARM_VFP 0x400\n#endif\n\n#if !defined(NT_ARM_TLS)\n#define NT_ARM_TLS 0x401\n#endif\n#endif  // __ARMEL__ || __aarch64__\n\n#endif  // CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_\n"
  },
  {
    "path": "compat/android/linux/prctl.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_\n#define CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_\n\n#include_next <linux/prctl.h>\n\n// Android 5.0.0 (API 21) NDK\n#if !defined(PR_SET_PTRACER)\n#define PR_SET_PTRACER 0x59616d61\n#endif\n\n#endif  // CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_\n"
  },
  {
    "path": "compat/android/linux/ptrace.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_LINUX_PTRACE_H_\n#define CRASHPAD_COMPAT_ANDROID_LINUX_PTRACE_H_\n\n#include_next <linux/ptrace.h>\n\n// Android 5.0.0 (API 21) NDK\n#if !defined(PTRACE_GETREGSET)\n#define PTRACE_GETREGSET 0x4204\n#endif\n\n#endif  // CRASHPAD_COMPAT_ANDROID_LINUX_PTRACE_H_\n"
  },
  {
    "path": "compat/android/sched.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_SCHED_H_\n#define CRASHPAD_COMPAT_ANDROID_SCHED_H_\n\n#include_next <sched.h>\n\n// Android 5.0.0 (API 21) NDK\n\n#if !defined(SCHED_BATCH)\n#define SCHED_BATCH 3\n#endif\n\n#if !defined(SCHED_IDLE)\n#define SCHED_IDLE 5\n#endif\n\n#endif  // CRASHPAD_COMPAT_ANDROID_SCHED_H_\n"
  },
  {
    "path": "compat/android/sys/epoll.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <sys/epoll.h>\n\n#include <dlfcn.h>\n#include <sys/syscall.h>\n#include <unistd.h>\n\n#include \"dlfcn_internal.h\"\n#include \"util/misc/no_cfi_icall.h\"\n\n#if __ANDROID_API__ < 21\n\nextern \"C\" {\n\nint epoll_create1(int flags) {\n  static const crashpad::NoCfiIcall<decltype(epoll_create1)*> epoll_create1_p(\n      crashpad::internal::Dlsym(RTLD_DEFAULT, \"epoll_create1\"));\n  return epoll_create1_p ? epoll_create1_p(flags)\n                         : syscall(SYS_epoll_create1, flags);\n}\n\n}  // extern \"C\"\n\n#endif  // __ANDROID_API__ < 21\n"
  },
  {
    "path": "compat/android/sys/epoll.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_\n#define CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_\n\n#include_next <sys/epoll.h>\n\n#include <android/api-level.h>\n#include <fcntl.h>\n\n// This is missing from traditional headers before API 21.\n#if !defined(EPOLLRDHUP)\n#define EPOLLRDHUP 0x00002000\n#endif\n\n// EPOLL_CLOEXEC is undefined in traditional headers before API 21 and removed\n// from unified headers at API levels < 21 as a means to indicate that\n// epoll_create1 is missing from the C library, but the raw system call should\n// still be available.\n#if !defined(EPOLL_CLOEXEC)\n#define EPOLL_CLOEXEC O_CLOEXEC\n#endif\n\n#if __ANDROID_API__ < 21\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint epoll_create1(int flags);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // __ANDROID_API__ < 21\n\n#endif  // CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_\n"
  },
  {
    "path": "compat/android/sys/mman.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_\n#define CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_\n\n#include_next <sys/mman.h>\n\n#include <android/api-level.h>\n#include <sys/cdefs.h>\n\n// There’s no mmap() wrapper compatible with a 64-bit off_t for 32-bit code\n// until API 21 (Android 5.0/“Lollipop”). A custom mmap() wrapper is provided\n// here. Note that this scenario is only possible with NDK unified headers.\n//\n// https://android.googlesource.com/platform/bionic/+/0bfcbaf4d069e005d6e959d97f8d11c77722b70d/docs/32-bit-abi.md#is-32_bit-1\n\n#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21\n\n#endif  // CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_\n"
  },
  {
    "path": "compat/android/sys/mman_mmap.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <sys/mman.h>\n\n#include <dlfcn.h>\n#include <errno.h>\n#include <stdint.h>\n#include <unistd.h>\n\n#include \"dlfcn_internal.h\"\n#include \"util/misc/no_cfi_icall.h\"\n\n#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21\n\n// Bionic has provided a wrapper for __mmap2() since the beginning of time. See\n// bionic/libc/SYSCALLS.TXT in any Android version.\nextern \"C\" void* __mmap2(void* addr,\n                         size_t size,\n                         int prot,\n                         int flags,\n                         int fd,\n                         size_t pgoff);\n\nnamespace {\n\ntemplate <typename T>\nT Align(T value, size_t alignment) {\n  return (value + alignment - 1) & ~(alignment - 1);\n}\n\n// Adapted from Android 8.0.0 bionic/libc/bionic/mmap.cpp.\nvoid* LocalMmap64(void* addr,\n                  size_t size,\n                  int prot,\n                  int flags,\n                  int fd,\n                  off64_t offset) {\n  constexpr int kMmap2Shift = 12;\n\n  if (offset < 0 || (offset & ((1UL << kMmap2Shift) - 1)) != 0) {\n    errno = EINVAL;\n    return MAP_FAILED;\n  }\n\n  const size_t rounded = Align(size, getpagesize());\n  if (rounded < size || rounded > PTRDIFF_MAX) {\n    errno = ENOMEM;\n    return MAP_FAILED;\n  }\n\n  const bool is_private_anonymous =\n      (flags & (MAP_PRIVATE | MAP_ANONYMOUS)) == (MAP_PRIVATE | MAP_ANONYMOUS);\n  const bool is_stack_or_grows_down =\n      (flags & (MAP_STACK | MAP_GROWSDOWN)) != 0;\n\n  void* const result =\n      __mmap2(addr, size, prot, flags, fd, offset >> kMmap2Shift);\n\n  static bool kernel_has_MADV_MERGEABLE = true;\n  if (result != MAP_FAILED && kernel_has_MADV_MERGEABLE &&\n      is_private_anonymous && !is_stack_or_grows_down) {\n    const int saved_errno = errno;\n    const int rc = madvise(result, size, MADV_MERGEABLE);\n    if (rc == -1 && errno == EINVAL) {\n      kernel_has_MADV_MERGEABLE = false;\n    }\n    errno = saved_errno;\n  }\n\n  return result;\n}\n\n}  // namespace\n\nextern \"C\" {\n\nvoid* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) {\n  // Use the system’s mmap64() wrapper if available. It will be available on\n  // Android 5.0 (“Lollipop”) and later.\n  static const crashpad::NoCfiIcall<decltype(LocalMmap64)*> mmap64(\n      crashpad::internal::Dlsym(RTLD_DEFAULT, \"mmap64\"));\n  if (mmap64) {\n    return mmap64(addr, size, prot, flags, fd, offset);\n  }\n\n  // Otherwise, use the local implementation, which should amount to exactly the\n  // same thing.\n  return LocalMmap64(addr, size, prot, flags, fd, offset);\n}\n\n}  // extern \"C\"\n\n#endif  // defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21\n"
  },
  {
    "path": "compat/android/sys/syscall.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_\n#define CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_\n\n#include_next <sys/syscall.h>\n\n// Android 5.0.0 (API 21) NDK\n\n#if !defined(SYS_epoll_create1)\n#define SYS_epoll_create1 __NR_epoll_create1\n#endif\n\n#if !defined(SYS_gettid)\n#define SYS_gettid __NR_gettid\n#endif\n\n#if !defined(SYS_timer_create)\n#define SYS_timer_create __NR_timer_create\n#endif\n\n#if !defined(SYS_timer_getoverrun)\n#define SYS_timer_getoverrun __NR_timer_getoverrun\n#endif\n\n#if !defined(SYS_timer_settime)\n#define SYS_timer_settime __NR_timer_settime\n#endif\n\n#endif  // CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_\n"
  },
  {
    "path": "compat/android/sys/user.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_ANDROID_SYS_USER_H_\n#define CRASHPAD_COMPAT_ANDROID_SYS_USER_H_\n\n// This is needed for traditional headers.\n#include <sys/types.h>\n\n#include_next <sys/user.h>\n\n#endif  // CRASHPAD_COMPAT_ANDROID_SYS_USER_H_\n"
  },
  {
    "path": "compat/ios/mach/exc.defs",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_\n#define CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_\n\n#include \"third_party/xnu/osfmk/mach/exc.defs\"\n\n#endif  // CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_\n"
  },
  {
    "path": "compat/ios/mach/mach_exc.defs",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_\n#define CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_\n\n#include \"third_party/xnu/osfmk/mach/mach_exc.defs\"\n\n#endif  // CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_\n"
  },
  {
    "path": "compat/ios/mach/mach_types.defs",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_\n#define CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_\n\n#include \"third_party/xnu/osfmk/mach/mach_types.defs\"\n\n#endif  // CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_\n"
  },
  {
    "path": "compat/ios/mach/machine/machine_types.defs",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_\n#define CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_\n\n#include \"third_party/xnu/osfmk/mach/machine/machine_types.defs\"\n\n#endif  // CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_\n"
  },
  {
    "path": "compat/ios/mach/std_types.defs",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_\n#define CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_\n\n#include \"third_party/xnu/osfmk/mach/std_types.defs\"\n\n#endif  // CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_\n"
  },
  {
    "path": "compat/linux/signal.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_LINUX_SIGNAL_H_\n#define CRASHPAD_COMPAT_LINUX_SIGNAL_H_\n\n#include_next <signal.h>\n\n// Missing from glibc and bionic\n#if !defined(SS_AUTODISARM)\n#define SS_AUTODISARM (1u << 31)\n#endif\n\n// Linux Kernel >= 5.11 flag for `sigaction::sa_flags`. Missing in headers from\n// earlier versions of Linux.\n#if !defined(SA_EXPOSE_TAGBITS)\n#define SA_EXPOSE_TAGBITS 0x00000800\n#endif\n\n// Missing from glibc and bionic-x86_64\n\n#if defined(__x86_64__) || defined(__i386__)\n#if !defined(X86_FXSR_MAGIC)\n#define X86_FXSR_MAGIC 0x0000\n#endif\n#endif  // __x86_64__ || __i386__\n\n#if defined(__aarch64__) || defined(__arm__)\n\n#if !defined(FPSIMD_MAGIC)\n#define FPSIMD_MAGIC 0x46508001\n#endif\n\n#if !defined(ESR_MAGIC)\n#define ESR_MAGIC 0x45535201\n#endif\n\n#if !defined(EXTRA_MAGIC)\n#define EXTRA_MAGIC 0x45585401\n#endif\n\n#if !defined(VFP_MAGIC)\n#define VFP_MAGIC 0x56465001\n#endif\n\n#if !defined(CRUNCH_MAGIC)\n#define CRUNCH_MAGIC 0x5065cf03\n#endif\n\n#if !defined(DUMMY_MAGIC)\n#define DUMMY_MAGIC 0xb0d9ed01\n#endif\n\n#if !defined(IWMMXT_MAGIC)\n#define IWMMXT_MAGIC 0x12ef842a\n#endif\n\n#endif  // __aarch64__ || __arm__\n\n#endif  // CRASHPAD_COMPAT_LINUX_SIGNAL_H_\n"
  },
  {
    "path": "compat/linux/sys/mman.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_\n#define CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_\n\n#include_next <sys/mman.h>\n\n#include <features.h>\n\n// There's no memfd_create() wrapper before glibc 2.27.\n// This can't select for glibc < 2.27 because linux-chromeos-rel bots build this\n// code using a sysroot which has glibc 2.27, but then run it on Ubuntu 16.04,\n// which doesn't.\n#if defined(__GLIBC__)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint memfd_create(const char* name, unsigned int flags) __THROW;\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // __GLIBC__\n\n#endif  // CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_\n"
  },
  {
    "path": "compat/linux/sys/mman_memfd_create.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <sys/mman.h>\n\n#include <dlfcn.h>\n#include <sys/syscall.h>\n#include <unistd.h>\n\n#include \"util/misc/no_cfi_icall.h\"\n\n#if defined(__GLIBC__)\n\nextern \"C\" {\n\nint memfd_create(const char* name, unsigned int flags) __THROW {\n  static const crashpad::NoCfiIcall<decltype(memfd_create)*> next_memfd_create(\n      dlsym(RTLD_NEXT, \"memfd_create\"));\n  return next_memfd_create ? next_memfd_create(name, flags)\n                           : syscall(SYS_memfd_create, name, flags);\n}\n\n}  // extern \"C\"\n\n#endif  // __GLIBC__\n"
  },
  {
    "path": "compat/linux/sys/ptrace.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_\n#define CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_\n\n#include_next <sys/ptrace.h>\n\n#include <sys/cdefs.h>\n\n// https://sourceware.org/bugzilla/show_bug.cgi?id=22433\n#if !defined(PTRACE_GET_THREAD_AREA) && !defined(PT_GET_THREAD_AREA) && \\\n    defined(__GLIBC__)\n#if defined(__i386__) || defined(__x86_64__)\nstatic constexpr __ptrace_request PTRACE_GET_THREAD_AREA =\n    static_cast<__ptrace_request>(25);\n#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA\n// https://bugs.chromium.org/p/chromium/issues/detail?id=873168\n#elif defined(__arm__) || (defined(__aarch64__) && __GLIBC_PREREQ(2,28))\nstatic constexpr __ptrace_request PTRACE_GET_THREAD_AREA =\n    static_cast<__ptrace_request>(22);\n#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA\n#elif defined(__mips__)\nstatic constexpr __ptrace_request PTRACE_GET_THREAD_AREA =\n    static_cast<__ptrace_request>(25);\n#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA\nstatic constexpr __ptrace_request PTRACE_GET_THREAD_AREA_3264 =\n    static_cast<__ptrace_request>(0xc4);\n#define PTRACE_GET_THREAD_AREA_3264 PTRACE_GET_THREAD_AREA_3264\n#endif\n#endif  // !PTRACE_GET_THREAD_AREA && !PT_GET_THREAD_AREA && defined(__GLIBC__)\n\n// https://sourceware.org/bugzilla/show_bug.cgi?id=22433\n#if !defined(PTRACE_GETVFPREGS) && !defined(PT_GETVFPREGS) && \\\n    defined(__GLIBC__) && (defined(__arm__) || defined(__aarch64__))\nstatic constexpr __ptrace_request PTRACE_GETVFPREGS =\n    static_cast<__ptrace_request>(27);\n#define PTRACE_GETVFPREGS PTRACE_GETVFPREGS\n#endif\n\n#endif  // CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_\n"
  },
  {
    "path": "compat/linux/sys/user.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_LINUX_SYS_USER_H_\n#define CRASHPAD_COMPAT_LINUX_SYS_USER_H_\n\n#include_next <sys/user.h>\n\n#include <features.h>\n\n// glibc for 64-bit ARM uses different names for these structs prior to 2.20.\n// However, Debian's glibc 2.19-8 backported the change so it's not sufficient\n// to only test the version. user_pt_regs and user_fpsimd_state are actually\n// defined in <asm/ptrace.h> so we use the include guard here.\n#if defined(__aarch64__) && defined(__GLIBC__)\n#if !__GLIBC_PREREQ(2, 20) && defined(__ASM_PTRACE_H)\nusing user_regs_struct = user_pt_regs;\nusing user_fpsimd_struct = user_fpsimd_state;\n#endif\n#endif\n\n#endif  // CRASHPAD_COMPAT_LINUX_SYS_USER_H_\n"
  },
  {
    "path": "compat/mac/Availability.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_MAC_AVAILABILITY_H_\n#define CRASHPAD_COMPAT_MAC_AVAILABILITY_H_\n\n// Until the 10.15 SDK, the contents of <AvailabilityVersions.h> was in-line in\n// <Availability.h>, but since then, it was broken out into its own header.\n// This compat version of <Availability.h> allows these macros to always appear\n// to be provided by the new header, <AvailabilityVersions.h>, even when an\n// older SDK is in use.\n\n#include_next <Availability.h>\n\n#include <AvailabilityVersions.h>\n\n#endif  // CRASHPAD_COMPAT_MAC_AVAILABILITY_H_\n"
  },
  {
    "path": "compat/mac/AvailabilityVersions.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_MAC_AVAILABILITYVERSIONS_H_\n#define CRASHPAD_COMPAT_MAC_AVAILABILITYVERSIONS_H_\n\n#if __has_include_next(<AvailabilityVersions.h>)\n#include_next <AvailabilityVersions.h>\n#endif\n\n// 10.7 SDK\n\n#ifndef __MAC_10_7\n#define __MAC_10_7 1070\n#endif\n\n// 10.8 SDK\n\n#ifndef __MAC_10_8\n#define __MAC_10_8 1080\n#endif\n\n// 10.9 SDK\n\n#ifndef __MAC_10_9\n#define __MAC_10_9 1090\n#endif\n\n// 10.10 SDK\n\n#ifndef __MAC_10_10\n#define __MAC_10_10 101000\n#endif\n\n// 10.11 SDK\n\n#ifndef __MAC_10_11\n#define __MAC_10_11 101100\n#endif\n\n// 10.12 SDK\n\n#ifndef __MAC_10_12\n#define __MAC_10_12 101200\n#endif\n\n// 10.13 SDK\n\n#ifndef __MAC_10_13\n#define __MAC_10_13 101300\n#endif\n\n#ifndef __MAC_10_13_4\n#define __MAC_10_13_4 101304\n#endif\n\n// 10.14 SDK\n\n#ifndef __MAC_10_14\n#define __MAC_10_14 101400\n#endif\n\n// 10.15 SDK\n\n#ifndef __MAC_10_15\n#define __MAC_10_15 101500\n#endif\n\n// 11.0 SDK\n\n#ifndef __MAC_10_16\n#define __MAC_10_16 101600\n#endif\n\n#ifndef __MAC_11_0\n#define __MAC_11_0 110000\n#endif\n\n// 12.0 SDK\n\n#ifndef __MAC_12_0\n#define __MAC_12_0 120000\n#endif\n\n#endif  // CRASHPAD_COMPAT_MAC_AVAILABILITYVERSIONS_H_\n"
  },
  {
    "path": "compat/mac/kern/exc_resource.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_\n#define CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_\n\n#if __has_include_next(<kern/exc_resource.h>)\n#include_next <kern/exc_resource.h>\n#endif\n\n// 10.9 SDK\n\n#ifndef EXC_RESOURCE_DECODE_RESOURCE_TYPE\n#define EXC_RESOURCE_DECODE_RESOURCE_TYPE(code) (((code) >> 61) & 0x7ull)\n#endif\n\n#ifndef EXC_RESOURCE_DECODE_FLAVOR\n#define EXC_RESOURCE_DECODE_FLAVOR(code) (((code) >> 58) & 0x7ull)\n#endif\n\n#ifndef RESOURCE_TYPE_CPU\n#define RESOURCE_TYPE_CPU 1\n#endif\n\n#ifndef RESOURCE_TYPE_WAKEUPS\n#define RESOURCE_TYPE_WAKEUPS 2\n#endif\n\n#ifndef RESOURCE_TYPE_MEMORY\n#define RESOURCE_TYPE_MEMORY 3\n#endif\n\n#ifndef FLAVOR_CPU_MONITOR\n#define FLAVOR_CPU_MONITOR 1\n#endif\n\n#ifndef FLAVOR_WAKEUPS_MONITOR\n#define FLAVOR_WAKEUPS_MONITOR 1\n#endif\n\n#ifndef FLAVOR_HIGH_WATERMARK\n#define FLAVOR_HIGH_WATERMARK 1\n#endif\n\n// 10.10 SDK\n\n#ifndef FLAVOR_CPU_MONITOR_FATAL\n#define FLAVOR_CPU_MONITOR_FATAL 2\n#endif\n\n// 10.12 SDK\n\n#ifndef RESOURCE_TYPE_IO\n#define RESOURCE_TYPE_IO 4\n#endif\n\n#ifndef FLAVOR_IO_PHYSICAL_WRITES\n#define FLAVOR_IO_PHYSICAL_WRITES 1\n#endif\n\n#ifndef FLAVOR_IO_LOGICAL_WRITES\n#define FLAVOR_IO_LOGICAL_WRITES 2\n#endif\n\n#endif  // CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_\n"
  },
  {
    "path": "compat/mac/mach/i386/thread_state.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_\n#define CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_\n\n#include_next <mach/i386/thread_state.h>\n\n// 10.13 SDK\n//\n// This was defined as 244 in the 10.7 through 10.12 SDKs, and 144 previously.\n#if I386_THREAD_STATE_MAX < 614\n#undef I386_THREAD_STATE_MAX\n#define I386_THREAD_STATE_MAX (614)\n#endif\n\n#endif  // CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_\n"
  },
  {
    "path": "compat/mac/mach/mach.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_MAC_MACH_MACH_H_\n#define CRASHPAD_COMPAT_MAC_MACH_MACH_H_\n\n#include_next <mach/mach.h>\n\n// <mach/exception_types.h>\n\n// 10.8 SDK\n\n#ifndef EXC_RESOURCE\n#define EXC_RESOURCE 11\n#endif\n\n#ifndef EXC_MASK_RESOURCE\n#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE)\n#endif\n\n// 10.9 SDK\n\n#ifndef EXC_GUARD\n#define EXC_GUARD 12\n#endif\n\n#ifndef EXC_MASK_GUARD\n#define EXC_MASK_GUARD (1 << EXC_GUARD)\n#endif\n\n// 10.11 SDK\n\n#ifndef EXC_CORPSE_NOTIFY\n#define EXC_CORPSE_NOTIFY 13\n#endif\n\n#ifndef EXC_MASK_CORPSE_NOTIFY\n#define EXC_MASK_CORPSE_NOTIFY (1 << EXC_CORPSE_NOTIFY)\n#endif\n\n// Don’t expose EXC_MASK_ALL at all, because its definition varies with SDK, and\n// older kernels will reject values that they don’t understand. Instead, use\n// crashpad::ExcMaskAll(), which computes the correct value of EXC_MASK_ALL for\n// the running system.\n#undef EXC_MASK_ALL\n\n#if defined(__i386__) || defined(__x86_64__)\n\n// <mach/i386/exception.h>\n\n// 10.11 SDK\n\n#if EXC_TYPES_COUNT > 14  // Definition varies with SDK\n#error Update this file for new exception types\n#elif EXC_TYPES_COUNT != 14\n#undef EXC_TYPES_COUNT\n#define EXC_TYPES_COUNT 14\n#endif\n\n// <mach/i386/thread_status.h>\n\n// 10.6 SDK\n//\n// Earlier versions of this SDK didn’t have AVX definitions. They didn’t appear\n// until the version of the 10.6 SDK that shipped with Xcode 4.2, although\n// versions of this SDK appeared with Xcode releases as early as Xcode 3.2.\n// Similarly, the kernel didn’t handle AVX state until Mac OS X 10.6.8\n// (xnu-1504.15.3) and presumably the hardware-specific versions of Mac OS X\n// 10.6.7 intended to run on processors with AVX.\n\n#ifndef x86_AVX_STATE32\n#define x86_AVX_STATE32 16\n#endif\n\n#ifndef x86_AVX_STATE64\n#define x86_AVX_STATE64 17\n#endif\n\n// 10.8 SDK\n\n#ifndef x86_AVX_STATE\n#define x86_AVX_STATE 18\n#endif\n\n// 10.13 SDK\n\n#ifndef x86_AVX512_STATE32\n#define x86_AVX512_STATE32 19\n#endif\n\n#ifndef x86_AVX512_STATE64\n#define x86_AVX512_STATE64 20\n#endif\n\n#ifndef x86_AVX512_STATE\n#define x86_AVX512_STATE 21\n#endif\n\n#endif  // defined(__i386__) || defined(__x86_64__)\n\n// <mach/thread_status.h>\n\n// 10.8 SDK\n\n#ifndef THREAD_STATE_FLAVOR_LIST_10_9\n#define THREAD_STATE_FLAVOR_LIST_10_9 129\n#endif\n\n#endif  // CRASHPAD_COMPAT_MAC_MACH_MACH_H_\n"
  },
  {
    "path": "compat/mac/mach-o/loader.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_\n#define CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_\n\n#include_next <mach-o/loader.h>\n\n// 10.7 SDK\n\n#ifndef S_THREAD_LOCAL_ZEROFILL\n#define S_THREAD_LOCAL_ZEROFILL 0x12\n#endif\n\n// 10.8 SDK\n\n#ifndef LC_SOURCE_VERSION\n#define LC_SOURCE_VERSION 0x2a\n#endif\n\n#endif  // CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_\n"
  },
  {
    "path": "compat/mac/sys/resource.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_\n#define CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_\n\n#include_next <sys/resource.h>\n\n// 10.9 SDK\n\n#ifndef WAKEMON_MAKE_FATAL\n#define WAKEMON_MAKE_FATAL 0x10\n#endif\n\n#endif  // CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_\n"
  },
  {
    "path": "compat/non_mac/mach/mach.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_\n#define CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_\n\n//! \\file\n\n// <mach/exception_types.h>\n\n//! \\anchor EXC_x\n//! \\name EXC_*\n//!\n//! \\brief Mach exception type definitions.\n//! \\{\n#define EXC_BAD_ACCESS 1\n#define EXC_BAD_INSTRUCTION 2\n#define EXC_ARITHMETIC 3\n#define EXC_EMULATION 4\n#define EXC_SOFTWARE 5\n#define EXC_BREAKPOINT 6\n#define EXC_SYSCALL 7\n#define EXC_MACH_SYSCALL 8\n#define EXC_RPC_ALERT 9\n#define EXC_CRASH 10\n#define EXC_RESOURCE 11\n#define EXC_GUARD 12\n#define EXC_CORPSE_NOTIFY 13\n\n#define EXC_TYPES_COUNT 14\n//! \\}\n\n#endif  // CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_\n"
  },
  {
    "path": "compat/non_mac/mach/machine.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_\n#define CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_\n\ntypedef int cpu_type_t;\ntypedef int cpu_subtype_t;\n\n#endif  // CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_\n"
  },
  {
    "path": "compat/non_mac/mach/vm_prot.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_\n#define CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_\n\ntypedef int vm_prot_t;\n\n#endif  // CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_\n"
  },
  {
    "path": "compat/non_mac/mach-o/loader.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_\n#define CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_\n\n#include \"third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h\"\n\n#endif  // CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_\n"
  },
  {
    "path": "compat/non_win/dbghelp.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_\n#define CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_\n\n#include <stdint.h>\n\n#include \"compat/non_win/timezoneapi.h\"\n#include \"compat/non_win/verrsrc.h\"\n#include \"compat/non_win/winnt.h\"\n\n//! \\file\n\n//! \\brief The magic number for a minidump file, stored in\n//!     MINIDUMP_HEADER::Signature.\n//!\n//! A hex dump of a little-endian minidump file will begin with the string\n//! “MDMP”.\n#define MINIDUMP_SIGNATURE ('PMDM')  // 0x4d444d50\n\n//! \\brief The version of a minidump file, stored in MINIDUMP_HEADER::Version.\n#define MINIDUMP_VERSION (42899)\n\n//! \\brief An offset within a minidump file, relative to the start of its\n//!     MINIDUMP_HEADER.\n//!\n//! RVA stands for “relative virtual address”. Within a minidump file, RVAs are\n//! used as pointers to link structures together.\n//!\n//! \\sa MINIDUMP_LOCATION_DESCRIPTOR\ntypedef uint32_t RVA;\n\n//! \\brief A 64-bit offset within a minidump file, relative to the start of its\n//!     MINIDUMP_HEADER.\n//!\n//! RVA stands for “relative virtual address”. Within a minidump file, RVAs are\n//! used as pointers to link structures together.\n//!\n//! \\sa MINIDUMP_LOCATION_DESCRIPTOR64\ntypedef uint64_t RVA64;\n\n//! \\brief A pointer to a structure or union within a minidump file.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_LOCATION_DESCRIPTOR {\n  //! \\brief The size of the referenced structure or union, in bytes.\n  uint32_t DataSize;\n\n  //! \\brief The relative virtual address of the structure or union within the\n  //!     minidump file.\n  RVA Rva;\n};\n\n//! \\brief A 64-bit pointer to a structure or union within a minidump file.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_LOCATION_DESCRIPTOR64 {\n  //! \\brief The size of the referenced structure or union, in bytes.\n  uint64_t DataSize;\n\n  //! \\brief The 64-bit relative virtual address of the structure or union\n  //!     within the minidump file.\n  RVA64 Rva;\n};\n\n//! \\brief A pointer to a snapshot of a region of memory contained within a\n//!     minidump file.\n//!\n//! \\sa MINIDUMP_MEMORY_LIST\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_DESCRIPTOR {\n  //! \\brief The base address of the memory region in the address space of the\n  //!     process that the minidump file contains a snapshot of.\n  uint64_t StartOfMemoryRange;\n\n  //! \\brief The contents of the memory region.\n  MINIDUMP_LOCATION_DESCRIPTOR Memory;\n};\n\n//! \\brief The top-level structure identifying a minidump file.\n//!\n//! This structure contains a pointer to the stream directory, a second-level\n//! structure which in turn contains pointers to third-level structures\n//! (“streams”) containing the data within the minidump file. This structure\n//! also contains the minidump file’s magic numbers, and other bookkeeping data.\n//!\n//! This structure must be present at the beginning of a minidump file (at ::RVA\n//! 0).\nstruct __attribute__((packed, aligned(4))) MINIDUMP_HEADER {\n  //! \\brief The minidump file format magic number, ::MINIDUMP_SIGNATURE.\n  uint32_t Signature;\n\n  //! \\brief The minidump file format version number, ::MINIDUMP_VERSION.\n  uint32_t Version;\n\n  //! \\brief The number of MINIDUMP_DIRECTORY elements present in the directory\n  //!     referenced by #StreamDirectoryRva.\n  uint32_t NumberOfStreams;\n\n  //! \\brief A pointer to an array of MINIDUMP_DIRECTORY structures that\n  //!     identify all of the streams within this minidump file. The array has\n  //!     #NumberOfStreams elements present.\n  RVA StreamDirectoryRva;\n\n  //! \\brief The minidump file’s checksum. This can be `0`, and in practice, `0`\n  //!     is the only value that has ever been seen in this field.\n  uint32_t CheckSum;\n\n  //! \\brief The time that the minidump file was generated, in `time_t` format,\n  //!     the number of seconds since the POSIX epoch.\n  uint32_t TimeDateStamp;\n\n  //! \\brief A bitfield containing members of ::MINIDUMP_TYPE, describing the\n  //!     types of data carried within this minidump file.\n  uint64_t Flags;\n};\n\n//! \\brief A pointer to a stream within a minidump file.\n//!\n//! Each stream present in a minidump file will have a corresponding\n//! MINIDUMP_DIRECTORY entry in the stream directory referenced by\n//! MINIDUMP_HEADER::StreamDirectoryRva.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_DIRECTORY {\n  //! \\brief The type of stream referenced, a value of ::MINIDUMP_STREAM_TYPE.\n  uint32_t StreamType;\n\n  //! \\brief A pointer to the stream data within the minidump file.\n  MINIDUMP_LOCATION_DESCRIPTOR Location;\n};\n\n//! \\brief A variable-length UTF-16-encoded string carried within a minidump\n//!     file.\n//!\n//! The UTF-16 string is stored as UTF-16LE or UTF-16BE according to the byte\n//! ordering of the minidump file itself.\n//!\n//! \\sa crashpad::MinidumpUTF8String\nstruct __attribute__((packed, aligned(4))) MINIDUMP_STRING {\n  //! \\brief The length of the #Buffer field in bytes, not including the `NUL`\n  //!     terminator.\n  //!\n  //! \\note This field is interpreted as a byte count, not a count of UTF-16\n  //!     code units or Unicode code points.\n  uint32_t Length;\n\n  //! \\brief The string, encoded in UTF-16, and terminated with a UTF-16 `NUL`\n  //!     code unit (two `NUL` bytes).\n  char16_t Buffer[0];\n};\n\n//! \\brief Minidump stream type values for MINIDUMP_DIRECTORY::StreamType. Each\n//!     stream structure has a corresponding stream type value to identify it.\n//!\n//! \\sa crashpad::MinidumpStreamType\nenum MINIDUMP_STREAM_TYPE {\n  //! \\brief The stream type for MINIDUMP_THREAD_LIST.\n  ThreadListStream = 3,\n\n  //! \\brief The stream type for MINIDUMP_MODULE_LIST.\n  ModuleListStream = 4,\n\n  //! \\brief The stream type for MINIDUMP_MEMORY_LIST.\n  MemoryListStream = 5,\n\n  //! \\brief The stream type for MINIDUMP_EXCEPTION_STREAM.\n  ExceptionStream = 6,\n\n  //! \\brief The stream type for MINIDUMP_SYSTEM_INFO.\n  SystemInfoStream = 7,\n\n  //! \\brief The stream contains information about active `HANDLE`s.\n  HandleDataStream = 12,\n\n  //! \\brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST.\n  UnloadedModuleListStream = 14,\n\n  //! \\brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,\n  //!     MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, and MINIDUMP_MISC_INFO_5.\n  //!\n  //! More recent versions of this stream are supersets of earlier versions.\n  //!\n  //! The exact version of the stream that is present is implied by the stream’s\n  //! size. Furthermore, this stream contains a field,\n  //! MINIDUMP_MISC_INFO::Flags1, that indicates which data is present and\n  //! valid.\n  MiscInfoStream = 15,\n\n  //! \\brief The stream type for MINIDUMP_MEMORY_INFO_LIST.\n  MemoryInfoListStream = 16,\n\n  //! \\brief The stream type for MINIDUMP_THREAD_NAME_LIST.\n  ThreadNamesStream = 24,\n\n  //! \\brief Values greater than this value will not be used by the system\n  //!     and can be used for custom user data streams.\n  LastReservedStream = 0xffff,\n};\n\n//! \\brief Information about the CPU (or CPUs) that ran the process that the\n//!     minidump file contains a snapshot of.\n//!\n//! This union only appears as MINIDUMP_SYSTEM_INFO::Cpu. Its interpretation is\n//! controlled by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.\nunion __attribute__((packed, aligned(4))) CPU_INFORMATION {\n  //! \\brief Information about 32-bit x86 CPUs, or x86_64 CPUs when running\n  //!     32-bit x86 processes.\n  struct __attribute__((packed, aligned(4))) {\n    //! \\brief The CPU’s vendor identification string as encoded in `cpuid 0`\n    //!     `ebx`, `edx`, and `ecx`, represented as it appears in these\n    //!     registers.\n    //!\n    //! For Intel CPUs, `[0]` will encode “Genu”, `[1]` will encode “ineI”, and\n    //! `[2]` will encode “ntel”, for a vendor ID string “GenuineIntel”.\n    //!\n    //! \\note The Windows documentation incorrectly states that these fields are\n    //!     to be interpreted as `cpuid 0` `eax`, `ebx`, and `ecx`.\n    uint32_t VendorId[3];\n\n    //! \\brief Family, model, and stepping ID values as encoded in `cpuid 1`\n    //!     `eax`.\n    uint32_t VersionInformation;\n\n    //! \\brief A bitfield containing supported CPU capabilities as encoded in\n    //!     `cpuid 1` `edx`.\n    uint32_t FeatureInformation;\n\n    //! \\brief A bitfield containing supported CPU capabalities as encoded in\n    //!     `cpuid 0x80000001` `edx`.\n    //!\n    //! This field is only valid if #VendorId identifies the CPU vendor as\n    //! “AuthenticAMD” or \"HygonGenuine\".\n    uint32_t AMDExtendedCpuFeatures;\n  } X86CpuInfo;\n\n  //! \\brief Information about non-x86 CPUs, and x86_64 CPUs when not running\n  //!     32-bit x86 processes.\n  struct __attribute__((packed, aligned(4))) {\n    //! \\brief Bitfields containing supported CPU capabilities as identified by\n    //!     bits corresponding to \\ref PF_x \"PF_*\" values passed to\n    //!     `IsProcessorFeaturePresent()`.\n    uint64_t ProcessorFeatures[2];\n  } OtherCpuInfo;\n};\n\n//! \\brief Information about the system that hosted the process that the\n//!     minidump file contains a snapshot of.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_SYSTEM_INFO {\n  // The next 4 fields are from the SYSTEM_INFO structure returned by\n  // GetSystemInfo().\n\n  //! \\brief The system’s CPU architecture. This may be a \\ref\n  //!     PROCESSOR_ARCHITECTURE_x \"PROCESSOR_ARCHITECTURE_*\" value, or a member\n  //!     of crashpad::MinidumpCPUArchitecture.\n  //!\n  //! In some cases, a system may be able to run processes of multiple specific\n  //! architecture types. For example, systems based on 64-bit architectures\n  //! such as x86_64 are often able to run 32-bit code of another architecture\n  //! in the same family, such as 32-bit x86. On these systems, this field will\n  //! identify the architecture of the process that the minidump file contains a\n  //! snapshot of.\n  uint16_t ProcessorArchitecture;\n\n  //! \\brief General CPU version information.\n  //!\n  //! The precise interpretation of this field is specific to each CPU\n  //! architecture. For x86-family CPUs (including x86_64 and 32-bit x86), this\n  //! field contains the CPU family ID value from `cpuid 1` `eax`, adjusted to\n  //! take the extended family ID into account.\n  uint16_t ProcessorLevel;\n\n  //! \\brief Specific CPU version information.\n  //!\n  //! The precise interpretation of this field is specific to each CPU\n  //! architecture. For x86-family CPUs (including x86_64 and 32-bit x86), this\n  //! field contains values obtained from `cpuid 1` `eax`: the high byte\n  //! contains the CPU model ID value adjusted to take the extended model ID\n  //! into account, and the low byte contains the CPU stepping ID value.\n  uint16_t ProcessorRevision;\n\n  //! \\brief The total number of CPUs present in the system.\n  uint8_t NumberOfProcessors;\n\n  // The next 7 fields are from the OSVERSIONINFOEX structure returned by\n  // GetVersionEx().\n\n  //! \\brief The system’s operating system type, which distinguishes between\n  //!     “desktop” or “workstation” systems and “server” systems. This may be a\n  //!     \\ref VER_NT_x \"VER_NT_*\" value, or a member of\n  //!     crashpad::MinidumpOSType.\n  uint8_t ProductType;\n\n  //! \\brief The system’s operating system version number’s first (major)\n  //!     component.\n  //!\n  //!  - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `6`.\n  //!  - For macOS 10.12.1, this would be `10`.\n  uint32_t MajorVersion;\n\n  //! \\brief The system’s operating system version number’s second (minor)\n  //!     component.\n  //!\n  //!  - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `1`.\n  //!  - For macOS 10.12.1, this would be `12`.\n  uint32_t MinorVersion;\n\n  //! \\brief The system’s operating system version number’s third (build or\n  //!     patch) component.\n  //!\n  //!  - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `7601`.\n  //!  - For macOS 10.12.1, this would be `1`.\n  uint32_t BuildNumber;\n\n  //! \\brief The system’s operating system family. This may be a \\ref\n  //!     VER_PLATFORM_x \"VER_PLATFORM_*\" value, or a member of\n  //!     crashpad::MinidumpOS.\n  uint32_t PlatformId;\n\n  //! \\brief ::RVA of a MINIDUMP_STRING containing operating system-specific\n  //!     version information.\n  //!\n  //! This field further identifies an operating system version beyond its\n  //! version number fields. Historically, “CSD” stands for “corrective service\n  //! diskette.”\n  //!\n  //!  - On Windows, this is the name of the installed operating system service\n  //!    pack, such as “Service Pack 1”. If no service pack is installed, this\n  //!    field references an empty string.\n  //!  - On macOS, this is the operating system build number from `sw_vers\n  //!    -buildVersion`. For macOS 10.12.1 on most hardware types, this would\n  //!    be `16B2657`.\n  //!  - On Linux and other Unix-like systems, this is the kernel version from\n  //!    `uname -srvm`, possibly with additional information appended. On\n  //!    Android, the `ro.build.fingerprint` system property is appended.\n  RVA CSDVersionRva;\n\n  //! \\brief A bitfield identifying products installed on the system. This is\n  //!     composed of \\ref VER_SUITE_x \"VER_SUITE_*\" values.\n  //!\n  //! This field is Windows-specific, and has no meaning on other operating\n  //! systems.\n  uint16_t SuiteMask;\n\n  uint16_t Reserved2;\n\n  //! \\brief Information about the system’s CPUs.\n  //!\n  //! This field is a union. Which of its members should be expressed is\n  //! controlled by the #ProcessorArchitecture field. If it is set to\n  //! crashpad::kMinidumpCPUArchitectureX86, the CPU_INFORMATION::X86CpuInfo\n  //! field is expressed. Otherwise, the CPU_INFORMATION::OtherCpuInfo field is\n  //! expressed.\n  //!\n  //! \\note Older Breakpad implementations produce minidump files that express\n  //!     CPU_INFORMATION::X86CpuInfo when #ProcessorArchitecture is set to\n  //!     crashpad::kMinidumpCPUArchitectureAMD64. Minidump files produced by\n  //!     `dbghelp.dll` on Windows express CPU_INFORMATION::OtherCpuInfo in this\n  //!     case.\n  CPU_INFORMATION Cpu;\n};\n\n//! \\brief Information about a specific thread within the process.\n//!\n//! \\sa MINIDUMP_THREAD_LIST\nstruct __attribute__((packed, aligned(4))) MINIDUMP_THREAD {\n  //! \\brief The thread’s ID. This may be referenced by\n  //!     MINIDUMP_EXCEPTION_STREAM::ThreadId.\n  uint32_t ThreadId;\n\n  //! \\brief The thread’s suspend count.\n  //!\n  //! This field will be `0` if the thread is schedulable (not suspended).\n  uint32_t SuspendCount;\n\n  //! \\brief The thread’s priority class.\n  //!\n  //! On Windows, this is a `*_PRIORITY_CLASS` value. `NORMAL_PRIORITY_CLASS`\n  //! has value `0x20`; higher priority classes have higher values.\n  uint32_t PriorityClass;\n\n  //! \\brief The thread’s priority level.\n  //!\n  //! On Windows, this is a `THREAD_PRIORITY_*` value. `THREAD_PRIORITY_NORMAL`\n  //! has value `0`; higher priorities have higher values, and lower priorities\n  //! have lower (negative) values.\n  uint32_t Priority;\n\n  //! \\brief The address of the thread’s thread environment block in the address\n  //!     space of the process that the minidump file contains a snapshot of.\n  //!\n  //! The thread environment block contains thread-local data.\n  //!\n  //! A MINIDUMP_MEMORY_DESCRIPTOR may be present in the MINIDUMP_MEMORY_LIST\n  //! stream containing the thread-local data pointed to by this field.\n  uint64_t Teb;\n\n  //! \\brief A snapshot of the thread’s stack.\n  //!\n  //! A MINIDUMP_MEMORY_DESCRIPTOR may be present in the MINIDUMP_MEMORY_LIST\n  //! stream containing a pointer to the same memory range referenced by this\n  //! field.\n  MINIDUMP_MEMORY_DESCRIPTOR Stack;\n\n  //! \\brief A pointer to a CPU-specific CONTEXT structure containing the\n  //!     thread’s context at the time the snapshot was taken.\n  //!\n  //! If the minidump file was generated as a result of an exception taken on\n  //! this thread, this field may identify a different context than the\n  //! exception context. For these minidump files, a MINIDUMP_EXCEPTION_STREAM\n  //! stream will be present, and the context contained within that stream will\n  //! be the exception context.\n  //!\n  //! The interpretation of the context structure is dependent on the CPU\n  //! architecture identified by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.\n  //! For crashpad::kMinidumpCPUArchitectureX86, this will be\n  //! crashpad::MinidumpContextX86. For crashpad::kMinidumpCPUArchitectureAMD64,\n  //! this will be crashpad::MinidumpContextAMD64.\n  MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;\n};\n\n//! \\brief Information about all threads within the process.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_THREAD_LIST {\n  //! \\brief The number of threads present in the #Threads array.\n  uint32_t NumberOfThreads;\n\n  //! \\brief Structures identifying each thread within the process.\n  MINIDUMP_THREAD Threads[0];\n};\n\n//! \\brief Information about an exception that occurred in the process.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION {\n  //! \\brief The top-level exception code identifying the exception, in\n  //!     operating system-specific values.\n  //!\n  //! For macOS minidumps, this will be an \\ref EXC_x \"EXC_*\" exception type,\n  //! such as `EXC_BAD_ACCESS`. `EXC_CRASH` will not appear here for exceptions\n  //! processed as `EXC_CRASH` when generated from another preceding exception:\n  //! the original exception code will appear instead. The exception type as it\n  //! was received will appear at index 0 of #ExceptionInformation.\n  //!\n  //! For Windows minidumps, this will be an `EXCEPTION_*` exception type, such\n  //! as `EXCEPTION_ACCESS_VIOLATION`.\n  //!\n  //! \\note This field is named ExceptionCode, but what is known as the\n  //!     “exception code” on macOS/Mach is actually stored in the\n  //!     #ExceptionFlags field of a minidump file.\n  //!\n  //! \\todo Document the possible values by OS. There may be OS-specific enums\n  //!     in minidump_extensions.h.\n  uint32_t ExceptionCode;\n\n  //! \\brief Additional exception flags that further identify the exception, in\n  //!     operating system-specific values.\n  //!\n  //! For macOS minidumps, this will be the value of the exception code at index\n  //! 0 as received by a Mach exception handler, except:\n  //!  * For exception type `EXC_CRASH` generated from another preceding\n  //!    exception, the original exception code will appear here, not the code\n  //!    as received by the Mach exception handler.\n  //!  * For exception types `EXC_RESOURCE` and `EXC_GUARD`, the high 32 bits of\n  //!    the code received by the Mach exception handler will appear here.\n  //!\n  //! In all cases for macOS minidumps, the code as it was received by the Mach\n  //! exception handler will appear at index 1 of #ExceptionInformation.\n  //!\n  //! For Windows minidumps, this will either be `0` if the exception is\n  //! continuable, or `EXCEPTION_NONCONTINUABLE` to indicate a noncontinuable\n  //! exception.\n  //!\n  //! \\todo Document the possible values by OS. There may be OS-specific enums\n  //!     in minidump_extensions.h.\n  uint32_t ExceptionFlags;\n\n  //! \\brief An address, in the address space of the process that this minidump\n  //!     file contains a snapshot of, of another MINIDUMP_EXCEPTION. This field\n  //!     is used for nested exceptions.\n  uint64_t ExceptionRecord;\n\n  //! \\brief The address that caused the exception.\n  //!\n  //! This may be the address that caused a fault on data access, or it may be\n  //! the instruction pointer that contained an offending instruction.\n  uint64_t ExceptionAddress;\n\n  //! \\brief The number of valid elements in #ExceptionInformation.\n  uint32_t NumberParameters;\n\n  uint32_t __unusedAlignment;\n\n  //! \\brief Additional information about the exception, specific to the\n  //!     operating system and possibly the #ExceptionCode.\n  //!\n  //! For macOS minidumps, this will contain the exception type as received by a\n  //! Mach exception handler and the values of the `codes[0]` and `codes[1]`\n  //! (exception code and subcode) parameters supplied to the Mach exception\n  //! handler. Unlike #ExceptionCode and #ExceptionFlags, the values received by\n  //! a Mach exception handler are used directly here even for the `EXC_CRASH`,\n  //! `EXC_RESOURCE`, and `EXC_GUARD` exception types.\n\n  //! For Windows, these are additional arguments (if any) as provided to\n  //! `RaiseException()`.\n  uint64_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];\n};\n\n//! \\brief Information about the exception that triggered a minidump file’s\n//!     generation.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION_STREAM {\n  //! \\brief The ID of the thread that caused the exception.\n  //!\n  //! \\sa MINIDUMP_THREAD::ThreadId\n  uint32_t ThreadId;\n\n  uint32_t __alignment;\n\n  //! \\brief Information about the exception.\n  MINIDUMP_EXCEPTION ExceptionRecord;\n\n  //! \\brief A pointer to a CPU-specific CONTEXT structure containing the\n  //!     thread’s context at the time the exception was caused.\n  //!\n  //! The interpretation of the context structure is dependent on the CPU\n  //! architecture identified by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.\n  //! For crashpad::kMinidumpCPUArchitectureX86, this will be\n  //! crashpad::MinidumpContextX86. For crashpad::kMinidumpCPUArchitectureAMD64,\n  //! this will be crashpad::MinidumpContextAMD64.\n  MINIDUMP_LOCATION_DESCRIPTOR ThreadContext;\n};\n\n//! \\brief Information about a specific module loaded within the process at the\n//!     time the snapshot was taken.\n//!\n//! A module may be the main executable, a shared library, or a loadable module.\n//!\n//! \\sa MINIDUMP_MODULE_LIST\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MODULE {\n  //! \\brief The base address of the loaded module in the address space of the\n  //!     process that the minidump file contains a snapshot of.\n  uint64_t BaseOfImage;\n\n  //! \\brief The size of the loaded module.\n  uint32_t SizeOfImage;\n\n  //! \\brief The loaded module’s checksum, or `0` if unknown.\n  //!\n  //! On Windows, this field comes from the `CheckSum` field of the module’s\n  //! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at\n  //! the time the module was linked.\n  uint32_t CheckSum;\n\n  //! \\brief The module’s timestamp, in `time_t` units, seconds since the POSIX\n  //!     epoch, or `0` if unknown.\n  //!\n  //! On Windows, this field comes from the `TimeDateStamp` field of the\n  //! module’s `IMAGE_FILE_HEADER` structure. It reflects the timestamp at the\n  //! time the module was linked.\n  uint32_t TimeDateStamp;\n\n  //! \\brief ::RVA of a MINIDUMP_STRING containing the module’s path or file\n  //!     name.\n  RVA ModuleNameRva;\n\n  //! \\brief The module’s version information.\n  VS_FIXEDFILEINFO VersionInfo;\n\n  //! \\brief A pointer to the module’s CodeView record, typically a link to its\n  //!     debugging information in crashpad::CodeViewRecordPDB70 format.\n  //!\n  //! The specific format of the CodeView record is indicated by its signature,\n  //! the first 32-bit value in the structure. For links to debugging\n  //! information in contemporary usage, this is normally a\n  //! crashpad::CodeViewRecordPDB70 structure, but may be a\n  //! crashpad::CodeViewRecordPDB20 structure instead. These structures identify\n  //! a link to debugging data within a `.pdb` (Program Database) file. See <a\n  //! href=\"http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles\">Matching\n  //! Debug Information</a>, PDB Files.\n  //!\n  //! On Windows, it is also possible for the CodeView record to contain\n  //! debugging information itself, as opposed to a link to a `.pdb` file. See\n  //! <a\n  //! href=\"http://pierrelib.pagesperso-orange.fr/exec_formats/MS_Symbol_Type_v1.0.pdf#page=71\">Microsoft\n  //! Symbol and Type Information</a>, section 7.2, “Debug Information Format”\n  //! for a list of debug information formats, and <i>Undocumented Windows 2000\n  //! Secrets</i>, Windows 2000 Debugging Support/Microsoft Symbol File\n  //! Internals/CodeView Subsections for an in-depth description of the CodeView\n  //! 4.1 format. Signatures seen in the wild include “NB09” (0x3930424e) for\n  //! CodeView 4.1 and “NB11” (0x3131424e) for CodeView 5.0. This form of\n  //! debugging information within the module, as opposed to a link to an\n  //! external `.pdb` file, is chosen by building with `/Z7` in Visual Studio\n  //! 6.0 (1998) and earlier. This embedded form of debugging information is now\n  //! considered obsolete.\n  //!\n  //! On Windows, the CodeView record is taken from a module’s\n  //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value\n  //! IMAGE_DEBUG_TYPE_CODEVIEW (`2`), if any. Records in\n  //! crashpad::CodeViewRecordPDB70 format are generated by Visual Studio .NET\n  //! (2002) (version 7.0) and later.\n  //!\n  //! When the CodeView record is not present, the fields of this\n  //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`.\n  MINIDUMP_LOCATION_DESCRIPTOR CvRecord;\n\n  //! \\brief A pointer to the module’s miscellaneous debugging record, a\n  //!     structure of type IMAGE_DEBUG_MISC.\n  //!\n  //! This field is Windows-specific, and has no meaning on other operating\n  //! systems. It is largely obsolete on Windows, where it was used to link to\n  //! debugging information stored in a `.dbg` file. `.dbg` files have been\n  //! superseded by `.pdb` files.\n  //!\n  //! On Windows, the miscellaneous debugging record is taken from module’s\n  //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value\n  //! IMAGE_DEBUG_TYPE_MISC (`4`), if any.\n  //!\n  //! When the miscellaneous debugging record is not present, the fields of this\n  //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`.\n  //!\n  //! \\sa #CvRecord\n  MINIDUMP_LOCATION_DESCRIPTOR MiscRecord;\n\n  uint64_t Reserved0;\n  uint64_t Reserved1;\n};\n\n//! \\brief Information about all modules loaded within the process at the time\n//!     the snapshot was taken.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MODULE_LIST {\n  //! \\brief The number of modules present in the #Modules array.\n  uint32_t NumberOfModules;\n\n  //! \\brief Structures identifying each module present in the minidump file.\n  MINIDUMP_MODULE Modules[0];\n};\n\n//! \\brief Information about memory regions within the process.\n//!\n//! Typically, a minidump file will not contain a snapshot of a process’ entire\n//! memory image. For minidump files identified as ::MiniDumpNormal in\n//! MINIDUMP_HEADER::Flags, memory regions are limited to those referenced by\n//! MINIDUMP_THREAD::Stack fields, and a small number of others possibly related\n//! to the exception that triggered the snapshot to be taken.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_LIST {\n  //! \\brief The number of memory regions present in the #MemoryRanges array.\n  uint32_t NumberOfMemoryRanges;\n\n  //! \\brief Structures identifying each memory region present in the minidump\n  //!     file.\n  MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges[0];\n};\n\n//! \\brief Contains the state of an individual system handle at the time the\n//!     snapshot was taken. This structure is Windows-specific.\n//!\n//! \\sa MINIDUMP_HANDLE_DESCRIPTOR_2\nstruct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR {\n  //! \\brief The Windows `HANDLE` value.\n  uint64_t Handle;\n\n  //! \\brief An RVA to a MINIDUMP_STRING structure that specifies the object\n  //!     type of the handle. This member can be zero.\n  RVA TypeNameRva;\n\n  //! \\brief An RVA to a MINIDUMP_STRING structure that specifies the object\n  //!     name of the handle. This member can be zero.\n  RVA ObjectNameRva;\n\n  //! \\brief The attributes for the handle, this corresponds to `OBJ_INHERIT`,\n  //!     `OBJ_CASE_INSENSITIVE`, etc.\n  uint32_t Attributes;\n\n  //! \\brief The `ACCESS_MASK` for the handle.\n  uint32_t GrantedAccess;\n\n  //! \\brief This is the number of open handles to the object that this handle\n  //!     refers to.\n  uint32_t HandleCount;\n\n  //! \\brief This is the number kernel references to the object that this\n  //!     handle refers to.\n  uint32_t PointerCount;\n};\n\n//! \\brief Contains the state of an individual system handle at the time the\n//!     snapshot was taken. This structure is Windows-specific.\n//!\n//! \\sa MINIDUMP_HANDLE_DESCRIPTOR\nstruct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR_2\n    : public MINIDUMP_HANDLE_DESCRIPTOR {\n  //! \\brief An RVA to a MINIDUMP_HANDLE_OBJECT_INFORMATION structure that\n  //!     specifies object-specific information. This member can be zero if\n  //!     there is no extra information.\n  RVA ObjectInfoRva;\n\n  //! \\brief Must be zero.\n  uint32_t Reserved0;\n};\n\n//! \\brief Represents the header for a handle data stream.\n//!\n//! A list of MINIDUMP_HANDLE_DESCRIPTOR or MINIDUMP_HANDLE_DESCRIPTOR_2\n//! structures will immediately follow in the stream.\nstruct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM {\n  //! \\brief The size of the header information for the stream, in bytes. This\n  //!     value is `sizeof(MINIDUMP_HANDLE_DATA_STREAM)`.\n  uint32_t SizeOfHeader;\n\n  //! \\brief The size of a descriptor in the stream, in bytes. This value is\n  //!     `sizeof(MINIDUMP_HANDLE_DESCRIPTOR)` or\n  //!     `sizeof(MINIDUMP_HANDLE_DESCRIPTOR_2)`.\n  uint32_t SizeOfDescriptor;\n\n  //! \\brief The number of descriptors in the stream.\n  uint32_t NumberOfDescriptors;\n\n  //! \\brief Must be zero.\n  uint32_t Reserved;\n};\n\n//! \\brief Information about a specific module that was recorded as being\n//!     unloaded at the time the snapshot was taken.\n//!\n//! An unloaded module may be a shared library or a loadable module.\n//!\n//! \\sa MINIDUMP_UNLOADED_MODULE_LIST\nstruct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE {\n  //! \\brief The base address where the module was loaded in the address space\n  //!     of the process that the minidump file contains a snapshot of.\n  uint64_t BaseOfImage;\n\n  //! \\brief The size of the unloaded module.\n  uint32_t SizeOfImage;\n\n  //! \\brief The module’s checksum, or `0` if unknown.\n  //!\n  //! On Windows, this field comes from the `CheckSum` field of the module’s\n  //! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at\n  //! the time the module was linked.\n  uint32_t CheckSum;\n\n  //! \\brief The module’s timestamp, in `time_t` units, seconds since the POSIX\n  //!     epoch, or `0` if unknown.\n  //!\n  //! On Windows, this field comes from the `TimeDateStamp` field of the\n  //! module’s `IMAGE_FILE_HEADER` structure. It reflects the timestamp at the\n  //! time the module was linked.\n  uint32_t TimeDateStamp;\n\n  //! \\brief ::RVA of a MINIDUMP_STRING containing the module’s path or file\n  //!     name.\n  RVA ModuleNameRva;\n};\n\n//! \\brief Information about all modules recorded as unloaded when the snapshot\n//!     was taken.\n//!\n//! A list of MINIDUMP_UNLOADED_MODULE structures will immediately follow in the\n//! stream.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST {\n  //! \\brief The size of the header information for the stream, in bytes. This\n  //!     value is `sizeof(MINIDUMP_UNLOADED_MODULE_LIST)`.\n  uint32_t SizeOfHeader;\n\n  //! \\brief The size of a descriptor in the stream, in bytes. This value is\n  //!     `sizeof(MINIDUMP_UNLOADED_MODULE)`.\n  uint32_t SizeOfEntry;\n\n  //! \\brief The number of entries in the stream.\n  uint32_t NumberOfEntries;\n};\n\n//! \\brief Information about XSAVE-managed state stored within CPU-specific\n//!     context structures.\nstruct __attribute__((packed, aligned(4))) XSTATE_CONFIG_FEATURE_MSC_INFO {\n  //! \\brief The size of this structure, in bytes. This value is\n  //!     `sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO)`.\n  uint32_t SizeOfInfo;\n\n  //! \\brief The size of a CPU-specific context structure carrying all XSAVE\n  //!     state components described by this structure.\n  //!\n  //! Equivalent to the value returned by `InitializeContext()` in \\a\n  //! ContextLength.\n  uint32_t ContextSize;\n\n  //! \\brief The XSAVE state-component bitmap, XSAVE_BV.\n  //!\n  //! See Intel Software Developer’s Manual, Volume 1: Basic Architecture\n  //! (253665-060), 13.4.2 “XSAVE Header”.\n  uint64_t EnabledFeatures;\n\n  //! \\brief The location of each state component within a CPU-specific context\n  //!     structure.\n  //!\n  //! This array is indexed by bit position numbers used in #EnabledFeatures.\n  XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES];\n};\n\n//! \\anchor MINIDUMP_MISCx\n//! \\name MINIDUMP_MISC*\n//!\n//! \\brief Field validity flag values for MINIDUMP_MISC_INFO::Flags1.\n//! \\{\n\n//! \\brief MINIDUMP_MISC_INFO::ProcessId is valid.\n#define MINIDUMP_MISC1_PROCESS_ID 0x00000001\n\n//! \\brief The time-related fields in MINIDUMP_MISC_INFO are valid.\n//!\n//! The following fields are valid:\n//!  - MINIDUMP_MISC_INFO::ProcessCreateTime\n//!  - MINIDUMP_MISC_INFO::ProcessUserTime\n//!  - MINIDUMP_MISC_INFO::ProcessKernelTime\n#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002\n\n//! \\brief The CPU-related fields in MINIDUMP_MISC_INFO_2 are valid.\n//!\n//! The following fields are valid:\n//!  - MINIDUMP_MISC_INFO_2::ProcessorMaxMhz\n//!  - MINIDUMP_MISC_INFO_2::ProcessorCurrentMhz\n//!  - MINIDUMP_MISC_INFO_2::ProcessorMhzLimit\n//!  - MINIDUMP_MISC_INFO_2::ProcessorMaxIdleState\n//!  - MINIDUMP_MISC_INFO_2::ProcessorCurrentIdleState\n//!\n//! \\note This macro should likely have been named\n//!     MINIDUMP_MISC2_PROCESSOR_POWER_INFO.\n#define MINIDUMP_MISC1_PROCESSOR_POWER_INFO 0x00000004\n\n//! \\brief MINIDUMP_MISC_INFO_3::ProcessIntegrityLevel is valid.\n#define MINIDUMP_MISC3_PROCESS_INTEGRITY 0x00000010\n\n//! \\brief MINIDUMP_MISC_INFO_3::ProcessExecuteFlags is valid.\n#define MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS 0x00000020\n\n//! \\brief The time zone-related fields in MINIDUMP_MISC_INFO_3 are valid.\n//!\n//! The following fields are valid:\n//!  - MINIDUMP_MISC_INFO_3::TimeZoneId\n//!  - MINIDUMP_MISC_INFO_3::TimeZone\n#define MINIDUMP_MISC3_TIMEZONE 0x00000040\n\n//! \\brief MINIDUMP_MISC_INFO_3::ProtectedProcess is valid.\n#define MINIDUMP_MISC3_PROTECTED_PROCESS 0x00000080\n\n//! \\brief The build string-related fields in MINIDUMP_MISC_INFO_4 are valid.\n//!\n//! The following fields are valid:\n//!  - MINIDUMP_MISC_INFO_4::BuildString\n//!  - MINIDUMP_MISC_INFO_4::DbgBldStr\n#define MINIDUMP_MISC4_BUILDSTRING 0x00000100\n\n//! \\brief MINIDUMP_MISC_INFO_5::ProcessCookie is valid.\n#define MINIDUMP_MISC5_PROCESS_COOKIE 0x00000200\n\n//! \\}\n\n//! \\brief Information about the process that the minidump file contains a\n//!     snapshot of, as well as the system that hosted that process.\n//!\n//! \\sa \\ref MINIDUMP_MISCx \"MINIDUMP_MISC*\"\n//! \\sa MINIDUMP_MISC_INFO_2\n//! \\sa MINIDUMP_MISC_INFO_3\n//! \\sa MINIDUMP_MISC_INFO_4\n//! \\sa MINIDUMP_MISC_INFO_5\n//! \\sa MINIDUMP_MISC_INFO_N\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO {\n  //! \\brief The size of the structure.\n  //!\n  //! This field can be used to distinguish between different versions of this\n  //! structure: MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3,\n  //! and MINIDUMP_MISC_INFO_4.\n  //!\n  //! \\sa Flags1\n  uint32_t SizeOfInfo;\n\n  //! \\brief A bit field of \\ref MINIDUMP_MISCx \"MINIDUMP_MISC*\" values\n  //!     indicating which fields of this structure contain valid data.\n  uint32_t Flags1;\n\n  //! \\brief The process ID of the process.\n  uint32_t ProcessId;\n\n  //! \\brief The time that the process started, in `time_t` units, seconds since\n  //!     the POSIX epoch.\n  uint32_t ProcessCreateTime;\n\n  //! \\brief The amount of user-mode CPU time used by the process, in seconds,\n  //!     at the time of the snapshot.\n  uint32_t ProcessUserTime;\n\n  //! \\brief The amount of system-mode (kernel) CPU time used by the process, in\n  //!     seconds, at the time of the snapshot.\n  uint32_t ProcessKernelTime;\n};\n\n//! \\brief Information about the process that the minidump file contains a\n//!     snapshot of, as well as the system that hosted that process.\n//!\n//! This structure variant is used on Windows Vista (NT 6.0) and later.\n//!\n//! \\sa \\ref MINIDUMP_MISCx \"MINIDUMP_MISC*\"\n//! \\sa MINIDUMP_MISC_INFO\n//! \\sa MINIDUMP_MISC_INFO_3\n//! \\sa MINIDUMP_MISC_INFO_4\n//! \\sa MINIDUMP_MISC_INFO_5\n//! \\sa MINIDUMP_MISC_INFO_N\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_2\n    : public MINIDUMP_MISC_INFO {\n  //! \\brief The maximum clock rate of the system’s CPU or CPUs, in MHz.\n  uint32_t ProcessorMaxMhz;\n\n  //! \\brief The clock rate of the system’s CPU or CPUs, in MHz, at the time of\n  //!     the snapshot.\n  uint32_t ProcessorCurrentMhz;\n\n  //! \\brief The maximum clock rate of the system’s CPU or CPUs, in MHz, reduced\n  //!     by any thermal limitations, at the time of the snapshot.\n  uint32_t ProcessorMhzLimit;\n\n  //! \\brief The maximum idle state of the system’s CPU or CPUs.\n  uint32_t ProcessorMaxIdleState;\n\n  //! \\brief The idle state of the system’s CPU or CPUs at the time of the\n  //!     snapshot.\n  uint32_t ProcessorCurrentIdleState;\n};\n\n//! \\brief Information about the process that the minidump file contains a\n//!     snapshot of, as well as the system that hosted that process.\n//!\n//! This structure variant is used on Windows 7 (NT 6.1) and later.\n//!\n//! \\sa \\ref MINIDUMP_MISCx \"MINIDUMP_MISC*\"\n//! \\sa MINIDUMP_MISC_INFO\n//! \\sa MINIDUMP_MISC_INFO_2\n//! \\sa MINIDUMP_MISC_INFO_4\n//! \\sa MINIDUMP_MISC_INFO_5\n//! \\sa MINIDUMP_MISC_INFO_N\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3\n    : public MINIDUMP_MISC_INFO_2 {\n  //! \\brief The process’ integrity level.\n  //!\n  //! Windows typically uses `SECURITY_MANDATORY_MEDIUM_RID` (0x2000) for\n  //! processes belonging to normal authenticated users and\n  //! `SECURITY_MANDATORY_HIGH_RID` (0x3000) for elevated processes.\n  //!\n  //! This field is Windows-specific, and has no meaning on other operating\n  //! systems.\n  uint32_t ProcessIntegrityLevel;\n\n  //! \\brief The process’ execute flags.\n  //!\n  //! On Windows, this appears to be returned by `NtQueryInformationProcess()`\n  //! with an argument of `ProcessExecuteFlags` (34).\n  //!\n  //! This field is Windows-specific, and has no meaning on other operating\n  //! systems.\n  uint32_t ProcessExecuteFlags;\n\n  //! \\brief Whether the process is protected.\n  //!\n  //! This field is Windows-specific, and has no meaning on other operating\n  //! systems.\n  uint32_t ProtectedProcess;\n\n  //! \\brief Whether daylight saving time was being observed in the system’s\n  //!     location at the time of the snapshot.\n  //!\n  //! This field can contain the following values:\n  //!  - `0` if the location does not observe daylight saving time at all. The\n  //!    TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the\n  //!    time zone name.\n  //!  - `1` if the location observes daylight saving time, but standard time\n  //!    was in effect at the time of the snapshot. The\n  //!    TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the\n  //!    time zone name.\n  //!  - `2` if the location observes daylight saving time, and it was in effect\n  //!    at the time of the snapshot. The TIME_ZONE_INFORMATION::DaylightName\n  //!    field of #TimeZoneId contains the time zone name.\n  //!\n  //! \\sa #TimeZone\n  uint32_t TimeZoneId;\n\n  //! \\brief Information about the time zone at the system’s location.\n  //!\n  //! \\sa #TimeZoneId\n  TIME_ZONE_INFORMATION TimeZone;\n};\n\n//! \\brief Information about the process that the minidump file contains a\n//!     snapshot of, as well as the system that hosted that process.\n//!\n//! This structure variant is used on Windows 8 (NT 6.2) and later.\n//!\n//! \\sa \\ref MINIDUMP_MISCx \"MINIDUMP_MISC*\"\n//! \\sa MINIDUMP_MISC_INFO\n//! \\sa MINIDUMP_MISC_INFO_2\n//! \\sa MINIDUMP_MISC_INFO_3\n//! \\sa MINIDUMP_MISC_INFO_5\n//! \\sa MINIDUMP_MISC_INFO_N\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4\n    : public MINIDUMP_MISC_INFO_3 {\n  //! \\brief The operating system’s “build string”, a string identifying a\n  //!     specific build of the operating system.\n  //!\n  //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit.\n  //!\n  //! On Windows 8.1 (NT 6.3), this is “6.3.9600.17031\n  //! (winblue_gdr.140221-1952)”.\n  char16_t BuildString[260];\n\n  //! \\brief The minidump producer’s “build string”, a string identifying the\n  //!     module that produced a minidump file.\n  //!\n  //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit.\n  //!\n  //! On Windows 8.1 (NT 6.3), this may be “dbghelp.i386,6.3.9600.16520” or\n  //! “dbghelp.amd64,6.3.9600.16520” depending on CPU architecture.\n  char16_t DbgBldStr[40];\n};\n\n//! \\brief Information about the process that the minidump file contains a\n//!     snapshot of, as well as the system that hosted that process.\n//!\n//! This structure variant is used on Windows 10 and later.\n//!\n//! \\sa \\ref MINIDUMP_MISCx \"MINIDUMP_MISC*\"\n//! \\sa MINIDUMP_MISC_INFO\n//! \\sa MINIDUMP_MISC_INFO_2\n//! \\sa MINIDUMP_MISC_INFO_3\n//! \\sa MINIDUMP_MISC_INFO_4\n//! \\sa MINIDUMP_MISC_INFO_N\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_5\n    : public MINIDUMP_MISC_INFO_4 {\n  //! \\brief Information about XSAVE-managed state stored within CPU-specific\n  //!     context structures.\n  //!\n  //! This information can be used to locate state components within\n  //! CPU-specific context structures.\n  XSTATE_CONFIG_FEATURE_MSC_INFO XStateData;\n\n  uint32_t ProcessCookie;\n};\n\n//! \\brief The latest known version of the MINIDUMP_MISC_INFO structure.\ntypedef MINIDUMP_MISC_INFO_5 MINIDUMP_MISC_INFO_N;\n\n//! \\brief Describes a region of memory.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO {\n  //! \\brief The base address of the region of pages.\n  uint64_t BaseAddress;\n\n  //! \\brief The base address of a range of pages in this region. The page is\n  //!     contained within this memory region.\n  uint64_t AllocationBase;\n\n  //! \\brief The memory protection when the region was initially allocated. This\n  //!     member can be one of the memory protection options (such as\n  //!     \\ref PAGE_x \"PAGE_EXECUTE\", \\ref PAGE_x \"PAGE_NOACCESS\", etc.), along\n  //!     with \\ref PAGE_x \"PAGE_GUARD\" or \\ref PAGE_x \"PAGE_NOCACHE\", as\n  //!     needed.\n  uint32_t AllocationProtect;\n\n  uint32_t __alignment1;\n\n  //! \\brief The size of the region beginning at the base address in which all\n  //!     pages have identical attributes, in bytes.\n  uint64_t RegionSize;\n\n  //! \\brief The state of the pages in the region. This can be one of\n  //!     \\ref MEM_x \"MEM_COMMIT\", \\ref MEM_x \"MEM_FREE\", or \\ref MEM_x\n  //!     \"MEM_RESERVE\".\n  uint32_t State;\n\n  //! \\brief The access protection of the pages in the region. This member is\n  //!     one of the values listed for the #AllocationProtect member.\n  uint32_t Protect;\n\n  //! \\brief The type of pages in the region. This can be one of \\ref MEM_x\n  //!     \"MEM_IMAGE\", \\ref MEM_x \"MEM_MAPPED\", or \\ref MEM_x \"MEM_PRIVATE\".\n  uint32_t Type;\n\n  uint32_t __alignment2;\n};\n\n//! \\brief Contains a list of memory regions.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO_LIST {\n  //! \\brief The size of the header data for the stream, in bytes. This is\n  //!     generally sizeof(MINIDUMP_MEMORY_INFO_LIST).\n  uint32_t SizeOfHeader;\n\n  //! \\brief The size of each entry following the header, in bytes. This is\n  //!     generally sizeof(MINIDUMP_MEMORY_INFO).\n  uint32_t SizeOfEntry;\n\n  //! \\brief The number of entries in the stream. These are generally\n  //!     MINIDUMP_MEMORY_INFO structures. The entries follow the header.\n  uint64_t NumberOfEntries;\n};\n\n//! \\brief Contains the name of the thread with the given thread ID.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_THREAD_NAME {\n  //! \\brief The identifier of the thread.\n  uint32_t ThreadId;\n\n  //! \\brief RVA64 of a MINIDUMP_STRING containing the name of the thread.\n  RVA64 RvaOfThreadName;\n};\n\n//! \\brief Variable-sized struct which contains a list of MINIDUMP_THREAD_NAME\n//! structs.\nstruct __attribute__((packed, aligned(4))) MINIDUMP_THREAD_NAME_LIST {\n  //! \\brief The number of MINIDUMP_THREAD_NAME structs following this field.\n  uint32_t NumberOfThreadNames;\n\n  //! \\brief A variably-sized array containing zero or more\n  //!    MINIDUMP_THREAD_NAME structs. The length of the array is indicated by\n  //!    the NumberOfThreadNames field in this struct.\n  struct MINIDUMP_THREAD_NAME ThreadNames[0];\n};\n\n//! \\brief Minidump file type values for MINIDUMP_HEADER::Flags. These bits\n//!     describe the types of data carried within a minidump file.\nenum MINIDUMP_TYPE {\n  //! \\brief A minidump file without any additional data.\n  //!\n  //! This type of minidump file contains:\n  //!  - A MINIDUMP_SYSTEM_INFO stream.\n  //!  - A MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, or\n  //!    MINIDUMP_MISC_INFO_4 stream, depending on which fields are present.\n  //!  - A MINIDUMP_THREAD_LIST stream. All threads are present, along with a\n  //!    snapshot of each thread’s stack memory sufficient to obtain backtraces.\n  //!  - If the minidump file was generated as a result of an exception, a\n  //!    MINIDUMP_EXCEPTION_STREAM describing the exception.\n  //!  - A MINIDUMP_MODULE_LIST stream. All loaded modules are present.\n  //!  - Typically, a MINIDUMP_MEMORY_LIST stream containing duplicate pointers\n  //!    to the stack memory regions also referenced by the MINIDUMP_THREAD_LIST\n  //!    stream. This type of minidump file also includes a\n  //!    MINIDUMP_MEMORY_DESCRIPTOR containing the 256 bytes centered around\n  //!    the exception address or the instruction pointer.\n  MiniDumpNormal = 0x00000000,\n\n  //! \\brief A minidump with extended contexts.\n  //!\n  //! Contains Normal plus a MISC_INFO_5 structure describing the contexts.\n  MiniDumpWithAvxXStateContext = 0x00200000,\n};\n\n#endif  // CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_\n"
  },
  {
    "path": "compat/non_win/minwinbase.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_\n#define CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_\n\n#include <stdint.h>\n\n//! \\brief Represents a date and time.\nstruct SYSTEMTIME {\n  //! \\brief The year, represented fully.\n  //!\n  //! The year 2014 would be represented in this field as `2014`.\n  uint16_t wYear;\n\n  //! \\brief The month of the year, `1` for January and `12` for December.\n  uint16_t wMonth;\n\n  //! \\brief The day of the week, `0` for Sunday and `6` for Saturday.\n  uint16_t wDayOfWeek;\n\n  //! \\brief The day of the month, `1` through `31`.\n  uint16_t wDay;\n\n  //! \\brief The hour of the day, `0` through `23`.\n  uint16_t wHour;\n\n  //! \\brief The minute of the hour, `0` through `59`.\n  uint16_t wMinute;\n\n  //! \\brief The second of the minute, `0` through `60`.\n  uint16_t wSecond;\n\n  //! \\brief The millisecond of the second, `0` through `999`.\n  uint16_t wMilliseconds;\n};\n\n#endif  // CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_\n"
  },
  {
    "path": "compat/non_win/timezoneapi.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_\n#define CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_\n\n#include <stdint.h>\n\n#include \"compat/non_win/minwinbase.h\"\n\n//! \\brief Information about a time zone and its daylight saving rules.\nstruct TIME_ZONE_INFORMATION {\n  //! \\brief The number of minutes west of UTC.\n  int32_t Bias;\n\n  //! \\brief The UTF-16-encoded name of the time zone when observing standard\n  //!     time.\n  char16_t StandardName[32];\n\n  //! \\brief The date and time to switch from daylight saving time to standard\n  //!     time.\n  //!\n  //! This can be a specific time, or with SYSTEMTIME::wYear set to `0`, it can\n  //! reflect an annual recurring transition. In that case, SYSTEMTIME::wDay in\n  //! the range `1` to `5` is interpreted as the given occurrence of\n  //! SYSTEMTIME::wDayOfWeek within the month, `1` being the first occurrence\n  //! and `5` being the last (even if there are fewer than 5).\n  SYSTEMTIME StandardDate;\n\n  //! \\brief The bias relative to #Bias to be applied when observing standard\n  //!     time.\n  int32_t StandardBias;\n\n  //! \\brief The UTF-16-encoded name of the time zone when observing daylight\n  //!     saving time.\n  char16_t DaylightName[32];\n\n  //! \\brief The date and time to switch from standard time to daylight saving\n  //!     time.\n  //!\n  //! This field is specified in the same manner as #StandardDate.\n  SYSTEMTIME DaylightDate;\n\n  //! \\brief The bias relative to #Bias to be applied when observing daylight\n  //!     saving time.\n  int32_t DaylightBias;\n};\n\n#endif  // CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_\n"
  },
  {
    "path": "compat/non_win/verrsrc.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_\n#define CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_\n\n#include <stdint.h>\n\n//! \\file\n\n//! \\brief The magic number for a VS_FIXEDFILEINFO structure, stored in\n//!     VS_FIXEDFILEINFO::dwSignature.\n#define VS_FFI_SIGNATURE 0xfeef04bd\n\n//! \\brief The version of a VS_FIXEDFILEINFO structure, stored in\n//!     VS_FIXEDFILEINFO::dwStrucVersion.\n#define VS_FFI_STRUCVERSION 0x00010000\n\n//! \\anchor VS_FF_x\n//! \\name VS_FF_*\n//!\n//! \\brief File attribute values for VS_FIXEDFILEINFO::dwFileFlags and\n//!     VS_FIXEDFILEINFO::dwFileFlagsMask.\n//! \\{\n#define VS_FF_DEBUG 0x00000001\n#define VS_FF_PRERELEASE 0x00000002\n#define VS_FF_PATCHED 0x00000004\n#define VS_FF_PRIVATEBUILD 0x00000008\n#define VS_FF_INFOINFERRED 0x00000010\n#define VS_FF_SPECIALBUILD 0x00000020\n//! \\}\n\n//! \\anchor VOS_x\n//! \\name VOS_*\n//!\n//! \\brief Operating system values for VS_FIXEDFILEINFO::dwFileOS.\n//! \\{\n#define VOS_UNKNOWN 0x00000000\n#define VOS_DOS 0x00010000\n#define VOS_OS216 0x00020000\n#define VOS_OS232 0x00030000\n#define VOS_NT 0x00040000\n#define VOS_WINCE 0x00050000\n#define VOS__BASE 0x00000000\n#define VOS__WINDOWS16 0x00000001\n#define VOS__PM16 0x00000002\n#define VOS__PM32 0x00000003\n#define VOS__WINDOWS32 0x00000004\n#define VOS_DOS_WINDOWS16 0x00010001\n#define VOS_DOS_WINDOWS32 0x00010004\n#define VOS_OS216_PM16 0x00020002\n#define VOS_OS232_PM32 0x00030003\n#define VOS_NT_WINDOWS32 0x00040004\n//! \\}\n\n//! \\anchor VFT_x\n//! \\name VFT_*\n//!\n//! \\brief File type values for VS_FIXEDFILEINFO::dwFileType.\n//! \\{\n#define VFT_UNKNOWN 0x00000000\n#define VFT_APP 0x00000001\n#define VFT_DLL 0x00000002\n#define VFT_DRV 0x00000003\n#define VFT_FONT 0x00000004\n#define VFT_VXD 0x00000005\n#define VFT_STATIC_LIB 0x00000007\n//! \\}\n\n//! \\anchor VFT2_x\n//! \\name VFT2_*\n//!\n//! \\brief File subtype values for VS_FIXEDFILEINFO::dwFileSubtype.\n//! \\{\n#define VFT2_UNKNOWN 0x00000000\n#define VFT2_DRV_PRINTER 0x00000001\n#define VFT2_DRV_KEYBOARD 0x00000002\n#define VFT2_DRV_LANGUAGE 0x00000003\n#define VFT2_DRV_DISPLAY 0x00000004\n#define VFT2_DRV_MOUSE 0x00000005\n#define VFT2_DRV_NETWORK 0x00000006\n#define VFT2_DRV_SYSTEM 0x00000007\n#define VFT2_DRV_INSTALLABLE 0x00000008\n#define VFT2_DRV_SOUND 0x00000009\n#define VFT2_DRV_COMM 0x0000000A\n#define VFT2_DRV_INPUTMETHOD 0x0000000B\n#define VFT2_DRV_VERSIONED_PRINTER 0x0000000C\n#define VFT2_FONT_RASTER 0x00000001\n#define VFT2_FONT_VECTOR 0x00000002\n#define VFT2_FONT_TRUETYPE 0x00000003\n//! \\}\n\n//! \\brief Version information for a file.\n//!\n//! On Windows, this information is derived from a file’s version information\n//! resource, and is obtained by calling `VerQueryValue()` with an `lpSubBlock`\n//! argument of `\"\\\"` (a single backslash).\nstruct VS_FIXEDFILEINFO {\n  //! \\brief The structure’s magic number, ::VS_FFI_SIGNATURE.\n  uint32_t dwSignature;\n\n  //! \\brief The structure’s version, ::VS_FFI_STRUCVERSION.\n  uint32_t dwStrucVersion;\n\n  //! \\brief The more-significant portion of the file’s version number.\n  //!\n  //! This field contains the first two components of a four-component version\n  //! number. For a file whose version is 1.2.3.4, this field would be\n  //! `0x00010002`.\n  //!\n  //! \\sa dwFileVersionLS\n  uint32_t dwFileVersionMS;\n\n  //! \\brief The less-significant portion of the file’s version number.\n  //!\n  //! This field contains the last two components of a four-component version\n  //! number. For a file whose version is 1.2.3.4, this field would be\n  //! `0x00030004`.\n  //!\n  //! \\sa dwFileVersionMS\n  uint32_t dwFileVersionLS;\n\n  //! \\brief The more-significant portion of the product’s version number.\n  //!\n  //! This field contains the first two components of a four-component version\n  //! number. For a product whose version is 1.2.3.4, this field would be\n  //! `0x00010002`.\n  //!\n  //! \\sa dwProductVersionLS\n  uint32_t dwProductVersionMS;\n\n  //! \\brief The less-significant portion of the product’s version number.\n  //!\n  //! This field contains the last two components of a four-component version\n  //! number. For a product whose version is 1.2.3.4, this field would be\n  //! `0x00030004`.\n  //!\n  //! \\sa dwProductVersionMS\n  uint32_t dwProductVersionLS;\n\n  //! \\brief A bitmask of \\ref VS_FF_x \"VS_FF_*\" values indicating which bits in\n  //!     #dwFileFlags are valid.\n  uint32_t dwFileFlagsMask;\n\n  //! \\brief A bitmask of \\ref VS_FF_x \"VS_FF_*\" values identifying attributes\n  //!     of the file. Only bits present in #dwFileFlagsMask are valid.\n  uint32_t dwFileFlags;\n\n  //! \\brief The file’s intended operating system, a value of \\ref VOS_x\n  //!     \"VOS_*\".\n  uint32_t dwFileOS;\n\n  //! \\brief The file’s type, a value of \\ref VFT_x \"VFT_*\".\n  uint32_t dwFileType;\n\n  //! \\brief The file’s subtype, a value of \\ref VFT2_x \"VFT2_*\" corresponding\n  //!     to its #dwFileType, if the file type has subtypes.\n  uint32_t dwFileSubtype;\n\n  //! \\brief The more-significant portion of the file’s creation date.\n  //!\n  //! The intended encoding of this field is unknown. This field is unused and\n  //! always has the value `0`.\n  uint32_t dwFileDateMS;\n\n  //! \\brief The less-significant portion of the file’s creation date.\n  //!\n  //! The intended encoding of this field is unknown. This field is unused and\n  //! always has the value `0`.\n  uint32_t dwFileDateLS;\n};\n\n#endif  // CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_\n"
  },
  {
    "path": "compat/non_win/windows.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// dbghelp.h on Windows requires inclusion of windows.h before it. To avoid\n// cluttering all inclusions of dbghelp.h with #ifdefs, always include windows.h\n// and have an empty one on non-Windows platforms.\n"
  },
  {
    "path": "compat/non_win/winnt.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_NON_WIN_WINNT_H_\n#define CRASHPAD_COMPAT_NON_WIN_WINNT_H_\n\n#include <stdint.h>\n\n//! \\file\n\n//! \\anchor VER_SUITE_x\n//! \\name VER_SUITE_*\n//!\n//! \\brief Installable product values for MINIDUMP_SYSTEM_INFO::SuiteMask.\n//! \\{\n#define VER_SUITE_SMALLBUSINESS 0x0001\n#define VER_SUITE_ENTERPRISE 0x0002\n#define VER_SUITE_BACKOFFICE 0x0004\n#define VER_SUITE_COMMUNICATIONS 0x0008\n#define VER_SUITE_TERMINAL 0x0010\n#define VER_SUITE_SMALLBUSINESS_RESTRICTED 0x0020\n#define VER_SUITE_EMBEDDEDNT 0x0040\n#define VER_SUITE_DATACENTER 0x0080\n#define VER_SUITE_SINGLEUSERTS 0x0100\n#define VER_SUITE_PERSONAL 0x0200\n#define VER_SUITE_BLADE 0x0400\n#define VER_SUITE_EMBEDDED_RESTRICTED 0x0800\n#define VER_SUITE_SECURITY_APPLIANCE 0x1000\n#define VER_SUITE_STORAGE_SERVER 0x2000\n#define VER_SUITE_COMPUTE_SERVER 0x4000\n#define VER_SUITE_WH_SERVER 0x8000\n//! \\}\n\n//! \\brief The maximum number of exception parameters present in the\n//!     MINIDUMP_EXCEPTION::ExceptionInformation array.\n#define EXCEPTION_MAXIMUM_PARAMETERS 15\n\n//! \\anchor PROCESSOR_ARCHITECTURE_x\n//! \\name PROCESSOR_ARCHITECTURE_*\n//!\n//! \\brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.\n//!\n//! \\sa crashpad::MinidumpCPUArchitecture\n//! \\{\n#define PROCESSOR_ARCHITECTURE_INTEL 0\n#define PROCESSOR_ARCHITECTURE_MIPS 1\n#define PROCESSOR_ARCHITECTURE_ALPHA 2\n#define PROCESSOR_ARCHITECTURE_PPC 3\n#define PROCESSOR_ARCHITECTURE_SHX 4\n#define PROCESSOR_ARCHITECTURE_ARM 5\n#define PROCESSOR_ARCHITECTURE_IA64 6\n#define PROCESSOR_ARCHITECTURE_ALPHA64 7\n#define PROCESSOR_ARCHITECTURE_MSIL 8\n#define PROCESSOR_ARCHITECTURE_AMD64 9\n#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10\n#define PROCESSOR_ARCHITECTURE_NEUTRAL 11\n#define PROCESSOR_ARCHITECTURE_ARM64 12\n#define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13\n#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xffff\n//! \\}\n\n//! \\anchor PF_x\n//! \\name PF_*\n//!\n//! \\brief CPU feature values for \\ref CPU_INFORMATION::ProcessorFeatures\n//!     \"CPU_INFORMATION::OtherCpuInfo::ProcessorFeatures\".\n//!\n//! \\{\n#define PF_FLOATING_POINT_PRECISION_ERRATA 0\n#define PF_FLOATING_POINT_EMULATED 1\n#define PF_COMPARE_EXCHANGE_DOUBLE 2\n#define PF_MMX_INSTRUCTIONS_AVAILABLE 3\n#define PF_PPC_MOVEMEM_64BIT_OK 4\n#define PF_ALPHA_BYTE_INSTRUCTIONS 5\n#define PF_XMMI_INSTRUCTIONS_AVAILABLE 6\n#define PF_3DNOW_INSTRUCTIONS_AVAILABLE 7\n#define PF_RDTSC_INSTRUCTION_AVAILABLE 8\n#define PF_PAE_ENABLED 9\n#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10\n#define PF_SSE_DAZ_MODE_AVAILABLE 11\n#define PF_NX_ENABLED 12\n#define PF_SSE3_INSTRUCTIONS_AVAILABLE 13\n#define PF_COMPARE_EXCHANGE128 14\n#define PF_COMPARE64_EXCHANGE128 15\n#define PF_CHANNELS_ENABLED 16\n#define PF_XSAVE_ENABLED 17\n#define PF_ARM_VFP_32_REGISTERS_AVAILABLE 18\n#define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19\n#define PF_SECOND_LEVEL_ADDRESS_TRANSLATION 20\n#define PF_VIRT_FIRMWARE_ENABLED 21\n#define PF_RDWRFSGSBASE_AVAILABLE 22\n#define PF_FASTFAIL_AVAILABLE 23\n#define PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE 24\n#define PF_ARM_64BIT_LOADSTORE_ATOMIC 25\n#define PF_ARM_EXTERNAL_CACHE_AVAILABLE 26\n#define PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE 27\n#define PF_RDRAND_INSTRUCTION_AVAILABLE 28\n#define PF_ARM_V8_INSTRUCTIONS_AVAILABLE 29\n#define PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE 30\n#define PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE 31\n#define PF_RDTSCP_INSTRUCTION_AVAILABLE 32\n//! \\}\n\n//! \\anchor PAGE_x\n//! \\name PAGE_*\n//!\n//! \\brief Memory protection constants for MINIDUMP_MEMORY_INFO::Protect and\n//!     MINIDUMP_MEMORY_INFO::AllocationProtect.\n//! \\{\n#define PAGE_NOACCESS 0x1\n#define PAGE_READONLY 0x2\n#define PAGE_READWRITE 0x4\n#define PAGE_WRITECOPY 0x8\n#define PAGE_EXECUTE 0x10\n#define PAGE_EXECUTE_READ 0x20\n#define PAGE_EXECUTE_READWRITE 0x40\n#define PAGE_EXECUTE_WRITECOPY 0x80\n#define PAGE_GUARD 0x100\n#define PAGE_NOCACHE 0x200\n#define PAGE_WRITECOMBINE 0x400\n//! \\}\n\n//! \\anchor MEM_x\n//! \\name MEM_*\n//!\n//! \\brief Memory state and type constants for MINIDUMP_MEMORY_INFO::State and\n//!     MINIDUMP_MEMORY_INFO::Type.\n//! \\{\n#define MEM_COMMIT 0x1000\n#define MEM_RESERVE 0x2000\n#define MEM_DECOMMIT 0x4000\n#define MEM_RELEASE 0x8000\n#define MEM_FREE 0x10000\n#define MEM_PRIVATE 0x20000\n#define MEM_MAPPED 0x40000\n#define MEM_RESET 0x80000\n//! \\}\n\n//! \\brief The maximum number of distinct identifiable features that could\n//!     possibly be carried in an XSAVE area.\n//!\n//! This corresponds to the number of bits in the XSAVE state-component bitmap,\n//! XSAVE_BV. See Intel Software Developer’s Manual, Volume 1: Basic\n//! Architecture (253665-060), 13.4.2 “XSAVE Header”.\n#define MAXIMUM_XSTATE_FEATURES (64)\n\n//! \\anchor XSTATE_x\n//! \\name XSTATE_*\n//!\n//! \\brief Offsets and constants for extended state.\n//! \\{\n#define XSTATE_COMPACTION_ENABLE (63)\n#define XSTATE_COMPACTION_ENABLE_MASK (1ull << XSTATE_COMPACTION_ENABLE)\n#define XSTATE_CET_U (11)\n#define XSTATE_MASK_CET_U (1ull << XSTATE_CET_U)\n//! \\}\n\n//! \\brief The location of a single state component within an XSAVE area.\nstruct XSTATE_FEATURE {\n  //! \\brief The location of a state component within a CPU-specific context\n  //!     structure.\n  //!\n  //! This is equivalent to the difference (`ptrdiff_t`) between the return\n  //! value of `LocateXStateFeature()` and its \\a Context argument.\n  uint32_t Offset;\n\n  //! \\brief The size of a state component with a CPU-specific context\n  //!     structure.\n  //!\n  //! This is equivalent to the size returned by `LocateXStateFeature()` in \\a\n  //!     Length.\n  uint32_t Size;\n};\n\n//! \\anchor IMAGE_DEBUG_MISC_x\n//! \\name IMAGE_DEBUG_MISC_*\n//!\n//! Data type values for IMAGE_DEBUG_MISC::DataType.\n//! \\{\n\n//! \\brief A pointer to a `.dbg` file.\n//!\n//! IMAGE_DEBUG_MISC::Data will contain the path or file name of the `.dbg` file\n//! associated with the module.\n#define IMAGE_DEBUG_MISC_EXENAME 1\n\n//! \\}\n\n//! \\brief Miscellaneous debugging record.\n//!\n//! This structure is referenced by MINIDUMP_MODULE::MiscRecord. It is obsolete,\n//! superseded by the CodeView record.\nstruct IMAGE_DEBUG_MISC {\n  //! \\brief The type of data carried in the #Data field.\n  //!\n  //! This is a value of \\ref IMAGE_DEBUG_MISC_x \"IMAGE_DEBUG_MISC_*\".\n  uint32_t DataType;\n\n  //! \\brief The length of this structure in bytes, including the entire #Data\n  //!     field and its `NUL` terminator.\n  //!\n  //! \\note The Windows documentation states that this field is rounded up to\n  //!     nearest nearest 4-byte multiple.\n  uint32_t Length;\n\n  //! \\brief The encoding of the #Data field.\n  //!\n  //! If this field is `0`, #Data contains narrow or multibyte character data.\n  //! If this field is `1`, #Data is UTF-16-encoded.\n  //!\n  //! On Windows, with this field set to `0`, #Data will be encoded in the code\n  //! page of the system that linked the module. On other operating systems,\n  //! UTF-8 may be used.\n  uint8_t Unicode;\n\n  uint8_t Reserved[3];\n\n  //! \\brief The data carried within this structure.\n  //!\n  //! For string data, this field will be `NUL`-terminated. If #Unicode is `1`,\n  //! this field is UTF-16-encoded, and will be terminated by a UTF-16 `NUL`\n  //! code unit (two `NUL` bytes).\n  uint8_t Data[1];\n};\n\n//! \\anchor VER_NT_x\n//! \\name VER_NT_*\n//!\n//! \\brief Operating system type values for MINIDUMP_SYSTEM_INFO::ProductType.\n//!\n//! \\sa crashpad::MinidumpOSType\n//! \\{\n#define VER_NT_WORKSTATION 1\n#define VER_NT_DOMAIN_CONTROLLER 2\n#define VER_NT_SERVER 3\n//! \\}\n\n//! \\anchor VER_PLATFORM_x\n//! \\name VER_PLATFORM_*\n//!\n//! \\brief Operating system family values for MINIDUMP_SYSTEM_INFO::PlatformId.\n//!\n//! \\sa crashpad::MinidumpOS\n//! \\{\n#define VER_PLATFORM_WIN32s 0\n#define VER_PLATFORM_WIN32_WINDOWS 1\n#define VER_PLATFORM_WIN32_NT 2\n//! \\}\n\n#endif  // CRASHPAD_COMPAT_NON_WIN_WINNT_H_\n"
  },
  {
    "path": "compat/win/getopt.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_WIN_GETOPT_H_\n#define CRASHPAD_COMPAT_WIN_GETOPT_H_\n\n#include \"third_party/getopt/getopt.h\"\n\n#endif  // CRASHPAD_COMPAT_WIN_GETOPT_H_\n"
  },
  {
    "path": "compat/win/strings.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <strings.h>\n\n#include <string.h>\n\nextern \"C\" {\n\nint strcasecmp(const char* s1, const char* s2) {\n  return _stricmp(s1, s2);\n}\n\n}  // extern \"C\"\n"
  },
  {
    "path": "compat/win/strings.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_WIN_STRINGS_H_\n#define CRASHPAD_COMPAT_WIN_STRINGS_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint strcasecmp(const char* s1, const char* s2);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // CRASHPAD_COMPAT_WIN_STRINGS_H_\n"
  },
  {
    "path": "compat/win/sys/time.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_WIN_SYS_TIME_H_\n#define CRASHPAD_COMPAT_WIN_SYS_TIME_H_\n\n#include <winsock2.h>\n\n#endif  // CRASHPAD_COMPAT_WIN_SYS_TIME_H_\n"
  },
  {
    "path": "compat/win/sys/types.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_WIN_SYS_TYPES_H_\n#define CRASHPAD_COMPAT_WIN_SYS_TYPES_H_\n\n// This is intended to be roughly equivalent to #include_next.\n#include <../ucrt/sys/types.h>\n\n#include <stdint.h>\n\n#endif  // CRASHPAD_COMPAT_WIN_SYS_TYPES_H_\n"
  },
  {
    "path": "compat/win/time.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <time.h>\n\nextern \"C\" {\n\nstruct tm* gmtime_r(const time_t* timep, struct tm* result) {\n  if (gmtime_s(result, timep) != 0)\n    return nullptr;\n  return result;\n}\n\nstruct tm* localtime_r(const time_t* timep, struct tm* result) {\n  if (localtime_s(result, timep) != 0)\n    return nullptr;\n  return result;\n}\n\nconst char* strptime(const char* buf, const char* format, struct tm* tm) {\n  // TODO(scottmg): strptime implementation.\n  return nullptr;\n}\n\ntime_t timegm(struct tm* tm) {\n  return _mkgmtime(tm);\n}\n\n}  // extern \"C\"\n"
  },
  {
    "path": "compat/win/time.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_WIN_TIME_H_\n#define CRASHPAD_COMPAT_WIN_TIME_H_\n\n// This is intended to be roughly equivalent to #include_next.\n#include <../ucrt/time.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct tm* gmtime_r(const time_t* timep, struct tm* result);\n\nstruct tm* localtime_r(const time_t* timep, struct tm* result);\n\nconst char* strptime(const char* buf, const char* format, struct tm* tm);\n\ntime_t timegm(struct tm* tm);\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n\n#endif  // CRASHPAD_COMPAT_WIN_TIME_H_\n"
  },
  {
    "path": "compat/win/winbase.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_WIN_WINBASE_H_\n#define CRASHPAD_COMPAT_WIN_WINBASE_H_\n\n// include_next <winbase.h>\n#include <../um/winbase.h>\n\n// 10.0.15063.0 SDK\n\n#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE\n#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)\n#endif\n\n#endif  // CRASHPAD_COMPAT_WIN_WINBASE_H_\n"
  },
  {
    "path": "compat/win/winnt.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_WIN_WINNT_H_\n#define CRASHPAD_COMPAT_WIN_WINNT_H_\n\n// include_next <winnt.h>\n#include <../um/winnt.h>\n\n// https://msdn.microsoft.com/library/aa373184.aspx: \"Note that this structure\n// definition was accidentally omitted from WinNT.h.\"\nstruct PROCESSOR_POWER_INFORMATION {\n  ULONG Number;\n  ULONG MaxMhz;\n  ULONG CurrentMhz;\n  ULONG MhzLimit;\n  ULONG MaxIdleState;\n  ULONG CurrentIdleState;\n};\n\n// 10.0.10240.0 SDK\n\n#ifndef PROCESSOR_ARCHITECTURE_ARM64\n#define PROCESSOR_ARCHITECTURE_ARM64 12\n#endif\n\n#ifndef PF_ARM_V8_INSTRUCTIONS_AVAILABLE\n#define PF_ARM_V8_INSTRUCTIONS_AVAILABLE 29\n#endif\n\n#ifndef PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE\n#define PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE 30\n#endif\n\n#ifndef PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE\n#define PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE 31\n#endif\n\n#ifndef PF_RDTSCP_INSTRUCTION_AVAILABLE\n#define PF_RDTSCP_INSTRUCTION_AVAILABLE 32\n#endif\n\n// 10.0.14393.0 SDK\n\n#ifndef PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64\n#define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13\n#endif\n\n#endif  // CRASHPAD_COMPAT_WIN_WINNT_H_\n"
  },
  {
    "path": "compat/win/winternl.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_COMPAT_WIN_WINTERNL_H_\n#define CRASHPAD_COMPAT_WIN_WINTERNL_H_\n\n// include_next <winternl.h>\n#include <../um/winternl.h>\n\n// 10.0.16299.0 SDK\n\ntypedef struct _CLIENT_ID CLIENT_ID;\n\n#endif  // CRASHPAD_COMPAT_WIN_WINTERNL_H_\n"
  },
  {
    "path": "doc/.gitignore",
    "content": "/generated\n"
  },
  {
    "path": "doc/appengine/README",
    "content": "This is the App Engine app that serves https://crashpad.chromium.org/.\n\nTo work on this app, obtain the following packages:\n\n - Go, from https://golang.org/dl/. This is only necessary for local development\n   and testing. The directory containing the “go” executable, such as\n   /usr/local/go/bin, must appear in $PATH. It does not appear critical for the\n   Go version used to match the Go runtime version used (for example, these\n   instructions were tested with Go 1.14 locally but a Go 1.11 runtime when\n   deployed), but if problems are encountered, it would be wise to use the same\n   version for both local development and AppEngine deployment.\n - The Google Cloud SDK (gcloud CLI) from\n   https://cloud.google.com/sdk/docs/install-sdk. This is necessary for both\n   local development and for AppEngine deployment. Unpacking this package\n   produces a google-cloud-sdk directory, whose bin child directory may be\n   added to $PATH for convenience, although this is not strictly necessary.\n\nThe commands in this README are expected to be run from the directory containing\nit.\n\nTo test locally:\n\n% go get -d ./src/crashpad-home\n% python3 …/google-cloud-sdk/bin/dev_appserver.py src/crashpad-home\n\ndev_appserver.py must be invoked using Python 3, but internally will use Python\n2, and a Python 2 interpreter must be available in the PATH as python2.\n\nLook for the “Starting module \"default\" running at: http://localhost:8080” line,\nwhich tells you the URL of the local running instance of the app. Test\nhttp://localhost:8080/ to ensure that it works.\n\nIt would be good to test http://localhost:8080/doxygen as well, but it may fail\nwith HTTP status 500 and the following error returned as the HTTP response body\nbecause memcache seems to not be available in the local dev_appserver\nenvironment:\n\nservice bridge HTTP failed: Post \"http://appengine.googleapis.internal:10001/rpc_http\": dial tcp: lookup appengine.googleapis.internal: no such host\n\nThe /doxygen URL can be tested in a verison of the app that’s been deployed\nbefore traffic has been migrated to it by visiting the staged deployed version\nfrom the App Engine console.\n\nTo deploy:\n\n% version=$(git rev-parse --short=12 HEAD)\n% [[ -n \"$(git status --porcelain)\" ]] && version+=-dirty\n% …/google-cloud-sdk/bin/gcloud app deploy \\\n      --project=crashpad-home --version=\"${version}\" --no-promote \\\n      \"$(pwd)/src/crashpad-home\"\n\n(Note: the $(pwd) is necessary for “gcloud app deploy” to recognize that the\napplication is in GOPATH, putting it into “GOPATH mode”. This normally happens\ncorrectly on its own even with a relative path, but will fail for relative\npaths when $(pwd) is a symbolic link. Using an absolute path here will save you\nfrom this frustration, freeing you up to undoubtedly experience other\nfrustrations.)\n\nActivate a newly-deployed version by visiting the App Engine console at\nhttps://console.cloud.google.com/appengine/versions?project=crashpad-home,\nselecting it, and choosing “Migrate Traffic”. It is also possible to delete old\nversions from this page when they are no longer needed.\n"
  },
  {
    "path": "doc/appengine/go.mod",
    "content": "module src/crashpad-home\n\ngo 1.21.6\n\nrequire google.golang.org/appengine/v2 v2.0.5\n\nrequire (\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgoogle.golang.org/protobuf v1.30.0 // indirect\n)\n"
  },
  {
    "path": "doc/appengine/go.sum",
    "content": "github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine/v2 v2.0.5 h1:4C+F3Cd3L2nWEfSmFEZDPjQvDwL8T0YCeZBysZifP3k=\ngoogle.golang.org/appengine/v2 v2.0.5/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=\ngoogle.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\n"
  },
  {
    "path": "doc/appengine/src/crashpad-home/app.yaml",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nruntime: go121\napp_engine_apis: true\n\nhandlers:\n- url: /.*\n  script: auto\n  secure: always\n"
  },
  {
    "path": "doc/appengine/src/crashpad-home/main.go",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"google.golang.org/appengine/v2\"\n\t\"google.golang.org/appengine/v2/memcache\"\n\t\"google.golang.org/appengine/v2/urlfetch\"\n)\n\nfunc main() {\n\tappengine.Main()\n}\n\nfunc init() {\n\thttp.HandleFunc(\"/\", handler)\n}\n\nfunc handler(w http.ResponseWriter, r *http.Request) {\n\tconst (\n\t\tbaseURL             = \"https://chromium.googlesource.com/crashpad/crashpad/+/\"\n\t\tmainBaseURL         = baseURL + \"main/\"\n\t\tgeneratedDocBaseURL = baseURL + \"doc/doc/generated/?format=TEXT\"\n\t\tbugBaseURL          = \"https://bugs.chromium.org/p/crashpad/\"\n\t)\n\n\tredirectMap := map[string]string{\n\t\t\"/\":                                mainBaseURL + \"README.md\",\n\t\t\"/bug\":                             bugBaseURL,\n\t\t\"/bug/\":                            bugBaseURL,\n\t\t\"/bug/new\":                         bugBaseURL + \"issues/entry\",\n\t\t\"/doc/developing.html\":             mainBaseURL + \"/doc/developing.md\",\n\t\t\"/doc/status.html\":                 mainBaseURL + \"/doc/status.md\",\n\t\t\"/index.html\":                      mainBaseURL + \"README.md\",\n\t\t\"/man\":                             mainBaseURL + \"doc/man.md\",\n\t\t\"/man/\":                            mainBaseURL + \"doc/man.md\",\n\t\t\"/man/catch_exception_tool.html\":   mainBaseURL + \"tools/mac/catch_exception_tool.md\",\n\t\t\"/man/crashpad_database_util.html\": mainBaseURL + \"tools/crashpad_database_util.md\",\n\t\t\"/man/crashpad_handler.html\":       mainBaseURL + \"handler/crashpad_handler.md\",\n\t\t\"/man/exception_port_tool.html\":    mainBaseURL + \"tools/mac/exception_port_tool.md\",\n\t\t\"/man/generate_dump.html\":          mainBaseURL + \"tools/generate_dump.md\",\n\t\t\"/man/index.html\":                  mainBaseURL + \"doc/man.md\",\n\t\t\"/man/on_demand_service_tool.html\": mainBaseURL + \"tools/mac/on_demand_service_tool.md\",\n\t\t\"/man/run_with_crashpad.html\":      mainBaseURL + \"tools/run_with_crashpad.md\",\n\t}\n\n\tctx := appengine.NewContext(r)\n\tclient := urlfetch.Client(ctx)\n\n\tdestinationURL, exists := redirectMap[r.URL.Path]\n\tif exists {\n\t\thttp.Redirect(w, r, destinationURL, http.StatusFound)\n\t\treturn\n\t}\n\n\tif strings.HasPrefix(r.URL.Path, \"/bug/\") {\n\t\thttp.Redirect(w, r, bugBaseURL+\"issues/detail?id=\"+r.URL.Path[5:], http.StatusFound)\n\t\treturn\n\t}\n\n\t// Don’t show dotfiles.\n\tif strings.HasPrefix(path.Base(r.URL.Path), \".\") {\n\t\thttp.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)\n\t\treturn\n\t}\n\n\tu, err := url.Parse(generatedDocBaseURL)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tu.Path = path.Join(u.Path, r.URL.Path)\n\turlStr := u.String()\n\n\titem, err := memcache.Get(ctx, urlStr)\n\tif err == memcache.ErrCacheMiss {\n\t\tresp, err := client.Get(urlStr)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\tdefer resp.Body.Close()\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tw.WriteHeader(resp.StatusCode)\n\t\t\tfor k, v := range w.Header() {\n\t\t\t\tw.Header()[k] = v\n\t\t\t}\n\t\t\tio.Copy(w, resp.Body)\n\t\t\treturn\n\t\t}\n\n\t\t// Redirect directories to their index pages (/doc/ -> /doc/index.html).\n\t\tif resp.Header.Get(\"X-Gitiles-Object-Type\") == \"tree\" {\n\t\t\thttp.Redirect(w, r, path.Join(r.URL.Path, \"/index.html\"), http.StatusFound)\n\t\t\treturn\n\t\t}\n\n\t\tdecoder := base64.NewDecoder(base64.StdEncoding, resp.Body)\n\t\tb, err := ioutil.ReadAll(decoder)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t\titem = &memcache.Item{\n\t\t\tKey:        urlStr,\n\t\t\tValue:      b,\n\t\t\tExpiration: 1 * time.Hour,\n\t\t}\n\t\tif err := memcache.Set(ctx, item); err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t} else if err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", contentType(path.Base(u.Path)))\n\tfmt.Fprintf(w, \"%s\", item.Value)\n}\n\n// contentType returns the appropriate content type header for file.\nfunc contentType(file string) string {\n\tcontentTypes := map[string]string{\n\t\t\".css\":  \"text/css; charset=UTF-8\",\n\t\t\".html\": \"text/html; charset=UTF-8\",\n\t\t\".ico\":  \"image/x-icon\",\n\t\t\".js\":   \"text/javascript; charset=UTF-8\",\n\t\t\".png\":  \"image/png\",\n\t\t\".svg\":  \"image/svg+xml\",\n\t}\n\tfor suffix, typ := range contentTypes {\n\t\tif strings.HasSuffix(file, suffix) {\n\t\t\treturn typ\n\t\t}\n\t}\n\treturn \"text/plain; charset=UTF-8\"\n}\n"
  },
  {
    "path": "doc/developing.md",
    "content": "<!--\nCopyright 2015 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# Developing Crashpad\n\n## Status\n\n[Project status](status.md) information has moved to its own page.\n\n## Introduction\n\nCrashpad is a [Chromium project](https://www.chromium.org/Home). Most of its\ndevelopment practices follow Chromium’s. In order to function on its own in\nother projects, Crashpad uses\n[mini_chromium](https://chromium.googlesource.com/chromium/mini_chromium/), a\nsmall, self-contained library that provides many of Chromium’s useful low-level\nbase routines. [mini_chromium’s\nREADME](https://chromium.googlesource.com/chromium/mini_chromium/+/main/README.md)\nprovides more detail.\n\n## Prerequisites\n\nTo develop Crashpad, the following tools are necessary, and must be present in\nthe `$PATH` environment variable:\n\n * Appropriate development tools.\n    * On macOS, install [Xcode](https://developer.apple.com/xcode/). The latest\n      version is generally recommended.\n    * On Windows, install [Visual Studio](https://www.visualstudio.com/) with\n      C++ support and the Windows SDK. The latest version is generally\n      recommended. Some tests also require the CDB debugger, installed with\n      [Debugging Tools for\n      Windows](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/).\n    * On Linux, obtain appropriate tools for C++ development through any\n      appropriate means including the system’s package manager. On Debian and\n      Debian-based distributions, the `build-essential`, `zlib1g-dev`, and any\n      one of the `libcurl4-*-dev` packages such as `libcurl4-openssl-dev` should\n      suffice.\n * Chromium’s\n   [depot_tools](https://www.chromium.org/developers/how-tos/depottools).\n * [Git](https://git-scm.com/). This is provided by Xcode on macOS, by\n   depot_tools on Windows, and through any appropriate means including the\n   system’s package manager on Linux.\n * [Python](https://www.python.org/). This is provided by the operating system\n   on macOS, by depot_tools on Windows, and through any appropriate means\n   including the system’s package manager on Linux.\n\n## Getting the Source Code\n\nThe main source code repository is a Git repository hosted at\nhttps://chromium.googlesource.com/crashpad/crashpad. Although it is possible to\ncheck out this repository directly with `git clone`, Crashpad’s dependencies are\nmanaged by\n[`gclient`](https://www.chromium.org/developers/how-tos/depottools#TOC-gclient)\ninstead of Git submodules, so to work on Crashpad, it is best to use `fetch` to\nget the source code.\n\n`fetch` and `gclient` are part of the\n[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s\nno need to install them separately.\n\n### Initial Checkout\n\n```\n$ mkdir ~/crashpad\n$ cd ~/crashpad\n$ fetch crashpad\n```\n\n`fetch crashpad` performs the initial `git clone` and `gclient sync`,\nestablishing a fully-functional local checkout.\n\n### Subsequent Checkouts\n\n```\n$ cd ~/crashpad/crashpad\n$ git pull -r\n$ gclient sync\n```\n\n## Building\n\n### Windows, Mac, Linux, Fuchsia\n\nOn Windows, Mac, Linux, and Fuchsia, Crashpad uses\n[GN](https://gn.googlesource.com/gn) to generate\n[Ninja](https://ninja-build.org/) build files. For example,\n\n```\n$ cd ~/crashpad/crashpad\n$ gn gen out/Default\n$ ninja -C out/Default\n```\n\nYou can then use `gn args out/Default` or edit `out/Default/args.gn` to\nconfigure the build, for example things like `is_debug=true` or\n`target_cpu=\"x86\"`.\n\nGN and Ninja are part of the\n[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s\nno need to install them separately.\n\n#### Fuchsia\n\nIn order to instruct gclient to download the Fuchsia SDK, you need to add the\nfollowing to `~/crashpad/.gclient`:\n\n```\ntarget_os=[\"fuchsia\"]\n```\n\nIf you're using this tree to develop for multiple targets, you can also add\nother entries to the the list (e.g. `target_os=[\"fuchsia\", \"mac\"]`).\n\n#### Optional Linux Configs\n\nTo pull and use Crashpad's version of clang and sysroot, make the following\nchanges.\n\nAdd the following to `~/crashpad/.gclient`.\n```\n\"custom_vars\": { \"pull_linux_clang\": True },\n```\nAdd these args to `out/Default/args.gn`.\n```\nclang_path = \"//third_party/linux/clang/linux-amd64\"\ntarget_sysroot = \"//third_party/linux/sysroot\"\n```\n\n### Android\n\nThis build relies on cross-compilation. It’s possible to develop Crashpad for\nAndroid on any platform that the [Android NDK (Native Development\nKit)](https://developer.android.com/ndk/) runs on.\n\nIf it’s not already present on your system, [download the NDK package for your\nsystem](https://developer.android.com/ndk/downloads/) and expand it to a\nsuitable location. These instructions assume that it’s been expanded to\n`~/android-ndk-r21b`.\n\nNote that Chrome uses Android API level 21 for both 64-bit platforms and\n32-bit platforms. See Chrome’s\n[`build/config/android/config.gni`](https://chromium.googlesource.com/chromium/src/+/main/build/config/android/config.gni)\nwhich sets `android32_ndk_api_level` and `android64_ndk_api_level`.\n\nSet these gn args\n```\ntarget_os = \"android\"\nandroid_ndk_root = ~/android-ndk-r21b\nandroid_api_level = 21\n```\n\n## Testing\n\nCrashpad uses [Google Test](https://github.com/google/googletest/) as its\nunit-testing framework, and some tests use [Google\nMock](https://github.com/google/googletest/tree/master/googlemock/) as well. Its\ntests are currently split up into several test executables, each dedicated to\ntesting a different component. This may change in the future. After a successful\nbuild, the test executables will be found at `out/Debug/crashpad_*_test`.\n\n```\n$ cd ~/crashpad/crashpad\n$ out/Debug/crashpad_minidump_test\n$ out/Debug/crashpad_util_test\n```\n\nA script is provided to run all of Crashpad’s tests. It accepts a single\nargument, a path to the directory containing the test executables.\n\n```\n$ cd ~/crashpad/crashpad\n$ python build/run_tests.py out/Debug\n```\n\nTo run a subset of the tests, use the `--gtest_filter` flag, e.g., to run all\nthe tests for MinidumpStringWriter:\n\n```\n$ python build/run_tests.py out/Debug --gtest_filter MinidumpStringWriter\\*\n```\n\n### Windows\n\nOn Windows, `end_to_end_test.py` requires the CDB debugger, installed with\n[Debugging Tools for\nWindows](https://docs.microsoft.com/windows-hardware/drivers/debugger/). This\ncan be installed either as part of the [Windows Driver\nKit](https://docs.microsoft.com/windows-hardware/drivers/download-the-wdk) or\nthe [Windows\nSDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk/). If the\nWindows SDK has already been installed (possibly with Visual Studio) but\nDebugging Tools for Windows is not present, it can be installed from Add or\nremove programs→Windows Software Development Kit.\n\n### Android\n\nTo test on Android, [ADB (Android Debug\nBridge)](https://developer.android.com/studio/command-line/adb.html) from the\n[Android SDK](https://developer.android.com/sdk/) must be in the `PATH`. Note\nthat it is sufficient to install just the command-line tools from the Android\nSDK. The entire Android Studio IDE is not necessary to obtain ADB.\n\nWhen asked to test an Android build directory, `run_tests.py` will detect a\nsingle connected Android device (including an emulator). If multiple devices are\nconnected, one may be chosen explicitly with the `ANDROID_DEVICE` environment\nvariable. `run_tests.py` will upload test executables and data to a temporary\nlocation on the detected or selected device, run them, and clean up after itself\nwhen done.\n\n### Fuchsia\n\nTo test on Fuchsia, you need a connected device running Fuchsia. Run:\n\n```\n$ gn gen out/fuchsia --args 'target_os=\"fuchsia\" target_cpu=\"x64\" is_debug=true'\n$ ninja -C out/fuchsia\n$ python build/run_tests.py out/fuchsia\n```\n\nIf you have multiple devices running, you will need to specify which device you\nwant using their hostname, for instance:\n\n```\n$ ZIRCON_NODENAME=scare-brook-skip-dried python build/run_tests.py out/fuchsia\n```\n\n## Contributing\n\nCrashpad’s contribution process is very similar to [Chromium’s contribution\nprocess](https://chromium.googlesource.com/chromium/src/+/main/docs/contributing.md).\n\n### Code Review\n\nA code review must be conducted for every change to Crashpad’s source code. Code\nreview is conducted on [Chromium’s\nGerrit](https://chromium-review.googlesource.com/) system, and all code reviews\nmust be sent to an appropriate reviewer, with a Cc sent to\n[crashpad-dev](https://groups.google.com/a/chromium.org/group/crashpad-dev). The\n[`codereview.settings`](https://chromium.googlesource.com/crashpad/crashpad/+/main/codereview.settings)\nfile specifies this environment to `git-cl`.\n\n`git-cl` is part of the\n[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s\nno need to install it separately.\n\n```\n$ cd ~/crashpad/crashpad\n$ git checkout -b work_branch origin/main\n…do some work…\n$ git add …\n$ git commit\n$ git cl upload\n```\n\nUploading a patch to Gerrit does not automatically request a review. You must\nselect a reviewer on the Gerrit review page after running `git cl upload`. This\naction notifies your reviewer of the code review request. If you have lost track\nof the review page, `git cl issue` will remind you of its URL. Alternatively,\nyou can request review when uploading to Gerrit by using `git cl upload\n--send-mail`.\n\nGit branches maintain their association with Gerrit reviews, so if you need to\nmake changes based on review feedback, you can do so on the correct Git branch,\ncommitting your changes locally with `git commit`. You can then upload a new\npatch set with `git cl upload` and let your reviewer know you’ve addressed the\nfeedback.\n\nThe most recently uploaded patch set on a review may be tested on a\n[trybot](https://chromium.googlesource.com/chromium/src/+/main/docs/infra/trybot_usage.md)\nby running `git cl try` or by clicking the “CQ Dry Run” button in Gerrit. These\nset the “Commit-Queue: +1” label. This does not mean that the patch will be\ncommitted, but the trybot and commit queue share infrastructure and a Gerrit\nlabel. The patch will be tested on trybots in a variety of configurations.\nStatus information will be available on Gerrit. Trybot access is available to\nCrashpad and Chromium committers.\n\n### Landing Changes\n\nAfter code review is complete and “Code-Review: +1” has been received from all\nreviewers, the patch can be submitted to Crashpad’s [commit\nqueue](https://chromium.googlesource.com/chromium/src/+/main/docs/infra/cq.md)\nby clicking the “Submit to CQ” button in Gerrit. This sets the “Commit-Queue:\n+2” label, which tests the patch on trybots before landing it. Commit queue\naccess is available to Crashpad and Chromium committers.\n\nAlthough the commit queue is recommended, if needed, project members can bypass\nthe commit queue and land patches without testing by using the “Submit” button\nin Gerrit or by committing via `git cl land`:\n\n```\n$ cd ~/crashpad/crashpad\n$ git checkout work_branch\n$ git cl land\n```\n\n### External Contributions\n\nCopyright holders must complete the [Individual Contributor License\nAgreement](https://cla.developers.google.com/about/google-individual) or\n[Corporate Contributor License\nAgreement](https://cla.developers.google.com/about/google-corporate) as\nappropriate before any submission can be accepted, and must be listed in the\n[`AUTHORS`](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS)\nfile. Contributors may be listed in the\n[`CONTRIBUTORS`](https://chromium.googlesource.com/crashpad/crashpad/+/main/CONTRIBUTORS)\nfile.\n\n## Buildbot\n\nThe [Crashpad Buildbot](https://ci.chromium.org/p/crashpad/g/main/console)\nperforms automated builds and tests of Crashpad. Before checking out or updating\nthe Crashpad source code, and after checking in a new change, it is prudent to\ncheck the Buildbot to ensure that “the tree is green.”\n"
  },
  {
    "path": "doc/ios_overview_design.md",
    "content": "<!--\nCopyright 2021 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# iOS Crashpad Overview Design\n\n[TOC]\n\n## iOS Limitations\n\nCrashpad on other platforms captures exceptions out-of-process. The iOS sandbox,\nhowever, restricts applications from delegating work to separate processes.\nThis limitation means Crashpad on iOS must combine the work of the handler and\nthe client into the same process as the main application.\n\n## The Crashpad In-Process Handler\n\nIn-process handling comes with a number of limitations and difficulties. It is\nnot possible to catch the specific Mach exception `EXC_CRASH`, so certain groups\nof crashes cannot be captured. This includes some major ones, like out-of-memory\ncrashes. This also introduces difficulties in capturing all the relevant crash\ndata and writing the minidump, as the process itself is in an unsafe state.\n\nWhile handling an exception, the handler may not, for example:\n\n - Allocate memory.\n - Use libc, or most any library call.\n\nWhile handling an exception, the handler may only:\n\n - Use audited syscalls.\n - access memory via `vm_read`.\n\nIn conjunction with Crashpad’s existing minidump writer and structural\nlimitations of the minidump format, it is not possible to write a minidump\nimmediately from the crash handler. Instead, an intermediate dump is written\nwhen a handler would normally write a minidump (such as during an exception or a\nforced dump without crashing). The intermediate dump file will be converted to\na minidump on the next run (or when the application decides it's safe to do so).\n\nDuring Crashpad initialization, the handler gathers basic system information\nand opens a pending intermediate dump adjacent to the Crashpad database.\n\n## The Crashpad IntermediateDump Format\n\nDue to the limitations of in-process handling, an intermediate dump file is\nwritten during exceptions. The data is streamed to a file, which will be used to\ngenerate a final minidump when appropriate.\n\nThe file format is similar to binary JSON, supporting keyed properties, maps and\narrays.\n\n - `Property` [key:int, length:int, value:intarray]\n - `StartMap` [key:int], followed by repeating Properties until `EndMap`\n - `StartArray` [key:int], followed by repeating Maps until `EndArray`\n - `EndMap`, `EndArray`, `EndDocument`\n\nSimilar to JSON, maps can contain other maps, arrays and properties.\n\n## The life of an iOS crash report\n\nImmediately upon calling StartCrashpadInProcessHandler, the iOS in-process\nhandler is installed. This will open a temporary file within the database\ndirectory, in a subdirectory named `pending-serialized-ios-dump`. This file will\nbe used to write an intermediate dump in the event of a crash. This must happen\nbefore installing the various types of crash handlers, as each depends on having\na valid handler with an intermediate dump ready to be written to.\n\nAfter the in-process handler is initialized, the Mach exception, POSIX signal\nand Objective-C exception preprocessor handlers are installed.\n\n### Intermediate Dump File Locking\n\nIt is expected that multiple Crashpad clients may share the same database\ndirectory, and this directory may be inside an iOS app group directory. While\nit's possible for each Crashpad client to write to its own private directory,\nif a shared directory is used, it's possible for different applications to\nupload a crash report from any application in a shared group. This might be\nused, for example, by an application and its various app extensions, where each\nclient may generate a crash report but only the main application uploads\nreports. Alternatively, a suite of applications may upload each other's crash\nreports. Otherwise, the only opportunity to upload a report would be when a\nspecific app that crashed relaunches.\n\nTo prevent multiple clients from processing a pending intermediate dump, files\nmust be locked. However, POSIX locks on app group files will trigger app\ntermination on app backgrounding, so a custom file locking protocol is used.\nLocked temporary files are named `<bundle-id>@<uuid>.locked`. The `.locked`\nextension is removed when the file is unlocked. The `bundle-id` is used to\ndetermine which Crashpad clients can process leftover locked files.\n\n### Writing Crashes to Intermediate Dumps\n\nWhen an app encounters a crash (via a Mach exception, Objective-C exception, or\na POSIX signal), an intermediate dump is written to the temporary locked file,\nthe .locked extension is removed, and a new temporary locked file is opened.\n\nApp terminations not handled by Crashpad will leave behind a temporary\nlocked file, to be cleaned up on next launch. These files are still processed,\nbecause it is possible for the app to be terminated while writing an\nintermediate dump, and if enough data is written this may still be valuable.\n\nNote: Generally iOS apps are single-process, so it's safe for the client to\nconsider any files matching its `bundle-id`, but there are edge-cases (such as\nif a share-to app extension is opened at the same time in two different apps) so\nold locked files won't be cleared until after 24 hours. Any locked file found\nafter 60 days is unlocked regardless of `bundle-id`.\n\n### Writing to Intermediate Dumps without a Crash\n\nApps may also generate intermediate dumps without a crash, often used for\ndebugging. Chromium makes heavy use of this for detecting main thread hangs,\nsomething that can appear as a crash for the user, but is uncatchable for crash\nhandlers like Crashpad. When an app requests this (via DumpWithoutCrash,\nDumpWithoutCrashAndDeferProcessing), an intermediate dump is written to the\ntemporary locked file, the .locked extension is removed, and a new temporary\nlocked file is opened.\n\nNote: DumpWithoutCrashAndDeferProcessingAtPath writes an intermediate dump to\nthe requested location, not the previously opened temporary file. This is useful\nbecause Chromium's main thread hang detection will throw away hang reports in\ncertain circumstances (if the app recovers, if a different crash report is\nwritten, etc).\n\n## The Crashpad In-Process Client\n\nOther Crashpad platforms handle exceptions and upload minidumps out-of-process.\nOn iOS, everything must happen in-process. Once started, the client will\nautomatically handle exceptions and capture the crashed process state in an\nintermediate dump file. Converting that intermediate dump file into a minidump\nis likely not safe to do from within a crashed process, and uploading a minidump\nis definitely unsafe to do at crash time. Applications are expected to process\nintermediate dumps into pending minidumps and begin processing pending\nminidumps, possibly for upload, at suitable times following the next application\nrestart.\n\nNote: Applications are not required to call either of these methods. For\nexample, application extensions may choose to generate dumps but leave\nprocessing and uploading to the main applications. Clients that share the\nsame database directory between apps can take advantage of processing and\nuploading crash reports from different applications.\n\n### `ProcessIntermediateDumps`\nFor performance and stability reasons applications may choose the correct time\nto convert intermediate dumps, as well as append metadata to the pending\nintermediate dumps. This is expected to happen during application startup, when\nsuitable. After converting, a minidump will be written to the Crashpad database,\nsimilar to how other platforms write a minidump on exception handling. If\nuploading is enabled, this minidump will also be immediately uploaded. New\nintermediate dumps generated by exceptions or by\n`CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING` will not be processed until\nthe next call to `ProcessIntermediateDumps`. Conversely,\n`CRASHPAD_SIMULATE_CRASH` can be called when the client has no performance or\nstability concerns. In this case, intermediate dumps are automatically\nconverted to minidumps and immediately eligible for uploading.\n\nApplications can include annotations here as well. Chromium uses this for its\ninsta-crash logic, which detects if an app is crashing repeatedly on startup.\n\n### `StartProcessingPendingReports`\nFor similar reasons, applications may choose the correct time to begin uploading\npending reports, such as when ideal network conditions exist. By default,\nclients start with uploading disabled. Applications should call this API when\nit is determined that it is appropriate to do so (such as on a few seconds after\nstartup, or when network connectivity is appropriate).\n\n## tvOS considerations\n\ntvOS, an operating system based on iOS, shares the constraints described above\nand uses the same architecture. Additionally, tvOS does not allow the use of\nthe `mach_msg()` APIs so using Mach exceptions is impossible.\n\nConsequently, the Mach exception handler is _not_ installed once the in-process\nhandler is initialized; the tvOS handler only uses the POSIX signals and\nObjective-C exception preprocessors. Furthermore, the POSIX signal handler is\nunable to handle all crashes that the Mach exception handler can (e.g. stack\noverflows, as `sigaltstack()` cannot be used either). In other words, the tvOS\nin-process handler can only handle a subset of all exceptions that the iOS\nin-process handler does.\n"
  },
  {
    "path": "doc/man.md",
    "content": "<!--\nCopyright 2016 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# Man Pages\n\n## Section 1: User Commands\n\n * [crashpad_database_util](../tools/crashpad_database_util.md)\n * [crashpad_http_upload](../tools/crashpad_http_upload.md)\n * [generate_dump](../tools/generate_dump.md)\n\n### macOS-Specific\n\n * [catch_exception_tool](../tools/mac/catch_exception_tool.md)\n * [exception_port_tool](../tools/mac/exception_port_tool.md)\n * [on_demand_service_tool](../tools/mac/on_demand_service_tool.md)\n * [run_with_crashpad](../tools/run_with_crashpad.md)\n\n## Section 8: Dӕmons\n\n * [crashpad_handler](../handler/crashpad_handler.md)\n"
  },
  {
    "path": "doc/overview_design.md",
    "content": "<!--\nCopyright 2017 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# Crashpad Overview Design\n\n[TOC]\n\n## Objective\n\nCrashpad is a library for capturing, storing and transmitting postmortem crash\nreports from a client to an upstream collection server. Crashpad aims to make it\npossible for clients to capture process state at the time of crash with the best\npossible fidelity and coverage, with the minimum of fuss.\n\nCrashpad also provides a facility for clients to capture dumps of process state\non-demand for diagnostic purposes.\n\nCrashpad additionally provides minimal facilities for clients to adorn their\ncrashes with application-specific metadata in the form of per-process key/value\npairs. More sophisticated clients are able to adorn crash reports further\nthrough extensibility points that allow the embedder to augment the crash report\nwith application-specific metadata.\n\n## Background\n\nIt’s an unfortunate truth that any large piece of software will contain bugs\nthat will cause it to occasionally crash. Even in the absence of bugs, software\nincompatibilities can cause program instability.\n\nFixing bugs and incompatibilities in client software that ships to millions of\nusers around the world is a daunting task. User reports and manual reproduction\nof crashes can work, but even given a user report, often times the problem is\nnot readily reproducible. This is for various reasons, such as e.g. system\nversion or third-party software incompatibility, or the problem can happen due\nto a race of some sort. Users are also unlikely to report problems they\nencounter, and user reports are often of poor quality, as unfortunately most\nusers don’t have experience with making good bug reports.\n\nAutomatic crash telemetry has been the best solution to the problem so far, as\nthis relieves the burden of manual reporting from users, while capturing the\nhardware and software state at the time of crash.\n\nTODO(siggi): examples of this?\n\nCrash telemetry involves capturing postmortem crash dumps and transmitting them\nto a backend collection server. On the server they can be stackwalked and\nsymbolized, and evaluated and aggregated in various ways. Stackwalking and\nsymbolizing the reports on an upstream server has several benefits over\nperforming these tasks on the client. High-fidelity stackwalking requires access\nto bulky unwind data, and it may be desirable to not ship this to end users out\nof concern for the application size. The process of symbolization requires\naccess to debugging symbols, which can be quite large, and the symbolization\nprocess can consume considerable other resources. Transmitting un-stackwalked\nand un-symbolized postmortem dumps to the collection server also allows deep\nanalysis of individual dumps, which is often necessary to resolve the bug\ncausing the crash.\n\nTransmitting reports to the collection server allows aggregating crashes by\ncause, which in turn allows assessing the importance of different crashes in\nterms of the occurrence rate and e.g. the potential security impact.\n\nA postmortem crash dump must contain the program state at the time of crash\nwith sufficient fidelity to allow diagnosing and fixing the problem. As the full\nprogram state is usually too large to transmit to an upstream server, the\npostmortem dump captures a heuristic subset of the full state.\n\nThe crashed program is in an indeterminate state and, in fact, has often crashed\nbecause of corrupt global state - such as heap. It’s therefore important to\ngenerate crash reports with as little execution in the crashed process as\npossible. Different operating systems vary in the facilities they provide for\nthis.\n\n## Overview\n\nCrashpad is a client-side library that focuses on capturing machine and program\nstate in a postmortem crash report, and transmitting this report to a backend\nserver - a “collection server”. The Crashpad library is embedded by the client\napplication. Conceptually, Crashpad breaks down into the handler and the client.\nThe handler runs in a separate process from the client or clients. It is\nresponsible for snapshotting the crashing client process’ state on a crash,\nsaving it to a crash dump, and transmitting the crash dump to an upstream\nserver. Clients register with the handler to allow it to capture and upload\ntheir crashes. On iOS, there is no separate process for the handler.\n[This is a limitation of iOS.](ios_overview_design.md#ios-limitations)\n\n### The Crashpad handler\n\nThe Crashpad handler is instantiated in a process supplied by the embedding\napplication. It provides means for clients to register themselves by some means\nof IPC, or where operating system support is available, by taking advantage of\nsuch support to cause crash notifications to be delivered to the handler. On\ncrash, the handler snapshots the crashed client process’ state, writes it to a\npostmortem dump in a database, and may also transmit the dump to an upstream\nserver if so configured.\n\nThe Crashpad handler is able to handle cross-bitted requests and generate crash\ndumps across bitness, where e.g. the handler is a 64-bit process while the\nclient is a 32-bit process or vice versa. In the case of Windows, this is\nlimited by the OS such that a 32-bit handler can only generate crash dumps for\n32-bit clients, but a 64-bit handler can acquire nearly all of the detail for a\n32-bit process.\n\n### The Crashpad client\n\nThe Crashpad client provides two main facilities.\n1. Registration with the Crashpad handler.\n2. Metadata communication to the Crashpad handler on crash.\n\nA Crashpad embedder links the Crashpad client library into one or more\nexecutables, whether a loadable library or a program file. The client process\nthen registers with the Crashpad handler through some mode of IPC or other\noperating system-specific support.\n\nOn crash, metadata is communicated to the Crashpad handler via the CrashpadInfo\nstructure. Each client executable module linking the Crashpad client library\nembeds a CrashpadInfo structure, which can be updated by the client with\nwhatever state the client wishes to record with a crash.\n\n![Overview image](overview.png)\n\nHere is an overview picture of the conceptual relationships between embedder (in\nlight blue), client modules (darker blue), and Crashpad (in green). Note that\nmultiple client modules can contain a CrashpadInfo structure, but only one\nregistration is necessary.\n\n## Detailed Design\n\n### Requirements\n\nThe purpose of Crashpad is to capture machine, OS and application state in\nsufficient detail and fidelity to allow developers to diagnose and, where\npossible, fix the issue causing the crash.\n\nEach distinct crash report is assigned a globally unique ID, in order to allow\nusers to associate them with a user report, report in bug reports and so on.\n\nIt’s critical to safeguard the user’s privacy by ensuring that no crash report\nis ever uploaded without user consent. Likewise it’s important to ensure that\nCrashpad never captures or uploads reports from non-client processes.\n\n### Concepts\n\n* **Client ID**. A UUID tied to a single instance of a Crashpad database. When\n  creating a crash report, the Crashpad handler includes the client ID stored\n  in the database. This provides a means to determine how many individual end\n  users are affected by a specific crash signature.\n\n* **Crash ID**. A UUID representing a single crash report. Uploaded crash\n  reports also receive a “server ID.” The Crashpad database indexes both the\n  locally-generated and server-generated IDs.\n\n* **Collection Server**. See [crash server documentation.](\n  https://goto.google.com/crash-server-overview)\n\n* **Client Process**. Any process that has registered with a Crashpad handler.\n\n* **Handler process**. A process hosting the Crashpad handler library. This may\n  be a dedicated executable, or it may be hosted within a client executable\n  with control passed to it based on special signaling under the client’s\n  control, such as a command-line parameter.\n\n* **CrashpadInfo**. A structure used by client modules to provide information to\n  the handler.\n\n* **Annotations**. Each CrashpadInfo structure points to a dictionary of\n  {string, string} annotations that the client can use to communicate\n  application state in the case of crash.\n\n* **Database**. The Crashpad database contains persistent client settings as\n  well as crash dumps pending upload.\n\nTODO(siggi): moar concepts?\n\n### Overview Picture\n\nHere is a rough overview picture of the various Crashpad constructs, their\nlayering and intended use by clients.\n\n![Layering image](layering.png)\n\nDark blue boxes are interfaces, light blue boxes are implementation. Gray is the\nembedding client application. Note that wherever possible, implementation that\nnecessarily has to be OS-specific, exposes OS-agnostic interfaces to the rest of\nCrashpad and the client.\n\n### Registration\n\nThe particulars of how a client registers with the handler varies across\noperating systems.\n\n#### macOS\n\nAt registration time, the client designates a Mach port monitored by the\nCrashpad handler as the EXC_CRASH exception port for the client. The port may be\nacquired by launching a new handler process or by retrieving service already\nregistered with the system. The registration is maintained by the kernel and is\ninherited by subprocesses at creation time by default, so only the topmost\nprocess of a process tree need register.\n\nCrashpad provides a facility for a process to disassociate (unregister) with an\nexisting crash handler, which can be necessary when an older client spawns an\nupdated version.\n\n#### iOS\n\niOS registers both a signal handler for `SIGABRT` and a Mach exception handler\nwith a subset of available exceptions. [This is a limitation of\niOS.](ios_overview_design.md#ios-limitations)\n\n#### Windows\n\nThere are two modes of registration on Windows. In both cases the handler is\nadvised of the address of a set of structures in the client process’ address\nspace. These structures include a pair of ExceptionInformation structs, one for\ngenerating a postmortem dump for a crashing process, and another one for\ngenerating a dump for a non- crashing process.\n\n##### Normal registration\n\nIn the normal registration mode, the client connects to a named pipe by a\npre-arranged name. A registration request is written to the pipe. During\nregistration, the handler creates a set of events, duplicates them to the\nregistering client, then returns the handle values in the registration response.\nThis is a blocking process.\n\n##### Initial Handler Creation\n\nIn order to avoid blocking client startup for the creation and initialization of\nthe handler, a different mode of registration can be used for the handler\ncreation. In this mode, the client creates a set of event handles and inherits\nthem into the newly created handler process. The handler process is advised of\nthe handle values and the location of the ExceptionInformation structures by way\nof command line arguments in this mode.\n\n#### Linux/Android\n\nOn Linux, a registration is a connected socket pair between a client process and\nthe Crashpad handler. This socket pair may be private or shared among many\nclient processes.\n\n##### Private Connections\n\nPrivate connections are the default registration mode when starting the handler\nprocess in response to a crash or on behalf of another client. This mode is\nrequired to use a ptrace broker, which is in turn required to trace Android\nisolated processes.\n\n##### Shared Connections\n\nShared connections are the default mode when using a long-lived handler. The\nsame connected socket pair may be shared among any number of clients. The socket\npair is created by the first process to start the handler at which point the\nclient socket end may be shared with other clients by any convenient means (e.g.\ninheritance).\n\n### Capturing Exceptions\n\nThe details of how Crashpad captures the exceptions leading to crashes varies\nbetween operating systems.\n\n#### macOS\n\nOn macOS, the operating system will notify the handler of client crashes via the\nMach port set as the client process’ exception port. As exceptions are\ndispatched to the Mach port by the kernel, on macOS, exceptions can be handled\nentirely from the Crashpad handler without the need to run any code in the crash\nprocess at the time of the exception.\n\n#### iOS\n\nOn iOS, the operating system will notify the handler of crashes via the Mach\nexception port or the signal handler. As exceptions are handled in-process, an\nintermediate dump file is generated rather than a minidump. See more\ninformation about the [iOS in-process\nhandler.](ios_overview_design.md#ios-in-process-handler)\n\n#### Windows\n\nOn Windows, the OS dispatches exceptions in the context of the crashing thread.\nTo notify the handler of exceptions, the Crashpad client registers an\nUnhandledExceptionFilter (UEF) in the client process. When an exception trickles\nup to the UEF, it stores the exception information and the crashing thread’s ID\nin the ExceptionInformation structure registered with the handler. It then sets\nan event handle to signal the handler to go ahead and process the exception.\n\n##### Caveats\n\n* If the crashing thread’s stack is smashed when an exception occurs, the\n  exception cannot be dispatched. In this case the OS will summarily terminate\n  the process, without the handler having an opportunity to generate a crash\n  report.\n* If an exception is handled in the crashing thread, it will never propagate\n  to the UEF, and thus a crash report won’t be generated. This happens a fair\n  bit in Windows as system libraries will often dispatch callbacks under a\n  structured exception handler. This occurs during Window message dispatching\n  on some system configurations, as well as during e.g. DLL entry point\n  notifications.\n* A growing number of conditions in the system and runtime exist where\n  detected corruption or illegal calls result in summary termination of the\n  process, in which case no crash report will be generated.\n\n###### Out-Of-Process Exception Handling\n\nThere exists a mechanism in Windows Error Reporting (WER) that allows a client\nprocess to register for handling client exceptions out of the crashing process.\nUnfortunately this mechanism is difficult to use, and doesn’t provide coverage\nfor many of the caveats above. [Details\nhere.](https://crashpad.chromium.org/bug/133)\n\n#### Linux/Android\n\nOn Linux, exceptions are dispatched as signals to the crashing thread. Crashpad\nsignal handlers will send a message over the socket to the Crashpad handler\nnotifying it of the crash and the location of exception information to be read\nfrom the crashing process. When using a shared socket connection, communication\nis entirely one-way. The client sends its dump request to the handler and then\nwaits until the handler responds with a SIGCONT or a timeout occurs. When using\na private socket connection, the handler may respond over the socket to\ncommunicate with a ptrace broker process. The broker is forked from the crashing\nprocess, executes ptrace requests against the crashing process, and sends the\ninformation over the socket to the handler.\n\n### The CrashpadInfo structure\n\nThe CrashpadInfo structure is used to communicate information from the client to\nthe handler. Each executable module in a client process can contain a\nCrashpadInfo structure. On a crash, the handler crawls all modules in the\ncrashing process to locate all CrashpadInfo structures present. The CrashpadInfo\nstructures are linked into a special, named section of the executable, where the\nhandler can readily find them.\n\nThe CrashpadInfo structure has a magic signature, and contains a size and a\nversion field. The intent is to allow backwards compatibility from older client\nmodules to newer handler. It may also be necessary to provide forwards\ncompatibility from newer clients to older handler, though this hasn’t occurred\nyet.\n\nThe CrashpadInfo structure contains such properties as the cap for how much\nmemory to include in the crash dump, some tristate flags for controlling the\nhandler’s behavior, a pointer to an annotation dictionary and so on.\n\n### Snapshot\n\nSnapshot is a layer of interfaces that represent the machine and OS entities\nthat Crashpad cares about. Different concrete implementations of snapshot can\nthen be backed different ways, such as e.g. from the in-memory representation of\na crashed process, or e.g. from the contents of a minidump.\n\n### Crash Dump Creation\n\nTo create a crash dump, a subset of the machine, OS and application state is\ngrabbed from the crashed process into an in-memory snapshot structure in the\nhandler process. Since the full application state is typically too large for\ncapturing to disk and transmitting to an upstream server, the snapshot contains\na heuristically selected subset of the full state.\n\nThe precise details of what’s captured varies between operating systems, but\ngenerally includes the following\n* The set of modules (executable, shared libraries) that are loaded into the\n  crashing process.\n* An enumeration of the threads running in the crashing process, including the\n  register contents and the contents of stack memory of each thread.\n* A selection of the OS-related state of the process, such as e.g. the command\n  line, environment and so on.\n* A selection of memory potentially referenced from registers and from stack.\n\nTo capture a crash dump, the crashing process is first suspended, then a\nsnapshot is created in the handler process. The snapshot includes the\nCrashpadInfo structures of the modules loaded into the process, and the contents\nof those is used to control the level of detail captured for the crash dump.\n\nOnce the snapshot has been constructed, it is then written to a minidump file,\nwhich is added to the database. The process is un-suspended after the minidump\nfile has been written. In the case of a crash (as opposed to a client request to\nproduce a dump without crashing), it is then either killed by the operating\nsystem or the Crashpad handler.\n\nIn general the snapshotting process has to be very intimate with the operating\nsystem it’s working with, so there will be a set of concrete implementation\nclasses, many deriving from the snapshot interfaces, doing this for each\noperating system.\n\n### Minidump\n\nThe minidump implementation is responsible for writing a snapshot to a\nserialized on-disk file in the minidump format. The minidump implementation is\nOS-agnostic, as it works on an OS-agnostic Snapshot interface.\n\nTODO(siggi): Talk about two-phase writes and contents ordering here.\n\n### Database\n\nThe Crashpad database contains persistent client settings, including a unique\ncrash client identifier and the upload-enabled bit. Note that the crash client\nidentifier is assigned by Crashpad, and is distinct from any identifiers the\nclient application uses to identify users, installs, machines or such - if any.\nThe expectation is that the client application will manage the user’s upload\nconsent, and inform Crashpad of changes in consent.\n\nThe unique client identifier is set at the time of database creation. It is then\nrecorded into every crash report collected by the handler and communicated to\nthe upstream server.\n\nThe database stores a configurable number of recorded crash dumps to a\nconfigurable maximum aggregate size. For each crash dump it stores annotations\nrelating to whether the crash dumps have been uploaded. For successfully\nuploaded crash dumps it also stores their server-assigned ID.\n\nThe database consists of a settings file, named \"settings.dat\" with binary\ncontents (see crashpad::Settings::Data for the file format), as well as\ndirectory containing the crash dumps. Additionally each crash dump is adorned\nwith properties relating to the state of the dump for upload and such. The\ndetails of how these properties are stored vary between platforms.\n\n#### macOS\n\nThe macOS implementation simply stores database properties on the minidump files\nin filesystem extended attributes.\n\n#### iOS\n\nThe iOS implementation also stores database properties of minidump files in\nfilesystem extended attributes. Separate from the database, iOS also stores its\nintermediate dump files adjacent to the database. See more information about\n[iOS intermediate\ndumps.](ios_overview_design.md#the-crashpad-intermediatedump-format)\n\n#### Windows\n\nThe Windows implementation stores database properties in a binary file named\n“metadata” at the top level of the database directory.\n\n### Report Format\n\nCrash reports are recorded in the Windows minidump format with\nextensions to support Crashpad additions, such as e.g. Annotations.\n\n### Upload to collection server\n\n#### Wire Format\n\nFor the time being, Crashpad uses the Breakpad wire protocol, which is\nessentially a MIME multipart message communicated over HTTP(S). To support this,\nthe annotations from all the CrashpadInfo structures found in the crashing\nprocess are merged to create the Breakpad “crash keys” as form data. The\npostmortem minidump is then attached as an “application/octet- stream”\nattachment with the name “upload_file_minidump”. The entirety of the request\nbody, including the minidump, can be gzip-compressed to reduce transmission time\nand increase transmission reliability. Note that by convention there is a set of\n“crash keys” that are used to communicate the product, version, client ID and\nother relevant data about the client, to the server. Crashpad normally stores\nthese values in the minidump file itself, but retrieves them from the minidump\nand supplies them as form data for compatibility with the Breakpad-style server.\n\nThis is a temporary compatibility measure to allow the current Breakpad-based\nupstream server to handle Crashpad reports. In the fullness of time, the wire\nprotocol is expected to change to remove this redundant transmission and\nprocessing of the Annotations.\n\n#### Transport\n\nThe embedding client controls the URL of the collection server by the command\nline passed to the handler. The handler can upload crashes with HTTP or HTTPS,\ndepending on client’s preference. It’s strongly suggested use HTTPS transport\nfor crash uploads to protect the user’s privacy against man-in-the-middle\nsnoopers.\n\nTODO(mmentovai): Certificate pinning.\n\n#### Throttling & Retry Strategy\n\nTo protect both the collection server from DDoS as well as to protect the\nclients from unreasonable data transfer demands, the handler implements a\nclient-side throttling strategy. At the moment, the strategy is very simplistic,\nit simply limits uploads to one upload per hour, and failed uploads are aborted.\n\nAn experiment has been conducted to lift all throttling. Analysis on the\naggregate data this produced shows that multiple crashes within a short timespan\non the same client are nearly always due to the same cause. Therefore there is\nvery little loss of signal due to the throttling, though the ability to\nreconstruct at least the full crash count is highly desirable.\n\nThe lack of retry is expected to [change\nsoon](https://crashpad.chromium.org/bug/23), as this creates blind spots for\nclient crashes that exclusively occur on e.g. network down events, during\nsuspend and resume and such.\n\n### Extensibility\n\n#### Client Extensibility\n\nClients are able to extend the generated crash reports in two ways, by\nmanipulating their CrashpadInfo structure.\nThe two extensibility points are:\n1. Nominating a set of address ranges for inclusion in the crash report.\n2. Adding user-defined minidump streams for inclusion in the crash report.\n\nIn both cases the CrashpadInfo structure has to be updated before a crash\noccurs.\n\n##### Embedder Extensibility\n\nAdditionally, embedders of the handler can provide \"user stream data source\"\ninstances to the handler's main function. Any time a minidump is written, these\ninstances get called.\n\nEach data source may contribute a custom stream to the minidump, which can be\ncomputed from e.g. system or application state relevant to the crash.\n\nAs a case in point, it can be handy to know whether the system was under memory\nor other resource duress at the time of crash.\n\n### Dependencies\n\nAside from system headers and APIs, when used outside of Chromium, Crashpad has\na dependency on “mini_chromium”, which is a subset of the Chromium base library.\nThis is to allow non-Chromium clients to use Crashpad, without taking a direct\ndependency on the Chromium base, while allowing Chromium projects to use\nCrashpad with minimum code duplication or hassle. When using Crashpad as part of\nChromium, Chromium’s own copy of the base library is used instead of\nmini_chromium.\n\nThe downside to this is that mini_chromium must be kept up to date with\ninterface and implementation changes in Chromium base, for the subset of\nfunctionality used by Crashpad.\n\n## Caveats\n\nTODO(anyone): You may need to describe what you did not do or why simpler\napproaches don't work. Mention other things to watch out for (if any).\n\n## Security Considerations\n\nCrashpad may be used to capture the state of sandboxed processes and it writes\nminidumps to disk. It may therefore straddle security boundaries, so it’s\nimportant that Crashpad handle all data it reads out of the crashed process with\nextreme care. The Crashpad handler takes care to access client address spaces\nthrough specially-designed accessors that check pointer validity and enforce\naccesses within prescribed bounds. The flow of information into the Crashpad\nhandler is exclusively one-way: Crashpad never communicates anything back to\nits clients, aside from providing single-bit indications of completion.\n\n## Privacy Considerations\n\nCrashpad may capture arbitrary contents from crashed process’ memory, including\nuser IDs and passwords, credit card information, URLs and whatever other content\nusers have trusted the crashing program with. The client program must acquire\nand honor the user’s consent to upload crash reports, and appropriately manage\nthe upload state in Crashpad’s database.\n\nCrashpad must also be careful not to upload crashes for arbitrary processes on\nthe user’s system. To this end, Crashpad will never upload a process that hasn’t\nregistered with the handler, but note that registrations are inherited by child\nprocesses on some operating systems.\n"
  },
  {
    "path": "doc/status.md",
    "content": "<!--\nCopyright 2015 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# Project Status\n\n## Completed\n\nCrashpad has complete crash-reporting clients and some related tools for macOS,\nWindows, Fuchsia, and Linux (including Android and Chromium OS). Crashpad became\nthe crash reporter client for [Chromium](https://www.chromium.org/Home) on macOS\nas of [March\n2015](https://chromium.googlesource.com/chromium/src/\\+/d413b2dcb54d523811d386f1ff4084f677a6d089),\nWindows as of [November\n2015](https://chromium.googlesource.com/chromium/src/\\+/cfa5b01bb1d06bf96967bd37e21a44752801948c),\nand Android as of [January\n2019](https://chromium.googlesource.com/chromium/src/+/f890e4b5495ab693d2d37aec3c378239946154f7).\n\n\n## In Progress\n\nChromium is transitioning to Crashpad for [Chromium OS and Desktop\nLinux](https://crbug.com/942279).\n\nWork has begun on a Crashpad client for\n[iOS](https://crashpad.chromium.org/bug/31).\n\n## Future\n\nThere are also plans to implement a [crash report\nprocessor](https://crashpad.chromium.org/bug/29) as part of Crashpad. No\ntimeline for completing this work has been set yet.\n"
  },
  {
    "path": "doc/support/compat.sh",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nif [[ \"${BASH_SOURCE[0]}\" = \"${0}\" ]]; then\n  echo \"${0}: this file must be sourced, not run directly\" >& 2\n  exit 1\nfi\n\n# Some extensions of command-line tools behave differently on different systems.\n# $sed_ext should be a sed invocation that enables extended regular expressions.\n# $date_time_t should be a date invocation that causes it to print the date and\n# time corresponding to a time_t string that immediately follows it.\ncase \"$(uname -s)\" in\n  Darwin)\n    sed_ext=\"sed -E\"\n    date_time_t=\"date -r\"\n    ;;\n  Linux)\n    sed_ext=\"sed -r\"\n    date_time_t=\"date -d@\"\n    ;;\n  *)\n    echo \"${0}: unknown operating system\" >& 2\n    exit 1\n    ;;\nesac\n"
  },
  {
    "path": "doc/support/crashpad.doxy",
    "content": "# Doxyfile 1.13.2\n\n# This file describes the settings to be used by the documentation system\n# Doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n#\n# Note:\n#\n# Use Doxygen to compare the used configuration file with the template\n# configuration file:\n# doxygen -x [configFile]\n# Use Doxygen to compare the used configuration file with the template\n# configuration file without replacing the environment variables or CMake type\n# replacement variables:\n# doxygen -x_noenv [configFile]\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = Crashpad\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         =\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewers a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# With the PROJECT_ICON tag one can specify an icon that is included in the tabs\n# when the HTML document is shown. Doxygen will copy the logo to the output\n# directory.\n\nPROJECT_ICON           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where Doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = out/doc/doxygen\n\n# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096\n# sub-directories (in 2 levels) under the output directory of each output format\n# and will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding Doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise cause\n# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to\n# control the number of sub-directories.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# Controls the number of sub-directories that will be created when\n# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every\n# level increment doubles the number of directories, resulting in 4096\n# directories at level 8 which is the default and also the maximum value. The\n# sub-directories are organized in 2 levels, the first level always has a fixed\n# number of 16 directories.\n# Minimum value: 0, maximum value: 8, default value: 8.\n# This tag requires that the tag CREATE_SUBDIRS is set to YES.\n\nCREATE_SUBDIRS_LEVEL   = 8\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by Doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,\n# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English\n# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,\n# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with\n# English messages), Korean, Korean-en (Korean with English messages), Latvian,\n# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,\n# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,\n# Swedish, Turkish, Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# Doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which Doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where Doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    = . \\\n                         compat/non_mac \\\n                         compat/non_win\n\n# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but\n# less readable) file names. This can be useful if your file system doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the\n# first line (until the first dot, question mark or exclamation mark) of a\n# Javadoc-style comment as the brief description. If set to NO, the Javadoc-\n# style will behave just like regular Qt-style comments (thus requiring an\n# explicit @brief command for a brief description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by Doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first\n# line (until the first dot, question mark or exclamation mark) of a Qt-style\n# comment as the brief description. If set to NO, the Qt-style will behave just\n# like regular Qt-style comments (thus requiring an explicit \\brief command for\n# a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and Doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# Doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as Doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make Doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by Doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by Doxygen, so you can\n# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 6.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 0\n\n# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to\n# generate identifiers for the Markdown headings. Note: Every identifier is\n# unique.\n# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a\n# sequence number starting at 0 and GITHUB use the lower case version of title\n# with any whitespace replaced by '-' and punctuation characters removed.\n# The default value is: DOXYGEN.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nMARKDOWN_ID_STYLE      = DOXYGEN\n\n# When enabled Doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO. Words listed in the\n# AUTOLINK_IGNORE_WORDS tag are excluded from automatic linking.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# This tag specifies a list of words that, when matching the start of a word in\n# the documentation, will suppress auto links generation, if it is enabled via\n# AUTOLINK_SUPPORT. This list does not affect affect links explicitly created\n# using \\# or the \\link or commands.\n# This tag requires that the tag AUTOLINK_SUPPORT is set to YES.\n\nAUTOLINK_IGNORE_WORDS  =\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let Doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also makes the inheritance and\n# collaboration diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse\n# them like normal C++ but will assume all classes use public instead of private\n# inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# Doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then Doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = YES\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# Doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run Doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use\n# during processing. When set to 0 Doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n# If the TIMESTAMP tag is set different from NO then each generated page will\n# contain the date or date and time when the page was generated. Setting this to\n# NO can help when comparing the output of multiple runs.\n# Possible values are: YES, NO, DATETIME and DATE.\n# The default value is: NO.\n\nTIMESTAMP              = NO\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = NO\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# will also hide undocumented C++ concepts if enabled. This option has no effect\n# if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_UNDOC_NAMESPACES tag is set to YES, Doxygen will hide all\n# undocumented namespaces that are normally visible in the namespace hierarchy.\n# If set to NO, these namespaces will be included in the various overviews. This\n# option has no effect if EXTRACT_ALL is enabled.\n# The default value is: YES.\n\nHIDE_UNDOC_NAMESPACES  = YES\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and macOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# Possible values are: SYSTEM, NO and YES.\n# The default value is: SYSTEM.\n\nCASE_SENSE_NAMES       = YES\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = YES\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = NO\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = NO\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = NO\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= NO\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# Doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by Doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by Doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents Doxygen's defaults, run Doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run Doxygen from a directory containing a file called\n# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH\n# environment variable) so that external tools such as latex and gs can be\n# found.\n# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the\n# path already specified by the PATH variable, and are added in the order\n# specified.\n# Note: This option is particularly useful for macOS version 14 (Sonoma) and\n# higher, when running Doxygen from Doxywizard, because in this case any user-\n# defined changes to the PATH are ignored. A typical example on macOS is to set\n# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin\n# together with the standard path, the full search path used by doxygen when\n# launching external tools will then become\n# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin\n\nEXTERNAL_TOOL_PATH     =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by Doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = YES\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = NO\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete\n# function parameter documentation. If set to NO, Doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, Doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about\n# undocumented enumeration values. If set to NO, Doxygen will accept\n# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: NO.\n\nWARN_IF_UNDOC_ENUM_VAL = NO\n\n# If WARN_LAYOUT_FILE option is set to YES, Doxygen will warn about issues found\n# while parsing the user defined layout file, such as missing or wrong elements.\n# See also LAYOUT_FILE for details. If set to NO, problems with the layout file\n# will be suppressed.\n# The default value is: YES.\n\nWARN_LAYOUT_FILE       = YES\n\n# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the Doxygen process Doxygen will return with a non-zero status.\n# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves\n# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not\n# write the warning messages in between other messages but write them at the end\n# of a run, in case a WARN_LOGFILE is defined the warning messages will be\n# besides being in the defined file also be shown at the end of a run, unless\n# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case\n# the behavior will remain as with the setting FAIL_ON_WARNINGS.\n# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that Doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# See also: WARN_LINE_FORMAT\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# In the $text part of the WARN_FORMAT command it is possible that a reference\n# to a more specific place is given. To make it easier to jump to this place\n# (outside of Doxygen) the user can define a custom \"cut\" / \"paste\" string.\n# Example:\n# WARN_LINE_FORMAT = \"'vi $file +$line'\"\n# See also: WARN_FORMAT\n# The default value is: at line $line of file $file.\n\nWARN_LINE_FORMAT       = \"at line $line of file $file\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr). In case the file specified cannot be opened for writing the\n# warning and error messages are written to standard error. When as file - is\n# specified the warning and error messages are written to standard output\n# (stdout).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  =\n\n# This tag can be used to specify the character encoding of the source files\n# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# See also: INPUT_FILE_ENCODING\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# This tag can be used to specify the character encoding of the source files\n# that Doxygen parses. The INPUT_FILE_ENCODING tag can be used to specify\n# character encoding on a per file pattern basis. Doxygen will compare the file\n# name with each pattern and apply the encoding instead of the default\n# INPUT_ENCODING if there is a match. The character encodings are a list of the\n# form: pattern=encoding (like *.php=ISO-8859-1).\n# See also: INPUT_ENCODING for further information on supported encodings.\n\nINPUT_FILE_ENCODING    =\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by Doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,\n# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,\n# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,\n# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to\n# be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,\n# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.h \\\n                         *.m \\\n                         *.mm\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which Doxygen is\n# run.\n\nEXCLUDE                = compat/android \\\n                         compat/ios \\\n                         compat/linux \\\n                         compat/mac \\\n                         compat/win \\\n                         third_party\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       = out*\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# ANamespace::AClass, ANamespace::*Test\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that Doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that Doxygen will use the data processed and written to standard output\n# for further processing, therefore nothing else, like debug statements or used\n# commands (so in case of a Windows batch file always use @echo OFF), should be\n# written to standard output.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by Doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by Doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the Doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n# If the IMPLICIT_DIR_DOCS tag is set to YES, any README.md file found in sub-\n# directories of the project's root, is used as the documentation for that sub-\n# directory, except when the README.md starts with a \\dir, \\page or \\mainpage\n# command. If set to NO, the README.md file needs to start with an explicit \\dir\n# command in order to be used as directory documentation.\n# The default value is: YES.\n\nIMPLICIT_DIR_DOCS      = YES\n\n# The Fortran standard specifies that for fixed formatted Fortran code all\n# characters from position 72 are to be considered as comment. A common\n# extension is to allow longer lines before the automatic comment starts. The\n# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can\n# be processed before the automatic comment starts.\n# Minimum value: 7, maximum value: 10000, default value: 72.\n\nFORTRAN_COMMENT_AFTER  = 72\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# multi-line macros, enums or list initialized variables directly into the\n# documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of Doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by Doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)\n# that should be ignored while generating the index headers. The IGNORE_PREFIX\n# tag works for classes, function and member names. The entity will be placed in\n# the alphabetical list under the first letter of the entity name that remains\n# after removing the prefix.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank Doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that Doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that Doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of Doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank Doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that Doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank Doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that Doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by Doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# Note: Since the styling of scrollbars can currently not be overruled in\n# Webkit/Chromium, the styling will be left out of the default doxygen.css if\n# one or more extra stylesheets have been specified. So if scrollbar\n# customization is desired it has to be added explicitly. For an example see the\n# documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  = doc/support/crashpad_doxygen.css\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output\n# should be rendered with a dark or light theme.\n# Possible values are: LIGHT always generates light mode output, DARK always\n# generates dark mode output, AUTO_LIGHT automatically sets the mode according\n# to the user preference, uses light mode if no preference is set (the default),\n# AUTO_DARK automatically sets the mode according to the user preference, uses\n# dark mode if no preference is set and TOGGLE allows a user to switch between\n# light and dark mode via a button.\n# The default value is: AUTO_LIGHT.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE        = AUTO_LIGHT\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be\n# dynamically folded and expanded in the generated HTML source code.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_CODE_FOLDING      = YES\n\n# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in\n# the top right corner of code and text fragments that allows the user to copy\n# its content to the clipboard. Note this only works if supported by the browser\n# and the web page is served via a secure context (see:\n# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:\n# protocol.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COPY_CLIPBOARD    = YES\n\n# Doxygen stores a couple of settings persistently in the browser (via e.g.\n# cookies). By default these settings apply to all HTML pages generated by\n# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store\n# the settings under a project specific key, such that the user preferences will\n# be stored separately.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_PROJECT_COOKIE    =\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, Doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag determines the URL of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDURL         =\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline (the HTML help workshop was already many\n# years in maintenance mode). You can download the HTML help workshop from the\n# web archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# Doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# The SITEMAP_URL tag is used to specify the full URL of the place where the\n# generated documentation will be placed on the server by the user during the\n# deployment of the documentation. The generated sitemap is called sitemap.xml\n# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL\n# is specified no sitemap is generated. For information about the sitemap\n# protocol see https://www.sitemaps.org\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSITEMAP_URL            =\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = YES\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by Doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = YES\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATE_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# Doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 0\n\n# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified\n# enumeration values besides the enumeration mnemonics.\n# The default value is: NO.\n\nSHOW_ENUM_VALUES       = NO\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email\n# addresses.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nOBFUSCATE_EMAILS       = YES\n\n# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# Doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        =\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for\n# the HTML output. The underlying search engine uses JavaScript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the JavaScript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, Doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled Doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through Doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, Doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         =\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, Doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank Doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that Doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of Doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank Doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that Doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by Doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, Doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.\n# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch\n# mode nothing is printed on the terminal, errors are scrolled as if <return> is\n# hit at every error; missing files that TeX tries to input or request from\n# keyboard input (\\read on a not open input stream) cause the job to abort,\n# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,\n# but there is no possibility of user interaction just like in batch mode,\n# SCROLL In scroll mode, TeX will stop only for missing files to input or if\n# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at\n# each error, asking for user intervention.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then Doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plainnat.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plainnat\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, Doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, Doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to Doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that Doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to Doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# The RTF_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the RTF_OUTPUT output directory.\n# Note that the files will be copied as-is; there are no commands or markers\n# available.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTRA_FILES        =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, Doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and Doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, Doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, Doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, Doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, Doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, Doxygen will generate an\n# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to Sqlite3 output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_SQLITE3 tag is set to YES Doxygen will generate a Sqlite3\n# database with symbols found by Doxygen stored in tables.\n# The default value is: NO.\n\nGENERATE_SQLITE3       = NO\n\n# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be\n# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put\n# in front of it.\n# The default directory is: sqlite3.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_OUTPUT         = sqlite3\n\n# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db\n# database file will be recreated with each Doxygen run. If set to NO, Doxygen\n# will warn if a database file is already found and not modify it.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_RECREATE_DB    = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, Doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, Doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, Doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, Doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = YES\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = YES\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of\n# RECURSIVE has no effect here.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             = ALIGNAS(x)= \\\n                         DOXYGEN \\\n                         MSVC_POP_WARNING()= \\\n                         MSVC_PUSH_DISABLE_WARNING(x)= \\\n                         __attribute__(x)=\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then Doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which Doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, Doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces\n# will be listed in the class and namespace index. If set to NO, only the\n# inherited external classes will be listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the topic index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to diagram generator tools\n#---------------------------------------------------------------------------\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then Doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = YES\n\n# The DOT_NUM_THREADS specifies the number of dot invocations Doxygen is allowed\n# to run in parallel. When set to 0 Doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of\n# subgraphs. When you want a differently looking font in the dot files that\n# Doxygen generates you can specify fontname, fontcolor and fontsize attributes.\n# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,\n# Edge and Graph Attributes specification</a> You need to make sure dot is able\n# to find the font, which can be done by putting it in a standard location or by\n# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the\n# directory containing the font. Default graphviz fontsize is 14.\n# The default value is: fontname=Helvetica,fontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_COMMON_ATTR        = \"fontname=\\\"Noto Sans, Lucida Grande, Lucida Sans Unicode, Helvetica, Arial, sans-serif\\\",fontsize=10\"\n\n# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can\n# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a\n# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about\n# arrows shapes.</a>\n# The default value is: labelfontname=Helvetica,labelfontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_EDGE_ATTR          = \"labelfontname=\\\"Noto Sans, Lucida Grande, Lucida Sans Unicode, Helvetica, Arial, sans-serif\\\",labelfontsize=10\"\n\n# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes\n# around nodes set 'shape=plain' or 'shape=plaintext' <a\n# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>\n# The default value is: shape=box,height=0.2,width=0.4.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NODE_ATTR          = \"shape=box,height=0.2,width=0.4\"\n\n# You can set the path where dot can find font specified with fontname in\n# DOT_COMMON_ATTR and others dot attributes.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then Doxygen will\n# generate a graph for each documented class showing the direct and indirect\n# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and\n# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case\n# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the\n# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.\n# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance\n# relations will be shown as texts / links. Explicit enabling an inheritance\n# graph or choosing a different representation for an inheritance graph of a\n# specific class, can be accomplished by means of the command \\inheritancegraph.\n# Disabling an inheritance graph can be accomplished by means of the command\n# \\hideinheritancegraph.\n# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.\n# The default value is: YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then Doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes. Explicit enabling a collaboration graph,\n# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the\n# command \\collaborationgraph. Disabling a collaboration graph can be\n# accomplished by means of the command \\hidecollaborationgraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then Doxygen will generate a graph for\n# groups, showing the direct groups dependencies. Explicit enabling a group\n# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means\n# of the command \\groupgraph. Disabling a directory graph can be accomplished by\n# means of the command \\hidegroupgraph. See also the chapter Grouping in the\n# manual.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, Doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, Doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, Doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, Doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will be wrapped across multiple lines. Some heuristics are\n# applied to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then Doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,\n# can be accomplished by means of the command \\includegraph. Disabling an\n# include graph can be accomplished by means of the command \\hideincludegraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then Doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set\n# to NO, can be accomplished by means of the command \\includedbygraph. Disabling\n# an included by graph can be accomplished by means of the command\n# \\hideincludedbygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then Doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then Doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then Doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then Doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories. Explicit enabling a directory graph, when\n# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command\n# \\directorygraph. Disabling a directory graph can be accomplished by means of\n# the command \\hidedirectorygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels\n# of child directories generated in directory dependency graphs by dot.\n# Minimum value: 1, maximum value: 25, default value: 1.\n# This tag requires that the tag DIRECTORY_GRAPH is set to YES.\n\nDIR_GRAPH_MAX_DEPTH    = 1\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# https://www.graphviz.org/)).\n#\n# Note the formats svg:cairo and svg:cairo:cairo cannot be used in combination\n# with INTERACTIVE_SVG (the INTERACTIVE_SVG will be set to NO).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus,\n# png:gdiplus:gdiplus, svg:cairo, svg:cairo:cairo, svg:svg, svg:svg:core,\n# gif:cairo, gif:cairo:gd, gif:cairo:gdiplus, gif:gdiplus, gif:gdiplus:gdiplus,\n# gif:gd, gif:gd:gd, jpg:cairo, jpg:cairo:gd, jpg:cairo:gdiplus, jpg:gd,\n# jpg:gd:gd, jpg:gdiplus and jpg:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = svg\n\n# If DOT_IMAGE_FORMAT is set to svg or svg:svg or svg:svg:core, then this option\n# can be set to YES to enable generation of interactive SVG images that allow\n# zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n#\n# Note This option will be automatically disabled when DOT_IMAGE_FORMAT is set\n# to svg:cairo or svg:cairo:cairo.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# You can include diagrams made with dia in Doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using PlantUML, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file or to the filename of jar file\n# to be used. If left blank, it is assumed PlantUML is not used or called during\n# a preprocessing step. Doxygen will generate a warning when it encounters a\n# \\startuml command in this case and will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using PlantUML, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for PlantUML.\n\nPLANTUML_CFG_FILE      =\n\n# When using PlantUML, the specified paths are searched for files specified by\n# the !include statement in a PlantUML block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The PLANTUMLFILE_DIRS tag can be used to specify one or more directories that\n# contain PlantUml files that are included in the documentation (see the\n# \\plantumlfile command).\n\nPLANTUMLFILE_DIRS      =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, Doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES Doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# Note: This tag requires that UML_LOOK isn't set, i.e. the Doxygen internal\n# graphical representation for inheritance and collaboration diagrams is used.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, Doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n\n# You can define message sequence charts within Doxygen comments using the \\msc\n# command. If the MSCGEN_TOOL tag is left empty (the default), then Doxygen will\n# use a built-in version of mscgen tool to produce the charts. Alternatively,\n# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,\n# specifying prog as the value, Doxygen will call the tool as prog -T\n# <outfile_format> -o <outputfile> <inputfile>. The external tool should support\n# output file formats \"png\", \"eps\", \"svg\", and \"ismap\".\n\nMSCGEN_TOOL            =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n"
  },
  {
    "path": "doc/support/crashpad.doxy.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#error This file is not intended to be #included.\n\n//! \\namespace crashpad\n//! \\brief The main namespace.\n\n//! \\namespace crashpad::internal\n//! \\brief The internal namespace, not for public use.\n\n//! \\namespace crashpad::test\n//! \\brief The testing namespace, for use in test code only.\n\n//! \\mainpage Crashpad Interface Documentation\n//!\n//! Most generated interface documentation is reachable through <a\n//! href=\"namespaces.html\">Namespaces</a>, <a href=\"annotated.html\">Classes</a>\n//! (includes `struct`s, `union`s, and interfaces), or <a\n//! href=\"files.html\">Files</a> (includes macros).\n//!\n//! Additional documentation is available at the <a\n//! href=\"https://crashpad.chromium.org/\">Crashpad home page</a>.\n"
  },
  {
    "path": "doc/support/crashpad_doxygen.css",
    "content": "/* Copyright 2016 The Crashpad Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License. */\n\n@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap');\n@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:ital,wght@0,200..900;1,200..900&display=swap');\n\n:root {\n  --font-family-normal: 'Noto Sans',\n    'Lucida Grande',\n    'Lucida Sans Unicode',\n    Helvetica,\n    Arial,\n    sans-serif;\n  --font-family-nav: var(--font-family-normal);\n  --font-family-title: var(--font-family-normal);\n  --font-family-toc: var(--font-family-normal);\n  --font-family-search: var(--font-family-normal);\n  --font-family-icon: var(--font-family-normal);\n  --font-family-tooltip: var(--font-family-normal);\n\n  --font-family-monospace: 'Source Code Pro',\n    Monaco,\n    'Lucida Console',\n    monospace;\n}\n\n.icon {\n  height: auto;\n}\n"
  },
  {
    "path": "doc/support/generate.sh",
    "content": "#!/bin/bash\n\n# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -e\n\nfunction maybe_mkdir() {\n  local dir=\"${1}\"\n  if [[ ! -d \"${dir}\" ]]; then\n    mkdir \"${dir}\"\n  fi\n}\n\n# Run from the Crashpad project root directory.\ncd \"$(dirname \"${0}\")/../..\"\n\nsource doc/support/compat.sh\n\ndoc/support/generate_doxygen.py\n\noutput_dir=doc/generated\nmaybe_mkdir \"${output_dir}\"\n\nmaybe_mkdir \"${output_dir}/doxygen\"\nrsync -Ilr --delete --exclude .git \"out/doc/doxygen/html/\" \\\n    \"${output_dir}/doxygen\"\n\n# Ensure a favicon exists at the root since the browser will always request it.\ncp doc/favicon.ico \"${output_dir}/\"\n"
  },
  {
    "path": "doc/support/generate_doxygen.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Generating Doxygen documentation requires Doxygen, http://www.doxygen.org/.\n\nimport os\nimport shutil\nimport subprocess\nimport sys\n\n\ndef main(args):\n    script_dir = os.path.dirname(__file__)\n    crashpad_dir = os.path.join(script_dir, os.pardir, os.pardir)\n\n    # Run from the Crashpad project root directory.\n    os.chdir(crashpad_dir)\n\n    output_dir = os.path.join('out', 'doc', 'doxygen')\n\n    if os.path.isdir(output_dir) and not os.path.islink(output_dir):\n        shutil.rmtree(output_dir)\n    elif os.path.exists(output_dir):\n        os.unlink(output_dir)\n\n    os.makedirs(output_dir, 0o755)\n\n    doxy_file = os.path.join('doc', 'support', 'crashpad.doxy')\n    subprocess.check_call(['doxygen', doxy_file])\n\n    return 0\n\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv[1:]))\n"
  },
  {
    "path": "doc/support/generate_git.sh",
    "content": "#!/bin/bash\n\n# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -e\n\n# Run from the Crashpad project root directory.\ncd \"$(dirname \"${0}\")/../..\"\n\nsource doc/support/compat.sh\n\nbasename=\"$(basename \"${0}\")\"\n\nstatus=\"$(git status --porcelain)\"\nif [[ -n \"${status}\" ]]; then\n  echo \"${basename}: the working directory must be clean\" >& 2\n  git status\n  exit 1\nfi\n\n# git symbolic-ref gives the current branch name, but fails for detached HEAD.\n# In that case, git rev-parse will give the current hash.\noriginal_branch=\"$(git symbolic-ref --short --quiet HEAD || git rev-parse HEAD)\"\n\nlocal_branch=\"\\\n$(${sed_ext} -e 's/(.*)\\..*/\\1/' <<< \"${basename}\").${$}.${RANDOM}\"\n\nremote_name=origin\nremote_main_branch_name=main\nremote_main_branch=\"${remote_name}/${remote_main_branch_name}\"\nremote_doc_branch_name=doc\nremote_doc_branch=\"${remote_name}/${remote_doc_branch_name}\"\n\ngit fetch\n\ngit checkout -b \"${local_branch}\" \"${remote_doc_branch}\"\n\ndirty=\n\nfunction cleanup() {\n  if [[ \"${dirty}\" ]]; then\n    git reset --hard HEAD\n    git clean --force\n  fi\n\n  git checkout \"${original_branch}\"\n  git branch -D \"${local_branch}\"\n}\n\ntrap cleanup EXIT\n\nmain_hash=$(git rev-parse --short=12 \"${remote_main_branch}\")\ngit merge \"${remote_main_branch}\" \\\n    -m \"Merge ${remote_main_branch_name} ${main_hash} into doc\"\n\ndirty=y\n\ndoc/support/generate.sh\n\ngit add -A doc/generated\n\ncount=\"$(git diff --staged --numstat | wc -l)\"\nif [[ $count -gt 0 ]]; then\n  git commit \\\n      -m \"Update documentation to ${remote_main_branch_name} ${main_hash}\"\n  dirty=\n\n  git push \"${remote_name}\" \"HEAD:${remote_doc_branch_name}\"\nelse\n  dirty=\nfi\n\n# cleanup will run on exit\n"
  },
  {
    "path": "handler/BUILD.gn",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../build/crashpad_buildconfig.gni\")\n\nstatic_library(\"common\") {\n  sources = [\n    \"crash_report_upload_rate_limit.cc\",\n    \"crash_report_upload_rate_limit.h\",\n    \"crash_report_upload_thread.cc\",\n    \"crash_report_upload_thread.h\",\n    \"minidump_to_upload_parameters.cc\",\n    \"minidump_to_upload_parameters.h\",\n    \"user_stream_data_source.cc\",\n    \"user_stream_data_source.h\",\n  ]\n  if (crashpad_is_apple) {\n    sources += [\n      \"mac/file_limit_annotation.cc\",\n      \"mac/file_limit_annotation.h\",\n    ]\n  }\n  public_configs = [ \"..:crashpad_config\" ]\n  public_deps = [\n    \"../third_party/mini_chromium:base\",\n    \"../util\",\n  ]\n  deps = [\n    \"../client:common\",\n    \"../minidump\",\n    \"../snapshot\",\n    \"../util\",\n    \"../util:net\",\n  ]\n  if (crashpad_is_win) {\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n  }\n}\n\nif (!crashpad_is_ios) {\n  static_library(\"handler\") {\n    sources = [\n      \"handler_main.cc\",\n      \"handler_main.h\",\n      \"prune_crash_reports_thread.cc\",\n      \"prune_crash_reports_thread.h\",\n    ]\n\n    if (crashpad_is_mac) {\n      sources += [\n        \"mac/crash_report_exception_handler.cc\",\n        \"mac/crash_report_exception_handler.h\",\n        \"mac/exception_handler_server.cc\",\n        \"mac/exception_handler_server.h\",\n      ]\n    }\n\n    if (crashpad_is_linux || crashpad_is_android) {\n      sources += [\n        \"linux/capture_snapshot.cc\",\n        \"linux/capture_snapshot.h\",\n        \"linux/crash_report_exception_handler.cc\",\n        \"linux/crash_report_exception_handler.h\",\n        \"linux/exception_handler_server.cc\",\n        \"linux/exception_handler_server.h\",\n      ]\n    }\n\n    if (crashpad_is_linux) {\n      sources += [\n        \"linux/cros_crash_report_exception_handler.cc\",\n        \"linux/cros_crash_report_exception_handler.h\",\n      ]\n\n      # TODO(https://crbug.com/1420445): Remove this config when M115 branches.\n      configs += [ \"../build:crashpad_is_in_chromium\" ]\n    }\n\n    if (crashpad_is_win) {\n      sources += [\n        \"win/crash_report_exception_handler.cc\",\n        \"win/crash_report_exception_handler.h\",\n      ]\n    }\n\n    public_configs = [ \"..:crashpad_config\" ]\n\n    public_deps = [\n      \":common\",\n      \"../client\",\n      \"../third_party/mini_chromium:base\",\n      \"../util\",\n    ]\n\n    deps = [\n      \":common\",\n      \"../minidump\",\n      \"../snapshot\",\n      \"../tools:tool_support\",\n    ]\n\n    if (crashpad_is_win) {\n      cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n    }\n  }\n\n  if (crashpad_is_android) {\n    # CrashpadHandlerMain is defined in a separate target so that it can be\n    # overriden by implementers\n    source_set(\"crashpad_handler_main\") {\n      sources = [ \"crashpad_handler_main.cc\" ]\n\n      deps = [ \":handler\" ]\n    }\n  }\n\n  source_set(\"handler_test\") {\n    testonly = true\n\n    sources = [\n      \"crash_report_upload_rate_limit_test.cc\",\n      \"minidump_to_upload_parameters_test.cc\",\n    ]\n\n    if (crashpad_is_linux || crashpad_is_android) {\n      sources += [ \"linux/exception_handler_server_test.cc\" ]\n    }\n\n    if (crashpad_is_win) {\n      sources += [ \"crashpad_handler_test.cc\" ]\n    }\n\n    deps = [\n      \":handler\",\n      \"../client\",\n      \"../compat\",\n      \"../snapshot\",\n      \"../snapshot:test_support\",\n      \"../test\",\n      \"../third_party/googletest\",\n      \"../third_party/mini_chromium:base\",\n      \"../util\",\n    ]\n\n    if (crashpad_is_win) {\n      deps += [\n        \"../minidump:test_support\",\n        \"win/wer:crashpad_wer_test\",\n      ]\n\n      data_deps = [\n        \":crashpad_handler_test_extended_handler\",\n        \":fake_handler_that_crashes_at_startup\",\n      ]\n    }\n  }\n\n  crashpad_executable(\"crashpad_handler\") {\n    sources = [ \"main.cc\" ]\n\n    deps = [\n      \":handler\",\n      \"../build:default_exe_manifest_win\",\n      \"../compat\",\n      \"../third_party/mini_chromium:base\",\n      \"../tools:tool_support\",\n    ]\n\n    if (crashpad_is_win) {\n      if (crashpad_is_in_chromium || crashpad_is_in_dart) {\n        remove_configs = [ \"//build/config/win:console\" ]\n        configs = [ \"//build/config/win:windowed\" ]\n      } else if (crashpad_is_external) {\n        remove_configs =\n            [ \"//../../mini_chromium/mini_chromium/build/config:win_console\" ]\n        configs =\n            [ \"//../../mini_chromium/mini_chromium/build/config:win_windowed\" ]\n      } else {\n        remove_configs = [\n          \"//third_party/mini_chromium/mini_chromium/build/config:win_console\",\n        ]\n        configs = [\n          \"//third_party/mini_chromium/mini_chromium/build/config:win_windowed\",\n        ]\n      }\n    }\n\n    if (crashpad_is_linux) {\n      deps += [ \"../client:pthread_create\" ]\n    }\n  }\n}\n\n# There is not any normal way to package native executables in an Android APK.\n# It is normal to package native code as a loadable module but Android's APK\n# installer will ignore files not named like a shared object, so give the\n# handler executable an acceptable name.\nif (crashpad_is_android) {\n  copy(\"crashpad_handler_named_as_so\") {\n    deps = [ \":crashpad_handler\" ]\n\n    sources = [ \"$root_out_dir/crashpad_handler\" ]\n\n    outputs = [ \"$root_out_dir/libcrashpad_handler.so\" ]\n  }\n\n  crashpad_executable(\"crashpad_handler_trampoline\") {\n    output_name = \"libcrashpad_handler_trampoline.so\"\n\n    sources = [ \"linux/handler_trampoline.cc\" ]\n\n    deps = [ \"../util:no_cfi_icall\" ]\n\n    libs = [ \"log\" ]\n\n    if (crashpad_is_in_chromium) {\n      # Chromium's sanitizer runtime libraries do not include an unwinder,\n      # so add Chromium's standard dependencies to link against the in-tree\n      # libunwind. The coverage wrapper similarly requires an unwinder, as\n      # well as a few other bits from libc++abi. There are some issues with pgo\n      # as well.\n      import(\"//build/config/compiler/pgo/pgo.gni\")\n      import(\"//build/config/coverage/coverage.gni\")\n      import(\"//build/config/sanitizers/sanitizers.gni\")\n      no_default_deps =\n          !use_clang_coverage && !using_sanitizer && chrome_pgo_phase != 1\n      remove_configs =\n          [ \"//build/config/android:default_orderfile_instrumentation\" ]\n    }\n  }\n}\n\nif (!crashpad_is_ios) {\n  crashpad_executable(\"crashpad_handler_test_extended_handler\") {\n    testonly = true\n\n    sources = [ \"crashpad_handler_test_extended_handler.cc\" ]\n\n    deps = [\n      \":handler\",\n      \"../build:default_exe_manifest_win\",\n      \"../compat\",\n      \"../minidump:test_support\",\n      \"../third_party/mini_chromium:base\",\n      \"../tools:tool_support\",\n    ]\n  }\n}\n\nif (crashpad_is_win) {\n  crashpad_executable(\"crashpad_handler_com\") {\n    sources = [ \"main.cc\" ]\n\n    # Avoid .exp, .ilk, and .lib file collisions with crashpad_handler.exe by\n    # having this target produce crashpad_handler_com.com. Don’t use this target\n    # directly. Instead, use crashpad_handler_console.\n    output_extension = \"com\"\n\n    deps = [\n      \":handler\",\n      \"../build:default_exe_manifest_win\",\n      \"../compat\",\n      \"../third_party/mini_chromium:base\",\n      \"../tools:tool_support\",\n    ]\n  }\n\n  copy(\"crashpad_handler_console\") {\n    deps = [ \":crashpad_handler_com\" ]\n    sources = [ \"$root_out_dir/crashpad_handler_com.com\" ]\n    outputs = [ \"$root_out_dir/crashpad_handler.com\" ]\n  }\n\n  crashpad_executable(\"crash_other_program\") {\n    testonly = true\n\n    sources = [ \"win/crash_other_program.cc\" ]\n\n    deps = [\n      \"../client\",\n      \"../test\",\n      \"../third_party/googletest\",\n      \"../third_party/mini_chromium:base\",\n    ]\n  }\n\n  crashpad_executable(\"crashy_program\") {\n    testonly = true\n\n    sources = [ \"win/crashy_test_program.cc\" ]\n\n    deps = [\n      \"../client\",\n      \"../third_party/mini_chromium:base\",\n    ]\n  }\n\n  crashpad_executable(\"crashy_signal\") {\n    testonly = true\n\n    sources = [ \"win/crashy_signal.cc\" ]\n\n    cflags = [ \"/wd4702\" ]  # Unreachable code.\n\n    deps = [\n      \"../client\",\n      \"../third_party/mini_chromium:base\",\n    ]\n  }\n\n  crashpad_executable(\"fake_handler_that_crashes_at_startup\") {\n    testonly = true\n\n    sources = [ \"win/fake_handler_that_crashes_at_startup.cc\" ]\n  }\n\n  crashpad_executable(\"hanging_program\") {\n    testonly = true\n\n    sources = [ \"win/hanging_program.cc\" ]\n\n    deps = [\n      \"../client\",\n      \"../third_party/mini_chromium:base\",\n    ]\n  }\n\n  crashpad_loadable_module(\"loader_lock_dll\") {\n    testonly = true\n\n    sources = [ \"win/loader_lock_dll.cc\" ]\n  }\n\n  crashpad_executable(\"self_destroying_program\") {\n    testonly = true\n\n    sources = [ \"win/self_destroying_test_program.cc\" ]\n\n    deps = [\n      \"../client\",\n      \"../compat\",\n      \"../snapshot\",\n      \"../third_party/mini_chromium:base\",\n    ]\n  }\n\n  crashpad_executable(\"heap_corrupting_program\") {\n    testonly = true\n\n    sources = [ \"win/heap_corrupting_program.cc\" ]\n\n    deps = [\n      \"../client\",\n      \"../compat\",\n      \"../snapshot\",\n      \"../third_party/mini_chromium:base\",\n    ]\n  }\n\n  if (current_cpu == \"x86\") {\n    # Cannot create an x64 DLL with embedded debug info.\n    crashpad_executable(\"crashy_z7_loader\") {\n      testonly = true\n\n      sources = [ \"win/crashy_test_z7_loader.cc\" ]\n\n      deps = [\n        \"../client\",\n        \"../test\",\n        \"../third_party/mini_chromium:base\",\n      ]\n    }\n  }\n\n  config(\"enable_cfg\") {\n    cflags = [ \"/guard:cf\" ]\n    ldflags = [ \"/guard:cf\" ]\n  }\n  crashpad_executable(\"fastfail_program\") {\n    testonly = true\n\n    sources = [ \"win/fastfail_test_program.cc\" ]\n    configs = [ \":enable_cfg\" ]\n\n    deps = [\n      \"../client\",\n      \"../third_party/mini_chromium:base\",\n    ]\n  }\n}\n"
  },
  {
    "path": "handler/crash_report_upload_rate_limit.cc",
    "content": "// Copyright 2025 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/crash_report_upload_rate_limit.h\"\n\n#include <time.h>\n\n#include <optional>\n\n#include \"util/misc/metrics.h\"\n\nnamespace crashpad {\n\nShouldRateLimitResult ShouldRateLimit(time_t now,\n                                      time_t last_upload_attempt_time,\n                                      time_t interval_seconds) {\n  if (now >= last_upload_attempt_time) {\n    // If the most recent upload attempt occurred within the past interval,\n    // don’t attempt to upload the new report. If it happened longer ago,\n    // attempt to upload the report.\n    if (now - last_upload_attempt_time < interval_seconds) {\n      return {1, Metrics::CrashSkippedReason::kUploadThrottled};\n    }\n  } else if (last_upload_attempt_time - now < kBackwardsClockTolerance) {\n    // The most recent upload attempt purportedly occurred in the future. If\n    // it “happened” at least `kBackwardsClockTolerance` in the future, assume\n    // that the last upload attempt time is bogus, and attempt to upload the\n    // report. If the most recent upload time is in the future but within\n    // `kBackwardsClockTolerance`, accept it and don’t attempt to upload the\n    // report.\n    return {1, Metrics::CrashSkippedReason::kUnexpectedTime};\n  }\n  return {0, std::nullopt};\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/crash_report_upload_rate_limit.h",
    "content": "// Copyright 2025 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_RATE_LIMIT_H_\n#define CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_RATE_LIMIT_H_\n\n#include <time.h>\n\n#include <optional>\n\n#include \"util/misc/metrics.h\"\n\nnamespace crashpad {\n\n//! \\brief How far into the future an upload-attempt timestamp can be and still\n//!     be treated as valid. Beyond this tolerance, timestamps are disregarded.\n//!     Exposed for testing.\ninline constexpr int kBackwardsClockTolerance = 60 * 60 * 24;  // 1 day\n\n//! \\brief The return type of `ShouldRateLimit()`. Describes the number of\n//!     crash-report upload attempts in the past interval, and if upload\n//!     should be skipped, the reason why.\nstruct ShouldRateLimitResult {\n  //! \\brief The number of upload attempts in the past interval.\n  unsigned int attempts_in_interval;\n\n  //! \\brief If the crash should be skipped, the reason why. `std::nullopt`\n  //!     otherwise.\n  std::optional<Metrics::CrashSkippedReason> skip_reason;\n};\n\n//! \\brief Determines whether a crash report can be uploaded now, or whether\n//!     uploads should be rate limited at this time.\n//!\n//! \\param[in] now The current time.\n//! \\param[in] last_upload_attempt_time The timestamp of the last crash-report\n//!     upload attempt.\n//! \\param[in] interval_seconds The unit of time, in seconds, over which to\n//!     perform rate-limiting calculations (an \"interval\").\n//!\n//! \\return The number of upload attempts in the past interval, and if the\n//!     current crash report should be skipped, the reason why.\nShouldRateLimitResult ShouldRateLimit(time_t now,\n                                      time_t last_upload_attempt_time,\n                                      time_t interval_seconds);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_RATE_LIMIT_H_\n"
  },
  {
    "path": "handler/crash_report_upload_rate_limit_test.cc",
    "content": "// Copyright 2025 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/crash_report_upload_rate_limit.h\"\n\n#include <time.h>\n\n#include <optional>\n\n#include \"gtest/gtest.h\"\n#include \"util/misc/metrics.h\"\n\nnamespace crashpad::test {\nnamespace {\n\nusing Metrics::CrashSkippedReason::kUnexpectedTime;\nusing Metrics::CrashSkippedReason::kUploadThrottled;\n\nconstexpr time_t kOneHour = 60 * 60;\n\nTEST(CrashReportUploadRateLimitTest, ShouldRateLimit_OnePerHour) {\n  const time_t now = time(nullptr);\n\n  // Allow upload if the last upload attempt time is in the future, at or beyond\n  // `kBackwardsClockTolerance`.\n  auto should_rate_limit =\n      ShouldRateLimit(now, now + kBackwardsClockTolerance + 1, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, std::nullopt);\n\n  should_rate_limit =\n      ShouldRateLimit(now, now + kBackwardsClockTolerance, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, std::nullopt);\n\n  // Skip upload if the last attempt time is in the future but within\n  // `kBackwardsClockTolerance`.\n  should_rate_limit =\n      ShouldRateLimit(now, now + kBackwardsClockTolerance - 1, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, kUnexpectedTime);\n\n  should_rate_limit = ShouldRateLimit(now, now + 1, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, kUnexpectedTime);\n\n  // Skip upload if the last attempt was under one interval ago.\n  should_rate_limit = ShouldRateLimit(now, now, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, kUploadThrottled);\n\n  should_rate_limit = ShouldRateLimit(now, now - 1, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, kUploadThrottled);\n\n  should_rate_limit = ShouldRateLimit(now, now - kOneHour + 1, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, kUploadThrottled);\n\n  // Allow upload if the last attempt was one interval or more ago.\n  should_rate_limit = ShouldRateLimit(now, now - kOneHour, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, std::nullopt);\n\n  should_rate_limit = ShouldRateLimit(now, now - kOneHour - 1, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, std::nullopt);\n\n  should_rate_limit = ShouldRateLimit(now, 0, kOneHour);\n  EXPECT_EQ(should_rate_limit.skip_reason, std::nullopt);\n}\n\n}  // namespace\n}  // namespace crashpad::test\n"
  },
  {
    "path": "handler/crash_report_upload_thread.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/crash_report_upload_thread.h\"\n\n#include <errno.h>\n#include <time.h>\n\n#include <algorithm>\n#include <map>\n#include <memory>\n#include <vector>\n\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"client/settings.h\"\n#include \"handler/crash_report_upload_rate_limit.h\"\n#include \"handler/minidump_to_upload_parameters.h\"\n#include \"snapshot/minidump/process_snapshot_minidump.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/net/http_body.h\"\n#include \"util/net/http_multipart_builder.h\"\n#include \"util/net/http_transport.h\"\n#include \"util/net/url.h\"\n#include \"util/stdlib/map_insert.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include \"handler/mac/file_limit_annotation.h\"\n#endif  // BUILDFLAG(IS_APPLE)\n\n#if BUILDFLAG(IS_IOS)\n#include \"util/ios/scoped_background_task.h\"\n#endif  // BUILDFLAG(IS_IOS)\n\nnamespace crashpad {\n\nnamespace {\n\n// The number of seconds to wait between checking for pending reports.\nconst int kRetryWorkIntervalSeconds = 15 * 60;\n\n#if BUILDFLAG(IS_IOS)\n// The number of times to attempt to upload a pending report, repeated on\n// failure. Attempts will happen once per launch, once per call to\n// ReportPending(), and, if Options.watch_pending_reports is true, once every\n// kRetryWorkIntervalSeconds. Currently iOS only.\nconst int kRetryAttempts = 5;\n#endif\n\n// Wraps a reference to a no-args function (which can be empty). When this\n// object goes out of scope, invokes the function if it is non-empty.\n//\n// The lifetime of the function must outlive the lifetime of this object.\nclass ScopedFunctionInvoker final {\n public:\n  ScopedFunctionInvoker(const std::function<void()>& function)\n      : function_(function) {}\n  ScopedFunctionInvoker(const ScopedFunctionInvoker&) = delete;\n  ScopedFunctionInvoker& operator=(const ScopedFunctionInvoker&) = delete;\n\n  ~ScopedFunctionInvoker() {\n    if (function_) {\n      function_();\n    }\n  }\n\n private:\n  const std::function<void()>& function_;\n};\n\n}  // namespace\n\nCrashReportUploadThread::CrashReportUploadThread(\n    CrashReportDatabase* database,\n    const std::string& url,\n    const Options& options,\n    ProcessPendingReportsObservationCallback callback)\n    : options_(options),\n      callback_(callback),\n      url_(url),\n      // When watching for pending reports, check every 15 minutes, even in the\n      // absence of a signal from the handler thread. This allows for failed\n      // uploads to be retried periodically, and for pending reports written by\n      // other processes to be recognized.\n      thread_(options.watch_pending_reports ? kRetryWorkIntervalSeconds\n                                            : WorkerThread::kIndefiniteWait,\n              this),\n      known_pending_report_uuids_(),\n      database_(database) {\n  DCHECK(!url_.empty());\n}\n\nCrashReportUploadThread::~CrashReportUploadThread() {\n}\n\nvoid CrashReportUploadThread::ReportPending(const UUID& report_uuid) {\n  known_pending_report_uuids_.PushBack(report_uuid);\n  if (thread_.is_running())\n    thread_.DoWorkNow();\n}\n\nvoid CrashReportUploadThread::Start() {\n  thread_.Start(\n      options_.watch_pending_reports ? 0.0 : WorkerThread::kIndefiniteWait);\n}\n\nvoid CrashReportUploadThread::Stop() {\n  thread_.Stop();\n}\n\nvoid CrashReportUploadThread::ProcessPendingReports() {\n#if BUILDFLAG(IS_IOS)\n  internal::ScopedBackgroundTask scoper(\"CrashReportUploadThread\");\n#endif  // BUILDFLAG(IS_IOS)\n\n  // If callback_ is non-empty, invoke it when this function returns after\n  // uploads complete (regardless of whether or not that succeeded).\n  ScopedFunctionInvoker scoped_function_invoker(callback_);\n\n  std::vector<UUID> known_report_uuids = known_pending_report_uuids_.Drain();\n  for (const UUID& report_uuid : known_report_uuids) {\n    CrashReportDatabase::Report report;\n    if (database_->LookUpCrashReport(report_uuid, &report) !=\n        CrashReportDatabase::kNoError) {\n      continue;\n    }\n\n    ProcessPendingReport(report);\n\n    // Respect Stop() being called after at least one attempt to process a\n    // report.\n    if (!thread_.is_running()) {\n      return;\n    }\n  }\n\n  // Known pending reports are always processed (above). The rest of this\n  // function is concerned with scanning for pending reports not already known\n  // to this thread.\n  if (!options_.watch_pending_reports) {\n    return;\n  }\n\n  std::vector<CrashReportDatabase::Report> reports;\n  if (database_->GetPendingReports(&reports) != CrashReportDatabase::kNoError) {\n    // The database is sick. It might be prudent to stop trying to poke it from\n    // this thread by abandoning the thread altogether. On the other hand, if\n    // the problem is transient, it might be possible to talk to it again on the\n    // next pass. For now, take the latter approach.\n    return;\n  }\n\n  for (const CrashReportDatabase::Report& report : reports) {\n    if (std::find(known_report_uuids.begin(),\n                  known_report_uuids.end(),\n                  report.uuid) != known_report_uuids.end()) {\n      // An attempt to process the report already occurred above. The report is\n      // still pending, so upload must have failed. Don’t retry it immediately,\n      // it can wait until at least the next pass through this method.\n      continue;\n    }\n\n    ProcessPendingReport(report);\n\n    // Respect Stop() being called after at least one attempt to process a\n    // report.\n    if (!thread_.is_running()) {\n      return;\n    }\n  }\n}\n\nvoid CrashReportUploadThread::ProcessPendingReport(\n    const CrashReportDatabase::Report& report) {\n#if BUILDFLAG(IS_APPLE)\n  RecordFileLimitAnnotation();\n#endif  // BUILDFLAG(IS_APPLE)\n\n  Settings* const settings = database_->GetSettings();\n\n  bool uploads_enabled;\n  if (!report.upload_explicitly_requested &&\n      (!settings->GetUploadsEnabled(&uploads_enabled) || !uploads_enabled)) {\n    // Don’t attempt an upload if there’s no URL to upload to. Allow upload if\n    // it has been explicitly requested by the user, otherwise, respect the\n    // upload-enabled state stored in the database’s settings.\n    database_->SkipReportUpload(report.uuid,\n                                Metrics::CrashSkippedReason::kUploadsDisabled);\n    return;\n  }\n\n  if (ShouldRateLimitUpload(report))\n    return;\n\n#if BUILDFLAG(IS_IOS)\n  if (ShouldRateLimitRetry(report))\n    return;\n#endif  // BUILDFLAG(IS_IOS)\n\n  std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;\n  CrashReportDatabase::OperationStatus status =\n      database_->GetReportForUploading(report.uuid, &upload_report);\n  switch (status) {\n    case CrashReportDatabase::kNoError:\n      break;\n\n    case CrashReportDatabase::kBusyError:\n    case CrashReportDatabase::kReportNotFound:\n      // Someone else may have gotten to it first. If they’re working on it now,\n      // this will be kBusyError. If they’ve already finished with it, it’ll be\n      // kReportNotFound.\n      return;\n\n    case CrashReportDatabase::kFileSystemError:\n    case CrashReportDatabase::kDatabaseError:\n      // In these cases, SkipReportUpload() might not work either, but it’s best\n      // to at least try to get the report out of the way.\n      database_->SkipReportUpload(report.uuid,\n                                  Metrics::CrashSkippedReason::kDatabaseError);\n      return;\n\n    case CrashReportDatabase::kCannotRequestUpload:\n      NOTREACHED();\n  }\n\n  std::string response_body;\n  UploadResult upload_result =\n      UploadReport(upload_report.get(), &response_body);\n  switch (upload_result) {\n    case UploadResult::kSuccess:\n      database_->RecordUploadComplete(std::move(upload_report), response_body);\n      break;\n    case UploadResult::kPermanentFailure:\n      upload_report.reset();\n      database_->SkipReportUpload(\n          report.uuid, Metrics::CrashSkippedReason::kPrepareForUploadFailed);\n      break;\n    case UploadResult::kRetry:\n#if BUILDFLAG(IS_IOS)\n      if (upload_report->upload_attempts > kRetryAttempts) {\n        upload_report.reset();\n        database_->SkipReportUpload(report.uuid,\n                                    Metrics::CrashSkippedReason::kUploadFailed);\n      } else {\n        Metrics::CrashUploadSkipped(\n            Metrics::CrashSkippedReason::kUploadFailedButCanRetry);\n        retry_uuid_time_map_[report.uuid] =\n            time(nullptr) +\n            (1 << upload_report->upload_attempts) * kRetryWorkIntervalSeconds;\n      }\n#else\n      upload_report.reset();\n\n      // TODO(mark): Deal with retries properly: don’t call SkipReportUplaod()\n      // if the result was kRetry and the report hasn’t already been retried\n      // too many times.\n      database_->SkipReportUpload(report.uuid,\n                                  Metrics::CrashSkippedReason::kUploadFailed);\n#endif\n      break;\n  }\n}\n\nCrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(\n    const CrashReportDatabase::UploadReport* report,\n    std::string* response_body) {\n  std::map<std::string, std::string> parameters;\n\n  FileReader* reader = report->Reader();\n  FileOffset start_offset = reader->SeekGet();\n  if (start_offset < 0) {\n    return UploadResult::kPermanentFailure;\n  }\n\n  // Ignore any errors that might occur when attempting to interpret the\n  // minidump file. This may result in its being uploaded with few or no\n  // parameters, but as long as there’s a dump file, the server can decide what\n  // to do with it.\n  ProcessSnapshotMinidump minidump_process_snapshot;\n  if (minidump_process_snapshot.Initialize(reader)) {\n    parameters =\n        BreakpadHTTPFormParametersFromMinidump(&minidump_process_snapshot);\n  }\n\n  if (!reader->SeekSet(start_offset)) {\n    return UploadResult::kPermanentFailure;\n  }\n\n  HTTPMultipartBuilder http_multipart_builder;\n  http_multipart_builder.SetGzipEnabled(options_.upload_gzip);\n\n  static constexpr char kMinidumpKey[] = \"upload_file_minidump\";\n\n  for (const auto& kv : parameters) {\n    if (kv.first == kMinidumpKey) {\n      LOG(WARNING) << \"reserved key \" << kv.first << \", discarding value \"\n                   << kv.second;\n    } else {\n      http_multipart_builder.SetFormData(kv.first, kv.second);\n    }\n  }\n\n  for (const auto& it : report->GetAttachments()) {\n    http_multipart_builder.SetFileAttachment(\n        it.first, it.first, it.second, \"application/octet-stream\");\n  }\n\n  http_multipart_builder.SetFileAttachment(kMinidumpKey,\n                                           report->uuid.ToString() + \".dmp\",\n                                           reader,\n                                           \"application/octet-stream\");\n\n  std::unique_ptr<HTTPTransport> http_transport(HTTPTransport::Create());\n  if (!http_transport) {\n    return UploadResult::kPermanentFailure;\n  }\n\n  HTTPHeaders content_headers;\n  http_multipart_builder.PopulateContentHeaders(&content_headers);\n  for (const auto& content_header : content_headers) {\n    http_transport->SetHeader(content_header.first, content_header.second);\n  }\n  http_transport->SetBodyStream(http_multipart_builder.GetBodyStream());\n  // TODO(mark): The timeout should be configurable by the client.\n  http_transport->SetTimeout(internal::kUploadReportTimeoutSeconds);\n\n  std::string url = url_;\n  if (options_.identify_client_via_url) {\n    // Add parameters to the URL which identify the client to the server.\n    static constexpr struct {\n      const char* key;\n      const char* url_field_name;\n    } kURLParameterMappings[] = {\n        {\"prod\", \"product\"},\n        {\"ver\", \"version\"},\n        {\"guid\", \"guid\"},\n    };\n\n    for (const auto& parameter_mapping : kURLParameterMappings) {\n      const auto it = parameters.find(parameter_mapping.key);\n      if (it != parameters.end()) {\n        url.append(\n            base::StringPrintf(\"%c%s=%s\",\n                               url.find('?') == std::string::npos ? '?' : '&',\n                               parameter_mapping.url_field_name,\n                               URLEncode(it->second).c_str()));\n      }\n    }\n  }\n  http_transport->SetURL(url);\n\n  if (!http_transport->ExecuteSynchronously(response_body)) {\n    return UploadResult::kRetry;\n  }\n\n  return UploadResult::kSuccess;\n}\n\nvoid CrashReportUploadThread::DoWork(const WorkerThread* thread) {\n  ProcessPendingReports();\n}\n\nbool CrashReportUploadThread::ShouldRateLimitUpload(\n    const CrashReportDatabase::Report& report) {\n  if (report.upload_explicitly_requested || !options_.rate_limit)\n    return false;\n\n  Settings* const settings = database_->GetSettings();\n  time_t last_upload_attempt_time;\n  if (settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) {\n    const time_t now = time(nullptr);\n    constexpr int kUploadAttemptIntervalSeconds = 60 * 60;  // 1 hour\n    const auto should_rate_limit = ShouldRateLimit(\n        now, last_upload_attempt_time, kUploadAttemptIntervalSeconds);\n    if (should_rate_limit.skip_reason.has_value()) {\n      database_->SkipReportUpload(report.uuid, *should_rate_limit.skip_reason);\n      return true;\n    }\n  }\n  return false;\n}\n\n#if BUILDFLAG(IS_IOS)\nbool CrashReportUploadThread::ShouldRateLimitRetry(\n    const CrashReportDatabase::Report& report) {\n  if (retry_uuid_time_map_.find(report.uuid) != retry_uuid_time_map_.end()) {\n    time_t now = time(nullptr);\n    if (now < retry_uuid_time_map_[report.uuid]) {\n      return true;\n    } else {\n      retry_uuid_time_map_.erase(report.uuid);\n    }\n  }\n  return false;\n}\n#endif\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/crash_report_upload_thread.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_\n#define CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_\n\n#include <functional>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"build/build_config.h\"\n#include \"client/crash_report_database.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/stdlib/thread_safe_vector.h\"\n#include \"util/thread/stoppable.h\"\n#include \"util/thread/worker_thread.h\"\n\nnamespace crashpad {\n\n//! \\brief A thread that processes pending crash reports in a\n//!     CrashReportDatabase by uploading them or marking them as completed\n//!     without upload, as desired.\n//!\n//! A producer of crash reports should notify an object of this class that a new\n//! report has been added to the database by calling ReportPending().\n//!\n//! Independently of being triggered by ReportPending(), objects of this class\n//! can periodically examine the database for pending reports. This allows\n//! failed upload attempts for reports left in the pending state to be retried.\n//! It also catches reports that are added without a ReportPending() signal\n//! being caught. This may happen if crash reports are added to the database by\n//! other processes.\nclass CrashReportUploadThread : public WorkerThread::Delegate,\n                                public Stoppable {\n public:\n   //! \\brief Options to be passed to the CrashReportUploadThread constructor.\n   struct Options {\n    //! Whether client identifying parameters like product name or version\n    //! should be added to the URL.\n    bool identify_client_via_url;\n\n    //! Whether uploads should be throttled to a (currently hardcoded) rate.\n    bool rate_limit;\n\n    //! Whether uploads should use `gzip` compression.\n    bool upload_gzip;\n\n    //! Whether to periodically check for new pending reports not already known\n    //! to exist. When `false`, only an initial upload attempt will be made for\n    //! reports known to exist by having been added by the ReportPending()\n    //! method. No scans for new pending reports will be conducted.\n    bool watch_pending_reports;\n  };\n\n  //! \\brief Observation callback invoked each time the in-process handler\n  //!     finishes processing and attempting to upload on-disk crash reports\n  //!     (whether or not the uploads succeeded).\n  //!\n  //! This callback is copied into this object. Any references or pointers\n  //! inside must outlive this object.\n  //!\n  //! The callback might be invoked on a background thread, so clients must\n  //! synchronize appropriately.\n  using ProcessPendingReportsObservationCallback = std::function<void()>;\n\n  //! \\brief Constructs a new object.\n  //!\n  //! \\param[in] database The database to upload crash reports from.\n  //! \\param[in] url The URL of the server to upload crash reports to.\n  //! \\param[in] options Options for the report uploads.\n  //! \\param[in] callback Optional callback invoked zero or more times\n  //!     on a background thread each time the this object finishes\n  //!     processing and attempting to upload on-disk crash reports.\n  //!     If this callback is empty, it is not invoked.\n  CrashReportUploadThread(CrashReportDatabase* database,\n                          const std::string& url,\n                          const Options& options,\n                          ProcessPendingReportsObservationCallback callback);\n\n  CrashReportUploadThread(const CrashReportUploadThread&) = delete;\n  CrashReportUploadThread& operator=(const CrashReportUploadThread&) = delete;\n\n  ~CrashReportUploadThread();\n\n  //! \\brief Informs the upload thread that a new pending report has been added\n  //!     to the database.\n  //!\n  //! \\param[in] report_uuid The unique identifier of the newly added pending\n  //!     report.\n  //!\n  //! This method may be called from any thread.\n  void ReportPending(const UUID& report_uuid);\n\n  // Stoppable:\n\n  //! \\brief Starts a dedicated upload thread, which executes ThreadMain().\n  //!\n  //! This method may only be be called on a newly-constructed object or after\n  //! a call to Stop().\n  void Start() override;\n\n  //! \\brief Stops the upload thread.\n  //!\n  //! The upload thread will terminate after completing whatever task it is\n  //! performing. If it is not performing any task, it will terminate\n  //! immediately. This method blocks while waiting for the upload thread to\n  //! terminate.\n  //!\n  //! This method must only be called after Start(). If Start() has been called,\n  //! this method must be called before destroying an object of this class.\n  //!\n  //! This method may be called from any thread other than the upload thread.\n  //! It is expected to only be called from the same thread that called Start().\n  void Stop() override;\n\n  //! \\return `true` if the thread is running, `false` if it is not.\n  bool is_running() const { return thread_.is_running(); }\n\n private:\n  //! \\brief The result code from UploadReport().\n  enum class UploadResult {\n    //! \\brief The crash report was uploaded successfully.\n    kSuccess,\n\n    //! \\brief The crash report upload failed in such a way that recovery is\n    //!     impossible.\n    //!\n    //! No further upload attempts should be made for the report.\n    kPermanentFailure,\n\n    //! \\brief The crash report upload failed, but it might succeed again if\n    //!     retried in the future.\n    //!\n    //! If the report has not already been retried too many times, the caller\n    //! may arrange to call UploadReport() for the report again in the future,\n    //! after a suitable delay.\n    kRetry,\n  };\n\n  //! \\brief Calls ProcessPendingReport() on pending reports.\n  //!\n  //! Assuming Stop() has not been called, this will process reports that the\n  //! object has been made aware of in ReportPending(). Additionally, if the\n  //! object was constructed with \\a watch_pending_reports, it will also scan\n  //! the crash report database for other pending reports, and process those as\n  //! well.\n  void ProcessPendingReports();\n\n  //! \\brief Processes a single pending report from the database.\n  //!\n  //! \\param[in] report The crash report to process.\n  //!\n  //! If report upload is enabled, this method attempts to upload \\a report by\n  //! calling UplaodReport(). If the upload is successful, the report will be\n  //! marked as “completed” in the database. If the upload fails and more\n  //! retries are desired, the report’s upload-attempt count and\n  //! last-upload-attempt time will be updated in the database and it will\n  //! remain in the “pending” state. If the upload fails and no more retries are\n  //! desired, or report upload is disabled, it will be marked as “completed” in\n  //! the database without ever having been uploaded.\n  void ProcessPendingReport(const CrashReportDatabase::Report& report);\n\n  //! \\brief Attempts to upload a crash report.\n  //!\n  //! \\param[in] report The report to upload. The caller is responsible for\n  //!     calling CrashReportDatabase::GetReportForUploading() before calling\n  //!     this method, and for calling\n  //!     CrashReportDatabase::RecordUploadComplete() after calling this method.\n  //! \\param[out] response_body If the upload attempt is successful, this will\n  //!     be set to the response body sent by the server. Breakpad-type servers\n  //!     provide the crash ID assigned by the server in the response body.\n  //!\n  //! \\return A member of UploadResult indicating the result of the upload\n  //!    attempt.\n  UploadResult UploadReport(const CrashReportDatabase::UploadReport* report,\n                            std::string* response_body);\n\n  // WorkerThread::Delegate:\n  //! \\brief Calls ProcessPendingReports() in response to ReportPending() having\n  //!     been called on any thread, as well as periodically on a timer.\n  void DoWork(const WorkerThread* thread) override;\n\n  //! \\brief Rate-limit uploads.\n  //!\n  //! \\param[in] report The crash report to process.\n  //!\n  //! This currently implements very simplistic rate-limiting, compatible with\n  //! the Breakpad client, where the strategy is to permit one upload attempt\n  //! per hour, and retire reports that would exceed this limit or for which the\n  //! upload fails on the first attempt.\n  //! If upload was requested explicitly (i.e. by user action), do not throttle\n  //! the upload.\n  //!\n  //! TODO(mark): Provide a proper rate-limiting strategy and allow for failed\n  //! upload attempts to be retried.\n  bool ShouldRateLimitUpload(const CrashReportDatabase::Report& report);\n\n#if BUILDFLAG(IS_IOS)\n  //! \\brief Rate-limit report retries.\n  //!\n  //! \\param[in] report The crash report to process.\n  //!\n  //! This implements a per-report retry rate limit (as opposed to per upload\n  //! rate limit in ShouldRateLimitUpload). When a report upload ends in a retry\n  //! state, an in-memory only timestamp is stored in |retry_uuid_time_map_|\n  //! with the next possible retry time. This timestamp is a backoff from the\n  //! main thread work interval, doubling on each attemt. Because this is only\n  //! stored in memory, on restart reports in the retry state will always be\n  //! tried once, and then fall back into the next backoff. This continues until\n  //! kRetryAttempts is reached.\n  bool ShouldRateLimitRetry(const CrashReportDatabase::Report& report);\n#endif\n\n  const Options options_;\n  const ProcessPendingReportsObservationCallback callback_;\n  const std::string url_;\n  WorkerThread thread_;\n  ThreadSafeVector<UUID> known_pending_report_uuids_;\n#if BUILDFLAG(IS_IOS)\n  // This is not thread-safe, and only used by the worker thread.\n  std::map<UUID, time_t> retry_uuid_time_map_;\n#endif\n  CrashReportDatabase* database_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_\n"
  },
  {
    "path": "handler/crashpad_handler.md",
    "content": "<!--\nCopyright 2014 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# crashpad_handler(8)\n\n## Name\n\ncrashpad_handler—Crashpad’s exception handler server\n\n## Synopsis\n\n**crashpad_handler** [_OPTION…_]\n\n## Description\n\nThis program is Crashpad’s main exception-handling server. It is responsible for\ncatching exceptions, writing crash reports, and uploading them to a crash report\ncollection server. Uploads are disabled by default, and can only be enabled by a\nCrashpad client using the Crashpad client library, typically in response to a\nuser requesting this behavior.\n\nOn macOS, this server may be started by its initial client, in which case it\nperforms a handshake with this client via a pipe established by the client that\nis inherited by the server, referenced by the **--handshake-fd** argument.\nDuring the handshake, the server furnishes the client with a send right that the\nclient may use as an exception port. The server retains the corresponding\nreceive right, which it monitors for exception messages. When the receive right\nloses all senders, the server exits after allowing any upload in progress to\ncomplete.\n\nAlternatively, on macOS, this server may be started from launchd(8), where it\nreceives the Mach service name in a **--mach-service** argument. It checks in\nwith the bootstrap server under this service name, and clients may look it up\nwith the bootstrap server under this service name. It monitors this service for\nexception messages. Upon receipt of `SIGTERM`, the server exits after allowing\nany upload in progress to complete. `SIGTERM` is normally sent by launchd(8)\nwhen it determines that the server should exit.\n\nOn Windows, clients register with this server by communicating with it via the\nnamed pipe identified by the **--pipe-name** argument. Alternatively, the server\ncan inherit an already-created pipe from a parent process by using the\n**--initial-client-data** mechanism. That argument also takes all of the\narguments that would normally be passed in a registration message, and so\nconstitutes registration of the first client. Subsequent clients may then\nregister by communicating with the server via the pipe. During registration, a\nclient provides the server with an OS event object that it will signal should it\ncrash. The server obtains the client’s process handle and waits on the crash\nevent object for a crash, as well as the client’s process handle for the client\nto exit cleanly without crashing. When a server started via the\n**--initial-client-data** mechanism loses all of its clients, it exits after\nallowing any upload in progress to complete.\n\nOn Windows, this executable is built by default as a Windows GUI app, so no\nconsole will appear in normal usage. This is the version that will typically be\nused. A second copy is also made with a `.com` extension, rather than `.exe`. In\nthis second copy, the PE header is modified to indicate that it’s a console app.\nThis is useful because the `.com` is found in the path before the `.exe`, so\nwhen run normally from a shell using only the basename (without an explicit\n`.com` or `.exe` extension), the `.com` console version will be chosen, and so\nstdio will be hooked up as expected to the parent console so that logging output\nwill be visible.\n\nOn Linux/Android, the handler may create a crash dump for its parent process\nusing **--trace-parent-with-exception**. In this mode, the handler process\ncreates a crash dump for its parent and exits. Alternatively, the handler may\nbe launched with **--initial-client-fd** which will start the server connected\nto an initial client. The server will exit when all connected client sockets are\nclosed.\n\nIt is not normally appropriate to invoke this program directly. Usually, it will\nbe invoked by a Crashpad client using the Crashpad client library, or started by\nanother system service. On macOS, arbitrary programs may be run with a Crashpad\nhandler by using [run_with_crashpad(1)](../tools/run_with_crashpad.md) to\nestablish the Crashpad client environment before running a program.\n\n## Options\n\n * **--annotation**=_KEY_=_VALUE_\n\n   Sets a process-level annotation mapping _KEY_ to _VALUE_ in each crash report\n   that is written. This option may appear zero, one, or multiple times.\n\n   Most annotations should be provided by the Crashpad client as module-level\n   annotations instead of process-level annotations. Module-level annotations\n   are more flexible in that they can be modified and cleared during the client\n   program’s lifetime. Module-level annotations can be set via the Crashpad\n   client library. Process-level annotations are useful for annotations that the\n   collection server requires be present, that have fixed values, and for cases\n   where a program that does not use the Crashpad client library is being\n   monitored.\n\n   Breakpad-type collection servers only require the `\"prod\"` and `\"ver\"`\n   annotations, which should be set to the product name or identifier and\n   product version, respectively. It is unusual to specify other annotations as\n   process-level annotations via this argument.\n\n * **--database**=_PATH_\n\n   Use _PATH_ as the path to the Crashpad crash report database. This option is\n   required. Crash reports are written to this database, and if uploads are\n   enabled, uploaded from this database to a crash report collection server. If\n   the database does not exist, it will be created, provided that the parent\n   directory of _PATH_ exists.\n\n * **--handshake-fd**=_FD_\n\n   Perform the handshake with the initial client on the file descriptor at _FD_.\n   Either this option or **--mach-service**, but not both, is required. This\n   option is only valid on macOS.\n\n * **--initial-client-data**=*HANDLE_request_crash_dump*,*HANDLE_request_non_crash_dump*,*HANDLE_non_crash_dump_completed*,*HANDLE_first_pipe_instance*,*HANDLE_client_process*,*Address_crash_exception_information*,*Address_non_crash_exception_information*,*Address_debug_critical_section*\n\n   Register the initial client using the inherited handles and data provided.\n   For more information on the argument’s format, see the implementations of\n   `CrashpadClient` and `ExceptionHandlerServer`. Either this option or\n   **--pipe-name**, but not both, is required. This option is only valid on\n   Windows.\n\n   When this option is present, the server creates a new named pipe at a random\n   name and informs its client of the name. The server waits for at least one\n   client to register, and exits when all clients have exited, after waiting for\n   any uploads in progress to complete.\n\n * **--initial-client-fd**=_FD_\n\n   Wait for client requests on _FD_. Either this option or\n   **--trace-parent-with-exception**, but not both, is required. The handler\n   exits when all client connections have been closed. This option is only valid\n   on Linux platforms.\n\n * **--mach-service**=_SERVICE_\n\n   Check in with the bootstrap server under the name _SERVICE_. Either this\n   option or **--handshake-fd**, but not both, is required. This option is only\n   valid on macOS.\n\n   _SERVICE_ may already be reserved with the bootstrap server in cases where\n   this tool is started by launchd(8) as a result of a message being sent to a\n   service declared in a job’s `MachServices` dictionary (see launchd.plist(5)).\n   The service name may also be completely unknown to the system.\n\n * **--metrics-dir**=_DIR_\n\n   Metrics information will be written to _DIR_. This option only has an effect\n   when built as part of Chromium. In non-Chromium builds, and in the absence of\n   this option, metrics information will not be written.\n\n * **--monitor-self**\n\n   Causes a second instance of the Crashpad handler program to be started,\n   monitoring the original instance for exceptions. The original instance will\n   become a client of the second one. The second instance will be started with\n   the same **--annotation**, **--database**, **--monitor-self-annotation**,\n   **--no-rate-limit**, **--no-upload-gzip**, and **--url** arguments as the\n   original one. The second instance will always be started with a\n   **--no-periodic-tasks** argument, and will not be started with a\n   **--metrics-dir** argument even if the original instance was.\n\n   Where supported by the underlying operating system, the second instance will\n   be restarted should it exit before the first instance. The second instance\n   will not be eligible to be started asynchronously.\n\n * **--monitor-self-annotation**=_KEY_=_VALUE_\n\n   Sets a module-level annotation mapping _KEY_ to _VALUE_ in the Crashpad\n   handler. This option may appear zero, one, or more times.\n\n   If **--monitor-self** is in use, the second instance of the Crashpad handler\n   program will find these annotations stored in the original instance and will\n   include them in any crash reports written for the original instance.\n\n   These annotations will only appear in crash reports written for the Crashpad\n   handler itself. To apply a process-level annotation to all crash reports\n   written by an instance of the Crashpad handler, use **--annotation** instead.\n\n * **--monitor-self-argument**=_ARGUMENT_\n\n   When directed by **--monitor-self** to start a second instance of the\n   Crashpad handler program, the second instance will be started with _ARGUMENT_\n   as one of its arguments. This option may appear zero, one, or more times.\n   This option has no effect in the absence of **--monitor-self**.\n\n   This supports embedding the Crashpad handler into a multi-purpose executable\n   that dispatches to the desired entry point based on a command-line argument.\n   To prevent excessive accumulation of handler processes, _ARGUMENT_ must not\n   be `--monitor-self`.\n\n* **--no-identify-client-via-url**\n\n   Do not add client-identifying fields to the URL. By default, `\"prod\"`,\n   `\"ver\"`, and `\"guid\"` annotations are added to the upload URL as name-value\n   pairs `\"product\"`, `\"version\"`, and `\"guid\"`, respectively. Using this\n   option disables that behavior.\n\n * **--no-periodic-tasks**\n\n   Do not scan for new pending crash reports or prune the crash report database.\n   Only crash reports recorded by this instance of the Crashpad handler will\n   become eligible for upload in this instance, and only a single initial upload\n   attempt will be made.\n\n   This option is not intended for general use. It is provided to prevent\n   multiple instances of the Crashpad handler from duplicating the effort of\n   performing the same periodic tasks. In normal use, the first instance of the\n   Crashpad handler will assume the responsibility for performing these tasks,\n   and will provide this argument to any second instance. See\n   **--monitor-self**.\n\n * **--no-rate-limit**\n\n   Do not rate limit the upload of crash reports. By default uploads are\n   throttled to one per hour. Using this option disables that behavior, and\n   Crashpad will attempt to upload all captured reports.\n\n * **--no-upload-gzip**\n\n   Do not use `gzip` compression for uploaded crash reports. Normally, the\n   entire request body is compressed into a `gzip` stream and transmitted with\n   `Content-Encoding: gzip`. This option disables compression, and is intended\n   for use with collection servers that don’t accept uploads compressed in this\n   way.\n\n * **--no-write-minidump-to-database**\n\n   Do not write the minidump to database. Normally, the minidump is written to\n   database for upload. Use this option with **--write-minidump-to-log** to\n   only write the minidump to log. This option is only available to Android.\n\n * **--pipe-name**=_PIPE_\n\n   Listen on the given pipe name for connections from clients. _PIPE_ must be of\n   the form `\\\\.\\pipe\\<somename>`. Either this option or\n   **--initial-client-data**, but not both, is required. This option is only\n   valid on Windows.\n\n   When this option is present, the server creates a named pipe at _PIPE_, a\n   name known to both the server and its clients. The server continues running\n   even after all clients have exited.\n\n * **--reset-own-crash-exception-port-to-system-default**\n\n   Causes the exception handler server to set its own crash handler to the\n   system default before beginning operation. This is only expected to be useful\n   in cases where the server inherits an inappropriate crash handler from its\n   parent process. This option is only valid on macOS. Use of this option is\n   discouraged. It should not be used absent extraordinary circumstances.\n\n * **--sanitization-information**=_SANITIZATION-INFORMATION-ADDRESS_\n\n   Provides sanitization settings in a SanitizationInformation struct at\n   _SANITIZATION-INFORMATION-ADDRESS_. This option requires\n   **--trace-parent-with-exception** and is only valid on Linux platforms.\n\n * **--shared-client-connection**\n\n   Indicates that the file descriptor provided by **--initial-client-fd** is\n   shared among mulitple clients. Using a broker process is not supported for\n   clients using this option. This option is only valid on Linux platforms.\n\n * **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_\n\n   Causes the handler process to trace its parent process and exit. The parent\n   process should have an ExceptionInformation struct at\n   _EXCEPTION-INFORMATION-ADDRESS_. This option is only valid on Linux\n   platforms.\n\n * **--url**=_URL_\n\n   If uploads are enabled, sends crash reports to the Breakpad-type crash report\n   collection server at _URL_. Uploads are disabled by default, and can only be\n   enabled for a database by a Crashpad client using the Crashpad client\n   library, typically in response to a user requesting this behavior. If this\n   option is not specified, this program will behave as if uploads are disabled.\n\n * **--use-cros-crash-reporter**\n\n   Causes crash reports to be passed via an in-memory file to\n   `/sbin/crash_reporter` instead of storing them in the database. The database\n   is still used for Crashpad settings. This option is only valid on Chromium\n   OS.\n\n* **--write-minidump-to-log**\n\n  Write the minidump to log. By default the minidump is only written to\n  database. Use this option with **--no-write-minidump-to-database** to only\n  write the minidump to log. This option is only available to Android.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Exit Status\n\n * **0**\n\n   Success.\n\n * **1**\n\n   Failure, with a message printed to the standard error stream.\n\n## See Also\n\n[catch_exception_tool(1)](../tools/mac/catch_exception_tool.md),\n[crashpad_database_util(1)](../tools/crashpad_database_util.md),\n[generate_dump(1)](../tools/generate_dump.md),\n[run_with_crashpad(1)](../tools/run_with_crashpad.md)\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2014 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "handler/crashpad_handler_main.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/handler_main.h\"\n\nnamespace crashpad {\n\nextern \"C\" {\n\n__attribute__((visibility(\"default\"), used)) int CrashpadHandlerMain(\n    int argc,\n    char* argv[]) {\n  return HandlerMain(argc, argv, nullptr);\n}\n\n}  // extern \"C\"\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/crashpad_handler_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <string.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"client/crash_report_database.h\"\n#include \"client/crashpad_client.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"test/test_paths.h\"\n#include \"test/win/win_multiprocess_with_temp_dir.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/capture_context.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr DWORD kExpectedExitCode = 0x1CEB00DA;\n\nvoid StartAndCrashWithExtendedHandler(const base::FilePath& temp_dir) {\n  base::FilePath handler_path = TestPaths::BuildArtifact(\n      L\"handler\", L\"extended_handler\", TestPaths::FileType::kExecutable);\n\n  CrashpadClient client;\n  ASSERT_TRUE(client.StartHandler(handler_path,\n                                  temp_dir,\n                                  base::FilePath(),\n                                  \"\",\n                                  std::map<std::string, std::string>(),\n                                  std::vector<std::string>(),\n                                  false,\n                                  false));\n\n  // It appears that the Google Test fixture will catch and handle exceptions\n  // from here. Hence the fabricated crash in favor of raising an exception.\n  EXCEPTION_RECORD exception_record = {kExpectedExitCode,\n                                       EXCEPTION_NONCONTINUABLE};\n  CONTEXT context;\n  CaptureContext(&context);\n  EXCEPTION_POINTERS exception_pointers = {&exception_record, &context};\n  CrashpadClient::DumpAndCrash(&exception_pointers);\n}\n\nclass CrashWithExtendedHandler final : public WinMultiprocessWithTempDir {\n public:\n  CrashWithExtendedHandler() : WinMultiprocessWithTempDir() {}\n  ~CrashWithExtendedHandler() {}\n\n private:\n  void ValidateGeneratedDump();\n\n  void WinMultiprocessParent() override {\n    SetExpectedChildExitCode(kExpectedExitCode);\n  }\n\n  void WinMultiprocessChild() override {\n    StartAndCrashWithExtendedHandler(GetTempDirPath());\n  }\n\n  void WinMultiprocessParentAfterChild(HANDLE child) override {\n    // At this point the child has exited, which means the crash report should\n    // have been written.\n    ValidateGeneratedDump();\n\n    // Delegate the cleanup to the superclass.\n    WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(child);\n  }\n};\n\nvoid CrashWithExtendedHandler::ValidateGeneratedDump() {\n  // Open the database and find the sole dump that should have been created.\n  std::unique_ptr<CrashReportDatabase> database(\n      CrashReportDatabase::Initialize(GetTempDirPath()));\n  ASSERT_TRUE(database);\n\n  std::vector<CrashReportDatabase::Report> reports;\n  ASSERT_EQ(database->GetPendingReports(&reports),\n            CrashReportDatabase::kNoError);\n  ASSERT_EQ(reports.size(), 1u);\n\n  // Open the dump and validate that it has the extension stream with the\n  // expected contents.\n  FileReader reader;\n  ASSERT_TRUE(reader.Open(reports[0].file_path));\n\n  // Read the header.\n  MINIDUMP_HEADER header = {};\n  ASSERT_TRUE(reader.ReadExactly(&header, sizeof(header)));\n\n  // Read the directory.\n  std::vector<MINIDUMP_DIRECTORY> directory(header.NumberOfStreams);\n  ASSERT_TRUE(reader.SeekSet(header.StreamDirectoryRva));\n  ASSERT_TRUE(reader.ReadExactly(directory.data(),\n                                 directory.size() * sizeof(directory[0])));\n\n  // Search for the extension stream.\n  size_t found_extension_streams = 0;\n  for (const auto& entry : directory) {\n    if (entry.StreamType == 0xCAFEBABE) {\n      ++found_extension_streams;\n\n      ASSERT_TRUE(reader.SeekSet(entry.Location.Rva));\n\n      std::vector<char> data;\n      data.resize(entry.Location.DataSize);\n\n      ASSERT_TRUE(reader.ReadExactly(data.data(), data.size()));\n\n      static constexpr char kExpectedData[] = \"Injected extension stream!\";\n      EXPECT_EQ(memcmp(kExpectedData, data.data(), sizeof(kExpectedData)), 0);\n    }\n  }\n\n  EXPECT_EQ(found_extension_streams, 1u);\n}\n\n#if defined(ADDRESS_SANITIZER)\n// https://crbug.com/845011\n#define MAYBE_ExtensibilityCalloutsWork DISABLED_ExtensibilityCalloutsWork\n#else\n#define MAYBE_ExtensibilityCalloutsWork ExtensibilityCalloutsWork\n#endif\nTEST(CrashpadHandler, MAYBE_ExtensibilityCalloutsWork) {\n  WinMultiprocessWithTempDir::Run<CrashWithExtendedHandler>();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/crashpad_handler_test_extended_handler.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <memory>\n\n#include \"build/build_config.h\"\n#include \"handler/handler_main.h\"\n#include \"minidump/test/minidump_user_extension_stream_util.h\"\n#include \"tools/tool_support.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif\n\nnamespace {\n\nclass TestUserStreamDataSource : public crashpad::UserStreamDataSource {\n public:\n  TestUserStreamDataSource() {}\n\n  TestUserStreamDataSource(const TestUserStreamDataSource&) = delete;\n  TestUserStreamDataSource& operator=(const TestUserStreamDataSource&) = delete;\n\n  std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>\n  ProduceStreamData(crashpad::ProcessSnapshot* process_snapshot) override;\n};\n\nstd::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>\nTestUserStreamDataSource::ProduceStreamData(\n    crashpad::ProcessSnapshot* process_snapshot) {\n  static constexpr char kTestData[] = \"Injected extension stream!\";\n\n  return std::make_unique<crashpad::test::BufferExtensionStreamDataSource>(\n      0xCAFEBABE, kTestData, sizeof(kTestData));\n}\n\nint ExtendedHandlerMain(int argc, char* argv[]) {\n  crashpad::UserStreamDataSources user_stream_data_sources;\n  user_stream_data_sources.push_back(\n      std::make_unique<TestUserStreamDataSource>());\n\n  return crashpad::HandlerMain(argc, argv, &user_stream_data_sources);\n}\n\n}  // namespace\n\n#if BUILDFLAG(IS_POSIX)\n\nint main(int argc, char* argv[]) {\n  return ExtendedHandlerMain(argc, argv);\n}\n\n#elif BUILDFLAG(IS_WIN)\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::ToolSupport::Wmain(argc, argv, &ExtendedHandlerMain);\n}\n\n#endif  // BUILDFLAG(IS_POSIX)\n"
  },
  {
    "path": "handler/handler_main.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/handler_main.h\"\n\n#include <errno.h>\n#include <getopt.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <atomic>\n#include <map>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"base/auto_reset.h\"\n#include \"base/files/file_path.h\"\n#include \"base/files/scoped_file.h\"\n#include \"base/logging.h\"\n#include \"base/metrics/persistent_histogram_allocator.h\"\n#include \"base/scoped_generic.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"client/crash_report_database.h\"\n#include \"client/crashpad_client.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/prune_crash_reports.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"handler/crash_report_upload_thread.h\"\n#include \"handler/prune_crash_reports_thread.h\"\n#include \"tools/tool_support.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/misc/paths.h\"\n#include \"util/numeric/in_range_cast.h\"\n#include \"util/stdlib/map_insert.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n#include \"util/string/split_string.h\"\n#include \"util/synchronization/semaphore.h\"\n\n#if BUILDFLAG(IS_CHROMEOS)\n#include \"handler/linux/cros_crash_report_exception_handler.h\"\n#endif\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include <unistd.h>\n\n#include \"handler/linux/crash_report_exception_handler.h\"\n#include \"handler/linux/exception_handler_server.h\"\n#include \"util/posix/signals.h\"\n#elif BUILDFLAG(IS_APPLE)\n#include <libgen.h>\n#include <signal.h>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"handler/mac/crash_report_exception_handler.h\"\n#include \"handler/mac/exception_handler_server.h\"\n#include \"handler/mac/file_limit_annotation.h\"\n#include \"util/mach/bootstrap.h\"\n#include \"util/mach/child_port_handshake.h\"\n#include \"util/posix/close_stdio.h\"\n#include \"util/posix/signals.h\"\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n\n#include \"handler/win/crash_report_exception_handler.h\"\n#include \"util/win/exception_handler_server.h\"\n#include \"util/win/handle.h\"\n#include \"util/win/initial_client_data.h\"\n#include \"util/win/session_end_watcher.h\"\n#endif  // BUILDFLAG(IS_APPLE)\n\nnamespace crashpad {\n\nnamespace {\n\n#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \\\n    BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC)\n#define ATTACHMENTS_SUPPORTED 1\n#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC)\n\nvoid Usage(const base::FilePath& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %\" PRFilePath \" [OPTION]...\\n\"\n\"Crashpad's exception handler server.\\n\"\n\"\\n\"\n\"      --annotation=KEY=VALUE  set a process annotation in each crash report\\n\"\n  // clang-format on\n#if defined(ATTACHMENTS_SUPPORTED)\n      // clang-format off\n\"      --attachment=FILE_PATH  attach specified file to each crash report\\n\"\n\"                              at the time of the crash\\n\"\n  // clang-format on\n#endif  // ATTACHMENTS_SUPPORTED\n      // clang-format off\n\"      --database=PATH         store the crash report database at PATH\\n\"\n  // clang-format on\n#if BUILDFLAG(IS_APPLE)\n      // clang-format off\n\"      --handshake-fd=FD       establish communication with the client over FD\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_WIN)\n      // clang-format off\n\"      --initial-client-data=HANDLE_request_crash_dump,\\n\"\n\"                            HANDLE_request_non_crash_dump,\\n\"\n\"                            HANDLE_non_crash_dump_completed,\\n\"\n\"                            HANDLE_pipe,\\n\"\n\"                            HANDLE_client_process,\\n\"\n\"                            Address_crash_exception_information,\\n\"\n\"                            Address_non_crash_exception_information,\\n\"\n\"                            Address_debug_critical_section\\n\"\n\"                              use precreated data to register initial client\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_WIN)\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n      // clang-format off\n\"      --initial-client-fd=FD  a socket connected to a client.\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n#if BUILDFLAG(IS_APPLE)\n      // clang-format off\n\"      --mach-service=SERVICE  register SERVICE with the bootstrap server\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_APPLE)\n      // clang-format off\n\"      --metrics-dir=DIR       store metrics files in DIR (only in Chromium)\\n\"\n\"      --monitor-self          run a second handler to catch crashes in the first\\n\"\n\"      --monitor-self-annotation=KEY=VALUE\\n\"\n\"                              set a module annotation in the handler\\n\"\n\"      --monitor-self-argument=ARGUMENT\\n\"\n\"                              provide additional arguments to the second handler\\n\"\n\"      --no-identify-client-via-url\\n\"\n\"                              when uploading crash report, don't add\\n\"\n\"                              client-identifying arguments to URL\\n\"\n\"      --no-periodic-tasks     don't scan for new reports or prune the database\\n\"\n\"      --no-rate-limit         don't rate limit crash uploads\\n\"\n\"      --no-upload-gzip        don't use gzip compression when uploading\\n\"\n  // clang-format on\n#if BUILDFLAG(IS_ANDROID)\n      // clang-format off\n\"      --no-write-minidump-to-database\\n\"\n\"                              don't write minidump to database\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_ANDROID)\n#if BUILDFLAG(IS_WIN)\n      // clang-format off\n\"      --pipe-name=PIPE        communicate with the client over PIPE\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_WIN)\n#if BUILDFLAG(IS_APPLE)\n      // clang-format off\n\"      --reset-own-crash-exception-port-to-system-default\\n\"\n\"                              reset the server's exception handler to default\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n      // clang-format off\n\"      --sanitization-information=SANITIZATION_INFORMATION_ADDRESS\\n\"\n\"                              the address of a SanitizationInformation struct.\\n\"\n\"      --shared-client-connection the file descriptor provided by\\n\"\n\"                              --initial-client-fd is shared among multiple\\n\"\n\"                              clients\\n\"\n\"      --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\\n\"\n\"                              request a dump for the handler's parent process\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID)\n      // clang-format off\n\"      --url=URL               send crash reports to this Breakpad server URL,\\n\"\n\"                              only if uploads are enabled for the database\\n\"\n  // clang-format on\n#if BUILDFLAG(IS_CHROMEOS)\n      // clang-format off\n\"      --use-cros-crash-reporter\\n\"\n\"                              pass crash reports to /sbin/crash_reporter\\n\"\n\"                              instead of storing them in the database\\n\"\n\"      --minidump-dir-for-tests=TEST_MINIDUMP_DIR\\n\"\n\"                              causes /sbin/crash_reporter to leave dumps in\\n\"\n\"                              this directory instead of the normal location\\n\"\n\"      --always-allow-feedback\\n\"\n\"                              pass the --always_allow_feedback flag to\\n\"\n\"                              crash_reporter, thus skipping metrics consent\\n\"\n\"                              checks\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_CHROMEOS)\n#if BUILDFLAG(IS_ANDROID)\n      // clang-format off\n\"      --write-minidump-to-log write minidump to log\\n\"\n  // clang-format on\n#endif  // BUILDFLAG(IS_ANDROID)\n      // clang-format off\n\"      --help                  display this help and exit\\n\"\n\"      --version               output version information and exit\\n\",\n          me.value().c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nstruct Options {\n  std::map<std::string, std::string> annotations;\n  std::map<std::string, std::string> monitor_self_annotations;\n  std::string url;\n  base::FilePath database;\n  base::FilePath metrics_dir;\n  std::vector<std::string> monitor_self_arguments;\n#if BUILDFLAG(IS_APPLE)\n  std::string mach_service;\n  int handshake_fd;\n  bool reset_own_crash_exception_port_to_system_default;\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  VMAddress exception_information_address;\n  VMAddress sanitization_information_address;\n  int initial_client_fd;\n  bool shared_client_connection;\n#if BUILDFLAG(IS_ANDROID)\n  bool write_minidump_to_log;\n  bool write_minidump_to_database;\n#endif  // BUILDFLAG(IS_ANDROID)\n#elif BUILDFLAG(IS_WIN)\n  std::string pipe_name;\n  InitialClientData initial_client_data;\n#endif  // BUILDFLAG(IS_APPLE)\n  bool identify_client_via_url;\n  bool monitor_self;\n  bool periodic_tasks;\n  bool rate_limit;\n  bool upload_gzip;\n#if BUILDFLAG(IS_CHROMEOS)\n  bool use_cros_crash_reporter = false;\n  base::FilePath minidump_dir_for_tests;\n  bool always_allow_feedback = false;\n#endif  // BUILDFLAG(IS_CHROMEOS)\n#if defined(ATTACHMENTS_SUPPORTED)\n  std::vector<base::FilePath> attachments;\n#endif  // ATTACHMENTS_SUPPORTED\n};\n\n// Splits |key_value| on '=' and inserts the resulting key and value into |map|.\n// If |key_value| has the wrong format, logs an error and returns false. If the\n// key is already in the map, logs a warning, replaces the existing value, and\n// returns true. If the key and value were inserted into the map, returns true.\n// |argument| is used to give context to logged messages.\nbool AddKeyValueToMap(std::map<std::string, std::string>* map,\n                      const std::string& key_value,\n                      const char* argument) {\n  std::string key;\n  std::string value;\n  if (!SplitStringFirst(key_value, '=', &key, &value)) {\n    LOG(ERROR) << argument << \" requires KEY=VALUE\";\n    return false;\n  }\n\n  std::string old_value;\n  if (!MapInsertOrReplace(map, key, value, &old_value)) {\n    LOG(WARNING) << argument << \" has duplicate key \" << key\n                 << \", discarding value \" << old_value;\n  }\n  return true;\n}\n\n// Calls Metrics::HandlerLifetimeMilestone, but only on the first call. This is\n// to prevent multiple exit events from inadvertently being recorded, which\n// might happen if a crash occurs during destruction in what would otherwise be\n// a normal exit, or if a CallMetricsRecordNormalExit object is destroyed after\n// something else logs an exit event.\nvoid MetricsRecordExit(Metrics::LifetimeMilestone milestone) {\n#if !defined(__cpp_lib_atomic_value_initialization) || \\\n    __cpp_lib_atomic_value_initialization < 201911L\n  static std::atomic_flag metrics_exit_recorded = ATOMIC_FLAG_INIT;\n#else\n  static std::atomic_flag metrics_exit_recorded;\n#endif\n  if (!metrics_exit_recorded.test_and_set()) {\n    Metrics::HandlerLifetimeMilestone(milestone);\n  }\n}\n\n// Calls MetricsRecordExit() to record a failure, and returns EXIT_FAILURE for\n// the convenience of callers in main() which can simply write “return\n// ExitFailure();”.\nint ExitFailure() {\n  MetricsRecordExit(Metrics::LifetimeMilestone::kFailed);\n  return EXIT_FAILURE;\n}\n\nclass CallMetricsRecordNormalExit {\n public:\n  CallMetricsRecordNormalExit() {}\n\n  CallMetricsRecordNormalExit(const CallMetricsRecordNormalExit&) = delete;\n  CallMetricsRecordNormalExit& operator=(const CallMetricsRecordNormalExit&) =\n      delete;\n\n  ~CallMetricsRecordNormalExit() {\n    MetricsRecordExit(Metrics::LifetimeMilestone::kExitedNormally);\n  }\n};\n\n#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \\\n    BUILDFLAG(IS_ANDROID)\n\nvoid HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {\n  MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);\n\n  // Is siginfo->si_code useful? The only interesting values on macOS are 0 (not\n  // useful, signals generated asynchronously such as by kill() or raise()) and\n  // small positive numbers (useful, signal generated via a hardware fault). The\n  // standard specifies these other constants, and while xnu never uses them,\n  // they are intended to denote signals generated asynchronously and are\n  // included here. Additionally, existing practice on other systems\n  // (acknowledged by the standard) is for negative numbers to indicate that a\n  // signal was generated asynchronously. Although xnu does not do this, allow\n  // for the possibility for completeness.\n  bool si_code_valid = !(siginfo->si_code <= 0 ||\n                         siginfo->si_code == SI_USER ||\n                         siginfo->si_code == SI_QUEUE ||\n                         siginfo->si_code == SI_TIMER ||\n                         siginfo->si_code == SI_ASYNCIO ||\n                         siginfo->si_code == SI_MESGQ);\n\n  // 0x5343 = 'SC', signifying “signal and code”, disambiguates from the schema\n  // used by ExceptionCodeForMetrics(). That system primarily uses Mach\n  // exception types and codes, which are not available to a POSIX signal\n  // handler. It does provide a way to encode only signal numbers, but does so\n  // with the understanding that certain “raw” signals would not be encountered\n  // without a Mach exception. Furthermore, it does not allow siginfo->si_code\n  // to be encoded, because that’s not available to Mach exception handlers. It\n  // would be a shame to lose that information available to a POSIX signal\n  // handler.\n  int metrics_code = 0x53430000 | (InRangeCast<uint8_t>(sig, 0xff) << 8);\n  if (si_code_valid) {\n    metrics_code |= InRangeCast<uint8_t>(siginfo->si_code, 0xff);\n  }\n  Metrics::HandlerCrashed(metrics_code);\n\n  Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);\n}\n\nvoid HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {\n  MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);\n  Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);\n}\n\nvoid ReinstallCrashHandler() {\n  // This is used to re-enable the metrics-recording crash handler after\n  // MonitorSelf() sets up a Crashpad exception handler. On macOS, the\n  // metrics-recording handler uses signals and the Crashpad handler uses Mach\n  // exceptions, so there’s nothing to re-enable.\n  // On Linux, the signal handler installed by StartHandler() restores the\n  // previously installed signal handler by default.\n}\n\nvoid InstallCrashHandler() {\n  Signals::InstallCrashHandlers(HandleCrashSignal, 0, nullptr);\n\n  // Not a crash handler, but close enough.\n  Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);\n}\n\n#if BUILDFLAG(IS_APPLE)\n\nstruct ResetSIGTERMTraits {\n  static struct sigaction* InvalidValue() {\n    return nullptr;\n  }\n\n  static void Free(struct sigaction* sa) {\n    int rv = sigaction(SIGTERM, sa, nullptr);\n    PLOG_IF(ERROR, rv != 0) << \"sigaction\";\n  }\n};\nusing ScopedResetSIGTERM =\n    base::ScopedGeneric<struct sigaction*, ResetSIGTERMTraits>;\n\nExceptionHandlerServer* g_exception_handler_server;\n\n// This signal handler is only operative when being run from launchd.\nvoid HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {\n  // Don’t call MetricsRecordExit(). This is part of the normal exit path when\n  // running from launchd.\n\n  DCHECK(g_exception_handler_server);\n  g_exception_handler_server->Stop();\n}\n\n#endif  // BUILDFLAG(IS_APPLE)\n\n#elif BUILDFLAG(IS_WIN)\n\nLONG(WINAPI* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr;\n\nLONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {\n  MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);\n  Metrics::HandlerCrashed(exception_pointers->ExceptionRecord->ExceptionCode);\n\n  if (g_original_exception_filter)\n    return g_original_exception_filter(exception_pointers);\n  else\n    return EXCEPTION_CONTINUE_SEARCH;\n}\n\n// Handles events like Control-C and Control-Break on a console.\nBOOL WINAPI ConsoleHandler(DWORD console_event) {\n  MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);\n  return false;\n}\n\n// Handles a WM_ENDSESSION message sent when the user session is ending.\nclass TerminateHandler final : public SessionEndWatcher {\n public:\n  TerminateHandler() : SessionEndWatcher() {}\n\n  TerminateHandler(const TerminateHandler&) = delete;\n  TerminateHandler& operator=(const TerminateHandler&) = delete;\n\n  ~TerminateHandler() override {}\n\n private:\n  // SessionEndWatcher:\n  void SessionEnding() override {\n    MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated);\n  }\n};\n\nvoid ReinstallCrashHandler() {\n  // This is used to re-enable the metrics-recording crash handler after\n  // MonitorSelf() sets up a Crashpad exception handler. The Crashpad handler\n  // takes over the UnhandledExceptionFilter, so reinstall the metrics-recording\n  // one.\n  g_original_exception_filter =\n      SetUnhandledExceptionFilter(&UnhandledExceptionHandler);\n}\n\nvoid InstallCrashHandler() {\n  ReinstallCrashHandler();\n\n  // These are termination handlers, not crash handlers, but that’s close\n  // enough. Note that destroying the TerminateHandler would wait for its thread\n  // to exit, which isn’t necessary or desirable.\n  SetConsoleCtrlHandler(ConsoleHandler, true);\n  [[maybe_unused]] static TerminateHandler* terminate_handler =\n      new TerminateHandler();\n}\n\n#endif  // BUILDFLAG(IS_APPLE)\n\nvoid MonitorSelf(const Options& options) {\n  base::FilePath executable_path;\n  if (!Paths::Executable(&executable_path)) {\n    return;\n  }\n\n  if (std::find(options.monitor_self_arguments.begin(),\n                options.monitor_self_arguments.end(),\n                \"--monitor-self\") != options.monitor_self_arguments.end()) {\n    LOG(WARNING) << \"--monitor-self-argument=--monitor-self is not supported\";\n    return;\n  }\n  std::vector<std::string> extra_arguments(options.monitor_self_arguments);\n  if (!options.identify_client_via_url) {\n    extra_arguments.push_back(\"--no-identify-client-via-url\");\n  }\n  extra_arguments.push_back(\"--no-periodic-tasks\");\n  if (!options.rate_limit) {\n    extra_arguments.push_back(\"--no-rate-limit\");\n  }\n  if (!options.upload_gzip) {\n    extra_arguments.push_back(\"--no-upload-gzip\");\n  }\n  for (const auto& iterator : options.monitor_self_annotations) {\n    extra_arguments.push_back(\n        base::StringPrintf(\"--monitor-self-annotation=%s=%s\",\n                           iterator.first.c_str(),\n                           iterator.second.c_str()));\n  }\n\n  // Don’t use options.metrics_dir. The current implementation only allows one\n  // instance of crashpad_handler to be writing metrics at a time, and it should\n  // be the primary instance.\n  CrashpadClient crashpad_client;\n#if BUILDFLAG(IS_ANDROID)\n  if (!crashpad_client.StartHandlerAtCrash(executable_path,\n                                           options.database,\n                                           base::FilePath(),\n                                           options.url,\n                                           options.annotations,\n                                           extra_arguments)) {\n    return;\n  }\n#else\n  if (!crashpad_client.StartHandler(executable_path,\n                                    options.database,\n                                    base::FilePath(),\n                                    options.url,\n                                    options.annotations,\n                                    extra_arguments,\n                                    true,\n                                    false)) {\n    return;\n  }\n#endif\n\n  // Make sure that appropriate metrics will be recorded on crash before this\n  // process is terminated.\n  ReinstallCrashHandler();\n}\n\nclass ScopedStoppable {\n public:\n  ScopedStoppable() = default;\n\n  ScopedStoppable(const ScopedStoppable&) = delete;\n  ScopedStoppable& operator=(const ScopedStoppable&) = delete;\n\n  ~ScopedStoppable() {\n    if (stoppable_) {\n      stoppable_->Stop();\n    }\n  }\n\n  void Reset(Stoppable* stoppable) { stoppable_.reset(stoppable); }\n\n  Stoppable* Get() { return stoppable_.get(); }\n\n private:\n  std::unique_ptr<Stoppable> stoppable_;\n};\n\nvoid InitCrashpadLogging() {\n  logging::LoggingSettings settings;\n#if BUILDFLAG(IS_CHROMEOS)\n  settings.logging_dest = logging::LOG_TO_FILE;\n  settings.log_file_path = \"/var/log/chrome/chrome\";\n#elif BUILDFLAG(IS_WIN)\n  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;\n#else\n  settings.logging_dest =\n      logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;\n#endif\n  logging::InitLogging(settings);\n}\n\n}  // namespace\n\nint HandlerMain(int argc,\n                char* argv[],\n                const UserStreamDataSources* user_stream_sources) {\n  InitCrashpadLogging();\n\n  InstallCrashHandler();\n  CallMetricsRecordNormalExit metrics_record_normal_exit;\n\n  const base::FilePath argv0(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));\n  const base::FilePath me(argv0.BaseName());\n\n  enum OptionFlags {\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n    kOptionAnnotation,\n#if defined(ATTACHMENTS_SUPPORTED)\n    kOptionAttachment,\n#endif  // defined(ATTACHMENTS_SUPPORTED)\n    kOptionDatabase,\n#if BUILDFLAG(IS_APPLE)\n    kOptionHandshakeFD,\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_WIN)\n    kOptionInitialClientData,\n#endif  // BUILDFLAG(IS_WIN)\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    kOptionInitialClientFD,\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n#if BUILDFLAG(IS_APPLE)\n    kOptionMachService,\n#endif  // BUILDFLAG(IS_APPLE)\n    kOptionMetrics,\n    kOptionMonitorSelf,\n    kOptionMonitorSelfAnnotation,\n    kOptionMonitorSelfArgument,\n    kOptionNoIdentifyClientViaUrl,\n    kOptionNoPeriodicTasks,\n    kOptionNoRateLimit,\n    kOptionNoUploadGzip,\n#if BUILDFLAG(IS_ANDROID)\n    kOptionNoWriteMinidumpToDatabase,\n#endif  // BUILDFLAG(IS_ANDROID)\n#if BUILDFLAG(IS_WIN)\n    kOptionPipeName,\n#endif  // BUILDFLAG(IS_WIN)\n#if BUILDFLAG(IS_APPLE)\n    kOptionResetOwnCrashExceptionPortToSystemDefault,\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n    kOptionSanitizationInformation,\n    kOptionSharedClientConnection,\n    kOptionTraceParentWithException,\n#endif\n    kOptionURL,\n#if BUILDFLAG(IS_CHROMEOS)\n    kOptionUseCrosCrashReporter,\n    kOptionMinidumpDirForTests,\n    kOptionAlwaysAllowFeedback,\n#endif  // BUILDFLAG(IS_CHROMEOS)\n#if BUILDFLAG(IS_ANDROID)\n    kOptionWriteMinidumpToLog,\n#endif  // BUILDFLAG(IS_ANDROID)\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  static constexpr option long_options[] = {\n    {\"annotation\", required_argument, nullptr, kOptionAnnotation},\n#if defined(ATTACHMENTS_SUPPORTED)\n    {\"attachment\", required_argument, nullptr, kOptionAttachment},\n#endif  // ATTACHMENTS_SUPPORTED\n    {\"database\", required_argument, nullptr, kOptionDatabase},\n#if BUILDFLAG(IS_APPLE)\n    {\"handshake-fd\", required_argument, nullptr, kOptionHandshakeFD},\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_WIN)\n    {\"initial-client-data\",\n     required_argument,\n     nullptr,\n     kOptionInitialClientData},\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    {\"initial-client-fd\", required_argument, nullptr, kOptionInitialClientFD},\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n#if BUILDFLAG(IS_APPLE)\n    {\"mach-service\", required_argument, nullptr, kOptionMachService},\n#endif  // BUILDFLAG(IS_APPLE)\n    {\"metrics-dir\", required_argument, nullptr, kOptionMetrics},\n    {\"monitor-self\", no_argument, nullptr, kOptionMonitorSelf},\n    {\"monitor-self-annotation\",\n     required_argument,\n     nullptr,\n     kOptionMonitorSelfAnnotation},\n    {\"monitor-self-argument\",\n     required_argument,\n     nullptr,\n     kOptionMonitorSelfArgument},\n    {\"no-identify-client-via-url\",\n     no_argument,\n     nullptr,\n     kOptionNoIdentifyClientViaUrl},\n    {\"no-periodic-tasks\", no_argument, nullptr, kOptionNoPeriodicTasks},\n    {\"no-rate-limit\", no_argument, nullptr, kOptionNoRateLimit},\n    {\"no-upload-gzip\", no_argument, nullptr, kOptionNoUploadGzip},\n#if BUILDFLAG(IS_ANDROID)\n    {\"no-write-minidump-to-database\",\n     no_argument,\n     nullptr,\n     kOptionNoWriteMinidumpToDatabase},\n#endif  // BUILDFLAG(IS_ANDROID)\n#if BUILDFLAG(IS_WIN)\n    {\"pipe-name\", required_argument, nullptr, kOptionPipeName},\n#endif  // BUILDFLAG(IS_WIN)\n#if BUILDFLAG(IS_APPLE)\n    {\"reset-own-crash-exception-port-to-system-default\",\n     no_argument,\n     nullptr,\n     kOptionResetOwnCrashExceptionPortToSystemDefault},\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n    {\"sanitization-information\",\n     required_argument,\n     nullptr,\n     kOptionSanitizationInformation},\n    {\"shared-client-connection\",\n     no_argument,\n     nullptr,\n     kOptionSharedClientConnection},\n    {\"trace-parent-with-exception\",\n     required_argument,\n     nullptr,\n     kOptionTraceParentWithException},\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID)\n    {\"url\", required_argument, nullptr, kOptionURL},\n#if BUILDFLAG(IS_CHROMEOS)\n    {\"use-cros-crash-reporter\",\n     no_argument,\n     nullptr,\n     kOptionUseCrosCrashReporter},\n    {\"minidump-dir-for-tests\",\n     required_argument,\n     nullptr,\n     kOptionMinidumpDirForTests},\n    {\"always-allow-feedback\", no_argument, nullptr, kOptionAlwaysAllowFeedback},\n#endif  // BUILDFLAG(IS_CHROMEOS)\n#if BUILDFLAG(IS_ANDROID)\n    {\"write-minidump-to-log\", no_argument, nullptr, kOptionWriteMinidumpToLog},\n#endif  // BUILDFLAG(IS_ANDROID)\n    {\"help\", no_argument, nullptr, kOptionHelp},\n    {\"version\", no_argument, nullptr, kOptionVersion},\n    {nullptr, 0, nullptr, 0},\n  };\n\n  Options options = {};\n#if BUILDFLAG(IS_APPLE)\n  options.handshake_fd = -1;\n#endif\n  options.identify_client_via_url = true;\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  options.initial_client_fd = kInvalidFileHandle;\n#endif\n  options.periodic_tasks = true;\n  options.rate_limit = true;\n  options.upload_gzip = true;\n#if BUILDFLAG(IS_ANDROID)\n  options.write_minidump_to_database = true;\n#endif\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"\", long_options, nullptr)) != -1) {\n    switch (opt) {\n      case kOptionAnnotation: {\n        if (!AddKeyValueToMap(&options.annotations, optarg, \"--annotation\")) {\n          return ExitFailure();\n        }\n        break;\n      }\n#if defined(ATTACHMENTS_SUPPORTED)\n      case kOptionAttachment: {\n        options.attachments.push_back(base::FilePath(\n            ToolSupport::CommandLineArgumentToFilePathStringType(optarg)));\n        break;\n      }\n#endif  // ATTACHMENTS_SUPPORTED\n      case kOptionDatabase: {\n        options.database = base::FilePath(\n            ToolSupport::CommandLineArgumentToFilePathStringType(optarg));\n        break;\n      }\n#if BUILDFLAG(IS_APPLE)\n      case kOptionHandshakeFD: {\n        if (!StringToNumber(optarg, &options.handshake_fd) ||\n            options.handshake_fd < 0) {\n          ToolSupport::UsageHint(me,\n                                 \"--handshake-fd requires a file descriptor\");\n          return ExitFailure();\n        }\n        break;\n      }\n      case kOptionMachService: {\n        options.mach_service = optarg;\n        break;\n      }\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_WIN)\n      case kOptionInitialClientData: {\n        if (!options.initial_client_data.InitializeFromString(optarg)) {\n          ToolSupport::UsageHint(\n              me, \"failed to parse --initial-client-data\");\n          return ExitFailure();\n        }\n        break;\n      }\n#endif  // BUILDFLAG(IS_WIN)\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n      case kOptionInitialClientFD: {\n        if (!base::StringToInt(optarg, &options.initial_client_fd)) {\n          ToolSupport::UsageHint(me, \"failed to parse --initial-client-fd\");\n          return ExitFailure();\n        }\n        break;\n      }\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n      case kOptionMetrics: {\n        options.metrics_dir = base::FilePath(\n            ToolSupport::CommandLineArgumentToFilePathStringType(optarg));\n        break;\n      }\n      case kOptionMonitorSelf: {\n        options.monitor_self = true;\n        break;\n      }\n      case kOptionMonitorSelfAnnotation: {\n        if (!AddKeyValueToMap(&options.monitor_self_annotations,\n                              optarg,\n                              \"--monitor-self-annotation\")) {\n          return ExitFailure();\n        }\n        break;\n      }\n      case kOptionMonitorSelfArgument: {\n        options.monitor_self_arguments.push_back(optarg);\n        break;\n      }\n      case kOptionNoIdentifyClientViaUrl: {\n        options.identify_client_via_url = false;\n        break;\n      }\n      case kOptionNoPeriodicTasks: {\n        options.periodic_tasks = false;\n        break;\n      }\n      case kOptionNoRateLimit: {\n        options.rate_limit = false;\n        break;\n      }\n      case kOptionNoUploadGzip: {\n        options.upload_gzip = false;\n        break;\n      }\n#if BUILDFLAG(IS_ANDROID)\n      case kOptionNoWriteMinidumpToDatabase: {\n        options.write_minidump_to_database = false;\n        break;\n      }\n#endif  // BUILDFLAG(IS_ANDROID)\n#if BUILDFLAG(IS_WIN)\n      case kOptionPipeName: {\n        options.pipe_name = optarg;\n        break;\n      }\n#endif  // BUILDFLAG(IS_WIN)\n#if BUILDFLAG(IS_APPLE)\n      case kOptionResetOwnCrashExceptionPortToSystemDefault: {\n        options.reset_own_crash_exception_port_to_system_default = true;\n        break;\n      }\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n      case kOptionSanitizationInformation: {\n        if (!StringToNumber(optarg,\n                            &options.sanitization_information_address)) {\n          ToolSupport::UsageHint(me,\n                                 \"failed to parse --sanitization-information\");\n          return ExitFailure();\n        }\n        break;\n      }\n      case kOptionSharedClientConnection: {\n        options.shared_client_connection = true;\n        break;\n      }\n      case kOptionTraceParentWithException: {\n        if (!StringToNumber(optarg, &options.exception_information_address)) {\n          ToolSupport::UsageHint(\n              me, \"failed to parse --trace-parent-with-exception\");\n          return ExitFailure();\n        }\n        break;\n      }\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID)\n      case kOptionURL: {\n        options.url = optarg;\n        break;\n      }\n#if BUILDFLAG(IS_CHROMEOS)\n      case kOptionUseCrosCrashReporter: {\n        options.use_cros_crash_reporter = true;\n        break;\n      }\n      case kOptionMinidumpDirForTests: {\n        options.minidump_dir_for_tests = base::FilePath(\n            ToolSupport::CommandLineArgumentToFilePathStringType(optarg));\n        break;\n      }\n      case kOptionAlwaysAllowFeedback: {\n        options.always_allow_feedback = true;\n        break;\n      }\n#endif  // BUILDFLAG(IS_CHROMEOS)\n#if BUILDFLAG(IS_ANDROID)\n      case kOptionWriteMinidumpToLog: {\n        options.write_minidump_to_log = true;\n        break;\n      }\n#endif  // BUILDFLAG(IS_ANDROID)\n      case kOptionHelp: {\n        Usage(me);\n        MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);\n        return EXIT_SUCCESS;\n      }\n      case kOptionVersion: {\n        ToolSupport::Version(me);\n        MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);\n        return EXIT_SUCCESS;\n      }\n      default: {\n        ToolSupport::UsageHint(me, nullptr);\n        return ExitFailure();\n      }\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n#if BUILDFLAG(IS_APPLE)\n  if (options.handshake_fd < 0 && options.mach_service.empty()) {\n    ToolSupport::UsageHint(me, \"--handshake-fd or --mach-service is required\");\n    return ExitFailure();\n  }\n  if (options.handshake_fd >= 0 && !options.mach_service.empty()) {\n    ToolSupport::UsageHint(\n        me, \"--handshake-fd and --mach-service are incompatible\");\n    return ExitFailure();\n  }\n#elif BUILDFLAG(IS_WIN)\n  if (!options.initial_client_data.IsValid() && options.pipe_name.empty()) {\n    ToolSupport::UsageHint(me,\n                           \"--initial-client-data or --pipe-name is required\");\n    return ExitFailure();\n  }\n  if (options.initial_client_data.IsValid() && !options.pipe_name.empty()) {\n    ToolSupport::UsageHint(\n        me, \"--initial-client-data and --pipe-name are incompatible\");\n    return ExitFailure();\n  }\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  if (!options.exception_information_address &&\n      options.initial_client_fd == kInvalidFileHandle) {\n    ToolSupport::UsageHint(\n        me, \"--trace-parent-with-exception or --initial-client-fd is required\");\n    return ExitFailure();\n  }\n  if (options.sanitization_information_address &&\n      !options.exception_information_address) {\n    ToolSupport::UsageHint(\n        me,\n        \"--sanitization_information requires --trace-parent-with-exception\");\n    return ExitFailure();\n  }\n  if (options.shared_client_connection &&\n      options.initial_client_fd == kInvalidFileHandle) {\n    ToolSupport::UsageHint(\n        me, \"--shared-client-connection requires --initial-client-fd\");\n    return ExitFailure();\n  }\n#if BUILDFLAG(IS_ANDROID)\n  if (!options.write_minidump_to_log && !options.write_minidump_to_database) {\n    ToolSupport::UsageHint(me,\n                           \"--no_write_minidump_to_database is required to use \"\n                           \"with --write_minidump_to_log.\");\n    ExitFailure();\n  }\n#endif  // BUILDFLAG(IS_ANDROID)\n#endif  // BUILDFLAG(IS_APPLE)\n\n  if (options.database.empty()) {\n    ToolSupport::UsageHint(me, \"--database is required\");\n    return ExitFailure();\n  }\n\n  if (argc) {\n    ToolSupport::UsageHint(me, nullptr);\n    return ExitFailure();\n  }\n\n#if BUILDFLAG(IS_APPLE)\n  if (options.reset_own_crash_exception_port_to_system_default) {\n    CrashpadClient::UseSystemDefaultHandler();\n  }\n#endif  // BUILDFLAG(IS_APPLE)\n\n  if (options.monitor_self) {\n    MonitorSelf(options);\n  }\n\n  if (!options.monitor_self_annotations.empty()) {\n    // Establish these annotations even if --monitor-self is not present, in\n    // case something such as generate_dump wants to try to access them later.\n    //\n    // If the handler is part of a multi-purpose executable, simple annotations\n    // may already be present for this module. If they are, use them.\n    CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();\n    SimpleStringDictionary* module_annotations =\n        crashpad_info->simple_annotations();\n    if (!module_annotations) {\n      module_annotations = new SimpleStringDictionary();\n      crashpad_info->set_simple_annotations(module_annotations);\n    }\n\n    for (const auto& iterator : options.monitor_self_annotations) {\n      module_annotations->SetKeyValue(iterator.first.c_str(),\n                                      iterator.second.c_str());\n    }\n  }\n\n  std::unique_ptr<CrashReportDatabase> database(\n      CrashReportDatabase::Initialize(options.database));\n  if (!database) {\n    return ExitFailure();\n  }\n\n  ScopedStoppable upload_thread;\n  if (!options.url.empty()) {\n    // TODO(scottmg): options.rate_limit should be removed when we have a\n    // configurable database setting to control upload limiting.\n    // See https://crashpad.chromium.org/bug/23.\n    CrashReportUploadThread::Options upload_thread_options;\n    upload_thread_options.identify_client_via_url =\n        options.identify_client_via_url;\n    upload_thread_options.rate_limit = options.rate_limit;\n    upload_thread_options.upload_gzip = options.upload_gzip;\n    upload_thread_options.watch_pending_reports = options.periodic_tasks;\n\n    upload_thread.Reset(new CrashReportUploadThread(\n        database.get(),\n        options.url,\n        upload_thread_options,\n        CrashReportUploadThread::ProcessPendingReportsObservationCallback()));\n    upload_thread.Get()->Start();\n  }\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  std::unique_ptr<ExceptionHandlerServer::Delegate> exception_handler;\n#else\n  std::unique_ptr<CrashReportExceptionHandler> exception_handler;\n#endif\n\n#if BUILDFLAG(IS_CHROMEOS)\n  if (options.use_cros_crash_reporter) {\n    auto cros_handler = std::make_unique<CrosCrashReportExceptionHandler>(\n        database.get(),\n        &options.annotations,\n        user_stream_sources);\n\n    if (!options.minidump_dir_for_tests.empty()) {\n      cros_handler->SetDumpDir(options.minidump_dir_for_tests);\n    }\n\n    if (options.always_allow_feedback) {\n      cros_handler->SetAlwaysAllowFeedback();\n    }\n\n    exception_handler = std::move(cros_handler);\n  } else {\n    exception_handler = std::make_unique<CrashReportExceptionHandler>(\n        database.get(),\n        static_cast<CrashReportUploadThread*>(upload_thread.Get()),\n        &options.annotations,\n        &options.attachments,\n        true,\n        false,\n        user_stream_sources);\n  }\n#else\n  exception_handler = std::make_unique<CrashReportExceptionHandler>(\n      database.get(),\n      static_cast<CrashReportUploadThread*>(upload_thread.Get()),\n      &options.annotations,\n#if defined(ATTACHMENTS_SUPPORTED)\n      &options.attachments,\n#endif  // ATTACHMENTS_SUPPORTED\n#if BUILDFLAG(IS_ANDROID)\n      options.write_minidump_to_database,\n      options.write_minidump_to_log,\n#endif  // BUILDFLAG(IS_ANDROID)\n#if BUILDFLAG(IS_LINUX)\n      true,\n      false,\n#endif  // BUILDFLAG(IS_LINUX)\n      user_stream_sources);\n#endif  // BUILDFLAG(IS_CHROMEOS)\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  if (options.exception_information_address) {\n    ExceptionHandlerProtocol::ClientInformation info;\n    info.exception_information_address = options.exception_information_address;\n    info.sanitization_information_address =\n        options.sanitization_information_address;\n    return exception_handler->HandleException(getppid(), geteuid(), info)\n               ? EXIT_SUCCESS\n               : ExitFailure();\n  }\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID)\n\n  ScopedStoppable prune_thread;\n  if (options.periodic_tasks) {\n    prune_thread.Reset(new PruneCrashReportThread(\n        database.get(), PruneCondition::GetDefault()));\n    prune_thread.Get()->Start();\n  }\n\n#if BUILDFLAG(IS_APPLE)\n  if (options.mach_service.empty()) {\n    // Don’t do this when being run by launchd. See launchd.plist(5).\n    CloseStdinAndStdout();\n  }\n\n  base::apple::ScopedMachReceiveRight receive_right;\n\n  if (options.handshake_fd >= 0) {\n    receive_right.reset(\n        ChildPortHandshake::RunServerForFD(\n            base::ScopedFD(options.handshake_fd),\n            ChildPortHandshake::PortRightType::kReceiveRight));\n  } else if (!options.mach_service.empty()) {\n    receive_right = BootstrapCheckIn(options.mach_service);\n  }\n\n  if (!receive_right.is_valid()) {\n    return ExitFailure();\n  }\n\n  ExceptionHandlerServer exception_handler_server(\n      std::move(receive_right), !options.mach_service.empty());\n  base::AutoReset<ExceptionHandlerServer*> reset_g_exception_handler_server(\n      &g_exception_handler_server, &exception_handler_server);\n\n  struct sigaction old_sigterm_action;\n  ScopedResetSIGTERM reset_sigterm;\n  if (!options.mach_service.empty()) {\n    // When running from launchd, no no-senders notification could ever be\n    // triggered, because launchd maintains a send right to the service. When\n    // launchd wants the job to exit, it will send a SIGTERM. See\n    // launchd.plist(5).\n    //\n    // Set up a SIGTERM handler that will call exception_handler_server.Stop().\n    // This replaces the HandleTerminateSignal handler for SIGTERM.\n    if (Signals::InstallHandler(\n            SIGTERM, HandleSIGTERM, 0, &old_sigterm_action)) {\n      reset_sigterm.reset(&old_sigterm_action);\n    }\n  }\n\n  RecordFileLimitAnnotation();\n#elif BUILDFLAG(IS_WIN)\n  // Shut down as late as possible relative to programs we're watching.\n  if (!SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY))\n    PLOG(ERROR) << \"SetProcessShutdownParameters\";\n\n  ExceptionHandlerServer exception_handler_server(!options.pipe_name.empty());\n\n  if (!options.pipe_name.empty()) {\n    exception_handler_server.SetPipeName(base::UTF8ToWide(options.pipe_name));\n  }\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  ExceptionHandlerServer exception_handler_server;\n#endif  // BUILDFLAG(IS_APPLE)\n\n  base::GlobalHistogramAllocator* histogram_allocator = nullptr;\n  if (!options.metrics_dir.empty()) {\n    static constexpr char kMetricsName[] = \"CrashpadMetrics\";\n    constexpr size_t kMetricsFileSize = 1 << 20;\n    if (base::GlobalHistogramAllocator::CreateWithActiveFileInDir(\n            options.metrics_dir, kMetricsFileSize, 0, kMetricsName)) {\n      histogram_allocator = base::GlobalHistogramAllocator::Get();\n      histogram_allocator->CreateTrackingHistograms(kMetricsName);\n    }\n  }\n\n  Metrics::HandlerLifetimeMilestone(Metrics::LifetimeMilestone::kStarted);\n\n#if BUILDFLAG(IS_WIN)\n  if (options.initial_client_data.IsValid()) {\n    exception_handler_server.InitializeWithInheritedDataForInitialClient(\n        options.initial_client_data, exception_handler.get());\n  }\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  if (options.initial_client_fd == kInvalidFileHandle ||\n      !exception_handler_server.InitializeWithClient(\n          ScopedFileHandle(options.initial_client_fd),\n          options.shared_client_connection)) {\n    return ExitFailure();\n  }\n#endif  // BUILDFLAG(IS_WIN)\n\n  exception_handler_server.Run(exception_handler.get());\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/handler_main.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_\n#define CRASHPAD_HANDLER_HANDLER_MAIN_H_\n\n#include \"build/build_config.h\"\n#include \"handler/user_stream_data_source.h\"\n\nnamespace crashpad {\n\n//! \\brief The `main()` of the `crashpad_handler` binary.\n//!\n//! This is exposed so that `crashpad_handler` can be embedded into another\n//! binary, but called and used as if it were a standalone executable.\n//!\n//! \\param[in] argc \\a argc as passed to `main()`.\n//! \\param[in] argv \\a argv as passed to `main()`.\n//! \\param[in] user_stream_sources An optional vector containing the\n//!     extensibility data sources to call on crash. Each time a minidump is\n//!     created, the sources are called in turn. Any streams returned are added\n//!     to the minidump.\nint HandlerMain(int argc,\n                char* argv[],\n                const UserStreamDataSources* user_stream_sources);\n\n#if BUILDFLAG(IS_ANDROID)\n//! \\brief The `main()` entry point for Android libraries.\n//!\n//! This symbol is the entry point for crashpad when it is dynamically loaded\n//! using /system/bin/linker.\n//!\n//! \\sa CrashpadClient::StartHandlerWithLinkerAtCrash()\nextern \"C\" int CrashpadHandlerMain(int argc, char* argv[]);\n#endif\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_HANDLER_MAIN_H_\n"
  },
  {
    "path": "handler/linux/capture_snapshot.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/linux/capture_snapshot.h\"\n\n#include <utility>\n\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/sanitized/sanitization_information.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/misc/tri_state.h\"\n\nnamespace crashpad {\n\nbool CaptureSnapshot(\n    PtraceConnection* connection,\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    const std::map<std::string, std::string>& process_annotations,\n    uid_t client_uid,\n    VMAddress requesting_thread_stack_address,\n    pid_t* requesting_thread_id,\n    std::unique_ptr<ProcessSnapshotLinux>* snapshot,\n    std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot) {\n  std::unique_ptr<ProcessSnapshotLinux> process_snapshot(\n      new ProcessSnapshotLinux());\n  if (!process_snapshot->Initialize(connection)) {\n    Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);\n    return false;\n  }\n\n  pid_t local_requesting_thread_id = -1;\n  if (requesting_thread_stack_address) {\n    local_requesting_thread_id = process_snapshot->FindThreadWithStackAddress(\n        requesting_thread_stack_address);\n  }\n\n  if (requesting_thread_id) {\n    *requesting_thread_id = local_requesting_thread_id;\n  }\n\n  if (!process_snapshot->InitializeException(info.exception_information_address,\n                                             local_requesting_thread_id)) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kExceptionInitializationFailed);\n    return false;\n  }\n\n  Metrics::ExceptionCode(process_snapshot->Exception()->Exception());\n\n  CrashpadInfoClientOptions client_options;\n  process_snapshot->GetCrashpadOptions(&client_options);\n  if (client_options.crashpad_handler_behavior == TriState::kDisabled) {\n    return false;\n  }\n\n  for (auto& p : process_annotations) {\n    process_snapshot->AddAnnotation(p.first, p.second);\n  }\n\n  if (info.sanitization_information_address) {\n    SanitizationInformation sanitization_info;\n    ProcessMemoryRange range;\n    if (!range.Initialize(connection->Memory(), connection->Is64Bit()) ||\n        !range.Read(info.sanitization_information_address,\n                    sizeof(sanitization_info),\n                    &sanitization_info)) {\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kSanitizationInitializationFailed);\n      return false;\n    }\n\n    auto allowed_annotations = std::make_unique<std::vector<std::string>>();\n    auto allowed_memory_ranges =\n        std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();\n    if (!ReadAllowedAnnotations(range,\n                                sanitization_info.allowed_annotations_address,\n                                allowed_annotations.get()) ||\n        !ReadAllowedMemoryRanges(\n            range,\n            sanitization_info.allowed_memory_ranges_address,\n            allowed_memory_ranges.get())) {\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kSanitizationInitializationFailed);\n      return false;\n    }\n\n    std::unique_ptr<ProcessSnapshotSanitized> sanitized(\n        new ProcessSnapshotSanitized());\n    if (!sanitized->Initialize(process_snapshot.get(),\n                               sanitization_info.allowed_annotations_address\n                                   ? std::move(allowed_annotations)\n                                   : nullptr,\n                               std::move(allowed_memory_ranges),\n                               sanitization_info.target_module_address,\n                               sanitization_info.sanitize_stacks)) {\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kSkippedDueToSanitization);\n      return false;\n    }\n    *sanitized_snapshot = std::move(sanitized);\n  }\n\n  *snapshot = std::move(process_snapshot);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/linux/capture_snapshot.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_\n#define CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_\n\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <string>\n\n#include \"snapshot/linux/process_snapshot_linux.h\"\n#include \"snapshot/sanitized/process_snapshot_sanitized.h\"\n#include \"util/linux/exception_handler_protocol.h\"\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/address_types.h\"\n\nnamespace crashpad {\n\n//! \\brief Captures a snapshot of a client over \\a connection.\n//!\n//! \\param[in] connection A PtraceConnection to the client to snapshot.\n//! \\param[in] info Information about the client configuring the snapshot.\n//! \\param[in] process_annotations A map of annotations to insert as\n//!     process-level annotations into the snapshot.\n//! \\param[in] client_uid The client's user ID.\n//! \\param[in] requesting_thread_stack_address An address on the stack of the\n//!     thread requesting the snapshot. If \\a info includes an exception\n//!     address, the exception will be assigned to the thread whose stack\n//!     address range contains this address. If 0, \\a requesting_thread_id will\n//!     be -1.\n//! \\param[out] requesting_thread_id The thread ID of the thread corresponding\n//!     to \\a requesting_thread_stack_address. Set to -1 if the thread ID could\n//!     not be determined. Optional.\n//! \\param[out] process_snapshot A snapshot of the client process, valid if this\n//!     function returns `true`.\n//! \\param[out] sanitized_snapshot A sanitized snapshot of the client process,\n//!     valid if this function returns `true` and sanitization was requested in\n//!     \\a info.\n//! \\return `true` if \\a process_snapshot was successfully created. A message\n//!     will be logged on failure, but not if the snapshot was skipped because\n//!     handling was disabled by CrashpadInfoClientOptions.\nbool CaptureSnapshot(\n    PtraceConnection* connection,\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    const std::map<std::string, std::string>& process_annotations,\n    uid_t client_uid,\n    VMAddress requesting_thread_stack_address,\n    pid_t* requesting_thread_id,\n    std::unique_ptr<ProcessSnapshotLinux>* process_snapshot,\n    std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_\n"
  },
  {
    "path": "handler/linux/crash_report_exception_handler.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/linux/crash_report_exception_handler.h\"\n\n#include <memory>\n#include <utility>\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"client/settings.h\"\n#include \"handler/linux/capture_snapshot.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"snapshot/linux/process_snapshot_linux.h\"\n#include \"snapshot/sanitized/process_snapshot_sanitized.h\"\n#include \"util/file/file_helper.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/output_stream_file_writer.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#include \"util/linux/ptrace_client.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/stream/base94_output_stream.h\"\n#include \"util/stream/log_output_stream.h\"\n#include \"util/stream/zlib_output_stream.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/log.h>\n#endif\n\nnamespace crashpad {\nnamespace {\n\nclass Logger final : public LogOutputStream::Delegate {\n public:\n  Logger() = default;\n\n  Logger(const Logger&) = delete;\n  Logger& operator=(const Logger&) = delete;\n\n  ~Logger() override = default;\n\n#if BUILDFLAG(IS_ANDROID)\n  bool Log(const char* buf) override {\n    return __android_log_buf_write(\n        LOG_ID_CRASH, ANDROID_LOG_FATAL, \"crashpad\", buf) > 0;\n  }\n\n  size_t OutputCap() override {\n    // Most minidumps are expected to be compressed and encoded into less than\n    // 128k.\n    return 128 * 1024;\n  }\n\n  size_t LineWidth() override {\n    // From Android NDK r20 <android/log.h>, log message text may be truncated\n    // to less than an implementation-specific limit (1023 bytes), for sake of\n    // safe and being easy to read in logcat, choose 512.\n    return 512;\n  }\n#else\n  // TODO(jperaza): Log to an appropriate location on Linux.\n  bool Log(const char* buf) override { return false; }\n  size_t OutputCap() override { return 0; }\n  size_t LineWidth() override { return 0; }\n#endif\n};\n\nbool WriteMinidumpLogFromFile(FileReaderInterface* file_reader) {\n  ZlibOutputStream stream(\n      ZlibOutputStream::Mode::kCompress,\n      std::make_unique<Base94OutputStream>(\n          Base94OutputStream::Mode::kEncode,\n          std::make_unique<LogOutputStream>(std::make_unique<Logger>())));\n  FileOperationResult read_result;\n  do {\n    uint8_t buffer[4096];\n    read_result = file_reader->Read(buffer, sizeof(buffer));\n    if (read_result < 0)\n      return false;\n\n    if (read_result > 0 && (!stream.Write(buffer, read_result)))\n      return false;\n  } while (read_result > 0);\n  return stream.Flush();\n}\n\n}  // namespace\n\nCrashReportExceptionHandler::CrashReportExceptionHandler(\n    CrashReportDatabase* database,\n    CrashReportUploadThread* upload_thread,\n    const std::map<std::string, std::string>* process_annotations,\n    const std::vector<base::FilePath>* attachments,\n    bool write_minidump_to_database,\n    bool write_minidump_to_log,\n    const UserStreamDataSources* user_stream_data_sources)\n    : database_(database),\n      upload_thread_(upload_thread),\n      process_annotations_(process_annotations),\n      attachments_(attachments),\n      write_minidump_to_database_(write_minidump_to_database),\n      write_minidump_to_log_(write_minidump_to_log),\n      user_stream_data_sources_(user_stream_data_sources) {\n  DCHECK(write_minidump_to_database_ | write_minidump_to_log_);\n}\n\nCrashReportExceptionHandler::~CrashReportExceptionHandler() = default;\n\nbool CrashReportExceptionHandler::HandleException(\n    pid_t client_process_id,\n    uid_t client_uid,\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    VMAddress requesting_thread_stack_address,\n    pid_t* requesting_thread_id,\n    UUID* local_report_id) {\n  Metrics::ExceptionEncountered();\n\n  DirectPtraceConnection connection;\n  if (!connection.Initialize(client_process_id)) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kDirectPtraceFailed);\n    return false;\n  }\n\n  return HandleExceptionWithConnection(&connection,\n                                       info,\n                                       client_uid,\n                                       requesting_thread_stack_address,\n                                       requesting_thread_id,\n                                       local_report_id);\n}\n\nbool CrashReportExceptionHandler::HandleExceptionWithBroker(\n    pid_t client_process_id,\n    uid_t client_uid,\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    int broker_sock,\n    UUID* local_report_id) {\n  Metrics::ExceptionEncountered();\n\n  PtraceClient client;\n  if (!client.Initialize(broker_sock, client_process_id)) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kBrokeredPtraceFailed);\n    return false;\n  }\n\n  return HandleExceptionWithConnection(\n      &client, info, client_uid, 0, nullptr, local_report_id);\n}\n\nbool CrashReportExceptionHandler::HandleExceptionWithConnection(\n    PtraceConnection* connection,\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    uid_t client_uid,\n    VMAddress requesting_thread_stack_address,\n    pid_t* requesting_thread_id,\n    UUID* local_report_id) {\n  std::unique_ptr<ProcessSnapshotLinux> process_snapshot;\n  std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;\n  if (!CaptureSnapshot(connection,\n                       info,\n                       *process_annotations_,\n                       client_uid,\n                       requesting_thread_stack_address,\n                       requesting_thread_id,\n                       &process_snapshot,\n                       &sanitized_snapshot)) {\n    return false;\n  }\n\n  UUID client_id;\n  Settings* const settings = database_->GetSettings();\n  if (settings && settings->GetClientID(&client_id)) {\n    process_snapshot->SetClientID(client_id);\n  }\n\n  return write_minidump_to_database_\n             ? WriteMinidumpToDatabase(process_snapshot.get(),\n                                       sanitized_snapshot.get(),\n                                       write_minidump_to_log_,\n                                       local_report_id)\n             : WriteMinidumpToLog(process_snapshot.get(),\n                                  sanitized_snapshot.get());\n}\n\nbool CrashReportExceptionHandler::WriteMinidumpToDatabase(\n    ProcessSnapshotLinux* process_snapshot,\n    ProcessSnapshotSanitized* sanitized_snapshot,\n    bool write_minidump_to_log,\n    UUID* local_report_id) {\n  std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n  CrashReportDatabase::OperationStatus database_status =\n      database_->PrepareNewCrashReport(&new_report);\n  if (database_status != CrashReportDatabase::kNoError) {\n    LOG(ERROR) << \"PrepareNewCrashReport failed\";\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kPrepareNewCrashReportFailed);\n    return false;\n  }\n\n  process_snapshot->SetReportID(new_report->ReportID());\n\n  ProcessSnapshot* snapshot =\n      sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)\n                         : implicit_cast<ProcessSnapshot*>(process_snapshot);\n\n  MinidumpFileWriter minidump;\n  minidump.InitializeFromSnapshot(snapshot);\n  AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);\n\n  if (!minidump.WriteEverything(new_report->Writer())) {\n    LOG(ERROR) << \"WriteEverything failed\";\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kMinidumpWriteFailed);\n    return false;\n  }\n\n  bool write_minidump_to_log_succeed = false;\n  if (write_minidump_to_log) {\n    if (auto* file_reader = new_report->Reader()) {\n      if (WriteMinidumpLogFromFile(file_reader))\n        write_minidump_to_log_succeed = true;\n      else\n        LOG(ERROR) << \"WriteMinidumpLogFromFile failed\";\n    }\n  }\n\n  for (const auto& attachment : (*attachments_)) {\n    FileReader file_reader;\n    if (!file_reader.Open(attachment)) {\n      LOG(ERROR) << \"attachment \" << attachment.value().c_str()\n                 << \" couldn't be opened, skipping\";\n      continue;\n    }\n\n    base::FilePath filename = attachment.BaseName();\n    FileWriter* file_writer = new_report->AddAttachment(filename.value());\n    if (file_writer == nullptr) {\n      LOG(ERROR) << \"attachment \" << filename.value().c_str()\n                 << \" couldn't be created, skipping\";\n      continue;\n    }\n\n    CopyFileContent(&file_reader, file_writer);\n  }\n\n  UUID uuid;\n  database_status =\n      database_->FinishedWritingCrashReport(std::move(new_report), &uuid);\n  if (database_status != CrashReportDatabase::kNoError) {\n    LOG(ERROR) << \"FinishedWritingCrashReport failed\";\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kFinishedWritingCrashReportFailed);\n    return false;\n  }\n\n  if (upload_thread_) {\n    upload_thread_->ReportPending(uuid);\n  }\n\n  if (local_report_id != nullptr) {\n    *local_report_id = uuid;\n  }\n\n  Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);\n\n  return write_minidump_to_log ? write_minidump_to_log_succeed : true;\n}\n\nbool CrashReportExceptionHandler::WriteMinidumpToLog(\n    ProcessSnapshotLinux* process_snapshot,\n    ProcessSnapshotSanitized* sanitized_snapshot) {\n  ProcessSnapshot* snapshot =\n      sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)\n                         : implicit_cast<ProcessSnapshot*>(process_snapshot);\n  MinidumpFileWriter minidump;\n  minidump.InitializeFromSnapshot(snapshot);\n  AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);\n\n  OutputStreamFileWriter writer(std::make_unique<ZlibOutputStream>(\n      ZlibOutputStream::Mode::kCompress,\n      std::make_unique<Base94OutputStream>(\n          Base94OutputStream::Mode::kEncode,\n          std::make_unique<LogOutputStream>(std::make_unique<Logger>()))));\n  if (!minidump.WriteMinidump(&writer, false /* allow_seek */)) {\n    LOG(ERROR) << \"WriteMinidump failed\";\n    return false;\n  }\n  return writer.Flush();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/linux/crash_report_exception_handler.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_\n#define CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_\n\n#include <map>\n#include <string>\n\n#include \"client/crash_report_database.h\"\n#include \"handler/crash_report_upload_thread.h\"\n#include \"handler/linux/exception_handler_server.h\"\n#include \"handler/user_stream_data_source.h\"\n#include \"util/linux/exception_handler_protocol.h\"\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\nclass ProcessSnapshotLinux;\nclass ProcessSnapshotSanitized;\n\n//! \\brief An exception handler that writes crash reports for exceptions\n//!     to a CrashReportDatabase.\nclass CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {\n public:\n  //! \\brief Creates a new object that will store crash reports in \\a database.\n  //!\n  //! \\param[in] database The database to store crash reports in. Weak.\n  //! \\param[in] upload_thread The upload thread to notify when a new crash\n  //!     report is written into \\a database. Report upload is skipped if this\n  //!     value is `nullptr`.\n  //! \\param[in] process_annotations A map of annotations to insert as\n  //!     process-level annotations into each crash report that is written. Do\n  //!     not confuse this with module-level annotations, which are under the\n  //!     control of the crashing process, and are used to implement Chrome’s\n  //!     “crash keys.” Process-level annotations are those that are beyond the\n  //!     control of the crashing process, which must reliably be set even if\n  //!     the process crashes before it’s able to establish its own annotations.\n  //!     To interoperate with Breakpad servers, the recommended practice is to\n  //!     specify values for the `\"prod\"` and `\"ver\"` keys as process\n  //!     annotations.\n  //! \\param[in] attachments A vector of file paths that should be captured with\n  //!     each report at the time of the crash.\n  //! \\param[in] write_minidump_to_database Whether the minidump shall be\n  //!     written to database.\n  //! \\param[in] write_minidump_to_log Whether the minidump shall be written to\n  //!     log.\n  //! \\param[in] user_stream_data_sources Data sources to be used to extend\n  //!     crash reports. For each crash report that is written, the data sources\n  //!     are called in turn. These data sources may contribute additional\n  //!     minidump streams. `nullptr` if not required.\n  CrashReportExceptionHandler(\n      CrashReportDatabase* database,\n      CrashReportUploadThread* upload_thread,\n      const std::map<std::string, std::string>* process_annotations,\n      const std::vector<base::FilePath>* attachments,\n      bool write_minidump_to_database,\n      bool write_minidump_to_log,\n      const UserStreamDataSources* user_stream_data_sources);\n\n  CrashReportExceptionHandler(const CrashReportExceptionHandler&) = delete;\n  CrashReportExceptionHandler& operator=(const CrashReportExceptionHandler&) =\n      delete;\n\n  ~CrashReportExceptionHandler() override;\n\n  // ExceptionHandlerServer::Delegate:\n\n  bool HandleException(pid_t client_process_id,\n                       uid_t client_uid,\n                       const ExceptionHandlerProtocol::ClientInformation& info,\n                       VMAddress requesting_thread_stack_address = 0,\n                       pid_t* requesting_thread_id = nullptr,\n                       UUID* local_report_id = nullptr) override;\n\n  bool HandleExceptionWithBroker(\n      pid_t client_process_id,\n      uid_t client_uid,\n      const ExceptionHandlerProtocol::ClientInformation& info,\n      int broker_sock,\n      UUID* local_report_id = nullptr) override;\n\n private:\n  bool HandleExceptionWithConnection(\n      PtraceConnection* connection,\n      const ExceptionHandlerProtocol::ClientInformation& info,\n      uid_t client_uid,\n      VMAddress requesting_thread_stack_address,\n      pid_t* requesting_thread_id,\n      UUID* local_report_id = nullptr);\n\n  bool WriteMinidumpToDatabase(ProcessSnapshotLinux* process_snapshot,\n                               ProcessSnapshotSanitized* sanitized_snapshot,\n                               bool write_minidump_to_log,\n                               UUID* local_report_id);\n  bool WriteMinidumpToLog(ProcessSnapshotLinux* process_snapshot,\n                          ProcessSnapshotSanitized* sanitized_snapshot);\n\n  CrashReportDatabase* database_;  // weak\n  CrashReportUploadThread* upload_thread_;  // weak\n  const std::map<std::string, std::string>* process_annotations_;  // weak\n  const std::vector<base::FilePath>* attachments_;  // weak\n  bool write_minidump_to_database_;\n  bool write_minidump_to_log_;\n  const UserStreamDataSources* user_stream_data_sources_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_\n"
  },
  {
    "path": "handler/linux/cros_crash_report_exception_handler.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/linux/cros_crash_report_exception_handler.h\"\n\n#include <vector>\n\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#if defined(CRASHPAD_IS_IN_CHROMIUM)\n#include \"base/system/sys_info.h\"\n#endif // CRASHPAD_IS_IN_CHROMIUM\n#include \"client/settings.h\"\n#include \"handler/linux/capture_snapshot.h\"\n#include \"handler/minidump_to_upload_parameters.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"snapshot/linux/process_snapshot_linux.h\"\n#include \"snapshot/minidump/process_snapshot_minidump.h\"\n#include \"snapshot/sanitized/process_snapshot_sanitized.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#include \"util/linux/ptrace_client.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/posix/spawn_subprocess.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Returns the process name for a pid.\nconst std::string GetProcessNameFromPid(pid_t pid) {\n  // Symlink to process binary is at /proc/###/exe.\n  std::string link_path = \"/proc/\" + std::to_string(pid) + \"/exe\";\n\n  std::string result(4096, '\\0');\n  ssize_t size = readlink(link_path.c_str(), result.data(), result.size());\n  if (size < 0) {\n    PLOG(ERROR) << \"Failed to readlink \" << link_path;\n    result.clear();\n  } else {\n    result.resize(size);\n    size_t last_slash_pos = result.rfind('/');\n    if (last_slash_pos != std::string::npos) {\n      result = result.substr(last_slash_pos + 1);\n    }\n  }\n  return result;\n}\n\nbool WriteAnnotationsAndMinidump(\n    const std::map<std::string, std::string>& parameters,\n    MinidumpFileWriter& minidump,\n    FileWriter& file_writer) {\n  for (const auto& kv : parameters) {\n    if (kv.first.find(':') != std::string::npos) {\n      LOG(ERROR) << \"Annotation key cannot have ':' in it \" << kv.first;\n      return false;\n    }\n    if (!file_writer.Write(kv.first.c_str(), strlen(kv.first.c_str()))) {\n      return false;\n    }\n    if (!file_writer.Write(\":\", 1)) {\n      return false;\n    }\n    size_t value_size = strlen(kv.second.c_str());\n    std::string value_size_str = std::to_string(value_size);\n    if (!file_writer.Write(value_size_str.c_str(), value_size_str.size())) {\n      return false;\n    }\n    if (!file_writer.Write(\":\", 1)) {\n      return false;\n    }\n    if (!file_writer.Write(kv.second.c_str(), strlen(kv.second.c_str()))) {\n      return false;\n    }\n  }\n\n  static constexpr char kMinidumpName[] =\n      \"upload_file_minidump\\\"; filename=\\\"dump\\\":\";\n  if (!file_writer.Write(kMinidumpName, sizeof(kMinidumpName) - 1)) {\n    return false;\n  }\n  crashpad::FileOffset dump_size_start_offset = file_writer.Seek(0, SEEK_CUR);\n  if (dump_size_start_offset < 0) {\n    LOG(ERROR) << \"Failed to get minidump size start offset\";\n    return false;\n  }\n  static constexpr char kMinidumpLengthFilling[] = \"00000000000000000000:\";\n  if (!file_writer.Write(kMinidumpLengthFilling,\n                         sizeof(kMinidumpLengthFilling) - 1)) {\n    return false;\n  }\n  crashpad::FileOffset dump_start_offset = file_writer.Seek(0, SEEK_CUR);\n  if (dump_start_offset < 0) {\n    LOG(ERROR) << \"Failed to get minidump start offset\";\n    return false;\n  }\n  if (!minidump.WriteEverything(&file_writer)) {\n    return false;\n  }\n  crashpad::FileOffset dump_end_offset = file_writer.Seek(0, SEEK_CUR);\n  if (dump_end_offset < 0) {\n    LOG(ERROR) << \"Failed to get minidump end offset\";\n    return false;\n  }\n\n  size_t dump_data_size = dump_end_offset - dump_start_offset;\n  std::string dump_data_size_str = std::to_string(dump_data_size);\n  file_writer.Seek(dump_size_start_offset + strlen(kMinidumpLengthFilling) - 1 -\n                       dump_data_size_str.size(),\n                   SEEK_SET);\n  if (!file_writer.Write(dump_data_size_str.c_str(),\n                         dump_data_size_str.size())) {\n    return false;\n  }\n  return true;\n}\n\n}  // namespace\n\nCrosCrashReportExceptionHandler::CrosCrashReportExceptionHandler(\n    CrashReportDatabase* database,\n    const std::map<std::string, std::string>* process_annotations,\n    const UserStreamDataSources* user_stream_data_sources)\n    : database_(database),\n      process_annotations_(process_annotations),\n      user_stream_data_sources_(user_stream_data_sources),\n      always_allow_feedback_(false) {}\n\nCrosCrashReportExceptionHandler::~CrosCrashReportExceptionHandler() = default;\n\nbool CrosCrashReportExceptionHandler::HandleException(\n    pid_t client_process_id,\n    uid_t client_uid,\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    VMAddress requesting_thread_stack_address,\n    pid_t* requesting_thread_id,\n    UUID* local_report_id) {\n  Metrics::ExceptionEncountered();\n\n  DirectPtraceConnection connection;\n  if (!connection.Initialize(client_process_id)) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kDirectPtraceFailed);\n    return false;\n  }\n\n  return HandleExceptionWithConnection(&connection,\n                                       info,\n                                       client_uid,\n                                       requesting_thread_stack_address,\n                                       requesting_thread_id,\n                                       local_report_id);\n}\n\nbool CrosCrashReportExceptionHandler::HandleExceptionWithBroker(\n    pid_t client_process_id,\n    uid_t client_uid,\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    int broker_sock,\n    UUID* local_report_id) {\n  Metrics::ExceptionEncountered();\n\n  PtraceClient client;\n  if (!client.Initialize(broker_sock, client_process_id)) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kBrokeredPtraceFailed);\n    return false;\n  }\n\n  return HandleExceptionWithConnection(\n      &client, info, client_uid, 0, nullptr, local_report_id);\n}\n\nbool CrosCrashReportExceptionHandler::HandleExceptionWithConnection(\n    PtraceConnection* connection,\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    uid_t client_uid,\n    VMAddress requesting_thread_stack_address,\n    pid_t* requesting_thread_id,\n    UUID* local_report_id) {\n  std::unique_ptr<ProcessSnapshotLinux> process_snapshot;\n  std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;\n  if (!CaptureSnapshot(connection,\n                       info,\n                       *process_annotations_,\n                       client_uid,\n                       requesting_thread_stack_address,\n                       requesting_thread_id,\n                       &process_snapshot,\n                       &sanitized_snapshot)) {\n    return false;\n  }\n\n  UUID client_id;\n  Settings* const settings = database_->GetSettings();\n  if (settings) {\n    // If GetSettings() or GetClientID() fails, something else will log a\n    // message and client_id will be left at its default value, all zeroes,\n    // which is appropriate.\n    settings->GetClientID(&client_id);\n  }\n  process_snapshot->SetClientID(client_id);\n\n  UUID uuid;\n  uuid.InitializeWithNew();\n  process_snapshot->SetReportID(uuid);\n\n  ProcessSnapshot* snapshot =\n      sanitized_snapshot\n          ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot.get())\n          : implicit_cast<ProcessSnapshot*>(process_snapshot.get());\n\n  MinidumpFileWriter minidump;\n  minidump.InitializeFromSnapshot(snapshot);\n  AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);\n\n  FileWriter file_writer;\n  if (!file_writer.OpenMemfd(base::FilePath(\"minidump\"))) {\n    Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kOpenMemfdFailed);\n    return false;\n  }\n\n  std::map<std::string, std::string> parameters =\n      BreakpadHTTPFormParametersFromMinidump(snapshot);\n  // Used to differentiate between breakpad and crashpad while the switch is\n  // ramping up.\n  parameters.emplace(\"crash_library\", \"crashpad\");\n\n  if (!WriteAnnotationsAndMinidump(parameters, minidump, file_writer)) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kMinidumpWriteFailed);\n    return false;\n  }\n\n  // CrOS uses crash_reporter instead of Crashpad to report crashes.\n  // crash_reporter needs to know the pid and uid of the crashing process.\n  std::vector<std::string> argv({\"/sbin/crash_reporter\"});\n\n#if defined(CRASHPAD_IS_IN_CHROMIUM)\n  int32_t major_version = 0, minor_version = 0, bugfix_version = 0;\n  base::SysInfo::OperatingSystemVersionNumbers(\n      &major_version, &minor_version, &bugfix_version);\n  // The version on which https://crrev.com/c/4265753 landed.\n  constexpr int32_t kFixedVersion = 15363;\n  // TODO(https://crbug.com/1420445): Remove this check (and the\n  // CRASHPAD_IS_IN_CHROMIUM defines) when M115 branches.\n  if (major_version >= kFixedVersion) {\n    // Used to distinguish between non-fatal and fatal crashes.\n    const ExceptionSnapshot* const exception_snapshot = snapshot->Exception();\n    if (exception_snapshot) {\n      // convert to int32, since crashpad uses -1 as a signal for non-fatal\n      // crashes.\n      argv.push_back(base::StringPrintf(\n          \"--chrome_signal=%d\",\n          static_cast<int32_t>(exception_snapshot->Exception())));\n    }\n  }\n#endif // CRASHPAD_IS_IN_CHROMIUM\n\n  argv.push_back(\"--chrome_memfd=\" + std::to_string(file_writer.fd()));\n\n  const pid_t pid = process_snapshot->ProcessID();\n  argv.push_back(\"--pid=\" + std::to_string(pid));\n  argv.push_back(\"--uid=\" + std::to_string(client_uid));\n\n  std::string process_name = GetProcessNameFromPid(pid);\n  argv.push_back(\"--exe=\" + (process_name.empty() ? \"chrome\" : process_name));\n\n  if (info.crash_loop_before_time != 0) {\n    argv.push_back(\"--crash_loop_before=\" +\n                   std::to_string(info.crash_loop_before_time));\n  }\n  if (!dump_dir_.empty()) {\n    argv.push_back(\"--chrome_dump_dir=\" + dump_dir_.value());\n  }\n  if (always_allow_feedback_) {\n    argv.push_back(\"--always_allow_feedback\");\n  }\n\n  if (!SpawnSubprocess(argv,\n                       nullptr /* envp */,\n                       file_writer.fd() /* preserve_fd */,\n                       false /* use_path */,\n                       nullptr /* child_function */)) {\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kFinishedWritingCrashReportFailed);\n    return false;\n  }\n\n  if (local_report_id != nullptr) {\n    *local_report_id = uuid;\n  }\n  LOG(INFO) << \"Successfully wrote report \" << uuid.ToString();\n\n  Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/linux/cros_crash_report_exception_handler.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_\n#define CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_\n\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n\n#include \"client/crash_report_database.h\"\n#include \"handler/linux/exception_handler_server.h\"\n#include \"handler/user_stream_data_source.h\"\n#include \"util/linux/exception_handler_protocol.h\"\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\n//! \\brief An exception handler that writes crash reports to the ChromeOS\n//!     crash_reporter.\nclass CrosCrashReportExceptionHandler\n    : public ExceptionHandlerServer::Delegate {\n public:\n  //! \\brief Creates a new object that will pass reports to\n  //!     `/sbin/crash_reporter`.\n  //!\n  //! \\param[in] database The database that supplies settings for this client.\n  //!     This object does not write its reports to this database.\n  //! \\param[in] process_annotations A map of annotations to insert as\n  //!     process-level annotations into each crash report that is written. Do\n  //!     not confuse this with module-level annotations, which are under the\n  //!     control of the crashing process, and are used to implement Chrome’s\n  //!     “crash keys.” Process-level annotations are those that are beyond the\n  //!     control of the crashing process, which must reliably be set even if\n  //!     the process crashes before it’s able to establish its own annotations.\n  //!     To interoperate with Breakpad servers, the recommended practice is to\n  //!     specify values for the `\"prod\"` and `\"ver\"` keys as process\n  //!     annotations.\n  //! \\param[in] user_stream_data_sources Data sources to be used to extend\n  //!     crash reports. For each crash report that is written, the data sources\n  //!     are called in turn. These data sources may contribute additional\n  //!     minidump streams. `nullptr` if not required.\n  CrosCrashReportExceptionHandler(\n      CrashReportDatabase* database,\n      const std::map<std::string, std::string>* process_annotations,\n      const UserStreamDataSources* user_stream_data_sources);\n\n  CrosCrashReportExceptionHandler(const CrosCrashReportExceptionHandler&) =\n      delete;\n  CrosCrashReportExceptionHandler& operator=(\n      const CrosCrashReportExceptionHandler&) = delete;\n\n  ~CrosCrashReportExceptionHandler() override;\n\n  // ExceptionHandlerServer::Delegate:\n\n  bool HandleException(pid_t client_process_id,\n                       uid_t client_uid,\n                       const ExceptionHandlerProtocol::ClientInformation& info,\n                       VMAddress requesting_thread_stack_address = 0,\n                       pid_t* requesting_thread_id = nullptr,\n                       UUID* local_report_id = nullptr) override;\n\n  bool HandleExceptionWithBroker(\n      pid_t client_process_id,\n      uid_t client_uid,\n      const ExceptionHandlerProtocol::ClientInformation& info,\n      int broker_sock,\n      UUID* local_report_id = nullptr) override;\n\n  void SetDumpDir(const base::FilePath& dump_dir) { dump_dir_ = dump_dir; }\n  void SetAlwaysAllowFeedback() { always_allow_feedback_ = true; }\n private:\n  bool HandleExceptionWithConnection(\n      PtraceConnection* connection,\n      const ExceptionHandlerProtocol::ClientInformation& info,\n      uid_t client_uid,\n      VMAddress requesting_thread_stack_address,\n      pid_t* requesting_thread_id,\n      UUID* local_report_id = nullptr);\n\n  CrashReportDatabase* database_;  // weak\n  const std::map<std::string, std::string>* process_annotations_;  // weak\n  const UserStreamDataSources* user_stream_data_sources_;  // weak\n  base::FilePath dump_dir_;\n  bool always_allow_feedback_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_\n"
  },
  {
    "path": "handler/linux/exception_handler_server.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/linux/exception_handler_server.h\"\n\n#include <errno.h>\n#include <linux/capability.h>\n#include <sys/epoll.h>\n#include <sys/eventfd.h>\n#include <sys/socket.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/compiler_specific.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/linux/proc_task_reader.h\"\n#include \"util/linux/socket.h\"\n#include \"util/misc/as_underlying_type.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Log an error for a socket after an EPOLLERR.\nvoid LogSocketError(int sock) {\n  int err;\n  socklen_t err_len = sizeof(err);\n  if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &err_len) != 0) {\n    PLOG(ERROR) << \"getsockopt\";\n  } else {\n    errno = err;\n    PLOG(ERROR) << \"EPOLLERR\";\n  }\n}\n\nenum class PtraceScope {\n  kClassic = 0,\n  kRestricted,\n  kAdminOnly,\n  kNoAttach,\n  kUnknown\n};\n\nPtraceScope GetPtraceScope() {\n  const base::FilePath settings_file(\"/proc/sys/kernel/yama/ptrace_scope\");\n  if (!IsRegularFile(base::FilePath(settings_file))) {\n    return PtraceScope::kClassic;\n  }\n\n  std::string contents;\n  if (!LoggingReadEntireFile(settings_file, &contents)) {\n    return PtraceScope::kUnknown;\n  }\n\n  if (contents.back() != '\\n') {\n    LOG(ERROR) << \"format error\";\n    return PtraceScope::kUnknown;\n  }\n  contents.pop_back();\n\n  int ptrace_scope;\n  if (!base::StringToInt(contents, &ptrace_scope)) {\n    LOG(ERROR) << \"format error\";\n    return PtraceScope::kUnknown;\n  }\n\n  if (ptrace_scope < static_cast<int>(PtraceScope::kClassic) ||\n      ptrace_scope >= static_cast<int>(PtraceScope::kUnknown)) {\n    LOG(ERROR) << \"invalid ptrace scope\";\n    return PtraceScope::kUnknown;\n  }\n\n  return static_cast<PtraceScope>(ptrace_scope);\n}\n\nbool HaveCapSysPtrace() {\n  __user_cap_header_struct cap_header;\n  cap_header.pid = getpid();\n  cap_header.version = _LINUX_CAPABILITY_VERSION_3;\n\n  __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3];\n  if (syscall(SYS_capget, &cap_header, &cap_data) != 0) {\n    PLOG(ERROR) << \"capget\";\n    LOG_IF(ERROR, errno == EINVAL) << \"cap_header.version \" << std::hex\n                                   << cap_header.version;\n    return false;\n  }\n\n  return (cap_data[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &\n          CAP_TO_MASK(CAP_SYS_PTRACE)) != 0;\n}\n\nbool SendMessageToClient(\n    int client_sock,\n    ExceptionHandlerProtocol::ServerToClientMessage::Type type) {\n  ExceptionHandlerProtocol::ServerToClientMessage message = {};\n  message.type = type;\n  if (type ==\n      ExceptionHandlerProtocol::ServerToClientMessage::kTypeSetPtracer) {\n    message.pid = getpid();\n  }\n  return LoggingWriteFile(client_sock, &message, sizeof(message));\n}\n\nint tgkill(pid_t pid, pid_t tid, int signo) {\n  return syscall(SYS_tgkill, pid, tid, signo);\n}\n\nvoid SendSIGCONT(pid_t pid, pid_t tid) {\n  if (tid > 0) {\n    if (tgkill(pid, tid, ExceptionHandlerProtocol::kDumpDoneSignal) != 0) {\n      PLOG(ERROR) << \"tgkill\";\n    }\n    return;\n  }\n\n  std::vector<pid_t> threads;\n  if (!ReadThreadIDs(pid, &threads)) {\n    return;\n  }\n  for (const auto& thread : threads) {\n    if (tgkill(pid, thread, ExceptionHandlerProtocol::kDumpDoneSignal) != 0) {\n      PLOG(ERROR) << \"tgkill\";\n    }\n  }\n}\n\nbool SendCredentials(int client_sock) {\n  ExceptionHandlerProtocol::ServerToClientMessage message = {};\n  message.type =\n      ExceptionHandlerProtocol::ServerToClientMessage::kTypeCredentials;\n  return UnixCredentialSocket::SendMsg(\n             client_sock, &message, sizeof(message)) == 0;\n}\n\nclass PtraceStrategyDeciderImpl : public PtraceStrategyDecider {\n public:\n  PtraceStrategyDeciderImpl() : PtraceStrategyDecider() {}\n  ~PtraceStrategyDeciderImpl() = default;\n\n  Strategy ChooseStrategy(int sock,\n                          bool multiple_clients,\n                          const ucred& client_credentials) override {\n    if (client_credentials.pid <= 0) {\n      LOG(ERROR) << \"invalid credentials\";\n      return Strategy::kNoPtrace;\n    }\n\n    switch (GetPtraceScope()) {\n      case PtraceScope::kClassic:\n        if (getuid() == client_credentials.uid || HaveCapSysPtrace()) {\n          return Strategy::kDirectPtrace;\n        }\n        return multiple_clients ? Strategy::kNoPtrace : TryForkingBroker(sock);\n\n      case PtraceScope::kRestricted:\n        if (multiple_clients) {\n          return Strategy::kDirectPtrace;\n        }\n        if (!SendMessageToClient(sock,\n                                 ExceptionHandlerProtocol::\n                                     ServerToClientMessage::kTypeSetPtracer)) {\n          return Strategy::kError;\n        }\n\n        ExceptionHandlerProtocol::Errno status;\n        if (!LoggingReadFileExactly(sock, &status, sizeof(status))) {\n          return Strategy::kError;\n        }\n\n        if (status != 0) {\n          errno = status;\n          PLOG(ERROR) << \"Handler Client SetPtracer\";\n          return TryForkingBroker(sock);\n        }\n        return Strategy::kDirectPtrace;\n\n      case PtraceScope::kAdminOnly:\n        if (HaveCapSysPtrace()) {\n          return Strategy::kDirectPtrace;\n        }\n        [[fallthrough]];\n      case PtraceScope::kNoAttach:\n        LOG(WARNING) << \"no ptrace\";\n        return Strategy::kNoPtrace;\n\n      case PtraceScope::kUnknown:\n        LOG(WARNING) << \"Unknown ptrace scope\";\n        return Strategy::kError;\n    }\n\n    DCHECK(false);\n    return Strategy::kError;\n  }\n\n private:\n  static Strategy TryForkingBroker(int client_sock) {\n    if (!SendMessageToClient(\n            client_sock,\n            ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker)) {\n      return Strategy::kError;\n    }\n\n    ExceptionHandlerProtocol::Errno status;\n    if (!LoggingReadFileExactly(client_sock, &status, sizeof(status))) {\n      return Strategy::kError;\n    }\n\n    if (status != 0) {\n      errno = status;\n      PLOG(ERROR) << \"Handler Client ForkBroker\";\n      return Strategy::kNoPtrace;\n    }\n    return Strategy::kUseBroker;\n  }\n};\n\n}  // namespace\n\nExceptionHandlerServer::ExceptionHandlerServer()\n    : clients_(),\n      shutdown_event_(),\n      strategy_decider_(new PtraceStrategyDeciderImpl()),\n      delegate_(nullptr),\n      pollfd_(),\n      keep_running_(true) {}\n\nExceptionHandlerServer::~ExceptionHandlerServer() = default;\n\nvoid ExceptionHandlerServer::SetPtraceStrategyDecider(\n    std::unique_ptr<PtraceStrategyDecider> decider) {\n  strategy_decider_ = std::move(decider);\n}\n\nbool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock,\n                                                  bool multiple_clients) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  pollfd_.reset(epoll_create1(EPOLL_CLOEXEC));\n  if (!pollfd_.is_valid()) {\n    PLOG(ERROR) << \"epoll_create1\";\n    return false;\n  }\n\n  shutdown_event_ = std::make_unique<Event>();\n  shutdown_event_->type = Event::Type::kShutdown;\n  shutdown_event_->fd.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));\n  if (!shutdown_event_->fd.is_valid()) {\n    PLOG(ERROR) << \"eventfd\";\n    return false;\n  }\n\n  epoll_event poll_event;\n  poll_event.events = EPOLLIN;\n  poll_event.data.ptr = shutdown_event_.get();\n  if (epoll_ctl(pollfd_.get(),\n                EPOLL_CTL_ADD,\n                shutdown_event_->fd.get(),\n                &poll_event) != 0) {\n    PLOG(ERROR) << \"epoll_ctl\";\n    return false;\n  }\n\n  if (!InstallClientSocket(std::move(sock),\n                           multiple_clients ? Event::Type::kSharedSocketMessage\n                                            : Event::Type::kClientMessage)) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nvoid ExceptionHandlerServer::Run(Delegate* delegate) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  delegate_ = delegate;\n\n  while (keep_running_ && clients_.size() > 0) {\n    epoll_event poll_event;\n    int res = HANDLE_EINTR(epoll_wait(pollfd_.get(), &poll_event, 1, -1));\n    if (res < 0) {\n      PLOG(ERROR) << \"epoll_wait\";\n      return;\n    }\n    DCHECK_EQ(res, 1);\n\n    Event* eventp = reinterpret_cast<Event*>(poll_event.data.ptr);\n    if (eventp->type == Event::Type::kShutdown) {\n      if (poll_event.events & EPOLLERR) {\n        LogSocketError(eventp->fd.get());\n      }\n      keep_running_ = false;\n    } else {\n      HandleEvent(eventp, poll_event.events);\n    }\n  }\n}\n\nvoid ExceptionHandlerServer::Stop() {\n  keep_running_ = false;\n  if (shutdown_event_ && shutdown_event_->fd.is_valid()) {\n    uint64_t value = 1;\n    LoggingWriteFile(shutdown_event_->fd.get(), &value, sizeof(value));\n  }\n}\n\nvoid ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) {\n  DCHECK_NE(AsUnderlyingType(event->type),\n            AsUnderlyingType(Event::Type::kShutdown));\n\n  if (event_type & EPOLLERR) {\n    LogSocketError(event->fd.get());\n    UninstallClientSocket(event);\n    return;\n  }\n\n  if (event_type & EPOLLIN) {\n    if (!ReceiveClientMessage(event)) {\n      UninstallClientSocket(event);\n    }\n    return;\n  }\n\n  if (event_type & EPOLLHUP || event_type & EPOLLRDHUP) {\n    UninstallClientSocket(event);\n    return;\n  }\n\n  LOG(ERROR) << \"Unexpected event 0x\" << std::hex << event_type;\n  return;\n}\n\nbool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket,\n                                                 Event::Type type) {\n  // The handler may not have permission to set SO_PASSCRED on the socket, but\n  // it doesn't need to if the client has already set it.\n  // https://bugs.chromium.org/p/crashpad/issues/detail?id=252\n  int optval;\n  socklen_t optlen = sizeof(optval);\n  if (getsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, &optlen) !=\n      0) {\n    PLOG(ERROR) << \"getsockopt\";\n    return false;\n  }\n  if (!optval) {\n    optval = 1;\n    optlen = sizeof(optval);\n    if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) !=\n        0) {\n      PLOG(ERROR) << \"setsockopt\";\n      return false;\n    }\n  }\n\n  auto event = std::make_unique<Event>();\n  event->type = type;\n  event->fd.reset(socket.release());\n\n  Event* eventp = event.get();\n\n  if (!clients_.insert(std::make_pair(event->fd.get(), std::move(event)))\n           .second) {\n    LOG(ERROR) << \"duplicate descriptor\";\n    return false;\n  }\n\n  epoll_event poll_event;\n  poll_event.events = EPOLLIN | EPOLLRDHUP;\n  poll_event.data.ptr = eventp;\n\n  if (epoll_ctl(pollfd_.get(), EPOLL_CTL_ADD, eventp->fd.get(), &poll_event) !=\n      0) {\n    PLOG(ERROR) << \"epoll_ctl\";\n    clients_.erase(eventp->fd.get());\n    return false;\n  }\n\n  return true;\n}\n\nbool ExceptionHandlerServer::UninstallClientSocket(Event* event) {\n  if (epoll_ctl(pollfd_.get(), EPOLL_CTL_DEL, event->fd.get(), nullptr) != 0) {\n    PLOG(ERROR) << \"epoll_ctl\";\n    return false;\n  }\n\n  if (clients_.erase(event->fd.get()) != 1) {\n    LOG(ERROR) << \"event not found\";\n    return false;\n  }\n\n  return true;\n}\n\nbool ExceptionHandlerServer::ReceiveClientMessage(Event* event) {\n  ExceptionHandlerProtocol::ClientToServerMessage message;\n  ucred creds;\n  if (!UnixCredentialSocket::RecvMsg(\n          event->fd.get(), &message, sizeof(message), &creds)) {\n    return false;\n  }\n\n  switch (message.type) {\n    case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCheckCredentials:\n      return SendCredentials(event->fd.get());\n\n    case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCrashDumpRequest:\n      return HandleCrashDumpRequest(\n          creds,\n          message.client_info,\n          message.requesting_thread_stack_address,\n          event->fd.get(),\n          event->type == Event::Type::kSharedSocketMessage);\n  }\n\n  DCHECK(false);\n  LOG(ERROR) << \"Unknown message type\";\n  return false;\n}\n\nbool ExceptionHandlerServer::HandleCrashDumpRequest(\n    const ucred& creds,\n    const ExceptionHandlerProtocol::ClientInformation& client_info,\n    VMAddress requesting_thread_stack_address,\n    int client_sock,\n    bool multiple_clients) {\n  pid_t client_process_id = creds.pid;\n  pid_t requesting_thread_id = -1;\n  uid_t client_uid = creds.uid;\n\n  switch (\n      strategy_decider_->ChooseStrategy(client_sock, multiple_clients, creds)) {\n    case PtraceStrategyDecider::Strategy::kError:\n      if (multiple_clients) {\n        SendSIGCONT(client_process_id, requesting_thread_id);\n      }\n      return false;\n\n    case PtraceStrategyDecider::Strategy::kNoPtrace:\n      if (multiple_clients) {\n        SendSIGCONT(client_process_id, requesting_thread_id);\n        return true;\n      }\n      return SendMessageToClient(\n          client_sock,\n          ExceptionHandlerProtocol::ServerToClientMessage::\n              kTypeCrashDumpFailed);\n\n    case PtraceStrategyDecider::Strategy::kDirectPtrace: {\n      delegate_->HandleException(client_process_id,\n                                 client_uid,\n                                 client_info,\n                                 requesting_thread_stack_address,\n                                 &requesting_thread_id);\n      if (multiple_clients) {\n        SendSIGCONT(client_process_id, requesting_thread_id);\n        return true;\n      }\n      break;\n    }\n\n    case PtraceStrategyDecider::Strategy::kUseBroker:\n      DCHECK(!multiple_clients);\n      delegate_->HandleExceptionWithBroker(\n          client_process_id, client_uid, client_info, client_sock);\n      break;\n  }\n\n  return SendMessageToClient(\n      client_sock,\n      ExceptionHandlerProtocol::ServerToClientMessage::kTypeCrashDumpComplete);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/linux/exception_handler_server.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_\n#define CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_\n\n#include <stdint.h>\n#include <sys/socket.h>\n\n#include <atomic>\n#include <memory>\n#include <unordered_map>\n\n#include \"util/file/file_io.h\"\n#include \"util/linux/exception_handler_protocol.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\n//! \\brief Abstract base class for deciding how the handler should `ptrace` a\n//!     client.\nclass PtraceStrategyDecider {\n public:\n  virtual ~PtraceStrategyDecider() = default;\n\n  //! \\brief The possible return values for ChooseStrategy().\n  enum class Strategy {\n    //! \\brief An error occurred,  with a message logged.\n    kError,\n\n    //! \\brief Ptrace cannot be used.\n    kNoPtrace,\n\n    //! \\brief The handler should `ptrace`-attach the client directly.\n    kDirectPtrace,\n\n    //! \\brief The client has `fork`ed a PtraceBroker for the handler.\n    kUseBroker,\n  };\n\n  //! \\brief Chooses an appropriate `ptrace` strategy.\n  //!\n  //! \\param[in] sock A socket conncted to a ExceptionHandlerClient.\n  //! \\param[in] multiple_clients `true` if the socket is connected to multiple\n  //!     clients. The broker is not supported in this configuration.\n  //! \\param[in] client_credentials The credentials for the connected client.\n  //! \\return the chosen #Strategy.\n  virtual Strategy ChooseStrategy(int sock,\n                                  bool multiple_clients,\n                                  const ucred& client_credentials) = 0;\n\n protected:\n  PtraceStrategyDecider() = default;\n};\n\n//! \\brief Runs the main exception-handling server in Crashpad’s handler\n//!     process.\nclass ExceptionHandlerServer {\n public:\n  class Delegate {\n   public:\n    //! \\brief Called on receipt of a crash dump request from a client.\n    //!\n    //! \\param[in] client_process_id The process ID of the crashing client.\n    //! \\param[in] client_uid The user ID of the crashing client.\n    //! \\param[in] info Information on the client.\n    //! \\param[in] requesting_thread_stack_address Any address within the stack\n    //!     range for the the thread that sent the crash dump request. Optional.\n    //!     If unspecified or 0, \\a requesting_thread_id will be -1.\n    //! \\param[out] requesting_thread_id The thread ID of the thread which\n    //!     requested the crash dump if not `nullptr`. Set to -1 if the thread\n    //!     ID could not be determined. Optional.\n    //! \\param[out] local_report_id The unique identifier for the report created\n    //!     in the local report database. Optional.\n    //! \\return `true` on success. `false` on failure with a message logged.\n    virtual bool HandleException(\n        pid_t client_process_id,\n        uid_t client_uid,\n        const ExceptionHandlerProtocol::ClientInformation& info,\n        VMAddress requesting_thread_stack_address = 0,\n        pid_t* requesting_thread_id = nullptr,\n        UUID* local_report_id = nullptr) = 0;\n\n    //! \\brief Called on the receipt of a crash dump request from a client for a\n    //!     crash that should be mediated by a PtraceBroker.\n    //!\n    //! \\param[in] client_process_id The process ID of the crashing client.\n    //! \\param[in] client_uid The uid of the crashing client.\n    //! \\param[in] info Information on the client.\n    //! \\param[in] broker_sock A socket connected to the PtraceBroker.\n    //! \\param[out] local_report_id The unique identifier for the report created\n    //!     in the local report database. Optional.\n    //! \\return `true` on success. `false` on failure with a message logged.\n    virtual bool HandleExceptionWithBroker(\n        pid_t client_process_id,\n        uid_t client_uid,\n        const ExceptionHandlerProtocol::ClientInformation& info,\n        int broker_sock,\n        UUID* local_report_id = nullptr) = 0;\n\n    virtual ~Delegate() {}\n  };\n\n  ExceptionHandlerServer();\n\n  ExceptionHandlerServer(const ExceptionHandlerServer&) = delete;\n  ExceptionHandlerServer& operator=(const ExceptionHandlerServer&) = delete;\n\n  ~ExceptionHandlerServer();\n\n  //! \\brief Sets the handler's PtraceStrategyDecider.\n  //!\n  //! If this method is not called, a default PtraceStrategyDecider will be\n  //! used.\n  void SetPtraceStrategyDecider(std::unique_ptr<PtraceStrategyDecider> decider);\n\n  //! \\brief Initializes this object.\n  //!\n  //! This method must be successfully called before Run().\n  //!\n  //! \\param[in] sock A socket on which to receive client requests.\n  //! \\param[in] multiple_clients `true` if this socket is used by multiple\n  //!     clients. Using a broker process is not supported in this\n  //!     configuration.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool InitializeWithClient(ScopedFileHandle sock, bool multiple_clients);\n\n  //! \\brief Runs the exception-handling server.\n  //!\n  //! This method must only be called once on an ExceptionHandlerServer object.\n  //! This method returns when there are no more client connections or Stop()\n  //! has been called.\n  //!\n  //! \\param[in] delegate An object to send exceptions to.\n  void Run(Delegate* delegate);\n\n  //! \\brief Stops a running exception-handling server.\n  //!\n  //! Stop() may be called at any time, and may be called from a signal handler.\n  //! If Stop() is called before Run() it will cause Run() to return as soon as\n  //! it is called. It is harmless to call Stop() after Run() has already\n  //! returned, or to call Stop() after it has already been called.\n  void Stop();\n\n private:\n  struct Event {\n    enum class Type {\n      // Used by Stop() to shutdown the server.\n      kShutdown,\n\n      // A message from a client on a private socket connection.\n      kClientMessage,\n\n      // A message from a client on a shared socket connection.\n      kSharedSocketMessage\n    };\n\n    Type type;\n    ScopedFileHandle fd;\n  };\n\n  void HandleEvent(Event* event, uint32_t event_type);\n  bool InstallClientSocket(ScopedFileHandle socket, Event::Type type);\n  bool UninstallClientSocket(Event* event);\n  bool ReceiveClientMessage(Event* event);\n  bool HandleCrashDumpRequest(\n      const ucred& creds,\n      const ExceptionHandlerProtocol::ClientInformation& client_info,\n      VMAddress requesting_thread_stack_address,\n      int client_sock,\n      bool multiple_clients);\n\n  std::unordered_map<int, std::unique_ptr<Event>> clients_;\n  std::unique_ptr<Event> shutdown_event_;\n  std::unique_ptr<PtraceStrategyDecider> strategy_decider_;\n  Delegate* delegate_;\n  ScopedFileHandle pollfd_;\n  std::atomic<bool> keep_running_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_\n"
  },
  {
    "path": "handler/linux/exception_handler_server_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/linux/exception_handler_server.h\"\n\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/linux/process_snapshot_linux.h\"\n#include \"test/errors.h\"\n#include \"test/multiprocess.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#include \"util/linux/exception_handler_client.h\"\n#include \"util/linux/ptrace_client.h\"\n#include \"util/linux/scoped_pr_set_ptracer.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/synchronization/semaphore.h\"\n#include \"util/thread/thread.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Runs the ExceptionHandlerServer on a background thread.\nclass RunServerThread : public Thread {\n public:\n  RunServerThread(ExceptionHandlerServer* server,\n                  ExceptionHandlerServer::Delegate* delegate)\n      : server_(server), delegate_(delegate), join_sem_(0) {}\n\n  RunServerThread(const RunServerThread&) = delete;\n  RunServerThread& operator=(const RunServerThread&) = delete;\n\n  ~RunServerThread() override {}\n\n  bool JoinWithTimeout(double timeout) {\n    if (!join_sem_.TimedWait(timeout)) {\n      return false;\n    }\n    Join();\n    return true;\n  }\n\n private:\n  // Thread:\n  void ThreadMain() override {\n    server_->Run(delegate_);\n    join_sem_.Signal();\n  }\n\n  ExceptionHandlerServer* server_;\n  ExceptionHandlerServer::Delegate* delegate_;\n  Semaphore join_sem_;\n};\n\nclass ScopedStopServerAndJoinThread {\n public:\n  ScopedStopServerAndJoinThread(ExceptionHandlerServer* server,\n                                RunServerThread* thread)\n      : server_(server), thread_(thread) {}\n\n  ScopedStopServerAndJoinThread(const ScopedStopServerAndJoinThread&) = delete;\n  ScopedStopServerAndJoinThread& operator=(\n      const ScopedStopServerAndJoinThread&) = delete;\n\n  ~ScopedStopServerAndJoinThread() {\n    server_->Stop();\n    EXPECT_TRUE(thread_->JoinWithTimeout(5.0));\n  }\n\n private:\n  ExceptionHandlerServer* server_;\n  RunServerThread* thread_;\n};\n\nclass TestDelegate : public ExceptionHandlerServer::Delegate {\n public:\n  TestDelegate()\n      : Delegate(), last_exception_address_(0), last_client_(-1), sem_(0) {}\n\n  TestDelegate(const TestDelegate&) = delete;\n  TestDelegate& operator=(const TestDelegate&) = delete;\n\n  ~TestDelegate() {}\n\n  bool WaitForException(double timeout_seconds,\n                        pid_t* last_client,\n                        VMAddress* last_address) {\n    if (sem_.TimedWait(timeout_seconds)) {\n      *last_client = last_client_;\n      *last_address = last_exception_address_;\n      return true;\n    }\n\n    return false;\n  }\n\n  bool HandleException(pid_t client_process_id,\n                       uid_t client_uid,\n                       const ExceptionHandlerProtocol::ClientInformation& info,\n                       VMAddress requesting_thread_stack_address,\n                       pid_t* requesting_thread_id = nullptr,\n                       UUID* local_report_id = nullptr) override {\n    DirectPtraceConnection connection;\n    bool connected = connection.Initialize(client_process_id);\n    EXPECT_TRUE(connected);\n\n    last_exception_address_ = info.exception_information_address;\n    last_client_ = client_process_id;\n    sem_.Signal();\n    if (!connected) {\n      return false;\n    }\n\n    if (requesting_thread_id) {\n      if (requesting_thread_stack_address) {\n        ProcessSnapshotLinux process_snapshot;\n        if (!process_snapshot.Initialize(&connection)) {\n          ADD_FAILURE();\n          return false;\n        }\n        *requesting_thread_id = process_snapshot.FindThreadWithStackAddress(\n            requesting_thread_stack_address);\n      } else {\n        *requesting_thread_id = -1;\n      }\n    }\n    return true;\n  }\n\n  bool HandleExceptionWithBroker(\n      pid_t client_process_id,\n      uid_t client_uid,\n      const ExceptionHandlerProtocol::ClientInformation& info,\n      int broker_sock,\n      UUID* local_report_id = nullptr) override {\n    PtraceClient client;\n    bool connected = client.Initialize(broker_sock, client_process_id);\n    EXPECT_TRUE(connected);\n\n    last_exception_address_ = info.exception_information_address,\n    last_client_ = client_process_id;\n    sem_.Signal();\n    return connected;\n  }\n\n private:\n  VMAddress last_exception_address_;\n  pid_t last_client_;\n  Semaphore sem_;\n};\n\nclass MockPtraceStrategyDecider : public PtraceStrategyDecider {\n public:\n  MockPtraceStrategyDecider(PtraceStrategyDecider::Strategy strategy)\n      : PtraceStrategyDecider(), strategy_(strategy) {}\n\n  MockPtraceStrategyDecider(const MockPtraceStrategyDecider&) = delete;\n  MockPtraceStrategyDecider& operator=(const MockPtraceStrategyDecider&) =\n      delete;\n\n  ~MockPtraceStrategyDecider() {}\n\n  Strategy ChooseStrategy(int sock,\n                          bool multiple_clients,\n                          const ucred& client_credentials) override {\n    if (strategy_ == Strategy::kUseBroker) {\n      ExceptionHandlerProtocol::ServerToClientMessage message = {};\n      message.type =\n          ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker;\n\n      ExceptionHandlerProtocol::Errno status;\n      bool result = LoggingWriteFile(sock, &message, sizeof(message)) &&\n                    LoggingReadFileExactly(sock, &status, sizeof(status));\n      EXPECT_TRUE(result);\n\n      if (!result) {\n        return Strategy::kError;\n      }\n\n      if (status != 0) {\n        errno = status;\n        ADD_FAILURE() << ErrnoMessage(\"Handler Client ForkBroker\");\n        return Strategy::kNoPtrace;\n      }\n    }\n    return strategy_;\n  }\n\n private:\n  Strategy strategy_;\n};\n\nclass ExceptionHandlerServerTest : public testing::TestWithParam<bool> {\n public:\n  ExceptionHandlerServerTest()\n      : server_(),\n        delegate_(),\n        server_thread_(&server_, &delegate_),\n        sock_to_handler_(),\n        use_multi_client_socket_(GetParam()) {}\n\n  ExceptionHandlerServerTest(const ExceptionHandlerServerTest&) = delete;\n  ExceptionHandlerServerTest& operator=(const ExceptionHandlerServerTest&) =\n      delete;\n\n  ~ExceptionHandlerServerTest() = default;\n\n  int SockToHandler() { return sock_to_handler_.get(); }\n\n  TestDelegate* Delegate() { return &delegate_; }\n\n  void Hangup() { sock_to_handler_.reset(); }\n\n  RunServerThread* ServerThread() { return &server_thread_; }\n\n  ExceptionHandlerServer* Server() { return &server_; }\n\n  class CrashDumpTest : public Multiprocess {\n   public:\n    CrashDumpTest(ExceptionHandlerServerTest* server_test, bool succeeds)\n        : Multiprocess(), server_test_(server_test), succeeds_(succeeds) {}\n\n    CrashDumpTest(const CrashDumpTest&) = delete;\n    CrashDumpTest& operator=(const CrashDumpTest&) = delete;\n\n    ~CrashDumpTest() = default;\n\n    void MultiprocessParent() override {\n      ExceptionHandlerProtocol::ClientInformation info;\n      ASSERT_TRUE(\n          LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info)));\n\n      if (succeeds_) {\n        VMAddress last_address;\n        pid_t last_client;\n        ASSERT_TRUE(server_test_->Delegate()->WaitForException(\n            5.0, &last_client, &last_address));\n        // `exception_information_address` is underaligned and `EXPECT_EQ`\n        // internally takes arguments by reference. Copy it into a temporary\n        // before comparing to avoid undefined behavior.\n        EXPECT_EQ(last_address, VMAddress{info.exception_information_address});\n        EXPECT_EQ(last_client, ChildPID());\n      } else {\n        CheckedReadFileAtEOF(ReadPipeHandle());\n      }\n    }\n\n    void MultiprocessChild() override {\n      ASSERT_EQ(close(server_test_->sock_to_client_), 0);\n\n      ExceptionHandlerProtocol::ClientInformation info;\n      info.exception_information_address = 42;\n      ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info)));\n\n      // If the current ptrace_scope is restricted, the broker needs to be set\n      // as the ptracer for this process. Setting this process as its own\n      // ptracer allows the broker to inherit this condition.\n      ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ true);\n\n      ExceptionHandlerClient client(server_test_->SockToHandler(),\n                                    server_test_->use_multi_client_socket_);\n      ASSERT_EQ(client.RequestCrashDump(info), 0);\n    }\n\n   private:\n    ExceptionHandlerServerTest* server_test_;\n    bool succeeds_;\n  };\n\n  void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy,\n                                    bool succeeds) {\n    Server()->SetPtraceStrategyDecider(\n        std::make_unique<MockPtraceStrategyDecider>(strategy));\n\n    ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());\n    ServerThread()->Start();\n\n    CrashDumpTest test(this, succeeds);\n    test.Run();\n  }\n\n  bool UsingMultiClientSocket() const { return use_multi_client_socket_; }\n\n protected:\n  void SetUp() override {\n    int socks[2];\n    ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0);\n    sock_to_handler_.reset(socks[0]);\n    sock_to_client_ = socks[1];\n\n    ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1]),\n                                             use_multi_client_socket_));\n  }\n\n private:\n  ExceptionHandlerServer server_;\n  TestDelegate delegate_;\n  RunServerThread server_thread_;\n  ScopedFileHandle sock_to_handler_;\n  int sock_to_client_;\n  bool use_multi_client_socket_;\n};\n\nTEST_P(ExceptionHandlerServerTest, ShutdownWithNoClients) {\n  ServerThread()->Start();\n  Hangup();\n  ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));\n}\n\nTEST_P(ExceptionHandlerServerTest, StopWithClients) {\n  ServerThread()->Start();\n  Server()->Stop();\n  ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));\n}\n\nTEST_P(ExceptionHandlerServerTest, StopBeforeRun) {\n  Server()->Stop();\n  ServerThread()->Start();\n  ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));\n}\n\nTEST_P(ExceptionHandlerServerTest, MultipleStops) {\n  ServerThread()->Start();\n  Server()->Stop();\n  Server()->Stop();\n  ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));\n}\n\nTEST_P(ExceptionHandlerServerTest, RequestCrashDumpDefault) {\n  ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());\n  ServerThread()->Start();\n\n  CrashDumpTest test(this, true);\n  test.Run();\n}\n\nTEST_P(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {\n  ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace,\n                               false);\n}\n\nTEST_P(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) {\n  if (UsingMultiClientSocket()) {\n    // The broker is not supported with multiple clients connected on a single\n    // socket.\n    return;\n  }\n  ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kUseBroker,\n                               true);\n}\n\nTEST_P(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {\n  ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace,\n                               true);\n}\n\nTEST_P(ExceptionHandlerServerTest, RequestCrashDumpError) {\n  ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false);\n}\n\nINSTANTIATE_TEST_SUITE_P(ExceptionHandlerServerTestSuite,\n                         ExceptionHandlerServerTest,\n                         testing::Bool()\n);\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/linux/handler_trampoline.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <android/log.h>\n#include <dlfcn.h>\n#include <stdlib.h>\n\n#include \"util/misc/no_cfi_icall.h\"\n\n// The first argument passed to the trampoline is the name of the native library\n// exporting the symbol `CrashpadHandlerMain`. The remaining arguments are the\n// same as for `HandlerMain()`.\nint main(int argc, char* argv[]) {\n  static constexpr char kTag[] = \"crashpad\";\n\n  if (argc < 2) {\n    __android_log_print(ANDROID_LOG_FATAL, kTag, \"usage: %s <path>\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  void* handle = dlopen(argv[1], RTLD_LAZY | RTLD_GLOBAL);\n  if (!handle) {\n    __android_log_print(ANDROID_LOG_FATAL, kTag, \"dlopen: %s\", dlerror());\n    return EXIT_FAILURE;\n  }\n\n  using MainType = int (*)(int, char*[]);\n  const crashpad::NoCfiIcall<MainType> crashpad_main(\n      dlsym(handle, \"CrashpadHandlerMain\"));\n  if (!crashpad_main) {\n    __android_log_print(ANDROID_LOG_FATAL, kTag, \"dlsym: %s\", dlerror());\n    return EXIT_FAILURE;\n  }\n\n  return crashpad_main(argc - 1, argv + 1);\n}\n"
  },
  {
    "path": "handler/mac/crash_report_exception_handler.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/mac/crash_report_exception_handler.h\"\n\n#include <utility>\n#include <vector>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/files/scoped_file.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"client/settings.h\"\n#include \"handler/mac/file_limit_annotation.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/minidump_user_extension_stream_data_source.h\"\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/mac/process_snapshot_mac.h\"\n#include \"util/file/file_helper.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/mach/bootstrap.h\"\n#include \"util/mach/exc_client_variants.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/exception_ports.h\"\n#include \"util/mach/exception_types.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/scoped_task_suspend.h\"\n#include \"util/mach/symbolic_constants_mach.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/misc/tri_state.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\nCrashReportExceptionHandler::CrashReportExceptionHandler(\n    CrashReportDatabase* database,\n    CrashReportUploadThread* upload_thread,\n    const std::map<std::string, std::string>* process_annotations,\n    const std::vector<base::FilePath>* attachments,\n    const UserStreamDataSources* user_stream_data_sources)\n    : database_(database),\n      upload_thread_(upload_thread),\n      process_annotations_(process_annotations),\n      attachments_(attachments),\n      user_stream_data_sources_(user_stream_data_sources) {}\n\nCrashReportExceptionHandler::~CrashReportExceptionHandler() {\n}\n\nkern_return_t CrashReportExceptionHandler::CatchMachException(\n    exception_behavior_t behavior,\n    exception_handler_t exception_port,\n    thread_t thread,\n    task_t task,\n    exception_type_t exception,\n    const mach_exception_data_type_t* code,\n    mach_msg_type_number_t code_count,\n    thread_state_flavor_t* flavor,\n    ConstThreadState old_state,\n    mach_msg_type_number_t old_state_count,\n    thread_state_t new_state,\n    mach_msg_type_number_t* new_state_count,\n    const mach_msg_trailer_t* trailer,\n    bool* destroy_complex_request) {\n  RecordFileLimitAnnotation();\n  Metrics::ExceptionEncountered();\n  Metrics::ExceptionCode(ExceptionCodeForMetrics(exception, code[0]));\n  *destroy_complex_request = true;\n\n  // The expected behavior is EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,\n  // but it’s possible to deal with any exception behavior as long as it\n  // carries identity information (valid thread and task ports).\n  if (!ExceptionBehaviorHasIdentity(behavior)) {\n    LOG(ERROR) << base::StringPrintf(\n        \"unexpected exception behavior %s, rejecting\",\n        ExceptionBehaviorToString(\n            behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kUnexpectedExceptionBehavior);\n    return KERN_FAILURE;\n  } else if (behavior != (EXCEPTION_STATE_IDENTITY | kMachExceptionCodes)) {\n    LOG(WARNING) << base::StringPrintf(\n        \"unexpected exception behavior %s, proceeding\",\n        ExceptionBehaviorToString(\n            behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());\n  }\n\n  if (task == mach_task_self()) {\n    LOG(ERROR) << \"cannot suspend myself\";\n    Metrics::ExceptionCaptureResult(\n        Metrics::CaptureResult::kFailedDueToSuspendSelf);\n    return KERN_FAILURE;\n  }\n\n  ScopedTaskSuspend suspend(task);\n\n  ProcessSnapshotMac process_snapshot;\n  if (!process_snapshot.Initialize(task)) {\n    Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);\n    return KERN_FAILURE;\n  }\n\n  // Check for suspicious message sources. A suspicious exception message comes\n  // from a source other than the kernel or the process that the exception\n  // purportedly occurred in.\n  //\n  // TODO(mark): Consider exceptions outside of the range (0, 32) from the\n  // kernel to be suspicious, and exceptions other than kMachExceptionSimulated\n  // from the process itself to be suspicious.\n  const pid_t pid = process_snapshot.ProcessID();\n  pid_t audit_pid = AuditPIDFromMachMessageTrailer(trailer);\n  if (audit_pid != -1 && audit_pid != 0) {\n    if (audit_pid != pid) {\n      LOG(WARNING) << \"exception for pid \" << pid << \" sent by pid \"\n                   << audit_pid;\n    }\n  }\n\n  CrashpadInfoClientOptions client_options;\n  process_snapshot.GetCrashpadOptions(&client_options);\n\n  if (client_options.crashpad_handler_behavior != TriState::kDisabled &&\n      !IsExceptionNonfatalResource(exception, code[0], pid)) {\n    // Non-fatal resource exceptions are never user-visible and are not\n    // currently of interest to Crashpad.\n\n    if (!process_snapshot.InitializeException(behavior,\n                                              thread,\n                                              exception,\n                                              code,\n                                              code_count,\n                                              *flavor,\n                                              old_state,\n                                              old_state_count)) {\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kExceptionInitializationFailed);\n      return KERN_FAILURE;\n    }\n\n    UUID client_id;\n    Settings* const settings = database_->GetSettings();\n    if (settings && settings->GetClientID(&client_id)) {\n      process_snapshot.SetClientID(client_id);\n    }\n    process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);\n\n    std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n    CrashReportDatabase::OperationStatus database_status =\n        database_->PrepareNewCrashReport(&new_report);\n    if (database_status != CrashReportDatabase::kNoError) {\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kPrepareNewCrashReportFailed);\n      return KERN_FAILURE;\n    }\n\n    process_snapshot.SetReportID(new_report->ReportID());\n\n    MinidumpFileWriter minidump;\n    minidump.InitializeFromSnapshot(&process_snapshot);\n    AddUserExtensionStreams(\n        user_stream_data_sources_, &process_snapshot, &minidump);\n\n    for (const auto& attachment : *attachments_) {\n      base::FilePath name = attachment.BaseName();\n      FileWriter* writer = new_report->AddAttachment(name.value());\n      if (!writer) {\n        LOG(WARNING) << \"Failed to add attachment\";\n        continue;\n      }\n      FileReader reader;\n      if (!reader.Open(attachment)) {\n        LOG(WARNING) << \"Failed to open attachment \" << attachment.value();\n        continue;\n      }\n      CopyFileContent(&reader, writer);\n    }\n\n    if (!minidump.WriteEverything(new_report->Writer())) {\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kMinidumpWriteFailed);\n      return KERN_FAILURE;\n    }\n\n    UUID uuid;\n    database_status =\n        database_->FinishedWritingCrashReport(std::move(new_report), &uuid);\n    if (database_status != CrashReportDatabase::kNoError) {\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kFinishedWritingCrashReportFailed);\n      return KERN_FAILURE;\n    }\n\n    if (upload_thread_) {\n      upload_thread_->ReportPending(uuid);\n    }\n  }\n\n  if (client_options.system_crash_reporter_forwarding != TriState::kDisabled) {\n    if (exception == EXC_CRASH) {\n      // For exception handlers that respond to state-carrying behaviors, when\n      // the handler is called by the kernel (as it is normally), the kernel\n      // will attempt to set a new thread state when the exception handler\n      // returns successfully. Other code that mimics the kernel’s\n      // exception-delivery semantics may implement the same or similar\n      // behavior. In some situations, it is undesirable to set a new thread\n      // state. If the exception handler were to return unsuccessfully, however,\n      // the kernel would continue searching for an exception handler at a wider\n      // (task or host) scope. This may also be undesirable.\n      //\n      // If such exception handlers return `MACH_RCV_PORT_DIED`, the kernel will\n      // not set a new thread state and will also not search for another\n      // exception handler. See 15.3 xnu-11215.84.4/osfmk/kern/exception.c.\n      // `exception_deliver()` will only set a new thread state if the handler’s\n      // return code was `MACH_MSG_SUCCESS` (a synonym for `KERN_SUCCESS`), and\n      // subsequently, `exception_triage()` will not search for a new handler if\n      // the handler’s return code was `KERN_SUCCESS` or `MACH_RCV_PORT_DIED`.\n      //\n      // Another effect of returning `MACH_RCV_PORT_DIED` for `EXC_CRASH` is\n      // that an `EXC_CORPSE_NOTIFY` exception is generated. Starting with macOS\n      // 10.15, for the system crash reporter to generate a report,\n      // `EXC_CORPSE_NOTIFY` *must* be generated and forwarding `EXC_CRASH` (as\n      // we do below with `EXC_RESOURCE` and pre-macOS 13 `EXC_GUARD`) is not\n      // sufficient. Between macOS 10.11 and macOS 10.14 (inclusive), both\n      // forwarding as below, and causing `EXC_CORPSE_NOTIFY` to be generated\n      // are sufficient (and in fact, if we do both, two crash reports are\n      // generated).\n      return MACH_RCV_PORT_DIED;\n    }\n    if (exception == EXC_RESOURCE || exception == EXC_GUARD) {\n      // Only forward the types of exceptions that the crash reporter would\n      // receive under normal conditions. Otherwise, system crash reporter could\n      // present the system’s crash UI for processes that haven’t actually\n      // crashed, and could result in reports not actually associated with\n      // crashes being sent to the operating system vendor.\n      base::apple::ScopedMachSendRight system_crash_reporter_handler(\n          SystemCrashReporterHandler());\n      if (system_crash_reporter_handler.get()) {\n        // Make copies of mutable out parameters so that the system crash\n        // reporter can’t influence the state returned by this method.\n        thread_state_flavor_t flavor_forward = *flavor;\n        mach_msg_type_number_t new_state_forward_count = *new_state_count;\n        std::vector<natural_t> new_state_forward;\n        if (new_state_forward_count) {\n          new_state_forward.assign(new_state,\n                                   new_state + new_state_forward_count);\n        }\n        kern_return_t kr = UniversalExceptionRaise(\n            EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,\n            system_crash_reporter_handler.get(),\n            thread,\n            task,\n            exception,\n            code,\n            code_count,\n            &flavor_forward,\n            old_state,\n            old_state_count,\n            new_state_forward_count ? &new_state_forward[0] : nullptr,\n            &new_state_forward_count);\n        MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr)\n            << \"UniversalExceptionRaise\";\n      }\n    }\n  }\n\n  ExcServerCopyState(\n      behavior, old_state, old_state_count, new_state, new_state_count);\n\n  Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);\n  return KERN_SUCCESS;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/mac/crash_report_exception_handler.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_\n#define CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_\n\n#include <mach/mach.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"client/crash_report_database.h\"\n#include \"handler/crash_report_upload_thread.h\"\n#include \"handler/user_stream_data_source.h\"\n#include \"util/mach/exc_server_variants.h\"\n\nnamespace crashpad {\n\n//! \\brief An exception handler that writes crash reports for exception messages\n//!     to a CrashReportDatabase.\nclass CrashReportExceptionHandler final\n    : public UniversalMachExcServer::Interface {\n public:\n  //! \\brief Creates a new object that will store crash reports in \\a database.\n  //!\n  //! \\param[in] database The database to store crash reports in. Weak.\n  //! \\param[in] upload_thread The upload thread to notify when a new crash\n  //!     report is written into \\a database. Report upload is skipped if this\n  //!     value is `nullptr`.\n  //! \\param[in] process_annotations A map of annotations to insert as\n  //!     process-level annotations into each crash report that is written. Do\n  //!     not confuse this with module-level annotations, which are under the\n  //!     control of the crashing process, and are used to implement Chrome’s\n  //!     “crash keys.” Process-level annotations are those that are beyond the\n  //!     control of the crashing process, which must reliably be set even if\n  //!     the process crashes before it’s able to establish its own annotations.\n  //!     To interoperate with Breakpad servers, the recommended practice is to\n  //!     specify values for the `\"prod\"` and `\"ver\"` keys as process\n  //!     annotations.\n  //! \\param[in] user_stream_data_sources Data sources to be used to extend\n  //!     crash reports. For each crash report that is written, the data sources\n  //!     are called in turn. These data sources may contribute additional\n  //!     minidump streams. `nullptr` if not required.\n  CrashReportExceptionHandler(\n      CrashReportDatabase* database,\n      CrashReportUploadThread* upload_thread,\n      const std::map<std::string, std::string>* process_annotations,\n      const std::vector<base::FilePath>* attachments,\n      const UserStreamDataSources* user_stream_data_sources);\n\n  CrashReportExceptionHandler(const CrashReportExceptionHandler&) = delete;\n  CrashReportExceptionHandler& operator=(const CrashReportExceptionHandler&) =\n      delete;\n\n  ~CrashReportExceptionHandler();\n\n  // UniversalMachExcServer::Interface:\n\n  //! \\brief Processes an exception message by writing a crash report to this\n  //!     object’s CrashReportDatabase.\n  kern_return_t CatchMachException(\n      exception_behavior_t behavior,\n      exception_handler_t exception_port,\n      thread_t thread,\n      task_t task,\n      exception_type_t exception,\n      const mach_exception_data_type_t* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer,\n      bool* destroy_complex_request) override;\n\n private:\n  CrashReportDatabase* database_;  // weak\n  CrashReportUploadThread* upload_thread_;  // weak\n  const std::map<std::string, std::string>* process_annotations_;  // weak\n  const std::vector<base::FilePath>* attachments_;  // weak\n  const UserStreamDataSources* user_stream_data_sources_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_\n"
  },
  {
    "path": "handler/mac/exception_handler_server.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/mac/exception_handler_server.h\"\n\n#include <utility>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"util/mach/composite_mach_message_server.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n#include \"util/mach/notify_server.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nclass ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,\n                                  public NotifyServer::DefaultInterface {\n public:\n  ExceptionHandlerServerRun(\n      mach_port_t exception_port,\n      mach_port_t notify_port,\n      bool launchd,\n      UniversalMachExcServer::Interface* exception_interface)\n      : UniversalMachExcServer::Interface(),\n        NotifyServer::DefaultInterface(),\n        mach_exc_server_(this),\n        notify_server_(this),\n        composite_mach_message_server_(),\n        exception_interface_(exception_interface),\n        exception_port_(exception_port),\n        notify_port_(notify_port),\n        running_(true),\n        launchd_(launchd) {\n    composite_mach_message_server_.AddHandler(&mach_exc_server_);\n    composite_mach_message_server_.AddHandler(&notify_server_);\n  }\n\n  ExceptionHandlerServerRun(const ExceptionHandlerServerRun&) = delete;\n  ExceptionHandlerServerRun& operator=(const ExceptionHandlerServerRun&) =\n      delete;\n\n  ~ExceptionHandlerServerRun() {\n  }\n\n  void Run() {\n    DCHECK(running_);\n\n    kern_return_t kr;\n    if (!launchd_) {\n      // Request that a no-senders notification for exception_port_ be sent to\n      // notify_port_.\n      mach_port_t previous;\n      kr = mach_port_request_notification(mach_task_self(),\n                                          exception_port_,\n                                          MACH_NOTIFY_NO_SENDERS,\n                                          0,\n                                          notify_port_,\n                                          MACH_MSG_TYPE_MAKE_SEND_ONCE,\n                                          &previous);\n      MACH_CHECK(kr == KERN_SUCCESS, kr) << \"mach_port_request_notification\";\n      base::apple::ScopedMachSendRight previous_owner(previous);\n    }\n\n    // A single CompositeMachMessageServer will dispatch both exception messages\n    // and the no-senders notification. Put both receive rights into a port set.\n    //\n    // A single receive right can’t be used because the notification request\n    // requires a send-once right, which would prevent the no-senders condition\n    // from ever existing. Using distinct receive rights also allows the handler\n    // methods to ensure that the messages they process were sent by a holder of\n    // the proper send right.\n    base::apple::ScopedMachPortSet server_port_set(\n        NewMachPort(MACH_PORT_RIGHT_PORT_SET));\n    CHECK(server_port_set.is_valid());\n\n    kr = mach_port_insert_member(\n        mach_task_self(), exception_port_, server_port_set.get());\n    MACH_CHECK(kr == KERN_SUCCESS, kr) << \"mach_port_insert_member\";\n\n    kr = mach_port_insert_member(\n        mach_task_self(), notify_port_, server_port_set.get());\n    MACH_CHECK(kr == KERN_SUCCESS, kr) << \"mach_port_insert_member\";\n\n    // Run the server in kOneShot mode so that running_ can be reevaluated after\n    // each message. Receipt of a valid no-senders notification causes it to be\n    // set to false.\n    while (running_) {\n      // This will result in a call to CatchMachException() or\n      // DoMachNotifyNoSenders() as appropriate.\n      mach_msg_return_t mr =\n          MachMessageServer::Run(&composite_mach_message_server_,\n                                 server_port_set.get(),\n                                 kMachMessageReceiveAuditTrailer,\n                                 MachMessageServer::kOneShot,\n                                 MachMessageServer::kReceiveLargeIgnore,\n                                 kMachMessageTimeoutWaitIndefinitely);\n\n      // MACH_SEND_INVALID_DEST occurs when attempting to reply to a dead name.\n      // This can happen if a mach_exc or exc client disappears before a reply\n      // can be sent to it. That’s unusal for kernel-generated requests, but can\n      // easily happen if a task sends its own exception request (as\n      // SimulateCrash() does) and dies before the reply is sent.\n      MACH_CHECK(mr == MACH_MSG_SUCCESS || mr == MACH_SEND_INVALID_DEST, mr)\n          << \"MachMessageServer::Run\";\n    }\n  }\n\n  // UniversalMachExcServer::Interface:\n\n  kern_return_t CatchMachException(exception_behavior_t behavior,\n                                   exception_handler_t exception_port,\n                                   thread_t thread,\n                                   task_t task,\n                                   exception_type_t exception,\n                                   const mach_exception_data_type_t* code,\n                                   mach_msg_type_number_t code_count,\n                                   thread_state_flavor_t* flavor,\n                                   ConstThreadState old_state,\n                                   mach_msg_type_number_t old_state_count,\n                                   thread_state_t new_state,\n                                   mach_msg_type_number_t* new_state_count,\n                                   const mach_msg_trailer_t* trailer,\n                                   bool* destroy_complex_request) override {\n    if (exception_port != exception_port_) {\n      LOG(WARNING) << \"exception port mismatch\";\n      return KERN_FAILURE;\n    }\n\n    return exception_interface_->CatchMachException(behavior,\n                                                    exception_port,\n                                                    thread,\n                                                    task,\n                                                    exception,\n                                                    code,\n                                                    code_count,\n                                                    flavor,\n                                                    old_state,\n                                                    old_state_count,\n                                                    new_state,\n                                                    new_state_count,\n                                                    trailer,\n                                                    destroy_complex_request);\n  }\n\n  // NotifyServer::DefaultInterface:\n\n  kern_return_t DoMachNotifyNoSenders(\n      notify_port_t notify,\n      mach_port_mscount_t mscount,\n      const mach_msg_trailer_t* trailer) override {\n    if (notify != notify_port_) {\n      // The message was received as part of a port set. This check ensures that\n      // only the authorized sender of the no-senders notification is able to\n      // stop the exception server. Otherwise, a malicious client would be able\n      // to craft and send a no-senders notification via its exception port, and\n      // cause the handler to stop processing exceptions and exit.\n      LOG(WARNING) << \"notify port mismatch\";\n      return KERN_FAILURE;\n    }\n\n    running_ = false;\n\n    return KERN_SUCCESS;\n  }\n\n private:\n  UniversalMachExcServer mach_exc_server_;\n  NotifyServer notify_server_;\n  CompositeMachMessageServer composite_mach_message_server_;\n  UniversalMachExcServer::Interface* exception_interface_;  // weak\n  mach_port_t exception_port_;  // weak\n  mach_port_t notify_port_;  // weak\n  bool running_;\n  bool launchd_;\n};\n\n}  // namespace\n\nExceptionHandlerServer::ExceptionHandlerServer(\n    base::apple::ScopedMachReceiveRight receive_port,\n    bool launchd)\n    : receive_port_(std::move(receive_port)),\n      notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)),\n      launchd_(launchd) {\n  CHECK(receive_port_.is_valid());\n  CHECK(notify_port_.is_valid());\n}\n\nExceptionHandlerServer::~ExceptionHandlerServer() {\n}\n\nvoid ExceptionHandlerServer::Run(\n    UniversalMachExcServer::Interface* exception_interface) {\n  ExceptionHandlerServerRun run(\n      receive_port_.get(), notify_port_.get(), launchd_, exception_interface);\n  run.Run();\n}\n\nvoid ExceptionHandlerServer::Stop() {\n  // Cause the exception handler server to stop running by sending it a\n  // synthesized no-senders notification.\n  //\n  // mach_no_senders_notification_t defines the receive side of this structure,\n  // with a trailer element that’s undesirable for the send side.\n  struct {\n    mach_msg_header_t header;\n    NDR_record_t ndr;\n    mach_msg_type_number_t mscount;\n  } no_senders_notification = {};\n  no_senders_notification.header.msgh_bits =\n      MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0);\n  no_senders_notification.header.msgh_size = sizeof(no_senders_notification);\n  no_senders_notification.header.msgh_remote_port = notify_port_.get();\n  no_senders_notification.header.msgh_local_port = MACH_PORT_NULL;\n  no_senders_notification.header.msgh_id = MACH_NOTIFY_NO_SENDERS;\n  no_senders_notification.ndr = NDR_record;\n  no_senders_notification.mscount = 0;\n\n  kern_return_t kr = mach_msg(&no_senders_notification.header,\n                              MACH_SEND_MSG,\n                              sizeof(no_senders_notification),\n                              0,\n                              MACH_PORT_NULL,\n                              MACH_MSG_TIMEOUT_NONE,\n                              MACH_PORT_NULL);\n  MACH_CHECK(kr == KERN_SUCCESS, kr) << \"mach_msg\";\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/mac/exception_handler_server.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_\n#define CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_\n\n#include <mach/mach.h>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"util/mach/exc_server_variants.h\"\n\nnamespace crashpad {\n\n//! \\brief Runs the main exception-handling server in Crashpad’s handler\n//!     process.\nclass ExceptionHandlerServer {\n public:\n  //! \\brief Constructs an ExceptionHandlerServer object.\n  //!\n  //! \\param[in] receive_port The port that exception messages and no-senders\n  //!     notifications will be received on.\n  //! \\param[in] launchd If `true`, the exception handler is being run from\n  //!     launchd. \\a receive_port is not monitored for no-senders\n  //!     notifications, and instead, Stop() must be called to provide a “quit”\n  //!     signal.\n  ExceptionHandlerServer(base::apple::ScopedMachReceiveRight receive_port,\n                         bool launchd);\n\n  ExceptionHandlerServer(const ExceptionHandlerServer&) = delete;\n  ExceptionHandlerServer& operator=(const ExceptionHandlerServer&) = delete;\n\n  ~ExceptionHandlerServer();\n\n  //! \\brief Runs the exception-handling server.\n  //!\n  //! \\param[in] exception_interface An object to send exception messages to.\n  //!\n  //! This method monitors the receive port for exception messages and, if\n  //! not being run by launchd, no-senders notifications. It continues running\n  //! until it has no more clients, indicated by the receipt of a no-senders\n  //! notification, or until Stop() is called. When not being run by launchd, it\n  //! is important to assure that a send right exists in a client (or has been\n  //! queued by `mach_msg()` to be sent to a client) prior to calling this\n  //! method, or it will detect that it is sender-less and return immediately.\n  //!\n  //! All exception messages will be passed to \\a exception_interface.\n  //!\n  //! This method must only be called once on an ExceptionHandlerServer object.\n  //!\n  //! If an unexpected condition that prevents this method from functioning is\n  //! encountered, it will log a message and terminate execution. Receipt of an\n  //! invalid message on the receive port will cause a message to be logged, but\n  //! this method will continue running normally.\n  void Run(UniversalMachExcServer::Interface* exception_interface);\n\n  //! \\brief Stops a running exception-handling server.\n  //!\n  //! Stop() may be called at any time, and may be called from a signal handler.\n  //! If Stop() is called before Run() it will cause Run() to return as soon as\n  //! it is called. It is harmless to call Stop() after Run() has already\n  //! returned, or to call Stop() after it has already been called.\n  void Stop();\n\n private:\n  base::apple::ScopedMachReceiveRight receive_port_;\n  base::apple::ScopedMachReceiveRight notify_port_;\n  bool launchd_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_\n"
  },
  {
    "path": "handler/mac/file_limit_annotation.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/mac/file_limit_annotation.h\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <string.h>\n#include <sys/resource.h>\n#include <sys/sysctl.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <string>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"util/posix/scoped_dir.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// rv is the return value from sysctl() or sysctlbyname(), and value and size\n// are the pointers passed as oldp and oldlenp. If sysctl() failed, the returned\n// string will be \"E\" followed by the error number. If there was a size\n// mismatch, the returned string will be \"Z\" followed by the size indicated by\n// sysctl(). Normally, a string representation of *value will be returned.\nstd::string FormatFromSysctl(int rv, const int* value, const size_t* size) {\n  if (rv != 0) {\n    return base::StringPrintf(\"E%d\", errno);\n  }\n  if (*size != sizeof(*value)) {\n    return base::StringPrintf(\"Z%zu\", *size);\n  }\n  return base::StringPrintf(\"%d\", *value);\n}\n\n// Counts the number of open file descriptors in the process and returns it as a\n// string. This /dev/fd and the value returned will include the open file\n// descriptor for that directory. If opendir() fails, the returned string will\n// be \"E\" followed by the error number. If readdir() fails, it will be \"R\"\n// followed by the error number.\nstd::string CountOpenFileDescriptors() {\n  DIR* dir = opendir(\"/dev/fd\");\n  if (!dir) {\n    return base::StringPrintf(\"E%d\", errno);\n  }\n\n  ScopedDIR dir_owner(dir);\n\n  dirent* entry;\n  int count = 0;\n  while ((errno = 0, entry = readdir(dir)) != nullptr) {\n    const char* entry_name = entry->d_name;\n    if (strcmp(entry_name, \".\") == 0 || strcmp(entry_name, \"..\") == 0) {\n      continue;\n    }\n\n    ++count;\n  }\n\n  if (errno != 0) {\n    return base::StringPrintf(\"R%d\", errno);\n  }\n\n  return base::StringPrintf(\"%d\", count);\n}\n\n// Returns a string for |limit|, or \"inf\" if |limit| is RLIM_INFINITY.\nstd::string StringForRLim(rlim_t limit) {\n  if (limit == RLIM_INFINITY) {\n    return std::string(\"inf\");\n  }\n\n  return base::StringPrintf(\"%\" PRIu64, limit);\n}\n\n}  // namespace\n\nvoid RecordFileLimitAnnotation() {\n  CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();\n  SimpleStringDictionary* simple_annotations =\n      crashpad_info->simple_annotations();\n  if (!simple_annotations) {\n    simple_annotations = new SimpleStringDictionary();\n    crashpad_info->set_simple_annotations(simple_annotations);\n  }\n\n  int value;\n  size_t size = sizeof(value);\n  std::string num_files = FormatFromSysctl(\n      sysctlbyname(\"kern.num_files\", &value, &size, nullptr, 0), &value, &size);\n\n  int mib[] = {CTL_KERN, KERN_MAXFILES};\n  size = sizeof(value);\n  std::string max_files = FormatFromSysctl(\n      sysctl(mib, std::size(mib), &value, &size, nullptr, 0), &value, &size);\n\n  std::string open_files = CountOpenFileDescriptors();\n\n  rlimit limit;\n  std::string nofile;\n  if (getrlimit(RLIMIT_NOFILE, &limit) != 0) {\n    nofile = base::StringPrintf(\"E%d,E%d\", errno, errno);\n  } else {\n    nofile =\n        StringForRLim(limit.rlim_cur) + \",\" + StringForRLim(limit.rlim_max);\n  }\n\n  std::string annotation = base::StringPrintf(\"%s,%s,%s,%s\",\n                                              num_files.c_str(),\n                                              max_files.c_str(),\n                                              open_files.c_str(),\n                                              nofile.c_str());\n  simple_annotations->SetKeyValue(\"file-limits\", annotation.c_str());\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/mac/file_limit_annotation.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_\n#define CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_\n\nnamespace crashpad {\n\n//! \\brief Records a `\"file-limits\"` simple annotation for the process.\n//!\n//! This annotation will be used to confirm the theory that certain crashes are\n//! caused by systems at or near their file descriptor table size limits.\n//!\n//! The format of the annotation is four comma-separated values: the system-wide\n//! `kern.num_files` and `kern.maxfiles` values from `sysctl()`, and the\n//! process-specific current and maximum file descriptor limits from\n//! `getrlimit(RLIMIT_NOFILE, …)`.\n//!\n//! See https://crashpad.chromium.org/bug/180.\n//!\n//! TODO(mark): Remove this annotation after sufficient data has been collected\n//! for analysis.\nvoid RecordFileLimitAnnotation();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_\n"
  },
  {
    "path": "handler/main.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/handler_main.h\"\n\n#include \"build/build_config.h\"\n#include \"tools/tool_support.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <stdlib.h>\n#include <windows.h>\n#endif\n\n#if BUILDFLAG(IS_POSIX)\n\nint main(int argc, char* argv[]) {\n  return crashpad::HandlerMain(argc, argv, nullptr);\n}\n\n#elif BUILDFLAG(IS_WIN)\n\nnamespace {\n\nint HandlerMainAdaptor(int argc, char* argv[]) {\n  return crashpad::HandlerMain(argc, argv, nullptr);\n}\n\n}  // namespace\n\n// The default entry point for /subsystem:windows. In Crashpad’s own build, this\n// is used by crashpad_handler.exe. It’s also used by crashpad_handler.com when\n// produced by editbin from a copy of crashpad_handler.exe.\nint APIENTRY wWinMain(HINSTANCE, HINSTANCE, wchar_t*, int) {\n  return crashpad::ToolSupport::Wmain(__argc, __wargv, HandlerMainAdaptor);\n}\n\n// The default entry point for /subsystem:console. This is not currently used by\n// Crashpad’s own build, but may be used by other builds.\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::ToolSupport::Wmain(argc, argv, HandlerMainAdaptor);\n}\n\n#endif  // BUILDFLAG(IS_POSIX)\n"
  },
  {
    "path": "handler/minidump_to_upload_parameters.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/minidump_to_upload_parameters.h\"\n\n#include \"base/logging.h\"\n#include \"client/annotation.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"util/stdlib/map_insert.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nvoid InsertOrReplaceMapEntry(std::map<std::string, std::string>* map,\n                             const std::string& key,\n                             const std::string& value) {\n  std::string old_value;\n  if (!MapInsertOrReplace(map, key, value, &old_value)) {\n    LOG(WARNING) << \"duplicate key \" << key << \", discarding value \"\n                 << old_value;\n  }\n}\n\n}  // namespace\n\nstd::map<std::string, std::string> BreakpadHTTPFormParametersFromMinidump(\n    const ProcessSnapshot* process_snapshot) {\n  std::map<std::string, std::string> parameters =\n      process_snapshot->AnnotationsSimpleMap();\n\n  std::string list_annotations;\n  for (const ModuleSnapshot* module : process_snapshot->Modules()) {\n    for (const auto& kv : module->AnnotationsSimpleMap()) {\n      if (!parameters.insert(kv).second) {\n        LOG(WARNING) << \"duplicate key \" << kv.first << \", discarding value \"\n                     << kv.second;\n      }\n    }\n\n    for (const std::string& annotation : module->AnnotationsVector()) {\n      list_annotations.append(annotation);\n      list_annotations.append(\"\\n\");\n    }\n\n    for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {\n      if (annotation.type != static_cast<uint16_t>(Annotation::Type::kString)) {\n        continue;\n      }\n\n      std::string value(reinterpret_cast<const char*>(annotation.value.data()),\n                        annotation.value.size());\n      std::pair<std::string, std::string> entry(annotation.name, value);\n      if (!parameters.insert(entry).second) {\n        LOG(WARNING) << \"duplicate annotation name \" << annotation.name\n                     << \", discarding value \" << value;\n      }\n    }\n  }\n\n  if (!list_annotations.empty()) {\n    // Remove the final newline character.\n    list_annotations.resize(list_annotations.size() - 1);\n\n    InsertOrReplaceMapEntry(&parameters, \"list_annotations\", list_annotations);\n  }\n\n  UUID client_id;\n  process_snapshot->ClientID(&client_id);\n  InsertOrReplaceMapEntry(&parameters, \"guid\", client_id.ToString());\n\n  return parameters;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/minidump_to_upload_parameters.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_\n#define HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_\n\n#include <map>\n#include <string>\n\nnamespace crashpad {\n\nclass ProcessSnapshot;\n\n//! \\brief Given a ProcessSnapshot, returns a map of key-value pairs to use as\n//!     HTTP form parameters for upload to a Breakpad crash report colleciton\n//!     server.\n//!\n//! The map is built by combining the process simple annotations map with\n//! each module’s simple annotations map and annotation objects.\n//!\n//! In the case of duplicate simple map keys or annotation names, the map will\n//! retain the first value found for any key, and will log a warning about\n//! discarded values. The precedence rules for annotation names are: the two\n//! reserved keys discussed below, process simple annotations, module simple\n//! annotations, and module annotation objects.\n//!\n//! For annotation objects, only ones of that are Annotation::Type::kString are\n//! included.\n//!\n//! Each module’s annotations vector is also examined and built into a single\n//! string value, with distinct elements separated by newlines, and stored at\n//! the key named “list_annotations”, which supersedes any other key found by\n//! that name.\n//!\n//! The client ID stored in the minidump is converted to a string and stored at\n//! the key named “guid”, which supersedes any other key found by that name.\n//!\n//! In the event of an error reading the minidump file, a message will be\n//! logged.\n//!\n//! \\param[in] process_snapshot The process snapshot from which annotations\n//!     will be extracted.\n//!\n//! \\returns A string map of the annotations.\nstd::map<std::string, std::string> BreakpadHTTPFormParametersFromMinidump(\n    const ProcessSnapshot* process_snapshot);\n\n}  // namespace crashpad\n\n#endif  // HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_\n"
  },
  {
    "path": "handler/minidump_to_upload_parameters_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/minidump_to_upload_parameters.h\"\n\n#include \"gtest/gtest.h\"\n#include \"snapshot/test/test_module_snapshot.h\"\n#include \"snapshot/test/test_process_snapshot.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(MinidumpToUploadParameters, PrecedenceRules) {\n  const std::string guid = \"00112233-4455-6677-8899-aabbccddeeff\";\n  UUID uuid;\n  ASSERT_TRUE(uuid.InitializeFromString(guid));\n\n  TestProcessSnapshot process_snapshot;\n  process_snapshot.SetClientID(uuid);\n  process_snapshot.SetAnnotationsSimpleMap({\n      {\"process-1\", \"abcdefg\"},\n      {\"list_annotations\", \"BAD: process_annotations\"},\n      {\"guid\", \"BAD: process_annotations\"},\n      {\"first\", \"process\"},\n  });\n\n  auto module_snapshot_0 = std::make_unique<TestModuleSnapshot>();\n  module_snapshot_0->SetAnnotationsVector(\n      {\"list-module-0-1\", \"list-module-0-2\"});\n  module_snapshot_0->SetAnnotationsSimpleMap({\n      {\"module-0-1\", \"goat\"},\n      {\"module-0-2\", \"doge\"},\n      {\"list_annotations\", \"BAD: module 0\"},\n      {\"guid\", \"BAD: module 0\"},\n      {\"first\", \"BAD: module 0\"},\n      {\"second\", \"module 0\"},\n  });\n  module_snapshot_0->SetAnnotationObjects({\n      {\"module-0-3\", 1, {'s', 't', 'a', 'r'}},\n      {\"module-0-4\", 0xFFFA, {0x42}},\n      {\"guid\", 1, {'B', 'A', 'D', '*', '0', '-', '0'}},\n      {\"list_annotations\", 1, {'B', 'A', 'D', '*', '0', '-', '1'}},\n      {\"first\", 1, {'B', 'A', 'D', '*', '0', '-', '2'}},\n  });\n  process_snapshot.AddModule(std::move(module_snapshot_0));\n\n  auto module_snapshot_1 = std::make_unique<TestModuleSnapshot>();\n  module_snapshot_1->SetAnnotationsVector(\n      {\"list-module-1-1\", \"list-module-1-2\"});\n  module_snapshot_1->SetAnnotationsSimpleMap({\n      {\"module-1-1\", \"bear\"},\n      {\"list_annotations\", \"BAD: module 1\"},\n      {\"guid\", \"BAD: module 1\"},\n      {\"first\", \"BAD: module 1\"},\n      {\"second\", \"BAD: module 1\"},\n  });\n  module_snapshot_1->SetAnnotationObjects({\n      {\"module-1-3\", 0xBEEF, {'a', 'b', 'c'}},\n      {\"module-1-4\", 1, {'m', 'o', 'o', 'n'}},\n      {\"guid\", 1, {'B', 'A', 'D', '*', '1', '-', '0'}},\n      {\"list_annotations\", 1, {'B', 'A', 'D', '*', '1', '-', '1'}},\n      {\"second\", 1, {'B', 'A', 'D', '*', '1', '-', '2'}},\n  });\n  process_snapshot.AddModule(std::move(module_snapshot_1));\n\n  auto upload_parameters =\n      BreakpadHTTPFormParametersFromMinidump(&process_snapshot);\n\n  EXPECT_EQ(upload_parameters.size(), 10u);\n  EXPECT_EQ(upload_parameters[\"process-1\"], \"abcdefg\");\n  EXPECT_EQ(upload_parameters[\"first\"], \"process\");\n  EXPECT_EQ(upload_parameters[\"module-0-1\"], \"goat\");\n  EXPECT_EQ(upload_parameters[\"module-0-2\"], \"doge\");\n  EXPECT_EQ(upload_parameters[\"module-0-3\"], \"star\");\n  EXPECT_EQ(upload_parameters[\"second\"], \"module 0\");\n  EXPECT_EQ(upload_parameters[\"module-1-1\"], \"bear\");\n  EXPECT_EQ(upload_parameters[\"module-1-4\"], \"moon\");\n  EXPECT_EQ(upload_parameters[\"list_annotations\"],\n            \"list-module-0-1\\nlist-module-0-2\\n\"\n            \"list-module-1-1\\nlist-module-1-2\");\n  EXPECT_EQ(upload_parameters[\"guid\"], guid);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/prune_crash_reports_thread.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/prune_crash_reports_thread.h\"\n\n#include <utility>\n\n#include \"client/prune_crash_reports.h\"\n\nnamespace crashpad {\n\nPruneCrashReportThread::PruneCrashReportThread(\n    CrashReportDatabase* database,\n    std::unique_ptr<PruneCondition> condition)\n    : thread_(60 * 60 * 24, this),\n      condition_(std::move(condition)),\n      database_(database) {}\n\nPruneCrashReportThread::~PruneCrashReportThread() {}\n\nvoid PruneCrashReportThread::Start() {\n  thread_.Start(60 * 10);\n}\n\nvoid PruneCrashReportThread::Stop() {\n  thread_.Stop();\n}\n\nvoid PruneCrashReportThread::DoWork(const WorkerThread* thread) {\n  database_->CleanDatabase(60 * 60 * 24 * 3);\n  PruneCrashReportDatabase(database_, condition_.get());\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/prune_crash_reports_thread.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_\n#define CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_\n\n#include <memory>\n\n#include \"util/thread/stoppable.h\"\n#include \"util/thread/worker_thread.h\"\n\nnamespace crashpad {\n\nclass CrashReportDatabase;\nclass PruneCondition;\n\n//! \\brief A thread that periodically prunes crash reports from the database\n//!     using the specified condition.\n//!\n//! After the thread is started, the database is pruned using the condition\n//! every 24 hours. Upon calling Start(), the thread waits 10 minutes before\n//! performing the initial prune operation.\nclass PruneCrashReportThread : public WorkerThread::Delegate, public Stoppable {\n public:\n  //! \\brief Constructs a new object.\n  //!\n  //! \\param[in] database The database to prune crash reports from.\n  //! \\param[in] condition The condition used to evaluate crash reports for\n  //!     pruning.\n  PruneCrashReportThread(CrashReportDatabase* database,\n                         std::unique_ptr<PruneCondition> condition);\n\n  PruneCrashReportThread(const PruneCrashReportThread&) = delete;\n  PruneCrashReportThread& operator=(const PruneCrashReportThread&) = delete;\n\n  ~PruneCrashReportThread();\n\n  // Stoppable:\n\n  //! \\brief Starts a dedicated pruning thread.\n  //!\n  //! The thread waits before running the initial prune, so as to not interfere\n  //! with any startup-related IO performed by the client.\n  //!\n  //! This method may only be be called on a newly-constructed object or after\n  //! a call to Stop().\n  void Start() override;\n\n  //! \\brief Stops the pruning thread.\n  //!\n  //! This method must only be called after Start(). If Start() has been called,\n  //! this method must be called before destroying an object of this class.\n  //!\n  //! This method may be called from any thread other than the pruning thread.\n  //! It is expected to only be called from the same thread that called Start().\n  void Stop() override;\n\n private:\n  // WorkerThread::Delegate:\n  void DoWork(const WorkerThread* thread) override;\n\n  WorkerThread thread_;\n  std::unique_ptr<PruneCondition> condition_;\n  CrashReportDatabase* database_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_\n"
  },
  {
    "path": "handler/user_stream_data_source.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/user_stream_data_source.h\"\n\n#include \"base/logging.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/minidump_user_extension_stream_data_source.h\"\n#include \"snapshot/process_snapshot.h\"\n\nnamespace crashpad {\n\nvoid AddUserExtensionStreams(\n    const UserStreamDataSources* user_stream_data_sources,\n    ProcessSnapshot* process_snapshot,\n    MinidumpFileWriter* minidump_file_writer) {\n  if (!user_stream_data_sources)\n    return;\n  for (const auto& source : *user_stream_data_sources) {\n    std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source(\n        source->ProduceStreamData(process_snapshot));\n    if (data_source &&\n        !minidump_file_writer->AddUserExtensionStream(std::move(data_source))) {\n      // This should only happen if multiple user stream sources yield the\n      // same stream type. It's the user's responsibility to make sure\n      // sources don't collide on the same stream type.\n      LOG(ERROR) << \"AddUserExtensionStream failed\";\n    }\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/user_stream_data_source.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_\n#define CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_\n\n#include <memory>\n#include <vector>\n\nnamespace crashpad {\n\nclass MinidumpFileWriter;\nclass MinidumpUserExtensionStreamDataSource;\nclass ProcessSnapshot;\n\n//! \\brief Extensibility interface for embedders who wish to add custom streams\n//!     to minidumps.\nclass UserStreamDataSource {\n public:\n  virtual ~UserStreamDataSource() {}\n\n  //! \\brief Produce the contents for an extension stream for a crashed program.\n  //!\n  //! Called after \\a process_snapshot has been initialized for the crashed\n  //! process to (optionally) produce the contents of a user extension stream\n  //! that will be attached to the minidump.\n  //!\n  //! \\param[in] process_snapshot An initialized snapshot for the crashed\n  //!     process.\n  //!\n  //! \\return A new data source for the stream to add to the minidump or\n  //!      `nullptr` on failure or to opt out of adding a stream.\n  virtual std::unique_ptr<MinidumpUserExtensionStreamDataSource>\n  ProduceStreamData(ProcessSnapshot* process_snapshot) = 0;\n};\n\nusing UserStreamDataSources =\n    std::vector<std::unique_ptr<UserStreamDataSource>>;\n\n//! \\brief Adds user extension streams to a minidump.\n//!\n//! Dispatches to each source in \\a user_stream_data_sources and adds returned\n//! extension streams to \\a minidump_file_writer.\n//!\n//! \\param[in] user_stream_data_sources A pointer to the data sources, or\n//!     `nullptr`.\n//! \\param[in] process_snapshot An initialized snapshot to the crashing process.\n//! \\param[in] minidump_file_writer Any extension streams will be added to this\n//!     minidump.\nvoid AddUserExtensionStreams(\n    const UserStreamDataSources* user_stream_data_sources,\n    ProcessSnapshot* process_snapshot,\n    MinidumpFileWriter* minidump_file_writer);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_\n"
  },
  {
    "path": "handler/win/.gitattributes",
    "content": "# Copyright 2019 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This should be a .cc file, which would allow its attributes to be controlled\n# by the *.cc pattern in the root .gitattributes file, but it’s named with a\n# .cpp extension instead. This file needs to be built with VC++6, a vintage 1998\n# compiler, which might not understand .cc to mean C++.  Rather than setting\n# attributes globally for .cpp files, which are undesirable (.cc should be used\n# in its place), provide a file-specific mapping here.\n/z7_test.cpp text eol=lf\n"
  },
  {
    "path": "handler/win/crash_other_program.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdlib.h>\n#include <windows.h>\n#include <tlhelp32.h>\n\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"client/crashpad_client.h\"\n#include \"gtest/gtest.h\"\n#include \"test/test_paths.h\"\n#include \"test/win/child_launcher.h\"\n#include \"util/file/file_io.h\"\n#include \"util/win/exception_codes.h\"\n#include \"util/win/scoped_handle.h\"\n#include \"util/win/xp_compat.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr DWORD kCrashAndDumpTargetExitCode = 0xdeadbea7;\n\nbool CrashAndDumpTarget(HANDLE process) {\n  DWORD target_pid = GetProcessId(process);\n\n  ScopedFileHANDLE thread_snap(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));\n  if (!thread_snap.is_valid()) {\n    PLOG(ERROR) << \"CreateToolhelp32Snapshot\";\n    return false;\n  }\n\n  THREADENTRY32 te32;\n  te32.dwSize = sizeof(THREADENTRY32);\n  if (!Thread32First(thread_snap.get(), &te32)) {\n    PLOG(ERROR) << \"Thread32First\";\n    return false;\n  }\n\n  do {\n    if (te32.th32OwnerProcessID == target_pid) {\n      // We set the thread priority of \"Thread1\" to a non-default value before\n      // going to sleep. Dump and blame this thread. For an explanation of \"9\",\n      // see https://msdn.microsoft.com/library/ms685100.aspx.\n      if (te32.tpBasePri == 9) {\n        ScopedKernelHANDLE thread(\n            OpenThread(kXPThreadAllAccess, false, te32.th32ThreadID));\n        if (!thread.is_valid()) {\n          PLOG(ERROR) << \"OpenThread\";\n          return false;\n        }\n        if (!CrashpadClient::DumpAndCrashTargetProcess(\n                process, thread.get(), kCrashAndDumpTargetExitCode)) {\n          return false;\n        }\n        return true;\n      }\n    }\n  } while (Thread32Next(thread_snap.get(), &te32));\n\n  LOG(ERROR) << \"target not found\";\n  return false;\n}\n\nint CrashOtherProgram(int argc, wchar_t* argv[]) {\n  CrashpadClient client;\n\n  if (argc == 2 || argc == 3) {\n    if (!client.SetHandlerIPCPipe(argv[1])) {\n      LOG(ERROR) << \"SetHandlerIPCPipe\";\n      return EXIT_FAILURE;\n    }\n  } else {\n    fprintf(stderr, \"Usage: %ls <server_pipe_name> [noexception]\\n\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  // Launch another process that hangs.\n  base::FilePath test_executable = TestPaths::Executable();\n  base::FilePath child_test_executable =\n      test_executable.DirName().Append(L\"hanging_program.exe\");\n  ChildLauncher child(child_test_executable, argv[1]);\n  child.Start();\n  if (testing::Test::HasFatalFailure()) {\n    LOG(ERROR) << \"failed to start child\";\n    return EXIT_FAILURE;\n  }\n\n  // Wait until it's ready.\n  char c;\n  if (!LoggingReadFileExactly(child.stdout_read_handle(), &c, sizeof(c)) ||\n      c != ' ') {\n    LOG(ERROR) << \"failed child communication\";\n    return EXIT_FAILURE;\n  }\n\n  DWORD expect_exit_code;\n  if (argc == 3 && wcscmp(argv[2], L\"noexception\") == 0) {\n    expect_exit_code = ExceptionCodes::kTriggeredExceptionCode;\n    if (!CrashpadClient::DumpAndCrashTargetProcess(\n            child.process_handle(), 0, 0))\n      return EXIT_FAILURE;\n  } else {\n    expect_exit_code = kCrashAndDumpTargetExitCode;\n    if (!CrashAndDumpTarget(child.process_handle())) {\n      return EXIT_FAILURE;\n    }\n  }\n\n  DWORD exit_code = child.WaitForExit();\n  if (exit_code != expect_exit_code) {\n    LOG(ERROR) << base::StringPrintf(\n        \"incorrect exit code, expected 0x%lx, observed 0x%lx\",\n        expect_exit_code,\n        exit_code);\n    return EXIT_FAILURE;\n  }\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::test::CrashOtherProgram(argc, argv);\n}\n"
  },
  {
    "path": "handler/win/crash_report_exception_handler.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler/win/crash_report_exception_handler.h\"\n\n#include <type_traits>\n#include <utility>\n\n#include \"base/strings/utf_string_conversions.h\"\n#include \"client/crash_report_database.h\"\n#include \"client/settings.h\"\n#include \"handler/crash_report_upload_thread.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/minidump_user_extension_stream_data_source.h\"\n#include \"snapshot/win/process_snapshot_win.h\"\n#include \"util/file/file_helper.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/win/registration_protocol_win.h\"\n#include \"util/win/scoped_process_suspend.h\"\n#include \"util/win/termination_codes.h\"\n\nnamespace crashpad {\n\nCrashReportExceptionHandler::CrashReportExceptionHandler(\n    CrashReportDatabase* database,\n    CrashReportUploadThread* upload_thread,\n    const std::map<std::string, std::string>* process_annotations,\n    const std::vector<base::FilePath>* attachments,\n    const UserStreamDataSources* user_stream_data_sources)\n    : database_(database),\n      upload_thread_(upload_thread),\n      process_annotations_(process_annotations),\n      attachments_(attachments),\n      user_stream_data_sources_(user_stream_data_sources) {}\n\nCrashReportExceptionHandler::~CrashReportExceptionHandler() {}\n\nvoid CrashReportExceptionHandler::ExceptionHandlerServerStarted() {}\n\nunsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(\n    HANDLE process,\n    WinVMAddress exception_information_address,\n    WinVMAddress debug_critical_section_address) {\n  Metrics::ExceptionEncountered();\n\n  ScopedProcessSuspend suspend(process);\n\n  ProcessSnapshotWin process_snapshot;\n  if (!process_snapshot.Initialize(process,\n                                   ProcessSuspensionState::kSuspended,\n                                   exception_information_address,\n                                   debug_critical_section_address)) {\n    Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);\n    return kTerminationCodeSnapshotFailed;\n  }\n\n  // Now that we have the exception information, even if something else fails we\n  // can terminate the process with the correct exit code.\n  const unsigned int termination_code =\n      process_snapshot.Exception()->Exception();\n  static_assert(\n      std::is_same<std::remove_const<decltype(termination_code)>::type,\n                   decltype(process_snapshot.Exception()->Exception())>::value,\n      \"expected ExceptionCode() and process termination code to match\");\n\n  Metrics::ExceptionCode(termination_code);\n\n  CrashpadInfoClientOptions client_options;\n  process_snapshot.GetCrashpadOptions(&client_options);\n  if (client_options.crashpad_handler_behavior != TriState::kDisabled) {\n    UUID client_id;\n    Settings* const settings = database_->GetSettings();\n    if (settings && settings->GetClientID(&client_id)) {\n      process_snapshot.SetClientID(client_id);\n    }\n\n    process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);\n\n    std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n    CrashReportDatabase::OperationStatus database_status =\n        database_->PrepareNewCrashReport(&new_report);\n    if (database_status != CrashReportDatabase::kNoError) {\n      LOG(ERROR) << \"PrepareNewCrashReport failed\";\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kPrepareNewCrashReportFailed);\n      return termination_code;\n    }\n\n    process_snapshot.SetReportID(new_report->ReportID());\n\n    MinidumpFileWriter minidump;\n    minidump.InitializeFromSnapshot(&process_snapshot);\n    AddUserExtensionStreams(\n        user_stream_data_sources_, &process_snapshot, &minidump);\n\n    if (!minidump.WriteEverything(new_report->Writer())) {\n      LOG(ERROR) << \"WriteEverything failed\";\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kMinidumpWriteFailed);\n      return termination_code;\n    }\n\n    for (const auto& attachment : (*attachments_)) {\n      FileReader file_reader;\n      if (!file_reader.Open(attachment)) {\n        LOG(ERROR) << \"attachment \" << attachment\n                   << \" couldn't be opened, skipping\";\n        continue;\n      }\n\n      base::FilePath filename = attachment.BaseName();\n      FileWriter* file_writer =\n          new_report->AddAttachment(base::WideToUTF8(filename.value()));\n      if (file_writer == nullptr) {\n        LOG(ERROR) << \"attachment \" << filename\n                   << \" couldn't be created, skipping\";\n        continue;\n      }\n\n      CopyFileContent(&file_reader, file_writer);\n    }\n\n    UUID uuid;\n    database_status =\n        database_->FinishedWritingCrashReport(std::move(new_report), &uuid);\n    if (database_status != CrashReportDatabase::kNoError) {\n      LOG(ERROR) << \"FinishedWritingCrashReport failed\";\n      Metrics::ExceptionCaptureResult(\n          Metrics::CaptureResult::kFinishedWritingCrashReportFailed);\n      return termination_code;\n    }\n\n    if (upload_thread_) {\n      upload_thread_->ReportPending(uuid);\n    }\n  }\n\n  Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);\n  return termination_code;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/win/crash_report_exception_handler.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_\n#define CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_\n\n#include <windows.h>\n\n#include <map>\n#include <string>\n\n#include \"handler/user_stream_data_source.h\"\n#include \"util/win/exception_handler_server.h\"\n\nnamespace crashpad {\n\nclass CrashReportDatabase;\nclass CrashReportUploadThread;\n\n//! \\brief An exception handler that writes crash reports for exception messages\n//!     to a CrashReportDatabase.\nclass CrashReportExceptionHandler final\n    : public ExceptionHandlerServer::Delegate {\n public:\n  //! \\brief Creates a new object that will store crash reports in \\a database.\n  //!\n  //! \\param[in] database The database to store crash reports in. Weak.\n  //! \\param[in] upload_thread The upload thread to notify when a new crash\n  //!     report is written into \\a database. Report upload is skipped if this\n  //!     value is `nullptr`.\n  //! \\param[in] process_annotations A map of annotations to insert as\n  //!     process-level annotations into each crash report that is written. Do\n  //!     not confuse this with module-level annotations, which are under the\n  //!     control of the crashing process, and are used to implement Chrome's\n  //!     \"crash keys.\" Process-level annotations are those that are beyond the\n  //!     control of the crashing process, which must reliably be set even if\n  //!     the process crashes before it's able to establish its own annotations.\n  //!     To interoperate with Breakpad servers, the recommended practice is to\n  //!     specify values for the `\"prod\"` and `\"ver\"` keys as process\n  //!     annotations.\n  //! \\param[in] attachments A vector of file paths that should be captured with\n  //!     each report at the time of the crash.\n  //! \\param[in] user_stream_data_sources Data sources to be used to extend\n  //!     crash reports. For each crash report that is written, the data sources\n  //!     are called in turn. These data sources may contribute additional\n  //!     minidump streams. `nullptr` if not required.\n  CrashReportExceptionHandler(\n      CrashReportDatabase* database,\n      CrashReportUploadThread* upload_thread,\n      const std::map<std::string, std::string>* process_annotations,\n      const std::vector<base::FilePath>* attachments,\n      const UserStreamDataSources* user_stream_data_sources);\n\n  CrashReportExceptionHandler(const CrashReportExceptionHandler&) = delete;\n  CrashReportExceptionHandler& operator=(const CrashReportExceptionHandler&) =\n      delete;\n\n  ~CrashReportExceptionHandler();\n\n  // ExceptionHandlerServer::Delegate:\n\n  //! \\brief Processes an exception message by writing a crash report to this\n  //!     object's CrashReportDatabase.\n  void ExceptionHandlerServerStarted() override;\n  unsigned int ExceptionHandlerServerException(\n      HANDLE process,\n      WinVMAddress exception_information_address,\n      WinVMAddress debug_critical_section_address) override;\n\n private:\n  CrashReportDatabase* database_;  // weak\n  CrashReportUploadThread* upload_thread_;  // weak\n  const std::map<std::string, std::string>* process_annotations_;  // weak\n  const std::vector<base::FilePath>* attachments_;  // weak\n  const UserStreamDataSources* user_stream_data_sources_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_\n"
  },
  {
    "path": "handler/win/crashy_signal.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <windows.h>\n\n#include \"base/logging.h\"\n#include \"client/crashpad_client.h\"\n\nnamespace crashpad {\nnamespace {\n\nenum WhereToSignalFrom {\n  kUnknown = -1,\n  kMain = 0,\n  kBackground = 1,\n};\n\nWhereToSignalFrom MainOrBackground(wchar_t* name) {\n  if (wcscmp(name, L\"main\") == 0)\n    return kMain;\n  if (wcscmp(name, L\"background\") == 0)\n    return kBackground;\n  return kUnknown;\n}\n\nDWORD WINAPI BackgroundThread(void* arg) {\n  abort();\n}\n\nint CrashySignalMain(int argc, wchar_t* argv[]) {\n  CrashpadClient client;\n\n  WhereToSignalFrom from;\n  if (argc == 3 && (from = MainOrBackground(argv[2])) != kUnknown) {\n    if (!client.SetHandlerIPCPipe(argv[1])) {\n      LOG(ERROR) << \"SetHandler\";\n      return EXIT_FAILURE;\n    }\n  } else {\n    fprintf(stderr, \"Usage: %ls <server_pipe_name> main|background\\n\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  // In debug builds part of abort() is to open a dialog. We don't want tests to\n  // block at that dialog, so disable it.\n  _set_abort_behavior(0, _WRITE_ABORT_MSG);\n\n  if (from == kBackground) {\n    HANDLE thread = CreateThread(nullptr,\n                                 0,\n                                 &BackgroundThread,\n                                 nullptr,\n                                 0,\n                                 nullptr);\n    if (!thread) {\n      PLOG(ERROR) << \"CreateThread\";\n      return EXIT_FAILURE;\n    }\n    if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) {\n      PLOG(ERROR) << \"WaitForSingleObject\";\n      return EXIT_FAILURE;\n    }\n  } else {\n    abort();\n  }\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::CrashySignalMain(argc, argv);\n}\n"
  },
  {
    "path": "handler/win/crashy_test_program.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <intrin.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <windows.h>\n#include <winternl.h>\n\n#include <iterator>\n#include <map>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#include \"base/check.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"client/crashpad_client.h\"\n#include \"client/crashpad_info.h\"\n#include \"util/win/critical_section_with_debug_info.h\"\n#include \"util/win/get_function.h\"\n\n// ntstatus.h conflicts with windows.h so define this locally.\n#ifndef STATUS_NO_SUCH_FILE\n#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F)\n#endif\n\nnamespace crashpad {\n\nuint32_t* g_extra_memory_pointer;\nuint32_t* g_extra_memory_not_saved;\n\nnamespace {\n\nCRITICAL_SECTION g_test_critical_section;\n\nunsigned char g_test_memory[] = {\n  99, 98, 97, 96, 95, 94, 93, 92, 91, 90,\n  89, 88, 87, 86, 85, 84, 83, 82, 81, 80,\n};\n\nULONG RtlNtStatusToDosError(NTSTATUS status) {\n  static const auto rtl_nt_status_to_dos_error =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::RtlNtStatusToDosError);\n  return rtl_nt_status_to_dos_error(status);\n}\n\nvoid AllocateMemoryOfVariousProtections() {\n  SYSTEM_INFO system_info;\n  GetSystemInfo(&system_info);\n\n  const size_t kPageSize = system_info.dwPageSize;\n\n  static constexpr uint32_t kPageTypes[] = {\n      PAGE_NOACCESS,\n      PAGE_READONLY,\n      PAGE_READWRITE,\n      PAGE_EXECUTE,\n      PAGE_EXECUTE_READ,\n      PAGE_EXECUTE_READWRITE,\n\n      // PAGE_NOACCESS is invalid with PAGE_GUARD.\n      PAGE_READONLY | PAGE_GUARD,\n      PAGE_READWRITE | PAGE_GUARD,\n      PAGE_EXECUTE | PAGE_GUARD,\n      PAGE_EXECUTE_READ | PAGE_GUARD,\n      PAGE_EXECUTE_READWRITE | PAGE_GUARD,\n  };\n\n  // All of these allocations are leaked, we want to view them in windbg via\n  // !vprot.\n  void* reserve = VirtualAlloc(\n      nullptr, std::size(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);\n  PCHECK(reserve) << \"VirtualAlloc MEM_RESERVE\";\n  uintptr_t reserve_as_int = reinterpret_cast<uintptr_t>(reserve);\n\n  for (size_t i = 0; i < std::size(kPageTypes); ++i) {\n    void* result =\n        VirtualAlloc(reinterpret_cast<void*>(reserve_as_int + (kPageSize * i)),\n                     kPageSize,\n                     MEM_COMMIT,\n                     kPageTypes[i]);\n    PCHECK(result) << \"VirtualAlloc MEM_COMMIT \" << i;\n  }\n}\n\nDWORD WINAPI NullThreadProc(void* param) {\n  return 0;\n}\n\n// Creates a suspended background thread, and sets EDI/RDI/X17 to point at\n// g_test_memory so we can confirm it's available in the minidump.\nbool CreateThreadWithRegisterPointingToTestMemory() {\n  HANDLE thread = CreateThread(\n      nullptr, 0, &NullThreadProc, nullptr, CREATE_SUSPENDED, nullptr);\n  if (!thread) {\n    PLOG(ERROR) << \"CreateThread\";\n    return false;\n  }\n\n  CONTEXT context = {0};\n  context.ContextFlags = CONTEXT_INTEGER;\n  if (!GetThreadContext(thread, &context)) {\n    PLOG(ERROR) << \"GetThreadContext\";\n    return false;\n  }\n#if defined(ARCH_CPU_X86_64)\n  context.Rdi = reinterpret_cast<DWORD64>(g_test_memory);\n#elif defined(ARCH_CPU_X86)\n  context.Edi = reinterpret_cast<DWORD>(g_test_memory);\n#elif defined(ARCH_CPU_ARM64)\n  context.X17 = reinterpret_cast<DWORD64>(g_test_memory);\n#endif\n  if (!SetThreadContext(thread, &context)) {\n    PLOG(ERROR) << \"SetThreadContext\";\n    return false;\n  }\n\n  return true;\n}\n\n__declspec(noinline) void SomeCrashyFunction() {\n  // SetLastError and NTSTATUS so that we have something to view in !gle in\n  // windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the\n  // LastStatusError of the TEB as a side-effect, and we'll be setting\n  // ERROR_FILE_NOT_FOUND for GetLastError().\n  SetLastError(RtlNtStatusToDosError(STATUS_NO_SUCH_FILE));\n\n  volatile int* foo = reinterpret_cast<volatile int*>(7);\n  *foo = 42;\n}\n\nvoid AllocateExtraMemoryToBeSaved(\n    crashpad::SimpleAddressRangeBag* extra_ranges) {\n  constexpr size_t kNumVals = 2000;\n  auto extra_memory = new uint32_t[kNumVals];\n  g_extra_memory_pointer = extra_memory;\n  for (size_t i = 0; i < kNumVals; ++i)\n    extra_memory[i] =\n        static_cast<std::remove_reference<decltype(extra_memory[0])>::type>(\n            i * 13 + 2);\n  extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals);\n  extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer));\n}\n\nvoid AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) {\n  // Allocate some extra memory, and then Insert() but also Remove() it so we\n  // can confirm it doesn't get saved.\n  constexpr size_t kNumVals = 2000;\n  auto extra_memory = new uint32_t[kNumVals];\n  g_extra_memory_not_saved = extra_memory;\n  for (size_t i = 0; i < kNumVals; ++i)\n    extra_memory[i] =\n        static_cast<std::remove_reference<decltype(extra_memory[0])>::type>(\n            i * 17 + 7);\n  extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals);\n  extra_ranges->Insert(&g_extra_memory_not_saved,\n                       sizeof(g_extra_memory_not_saved));\n\n  // We keep the pointer's memory, but remove the pointed-to memory.\n  extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumVals);\n}\n\nint CrashyMain(int argc, wchar_t* argv[]) {\n  CrashpadClient client;\n\n  if (argc == 2) {\n    if (!client.SetHandlerIPCPipe(argv[1])) {\n      LOG(ERROR) << \"SetHandler\";\n      return EXIT_FAILURE;\n    }\n  } else if (argc == 3) {\n    if (!client.StartHandler(base::FilePath(argv[1]),\n                             base::FilePath(argv[2]),\n                             base::FilePath(),\n                             std::string(),\n                             std::map<std::string, std::string>(),\n                             std::vector<std::string>(),\n                             false,\n                             true)) {\n      LOG(ERROR) << \"StartHandler\";\n      return EXIT_FAILURE;\n    }\n  } else {\n    fprintf(stderr, \"Usage: %ls <server_pipe_name>\\n\", argv[0]);\n    fprintf(stderr, \"       %ls <handler_path> <database_path>\\n\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  crashpad::SimpleAddressRangeBag* extra_ranges =\n      new crashpad::SimpleAddressRangeBag();\n  crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(\n      extra_ranges);\n  AllocateExtraMemoryToBeSaved(extra_ranges);\n  AllocateExtraUnsavedMemory(extra_ranges);\n\n  // Load and unload some uncommonly used modules so we can see them in the list\n  // reported by `lm`. At least two so that we confirm we got the element size\n  // advancement of RTL_UNLOAD_EVENT_TRACE correct.\n  CHECK(GetModuleHandle(L\"lz32.dll\") == nullptr);\n  CHECK(GetModuleHandle(L\"wmerror.dll\") == nullptr);\n  HMODULE lz32 = LoadLibrary(L\"lz32.dll\");\n  HMODULE wmerror = LoadLibrary(L\"wmerror.dll\");\n  FreeLibrary(lz32);\n  FreeLibrary(wmerror);\n\n  // Make sure data pointed to by the stack is captured.\n  constexpr int kDataSize = 512;\n  int* pointed_to_data = new int[kDataSize];\n  for (int i = 0; i < kDataSize; ++i)\n    pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0);\n  int* offset_pointer = &pointed_to_data[128];\n  // Encourage the compiler to keep this variable around.\n  printf(\"%p, %p\\n\", offset_pointer, &offset_pointer);\n\n  crashpad::CrashpadInfo::GetCrashpadInfo()\n      ->set_gather_indirectly_referenced_memory(\n          TriState::kEnabled, std::numeric_limits<uint32_t>::max());\n\n  std::vector<uint8_t> data_stream1(128, 'x');\n  crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(\n      222222,\n      reinterpret_cast<const void*>(data_stream1.data()),\n      data_stream1.size());\n\n  std::vector<uint8_t> data_stream2(4096, 'z');\n  crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(\n      333333,\n      reinterpret_cast<const void*>(data_stream2.data()),\n      data_stream2.size());\n\n  AllocateMemoryOfVariousProtections();\n\n  if (InitializeCriticalSectionWithDebugInfoIfPossible(\n          &g_test_critical_section)) {\n    EnterCriticalSection(&g_test_critical_section);\n  }\n\n  if (!CreateThreadWithRegisterPointingToTestMemory())\n    return EXIT_FAILURE;\n\n  SomeCrashyFunction();\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::CrashyMain(argc, argv);\n}\n"
  },
  {
    "path": "handler/win/crashy_test_z7_loader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdlib.h>\n#include <windows.h>\n\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"client/crashpad_client.h\"\n#include \"test/test_paths.h\"\n\n#if !defined(ARCH_CPU_X86)\n#error This test is only supported on x86.\n#endif  // !ARCH_CPU_X86\n\nnamespace crashpad {\nnamespace {\n\nint CrashyLoadZ7Main(int argc, wchar_t* argv[]) {\n  if (argc != 2) {\n    fprintf(stderr, \"Usage: %ls <server_pipe_name>\\n\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  CrashpadClient client;\n  if (!client.SetHandlerIPCPipe(argv[1])) {\n    LOG(ERROR) << \"SetHandler\";\n    return EXIT_FAILURE;\n  }\n\n  // The DLL has /Z7 symbols embedded in the binary (rather than in a .pdb).\n  // There's only an x86 version of this dll as newer x64 toolchains can't\n  // generate this format any more.\n  base::FilePath z7_path = test::TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"handler/win/z7_test.dll\"));\n  HMODULE z7_test = LoadLibrary(z7_path.value().c_str());\n  if (!z7_test) {\n    PLOG(ERROR) << \"LoadLibrary\";\n    return EXIT_FAILURE;\n  }\n  FARPROC crash_me = GetProcAddress(z7_test, \"CrashMe\");\n  if (!crash_me) {\n    PLOG(ERROR) << \"GetProcAddress\";\n    return EXIT_FAILURE;\n  }\n  reinterpret_cast<void(*)()>(crash_me)();\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::CrashyLoadZ7Main(argc, argv);\n}\n"
  },
  {
    "path": "handler/win/fake_handler_that_crashes_at_startup.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This is used to test a crashpad_handler that launches successfully, but then\n// crashes before setting up.\nint wmain() {\n  __debugbreak();\n  return 0;\n}\n"
  },
  {
    "path": "handler/win/fastfail_test_program.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <string.h>\n\n#include \"base/check.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"client/crashpad_client.h\"\n#include \"util/misc/paths.h\"\n\n#include <Windows.h>\n\n// We set up a program that crashes with a CFG exception so must be built and\n// linked with /guard:cf. We register the crashpad runtime exception helper\n// module to intercept and trigger the crashpad handler. Note that Windows only\n// loads the module in WerFault after the crash for Windows 10 >= 20h1 (19041).\nnamespace crashpad {\nnamespace {\n\n// This function should not be on our stack as CFG prevents the modified\n// icall from happening.\nint CallRffeManyTimes() {\n  RaiseFailFastException(nullptr, nullptr, 0);\n  RaiseFailFastException(nullptr, nullptr, 0);\n  RaiseFailFastException(nullptr, nullptr, 0);\n  RaiseFailFastException(nullptr, nullptr, 0);\n  return 1;\n}\n\nusing FuncType = decltype(&CallRffeManyTimes);\nvoid IndirectCall(FuncType* func) {\n  // This code always generates CFG guards.\n  (*func)();\n}\n\nvoid CfgCrash() {\n  // Call into the middle of the Crashy function.\n  FuncType func = reinterpret_cast<FuncType>(\n      (reinterpret_cast<uintptr_t>(CallRffeManyTimes)) + 16);\n  __try {\n    // Generates a STATUS_STACK_BUFFER_OVERRUN exception if CFG triggers.\n    IndirectCall(&func);\n  } __except (EXCEPTION_EXECUTE_HANDLER) {\n    // CFG fast fail should never be caught.\n    NOTREACHED();\n  }\n  // Should only reach here if CFG is disabled.\n  abort();\n}\n\nvoid FastFailCrash() {\n  __fastfail(77);\n}\n\nint CrashyMain(int argc, wchar_t* argv[]) {\n  static CrashpadClient* client = new crashpad::CrashpadClient();\n\n  std::wstring type;\n  if (argc == 3) {\n    type = argv[2];\n    // We call this from end_to_end_test.py.\n    if (!client->SetHandlerIPCPipe(argv[1])) {\n      LOG(ERROR) << \"SetHandler\";\n      return EXIT_FAILURE;\n    }\n  } else if (argc == 4) {\n    type = argv[3];\n    // This is helpful for debugging.\n    if (!client->StartHandler(base::FilePath(argv[1]),\n                              base::FilePath(argv[2]),\n                              base::FilePath(),\n                              std::string(),\n                              std::map<std::string, std::string>(),\n                              std::vector<std::string>(),\n                              false,\n                              true)) {\n      LOG(ERROR) << \"StartHandler\";\n      return EXIT_FAILURE;\n    }\n    // Got to have a handler & registration.\n    if (!client->WaitForHandlerStart(10000)) {\n      LOG(ERROR) << \"Handler failed to start\";\n      return EXIT_FAILURE;\n    }\n  } else {\n    fprintf(stderr, \"Usage: %ls <server_pipe_name> [cf|ff]\\n\", argv[0]);\n    fprintf(\n        stderr, \"       %ls <handler_path> <database_path> [cf|ff]\\n\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  base::FilePath exe_path;\n  if (!Paths::Executable(&exe_path)) {\n    LOG(ERROR) << \"Failed getting path\";\n    return EXIT_FAILURE;\n  }\n\n  // Find the module.\n  auto mod_path = exe_path.DirName().Append(L\"crashpad_wer.dll\");\n\n  // Make sure it is registered in the registry.\n  DWORD dwOne = 1;\n  LSTATUS reg_res =\n      RegSetKeyValueW(HKEY_CURRENT_USER,\n                      L\"Software\\\\Microsoft\\\\Windows\\\\Windows Error Reporting\\\\\"\n                      L\"RuntimeExceptionHelperModules\",\n                      mod_path.value().c_str(),\n                      REG_DWORD,\n                      &dwOne,\n                      sizeof(DWORD));\n  if (reg_res != ERROR_SUCCESS) {\n    LOG(ERROR) << \"RegSetKeyValueW\";\n    return EXIT_FAILURE;\n  }\n\n  if (!client->RegisterWerModule(mod_path.value())) {\n    LOG(ERROR) << \"WerRegisterRuntimeExceptionModule\";\n    return EXIT_FAILURE;\n  }\n\n  // Some versions of python call SetErrorMode() which extends to children, and\n  // prevents the WerFault infrastructure from running.\n  SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);\n\n  if (type == L\"cf\")\n    CfgCrash();\n  if (type == L\"ff\")\n    FastFailCrash();\n\n  LOG(ERROR) << \"Invalid type or exception failed.\";\n  return EXIT_FAILURE;\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::CrashyMain(argc, argv);\n}\n"
  },
  {
    "path": "handler/win/hanging_program.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdio.h>\n#include <windows.h>\n\n#include <iterator>\n\n#include \"base/check.h\"\n#include \"base/debug/alias.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"client/crashpad_client.h\"\n#include \"client/crashpad_info.h\"\n\nnamespace {\n\n[[noreturn]] DWORD WINAPI Thread1(LPVOID context) {\n  HANDLE event = context;\n\n  // Increase the thread priority as a hacky way to signal to\n  // crash_other_program.exe that this is the thread to dump.\n  PCHECK(SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL));\n\n  // Let the main thread proceed.\n  PCHECK(SetEvent(event));\n\n  Sleep(INFINITE);\n\n  NOTREACHED();\n}\n\n[[noreturn]] DWORD WINAPI Thread2(LPVOID dummy) {\n  Sleep(INFINITE);\n  NOTREACHED();\n}\n\n[[noreturn]] DWORD WINAPI Thread3(LPVOID context) {\n  // This is a convenient way to pass the event handle to loader_lock_dll.dll.\n  HANDLE event = context;\n  PCHECK(SetEnvironmentVariable(\n      L\"CRASHPAD_TEST_DLL_EVENT\",\n      base::UTF8ToWide(base::StringPrintf(\"%p\", event)).c_str()));\n\n  HMODULE dll = LoadLibrary(L\"loader_lock_dll.dll\");\n  PCHECK(dll) << \"LoadLibrary\";\n\n  // This call is not expected to return.\n  PCHECK(FreeLibrary(dll));\n\n  NOTREACHED();\n}\n\n}  // namespace\n\nint wmain(int argc, wchar_t* argv[]) {\n  crashpad::CrashpadClient client;\n\n  if (argc == 2) {\n    if (!client.SetHandlerIPCPipe(argv[1])) {\n      LOG(ERROR) << \"SetHandlerIPCPipe\";\n      return EXIT_FAILURE;\n    }\n  } else {\n    fprintf(stderr, \"Usage: %ls <server_pipe_name>\\n\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  // Make sure this module has a CrashpadInfo structure.\n  crashpad::CrashpadInfo* crashpad_info =\n      crashpad::CrashpadInfo::GetCrashpadInfo();\n  base::debug::Alias(crashpad_info);\n\n  HANDLE event = CreateEvent(nullptr,\n                             false,  // bManualReset\n                             false,\n                             nullptr);\n  if (!event) {\n    PLOG(ERROR) << \"CreateEvent\";\n    return EXIT_FAILURE;\n  }\n\n  HANDLE threads[3];\n  threads[0] = CreateThread(nullptr, 0, Thread1, event, 0, nullptr);\n\n  threads[1] = CreateThread(nullptr, 0, Thread2, nullptr, 0, nullptr);\n\n  // Wait for Thread1() to complete its work and reach its Sleep() before\n  // starting the next thread, which will hold the loader lock and potentially\n  // block any further progress.\n  if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) {\n    PLOG(ERROR) << \"WaitForSingleObject\";\n    return EXIT_FAILURE;\n  }\n\n  // Use the same event object, which was automatically reset.\n  threads[2] = CreateThread(nullptr, 0, Thread3, event, 0, nullptr);\n\n  // Wait for loader_lock_dll.dll to signal that the loader lock is held and\n  // won’t be released.\n  if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) {\n    PLOG(ERROR) << \"WaitForSingleObject\";\n    return EXIT_FAILURE;\n  }\n\n  // Signal to the parent that everything is ready.\n  fprintf(stdout, \" \");\n  fflush(stdout);\n\n  // This is not expected to return.\n  DWORD count = WaitForMultipleObjects(\n      static_cast<DWORD>(std::size(threads)), threads, true, INFINITE);\n  if (count == WAIT_FAILED) {\n    PLOG(ERROR) << \"WaitForMultipleObjects\";\n  } else {\n    LOG(ERROR) << \"WaitForMultipleObjects: \" << count;\n  }\n\n  return EXIT_FAILURE;\n}\n"
  },
  {
    "path": "handler/win/heap_corrupting_program.cc",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <string.h>\n\n#include \"base/check.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"client/crashpad_client.h\"\n#include \"util/misc/paths.h\"\n\n#include <Windows.h>\n\n// We set up a program that crashes with a heap corruption exception.\n// STATUS_HEAP_CORRUPTION (0xC0000374 3221226356).\nnamespace crashpad {\nnamespace {\n\nvoid HeapCorruptionCrash() {\n  __try {\n    HANDLE heap = ::HeapCreate(0, 0, 0);\n    CHECK(heap);\n    CHECK(HeapSetInformation(\n        heap, HeapEnableTerminationOnCorruption, nullptr, 0));\n    void* addr = ::HeapAlloc(heap, 0, 0x1000);\n    CHECK(addr);\n    // Corrupt heap header.\n    char* addr_mutable = reinterpret_cast<char*>(addr);\n    memset(addr_mutable - sizeof(addr), 0xCC, sizeof(addr));\n\n    HeapFree(heap, 0, addr);\n    HeapDestroy(heap);\n  } __except (EXCEPTION_EXECUTE_HANDLER) {\n    // Heap corruption exception should never be caught.\n    NOTREACHED();\n  }\n  // Should never reach here.\n  abort();\n}\n\nint CrashyMain(int argc, wchar_t* argv[]) {\n  static CrashpadClient* client = new crashpad::CrashpadClient();\n\n  if (argc == 2) {\n    // We call this from end_to_end_test.py.\n    if (!client->SetHandlerIPCPipe(argv[1])) {\n      LOG(ERROR) << \"SetHandler\";\n      return EXIT_FAILURE;\n    }\n  } else if (argc == 3) {\n    // This is helpful for debugging.\n    if (!client->StartHandler(base::FilePath(argv[1]),\n                              base::FilePath(argv[2]),\n                              base::FilePath(),\n                              std::string(),\n                              std::map<std::string, std::string>(),\n                              std::vector<std::string>(),\n                              false,\n                              true)) {\n      LOG(ERROR) << \"StartHandler\";\n      return EXIT_FAILURE;\n    }\n    // Got to have a handler & registration.\n    if (!client->WaitForHandlerStart(10000)) {\n      LOG(ERROR) << \"Handler failed to start\";\n      return EXIT_FAILURE;\n    }\n  } else {\n    fprintf(stderr, \"Usage: %ls <server_pipe_name>\\n\", argv[0]);\n    fprintf(stderr, \"       %ls <handler_path> <database_path>\\n\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  HeapCorruptionCrash();\n\n  LOG(ERROR) << \"Invalid type or exception failed.\";\n  return EXIT_FAILURE;\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::CrashyMain(argc, argv);\n}\n"
  },
  {
    "path": "handler/win/loader_lock_dll.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdarg.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <windows.h>\n\nnamespace {\n\n// This custom logging function is to avoid taking a dependency on base in this\n// standalone DLL. Don’t call this directly, use the LOG_FATAL() and\n// PLOG_FATAL() macros below.\n__declspec(noreturn) void LogFatal(const char* file,\n                                   int line,\n                                   bool get_last_error,\n                                   const char* format,\n                                   ...) {\n  DWORD last_error = ERROR_SUCCESS;\n  if (get_last_error) {\n    last_error = GetLastError();\n  }\n\n  char fname[_MAX_FNAME];\n  char ext[_MAX_EXT];\n  if (_splitpath_s(file,\n                   nullptr,\n                   0,\n                   nullptr,\n                   0,\n                   fname,\n                   sizeof(fname),\n                   ext,\n                   sizeof(ext)) == 0) {\n    fprintf(stderr, \"%s%s\", fname, ext);\n  } else {\n    fputs(file, stderr);\n  }\n  fprintf(stderr, \":%d: \", line);\n\n  va_list va;\n  va_start(va, format);\n  vfprintf(stderr, format, va);\n  va_end(va);\n\n  if (get_last_error) {\n    fprintf(stderr, \": error %lu\", last_error);\n  }\n\n  fputs(\"\\n\", stderr);\n  fflush(stderr);\n\n  TerminateProcess(GetCurrentProcess(), 1);\n  __fastfail(FAST_FAIL_FATAL_APP_EXIT);\n}\n\n#define LOG_FATAL(...)                                \\\n  do {                                                \\\n    LogFatal(__FILE__, __LINE__, false, __VA_ARGS__); \\\n  } while (false)\n#define PLOG_FATAL(...)                              \\\n  do {                                               \\\n    LogFatal(__FILE__, __LINE__, true, __VA_ARGS__); \\\n  } while (false)\n\n}  // namespace\n\n// This program intentionally blocks in DllMain which is executed with the\n// loader lock locked. This allows us to test that\n// CrashpadClient::DumpAndCrashTargetProcess() can still dump the target in this\n// case.\nBOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) {\n  switch (reason) {\n    case DLL_PROCESS_DETACH:\n    case DLL_THREAD_DETACH: {\n      // Recover the event handle stashed by the main executable.\n      static constexpr size_t kEventStringSize = 19;\n      wchar_t event_string[kEventStringSize];\n      SetLastError(ERROR_SUCCESS);\n      DWORD size = GetEnvironmentVariable(\n          L\"CRASHPAD_TEST_DLL_EVENT\", event_string, kEventStringSize);\n      if (size == 0 && GetLastError() != ERROR_SUCCESS) {\n        PLOG_FATAL(\"GetEnvironmentVariable\");\n      }\n      if (size == 0 || size >= kEventStringSize) {\n        LOG_FATAL(\"GetEnvironmentVariable: size %u\", size);\n      }\n\n      HANDLE event;\n      int converted = swscanf(event_string, L\"%p\", &event);\n      if (converted != 1) {\n        LOG_FATAL(\"swscanf: converted %d\", converted);\n      }\n\n      // Let the main thread know that the loader lock is and will remain held.\n      if (!SetEvent(event)) {\n        PLOG_FATAL(\"SetEvent\");\n      }\n\n      Sleep(INFINITE);\n\n      break;\n    }\n  }\n\n  return TRUE;\n}\n"
  },
  {
    "path": "handler/win/self_destroying_test_program.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <malloc.h>\n#include <stdlib.h>\n#include <windows.h>\n#include <winternl.h>\n\n#include \"base/logging.h\"\n#include \"client/crashpad_client.h\"\n#include \"snapshot/win/process_reader_win.h\"\n\nnamespace crashpad {\nnamespace {\n\n// Without this, clang optimizes away the _alloca below, which in turn\n// makes the VirtualFree() crash with an access violation.\n#if defined(__clang__)\n#pragma clang optimize off\n#endif\n\n// We VirtualFree a region in ourselves (the stack) to confirm that the\n// exception reporter captures as much as possible in the minidump and doesn't\n// abort. __debugbreak() immediately after doing so because the process is\n// clearly in a very broken state at this point.\n__declspec(noinline) bool FreeOwnStackAndBreak() {\n  ProcessReaderWin process_reader;\n  if (!process_reader.Initialize(GetCurrentProcess(),\n                                 ProcessSuspensionState::kRunning)) {\n    LOG(ERROR) << \"ProcessReaderWin Initialize\";\n    return false;\n  }\n\n  const std::vector<ProcessReaderWin::Thread> threads =\n      process_reader.Threads();\n  if (threads.empty()) {\n    LOG(ERROR) << \"no threads\";\n    return false;\n  }\n\n  // Push the stack up a bit so that hopefully the crash handler can succeed,\n  // but won't be able to read the base of the stack.\n  [[maybe_unused]] volatile void* do_not_optimize_away = _alloca(16384);\n\n  // We can't succeed at MEM_RELEASEing this memory, but MEM_DECOMMIT is good\n  // enough to make it inaccessible.\n  if (!VirtualFree(reinterpret_cast<void*>(threads[0].stack_region_address),\n                   100,\n                   MEM_DECOMMIT)) {\n    PLOG(ERROR) << \"VirtualFree\";\n    return false;\n  }\n\n  // If the VirtualFree() succeeds, we may have already crashed. __debugbreak()\n  // just to be sure.\n  __debugbreak();\n\n  return true;\n}\n\n__declspec(noinline) int SelfDestroyingMain(int argc, wchar_t* argv[]) {\n  if (argc != 2) {\n    fprintf(stderr, \"Usage: %ls <server_pipe_name>\\n\", argv[0]);\n    return EXIT_FAILURE;\n  }\n\n  CrashpadClient client;\n  if (!client.SetHandlerIPCPipe(argv[1])) {\n    LOG(ERROR) << \"SetHandler\";\n    return EXIT_FAILURE;\n  }\n\n  if (!FreeOwnStackAndBreak())\n    return EXIT_FAILURE;\n\n  // This will never be reached. On success, we'll have crashed above, or\n  // otherwise returned before here.\n  return EXIT_SUCCESS;\n}\n\n#if defined(__clang__)\n#pragma clang optimize on\n#endif\n\n}  // namespace\n}  // namespace crashpad\n\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::SelfDestroyingMain(argc, argv);\n}\n"
  },
  {
    "path": "handler/win/wer/BUILD.gn",
    "content": "# Copyright 2022 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../../build/crashpad_buildconfig.gni\")\n\nassert(crashpad_is_win)\n\n# Allows other projects (e.g. Chrome) to wrap this as a dll.\nsource_set(\"crashpad_wer_handler\") {\n  sources = [\n    \"crashpad_wer.cc\",\n    \"crashpad_wer.h\",\n  ]\n  deps = [ \"../../../util:util_registration_protocol\" ]\n}\n\ncrashpad_loadable_module(\"crashpad_wer\") {\n  sources = [\n    \"crashpad_wer.def\",\n    \"crashpad_wer_main.cc\",\n  ]\n  deps = [ \":crashpad_wer_handler\" ]\n  configs = [ \"../../../:crashpad_config\" ]\n}\n\nsource_set(\"crashpad_wer_test\") {\n  testonly = true\n  sources = [ \"crashpad_wer_module_unittest.cc\" ]\n  deps = [\n    \":crashpad_wer\",\n    \":crashpad_wer_handler\",\n    \"../../../client\",\n    \"../../../test\",\n    \"../../../third_party/googletest\",\n    \"../../../util:util_registration_protocol\",\n  ]\n}\n"
  },
  {
    "path": "handler/win/wer/crashpad_wer.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// See:\n// https://docs.microsoft.com/en-us/windows/win32/api/werapi/nf-werapi-werregisterruntimeexceptionmodule\n\n#include \"handler/win/wer/crashpad_wer.h\"\n\n#include \"util/misc/address_types.h\"\n#include \"util/win/registration_protocol_win_structs.h\"\n\n#include <stddef.h>\n#include <Windows.h>\n#include <werapi.h>\n\nnamespace crashpad::wer {\nnamespace {\nusing crashpad::WerRegistration;\n\n// bIsFatal and dwReserved fields are not present in SDK < 19041.\nstruct WER_RUNTIME_EXCEPTION_INFORMATION_19041 {\n  DWORD dwSize;\n  HANDLE hProcess;\n  HANDLE hThread;\n  EXCEPTION_RECORD exceptionRecord;\n  CONTEXT context;\n  PCWSTR pwszReportId;\n  BOOL bIsFatal;\n  DWORD dwReserved;\n};\n\n// We have our own version of this to avoid pulling in //base.\nclass ScopedHandle {\n public:\n  ScopedHandle() : handle_(INVALID_HANDLE_VALUE) {}\n  ScopedHandle(HANDLE from) : handle_(from) {}\n  ~ScopedHandle() {\n    if (IsValid())\n      CloseHandle(handle_);\n  }\n  bool IsValid() {\n    if (handle_ == INVALID_HANDLE_VALUE || handle_ == 0)\n      return false;\n    return true;\n  }\n  HANDLE Get() { return handle_; }\n\n private:\n  HANDLE handle_;\n};\n\nScopedHandle DuplicateFromTarget(HANDLE target_process, HANDLE target_handle) {\n  HANDLE hTmp;\n  if (!DuplicateHandle(target_process,\n                       target_handle,\n                       GetCurrentProcess(),\n                       &hTmp,\n                       SYNCHRONIZE | EVENT_MODIFY_STATE,\n                       false,\n                       0)) {\n    return ScopedHandle();\n  }\n  return ScopedHandle(hTmp);\n}\n\nbool ProcessException(const DWORD* handled_exceptions,\n                      size_t num_handled_exceptions,\n                      const PVOID pContext,\n                      const PWER_RUNTIME_EXCEPTION_INFORMATION e_info) {\n  // Need to have been given a context.\n  if (!pContext)\n    return false;\n\n  // Older OSes might provide a smaller structure than SDK 19041 defines.\n  if (e_info->dwSize <=\n      offsetof(WER_RUNTIME_EXCEPTION_INFORMATION_19041, bIsFatal)) {\n    return false;\n  }\n\n  // If building with SDK < 19041 then the bIsFatal field isn't defined, so\n  // use our internal definition here.\n  if (!reinterpret_cast<const WER_RUNTIME_EXCEPTION_INFORMATION_19041*>(e_info)\n           ->bIsFatal) {\n    return false;\n  }\n\n  // Only deal with exceptions that crashpad would not have handled.\n  bool found = false;\n  for (size_t i = 0; i < num_handled_exceptions; i++) {\n    if (handled_exceptions[i] == e_info->exceptionRecord.ExceptionCode) {\n      found = true;\n      break;\n    }\n  }\n  // If num_handled_exceptions == 0, all exceptions should be passed on.\n  if (!found && num_handled_exceptions != 0)\n    return false;\n\n  // Grab out the handles to the crashpad server.\n  WerRegistration target_registration = {};\n  if (!ReadProcessMemory(e_info->hProcess,\n                         pContext,\n                         &target_registration,\n                         sizeof(target_registration),\n                         nullptr)) {\n    return false;\n  }\n\n  // Validate version of registration struct.\n  if (target_registration.version != WerRegistration::kWerRegistrationVersion)\n    return false;\n\n  // Dupe handles for triggering the dump.\n  auto dump_start = DuplicateFromTarget(\n      e_info->hProcess, target_registration.dump_without_crashing);\n  auto dump_done =\n      DuplicateFromTarget(e_info->hProcess, target_registration.dump_completed);\n\n  if (!dump_start.IsValid() || !dump_done.IsValid())\n    return false;\n\n  // It's possible that the target crashed while inside a DumpWithoutCrashing\n  // call - either in the DumpWithoutCrashing call or in another thread - if so\n  // we cannot trigger the dump until the first call's crash is dealth with as\n  // the crashpad handler might be reading from structures we will write to. We\n  // give the event a short while to be triggered and give up if it is not\n  // signalled.\n  if (target_registration.in_dump_without_crashing) {\n    constexpr DWORD kOneSecondInMs = 1000;\n    DWORD wait_result = WaitForSingleObject(dump_done.Get(), kOneSecondInMs);\n    if (wait_result != WAIT_OBJECT_0)\n      return false;\n  }\n\n  // Set up the crashpad handler's info structure.\n  crashpad::ExceptionInformation target_non_crash_exception_info{};\n  target_non_crash_exception_info.thread_id = GetThreadId(e_info->hThread);\n  target_non_crash_exception_info.exception_pointers =\n      static_cast<crashpad::VMAddress>(reinterpret_cast<uintptr_t>(pContext)) +\n      offsetof(WerRegistration, pointers);\n\n  if (!WriteProcessMemory(e_info->hProcess,\n                          target_registration.crashpad_exception_info,\n                          &target_non_crash_exception_info,\n                          sizeof(target_non_crash_exception_info),\n                          nullptr)) {\n    return false;\n  }\n\n  // Write Exception & Context to the areas reserved by the client.\n  if (!WriteProcessMemory(\n          e_info->hProcess,\n          reinterpret_cast<PVOID>(target_registration.pointers.ExceptionRecord),\n          &e_info->exceptionRecord,\n          sizeof(e_info->exceptionRecord),\n          nullptr)) {\n    return false;\n  }\n  if (!WriteProcessMemory(\n          e_info->hProcess,\n          reinterpret_cast<PVOID>(target_registration.pointers.ContextRecord),\n          &e_info->context,\n          sizeof(e_info->context),\n          nullptr)) {\n    return false;\n  }\n\n  // Request dump.\n  if (!SetEvent(dump_start.Get()))\n    return false;\n\n  constexpr DWORD kTenSecondsInMs = 10 * 1000;\n  DWORD result = WaitForSingleObject(dump_done.Get(), kTenSecondsInMs);\n\n  if (result == WAIT_OBJECT_0) {\n    // The handler signalled that it has written a dump, so we can terminate the\n    // target - this takes over from WER, sorry WER.\n    TerminateProcess(e_info->hProcess, e_info->exceptionRecord.ExceptionCode);\n    return true;\n  }\n  // Maybe some other handler can have a go.\n  return false;\n}\n}  // namespace\n\nbool ExceptionEvent(\n    const DWORD* handled_exceptions,\n    size_t num_handled_exceptions,\n    const PVOID pContext,\n    const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation) {\n  return ProcessException(handled_exceptions,\n                          num_handled_exceptions,\n                          pContext,\n                          pExceptionInformation);\n}\n\n}  // namespace crashpad::wer\n"
  },
  {
    "path": "handler/win/wer/crashpad_wer.def",
    "content": "; Copyright 2022 The Crashpad Authors\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\nLIBRARY  \"crashpad_wer.dll\"\n\nEXPORTS\n  ; These are required for the WER api.\n  OutOfProcessExceptionEventCallback\n  OutOfProcessExceptionEventSignatureCallback\n  OutOfProcessExceptionEventDebuggerLaunchCallback\n"
  },
  {
    "path": "handler/win/wer/crashpad_wer.h",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_HANDLER_WIN_WER_CRASHPAD_WER_H_\n#define CRASHPAD_HANDLER_WIN_WER_CRASHPAD_WER_H_\n\n#include <Windows.h>\n#include <werapi.h>\n\nnamespace crashpad::wer {\n//! \\brief Embedder calls this from OutOfProcessExceptionEventCallback().\n//!\n//! In the embedder's WER runtime exception helper, call this during\n//! OutOfProcessExceptionEventCallback().\n//!\n//! \\param[in] handled_exceptions is an array of exception codes that the helper\n//!     should pass on to crashpad handler (if possible). Pass nullptr and set\n//!     num_handled_exceptions to 0 to pass every exception on to the crashpad\n//!     handler.\n//! \\param[in] num_handled_exceptions is the number of elements in the array\n//!     passed to handled_exceptions.\n//! \\param[in] pContext is the context provided by WerFault to the helper.\n//! \\param[in] pExceptionInformation is the exception information provided by\n//!     WerFault to the helper DLL.\n//!\n//! \\return `true` if the target process was dumped by the crashpad handler then\n//! terminated, or `false` otherwise.\nbool ExceptionEvent(\n    const DWORD* handled_exceptions,\n    size_t num_handled_exceptions,\n    const PVOID pContext,\n    const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation);\n\n}  // namespace crashpad::wer\n\n#endif  // CRASHPAD_HANDLER_WIN_WER_CRASHPAD_WER_H_\n"
  },
  {
    "path": "handler/win/wer/crashpad_wer.ver",
    "content": "INTERNAL_NAME=crashpad_wer\nORIGINAL_FILENAME=crashpad_wer.dll\n"
  },
  {
    "path": "handler/win/wer/crashpad_wer_main.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// See:\n// https://docs.microsoft.com/en-us/windows/win32/api/werapi/nf-werapi-werregisterruntimeexceptionmodule\n\n#include \"handler/win/wer/crashpad_wer.h\"\n\n#include <Windows.h>\n#include <werapi.h>\n\n// Functions that will be exported from the DLL.\nextern \"C\" {\nBOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {\n  return true;\n}\n\n// PFN_WER_RUNTIME_EXCEPTION_EVENT\n// pContext is the address of a crashpad::internal::WerRegistration in the\n// target process.\nHRESULT OutOfProcessExceptionEventCallback(\n    PVOID pContext,\n    const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,\n    BOOL* pbOwnershipClaimed,\n    PWSTR pwszEventName,\n    PDWORD pchSize,\n    PDWORD pdwSignatureCount) {\n  static constexpr DWORD wanted_exceptions[] = {\n      0xC0000602,  // STATUS_FAIL_FAST_EXCEPTION\n      0xC0000409,  // STATUS_STACK_BUFFER_OVERRUN\n  };\n  // Default to not-claiming as bailing out is easier.\n  *pbOwnershipClaimed = FALSE;\n  bool result = crashpad::wer::ExceptionEvent(\n      wanted_exceptions,\n      sizeof(wanted_exceptions) / sizeof(wanted_exceptions[0]),\n      pContext,\n      pExceptionInformation);\n\n  if (result) {\n    *pbOwnershipClaimed = TRUE;\n    // Technically we failed as we terminated the process.\n    return E_FAIL;\n  }\n  // Pass.\n  return S_OK;\n}\n\n// PFN_WER_RUNTIME_EXCEPTION_EVENT_SIGNATURE\nHRESULT OutOfProcessExceptionEventSignatureCallback(\n    PVOID pContext,\n    const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,\n    DWORD dwIndex,\n    PWSTR pwszName,\n    PDWORD pchName,\n    PWSTR pwszValue,\n    PDWORD pchValue) {\n  // We handle everything in the call to OutOfProcessExceptionEventCallback.\n  // This function should never be called.\n  return E_FAIL;\n}\n\n// PFN_WER_RUNTIME_EXCEPTION_DEBUGGER_LAUNCH\nHRESULT OutOfProcessExceptionEventDebuggerLaunchCallback(\n    PVOID pContext,\n    const PWER_RUNTIME_EXCEPTION_INFORMATION pExceptionInformation,\n    PBOOL pbIsCustomDebugger,\n    PWSTR pwszDebuggerLaunch,\n    PDWORD pchDebuggerLaunch,\n    PBOOL pbIsDebuggerAutolaunch) {\n  // We handle everything in the call to OutOfProcessExceptionEventCallback.\n  // This function should never be called.\n  return E_FAIL;\n}\n}  // extern \"C\"\n"
  },
  {
    "path": "handler/win/wer/crashpad_wer_module_unittest.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"client/crashpad_client.h\"\n\n#include \"base/files/file_path.h\"\n#include \"gtest/gtest.h\"\n#include \"test/test_paths.h\"\n#include \"util/win/registration_protocol_win.h\"\n\n#include <Windows.h>\n#include <werapi.h>\n\nnamespace crashpad {\nnamespace test {\nnamespace {\nbase::FilePath ModulePath() {\n  auto dir = TestPaths::Executable().DirName();\n  return dir.Append(FILE_PATH_LITERAL(\"crashpad_wer.dll\"));\n}\n\n// Quick sanity check of the module, can't really test dumping etc. outside of\n// WerFault.exe loading it.\nTEST(CrashpadWerModule, Basic) {\n  HRESULT res = 0;\n  // Module loads.\n  HMODULE hMod = LoadLibraryW(ModulePath().value().c_str());\n  ASSERT_TRUE(hMod);\n\n  // Required functions exist.\n  auto wref = reinterpret_cast<PFN_WER_RUNTIME_EXCEPTION_EVENT>(\n      GetProcAddress(hMod, WER_RUNTIME_EXCEPTION_EVENT_FUNCTION));\n  ASSERT_TRUE(wref);\n  auto wrees = reinterpret_cast<PFN_WER_RUNTIME_EXCEPTION_EVENT_SIGNATURE>(\n      GetProcAddress(hMod, WER_RUNTIME_EXCEPTION_EVENT_SIGNATURE_FUNCTION));\n  ASSERT_TRUE(wrees);\n  auto wredl = reinterpret_cast<PFN_WER_RUNTIME_EXCEPTION_DEBUGGER_LAUNCH>(\n      GetProcAddress(hMod, WER_RUNTIME_EXCEPTION_DEBUGGER_LAUNCH));\n  ASSERT_TRUE(wredl);\n\n  // Not-implemented functions return E_FAIL as expected.\n  res = wrees(nullptr, nullptr, 0, nullptr, nullptr, nullptr, nullptr);\n  ASSERT_EQ(res, E_FAIL);\n  res = wredl(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);\n  ASSERT_EQ(res, E_FAIL);\n\n  // Dummy args for OutOfProcessExceptionEventCallback.\n  WER_RUNTIME_EXCEPTION_INFORMATION wer_ex;\n  wer_ex.dwSize = sizeof(WER_RUNTIME_EXCEPTION_INFORMATION);\n  BOOL bClaimed = FALSE;\n\n  // No context => skip.\n  res = wref(nullptr, &wer_ex, &bClaimed, nullptr, nullptr, nullptr);\n  ASSERT_EQ(res, S_OK);\n  ASSERT_EQ(bClaimed, FALSE);\n\n  // Following tests only make sense if building on SDK >= 19041 as\n  // bIsFatalField only exists after that.\n#if defined(NTDDI_WIN10_VB) && (WDK_NTDDI_VERSION >= NTDDI_WIN10_VB)\n  crashpad::WerRegistration registration;\n  // Non-fatal exceptions are skipped.\n  wer_ex.bIsFatal = FALSE;\n  res = wref(&registration, &wer_ex, &bClaimed, nullptr, nullptr, nullptr);\n  ASSERT_EQ(res, S_OK);\n  ASSERT_EQ(bClaimed, FALSE);\n\n  // Fatal exception with unhandled code is skipped.\n  wer_ex.bIsFatal = TRUE;\n  wer_ex.exceptionRecord.ExceptionCode = 0xc0000005;\n  res = wref(&registration, &wer_ex, &bClaimed, nullptr, nullptr, nullptr);\n  ASSERT_EQ(res, S_OK);\n  ASSERT_EQ(bClaimed, FALSE);\n#endif  // defined(NTDDI_WIN10_VB) && WDK_NTDDI_VERSION >= NTDDI_WIN10_VB\n  FreeLibrary(hMod);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "handler/win/z7_test.cpp",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Build in VC++6 or older command prompt with:\n//\n// cl /nologo /W4 /MT /Z7 z7_test.cpp /link /dll /out:z7_test.dll /debugtype:cv /pdb:none\n//\n// Given that this is quite tedious to build, the result is also checked in.\n\n#include <windows.h>\n#include <stdio.h>\n\nextern \"C\" __declspec(dllexport) void CrashMe() {\n  volatile int* foo = reinterpret_cast<volatile int*>(7);\n  *foo = 42;\n}\n\nBOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID) {\n  printf(\"%p %d\\n\", hinstance, reason);\n  return TRUE;\n}\n"
  },
  {
    "path": "infra/config/PRESUBMIT.py",
    "content": "# Copyright 2018 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\nUSE_PYTHON3 = True\nPRESUBMIT_VERSION = '2.0.0'\n\n\ndef CheckChangedLUCIConfigs(input_api, output_api):\n    return input_api.canned_checks.CheckChangedLUCIConfigs(\n        input_api, output_api)\n\n\ndef CheckLucicfgGenOutputMain(input_api, output_api):\n    return input_api.RunTests(\n        input_api.canned_checks.CheckLucicfgGenOutput(input_api, output_api,\n                                                      'main.star'))\n"
  },
  {
    "path": "infra/config/generated/commit-queue.cfg",
    "content": "# Auto-generated by lucicfg.\n# Do not modify manually.\n#\n# For the schema of this file, see Config message:\n#   https://config.luci.app/schemas/projects:commit-queue.cfg\n\ncq_status_host: \"chromium-cq-status.appspot.com\"\nsubmit_options {\n  max_burst: 4\n  burst_delay {\n    seconds: 480\n  }\n}\nconfig_groups {\n  name: \"crashpad\"\n  gerrit {\n    url: \"https://chromium-review.googlesource.com\"\n    projects {\n      name: \"crashpad/crashpad\"\n      ref_regexp: \"refs/heads/.+\"\n    }\n  }\n  verifiers {\n    gerrit_cq_ability {\n      committer_list: \"project-crashpad-tryjob-access\"\n      dry_run_access_list: \"project-crashpad-tryjob-access\"\n    }\n    tryjob {\n      builders {\n        name: \"crashpad/try/crashpad_fuchsia_arm64_dbg\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_fuchsia_arm64_rel\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_fuchsia_x64_dbg\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_fuchsia_x64_rel\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_ios_arm64_dbg\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_ios_arm64_rel\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_ios_x64_dbg\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_ios_x64_rel\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_linux_x64_dbg\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_linux_x64_rel\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_mac_arm64_dbg\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_mac_arm64_rel\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_mac_x64_dbg\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_mac_x64_rel\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_win_x64_dbg\"\n      }\n      builders {\n        name: \"crashpad/try/crashpad_win_x64_rel\"\n      }\n      retry_config {\n        single_quota: 1\n        global_quota: 2\n        failure_weight: 1\n        transient_failure_weight: 1\n        timeout_weight: 2\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "infra/config/generated/cr-buildbucket.cfg",
    "content": "# Auto-generated by lucicfg.\n# Do not modify manually.\n#\n# For the schema of this file, see BuildbucketCfg message:\n#   https://config.luci.app/schemas/projects:buildbucket.cfg\n\nbuckets {\n  name: \"ci\"\n  acls {\n    role: WRITER\n    group: \"project-crashpad-admins\"\n  }\n  acls {\n    group: \"all\"\n  }\n  acls {\n    role: SCHEDULER\n    identity: \"user:luci-scheduler@appspot.gserviceaccount.com\"\n  }\n  swarming {\n    builders {\n      name: \"crashpad_fuchsia_arm64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"fuchsia\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_fuchsia_arm64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"fuchsia\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_fuchsia_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"fuchsia\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_fuchsia_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"fuchsia\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_ios_arm64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"ios\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_ios\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_ios_arm64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"ios\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_ios\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_ios_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"ios\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_ios\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_ios_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"ios\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_ios\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_linux_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"linux\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_linux_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"linux\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_mac_arm64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:arm64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"mac\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_mac\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_mac_arm64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:arm64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"mac\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_mac\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_mac_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"mac\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_mac\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_mac_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"mac\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_mac\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_win_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Windows-10\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/windows_sdk\": {'\n        '    \"version\": \"uploaded:2024-01-11\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"win\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_win_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Windows-10\"\n      dimensions: \"pool:luci.flex.ci\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/windows_sdk\": {'\n        '    \"version\": \"uploaded:2024-01-11\"'\n        '  },'\n        '  \"$gatekeeper\": {'\n        '    \"group\": \"client.crashpad\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"win\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n  }\n  shadow: \"ci.shadow\"\n}\nbuckets {\n  name: \"ci.shadow\"\n  acls {\n    group: \"all\"\n  }\n  constraints {\n    pools: \"luci.flex.ci\"\n    service_accounts: \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n  }\n  dynamic_builder_template {}\n}\nbuckets {\n  name: \"try\"\n  acls {\n    role: WRITER\n    group: \"project-crashpad-admins\"\n  }\n  acls {\n    role: WRITER\n    group: \"service-account-crashpad-cq\"\n  }\n  acls {\n    group: \"all\"\n  }\n  acls {\n    role: SCHEDULER\n    group: \"project-crashpad-tryjob-access\"\n  }\n  acls {\n    role: SCHEDULER\n    group: \"service-account-cq\"\n  }\n  swarming {\n    builders {\n      name: \"crashpad_fuchsia_arm64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"fuchsia\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_fuchsia_arm64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"fuchsia\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_fuchsia_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"fuchsia\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_fuchsia_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"fuchsia\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_ios_arm64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"ios\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_ios\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_ios_arm64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"ios\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_ios\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_ios_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"ios\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_ios\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_ios_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"ios\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_ios\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_linux_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"linux\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_linux_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Ubuntu-24.04\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"linux\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_mac_arm64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:arm64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"mac\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_mac\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_mac_arm64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:arm64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_cpu\": \"arm64\",'\n        '  \"target_os\": \"mac\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_mac\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_mac_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"mac\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_mac\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_mac_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Mac-15\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/osx_sdk\": {'\n        '    \"sdk_version\": \"17c52\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"mac\"'\n        '}'\n      execution_timeout_secs: 10800\n      caches {\n        name: \"osx_sdk_mac\"\n        path: \"osx_sdk\"\n      }\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_win_x64_dbg\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Windows-10\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/windows_sdk\": {'\n        '    \"version\": \"uploaded:2024-01-11\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Debug\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"win\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n    builders {\n      name: \"crashpad_win_x64_rel\"\n      swarming_host: \"chromium-swarm.appspot.com\"\n      dimensions: \"cores:8\"\n      dimensions: \"cpu:x86-64\"\n      dimensions: \"os:Windows-10\"\n      dimensions: \"pool:luci.flex.try\"\n      exe {\n        cipd_package: \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\"\n        cipd_version: \"refs/heads/main\"\n        cmd: \"luciexe\"\n      }\n      properties:\n        '{'\n        '  \"$depot_tools/windows_sdk\": {'\n        '    \"version\": \"uploaded:2024-01-11\"'\n        '  },'\n        '  \"$kitchen\": {'\n        '    \"devshell\": true,'\n        '    \"git_auth\": true'\n        '  },'\n        '  \"config\": \"Release\",'\n        '  \"recipe\": \"crashpad/build\",'\n        '  \"target_os\": \"win\"'\n        '}'\n      execution_timeout_secs: 10800\n      build_numbers: YES\n      service_account: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n      experiments {\n        key: \"luci.recipes.use_python3\"\n        value: 100\n      }\n    }\n  }\n  shadow: \"try.shadow\"\n}\nbuckets {\n  name: \"try.shadow\"\n  acls {\n    group: \"all\"\n  }\n  constraints {\n    pools: \"luci.flex.try\"\n    service_accounts: \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n  }\n  dynamic_builder_template {}\n}\n"
  },
  {
    "path": "infra/config/generated/luci-logdog.cfg",
    "content": "# Auto-generated by lucicfg.\n# Do not modify manually.\n#\n# For the schema of this file, see ProjectConfig message:\n#   https://config.luci.app/schemas/projects:luci-logdog.cfg\n\nreader_auth_groups: \"all\"\nwriter_auth_groups: \"luci-logdog-chromium-writers\"\narchive_gs_bucket: \"chromium-luci-logdog\"\n"
  },
  {
    "path": "infra/config/generated/luci-milo.cfg",
    "content": "# Auto-generated by lucicfg.\n# Do not modify manually.\n#\n# For the schema of this file, see Project message:\n#   https://config.luci.app/schemas/projects:luci-milo.cfg\n\nconsoles {\n  id: \"main\"\n  name: \"Crashpad Main Console\"\n  repo_url: \"https://chromium.googlesource.com/crashpad/crashpad\"\n  refs: \"regexp:refs/heads/main\"\n  manifest_name: \"REVISION\"\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_fuchsia_arm64_dbg\"\n    category: \"fuchsia|arm64\"\n    short_name: \"dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_fuchsia_arm64_rel\"\n    category: \"fuchsia|arm64\"\n    short_name: \"rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_fuchsia_x64_dbg\"\n    category: \"fuchsia|x64\"\n    short_name: \"dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_fuchsia_x64_rel\"\n    category: \"fuchsia|x64\"\n    short_name: \"rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_ios_arm64_dbg\"\n    category: \"ios|arm64\"\n    short_name: \"dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_ios_arm64_rel\"\n    category: \"ios|arm64\"\n    short_name: \"rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_ios_x64_dbg\"\n    category: \"ios|x64\"\n    short_name: \"dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_ios_x64_rel\"\n    category: \"ios|x64\"\n    short_name: \"rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_linux_x64_dbg\"\n    category: \"linux|x64\"\n    short_name: \"dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_linux_x64_rel\"\n    category: \"linux|x64\"\n    short_name: \"rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_mac_x64_dbg\"\n    category: \"mac|x64\"\n    short_name: \"dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_mac_x64_rel\"\n    category: \"mac|x64\"\n    short_name: \"rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_mac_arm64_dbg\"\n    category: \"mac|arm64\"\n    short_name: \"dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_mac_arm64_rel\"\n    category: \"mac|arm64\"\n    short_name: \"rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_win_x64_dbg\"\n    category: \"win|x64\"\n    short_name: \"dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.ci/crashpad_win_x64_rel\"\n    category: \"win|x64\"\n    short_name: \"rel\"\n  }\n}\nconsoles {\n  id: \"try\"\n  name: \"Crashpad Try Builders\"\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_fuchsia_arm64_dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_fuchsia_arm64_rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_fuchsia_x64_dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_fuchsia_x64_rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_ios_arm64_dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_ios_arm64_rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_ios_x64_dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_ios_x64_rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_linux_x64_dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_linux_x64_rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_mac_x64_dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_mac_x64_rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_mac_arm64_dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_mac_arm64_rel\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_win_x64_dbg\"\n  }\n  builders {\n    name: \"buildbucket/luci.crashpad.try/crashpad_win_x64_rel\"\n  }\n  builder_view_only: true\n}\nlogo_url: \"https://storage.googleapis.com/chrome-infra-public/logo/crashpad-logo.svg\"\n"
  },
  {
    "path": "infra/config/generated/luci-scheduler.cfg",
    "content": "# Auto-generated by lucicfg.\n# Do not modify manually.\n#\n# For the schema of this file, see ProjectConfig message:\n#   https://config.luci.app/schemas/projects:luci-scheduler.cfg\n\njob {\n  id: \"crashpad_fuchsia_arm64_dbg\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_fuchsia_arm64_dbg\"\n  }\n}\njob {\n  id: \"crashpad_fuchsia_arm64_rel\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_fuchsia_arm64_rel\"\n  }\n}\njob {\n  id: \"crashpad_fuchsia_x64_dbg\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_fuchsia_x64_dbg\"\n  }\n}\njob {\n  id: \"crashpad_fuchsia_x64_rel\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_fuchsia_x64_rel\"\n  }\n}\njob {\n  id: \"crashpad_ios_arm64_dbg\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_ios_arm64_dbg\"\n  }\n}\njob {\n  id: \"crashpad_ios_arm64_rel\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_ios_arm64_rel\"\n  }\n}\njob {\n  id: \"crashpad_ios_x64_dbg\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_ios_x64_dbg\"\n  }\n}\njob {\n  id: \"crashpad_ios_x64_rel\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_ios_x64_rel\"\n  }\n}\njob {\n  id: \"crashpad_linux_x64_dbg\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_linux_x64_dbg\"\n  }\n}\njob {\n  id: \"crashpad_linux_x64_rel\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_linux_x64_rel\"\n  }\n}\njob {\n  id: \"crashpad_mac_arm64_dbg\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_mac_arm64_dbg\"\n  }\n}\njob {\n  id: \"crashpad_mac_arm64_rel\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_mac_arm64_rel\"\n  }\n}\njob {\n  id: \"crashpad_mac_x64_dbg\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_mac_x64_dbg\"\n  }\n}\njob {\n  id: \"crashpad_mac_x64_rel\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_mac_x64_rel\"\n  }\n}\njob {\n  id: \"crashpad_win_x64_dbg\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_win_x64_dbg\"\n  }\n}\njob {\n  id: \"crashpad_win_x64_rel\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  buildbucket {\n    server: \"cr-buildbucket.appspot.com\"\n    bucket: \"ci\"\n    builder: \"crashpad_win_x64_rel\"\n  }\n}\ntrigger {\n  id: \"master-gitiles-trigger\"\n  realm: \"ci\"\n  acl_sets: \"ci\"\n  triggers: \"crashpad_fuchsia_arm64_dbg\"\n  triggers: \"crashpad_fuchsia_arm64_rel\"\n  triggers: \"crashpad_fuchsia_x64_dbg\"\n  triggers: \"crashpad_fuchsia_x64_rel\"\n  triggers: \"crashpad_ios_arm64_dbg\"\n  triggers: \"crashpad_ios_arm64_rel\"\n  triggers: \"crashpad_ios_x64_dbg\"\n  triggers: \"crashpad_ios_x64_rel\"\n  triggers: \"crashpad_linux_x64_dbg\"\n  triggers: \"crashpad_linux_x64_rel\"\n  triggers: \"crashpad_mac_arm64_dbg\"\n  triggers: \"crashpad_mac_arm64_rel\"\n  triggers: \"crashpad_mac_x64_dbg\"\n  triggers: \"crashpad_mac_x64_rel\"\n  triggers: \"crashpad_win_x64_dbg\"\n  triggers: \"crashpad_win_x64_rel\"\n  gitiles {\n    repo: \"https://chromium.googlesource.com/crashpad/crashpad\"\n    refs: \"regexp:refs/heads/main\"\n  }\n}\nacl_sets {\n  name: \"ci\"\n  acls {\n    role: OWNER\n    granted_to: \"group:project-crashpad-admins\"\n  }\n  acls {\n    granted_to: \"group:all\"\n  }\n}\n"
  },
  {
    "path": "infra/config/generated/project.cfg",
    "content": "# Auto-generated by lucicfg.\n# Do not modify manually.\n#\n# For the schema of this file, see ProjectCfg message:\n#   https://config.luci.app/schemas/projects:project.cfg\n\nname: \"crashpad\"\naccess: \"group:all\"\nlucicfg {\n  version: \"1.46.2\"\n  package_dir: \"..\"\n  config_dir: \"generated\"\n  entry_point: \"main.star\"\n  experiments: \"crbug.com/1182002\"\n}\n"
  },
  {
    "path": "infra/config/generated/realms.cfg",
    "content": "# Auto-generated by lucicfg.\n# Do not modify manually.\n#\n# For the schema of this file, see RealmsCfg message:\n#   https://config.luci.app/schemas/projects:realms.cfg\n\nrealms {\n  name: \"@root\"\n  bindings {\n    role: \"role/buildbucket.reader\"\n    principals: \"group:all\"\n  }\n  bindings {\n    role: \"role/configs.reader\"\n    principals: \"group:all\"\n  }\n  bindings {\n    role: \"role/logdog.reader\"\n    principals: \"group:all\"\n  }\n  bindings {\n    role: \"role/logdog.writer\"\n    principals: \"group:luci-logdog-chromium-writers\"\n  }\n  bindings {\n    role: \"role/scheduler.owner\"\n    principals: \"group:project-crashpad-admins\"\n  }\n  bindings {\n    role: \"role/scheduler.reader\"\n    principals: \"group:all\"\n  }\n}\nrealms {\n  name: \"ci\"\n  bindings {\n    role: \"role/buildbucket.builderServiceAccount\"\n    principals: \"user:crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n  }\n  bindings {\n    role: \"role/buildbucket.owner\"\n    principals: \"group:project-crashpad-admins\"\n  }\n  bindings {\n    role: \"role/buildbucket.triggerer\"\n    principals: \"user:luci-scheduler@appspot.gserviceaccount.com\"\n  }\n}\nrealms {\n  name: \"ci.shadow\"\n  bindings {\n    role: \"role/buildbucket.builderServiceAccount\"\n    principals: \"user:crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\"\n  }\n  bindings {\n    role: \"role/buildbucket.creator\"\n    principals: \"group:mdb/chrome-build-access-sphinx\"\n  }\n}\nrealms {\n  name: \"try\"\n  bindings {\n    role: \"role/buildbucket.builderServiceAccount\"\n    principals: \"user:crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n  }\n  bindings {\n    role: \"role/buildbucket.owner\"\n    principals: \"group:project-crashpad-admins\"\n    principals: \"group:service-account-crashpad-cq\"\n  }\n  bindings {\n    role: \"role/buildbucket.triggerer\"\n    principals: \"group:project-crashpad-tryjob-access\"\n    principals: \"group:service-account-cq\"\n  }\n}\nrealms {\n  name: \"try.shadow\"\n  bindings {\n    role: \"role/buildbucket.builderServiceAccount\"\n    principals: \"user:crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\"\n  }\n  bindings {\n    role: \"role/buildbucket.creator\"\n    principals: \"group:mdb/chrome-build-access-sphinx\"\n  }\n}\n"
  },
  {
    "path": "infra/config/main.star",
    "content": "#!/usr/bin/env lucicfg\n\n# Copyright 2021 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nlucicfg.check_version(\"1.30.9\", \"Please update depot_tools\")\n\nREPO_URL = \"https://chromium.googlesource.com/crashpad/crashpad\"\nREVIEW_URL = \"https://chromium-review.googlesource.com/crashpad/crashpad\"\n\n# Use LUCI Scheduler BBv2 names and add Scheduler realms configs.\nlucicfg.enable_experiment(\"crbug.com/1182002\")\n\nluci.project(\n    name = \"crashpad\",\n    buildbucket = \"cr-buildbucket.appspot.com\",\n    swarming = \"chromium-swarm.appspot.com\",\n    acls = [\n        acl.entry(\n            roles = [\n                acl.LOGDOG_READER,\n                acl.PROJECT_CONFIGS_READER,\n                acl.SCHEDULER_READER,\n                acl.BUILDBUCKET_READER,\n            ],\n            groups = \"all\",\n        ),\n        acl.entry(\n            roles = acl.LOGDOG_WRITER,\n            groups = \"luci-logdog-chromium-writers\",\n        ),\n        acl.entry(\n            roles = acl.SCHEDULER_OWNER,\n            groups = \"project-crashpad-admins\",\n        ),\n    ],\n    logdog = \"luci-logdog.appspot.com\",\n    milo = \"luci-milo.appspot.com\",\n    scheduler = \"luci-scheduler.appspot.com\",\n)\n\nluci.cq(\n    status_host = \"chromium-cq-status.appspot.com\",\n    submit_max_burst = 4,\n    submit_burst_delay = 8 * time.minute,\n)\n\nluci.cq_group(\n    name = \"crashpad\",\n    watch = cq.refset(repo = REVIEW_URL, refs = [\"refs/heads/.+\"]),\n    retry_config = cq.retry_config(\n        single_quota = 1,\n        global_quota = 2,\n        failure_weight = 1,\n        transient_failure_weight = 1,\n        timeout_weight = 2,\n    ),\n    acls = [\n        acl.entry(\n            roles = acl.CQ_COMMITTER,\n            groups = \"project-crashpad-tryjob-access\",\n        ),\n        acl.entry(\n            roles = acl.CQ_DRY_RUNNER,\n            groups = \"project-crashpad-tryjob-access\",\n        ),\n    ],\n)\n\nluci.gitiles_poller(\n    name = \"master-gitiles-trigger\",\n    bucket = \"ci\",\n    repo = REPO_URL,\n)\n\nluci.logdog(\n    gs_bucket = \"chromium-luci-logdog\",\n)\n\nluci.milo(\n    logo = \"https://storage.googleapis.com/chrome-infra-public/logo/crashpad-logo.svg\",\n)\n\nluci.console_view(\n    name = \"main\",\n    repo = REPO_URL,\n    title = \"Crashpad Main Console\",\n)\n\nluci.list_view(\n    name = \"try\",\n    title = \"Crashpad Try Builders\",\n)\n\nluci.bucket(\n    name = \"ci\",\n    acls = [\n        acl.entry(\n            acl.BUILDBUCKET_OWNER,\n            groups = \"project-crashpad-admins\",\n        ),\n        acl.entry(\n            acl.BUILDBUCKET_TRIGGERER,\n            users = \"luci-scheduler@appspot.gserviceaccount.com\",\n        ),\n    ],\n)\n\nluci.bucket(\n    name = \"ci.shadow\",\n    shadows = \"ci\",\n    constraints = luci.bucket_constraints(\n        pools = [\"luci.flex.ci\"],\n        service_accounts = [\n            \"crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com\",\n        ],\n    ),\n    bindings = [\n        # For led permissions.\n        luci.binding(\n            roles = \"role/buildbucket.creator\",\n            groups = [\n                \"mdb/chrome-build-access-sphinx\",\n            ],\n        ),\n    ],\n    dynamic = True,\n)\n\nluci.bucket(\n    name = \"try\",\n    acls = [\n        acl.entry(\n            acl.BUILDBUCKET_OWNER,\n            groups = [\n                \"service-account-crashpad-cq\",\n                \"project-crashpad-admins\",\n            ],\n        ),\n        acl.entry(\n            acl.BUILDBUCKET_TRIGGERER,\n            groups = \"service-account-cq\",\n        ),\n        acl.entry(\n            acl.BUILDBUCKET_TRIGGERER,\n            groups = \"project-crashpad-tryjob-access\",\n        ),\n    ],\n)\n\nluci.bucket(\n    name = \"try.shadow\",\n    shadows = \"try\",\n    constraints = luci.bucket_constraints(\n        pools = [\"luci.flex.try\"],\n        service_accounts = [\n            \"crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com\",\n        ],\n    ),\n    bindings = [\n        # For led permissions.\n        luci.binding(\n            roles = \"role/buildbucket.creator\",\n            groups = [\n                \"mdb/chrome-build-access-sphinx\",\n            ],\n        ),\n    ],\n    dynamic = True,\n)\n\ndef crashpad_recipe():\n    return luci.recipe(\n        name = \"crashpad/build\",\n        cipd_package = \"infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build\",\n        use_python3 = True,\n    )\n\ndef crashpad_caches(platform):\n    if platform == \"ios\":\n        return [swarming.cache(\"osx_sdk\", name = \"osx_sdk_ios\")]\n    elif platform == \"mac\":\n        return [swarming.cache(\"osx_sdk\", name = \"osx_sdk_mac\")]\n\ndef crashpad_dimensions(platform, cpu, bucket):\n    dimensions = {}\n    dimensions[\"cpu\"] = \"x86-64\"\n    dimensions[\"pool\"] = \"luci.flex.\" + bucket\n\n    if platform == \"fuchsia\":\n        dimensions[\"os\"] = \"Ubuntu-24.04\"\n    elif platform == \"ios\":\n        dimensions[\"os\"] = \"Mac-15\"\n    elif platform == \"linux\":\n        dimensions[\"os\"] = \"Ubuntu-24.04\"\n    elif platform == \"mac\":\n        dimensions[\"os\"] = \"Mac-15\"\n        if cpu == \"arm64\":\n            dimensions[\"cpu\"] = cpu\n    elif platform == \"win\":\n        dimensions[\"os\"] = \"Windows-10\"\n\n    if platform == \"fuchsia\" or platform == \"linux\" or platform == \"win\":\n        dimensions[\"cores\"] = \"8\"\n\n    return dimensions\n\ndef crashpad_properties(platform, cpu, config, bucket):\n    properties = {}\n    properties[\"target_os\"] = platform\n    properties[\"$kitchen\"] = {\n        \"devshell\": True,\n        \"git_auth\": True,\n    }\n\n    if cpu != \"x64\":\n        properties[\"target_cpu\"] = cpu\n\n    if platform == \"win\":\n        properties[\"$depot_tools/windows_sdk\"] = {\n            \"version\": \"uploaded:2024-01-11\",\n        }\n\n    if platform == \"mac\" or platform == \"ios\":\n        properties[\"$depot_tools/osx_sdk\"] = {\n            \"sdk_version\": \"17c52\",\n        }\n\n    if bucket == \"ci\":\n        properties[\"$gatekeeper\"] = {\n            \"group\": \"client.crashpad\",\n        }\n\n    if config == \"dbg\":\n        properties[\"config\"] = \"Debug\"\n    elif config == \"rel\":\n        properties[\"config\"] = \"Release\"\n\n    return properties\n\ndef crashpad_builder(platform, cpu, config, bucket):\n    name = \"_\".join([\"crashpad\", platform, cpu, config])\n    triggered_by = None\n\n    if bucket == \"ci\":\n        luci.console_view_entry(\n            builder = \"ci/\" + name,\n            console_view = \"main\",\n            short_name = config,\n            category = platform + \"|\" + cpu,\n        )\n        triggered_by = [\"master-gitiles-trigger\"]\n    elif bucket == \"try\":\n        luci.list_view_entry(\n            builder = \"try/\" + name,\n            list_view = \"try\",\n        )\n        luci.cq_tryjob_verifier(\n            \"try/\" + name,\n            cq_group = \"crashpad\",\n        )\n\n    return luci.builder(\n        name = name,\n        bucket = bucket,\n        executable = crashpad_recipe(),\n        build_numbers = True,\n        caches = crashpad_caches(platform),\n        dimensions = crashpad_dimensions(platform, cpu, bucket),\n        execution_timeout = 3 * time.hour,\n        properties = crashpad_properties(platform, cpu, config, bucket),\n        service_account = \"crashpad-\" + bucket + \"-builder@chops-service-accounts.iam.gserviceaccount.com\",\n        triggered_by = triggered_by,\n    )\n\ncrashpad_builder(\"fuchsia\", \"arm64\", \"dbg\", \"ci\")\ncrashpad_builder(\"fuchsia\", \"arm64\", \"rel\", \"ci\")\ncrashpad_builder(\"fuchsia\", \"x64\", \"dbg\", \"ci\")\ncrashpad_builder(\"fuchsia\", \"x64\", \"rel\", \"ci\")\ncrashpad_builder(\"ios\", \"arm64\", \"dbg\", \"ci\")\ncrashpad_builder(\"ios\", \"arm64\", \"rel\", \"ci\")\ncrashpad_builder(\"ios\", \"x64\", \"dbg\", \"ci\")\ncrashpad_builder(\"ios\", \"x64\", \"rel\", \"ci\")\ncrashpad_builder(\"linux\", \"x64\", \"dbg\", \"ci\")\ncrashpad_builder(\"linux\", \"x64\", \"rel\", \"ci\")\ncrashpad_builder(\"mac\", \"x64\", \"dbg\", \"ci\")\ncrashpad_builder(\"mac\", \"x64\", \"rel\", \"ci\")\ncrashpad_builder(\"mac\", \"arm64\", \"dbg\", \"ci\")\ncrashpad_builder(\"mac\", \"arm64\", \"rel\", \"ci\")\ncrashpad_builder(\"win\", \"x64\", \"dbg\", \"ci\")\ncrashpad_builder(\"win\", \"x64\", \"rel\", \"ci\")\n\ncrashpad_builder(\"fuchsia\", \"arm64\", \"dbg\", \"try\")\ncrashpad_builder(\"fuchsia\", \"arm64\", \"rel\", \"try\")\ncrashpad_builder(\"fuchsia\", \"x64\", \"dbg\", \"try\")\ncrashpad_builder(\"fuchsia\", \"x64\", \"rel\", \"try\")\ncrashpad_builder(\"ios\", \"arm64\", \"dbg\", \"try\")\ncrashpad_builder(\"ios\", \"arm64\", \"rel\", \"try\")\ncrashpad_builder(\"ios\", \"x64\", \"dbg\", \"try\")\ncrashpad_builder(\"ios\", \"x64\", \"rel\", \"try\")\ncrashpad_builder(\"linux\", \"x64\", \"dbg\", \"try\")\ncrashpad_builder(\"linux\", \"x64\", \"rel\", \"try\")\ncrashpad_builder(\"mac\", \"x64\", \"dbg\", \"try\")\ncrashpad_builder(\"mac\", \"x64\", \"rel\", \"try\")\ncrashpad_builder(\"mac\", \"arm64\", \"dbg\", \"try\")\ncrashpad_builder(\"mac\", \"arm64\", \"rel\", \"try\")\ncrashpad_builder(\"win\", \"x64\", \"dbg\", \"try\")\ncrashpad_builder(\"win\", \"x64\", \"rel\", \"try\")\n"
  },
  {
    "path": "minidump/BUILD.gn",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../build/crashpad_buildconfig.gni\")\n\ncrashpad_static_library(\"minidump\") {\n  sources = [\n    \"minidump_annotation_writer.cc\",\n    \"minidump_annotation_writer.h\",\n    \"minidump_byte_array_writer.cc\",\n    \"minidump_byte_array_writer.h\",\n    \"minidump_context_writer.cc\",\n    \"minidump_context_writer.h\",\n    \"minidump_crashpad_info_writer.cc\",\n    \"minidump_crashpad_info_writer.h\",\n    \"minidump_exception_writer.cc\",\n    \"minidump_exception_writer.h\",\n    \"minidump_file_writer.cc\",\n    \"minidump_file_writer.h\",\n    \"minidump_handle_writer.cc\",\n    \"minidump_handle_writer.h\",\n    \"minidump_memory_info_writer.cc\",\n    \"minidump_memory_info_writer.h\",\n    \"minidump_memory_writer.cc\",\n    \"minidump_memory_writer.h\",\n    \"minidump_misc_info_writer.cc\",\n    \"minidump_misc_info_writer.h\",\n    \"minidump_module_crashpad_info_writer.cc\",\n    \"minidump_module_crashpad_info_writer.h\",\n    \"minidump_module_writer.cc\",\n    \"minidump_module_writer.h\",\n    \"minidump_rva_list_writer.cc\",\n    \"minidump_rva_list_writer.h\",\n    \"minidump_simple_string_dictionary_writer.cc\",\n    \"minidump_simple_string_dictionary_writer.h\",\n    \"minidump_stream_writer.cc\",\n    \"minidump_stream_writer.h\",\n    \"minidump_string_writer.cc\",\n    \"minidump_string_writer.h\",\n    \"minidump_system_info_writer.cc\",\n    \"minidump_system_info_writer.h\",\n    \"minidump_thread_id_map.cc\",\n    \"minidump_thread_id_map.h\",\n    \"minidump_thread_name_list_writer.cc\",\n    \"minidump_thread_name_list_writer.h\",\n    \"minidump_thread_writer.cc\",\n    \"minidump_thread_writer.h\",\n    \"minidump_unloaded_module_writer.cc\",\n    \"minidump_unloaded_module_writer.h\",\n    \"minidump_user_extension_stream_data_source.cc\",\n    \"minidump_user_extension_stream_data_source.h\",\n    \"minidump_user_stream_writer.cc\",\n    \"minidump_user_stream_writer.h\",\n    \"minidump_writable.cc\",\n    \"minidump_writable.h\",\n    \"minidump_writer_util.cc\",\n    \"minidump_writer_util.h\",\n  ]\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  public_deps = [\n    \":format\",\n    \"../compat\",\n  ]\n\n  deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../snapshot\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_win) {\n    cflags = [\n      \"/wd4201\",  # nonstandard extension used : nameless struct/union\n      \"/wd4324\",  # 'struct' : structure was padded due to __declspec(align())\n    ]\n  }\n}\n\n# :format is the only part of minidump that snapshot may depend on.\nstatic_library(\"format\") {\n  sources = [\n    \"minidump_context.h\",\n    \"minidump_extensions.cc\",\n    \"minidump_extensions.h\",\n  ]\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  public_deps = [ \"../compat\" ]\n\n  deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../snapshot:context\",\n    \"../util\",\n  ]\n}\n\nstatic_library(\"test_support\") {\n  testonly = true\n\n  sources = [\n    \"test/minidump_byte_array_writer_test_util.cc\",\n    \"test/minidump_byte_array_writer_test_util.h\",\n    \"test/minidump_context_test_util.cc\",\n    \"test/minidump_context_test_util.h\",\n    \"test/minidump_file_writer_test_util.cc\",\n    \"test/minidump_file_writer_test_util.h\",\n    \"test/minidump_memory_writer_test_util.cc\",\n    \"test/minidump_memory_writer_test_util.h\",\n    \"test/minidump_rva_list_test_util.cc\",\n    \"test/minidump_rva_list_test_util.h\",\n    \"test/minidump_string_writer_test_util.cc\",\n    \"test/minidump_string_writer_test_util.h\",\n    \"test/minidump_user_extension_stream_util.cc\",\n    \"test/minidump_user_extension_stream_util.h\",\n    \"test/minidump_writable_test_util.cc\",\n    \"test/minidump_writable_test_util.h\",\n  ]\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  public_deps = [ \":minidump\" ]\n\n  deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../snapshot:test_support\",\n    \"../test\",\n    \"../third_party/googletest\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_win) {\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n  }\n}\n\nsource_set(\"minidump_test\") {\n  testonly = true\n\n  sources = [\n    \"minidump_annotation_writer_test.cc\",\n    \"minidump_byte_array_writer_test.cc\",\n    \"minidump_context_writer_test.cc\",\n    \"minidump_crashpad_info_writer_test.cc\",\n    \"minidump_exception_writer_test.cc\",\n    \"minidump_file_writer_test.cc\",\n    \"minidump_handle_writer_test.cc\",\n    \"minidump_memory_info_writer_test.cc\",\n    \"minidump_memory_writer_test.cc\",\n    \"minidump_misc_info_writer_test.cc\",\n    \"minidump_module_crashpad_info_writer_test.cc\",\n    \"minidump_module_writer_test.cc\",\n    \"minidump_rva_list_writer_test.cc\",\n    \"minidump_simple_string_dictionary_writer_test.cc\",\n    \"minidump_string_writer_test.cc\",\n    \"minidump_system_info_writer_test.cc\",\n    \"minidump_thread_id_map_test.cc\",\n    \"minidump_thread_name_list_writer_test.cc\",\n    \"minidump_thread_writer_test.cc\",\n    \"minidump_unloaded_module_writer_test.cc\",\n    \"minidump_user_stream_writer_test.cc\",\n    \"minidump_writable_test.cc\",\n  ]\n\n  configs += [ \"../build:crashpad_is_in_fuchsia\" ]\n\n  deps = [\n    \":test_support\",\n    \"$mini_chromium_source_parent:base\",\n    \"../snapshot:test_support\",\n    \"../test\",\n    \"../third_party/googletest\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_win) {\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n  }\n}\n"
  },
  {
    "path": "minidump/minidump_annotation_writer.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_annotation_writer.h\"\n\n#include <memory>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpAnnotationWriter::MinidumpAnnotationWriter() = default;\n\nMinidumpAnnotationWriter::~MinidumpAnnotationWriter() = default;\n\nvoid MinidumpAnnotationWriter::InitializeFromSnapshot(\n    const AnnotationSnapshot& snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  name_.SetUTF8(snapshot.name);\n  annotation_.type = snapshot.type;\n  annotation_.reserved = 0;\n  value_.set_data(snapshot.value);\n}\n\nvoid MinidumpAnnotationWriter::InitializeWithData(\n    const std::string& name,\n    uint16_t type,\n    const std::vector<uint8_t>& data) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  name_.SetUTF8(name);\n  annotation_.type = type;\n  annotation_.reserved = 0;\n  value_.set_data(data);\n}\n\nbool MinidumpAnnotationWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  name_.RegisterRVA(&annotation_.name);\n  value_.RegisterRVA(&annotation_.value);\n\n  return true;\n}\n\nsize_t MinidumpAnnotationWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // This object is written by the MinidumpAnnotationListWriter, and its\n  // children write themselves.\n  return 0;\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpAnnotationWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return {&name_, &value_};\n}\n\nbool MinidumpAnnotationWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  // This object is written by the MinidumpAnnotationListWriter, and its\n  // children write themselves.\n  return true;\n}\n\nMinidumpAnnotationListWriter::MinidumpAnnotationListWriter()\n    : minidump_list_(new MinidumpAnnotationList()) {}\n\nMinidumpAnnotationListWriter::~MinidumpAnnotationListWriter() = default;\n\nvoid MinidumpAnnotationListWriter::InitializeFromList(\n    const std::vector<AnnotationSnapshot>& list) {\n  DCHECK_EQ(state(), kStateMutable);\n  for (const auto& annotation : list) {\n    auto writer = std::make_unique<MinidumpAnnotationWriter>();\n    writer->InitializeFromSnapshot(annotation);\n    AddObject(std::move(writer));\n  }\n}\n\nvoid MinidumpAnnotationListWriter::AddObject(\n    std::unique_ptr<MinidumpAnnotationWriter> annotation_writer) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  objects_.push_back(std::move(annotation_writer));\n}\n\nbool MinidumpAnnotationListWriter::IsUseful() const {\n  return !objects_.empty();\n}\n\nbool MinidumpAnnotationListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  if (!AssignIfInRange(&minidump_list_->count, objects_.size())) {\n    LOG(ERROR) << \"annotation list size \" << objects_.size()\n               << \" is out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpAnnotationListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(*minidump_list_) + sizeof(MinidumpAnnotation) * objects_.size();\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpAnnotationListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<internal::MinidumpWritable*> children(objects_.size());\n  for (size_t i = 0; i < objects_.size(); ++i) {\n    children[i] = objects_[i].get();\n  }\n\n  return children;\n}\n\nbool MinidumpAnnotationListWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  std::vector<WritableIoVec> iov;\n  iov.reserve(1 + objects_.size());\n  iov.emplace_back(\n      WritableIoVec{minidump_list_.get(), sizeof(*minidump_list_)});\n\n  for (const auto& object : objects_) {\n    iov.emplace_back(WritableIoVec{object->minidump_annotation(),\n                                   sizeof(MinidumpAnnotation)});\n  }\n\n  return file_writer->WriteIoVec(&iov);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_annotation_writer.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_byte_array_writer.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_string_writer.h\"\n#include \"minidump/minidump_writable.h\"\n#include \"snapshot/annotation_snapshot.h\"\n\nnamespace crashpad {\n\n//! \\brief The writer for a MinidumpAnnotation object in a minidump file.\n//!\n//! Because MinidumpAnnotation objects only appear as elements\n//! of MinidumpAnnotationList objects, this class does not write any\n//! data on its own. It makes its MinidumpAnnotation data available to its\n//! MinidumpAnnotationList parent, which writes it as part of a\n//! MinidumpAnnotationList.\nclass MinidumpAnnotationWriter final : public internal::MinidumpWritable {\n public:\n  MinidumpAnnotationWriter();\n\n  MinidumpAnnotationWriter(const MinidumpAnnotationWriter&) = delete;\n  MinidumpAnnotationWriter& operator=(const MinidumpAnnotationWriter&) = delete;\n\n  ~MinidumpAnnotationWriter();\n\n  //! \\brief Initializes the annotation writer with data from an\n  //!     AnnotationSnapshot.\n  void InitializeFromSnapshot(const AnnotationSnapshot& snapshot);\n\n  //! \\brief Initializes the annotation writer with data values.\n  void InitializeWithData(const std::string& name,\n                          uint16_t type,\n                          const std::vector<uint8_t>& data);\n\n  //! \\brief Returns the MinidumpAnnotation referencing this object’s data.\n  const MinidumpAnnotation* minidump_annotation() const { return &annotation_; }\n\n protected:\n  // MinidumpWritable:\n\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<internal::MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  MinidumpAnnotation annotation_;\n  internal::MinidumpUTF8StringWriter name_;\n  MinidumpByteArrayWriter value_;\n};\n\n//! \\brief The writer for a MinidumpAnnotationList object in a minidump file,\n//!     containing a list of MinidumpAnnotation objects.\nclass MinidumpAnnotationListWriter final : public internal::MinidumpWritable {\n public:\n  MinidumpAnnotationListWriter();\n\n  MinidumpAnnotationListWriter(const MinidumpAnnotationListWriter&) = delete;\n  MinidumpAnnotationListWriter& operator=(const MinidumpAnnotationListWriter&) =\n      delete;\n\n  ~MinidumpAnnotationListWriter();\n\n  //! \\brief Initializes the annotation list writer with a list of\n  //!      AnnotationSnapshot objects.\n  void InitializeFromList(const std::vector<AnnotationSnapshot>& list);\n\n  //! \\brief Adds a single MinidumpAnnotationWriter to the list to be written.\n  void AddObject(std::unique_ptr<MinidumpAnnotationWriter> annotation_writer);\n\n  //! \\brief Determines whether the object is useful.\n  //!\n  //! A useful object is one that carries data that makes a meaningful\n  //! contribution to a minidump file. An object carrying entries would be\n  //! considered useful.\n  //!\n  //! \\return `true` if the object is useful, `false` otherwise.\n  bool IsUseful() const;\n\n protected:\n  // MinidumpWritable:\n\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<internal::MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  std::unique_ptr<MinidumpAnnotationList> minidump_list_;\n  std::vector<std::unique_ptr<MinidumpAnnotationWriter>> objects_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_annotation_writer_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_annotation_writer.h\"\n\n#include <iterator>\n#include <memory>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/test/minidump_byte_array_writer_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconst MinidumpAnnotationList* MinidumpAnnotationListAtStart(\n    const std::string& file_contents,\n    uint32_t count) {\n  MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;\n  location_descriptor.DataSize = static_cast<uint32_t>(\n      sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation));\n  location_descriptor.Rva = 0;\n  return MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n      file_contents, location_descriptor);\n}\n\nTEST(MinidumpAnnotationWriter, EmptyList) {\n  StringFile string_file;\n\n  MinidumpAnnotationListWriter list_writer;\n\n  EXPECT_FALSE(list_writer.IsUseful());\n\n  EXPECT_TRUE(list_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(), sizeof(MinidumpAnnotationList));\n\n  auto* list = MinidumpAnnotationListAtStart(string_file.string(), 0);\n  ASSERT_TRUE(list);\n  EXPECT_EQ(list->count, 0u);\n}\n\nTEST(MinidumpAnnotationWriter, OneItem) {\n  StringFile string_file;\n\n  const char kName[] = \"name\";\n  const uint16_t kType = 0xFFFF;\n  const std::vector<uint8_t> kValue{'v', 'a', 'l', 'u', 'e', '\\0'};\n\n  auto annotation_writer = std::make_unique<MinidumpAnnotationWriter>();\n  annotation_writer->InitializeWithData(kName, kType, kValue);\n\n  MinidumpAnnotationListWriter list_writer;\n  list_writer.AddObject(std::move(annotation_writer));\n\n  EXPECT_TRUE(list_writer.IsUseful());\n\n  EXPECT_TRUE(list_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpAnnotationList) + sizeof(MinidumpAnnotation) +\n                sizeof(MinidumpUTF8String) + sizeof(kName) +\n                sizeof(MinidumpByteArray) + kValue.size() +\n                3);  // 3 for padding.\n\n  auto* list = MinidumpAnnotationListAtStart(string_file.string(), 1);\n  ASSERT_TRUE(list);\n  EXPECT_EQ(list->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            list->objects[0].name),\n            kName);\n  EXPECT_EQ(list->objects[0].type, kType);\n  EXPECT_EQ(list->objects[0].reserved, 0u);\n  EXPECT_EQ(\n\n      MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value),\n      kValue);\n}\n\nTEST(MinidumpAnnotationWriter, ThreeItems) {\n  StringFile string_file;\n\n  const char* kNames[] = {\n      \"~~FIRST~~\", \" second + \", \"3\",\n  };\n  const uint16_t kTypes[] = {\n      0x1, 0xABCD, 0x42,\n  };\n  const std::vector<uint8_t> kValues[] = {\n      {'\\0'}, {0xB0, 0xA0, 0xD0, 0xD0, 0xD0}, {'T'},\n  };\n\n  MinidumpAnnotationListWriter list_writer;\n\n  for (size_t i = 0; i < std::size(kNames); ++i) {\n    auto annotation = std::make_unique<MinidumpAnnotationWriter>();\n    annotation->InitializeWithData(kNames[i], kTypes[i], kValues[i]);\n    list_writer.AddObject(std::move(annotation));\n  }\n\n  EXPECT_TRUE(list_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpAnnotationList) + 3 * sizeof(MinidumpAnnotation) +\n                3 * sizeof(MinidumpUTF8String) + 3 * sizeof(MinidumpByteArray) +\n                strlen(kNames[0]) + 1 + kValues[0].size() + 2 +\n                strlen(kNames[1]) + 1 + 3 + kValues[1].size() + 1 +\n                strlen(kNames[2]) + 1 + 3 + kValues[2].size() + 2);\n\n  auto* list = MinidumpAnnotationListAtStart(string_file.string(), 3);\n  ASSERT_TRUE(list);\n  EXPECT_EQ(list->count, 3u);\n\n  for (size_t i = 0; i < 3; ++i) {\n    EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                              list->objects[i].name),\n              kNames[i]);\n    EXPECT_EQ(list->objects[i].type, kTypes[i]);\n    EXPECT_EQ(list->objects[i].reserved, 0u);\n    EXPECT_EQ(\n\n        MinidumpByteArrayAtRVA(string_file.string(), list->objects[i].value),\n        kValues[i]);\n  }\n}\n\nTEST(MinidumpAnnotationWriter, DuplicateNames) {\n  StringFile string_file;\n\n  const char kName[] = \"@@name!\";\n  const uint16_t kType = 0x1;\n  const std::vector<uint8_t> kValue1{'r', 'e', 'd', '\\0'};\n  const std::vector<uint8_t> kValue2{'m', 'a', 'g', 'e', 'n', 't', 'a', '\\0'};\n\n  MinidumpAnnotationListWriter list_writer;\n\n  auto annotation = std::make_unique<MinidumpAnnotationWriter>();\n  annotation->InitializeWithData(kName, kType, kValue1);\n  list_writer.AddObject(std::move(annotation));\n\n  annotation = std::make_unique<MinidumpAnnotationWriter>();\n  annotation->InitializeWithData(kName, kType, kValue2);\n  list_writer.AddObject(std::move(annotation));\n\n  EXPECT_TRUE(list_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpAnnotationList) + 2 * sizeof(MinidumpAnnotation) +\n                2 * sizeof(MinidumpUTF8String) + 2 * sizeof(MinidumpByteArray) +\n                2 * sizeof(kName) + kValue1.size() + kValue2.size());\n\n  auto* list = MinidumpAnnotationListAtStart(string_file.string(), 2);\n  ASSERT_TRUE(list);\n  EXPECT_EQ(list->count, 2u);\n\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            list->objects[0].name),\n            kName);\n  EXPECT_EQ(list->objects[0].type, kType);\n  EXPECT_EQ(\n\n      MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value),\n      kValue1);\n\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            list->objects[1].name),\n            kName);\n  EXPECT_EQ(list->objects[1].type, kType);\n  EXPECT_EQ(\n\n      MinidumpByteArrayAtRVA(string_file.string(), list->objects[1].value),\n      kValue2);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_byte_array_writer.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_byte_array_writer.h\"\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpByteArrayWriter::MinidumpByteArrayWriter()\n    : minidump_array_(new MinidumpByteArray()) {}\n\nMinidumpByteArrayWriter::~MinidumpByteArrayWriter() = default;\n\nvoid MinidumpByteArrayWriter::set_data(const uint8_t* data, size_t size) {\n  data_.clear();\n  data_.insert(data_.begin(), data, data + size);\n}\n\nbool MinidumpByteArrayWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  size_t size = data_.size();\n  if (!AssignIfInRange(&minidump_array_->length, size)) {\n    LOG(ERROR) << \"data size \" << size << \" is out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpByteArrayWriter::SizeOfObject() {\n  DCHECK_EQ(state(), kStateFrozen);\n\n  return sizeof(*minidump_array_) + data_.size();\n}\n\nbool MinidumpByteArrayWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = minidump_array_.get();\n  iov.iov_len = sizeof(*minidump_array_);\n\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  if (!data_.empty()) {\n    iov.iov_base = data_.data();\n    iov.iov_len = data_.size();\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_byte_array_writer.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\n//! \\brief Writes a variable-length byte array for a minidump into a\n//!     \\sa MinidumpByteArray.\nclass MinidumpByteArrayWriter final : public internal::MinidumpWritable {\n public:\n  MinidumpByteArrayWriter();\n\n  MinidumpByteArrayWriter(const MinidumpByteArrayWriter&) = delete;\n  MinidumpByteArrayWriter& operator=(const MinidumpByteArrayWriter&) = delete;\n\n  ~MinidumpByteArrayWriter() override;\n\n  //! \\brief Sets the data to be written.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void set_data(const std::vector<uint8_t>& data) { data_ = data; }\n\n  //! \\brief Sets the data to be written.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void set_data(const uint8_t* data, size_t size);\n\n  //! \\brief Gets the data to be written.\n  //!\n  //! \\note Valid in any state.\n  const std::vector<uint8_t>& data() const { return data_; }\n\n protected:\n  // MinidumpWritable:\n\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  std::unique_ptr<MinidumpByteArray> minidump_array_;\n  std::vector<uint8_t> data_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_byte_array_writer_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_byte_array_writer.h\"\n\n#include <iterator>\n#include <memory>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(MinidumpByteArrayWriter, Write) {\n  const std::vector<uint8_t> kTests[] = {\n      {'h', 'e', 'l', 'l', 'o'},\n      {0x42, 0x99, 0x00, 0xbe},\n      {0x00},\n      {},\n  };\n\n  for (size_t i = 0; i < std::size(kTests); ++i) {\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS, i));\n\n    StringFile string_file;\n\n    crashpad::MinidumpByteArrayWriter writer;\n    writer.set_data(kTests[i]);\n    EXPECT_TRUE(writer.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(),\n              sizeof(MinidumpByteArray) + kTests[i].size());\n\n    auto byte_array = std::make_unique<MinidumpByteArray>();\n    EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n    string_file.Read(byte_array.get(), sizeof(*byte_array));\n\n    EXPECT_EQ(byte_array->length, kTests[i].size());\n\n    std::vector<uint8_t> data(byte_array->length);\n    string_file.Read(data.data(), byte_array->length);\n\n    EXPECT_EQ(data, kTests[i]);\n  }\n}\n\nTEST(MinidumpByteArrayWriter, SetData) {\n  const std::vector<uint8_t> kTests[] = {\n    {1, 2, 3, 4, 5},\n    {0x0},\n    {},\n  };\n\n  for (size_t i = 0; i < std::size(kTests); ++i) {\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS, i));\n\n    crashpad::MinidumpByteArrayWriter writer;\n    writer.set_data(kTests[i].data(), kTests[i].size());\n    EXPECT_EQ(writer.data(), kTests[i]);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_context.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_\n\n#include <stdint.h>\n\n#include \"base/compiler_specific.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"util/numeric/int128.h\"\n\nnamespace crashpad {\n\n//! \\brief Architecture-independent flags for `context_flags` fields in Minidump\n//!    context structures.\n//\n// https://zachsaw.blogspot.com/2010/11/wow64-bug-getthreadcontext-may-return.html#c5639760895973344002\nenum MinidumpContextFlags : uint32_t {\n  //! \\brief The thread was executing a trap handler in kernel mode\n  //!     (`CONTEXT_EXCEPTION_ACTIVE`).\n  //!\n  //! If this bit is set, it indicates that the context is from a thread that\n  //! was executing a trap handler in the kernel. This bit is only valid when\n  //! ::kMinidumpContextExceptionReporting is also set. This bit is only used on\n  //! Windows.\n  kMinidumpContextExceptionActive = 0x08000000,\n\n  //! \\brief The thread was executing a system call in kernel mode\n  //!     (`CONTEXT_SERVICE_ACTIVE`).\n  //!\n  //! If this bit is set, it indicates that the context is from a thread that\n  //! was executing a system call in the kernel. This bit is only valid when\n  //! ::kMinidumpContextExceptionReporting is also set. This bit is only used on\n  //! Windows.\n  kMinidumpContextServiceActive = 0x10000000,\n\n  //! \\brief Kernel-mode state reporting is desired\n  //!     (`CONTEXT_EXCEPTION_REQUEST`).\n  //!\n  //! This bit is not used in context structures containing snapshots of thread\n  //! CPU context. It used when calling `GetThreadContext()` on Windows to\n  //! specify that kernel-mode state reporting\n  //! (::kMinidumpContextExceptionReporting) is desired in the returned context\n  //! structure.\n  kMinidumpContextExceptionRequest = 0x40000000,\n\n  //! \\brief Kernel-mode state reporting is provided\n  //!     (`CONTEXT_EXCEPTION_REPORTING`).\n  //!\n  //! If this bit is set, it indicates that the bits indicating how the thread\n  //! had entered kernel mode (::kMinidumpContextExceptionActive and\n  //! ::kMinidumpContextServiceActive) are valid. This bit is only used on\n  //! Windows.\n  kMinidumpContextExceptionReporting = 0x80000000,\n};\n\n//! \\brief 32-bit x86-specifc flags for MinidumpContextX86::context_flags.\nenum MinidumpContextX86Flags : uint32_t {\n  //! \\brief Identifies the context structure as 32-bit x86. This is the same as\n  //!     `CONTEXT_i386` and `CONTEXT_i486` on Windows for this architecture.\n  kMinidumpContextX86 = 0x00010000,\n\n  //! \\brief Indicates the validity of control registers (`CONTEXT_CONTROL`).\n  //!\n  //! The `ebp`, `eip`, `cs`, `eflags`, `esp`, and `ss` fields are valid.\n  kMinidumpContextX86Control = kMinidumpContextX86 | 0x00000001,\n\n  //! \\brief Indicates the validity of non-control integer registers\n  //!     (`CONTEXT_INTEGER`).\n  //!\n  //! The `edi`, `esi`, `ebx`, `edx`, `ecx`, and `eax` fields are valid.\n  kMinidumpContextX86Integer = kMinidumpContextX86 | 0x00000002,\n\n  //! \\brief Indicates the validity of non-control segment registers\n  //!     (`CONTEXT_SEGMENTS`).\n  //!\n  //! The `gs`, `fs`, `es`, and `ds` fields are valid.\n  kMinidumpContextX86Segment = kMinidumpContextX86 | 0x00000004,\n\n  //! \\brief Indicates the validity of floating-point state\n  //!     (`CONTEXT_FLOATING_POINT`).\n  //!\n  //! The `fsave` field is valid. The `float_save` field is included in this\n  //! definition, but its members have no practical use asdie from `fsave`.\n  kMinidumpContextX86FloatingPoint = kMinidumpContextX86 | 0x00000008,\n\n  //! \\brief Indicates the validity of debug registers\n  //!     (`CONTEXT_DEBUG_REGISTERS`).\n  //!\n  //! The `dr0` through `dr3`, `dr6`, and `dr7` fields are valid.\n  kMinidumpContextX86Debug = kMinidumpContextX86 | 0x00000010,\n\n  //! \\brief Indicates the validity of extended registers in `fxsave` format\n  //!     (`CONTEXT_EXTENDED_REGISTERS`).\n  //!\n  //! The `extended_registers` field is valid and contains `fxsave` data.\n  kMinidumpContextX86Extended = kMinidumpContextX86 | 0x00000020,\n\n  //! \\brief Indicates the validity of `xsave` data (`CONTEXT_XSTATE`).\n  //!\n  //! The context contains `xsave` data. This is used with an extended context\n  //! structure not currently defined here.\n  kMinidumpContextX86Xstate = kMinidumpContextX86 | 0x00000040,\n\n  //! \\brief Indicates the validity of control, integer, and segment registers.\n  //!     (`CONTEXT_FULL`).\n  kMinidumpContextX86Full = kMinidumpContextX86Control |\n                            kMinidumpContextX86Integer |\n                            kMinidumpContextX86Segment,\n\n  //! \\brief Indicates the validity of all registers except `xsave` data.\n  //!     (`CONTEXT_ALL`).\n  kMinidumpContextX86All =\n      kMinidumpContextX86Full | kMinidumpContextX86FloatingPoint |\n      kMinidumpContextX86Debug | kMinidumpContextX86Extended,\n};\n\n//! \\brief A 32-bit x86 CPU context (register state) carried in a minidump file.\n//!\n//! This is analogous to the `CONTEXT` structure on Windows when targeting\n//! 32-bit x86, and the `WOW64_CONTEXT` structure when targeting an x86-family\n//! CPU, either 32- or 64-bit. This structure is used instead of `CONTEXT` or\n//! `WOW64_CONTEXT` to make it available when targeting other architectures.\n//!\n//! \\note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and\n//!     normally alias `dr6` and `dr7`, respectively. See Intel Software\n//!     Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052),\n//!     17.2.2 “Debug Registers DR4 and DR5”.\nstruct MinidumpContextX86 {\n  //! \\brief A bitfield composed of values of #MinidumpContextFlags and\n  //!     #MinidumpContextX86Flags.\n  //!\n  //! This field identifies the context structure as a 32-bit x86 CPU context,\n  //! and indicates which other fields in the structure are valid.\n  uint32_t context_flags;\n\n  uint32_t dr0;\n  uint32_t dr1;\n  uint32_t dr2;\n  uint32_t dr3;\n  uint32_t dr6;\n  uint32_t dr7;\n\n  // CPUContextX86::Fsave has identical layout to what the x86 CONTEXT structure\n  // places here.\n  CPUContextX86::Fsave fsave;\n  union {\n    uint32_t spare_0;  // As in the native x86 CONTEXT structure since Windows 8\n    uint32_t cr0_npx_state;  // As in WOW64_CONTEXT and older SDKs’ x86 CONTEXT\n  } float_save;\n\n  uint32_t gs;\n  uint32_t fs;\n  uint32_t es;\n  uint32_t ds;\n\n  uint32_t edi;\n  uint32_t esi;\n  uint32_t ebx;\n  uint32_t edx;\n  uint32_t ecx;\n  uint32_t eax;\n\n  uint32_t ebp;\n  uint32_t eip;\n  uint32_t cs;\n  uint32_t eflags;\n  uint32_t esp;\n  uint32_t ss;\n\n  // CPUContextX86::Fxsave has identical layout to what the x86 CONTEXT\n  // structure places here.\n  CPUContextX86::Fxsave fxsave;\n};\n\n//! \\brief x86_64-specific flags for MinidumpContextAMD64::context_flags.\nenum MinidumpContextAMD64Flags : uint32_t {\n  //! \\brief Identifies the context structure as x86_64. This is the same as\n  //!     `CONTEXT_AMD64` on Windows for this architecture.\n  kMinidumpContextAMD64 = 0x00100000,\n\n  //! \\brief Indicates the validity of control registers (`CONTEXT_CONTROL`).\n  //!\n  //! The `cs`, `ss`, `eflags`, `rsp`, and `rip` fields are valid.\n  kMinidumpContextAMD64Control = kMinidumpContextAMD64 | 0x00000001,\n\n  //! \\brief Indicates the validity of non-control integer registers\n  //!     (`CONTEXT_INTEGER`).\n  //!\n  //! The `rax`, `rcx`, `rdx`, `rbx`, `rbp`, `rsi`, `rdi`, and `r8` through\n  //! `r15` fields are valid.\n  kMinidumpContextAMD64Integer = kMinidumpContextAMD64 | 0x00000002,\n\n  //! \\brief Indicates the validity of non-control segment registers\n  //!     (`CONTEXT_SEGMENTS`).\n  //!\n  //! The `ds`, `es`, `fs`, and `gs` fields are valid.\n  kMinidumpContextAMD64Segment = kMinidumpContextAMD64 | 0x00000004,\n\n  //! \\brief Indicates the validity of floating-point state\n  //!     (`CONTEXT_FLOATING_POINT`).\n  //!\n  //! The `xmm0` through `xmm15` fields are valid.\n  kMinidumpContextAMD64FloatingPoint = kMinidumpContextAMD64 | 0x00000008,\n\n  //! \\brief Indicates the validity of debug registers\n  //!     (`CONTEXT_DEBUG_REGISTERS`).\n  //!\n  //! The `dr0` through `dr3`, `dr6`, and `dr7` fields are valid.\n  kMinidumpContextAMD64Debug = kMinidumpContextAMD64 | 0x00000010,\n\n  //! \\brief Indicates the validity of `xsave` data (`CONTEXT_XSTATE`).\n  //!\n  //! The context contains `xsave` data. This is used with an extended context\n  //! structure which is partly implemented for CET state only.\n  kMinidumpContextAMD64Xstate = kMinidumpContextAMD64 | 0x00000040,\n\n  //! \\brief Indicates the validity of control, integer, and floating-point\n  //!     registers (`CONTEXT_FULL`).\n  kMinidumpContextAMD64Full = kMinidumpContextAMD64Control |\n                              kMinidumpContextAMD64Integer |\n                              kMinidumpContextAMD64FloatingPoint,\n\n  //! \\brief Indicates the validity of all registers except `xsave` data\n  //!     (`CONTEXT_ALL`).\n  kMinidumpContextAMD64All = kMinidumpContextAMD64Full |\n                             kMinidumpContextAMD64Segment |\n                             kMinidumpContextAMD64Debug,\n};\n\n//! \\brief An x86_64 (AMD64) CPU context (register state) carried in a minidump\n//!     file.\n//!\n//! This is analogous to the `CONTEXT` structure on Windows when targeting\n//! x86_64. This structure is used instead of `CONTEXT` to make it available\n//! when targeting other architectures.\n//!\n//! \\note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and\n//!     normally alias `dr6` and `dr7`, respectively. See Intel Software\n//!     Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052),\n//!     17.2.2 “Debug Registers DR4 and DR5”.\nstruct alignas(16) MinidumpContextAMD64 {\n  //! \\brief Register parameter home address.\n  //!\n  //! On Windows, this field may contain the “home” address (on-stack, in the\n  //! shadow area) of a parameter passed by register. This field is present for\n  //! convenience but is not necessarily populated, even if a corresponding\n  //! parameter was passed by register.\n  //!\n  //! \\{\n  uint64_t p1_home;\n  uint64_t p2_home;\n  uint64_t p3_home;\n  uint64_t p4_home;\n  uint64_t p5_home;\n  uint64_t p6_home;\n  //! \\}\n\n  //! \\brief A bitfield composed of values of #MinidumpContextFlags and\n  //!     #MinidumpContextAMD64Flags.\n  //!\n  //! This field identifies the context structure as an x86_64 CPU context, and\n  //! indicates which other fields in the structure are valid.\n  uint32_t context_flags;\n\n  uint32_t mx_csr;\n\n  uint16_t cs;\n  uint16_t ds;\n  uint16_t es;\n  uint16_t fs;\n  uint16_t gs;\n  uint16_t ss;\n\n  uint32_t eflags;\n\n  uint64_t dr0;\n  uint64_t dr1;\n  uint64_t dr2;\n  uint64_t dr3;\n  uint64_t dr6;\n  uint64_t dr7;\n\n  uint64_t rax;\n  uint64_t rcx;\n  uint64_t rdx;\n  uint64_t rbx;\n  uint64_t rsp;\n  uint64_t rbp;\n  uint64_t rsi;\n  uint64_t rdi;\n  uint64_t r8;\n  uint64_t r9;\n  uint64_t r10;\n  uint64_t r11;\n  uint64_t r12;\n  uint64_t r13;\n  uint64_t r14;\n  uint64_t r15;\n\n  uint64_t rip;\n\n  // CPUContextX86_64::Fxsave has identical layout to what the x86_64 CONTEXT\n  // structure places here.\n  CPUContextX86_64::Fxsave fxsave;\n\n  uint128_struct vector_register[26];\n  uint64_t vector_control;\n\n  //! \\brief Model-specific debug extension register.\n  //!\n  //! See Intel Software Developer’s Manual, Volume 3B: System Programming, Part\n  //! 2 (253669-051), 17.4 “Last Branch, Interrupt, and Exception Recording\n  //! Overview”, and AMD Architecture Programmer’s Manual, Volume 2: System\n  //! Programming (24593-3.24), 13.1.6 “Control-Transfer Breakpoint Features”.\n  //!\n  //! \\{\n  uint64_t debug_control;\n  uint64_t last_branch_to_rip;\n  uint64_t last_branch_from_rip;\n  uint64_t last_exception_to_rip;\n  uint64_t last_exception_from_rip;\n  //! \\}\n};\n\n//! \\brief 32-bit ARM-specifc flags for MinidumpContextARM::context_flags.\nenum MinidumpContextARMFlags : uint32_t {\n  //! \\brief Identifies the context structure as 32-bit ARM.\n  kMinidumpContextARM = 0x40000000,\n\n  //! \\brief Indicates the validity of integer regsiters.\n  //!\n  //! Regsiters `r0`-`r15` and `cpsr` are valid.\n  kMinidumpContextARMInteger = kMinidumpContextARM | 0x00000002,\n\n  //! \\brief Inidicates the validity of VFP regsiters.\n  //!\n  //! Registers `d0`-`d31` and `fpscr` are valid.\n  kMinidumpContextARMVFP = kMinidumpContextARM | 0x00000004,\n\n  //! \\brief Indicates the validity of all registers.\n  kMinidumpContextARMAll = kMinidumpContextARMInteger | kMinidumpContextARMVFP,\n};\n\n//! \\brief A 32-bit ARM CPU context (register state) carried in a minidump file.\nstruct MinidumpContextARM {\n  //! \\brief A bitfield composed of values of #MinidumpContextFlags and\n  //!     #MinidumpContextARMFlags.\n  //!\n  //! This field identifies the context structure as a 32-bit ARM CPU context,\n  //! and indicates which other fields in the structure are valid.\n  uint32_t context_flags;\n\n  //! \\brief General-purpose registers `r0`-`r15`.\n  uint32_t regs[11];\n  uint32_t fp;  // r11\n  uint32_t ip;  // r12\n  uint32_t sp;  // r13\n  uint32_t lr;  // r14\n  uint32_t pc;  // r15\n\n  //! \\brief Current program status register.\n  uint32_t cpsr;\n\n  //! \\brief Floating-point status and control register.\n  uint32_t fpscr;\n\n  //! \\brief VFP registers `d0`-`d31`.\n  uint64_t vfp[32];\n\n  //! \\brief This space is unused. It is included for compatibility with\n  //!     breakpad (which also doesn't use it).\n  uint32_t extra[8];\n};\n\n//! \\brief CONTEXT_CHUNK\nstruct MinidumpContextChunk {\n  int32_t offset;\n  uint32_t size;\n};\n\n//! \\brief CONTEXT_EX\nstruct MinidumpContextExHeader {\n  MinidumpContextChunk all;\n  MinidumpContextChunk legacy;\n  MinidumpContextChunk xstate;\n};\n\n//! \\brief XSAVE_AREA_HEADER\nstruct MinidumpXSaveAreaHeader {\n  uint64_t mask;\n  uint64_t compaction_mask;\n  uint64_t xsave_header_reserved[6];\n};\n\n//! \\brief Offset of first xsave feature in the full extended context.\n//!\n//! This is used to calculate the final size of the extended context, and\n//! can be validated by calling InitializeContext2 with one XSTATE feature,\n//! and LocateXStateFeature to determine the first offset.\n//! Also see “MANAGING STATE USING THE XSAVE FEATURE SET”, Ch. 13, Intel SDM.\nconstexpr uint32_t kMinidumpAMD64XSaveOffset = 0x550;\n\n//! \\brief Offset of first xsave feature within the extended context area.\n//!\n//! 0x240 is the size of the legacy area (512) + the xsave header(64 bytes)\n//! Intel SDM 13.4.1. This is not where the item is in the extended compacted\n//! context, but is the offset recorded in the minidump. It needs to be correct\n//! there. See https://windows-internals.com/cet-on-windows/ for some discussion\n//! “CONTEXT_XSTATE: Extended processor state chunk. The state is stored in the\n//! same format the XSAVE operation stores it with exception of the first 512\n//! bytes, i.e. starting from XSAVE_AREA_HEADER.” This may vary by cpuid.\nconstexpr uint32_t kXSaveAreaFirstOffset = 0x240;\n\n//! \\brief XSAVE_CET_U_FORMAT\nstruct MinidumpAMD64XSaveFormatCetU {\n  uint64_t cetmsr;\n  uint64_t ssp;\n};\n\n//! \\brief 64-bit ARM-specifc flags for MinidumpContextARM64::context_flags.\nenum MinidumpContextARM64Flags : uint32_t {\n  //! \\brief Identifies the context structure as 64-bit ARM.\n  kMinidumpContextARM64 = 0x00400000,\n\n  //! \\brief Indicates the validity of control registers.\n  //!\n  //! Registers `fp`, `lr`, `sp`, `pc`, and `cpsr`.\n  kMinidumpContextARM64Control = kMinidumpContextARM64 | 0x00000001,\n\n  //! \\brief Indicates the validty of integer registers.\n  //!\n  //! Registers `x0`-`x28`.\n  kMinidumpContextARM64Integer = kMinidumpContextARM64 | 0x00000002,\n\n  //! \\brief Indicates the validity of fpsimd registers.\n  //!\n  //! Registers `v0`-`v31`, `fpsr`, and `fpcr` are valid.\n  kMinidumpContextARM64Fpsimd = kMinidumpContextARM64 | 0x00000004,\n\n  //! \\brief Indicates the validity of debug registers.\n  //!\n  //! `bcr`, `bvr`, `wcr`, and `wvr` are valid.\n  kMinidumpContextARM64Debug = kMinidumpContextARM64 | 0x00000008,\n\n  //! \\brief Indicates the validity of control, integer and floating point\n  //!     registers.\n  kMinidumpContextARM64Full = kMinidumpContextARM64Control |\n                              kMinidumpContextARM64Integer |\n                              kMinidumpContextARM64Fpsimd,\n\n  //! \\brief Indicates the validity of all registers.\n  kMinidumpContextARM64All =\n      kMinidumpContextARM64Full | kMinidumpContextARM64Debug,\n};\n\n//! \\brief A 64-bit ARM CPU context (register state) carried in a minidump file.\nstruct MinidumpContextARM64 {\n  uint32_t context_flags;\n\n  //! \\brief Current program status register.\n  uint32_t cpsr;\n\n  //! \\brief General-purpose registers `x0`-`x28`.\n  uint64_t regs[29];\n\n  //! \\brief Frame pointer or `x29`.\n  uint64_t fp;\n\n  //! \\brief Link register or `x30`.\n  uint64_t lr;\n\n  //! \\brief Stack pointer or `x31`.\n  uint64_t sp;\n\n  //! \\brief Program counter.\n  uint64_t pc;\n\n  //! \\brief NEON registers `v0`-`v31`.\n  uint128_struct fpsimd[32];\n\n  //! \\brief Floating-point control register.\n  uint32_t fpcr;\n\n  //! \\brief Floating-point status register.\n  uint32_t fpsr;\n\n  //! \\brief Debug registers.\n  uint32_t bcr[8];\n  uint64_t bvr[8];\n  uint32_t wcr[2];\n  uint64_t wvr[2];\n};\n\n//! \\brief 32bit MIPS-specifc flags for MinidumpContextMIPS::context_flags.\n//! Based on minidump_cpu_mips.h from breakpad\nenum MinidumpContextMIPSFlags : uint32_t {\n  //! \\brief Identifies the context structure as MIPSEL.\n  kMinidumpContextMIPS = 0x00040000,\n\n  //! \\brief Indicates the validity of integer registers.\n  //!\n  //! Registers `0`-`31`, `mdhi`, `mdlo`, `epc`, `badvaddr`, `status` and\n  //! `cause` are valid.\n  kMinidumpContextMIPSInteger = kMinidumpContextMIPS | 0x00000002,\n\n  //! \\brief Indicates the validity of floating point registers.\n  //!\n  //! Floating point registers `0`-`31`, `fpcsr` and `fir` are valid\n  kMinidumpContextMIPSFloatingPoint = kMinidumpContextMIPS | 0x00000004,\n\n  //! \\brief Indicates the validity of DSP registers.\n  //!\n  //! Registers `hi0`-`hi2`, `lo0`-`lo2` and `dsp_control` are valid\n  kMinidumpContextMIPSDSP = kMinidumpContextMIPS | 0x00000008,\n\n  //! \\brief Indicates the validity of all registers.\n  kMinidumpContextMIPSAll = kMinidumpContextMIPSInteger |\n                            kMinidumpContextMIPSFloatingPoint |\n                            kMinidumpContextMIPSDSP,\n};\n\n//! \\brief A 32bit MIPS CPU context (register state) carried in a minidump file.\nstruct MinidumpContextMIPS {\n  uint32_t context_flags;\n\n  //! \\brief This padding field is included for breakpad compatibility.\n  uint32_t _pad0;\n  //! \\brief General purpose registers `0`-`31`.\n  uint64_t regs[32];\n\n  //! \\brief Multiply/divide result.\n  uint64_t mdhi, mdlo;\n\n  //! \\brief DSP registers.\n  uint32_t hi[3];\n  uint32_t lo[3];\n  uint32_t dsp_control;\n  //! \\brief This padding field is included for breakpad compatibility.\n  uint32_t _pad1;\n\n  // \\brief cp0 registers.\n  uint64_t epc;\n  uint64_t badvaddr;\n  uint32_t status;\n  uint32_t cause;\n\n  //! \\brief FPU registers.\n  union {\n    struct {\n      float _fp_fregs;\n      uint32_t _fp_pad;\n    } fregs[32];\n    double dregs[32];\n  } fpregs;\n\n  //! \\brief FPU status register.\n  uint32_t fpcsr;\n  //! \\brief FPU implementation register.\n  uint32_t fir;\n};\n\n//! \\brief 64bit MIPS-specifc flags for MinidumpContextMIPS64::context_flags.\n//! Based on minidump_cpu_mips.h from breakpad\nenum MinidumpContextMIPS64Flags : uint32_t {\n  //! \\brief Identifies the context structure as MIPS64EL.\n  kMinidumpContextMIPS64 = 0x00080000,\n\n  //! \\brief Indicates the validity of integer registers.\n  //!\n  //! Registers `0`-`31`, `mdhi`, `mdlo`, `epc`, `badvaddr`, `status` and\n  //! `cause` are valid.\n  kMinidumpContextMIPS64Integer = kMinidumpContextMIPS64 | 0x00000002,\n\n  //! \\brief Indicates the validity of floating point registers.\n  //!\n  //! Floating point registers `0`-`31`, `fpcsr` and `fir` are valid\n  kMinidumpContextMIPS64FloatingPoint = kMinidumpContextMIPS64 | 0x00000004,\n\n  //! \\brief Indicates the validity of DSP registers.\n  //!\n  //! Registers `hi0`-`hi2`, `lo0`-`lo2` and `dsp_control` are valid.\n  kMinidumpContextMIPS64DSP = kMinidumpContextMIPS64 | 0x00000008,\n\n  //! \\brief Indicates the validity of all registers.\n  kMinidumpContextMIPS64All = kMinidumpContextMIPS64Integer |\n                              kMinidumpContextMIPS64FloatingPoint |\n                              kMinidumpContextMIPS64DSP,\n};\n\n//! \\brief A 32bit MIPS CPU context (register state) carried in a minidump file.\nstruct MinidumpContextMIPS64 {\n  uint64_t context_flags;\n\n  //! \\brief General purpose registers.\n  uint64_t regs[32];\n\n  //! \\brief Multiply/divide result.\n  uint64_t mdhi, mdlo;\n\n  //! \\brief DSP registers.\n  uint64_t hi[3];\n  uint64_t lo[3];\n  uint64_t dsp_control;\n\n  //! \\brief cp0 registers.\n  uint64_t epc;\n  uint64_t badvaddr;\n  uint64_t status;\n  uint64_t cause;\n\n  //! \\brief FPU registers.\n  union {\n    struct {\n      float _fp_fregs;\n      uint32_t _fp_pad;\n    } fregs[32];\n    double dregs[32];\n  } fpregs;\n\n  //! \\brief FPU status register.\n  uint64_t fpcsr;\n  //! \\brief FPU implementation register.\n  uint64_t fir;\n};\n\n//! \\brief 64-bit RISCV-specific flags for\n//! MinidumpContextRISCV64::context_flags.\nenum MinidumpContextRISCV64Flags : uint32_t {\n  //! \\brief Identifies the context structure as RISCV64.\n  kMinidumpContextRISCV64 = 0x08000000,\n\n  //! \\brief Indicates the validity of integer registers.\n  //!\n  //! Registers 'pc' and `x1`-`x31` are valid.\n  kMinidumpContextRISCV64Integer = kMinidumpContextRISCV64 | 0x00000001,\n\n  //! \\brief Indicates the validity of floating point registers.\n  //!\n  //! Floating point registers `f0`-`f31` are valid.\n  kMinidumpContextRISCV64FloatingPoint = kMinidumpContextRISCV64 | 0x00000002,\n\n  //! \\brief Indicates the validity of all registers.\n  kMinidumpContextRISCV64All = kMinidumpContextRISCV64Integer |\n                               kMinidumpContextRISCV64FloatingPoint,\n};\n\n//! \\brief A 64-bit RISC-V CPU context (register state) carried in a minidump\n//! file.\n//!\n//! This structure is versioned. Increment |kVersion| when changing this\n//! structure.\nstruct MinidumpContextRISCV64 {\n\n  //! \\brief The structure’s currently-defined version number.\n  static constexpr uint32_t kVersion = 1;\n\n  //! \\brief Indicates the validity of fields in this structure.\n  uint32_t context_flags;\n\n  //! \\brief The structure’s version number.\n  uint32_t version;\n\n  //! \\brief The program counter register.\n  uint64_t pc;\n\n  //! \\brief The integer registers, x1 through x31.\n  uint64_t regs[31];\n\n  //! \\brief The floating point registers.\n  uint64_t fpregs[32];\n\n  //! \\brief The floating point control and status register.\n  uint32_t fcsr;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_\n"
  },
  {
    "path": "minidump/minidump_context_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_context_writer.h\"\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <string.h>\n\n#include \"base/check_op.h\"\n#include \"base/compiler_specific.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/stdlib/aligned_allocator.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Sanity-check complex structures to ensure interoperability.\nstatic_assert(sizeof(MinidumpContextX86) == 716, \"MinidumpContextX86 size\");\nstatic_assert(sizeof(MinidumpContextAMD64) == 1232,\n              \"MinidumpContextAMD64 size\");\n\n// These structures can also be checked against definitions in the Windows SDK.\n#if BUILDFLAG(IS_WIN)\n#if defined(ARCH_CPU_X86_FAMILY)\nstatic_assert(sizeof(MinidumpContextX86) == sizeof(WOW64_CONTEXT),\n              \"WOW64_CONTEXT size\");\n#if defined(ARCH_CPU_X86)\nstatic_assert(sizeof(MinidumpContextX86) == sizeof(CONTEXT), \"CONTEXT size\");\n#elif defined(ARCH_CPU_X86_64)\nstatic_assert(sizeof(MinidumpContextAMD64) == sizeof(CONTEXT), \"CONTEXT size\");\n#endif\n#endif  // ARCH_CPU_X86_FAMILY\n#endif  // BUILDFLAG(IS_WIN)\n\n}  // namespace\n\nMinidumpContextWriter::~MinidumpContextWriter() {\n}\n\n// static\nstd::unique_ptr<MinidumpContextWriter>\nMinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) {\n  std::unique_ptr<MinidumpContextWriter> context;\n\n  switch (context_snapshot->architecture) {\n    case kCPUArchitectureX86: {\n      MinidumpContextX86Writer* context_x86 = new MinidumpContextX86Writer();\n      context.reset(context_x86);\n      context_x86->InitializeFromSnapshot(context_snapshot->x86);\n      break;\n    }\n\n    case kCPUArchitectureX86_64: {\n      MinidumpContextAMD64Writer* context_amd64 =\n          new MinidumpContextAMD64Writer();\n      context.reset(context_amd64);\n      context_amd64->InitializeFromSnapshot(context_snapshot->x86_64);\n      break;\n    }\n\n    case kCPUArchitectureARM: {\n      context = std::make_unique<MinidumpContextARMWriter>();\n      reinterpret_cast<MinidumpContextARMWriter*>(context.get())\n          ->InitializeFromSnapshot(context_snapshot->arm);\n      break;\n    }\n\n    case kCPUArchitectureARM64: {\n      context = std::make_unique<MinidumpContextARM64Writer>();\n      reinterpret_cast<MinidumpContextARM64Writer*>(context.get())\n          ->InitializeFromSnapshot(context_snapshot->arm64);\n      break;\n    }\n\n    case kCPUArchitectureMIPSEL: {\n      context = std::make_unique<MinidumpContextMIPSWriter>();\n      reinterpret_cast<MinidumpContextMIPSWriter*>(context.get())\n          ->InitializeFromSnapshot(context_snapshot->mipsel);\n      break;\n    }\n\n    case kCPUArchitectureMIPS64EL: {\n      context = std::make_unique<MinidumpContextMIPS64Writer>();\n      reinterpret_cast<MinidumpContextMIPS64Writer*>(context.get())\n          ->InitializeFromSnapshot(context_snapshot->mips64);\n      break;\n    }\n\n    case kCPUArchitectureRISCV64: {\n      context = std::make_unique<MinidumpContextRISCV64Writer>();\n      reinterpret_cast<MinidumpContextRISCV64Writer*>(context.get())\n          ->InitializeFromSnapshot(context_snapshot->riscv64);\n      break;\n    }\n\n    default: {\n      LOG(ERROR) << \"unknown context architecture \"\n                 << context_snapshot->architecture;\n      break;\n    }\n  }\n\n  return context;\n}\n\nsize_t MinidumpContextWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return ContextSize();\n}\n\nsize_t MinidumpContextWriter::FreezeAndGetSizeOfObject() {\n  Freeze();\n  return SizeOfObject();\n}\n\nMinidumpContextX86Writer::MinidumpContextX86Writer()\n    : MinidumpContextWriter(), context_() {\n  context_.context_flags = kMinidumpContextX86;\n}\n\nMinidumpContextX86Writer::~MinidumpContextX86Writer() {\n}\n\nvoid MinidumpContextX86Writer::InitializeFromSnapshot(\n    const CPUContextX86* context_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(context_.context_flags, kMinidumpContextX86);\n\n  context_.context_flags = kMinidumpContextX86All;\n\n  context_.dr0 = context_snapshot->dr0;\n  context_.dr1 = context_snapshot->dr1;\n  context_.dr2 = context_snapshot->dr2;\n  context_.dr3 = context_snapshot->dr3;\n  context_.dr6 = context_snapshot->dr6;\n  context_.dr7 = context_snapshot->dr7;\n\n  // The contents of context_.fsave effectively alias everything in\n  // context_.fxsave that’s related to x87 FPU state. context_.fsave doesn’t\n  // carry state specific to SSE (or later), such as mxcsr and the xmm\n  // registers.\n  CPUContextX86::FxsaveToFsave(context_snapshot->fxsave, &context_.fsave);\n\n  context_.gs = context_snapshot->gs;\n  context_.fs = context_snapshot->fs;\n  context_.es = context_snapshot->es;\n  context_.ds = context_snapshot->ds;\n  context_.edi = context_snapshot->edi;\n  context_.esi = context_snapshot->esi;\n  context_.ebx = context_snapshot->ebx;\n  context_.edx = context_snapshot->edx;\n  context_.ecx = context_snapshot->ecx;\n  context_.eax = context_snapshot->eax;\n  context_.ebp = context_snapshot->ebp;\n  context_.eip = context_snapshot->eip;\n  context_.cs = context_snapshot->cs;\n  context_.eflags = context_snapshot->eflags;\n  context_.esp = context_snapshot->esp;\n  context_.ss = context_snapshot->ss;\n\n  // This is effectively a memcpy() of a big structure.\n  context_.fxsave = context_snapshot->fxsave;\n}\n\nbool MinidumpContextX86Writer::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return file_writer->Write(&context_, sizeof(context_));\n}\n\nsize_t MinidumpContextX86Writer::ContextSize() const {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(context_);\n}\n\nstatic_assert(alignof(MinidumpContextAMD64) >= 16,\n              \"MinidumpContextAMD64 alignment\");\nstatic_assert(alignof(MinidumpContextAMD64Writer) >=\n                  alignof(MinidumpContextAMD64),\n              \"MinidumpContextAMD64Writer alignment\");\n\nMinidumpContextAMD64Writer::MinidumpContextAMD64Writer()\n    : MinidumpContextWriter(), context_() {\n  context_.context_flags = kMinidumpContextAMD64;\n}\n\nMinidumpContextAMD64Writer::~MinidumpContextAMD64Writer() {\n}\n\n// static\nvoid* MinidumpContextAMD64Writer::operator new(size_t size) {\n  // MinidumpContextAMD64 requests an alignment of 16, which can be larger than\n  // what standard new provides. This may trigger MSVC warning C4316. As a\n  // workaround to this language deficiency, provide a custom allocation\n  // function to allocate a block meeting the alignment requirement.\n  return AlignedAllocate(alignof(MinidumpContextAMD64Writer), size);\n}\n\n// static\nvoid MinidumpContextAMD64Writer::operator delete(void* pointer) {\n  return AlignedFree(pointer);\n}\n\nvoid MinidumpContextAMD64Writer::InitializeFromSnapshot(\n    const CPUContextX86_64* context_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(context_.context_flags, kMinidumpContextAMD64);\n\n  if (context_snapshot->xstate.enabled_features != 0) {\n    // Extended context.\n    context_.context_flags =\n        kMinidumpContextAMD64All | kMinidumpContextAMD64Xstate;\n  } else {\n    // Fixed size context - no xsave components.\n    context_.context_flags = kMinidumpContextAMD64All;\n  }\n\n  context_.mx_csr = context_snapshot->fxsave.mxcsr;\n  context_.cs = context_snapshot->cs;\n  context_.fs = context_snapshot->fs;\n  context_.gs = context_snapshot->gs;\n  // The top 32 bits of rflags are reserved/unused.\n  context_.eflags = static_cast<uint32_t>(context_snapshot->rflags);\n  context_.dr0 = context_snapshot->dr0;\n  context_.dr1 = context_snapshot->dr1;\n  context_.dr2 = context_snapshot->dr2;\n  context_.dr3 = context_snapshot->dr3;\n  context_.dr6 = context_snapshot->dr6;\n  context_.dr7 = context_snapshot->dr7;\n  context_.rax = context_snapshot->rax;\n  context_.rcx = context_snapshot->rcx;\n  context_.rdx = context_snapshot->rdx;\n  context_.rbx = context_snapshot->rbx;\n  context_.rsp = context_snapshot->rsp;\n  context_.rbp = context_snapshot->rbp;\n  context_.rsi = context_snapshot->rsi;\n  context_.rdi = context_snapshot->rdi;\n  context_.r8 = context_snapshot->r8;\n  context_.r9 = context_snapshot->r9;\n  context_.r10 = context_snapshot->r10;\n  context_.r11 = context_snapshot->r11;\n  context_.r12 = context_snapshot->r12;\n  context_.r13 = context_snapshot->r13;\n  context_.r14 = context_snapshot->r14;\n  context_.r15 = context_snapshot->r15;\n  context_.rip = context_snapshot->rip;\n\n  // This is effectively a memcpy() of a big structure.\n  context_.fxsave = context_snapshot->fxsave;\n\n  // If XSave features are being recorded store in xsave_entries in xcomp_bv\n  // order. We will not see features we do not support as we provide flags\n  // to the OS when first obtaining a snapshot.\n  if (context_snapshot->xstate.enabled_features & XSTATE_MASK_CET_U) {\n    auto cet_u = std::make_unique<MinidumpXSaveAMD64CetU>();\n    cet_u->InitializeFromSnapshot(context_snapshot);\n    xsave_entries_.push_back(std::move(cet_u));\n  }\n}\n\nsize_t MinidumpContextAMD64Writer::Alignment() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // Match the alignment of MinidumpContextAMD64.\n  return 16;\n}\n\nbool MinidumpContextAMD64Writer::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  // Note: all sizes here come from our constants, not from untrustworthy data.\n  std::vector<unsigned char> data(ContextSize());\n  unsigned char* const buf = data.data();\n\n  // CONTEXT always comes first.\n  DCHECK_LE(sizeof(context_), data.size());\n  memcpy(buf, &context_, sizeof(context_));\n\n  if (xsave_entries_.size() > 0) {\n    MinidumpContextExHeader context_ex = {{0, 0}, {0, 0}, {0, 0}};\n    MinidumpXSaveAreaHeader xsave_header = {0, 0, {}};\n\n    // CONTEXT_EX goes directly after the CONTEXT. |offset| is relative to\n    // &CONTEXT_EX.\n    context_ex.all.offset = -static_cast<int32_t>(sizeof(context_));\n    context_ex.all.size = static_cast<uint32_t>(ContextSize());\n    context_ex.legacy.offset = context_ex.all.offset;\n    context_ex.legacy.size = sizeof(context_);\n    // Then... there is a gap.\n    //\n    // In the compacted format the XSave area header goes just before\n    // the first xsave entry.  It has a total size given by the header\n    // + (padded) sizes of all the entries.\n    context_ex.xstate.offset = static_cast<int32_t>(\n        kMinidumpAMD64XSaveOffset - sizeof(MinidumpXSaveAreaHeader) -\n        sizeof(context_));\n    context_ex.xstate.size =\n        static_cast<uint32_t>(sizeof(MinidumpXSaveAreaHeader) + ContextSize() -\n                              kMinidumpAMD64XSaveOffset);\n\n    // Store CONTEXT_EX now it is complete.\n    DCHECK_LE(sizeof(context_) + sizeof(context_ex), data.size());\n    memcpy(&buf[sizeof(context_)], &context_ex, sizeof(context_ex));\n\n    // Calculate flags for xsave header & write entries (they will be\n    // *after* the xsave header).\n    size_t cursor = kMinidumpAMD64XSaveOffset;\n    for (auto const& entry : xsave_entries_) {\n      xsave_header.mask |= 1ull << entry->XCompBVBit();\n      DCHECK_LE(cursor + entry->Size(), data.size());\n      entry->Copy(&buf[cursor]);\n      cursor += entry->Size();\n    }\n\n    xsave_header.compaction_mask =\n        xsave_header.mask | XSTATE_COMPACTION_ENABLE_MASK;\n\n    // Store xsave header at its calculated offset. It is before the entries\n    // above, but we need to add the |mask| bits before writing it.\n    DCHECK_LE(\n        context_ex.xstate.offset + sizeof(context_) + sizeof(xsave_header),\n        data.size());\n    memcpy(&buf[context_ex.xstate.offset + sizeof(context_)],\n           &xsave_header,\n           sizeof(xsave_header));\n  }\n\n  if (!file_writer->Write(data.data(), data.size()))\n    return false;\n\n  return true;\n}\n\nsize_t MinidumpContextAMD64Writer::ContextSize() const {\n  DCHECK_GE(state(), kStateFrozen);\n  if (xsave_entries_.size() == 0) {\n    return sizeof(context_);\n  } else {\n    DCHECK_EQ(context_.context_flags,\n              kMinidumpContextAMD64All | kMinidumpContextAMD64Xstate);\n    DCHECK(xsave_entries_.size() != 0);\n    size_t size = kMinidumpAMD64XSaveOffset;\n    for (auto& entry : xsave_entries_) {\n      size += entry->Size();\n    }\n    return size;\n  }\n}\n\nbool MinidumpXSaveAMD64CetU::InitializeFromSnapshot(\n    const CPUContextX86_64* context_snapshot) {\n  // Exception records do not carry CET registers but we have to provide the\n  // same shaped context for threads and exception contexts, so both 0 (no ssp\n  // present) and 1 (ssp present) are expected.\n  DCHECK(context_snapshot->xstate.cet_u.cetmsr == 0ull ||\n         context_snapshot->xstate.cet_u.cetmsr == 1ull);\n  cet_u_.cetmsr = context_snapshot->xstate.cet_u.cetmsr;\n  cet_u_.ssp = context_snapshot->xstate.cet_u.ssp;\n  return true;\n}\n\nbool MinidumpXSaveAMD64CetU::Copy(void* dst) const {\n  memcpy(dst, &cet_u_, sizeof(cet_u_));\n  return true;\n}\n\nMinidumpContextARMWriter::MinidumpContextARMWriter()\n    : MinidumpContextWriter(), context_() {\n  context_.context_flags = kMinidumpContextARM;\n}\n\nMinidumpContextARMWriter::~MinidumpContextARMWriter() = default;\n\nvoid MinidumpContextARMWriter::InitializeFromSnapshot(\n    const CPUContextARM* context_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(context_.context_flags, kMinidumpContextARM);\n\n  context_.context_flags = kMinidumpContextARMAll;\n\n  static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),\n                \"GPRS size mismatch\");\n  memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));\n  context_.fp = context_snapshot->fp;\n  context_.ip = context_snapshot->ip;\n  context_.sp = context_snapshot->sp;\n  context_.lr = context_snapshot->lr;\n  context_.pc = context_snapshot->pc;\n  context_.cpsr = context_snapshot->cpsr;\n\n  context_.fpscr = context_snapshot->vfp_regs.fpscr;\n  static_assert(sizeof(context_.vfp) == sizeof(context_snapshot->vfp_regs.vfp),\n                \"VFP size mismatch\");\n  memcpy(context_.vfp, context_snapshot->vfp_regs.vfp, sizeof(context_.vfp));\n\n  memset(context_.extra, 0, sizeof(context_.extra));\n}\n\nbool MinidumpContextARMWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  return file_writer->Write(&context_, sizeof(context_));\n}\n\nsize_t MinidumpContextARMWriter::ContextSize() const {\n  DCHECK_GE(state(), kStateFrozen);\n  return sizeof(context_);\n}\n\nMinidumpContextARM64Writer::MinidumpContextARM64Writer()\n    : MinidumpContextWriter(), context_() {\n  context_.context_flags = kMinidumpContextARM64;\n}\n\nMinidumpContextARM64Writer::~MinidumpContextARM64Writer() = default;\n\nvoid MinidumpContextARM64Writer::InitializeFromSnapshot(\n    const CPUContextARM64* context_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(context_.context_flags, kMinidumpContextARM64);\n\n  context_.context_flags = kMinidumpContextARM64Full;\n\n  static_assert(\n      sizeof(context_.regs) == sizeof(context_snapshot->regs) -\n                                   2 * sizeof(context_snapshot->regs[0]),\n      \"GPRs size mismatch\");\n  memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));\n  context_.fp = context_snapshot->regs[29];\n  context_.lr = context_snapshot->regs[30];\n  context_.sp = context_snapshot->sp;\n  context_.pc = context_snapshot->pc;\n  context_.cpsr = context_snapshot->spsr;\n\n  static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd),\n                \"FPSIMD size mismatch\");\n  memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd));\n  context_.fpcr = context_snapshot->fpcr;\n  context_.fpsr = context_snapshot->fpsr;\n\n  memset(context_.bcr, 0, sizeof(context_.bcr));\n  memset(context_.bvr, 0, sizeof(context_.bvr));\n  memset(context_.wcr, 0, sizeof(context_.wcr));\n  memset(context_.wvr, 0, sizeof(context_.wvr));\n}\n\nbool MinidumpContextARM64Writer::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  return file_writer->Write(&context_, sizeof(context_));\n}\n\nsize_t MinidumpContextARM64Writer::ContextSize() const {\n  DCHECK_GE(state(), kStateFrozen);\n  return sizeof(context_);\n}\n\nMinidumpContextMIPSWriter::MinidumpContextMIPSWriter()\n    : MinidumpContextWriter(), context_() {\n  context_.context_flags = kMinidumpContextMIPS;\n}\n\nMinidumpContextMIPSWriter::~MinidumpContextMIPSWriter() = default;\n\nvoid MinidumpContextMIPSWriter::InitializeFromSnapshot(\n    const CPUContextMIPS* context_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS);\n\n  context_.context_flags = kMinidumpContextMIPSAll;\n\n  static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),\n                \"GPRs size mismatch\");\n  memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));\n  context_.mdhi = context_snapshot->mdhi;\n  context_.mdlo = context_snapshot->mdlo;\n  context_.epc = context_snapshot->cp0_epc;\n  context_.badvaddr = context_snapshot->cp0_badvaddr;\n  context_.status = context_snapshot->cp0_status;\n  context_.cause = context_snapshot->cp0_cause;\n\n  static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),\n                \"FPRs size mismatch\");\n  memcpy(&context_.fpregs, &context_snapshot->fpregs, sizeof(context_.fpregs));\n  context_.fpcsr = context_snapshot->fpcsr;\n  context_.fir = context_snapshot->fir;\n\n  for (size_t index = 0; index < 3; ++index) {\n    context_.hi[index] = context_snapshot->hi[index];\n    context_.lo[index] = context_snapshot->lo[index];\n  }\n  context_.dsp_control = context_snapshot->dsp_control;\n}\n\nbool MinidumpContextMIPSWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  return file_writer->Write(&context_, sizeof(context_));\n}\n\nsize_t MinidumpContextMIPSWriter::ContextSize() const {\n  DCHECK_GE(state(), kStateFrozen);\n  return sizeof(context_);\n}\n\nMinidumpContextMIPS64Writer::MinidumpContextMIPS64Writer()\n    : MinidumpContextWriter(), context_() {\n  context_.context_flags = kMinidumpContextMIPS64;\n}\n\nMinidumpContextMIPS64Writer::~MinidumpContextMIPS64Writer() = default;\n\nvoid MinidumpContextMIPS64Writer::InitializeFromSnapshot(\n    const CPUContextMIPS64* context_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS64);\n\n  context_.context_flags = kMinidumpContextMIPS64All;\n\n  static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),\n                \"GPRs size mismatch\");\n  memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));\n  context_.mdhi = context_snapshot->mdhi;\n  context_.mdlo = context_snapshot->mdlo;\n  context_.epc = context_snapshot->cp0_epc;\n  context_.badvaddr = context_snapshot->cp0_badvaddr;\n  context_.status = context_snapshot->cp0_status;\n  context_.cause = context_snapshot->cp0_cause;\n\n  static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),\n                \"FPRs size mismatch\");\n  memcpy(context_.fpregs.dregs,\n         context_snapshot->fpregs.dregs,\n         sizeof(context_.fpregs.dregs));\n  context_.fpcsr = context_snapshot->fpcsr;\n  context_.fir = context_snapshot->fir;\n\n  for (size_t index = 0; index < 3; ++index) {\n    context_.hi[index] = context_snapshot->hi[index];\n    context_.lo[index] = context_snapshot->lo[index];\n  }\n  context_.dsp_control = context_snapshot->dsp_control;\n}\n\nbool MinidumpContextMIPS64Writer::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  return file_writer->Write(&context_, sizeof(context_));\n}\n\nsize_t MinidumpContextMIPS64Writer::ContextSize() const {\n  DCHECK_GE(state(), kStateFrozen);\n  return sizeof(context_);\n}\n\nMinidumpContextRISCV64Writer::MinidumpContextRISCV64Writer()\n    : MinidumpContextWriter(), context_() {\n  context_.context_flags = kMinidumpContextRISCV64;\n  context_.version = MinidumpContextRISCV64::kVersion;\n}\n\nMinidumpContextRISCV64Writer::~MinidumpContextRISCV64Writer() = default;\n\nvoid MinidumpContextRISCV64Writer::InitializeFromSnapshot(\n    const CPUContextRISCV64* context_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(context_.context_flags, kMinidumpContextRISCV64);\n\n  context_.context_flags = kMinidumpContextRISCV64All;\n  context_.version = MinidumpContextRISCV64::kVersion;\n  context_.pc = context_snapshot->pc;\n\n  static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),\n                \"GPRs size mismatch\");\n  memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));\n\n  static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs),\n                \"FPRs size mismatch\");\n  memcpy(context_.fpregs, context_snapshot->fpregs, sizeof(context_.fpregs));\n  context_.fcsr = context_snapshot->fcsr;\n}\n\nbool MinidumpContextRISCV64Writer::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  return file_writer->Write(&context_, sizeof(context_));\n}\n\nsize_t MinidumpContextRISCV64Writer::ContextSize() const {\n  DCHECK_GE(state(), kStateFrozen);\n  return sizeof(context_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_context_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_\n\n#include <sys/types.h>\n\n#include <memory>\n\n#include \"minidump/minidump_context.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\nstruct CPUContext;\nstruct CPUContextX86;\nstruct CPUContextX86_64;\nclass MinidumpMiscInfoWriter;\n\n//! \\brief The base class for writers of CPU context structures in minidump\n//!     files.\nclass MinidumpContextWriter : public internal::MinidumpWritable {\n public:\n  MinidumpContextWriter(const MinidumpContextWriter&) = delete;\n  MinidumpContextWriter& operator=(const MinidumpContextWriter&) = delete;\n\n  ~MinidumpContextWriter() override;\n\n  //! \\brief Creates a MinidumpContextWriter based on \\a context_snapshot.\n  //!\n  //! \\param[in] context_snapshot The context snapshot to use as source data.\n  //!\n  //! \\return A MinidumpContextWriter subclass, such as MinidumpContextWriterX86\n  //!     or MinidumpContextWriterAMD64, appropriate to the CPU type of \\a\n  //!     context_snapshot. The returned object is initialized using the source\n  //!     data in \\a context_snapshot. If \\a context_snapshot is an unknown CPU\n  //!     type’s context, logs a message and returns `nullptr`.\n  static std::unique_ptr<MinidumpContextWriter> CreateFromSnapshot(\n      const CPUContext* context_snapshot);\n\n  //! \\brief Returns the size of the context structure that this object will\n  //!     write.\n  //!\n  //! \\note This method will force this to #kStateFrozen, if it is not already.\n  size_t FreezeAndGetSizeOfObject();\n\n protected:\n  MinidumpContextWriter() : MinidumpWritable() {}\n\n  //! \\brief Returns the size of the context structure that this object will\n  //!     write.\n  //!\n  //! \\note This method will only be called in #kStateFrozen or a subsequent\n  //!     state.\n  virtual size_t ContextSize() const = 0;\n\n  // MinidumpWritable:\n  size_t SizeOfObject() final;\n};\n\n//! \\brief The writer for a MinidumpContextX86 structure in a minidump file.\nclass MinidumpContextX86Writer final : public MinidumpContextWriter {\n public:\n  MinidumpContextX86Writer();\n\n  MinidumpContextX86Writer(const MinidumpContextX86Writer&) = delete;\n  MinidumpContextX86Writer& operator=(const MinidumpContextX86Writer&) = delete;\n\n  ~MinidumpContextX86Writer() override;\n\n  //! \\brief Initializes the MinidumpContextX86 based on \\a context_snapshot.\n  //!\n  //! \\param[in] context_snapshot The context snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutation of context() may be done before\n  //!     calling this method, and it is not normally necessary to alter\n  //!     context() after calling this method.\n  void InitializeFromSnapshot(const CPUContextX86* context_snapshot);\n\n  //! \\brief Returns a pointer to the context structure that this object will\n  //!     write.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly. The context structure\n  //!     must only be modified while this object is in the #kStateMutable\n  //!     state.\n  MinidumpContextX86* context() { return &context_; }\n\n protected:\n  // MinidumpWritable:\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpContextWriter:\n  size_t ContextSize() const override;\n\n private:\n  MinidumpContextX86 context_;\n};\n\n//! \\brief Wraps an xsave feature that knows where and how big it is.\nclass MinidumpXSaveFeatureAMD64 {\n public:\n  virtual ~MinidumpXSaveFeatureAMD64() = default;\n  // Number of bytes that will be written. May need to vary by CPUID (see\n  // Intel 13.5).\n  virtual size_t Size() const = 0;\n  // Intel 13.4.2 XCOMP_BV.\n  virtual uint8_t XCompBVBit() const = 0;\n  // Write data to dst. Does not write padding.\n  virtual bool Copy(void* dst) const = 0;\n};\n\n//! \\brief XSAVE_CET_U_FORMAT\nclass MinidumpXSaveAMD64CetU final : public MinidumpXSaveFeatureAMD64 {\n public:\n  MinidumpXSaveAMD64CetU() {}\n  ~MinidumpXSaveAMD64CetU() {}\n  MinidumpXSaveAMD64CetU(const MinidumpXSaveAMD64CetU&) = delete;\n  MinidumpXSaveAMD64CetU& operator=(const MinidumpXSaveAMD64CetU&) = delete;\n\n  size_t Size() const override { return sizeof(cet_u_); }\n  uint8_t XCompBVBit() const override { return XSTATE_CET_U; }\n  bool Copy(void* dst) const override;\n  bool InitializeFromSnapshot(const CPUContextX86_64* context_snapshot);\n\n private:\n  MinidumpAMD64XSaveFormatCetU cet_u_;\n};\n\n//! \\brief The writer for a MinidumpContextAMD64 structure in a minidump file.\nclass MinidumpContextAMD64Writer final : public MinidumpContextWriter {\n public:\n  MinidumpContextAMD64Writer();\n\n  MinidumpContextAMD64Writer(const MinidumpContextAMD64Writer&) = delete;\n  MinidumpContextAMD64Writer& operator=(const MinidumpContextAMD64Writer&) =\n      delete;\n\n  ~MinidumpContextAMD64Writer() override;\n\n  // Ensure proper alignment of heap-allocated objects. This should not be\n  // necessary in C++17.\n  static void* operator new(size_t size);\n  static void operator delete(void* ptr);\n\n  // Prevent unaligned heap-allocated arrays. Provisions could be made to allow\n  // these if necessary, but there is currently no use for them.\n  static void* operator new[](size_t size) = delete;\n  static void operator delete[](void* ptr) = delete;\n\n  //! \\brief Initializes the MinidumpContextAMD64 based on \\a context_snapshot.\n  //!\n  //! \\param[in] context_snapshot The context snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutation of context() may be done before\n  //!     calling this method, and it is not normally necessary to alter\n  //!     context() after calling this method.\n  void InitializeFromSnapshot(const CPUContextX86_64* context_snapshot);\n\n  //! \\brief Returns a pointer to the context structure that this object will\n  //!     write.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly. The context structure\n  //!     must only be modified while this object is in the #kStateMutable\n  //!     state.\n  MinidumpContextAMD64* context() { return &context_; }\n\n protected:\n  // MinidumpWritable:\n  size_t Alignment() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpContextWriter:\n  size_t ContextSize() const override;\n\n private:\n  MinidumpContextAMD64 context_;\n  // These should be in order of XCompBVBit().\n  std::vector<std::unique_ptr<MinidumpXSaveFeatureAMD64>> xsave_entries_;\n};\n\n//! \\brief The writer for a MinidumpContextARM structure in a minidump file.\nclass MinidumpContextARMWriter final : public MinidumpContextWriter {\n public:\n  MinidumpContextARMWriter();\n\n  MinidumpContextARMWriter(const MinidumpContextARMWriter&) = delete;\n  MinidumpContextARMWriter& operator=(const MinidumpContextARMWriter&) = delete;\n\n  ~MinidumpContextARMWriter() override;\n\n  //! \\brief Initializes the MinidumpContextARM based on \\a context_snapshot.\n  //!\n  //! \\param[in] context_snapshot The context snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutation of context() may be done before\n  //!     calling this method, and it is not normally necessary to alter\n  //!     context() after calling this method.\n  void InitializeFromSnapshot(const CPUContextARM* context_snapshot);\n\n  //! \\brief Returns a pointer to the context structure that this object will\n  //!     write.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly. The context structure\n  //!     must only be modified while this object is in the #kStateMutable\n  //!     state.\n  MinidumpContextARM* context() { return &context_; }\n\n protected:\n  // MinidumpWritable:\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpContextWriter:\n  size_t ContextSize() const override;\n\n private:\n  MinidumpContextARM context_;\n};\n\n//! \\brief The writer for a MinidumpContextARM64 structure in a minidump file.\nclass MinidumpContextARM64Writer final : public MinidumpContextWriter {\n public:\n  MinidumpContextARM64Writer();\n\n  MinidumpContextARM64Writer(const MinidumpContextARM64Writer&) = delete;\n  MinidumpContextARM64Writer& operator=(const MinidumpContextARM64Writer&) =\n      delete;\n\n  ~MinidumpContextARM64Writer() override;\n\n  //! \\brief Initializes the MinidumpContextARM64 based on \\a context_snapshot.\n  //!\n  //! \\param[in] context_snapshot The context snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutation of context() may be done before\n  //!     calling this method, and it is not normally necessary to alter\n  //!     context() after calling this method.\n  void InitializeFromSnapshot(const CPUContextARM64* context_snapshot);\n\n  //! \\brief Returns a pointer to the context structure that this object will\n  //!     write.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly. The context structure\n  //!     must only be modified while this object is in the #kStateMutable\n  //!     state.\n  MinidumpContextARM64* context() { return &context_; }\n\n protected:\n  // MinidumpWritable:\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpContextWriter:\n  size_t ContextSize() const override;\n\n private:\n  MinidumpContextARM64 context_;\n};\n\n//! \\brief The writer for a MinidumpContextMIPS structure in a minidump file.\nclass MinidumpContextMIPSWriter final : public MinidumpContextWriter {\n public:\n  MinidumpContextMIPSWriter();\n\n  MinidumpContextMIPSWriter(const MinidumpContextMIPSWriter&) = delete;\n  MinidumpContextMIPSWriter& operator=(const MinidumpContextMIPSWriter&) =\n      delete;\n\n  ~MinidumpContextMIPSWriter() override;\n\n  //! \\brief Initializes the MinidumpContextMIPS based on \\a context_snapshot.\n  //!\n  //! \\param[in] context_snapshot The context snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutation of context() may be done before\n  //!     calling this method, and it is not normally necessary to alter\n  //!     context() after calling this method.\n  void InitializeFromSnapshot(const CPUContextMIPS* context_snapshot);\n\n  //! \\brief Returns a pointer to the context structure that this object will\n  //!     write.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly. The context structure\n  //!     must only be modified while this object is in the #kStateMutable\n  //!     state.\n  MinidumpContextMIPS* context() { return &context_; }\n\n protected:\n  // MinidumpWritable:\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpContextWriter:\n  size_t ContextSize() const override;\n\n private:\n  MinidumpContextMIPS context_;\n};\n\n//! \\brief The writer for a MinidumpContextMIPS64 structure in a minidump file.\nclass MinidumpContextMIPS64Writer final : public MinidumpContextWriter {\n public:\n  MinidumpContextMIPS64Writer();\n\n  MinidumpContextMIPS64Writer(const MinidumpContextMIPS64Writer&) = delete;\n  MinidumpContextMIPS64Writer& operator=(const MinidumpContextMIPS64Writer&) =\n      delete;\n\n  ~MinidumpContextMIPS64Writer() override;\n\n  //! \\brief Initializes the MinidumpContextMIPS based on \\a context_snapshot.\n  //!\n  //! \\param[in] context_snapshot The context snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutation of context() may be done before\n  //!     calling this method, and it is not normally necessary to alter\n  //!     context() after calling this method.\n  void InitializeFromSnapshot(const CPUContextMIPS64* context_snapshot);\n\n  //! \\brief Returns a pointer to the context structure that this object will\n  //!     write.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly. The context structure\n  //!     must only be modified while this object is in the #kStateMutable\n  //!     state.\n  MinidumpContextMIPS64* context() { return &context_; }\n\n protected:\n  // MinidumpWritable:\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpContextWriter:\n  size_t ContextSize() const override;\n\n private:\n  MinidumpContextMIPS64 context_;\n};\n\n//! \\brief The writer for a MinidumpContextRISCV64 structure in a minidump file.\nclass MinidumpContextRISCV64Writer final : public MinidumpContextWriter {\n public:\n  MinidumpContextRISCV64Writer();\n\n  MinidumpContextRISCV64Writer(const MinidumpContextRISCV64Writer&) = delete;\n  MinidumpContextRISCV64Writer& operator=(const MinidumpContextRISCV64Writer&) =\n      delete;\n\n  ~MinidumpContextRISCV64Writer() override;\n\n  //! \\brief Initializes the MinidumpContextRISCV64 based on \\a\n  //! context_snapshot.\n  //!\n  //! \\param[in] context_snapshot The context snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutation of context() may be done before\n  //!     calling this method, and it is not normally necessary to alter\n  //!     context() after calling this method.\n  void InitializeFromSnapshot(const CPUContextRISCV64* context_snapshot);\n\n  //! \\brief Returns a pointer to the context structure that this object will\n  //!     write.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly. The context structure\n  //!     must only be modified while this object is in the #kStateMutable\n  //!     state.\n  MinidumpContextRISCV64* context() { return &context_; }\n\n protected:\n  // MinidumpWritable:\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpContextWriter:\n  size_t ContextSize() const override;\n\n private:\n  MinidumpContextRISCV64 context_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_context_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_context_writer.h\"\n\n#include <stdint.h>\n\n#include <string>\n#include <type_traits>\n\n#include \"base/notreached.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_context.h\"\n#include \"minidump/test/minidump_context_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/test/test_cpu_context.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\ntemplate <typename Writer, typename Context, typename RVAType>\nvoid EmptyContextTest(void (*expect_context)(uint32_t, const Context*, bool)) {\n  Writer context_writer;\n  StringFile string_file;\n  EXPECT_TRUE(context_writer.WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(), sizeof(Context));\n\n  const Context* observed =\n      MinidumpWritableAtRVA<Context>(string_file.string(), RVAType(0));\n  ASSERT_TRUE(observed);\n\n  expect_context(0, observed, false);\n}\n\nclass TestTypeNames {\n public:\n  template <typename T>\n  static std::string GetName(int) {\n    static_assert(std::is_same<T, RVA>() || std::is_same<T, RVA64>());\n    return std::is_same<T, RVA>() ? \"RVA\" : \"RVA64\";\n  }\n};\n\ntemplate <typename RVAType>\nclass MinidumpContextWriter : public ::testing::Test {};\n\nusing RVATypes = ::testing::Types<RVA, RVA64>;\nTYPED_TEST_SUITE(MinidumpContextWriter, RVATypes, TestTypeNames);\n\nTYPED_TEST(MinidumpContextWriter, MinidumpContextX86Writer) {\n  StringFile string_file;\n\n  {\n    // Make sure that a context writer that’s untouched writes a zeroed-out\n    // context.\n    SCOPED_TRACE(\"zero\");\n\n    EmptyContextTest<MinidumpContextX86Writer, MinidumpContextX86, TypeParam>(\n        ExpectMinidumpContextX86);\n  }\n\n  {\n    SCOPED_TRACE(\"nonzero\");\n\n    string_file.Reset();\n    constexpr uint32_t kSeed = 0x8086;\n\n    MinidumpContextX86Writer context_writer;\n    InitializeMinidumpContextX86(context_writer.context(), kSeed);\n\n    EXPECT_TRUE(context_writer.WriteEverything(&string_file));\n    ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextX86));\n\n    const MinidumpContextX86* observed =\n        MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(),\n                                                  TypeParam(0));\n    ASSERT_TRUE(observed);\n\n    ExpectMinidumpContextX86(kSeed, observed, false);\n  }\n}\n\nTYPED_TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {\n  {\n    // Make sure that a heap-allocated context writer has the proper alignment,\n    // because it may be nonstandard.\n    auto context_writer = std::make_unique<MinidumpContextAMD64Writer>();\n    EXPECT_EQ(reinterpret_cast<uintptr_t>(context_writer.get()) &\n                  (alignof(MinidumpContextAMD64Writer) - 1),\n              0u);\n  }\n\n  StringFile string_file;\n\n  {\n    // Make sure that a context writer that’s untouched writes a zeroed-out\n    // context.\n    SCOPED_TRACE(\"zero\");\n\n    EmptyContextTest<MinidumpContextAMD64Writer,\n                     MinidumpContextAMD64,\n                     TypeParam>(ExpectMinidumpContextAMD64);\n  }\n\n  {\n    SCOPED_TRACE(\"nonzero\");\n\n    string_file.Reset();\n    constexpr uint32_t kSeed = 0x808664;\n\n    MinidumpContextAMD64Writer context_writer;\n    InitializeMinidumpContextAMD64(context_writer.context(), kSeed);\n\n    EXPECT_TRUE(context_writer.WriteEverything(&string_file));\n    ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextAMD64));\n\n    const MinidumpContextAMD64* observed =\n        MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(),\n                                                    TypeParam(0));\n    ASSERT_TRUE(observed);\n\n    ExpectMinidumpContextAMD64(kSeed, observed, false);\n  }\n}\n\nTYPED_TEST(MinidumpContextWriter, MinidumpContextRISCV64Writer) {\n  {\n    // Make sure that a heap-allocated context writer has the proper alignment,\n    // because it may be nonstandard.\n    auto context_writer = std::make_unique<MinidumpContextRISCV64Writer>();\n    EXPECT_EQ(reinterpret_cast<uintptr_t>(context_writer.get()) &\n                  (alignof(MinidumpContextRISCV64Writer) - 1),\n              0u);\n  }\n\n  StringFile string_file;\n\n  {\n    // Make sure that a context writer that’s untouched writes a zeroed-out\n    // context.\n    SCOPED_TRACE(\"zero\");\n\n    EmptyContextTest<MinidumpContextRISCV64Writer,\n                     MinidumpContextRISCV64,\n                     TypeParam>(ExpectMinidumpContextRISCV64);\n  }\n\n  {\n    SCOPED_TRACE(\"nonzero\");\n\n    string_file.Reset();\n    constexpr uint32_t kSeed = 0x808664;\n\n    MinidumpContextRISCV64Writer context_writer;\n    InitializeMinidumpContextRISCV64(context_writer.context(), kSeed);\n\n    EXPECT_TRUE(context_writer.WriteEverything(&string_file));\n    ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextRISCV64));\n\n    const MinidumpContextRISCV64* observed =\n        MinidumpWritableAtRVA<MinidumpContextRISCV64>(string_file.string(),\n                                                      TypeParam(0));\n    ASSERT_TRUE(observed);\n\n    ExpectMinidumpContextRISCV64(kSeed, observed, false);\n  }\n}\n\ntemplate <typename Writer, typename Context, typename RVAType>\nvoid FromSnapshotTest(const CPUContext& snapshot_context,\n                      void (*expect_context)(uint32_t, const Context*, bool),\n                      uint32_t seed) {\n  std::unique_ptr<::crashpad::MinidumpContextWriter> context_writer =\n      ::crashpad::MinidumpContextWriter::CreateFromSnapshot(&snapshot_context);\n  ASSERT_TRUE(context_writer);\n\n  StringFile string_file;\n  ASSERT_TRUE(context_writer->WriteEverything(&string_file));\n\n  const Context* observed =\n      MinidumpWritableAtRVA<Context>(string_file.string(), RVAType(0));\n  ASSERT_TRUE(observed);\n\n  expect_context(seed, observed, true);\n}\n\nTYPED_TEST(MinidumpContextWriter, X86_FromSnapshot) {\n  constexpr uint32_t kSeed = 32;\n  CPUContextX86 context_x86;\n  CPUContext context;\n  context.x86 = &context_x86;\n  InitializeCPUContextX86(&context, kSeed);\n  FromSnapshotTest<MinidumpContextX86Writer, MinidumpContextX86, TypeParam>(\n      context, ExpectMinidumpContextX86, kSeed);\n}\n\nTYPED_TEST(MinidumpContextWriter, AMD64_FromSnapshot) {\n  constexpr uint32_t kSeed = 64;\n  CPUContextX86_64 context_x86_64;\n  CPUContext context;\n  context.x86_64 = &context_x86_64;\n  InitializeCPUContextX86_64(&context, kSeed);\n  FromSnapshotTest<MinidumpContextAMD64Writer, MinidumpContextAMD64, TypeParam>(\n      context, ExpectMinidumpContextAMD64, kSeed);\n}\n\nTYPED_TEST(MinidumpContextWriter, AMD64_CetFromSnapshot) {\n  constexpr uint32_t kSeed = 77;\n  CPUContextX86_64 context_x86_64;\n  CPUContext context;\n  context.x86_64 = &context_x86_64;\n  InitializeCPUContextX86_64(&context, kSeed);\n  context_x86_64.xstate.enabled_features |= XSTATE_MASK_CET_U;\n  context_x86_64.xstate.cet_u.cetmsr = 1;\n  context_x86_64.xstate.cet_u.ssp = kSeed * kSeed;\n  // We cannot use FromSnapshotTest as we write more than the fixed context.\n  std::unique_ptr<::crashpad::MinidumpContextWriter> context_writer =\n      ::crashpad::MinidumpContextWriter::CreateFromSnapshot(&context);\n  ASSERT_TRUE(context_writer);\n\n  StringFile string_file;\n  ASSERT_TRUE(context_writer->WriteEverything(&string_file));\n\n  const MinidumpContextAMD64* observed =\n      MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(),\n                                                  TypeParam(0));\n  ASSERT_TRUE(observed);\n\n  ExpectMinidumpContextAMD64(kSeed, observed, true);\n}\n\nTYPED_TEST(MinidumpContextWriter, ARM_Zeros) {\n  EmptyContextTest<MinidumpContextARMWriter, MinidumpContextARM, TypeParam>(\n      ExpectMinidumpContextARM);\n}\n\nTYPED_TEST(MinidumpContextWriter, ARM64_Zeros) {\n  EmptyContextTest<MinidumpContextARM64Writer, MinidumpContextARM64, TypeParam>(\n      ExpectMinidumpContextARM64);\n}\n\nTYPED_TEST(MinidumpContextWriter, ARM_FromSnapshot) {\n  constexpr uint32_t kSeed = 32;\n  CPUContextARM context_arm;\n  CPUContext context;\n  context.arm = &context_arm;\n  InitializeCPUContextARM(&context, kSeed);\n  FromSnapshotTest<MinidumpContextARMWriter, MinidumpContextARM, TypeParam>(\n      context, ExpectMinidumpContextARM, kSeed);\n}\n\nTYPED_TEST(MinidumpContextWriter, ARM64_FromSnapshot) {\n  constexpr uint32_t kSeed = 64;\n  CPUContextARM64 context_arm64;\n  CPUContext context;\n  context.arm64 = &context_arm64;\n  InitializeCPUContextARM64(&context, kSeed);\n  FromSnapshotTest<MinidumpContextARM64Writer, MinidumpContextARM64, TypeParam>(\n      context, ExpectMinidumpContextARM64, kSeed);\n}\n\nTYPED_TEST(MinidumpContextWriter, MIPS_Zeros) {\n  EmptyContextTest<MinidumpContextMIPSWriter, MinidumpContextMIPS, TypeParam>(\n      ExpectMinidumpContextMIPS);\n}\n\nTYPED_TEST(MinidumpContextWriter, MIPS64_Zeros) {\n  EmptyContextTest<MinidumpContextMIPS64Writer,\n                   MinidumpContextMIPS64,\n                   TypeParam>(ExpectMinidumpContextMIPS64);\n}\n\nTYPED_TEST(MinidumpContextWriter, MIPS_FromSnapshot) {\n  constexpr uint32_t kSeed = 32;\n  CPUContextMIPS context_mips;\n  CPUContext context;\n  context.mipsel = &context_mips;\n  InitializeCPUContextMIPS(&context, kSeed);\n  FromSnapshotTest<MinidumpContextMIPSWriter, MinidumpContextMIPS, TypeParam>(\n      context, ExpectMinidumpContextMIPS, kSeed);\n}\n\nTYPED_TEST(MinidumpContextWriter, MIPS64_FromSnapshot) {\n  constexpr uint32_t kSeed = 64;\n  CPUContextMIPS64 context_mips;\n  CPUContext context;\n  context.mips64 = &context_mips;\n  InitializeCPUContextMIPS64(&context, kSeed);\n  FromSnapshotTest<MinidumpContextMIPS64Writer,\n                   MinidumpContextMIPS64,\n                   TypeParam>(context, ExpectMinidumpContextMIPS64, kSeed);\n}\n\nTYPED_TEST(MinidumpContextWriter, RISCV64_Zeros) {\n  EmptyContextTest<MinidumpContextRISCV64Writer,\n                   MinidumpContextRISCV64,\n                   TypeParam>(ExpectMinidumpContextRISCV64);\n}\n\nTYPED_TEST(MinidumpContextWriter, RISCV64_FromSnapshot) {\n  constexpr uint32_t kSeed = 64;\n  CPUContextRISCV64 context_riscv64;\n  CPUContext context;\n  context.riscv64 = &context_riscv64;\n  InitializeCPUContextRISCV64(&context, kSeed);\n  FromSnapshotTest<MinidumpContextRISCV64Writer,\n                   MinidumpContextRISCV64,\n                   TypeParam>(context, ExpectMinidumpContextRISCV64, kSeed);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_crashpad_info_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_crashpad_info_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"minidump/minidump_module_crashpad_info_writer.h\"\n#include \"minidump/minidump_simple_string_dictionary_writer.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"util/file/file_writer.h\"\n\nnamespace crashpad {\n\nMinidumpCrashpadInfoWriter::MinidumpCrashpadInfoWriter()\n    : MinidumpStreamWriter(),\n      crashpad_info_(),\n      simple_annotations_(nullptr),\n      module_list_(nullptr) {\n  crashpad_info_.version = MinidumpCrashpadInfo::kVersion;\n  crashpad_info_.reserved = 0;\n}\n\nMinidumpCrashpadInfoWriter::~MinidumpCrashpadInfoWriter() {\n}\n\nvoid MinidumpCrashpadInfoWriter::InitializeFromSnapshot(\n    const ProcessSnapshot* process_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!module_list_);\n\n  UUID report_id;\n  process_snapshot->ReportID(&report_id);\n  SetReportID(report_id);\n\n  UUID client_id;\n  process_snapshot->ClientID(&client_id);\n  SetClientID(client_id);\n\n  auto simple_annotations =\n      std::make_unique<MinidumpSimpleStringDictionaryWriter>();\n  simple_annotations->InitializeFromMap(\n      process_snapshot->AnnotationsSimpleMap());\n  if (simple_annotations->IsUseful()) {\n    SetSimpleAnnotations(std::move(simple_annotations));\n  }\n\n  if (process_snapshot->System()) {\n    SetAddressMask(process_snapshot->System()->AddressMask());\n  }\n\n  auto modules = std::make_unique<MinidumpModuleCrashpadInfoListWriter>();\n  modules->InitializeFromSnapshot(process_snapshot->Modules());\n\n  if (modules->IsUseful()) {\n    SetModuleList(std::move(modules));\n  }\n}\n\nvoid MinidumpCrashpadInfoWriter::SetReportID(const UUID& report_id) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  crashpad_info_.report_id = report_id;\n}\n\nvoid MinidumpCrashpadInfoWriter::SetClientID(const UUID& client_id) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  crashpad_info_.client_id = client_id;\n}\n\nvoid MinidumpCrashpadInfoWriter::SetSimpleAnnotations(\n    std::unique_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  simple_annotations_ = std::move(simple_annotations);\n}\n\nvoid MinidumpCrashpadInfoWriter::SetModuleList(\n    std::unique_ptr<MinidumpModuleCrashpadInfoListWriter> module_list) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  module_list_ = std::move(module_list);\n}\n\nvoid MinidumpCrashpadInfoWriter::SetAddressMask(uint64_t mask) {\n  crashpad_info_.address_mask = mask;\n}\n\nbool MinidumpCrashpadInfoWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  if (simple_annotations_) {\n    simple_annotations_->RegisterLocationDescriptor(\n        &crashpad_info_.simple_annotations);\n  }\n  if (module_list_) {\n    module_list_->RegisterLocationDescriptor(&crashpad_info_.module_list);\n  }\n\n  return true;\n}\n\nsize_t MinidumpCrashpadInfoWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(crashpad_info_);\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpCrashpadInfoWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children;\n  if (simple_annotations_) {\n    children.push_back(simple_annotations_.get());\n  }\n  if (module_list_) {\n    children.push_back(module_list_.get());\n  }\n\n  return children;\n}\n\nbool MinidumpCrashpadInfoWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return file_writer->Write(&crashpad_info_, sizeof(crashpad_info_));\n}\n\nMinidumpStreamType MinidumpCrashpadInfoWriter::StreamType() const {\n  return kMinidumpStreamTypeCrashpadInfo;\n}\n\nbool MinidumpCrashpadInfoWriter::IsUseful() const {\n  return crashpad_info_.report_id != UUID() ||\n         crashpad_info_.client_id != UUID() ||\n         simple_annotations_ ||\n         module_list_;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_crashpad_info_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_\n\n#include <sys/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_stream_writer.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\nclass MinidumpModuleCrashpadInfoListWriter;\nclass MinidumpSimpleStringDictionaryWriter;\nclass ProcessSnapshot;\n\n//! \\brief The writer for a MinidumpCrashpadInfo stream in a minidump file.\nclass MinidumpCrashpadInfoWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpCrashpadInfoWriter();\n\n  MinidumpCrashpadInfoWriter(const MinidumpCrashpadInfoWriter&) = delete;\n  MinidumpCrashpadInfoWriter& operator=(const MinidumpCrashpadInfoWriter&) =\n      delete;\n\n  ~MinidumpCrashpadInfoWriter() override;\n\n  //! \\brief Initializes MinidumpCrashpadInfo based on \\a process_snapshot.\n  //!\n  //! This method may add additional structures to the minidump file as children\n  //! of the MinidumpCrashpadInfo stream. To do so, it may obtain other\n  //! snapshot information from \\a process_snapshot, such as a list of\n  //! ModuleSnapshot objects used to initialize\n  //! MinidumpCrashpadInfo::module_list. Only data that is considered useful\n  //! will be included. For module information, usefulness is determined by\n  //! MinidumpModuleCrashpadInfoListWriter::IsUseful().\n  //!\n  //! \\param[in] process_snapshot The process snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot);\n\n  //! \\brief Sets MinidumpCrashpadInfo::report_id.\n  void SetReportID(const UUID& report_id);\n\n  //! \\brief Sets MinidumpCrashpadInfo::client_id.\n  void SetClientID(const UUID& client_id);\n\n  //! \\brief Arranges for MinidumpCrashpadInfo::simple_annotations to point to\n  //!     the MinidumpSimpleStringDictionaryWriter object to be written by \\a\n  //!     simple_annotations.\n  //!\n  //! This object takes ownership of \\a simple_annotations and becomes its\n  //! parent in the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetSimpleAnnotations(\n      std::unique_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations);\n\n  //! \\brief Arranges for MinidumpCrashpadInfo::module_list to point to the\n  //!     MinidumpModuleCrashpadInfoList object to be written by \\a\n  //!     module_list.\n  //!\n  //! This object takes ownership of \\a module_list and becomes its parent in\n  //! the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetModuleList(\n      std::unique_ptr<MinidumpModuleCrashpadInfoListWriter> module_list);\n\n  //! \\brief Sets MinidumpCrashpadInfo::address_mask.\n  void SetAddressMask(uint64_t mask);\n\n  //! \\brief Determines whether the object is useful.\n  //!\n  //! A useful object is one that carries data that makes a meaningful\n  //! contribution to a minidump file. An object carrying children would be\n  //! considered useful.\n  //!\n  //! \\return `true` if the object is useful, `false` otherwise.\n  bool IsUseful() const;\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  MinidumpCrashpadInfo crashpad_info_;\n  std::unique_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations_;\n  std::unique_ptr<MinidumpModuleCrashpadInfoListWriter> module_list_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_crashpad_info_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_crashpad_info_writer.h\"\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include <map>\n#include <string>\n#include <utility>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/minidump_module_crashpad_info_writer.h\"\n#include \"minidump/minidump_simple_string_dictionary_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_module_snapshot.h\"\n#include \"snapshot/test/test_process_snapshot.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid GetCrashpadInfoStream(\n    const std::string& file_contents,\n    const MinidumpCrashpadInfo** crashpad_info,\n    const MinidumpSimpleStringDictionary** simple_annotations,\n    const MinidumpModuleCrashpadInfoList** module_list) {\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeCrashpadInfo);\n\n  *crashpad_info = MinidumpWritableAtLocationDescriptor<MinidumpCrashpadInfo>(\n      file_contents, directory[0].Location);\n  ASSERT_TRUE(*crashpad_info);\n\n  *simple_annotations =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          file_contents, (*crashpad_info)->simple_annotations);\n\n  *module_list =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>(\n          file_contents, (*crashpad_info)->module_list);\n}\n\nTEST(MinidumpCrashpadInfoWriter, Empty) {\n  MinidumpFileWriter minidump_file_writer;\n  auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();\n  EXPECT_FALSE(crashpad_info_writer->IsUseful());\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MinidumpCrashpadInfo* crashpad_info = nullptr;\n  const MinidumpSimpleStringDictionary* simple_annotations = nullptr;\n  const MinidumpModuleCrashpadInfoList* module_list = nullptr;\n\n  ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(\n      string_file.string(), &crashpad_info, &simple_annotations, &module_list));\n\n  EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion);\n  EXPECT_EQ(crashpad_info->report_id, UUID());\n  EXPECT_EQ(crashpad_info->client_id, UUID());\n  EXPECT_FALSE(simple_annotations);\n  EXPECT_FALSE(module_list);\n}\n\nTEST(MinidumpCrashpadInfoWriter, ReportAndClientID) {\n  MinidumpFileWriter minidump_file_writer;\n  auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();\n\n  UUID report_id;\n  ASSERT_TRUE(\n      report_id.InitializeFromString(\"01234567-89ab-cdef-0123-456789abcdef\"));\n  crashpad_info_writer->SetReportID(report_id);\n\n  UUID client_id;\n  ASSERT_TRUE(\n      client_id.InitializeFromString(\"00112233-4455-6677-8899-aabbccddeeff\"));\n  crashpad_info_writer->SetClientID(client_id);\n\n  EXPECT_TRUE(crashpad_info_writer->IsUseful());\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MinidumpCrashpadInfo* crashpad_info = nullptr;\n  const MinidumpSimpleStringDictionary* simple_annotations = nullptr;\n  const MinidumpModuleCrashpadInfoList* module_list = nullptr;\n\n  ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(\n      string_file.string(), &crashpad_info, &simple_annotations, &module_list));\n\n  EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion);\n  EXPECT_EQ(crashpad_info->report_id, report_id);\n  EXPECT_EQ(crashpad_info->client_id, client_id);\n  EXPECT_FALSE(simple_annotations);\n  EXPECT_FALSE(module_list);\n}\n\nTEST(MinidumpCrashpadInfoWriter, AddressMask) {\n  MinidumpFileWriter minidump_file_writer;\n  auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();\n\n  constexpr uint64_t mask = 0xFFFFFF8000000000;\n  crashpad_info_writer->SetAddressMask(mask);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MinidumpCrashpadInfo* crashpad_info = nullptr;\n  const MinidumpSimpleStringDictionary* simple_annotations = nullptr;\n  const MinidumpModuleCrashpadInfoList* module_list = nullptr;\n\n  ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(\n      string_file.string(), &crashpad_info, &simple_annotations, &module_list));\n\n  UUID empty_report_id;\n  ASSERT_TRUE(empty_report_id.InitializeFromString(\n      \"00000000-0000-0000-0000-000000000000\"));\n  UUID empty_client_id;\n  ASSERT_TRUE(empty_client_id.InitializeFromString(\n      \"00000000-0000-0000-0000-000000000000\"));\n\n  // Copy address_mask into a local variable because\n  // |MinidumpCrashpadInfo::address_mask| requires 8-byte alignment but the\n  // struct itself is 4-byte aligned.\n  const auto address_mask = [&crashpad_info] {\n    uint64_t data = 0;\n    memcpy(&data, &crashpad_info->address_mask, sizeof(data));\n    return data;\n  }();\n\n  EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion);\n  EXPECT_EQ(address_mask, mask);\n  EXPECT_EQ(crashpad_info->report_id, empty_report_id);\n  EXPECT_EQ(crashpad_info->client_id, empty_client_id);\n  EXPECT_FALSE(simple_annotations);\n  EXPECT_FALSE(module_list);\n}\n\nTEST(MinidumpCrashpadInfoWriter, EmptyAddressMask) {\n  MinidumpFileWriter minidump_file_writer;\n  auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MinidumpCrashpadInfo* crashpad_info = nullptr;\n  const MinidumpSimpleStringDictionary* simple_annotations = nullptr;\n  const MinidumpModuleCrashpadInfoList* module_list = nullptr;\n\n  ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(\n      string_file.string(), &crashpad_info, &simple_annotations, &module_list));\n\n  // Copy address_mask into a local variable because\n  // |MinidumpCrashpadInfo::address_mask| requires 8-byte alignment but the\n  // struct itself is 4-byte aligned.\n  const auto address_mask = [&crashpad_info] {\n    uint64_t data = 0;\n    memcpy(&data, &crashpad_info->address_mask, sizeof(data));\n    return data;\n  }();\n\n  EXPECT_EQ(address_mask, 0UL);\n}\n\nTEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) {\n  MinidumpFileWriter minidump_file_writer;\n  auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();\n\n  static constexpr char kKey[] =\n      \"a thing that provides a means of gaining access to or understanding \"\n      \"something\";\n  static constexpr char kValue[] =\n      \"the numerical amount denoted by an algebraic term; a magnitude, \"\n      \"quantity, or number\";\n  auto simple_string_dictionary_writer =\n      std::make_unique<MinidumpSimpleStringDictionaryWriter>();\n  auto simple_string_dictionary_entry_writer =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  simple_string_dictionary_entry_writer->SetKeyValue(kKey, kValue);\n  simple_string_dictionary_writer->AddEntry(\n      std::move(simple_string_dictionary_entry_writer));\n  crashpad_info_writer->SetSimpleAnnotations(\n      std::move(simple_string_dictionary_writer));\n\n  EXPECT_TRUE(crashpad_info_writer->IsUseful());\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MinidumpCrashpadInfo* crashpad_info = nullptr;\n  const MinidumpSimpleStringDictionary* simple_annotations = nullptr;\n  const MinidumpModuleCrashpadInfoList* module_list = nullptr;\n\n  ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(\n      string_file.string(), &crashpad_info, &simple_annotations, &module_list));\n\n  EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion);\n  EXPECT_FALSE(module_list);\n\n  ASSERT_TRUE(simple_annotations);\n  ASSERT_EQ(simple_annotations->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            simple_annotations->entries[0].key),\n            kKey);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations->entries[0].value),\n            kValue);\n}\n\nTEST(MinidumpCrashpadInfoWriter, CrashpadModuleList) {\n  constexpr uint32_t kMinidumpModuleListIndex = 3;\n\n  MinidumpFileWriter minidump_file_writer;\n  auto crashpad_info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();\n\n  auto module_list_writer =\n      std::make_unique<MinidumpModuleCrashpadInfoListWriter>();\n  auto module_writer = std::make_unique<MinidumpModuleCrashpadInfoWriter>();\n  module_list_writer->AddModule(std::move(module_writer),\n                                kMinidumpModuleListIndex);\n  crashpad_info_writer->SetModuleList(std::move(module_list_writer));\n\n  EXPECT_TRUE(crashpad_info_writer->IsUseful());\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MinidumpCrashpadInfo* crashpad_info = nullptr;\n  const MinidumpSimpleStringDictionary* simple_annotations = nullptr;\n  const MinidumpModuleCrashpadInfoList* module_list = nullptr;\n\n  ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(\n      string_file.string(), &crashpad_info, &simple_annotations, &module_list));\n\n  EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion);\n  EXPECT_FALSE(simple_annotations);\n\n  ASSERT_TRUE(module_list);\n  ASSERT_EQ(module_list->count, 1u);\n\n  EXPECT_EQ(module_list->modules[0].minidump_module_list_index,\n            kMinidumpModuleListIndex);\n  const MinidumpModuleCrashpadInfo* module =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[0].location);\n  ASSERT_TRUE(module);\n\n  EXPECT_EQ(module->version, MinidumpModuleCrashpadInfo::kVersion);\n  EXPECT_EQ(module->list_annotations.DataSize, 0u);\n  EXPECT_EQ(module->list_annotations.Rva, 0u);\n  EXPECT_EQ(module->simple_annotations.DataSize, 0u);\n  EXPECT_EQ(module->simple_annotations.Rva, 0u);\n}\n\nTEST(MinidumpCrashpadInfoWriter, InitializeFromSnapshot) {\n  UUID report_id;\n  ASSERT_TRUE(\n      report_id.InitializeFromString(\"fedcba98-7654-3210-fedc-ba9876543210\"));\n\n  UUID client_id;\n  ASSERT_TRUE(\n      client_id.InitializeFromString(\"fedcba98-7654-3210-0123-456789abcdef\"));\n\n  static constexpr char kKey[] = \"version\";\n  static constexpr char kValue[] = \"40.0.2214.111\";\n  static constexpr char kEntry[] = \"This is a simple annotation in a list.\";\n\n  // Test with a useless module, one that doesn’t carry anything that would\n  // require MinidumpCrashpadInfo or any child object.\n  auto process_snapshot = std::make_unique<TestProcessSnapshot>();\n\n  auto module_snapshot = std::make_unique<TestModuleSnapshot>();\n  process_snapshot->AddModule(std::move(module_snapshot));\n\n  auto info_writer = std::make_unique<MinidumpCrashpadInfoWriter>();\n  info_writer->InitializeFromSnapshot(process_snapshot.get());\n  EXPECT_FALSE(info_writer->IsUseful());\n\n  // Try again with a useful module.\n  process_snapshot.reset(new TestProcessSnapshot());\n\n  process_snapshot->SetReportID(report_id);\n  process_snapshot->SetClientID(client_id);\n\n  std::map<std::string, std::string> annotations_simple_map;\n  annotations_simple_map[kKey] = kValue;\n  process_snapshot->SetAnnotationsSimpleMap(annotations_simple_map);\n\n  module_snapshot.reset(new TestModuleSnapshot());\n  std::vector<std::string> annotations_list(1, std::string(kEntry));\n  module_snapshot->SetAnnotationsVector(annotations_list);\n  process_snapshot->AddModule(std::move(module_snapshot));\n\n  info_writer.reset(new MinidumpCrashpadInfoWriter());\n  info_writer->InitializeFromSnapshot(process_snapshot.get());\n  EXPECT_TRUE(info_writer->IsUseful());\n\n  MinidumpFileWriter minidump_file_writer;\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MinidumpCrashpadInfo* info = nullptr;\n  const MinidumpSimpleStringDictionary* simple_annotations;\n  const MinidumpModuleCrashpadInfoList* module_list;\n  ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream(\n      string_file.string(), &info, &simple_annotations, &module_list));\n\n  EXPECT_EQ(info->version, MinidumpCrashpadInfo::kVersion);\n\n  EXPECT_EQ(info->report_id, report_id);\n  EXPECT_EQ(info->client_id, client_id);\n\n  ASSERT_TRUE(simple_annotations);\n  ASSERT_EQ(simple_annotations->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            simple_annotations->entries[0].key),\n            kKey);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations->entries[0].value),\n            kValue);\n\n  ASSERT_TRUE(module_list);\n  ASSERT_EQ(module_list->count, 1u);\n\n  EXPECT_EQ(module_list->modules[0].minidump_module_list_index, 0u);\n  const MinidumpModuleCrashpadInfo* module =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[0].location);\n  ASSERT_TRUE(module);\n\n  EXPECT_EQ(module->version, MinidumpModuleCrashpadInfo::kVersion);\n\n  const MinidumpRVAList* list_annotations =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          string_file.string(), module->list_annotations);\n  ASSERT_TRUE(list_annotations);\n\n  ASSERT_EQ(list_annotations->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            list_annotations->children[0]),\n            kEntry);\n\n  const MinidumpSimpleStringDictionary* module_simple_annotations =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module->simple_annotations);\n  EXPECT_FALSE(module_simple_annotations);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_exception_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_exception_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"minidump/minidump_context_writer.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/misc/arraysize.h\"\n\nnamespace crashpad {\n\nMinidumpExceptionWriter::MinidumpExceptionWriter()\n    : MinidumpStreamWriter(), exception_(), context_(nullptr) {\n}\n\nMinidumpExceptionWriter::~MinidumpExceptionWriter() {\n}\n\nvoid MinidumpExceptionWriter::InitializeFromSnapshot(\n    const ExceptionSnapshot* exception_snapshot,\n    const MinidumpThreadIDMap& thread_id_map,\n    bool allow_missing_thread_id_from_map) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!context_);\n\n  auto thread_id_it = thread_id_map.find(exception_snapshot->ThreadID());\n  bool thread_id_missing = thread_id_it == thread_id_map.end();\n  if (allow_missing_thread_id_from_map && thread_id_missing) {\n    SetThreadID(static_cast<uint32_t>(exception_snapshot->ThreadID()));\n  } else {\n    DCHECK(!thread_id_missing);\n    SetThreadID(thread_id_it->second);\n  }\n\n  SetExceptionCode(exception_snapshot->Exception());\n  SetExceptionFlags(exception_snapshot->ExceptionInfo());\n  SetExceptionAddress(exception_snapshot->ExceptionAddress());\n  SetExceptionInformation(exception_snapshot->Codes());\n\n  std::unique_ptr<MinidumpContextWriter> context =\n      MinidumpContextWriter::CreateFromSnapshot(exception_snapshot->Context());\n  SetContext(std::move(context));\n}\n\nvoid MinidumpExceptionWriter::SetContext(\n    std::unique_ptr<MinidumpContextWriter> context) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  context_ = std::move(context);\n}\n\nvoid MinidumpExceptionWriter::SetExceptionInformation(\n    const std::vector<uint64_t>& exception_information) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  const size_t parameters = exception_information.size();\n  constexpr size_t kMaxParameters =\n      ArraySize(exception_.ExceptionRecord.ExceptionInformation);\n  CHECK_LE(parameters, kMaxParameters);\n\n  exception_.ExceptionRecord.NumberParameters =\n      base::checked_cast<uint32_t>(parameters);\n  size_t parameter = 0;\n  for (; parameter < parameters; ++parameter) {\n    exception_.ExceptionRecord.ExceptionInformation[parameter] =\n        exception_information[parameter];\n  }\n  for (; parameter < kMaxParameters; ++parameter) {\n    exception_.ExceptionRecord.ExceptionInformation[parameter] = 0;\n  }\n}\n\nbool MinidumpExceptionWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n  CHECK(context_);\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  context_->RegisterLocationDescriptor(&exception_.ThreadContext);\n\n  return true;\n}\n\nsize_t MinidumpExceptionWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(exception_);\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpExceptionWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK(context_);\n\n  std::vector<MinidumpWritable*> children;\n  children.push_back(context_.get());\n\n  return children;\n}\n\nbool MinidumpExceptionWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return file_writer->Write(&exception_, sizeof(exception_));\n}\n\nMinidumpStreamType MinidumpExceptionWriter::StreamType() const {\n  return kMinidumpStreamTypeException;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_exception_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_thread_id_map.h\"\n\nnamespace crashpad {\n\nclass ExceptionSnapshot;\nclass MinidumpContextWriter;\nclass MinidumpMemoryListWriter;\n\n//! \\brief The writer for a MINIDUMP_EXCEPTION_STREAM stream in a minidump file.\nclass MinidumpExceptionWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpExceptionWriter();\n\n  MinidumpExceptionWriter(const MinidumpExceptionWriter&) = delete;\n  MinidumpExceptionWriter& operator=(const MinidumpExceptionWriter&) = delete;\n\n  ~MinidumpExceptionWriter() override;\n\n  //! \\brief Initializes the MINIDUMP_EXCEPTION_STREAM based on \\a\n  //!     exception_snapshot.\n  //!\n  //! \\param[in] exception_snapshot The exception snapshot to use as source\n  //!     data.\n  //! \\param[in] thread_id_map A MinidumpThreadIDMap to be consulted to\n  //!     determine the 32-bit minidump thread ID to use for the thread\n  //!     identified by \\a exception_snapshot.\n  //! \\param[in] allow_missing_thread_id_from_map Whether it is valid\n  //!     for \\a exception_snapshot->ThreadID() to be absent from the\n  //!     \\a thread_id_map, such as in an incomplete iOS intermediate dump. When\n  //!     false a missing thread id is considered invalid and will DCHECK.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const ExceptionSnapshot* exception_snapshot,\n                              const MinidumpThreadIDMap& thread_id_map,\n                              bool allow_missing_thread_id_from_map);\n\n  //! \\brief Arranges for MINIDUMP_EXCEPTION_STREAM::ThreadContext to point to\n  //!     the CPU context to be written by \\a context.\n  //!\n  //! A context is required in all MINIDUMP_EXCEPTION_STREAM objects.\n  //!\n  //! This object takes ownership of \\a context and becomes its parent in the\n  //! overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetContext(std::unique_ptr<MinidumpContextWriter> context);\n\n  //! \\brief Sets MINIDUMP_EXCEPTION_STREAM::ThreadId.\n  void SetThreadID(uint32_t thread_id) { exception_.ThreadId = thread_id; }\n\n  //! \\brief Sets MINIDUMP_EXCEPTION::ExceptionCode.\n  void SetExceptionCode(uint32_t exception_code) {\n    exception_.ExceptionRecord.ExceptionCode = exception_code;\n  }\n\n  //! \\brief Sets MINIDUMP_EXCEPTION::ExceptionFlags.\n  void SetExceptionFlags(uint32_t exception_flags) {\n    exception_.ExceptionRecord.ExceptionFlags = exception_flags;\n  }\n\n  //! \\brief Sets MINIDUMP_EXCEPTION::ExceptionRecord.\n  void SetExceptionRecord(uint64_t exception_record) {\n    exception_.ExceptionRecord.ExceptionRecord = exception_record;\n  }\n\n  //! \\brief Sets MINIDUMP_EXCEPTION::ExceptionAddress.\n  void SetExceptionAddress(uint64_t exception_address) {\n    exception_.ExceptionRecord.ExceptionAddress = exception_address;\n  }\n\n  //! \\brief Sets MINIDUMP_EXCEPTION::ExceptionInformation and\n  //!     MINIDUMP_EXCEPTION::NumberParameters.\n  //!\n  //! MINIDUMP_EXCEPTION::NumberParameters is set to the number of elements in\n  //! \\a exception_information. The elements of\n  //! MINIDUMP_EXCEPTION::ExceptionInformation are set to the elements of \\a\n  //! exception_information. Unused elements in\n  //! MINIDUMP_EXCEPTION::ExceptionInformation are set to `0`.\n  //!\n  //! \\a exception_information must have no more than\n  //! #EXCEPTION_MAXIMUM_PARAMETERS elements.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetExceptionInformation(\n      const std::vector<uint64_t>& exception_information);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  MINIDUMP_EXCEPTION_STREAM exception_;\n  std::unique_ptr<MinidumpContextWriter> context_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_exception_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_exception_writer.h\"\n\n#include <iterator>\n#include <string>\n#include <utility>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_context.h\"\n#include \"minidump/minidump_context_writer.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_context_test_util.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_cpu_context.h\"\n#include \"snapshot/test/test_exception_snapshot.h\"\n#include \"test/gtest_death.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// This returns the MINIDUMP_EXCEPTION_STREAM stream in |exception_stream|.\nvoid GetExceptionStream(const std::string& file_contents,\n                        const MINIDUMP_EXCEPTION_STREAM** exception_stream) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kExceptionStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kContextOffset =\n      kExceptionStreamOffset + sizeof(MINIDUMP_EXCEPTION_STREAM);\n  constexpr size_t kFileSize = kContextOffset + sizeof(MinidumpContextX86);\n  ASSERT_EQ(kFileSize, file_contents.size());\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeException);\n  EXPECT_EQ(directory[0].Location.Rva, kExceptionStreamOffset);\n\n  *exception_stream =\n      MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>(\n          file_contents, directory[0].Location);\n  ASSERT_TRUE(exception_stream);\n}\n\n// The MINIDUMP_EXCEPTION_STREAMs |expected| and |observed| are compared against\n// each other using Google Test assertions. The context will be recovered from\n// |file_contents| and stored in |context|.\nvoid ExpectExceptionStream(const MINIDUMP_EXCEPTION_STREAM* expected,\n                           const MINIDUMP_EXCEPTION_STREAM* observed,\n                           const std::string& file_contents,\n                           const MinidumpContextX86** context) {\n  EXPECT_EQ(observed->ThreadId, expected->ThreadId);\n  EXPECT_EQ(observed->__alignment, 0u);\n\n  // Copy the ExceptionRecords so that their uint64_t members can be accessed\n  // with the proper alignment.\n  const MINIDUMP_EXCEPTION observed_exception = observed->ExceptionRecord;\n  const MINIDUMP_EXCEPTION expected_exception = expected->ExceptionRecord;\n\n  EXPECT_EQ(observed_exception.ExceptionCode, expected_exception.ExceptionCode);\n  EXPECT_EQ(observed_exception.ExceptionFlags,\n            expected_exception.ExceptionFlags);\n  EXPECT_EQ(observed_exception.ExceptionRecord,\n            expected_exception.ExceptionRecord);\n  EXPECT_EQ(observed_exception.ExceptionAddress,\n            expected_exception.ExceptionAddress);\n  EXPECT_EQ(observed_exception.NumberParameters,\n            expected_exception.NumberParameters);\n  EXPECT_EQ(observed->ExceptionRecord.__unusedAlignment, 0u);\n  for (size_t index = 0;\n       index < std::size(observed_exception.ExceptionInformation);\n       ++index) {\n    EXPECT_EQ(observed_exception.ExceptionInformation[index],\n              expected_exception.ExceptionInformation[index]);\n  }\n  *context = MinidumpWritableAtLocationDescriptor<MinidumpContextX86>(\n      file_contents, observed->ThreadContext);\n  ASSERT_TRUE(context);\n}\n\nTEST(MinidumpExceptionWriter, Minimal) {\n  MinidumpFileWriter minidump_file_writer;\n  auto exception_writer = std::make_unique<MinidumpExceptionWriter>();\n\n  constexpr uint32_t kSeed = 100;\n\n  auto context_x86_writer = std::make_unique<MinidumpContextX86Writer>();\n  InitializeMinidumpContextX86(context_x86_writer->context(), kSeed);\n  exception_writer->SetContext(std::move(context_x86_writer));\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_EXCEPTION_STREAM* observed_exception_stream = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetExceptionStream(string_file.string(), &observed_exception_stream));\n\n  MINIDUMP_EXCEPTION_STREAM expected_exception_stream = {};\n  expected_exception_stream.ThreadContext.DataSize = sizeof(MinidumpContextX86);\n\n  const MinidumpContextX86* observed_context = nullptr;\n  ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expected_exception_stream,\n                                                observed_exception_stream,\n                                                string_file.string(),\n                                                &observed_context));\n\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectMinidumpContextX86(kSeed, observed_context, false));\n}\n\nTEST(MinidumpExceptionWriter, Standard) {\n  MinidumpFileWriter minidump_file_writer;\n  auto exception_writer = std::make_unique<MinidumpExceptionWriter>();\n\n  constexpr uint32_t kSeed = 200;\n  constexpr uint32_t kThreadID = 1;\n  constexpr uint32_t kExceptionCode = 2;\n  constexpr uint32_t kExceptionFlags = 3;\n  constexpr uint32_t kExceptionRecord = 4;\n  constexpr uint32_t kExceptionAddress = 5;\n  constexpr uint64_t kExceptionInformation0 = 6;\n  constexpr uint64_t kExceptionInformation1 = 7;\n  constexpr uint64_t kExceptionInformation2 = 7;\n\n  auto context_x86_writer = std::make_unique<MinidumpContextX86Writer>();\n  InitializeMinidumpContextX86(context_x86_writer->context(), kSeed);\n  exception_writer->SetContext(std::move(context_x86_writer));\n\n  exception_writer->SetThreadID(kThreadID);\n  exception_writer->SetExceptionCode(kExceptionCode);\n  exception_writer->SetExceptionFlags(kExceptionFlags);\n  exception_writer->SetExceptionRecord(kExceptionRecord);\n  exception_writer->SetExceptionAddress(kExceptionAddress);\n\n  // Set a lot of exception information at first, and then replace it with less.\n  // This tests that the exception that is written does not contain the\n  // “garbage” from the initial SetExceptionInformation() call.\n  std::vector<uint64_t> exception_information(EXCEPTION_MAXIMUM_PARAMETERS,\n                                              0x5a5a5a5a5a5a5a5a);\n  exception_writer->SetExceptionInformation(exception_information);\n\n  exception_information.clear();\n  exception_information.push_back(kExceptionInformation0);\n  exception_information.push_back(kExceptionInformation1);\n  exception_information.push_back(kExceptionInformation2);\n  exception_writer->SetExceptionInformation(exception_information);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_EXCEPTION_STREAM* observed_exception_stream = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetExceptionStream(string_file.string(), &observed_exception_stream));\n\n  MINIDUMP_EXCEPTION_STREAM expected_exception_stream = {};\n  expected_exception_stream.ThreadId = kThreadID;\n  expected_exception_stream.ExceptionRecord.ExceptionCode = kExceptionCode;\n  expected_exception_stream.ExceptionRecord.ExceptionFlags = kExceptionFlags;\n  expected_exception_stream.ExceptionRecord.ExceptionRecord = kExceptionRecord;\n  expected_exception_stream.ExceptionRecord.ExceptionAddress =\n      kExceptionAddress;\n  expected_exception_stream.ExceptionRecord.NumberParameters =\n      static_cast<uint32_t>(exception_information.size());\n  for (size_t index = 0; index < exception_information.size(); ++index) {\n    expected_exception_stream.ExceptionRecord.ExceptionInformation[index] =\n        exception_information[index];\n  }\n  expected_exception_stream.ThreadContext.DataSize = sizeof(MinidumpContextX86);\n\n  const MinidumpContextX86* observed_context = nullptr;\n  ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expected_exception_stream,\n                                                observed_exception_stream,\n                                                string_file.string(),\n                                                &observed_context));\n\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectMinidumpContextX86(kSeed, observed_context, false));\n}\n\nTEST(MinidumpExceptionWriter, InitializeFromSnapshot) {\n  std::vector<uint64_t> exception_codes;\n  exception_codes.push_back(0x1000000000000000);\n  exception_codes.push_back(0x5555555555555555);\n\n  MINIDUMP_EXCEPTION_STREAM expect_exception = {};\n\n  expect_exception.ThreadId = 123;\n  expect_exception.ExceptionRecord.ExceptionCode = 100;\n  expect_exception.ExceptionRecord.ExceptionFlags = 1;\n  expect_exception.ExceptionRecord.ExceptionAddress = 0xfedcba9876543210;\n  expect_exception.ExceptionRecord.NumberParameters =\n      static_cast<uint32_t>(exception_codes.size());\n  for (size_t index = 0; index < exception_codes.size(); ++index) {\n    expect_exception.ExceptionRecord.ExceptionInformation[index] =\n        exception_codes[index];\n  }\n  constexpr uint64_t kThreadID = 0xaaaaaaaaaaaaaaaa;\n  constexpr uint32_t kSeed = 65;\n\n  TestExceptionSnapshot exception_snapshot;\n  exception_snapshot.SetThreadID(kThreadID);\n  exception_snapshot.SetException(\n      expect_exception.ExceptionRecord.ExceptionCode);\n  exception_snapshot.SetExceptionInfo(\n      expect_exception.ExceptionRecord.ExceptionFlags);\n  exception_snapshot.SetExceptionAddress(\n      expect_exception.ExceptionRecord.ExceptionAddress);\n  exception_snapshot.SetCodes(exception_codes);\n\n  InitializeCPUContextX86(exception_snapshot.MutableContext(), kSeed);\n\n  MinidumpThreadIDMap thread_id_map;\n  thread_id_map[kThreadID] = expect_exception.ThreadId;\n\n  auto exception_writer = std::make_unique<MinidumpExceptionWriter>();\n  exception_writer->InitializeFromSnapshot(\n      &exception_snapshot,\n      thread_id_map,\n      /*allow_missing_thread_id_from_map=*/false);\n\n  MinidumpFileWriter minidump_file_writer;\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_EXCEPTION_STREAM* exception = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetExceptionStream(string_file.string(), &exception));\n\n  const MinidumpContextX86* observed_context = nullptr;\n  ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expect_exception,\n                                                exception,\n                                                string_file.string(),\n                                                &observed_context));\n\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectMinidumpContextX86(kSeed, observed_context, true));\n}\n\nTEST(MinidumpExceptionWriterDeathTest, NoContext) {\n  MinidumpFileWriter minidump_file_writer;\n  auto exception_writer = std::make_unique<MinidumpExceptionWriter>();\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer)));\n\n  StringFile string_file;\n  ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),\n                     \"context_\");\n}\n\nTEST(MinidumpExceptionWriterDeathTest, TooMuchInformation) {\n  MinidumpExceptionWriter exception_writer;\n  std::vector<uint64_t> exception_information(EXCEPTION_MAXIMUM_PARAMETERS + 1,\n                                              0x5a5a5a5a5a5a5a5a);\n  ASSERT_DEATH_CHECK(\n      exception_writer.SetExceptionInformation(exception_information),\n      \"kMaxParameters\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_extensions.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_extensions.h\"\n\nnamespace crashpad {\n\nconstexpr uint32_t MinidumpModuleCrashpadInfo::kVersion;\nconstexpr uint32_t MinidumpCrashpadInfo::kVersion;\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_extensions.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <winnt.h>\n\n#include \"base/compiler_specific.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/pdb_structures.h\"\n#include \"util/misc/uuid.h\"\n\n#if defined(COMPILER_MSVC)\n// C4200 is \"nonstandard extension used : zero-sized array in struct/union\".\n// We would like to globally disable this warning, but unfortunately, the\n// compiler is buggy and only supports disabling it with a pragma, so we can't\n// disable it with other silly warnings in the build files. See:\n//   https://connect.microsoft.com/VisualStudio/feedback/details/1114440\n#pragma warning(push)\n#pragma warning(disable: 4200)\n\n#define PACKED\n#pragma pack(push, 1)\n#else\n#define PACKED __attribute__((packed))\n#endif  // COMPILER_MSVC\n\nnamespace crashpad {\n\n//! \\brief Minidump stream type values for MINIDUMP_DIRECTORY::StreamType. Each\n//!     stream structure has a corresponding stream type value to identify it.\n//!\n//! \\sa MINIDUMP_STREAM_TYPE\nenum MinidumpStreamType : uint32_t {\n  //! \\brief The stream type for MINIDUMP_THREAD_LIST.\n  //!\n  //! \\sa ThreadListStream\n  kMinidumpStreamTypeThreadList = ThreadListStream,\n\n  //! \\brief The stream type for MINIDUMP_MODULE_LIST.\n  //!\n  //! \\sa ModuleListStream\n  kMinidumpStreamTypeModuleList = ModuleListStream,\n\n  //! \\brief The stream type for MINIDUMP_MEMORY_LIST.\n  //!\n  //! \\sa MemoryListStream\n  kMinidumpStreamTypeMemoryList = MemoryListStream,\n\n  //! \\brief The stream type for MINIDUMP_EXCEPTION_STREAM.\n  //!\n  //! \\sa ExceptionStream\n  kMinidumpStreamTypeException = ExceptionStream,\n\n  //! \\brief The stream type for MINIDUMP_SYSTEM_INFO.\n  //!\n  //! \\sa SystemInfoStream\n  kMinidumpStreamTypeSystemInfo = SystemInfoStream,\n\n  //! \\brief The stream type for MINIDUMP_HANDLE_DATA_STREAM.\n  //!\n  //! \\sa HandleDataStream\n  kMinidumpStreamTypeHandleData = HandleDataStream,\n\n  //! \\brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST.\n  //!\n  //! \\sa UnloadedModuleListStream\n  kMinidumpStreamTypeUnloadedModuleList = UnloadedModuleListStream,\n\n  //! \\brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,\n  //!     MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.\n  //!\n  //! \\sa MiscInfoStream\n  kMinidumpStreamTypeMiscInfo = MiscInfoStream,\n\n  //! \\brief The stream type for MINIDUMP_MEMORY_INFO_LIST.\n  //!\n  //! \\sa MemoryInfoListStream\n  kMinidumpStreamTypeMemoryInfoList = MemoryInfoListStream,\n\n  //! \\brief The stream type for MINIDUMP_THREAD_NAME_LIST.\n  //!\n  //! \\sa ThreadNamesStream\n  kMinidumpStreamTypeThreadNameList = ThreadNamesStream,\n\n  //! \\brief The last reserved minidump stream.\n  //!\n  //! \\sa MemoryInfoListStream\n  kMinidumpStreamTypeLastReservedStream = LastReservedStream,\n\n  // 0x4350 = \"CP\"\n\n  //! \\brief The stream type for MinidumpCrashpadInfo.\n  kMinidumpStreamTypeCrashpadInfo = 0x43500001,\n\n  //! \\brief The last reserved crashpad stream.\n  kMinidumpStreamTypeCrashpadLastReservedStream = 0x4350ffff,\n};\n\n//! \\brief A variable-length UTF-8-encoded string carried within a minidump\n//!     file.\n//!\n//! \\sa MINIDUMP_STRING\nstruct alignas(4) PACKED MinidumpUTF8String {\n  // The field names do not conform to typical style, they match the names used\n  // in MINIDUMP_STRING. This makes it easier to operate on MINIDUMP_STRING (for\n  // UTF-16 strings) and MinidumpUTF8String using templates.\n\n  //! \\brief The length of the #Buffer field in bytes, not including the `NUL`\n  //!     terminator.\n  //!\n  //! \\note This field is interpreted as a byte count, not a count of Unicode\n  //!     code points.\n  uint32_t Length;\n\n  //! \\brief The string, encoded in UTF-8, and terminated with a `NUL` byte.\n  uint8_t Buffer[0];\n};\n\n//! \\brief A variable-length array of bytes carried within a minidump file.\n//!     The data have no intrinsic type and should be interpreted according\n//!     to their referencing context.\nstruct alignas(4) PACKED MinidumpByteArray {\n  //! \\brief The length of the #data field.\n  uint32_t length;\n\n  //! \\brief The bytes of data.\n  uint8_t data[0];\n};\n\n//! \\brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.\n//!\n//! \\sa \\ref PROCESSOR_ARCHITECTURE_x \"PROCESSOR_ARCHITECTURE_*\"\nenum MinidumpCPUArchitecture : uint16_t {\n  //! \\brief 32-bit x86.\n  //!\n  //! These systems identify their CPUs generically as “x86” or “ia32”, or with\n  //! more specific names such as “i386”, “i486”, “i586”, and “i686”.\n  kMinidumpCPUArchitectureX86 = PROCESSOR_ARCHITECTURE_INTEL,\n\n  kMinidumpCPUArchitectureMIPS = PROCESSOR_ARCHITECTURE_MIPS,\n  kMinidumpCPUArchitectureAlpha = PROCESSOR_ARCHITECTURE_ALPHA,\n\n  //! \\brief 32-bit PowerPC.\n  //!\n  //! These systems identify their CPUs generically as “ppc”, or with more\n  //! specific names such as “ppc6xx”, “ppc7xx”, and “ppc74xx”.\n  kMinidumpCPUArchitecturePPC = PROCESSOR_ARCHITECTURE_PPC,\n\n  kMinidumpCPUArchitectureSHx = PROCESSOR_ARCHITECTURE_SHX,\n\n  //! \\brief 32-bit ARM.\n  //!\n  //! These systems identify their CPUs generically as “arm”, or with more\n  //! specific names such as “armv6” and “armv7”.\n  kMinidumpCPUArchitectureARM = PROCESSOR_ARCHITECTURE_ARM,\n\n  kMinidumpCPUArchitectureIA64 = PROCESSOR_ARCHITECTURE_IA64,\n  kMinidumpCPUArchitectureAlpha64 = PROCESSOR_ARCHITECTURE_ALPHA64,\n  kMinidumpCPUArchitectureMSIL = PROCESSOR_ARCHITECTURE_MSIL,\n\n  //! \\brief 64-bit x86.\n  //!\n  //! These systems identify their CPUs as “x86_64”, “amd64”, or “x64”.\n  kMinidumpCPUArchitectureAMD64 = PROCESSOR_ARCHITECTURE_AMD64,\n\n  //! \\brief A 32-bit x86 process running on IA-64 (Itanium).\n  //!\n  //! \\note This value is not used in minidump files for 32-bit x86 processes\n  //!     running on a 64-bit-capable x86 CPU and operating system. In that\n  //!     configuration, #kMinidumpCPUArchitectureX86 is used instead.\n  kMinidumpCPUArchitectureX86Win64 = PROCESSOR_ARCHITECTURE_IA32_ON_WIN64,\n\n  kMinidumpCPUArchitectureNeutral = PROCESSOR_ARCHITECTURE_NEUTRAL,\n\n  //! \\brief 64-bit ARM.\n  //!\n  //! These systems identify their CPUs generically as “arm64” or “aarch64”, or\n  //! with more specific names such as “armv8”.\n  //!\n  //! \\sa #kMinidumpCPUArchitectureARM64Breakpad\n  kMinidumpCPUArchitectureARM64 = PROCESSOR_ARCHITECTURE_ARM64,\n\n  kMinidumpCPUArchitectureARM32Win64 = PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64,\n  kMinidumpCPUArchitectureSPARC = 0x8001,\n\n  //! \\brief 64-bit PowerPC.\n  //!\n  //! These systems identify their CPUs generically as “ppc64”, or with more\n  //! specific names such as “ppc970”.\n  kMinidumpCPUArchitecturePPC64 = 0x8002,\n\n  //! \\brief Used by Breakpad for 64-bit ARM.\n  //!\n  //! \\deprecated Use #kMinidumpCPUArchitectureARM64 instead.\n  kMinidumpCPUArchitectureARM64Breakpad = 0x8003,\n\n  //! \\brief Used by Breakpad for 64-bit RISC-V.\n  kMinidumpCPUArchitectureRISCV64Breakpad = 0x8006,\n\n  //! \\brief Unknown CPU architecture.\n  kMinidumpCPUArchitectureUnknown = PROCESSOR_ARCHITECTURE_UNKNOWN,\n};\n\n//! \\brief Operating system type values for MINIDUMP_SYSTEM_INFO::ProductType.\n//!\n//! \\sa \\ref VER_NT_x \"VER_NT_*\"\nenum MinidumpOSType : uint8_t {\n  //! \\brief A “desktop” or “workstation” system.\n  kMinidumpOSTypeWorkstation = VER_NT_WORKSTATION,\n\n  //! \\brief A “domain controller” system. Windows-specific.\n  kMinidumpOSTypeDomainController = VER_NT_DOMAIN_CONTROLLER,\n\n  //! \\brief A “server” system.\n  kMinidumpOSTypeServer = VER_NT_SERVER,\n};\n\n//! \\brief Operating system family values for MINIDUMP_SYSTEM_INFO::PlatformId.\n//!\n//! \\sa \\ref VER_PLATFORM_x \"VER_PLATFORM_*\"\nenum MinidumpOS : uint32_t {\n  //! \\brief Windows 3.1.\n  kMinidumpOSWin32s = VER_PLATFORM_WIN32s,\n\n  //! \\brief Windows 95, Windows 98, and Windows Me.\n  kMinidumpOSWin32Windows = VER_PLATFORM_WIN32_WINDOWS,\n\n  //! \\brief Windows NT, Windows 2000, and later.\n  kMinidumpOSWin32NT = VER_PLATFORM_WIN32_NT,\n\n  kMinidumpOSUnix = 0x8000,\n\n  //! \\brief macOS, Darwin for traditional systems.\n  kMinidumpOSMacOSX = 0x8101,\n\n  //! \\brief iOS, Darwin for mobile devices.\n  kMinidumpOSIOS = 0x8102,\n\n  //! \\brief Linux, not including Android.\n  kMinidumpOSLinux = 0x8201,\n\n  kMinidumpOSSolaris = 0x8202,\n\n  //! \\brief Android.\n  kMinidumpOSAndroid = 0x8203,\n\n  kMinidumpOSPS3 = 0x8204,\n\n  //! \\brief Native Client (NaCl).\n  kMinidumpOSNaCl = 0x8205,\n\n  //! \\brief Fuchsia.\n  kMinidumpOSFuchsia = 0x8206,\n\n  //! \\brief Unknown operating system.\n  kMinidumpOSUnknown = 0xffffffff,\n};\n\n//! \\brief A list of ::RVA pointers.\nstruct alignas(4) PACKED MinidumpRVAList {\n  //! \\brief The number of children present in the #children array.\n  uint32_t count;\n\n  //! \\brief Pointers to other structures in the minidump file.\n  RVA children[0];\n};\n\n//! \\brief A key-value pair.\nstruct alignas(4) PACKED MinidumpSimpleStringDictionaryEntry {\n  //! \\brief ::RVA of a MinidumpUTF8String containing the key of a key-value\n  //!     pair.\n  RVA key;\n\n  //! \\brief ::RVA of a MinidumpUTF8String containing the value of a key-value\n  //!     pair.\n  RVA value;\n};\n\n//! \\brief A list of key-value pairs.\nstruct alignas(4) PACKED MinidumpSimpleStringDictionary {\n  //! \\brief The number of key-value pairs present.\n  uint32_t count;\n\n  //! \\brief A list of MinidumpSimpleStringDictionaryEntry entries.\n  MinidumpSimpleStringDictionaryEntry entries[0];\n};\n\n//! \\brief A typed annotation object.\nstruct alignas(4) PACKED MinidumpAnnotation {\n  //! \\brief ::RVA of a MinidumpUTF8String containing the name of the\n  //!     annotation.\n  RVA name;\n\n  //! \\brief The type of data stored in the \\a value of the annotation. This\n  //!     may correspond to an \\a Annotation::Type or it may be user-defined.\n  uint16_t type;\n\n  //! \\brief This field is always `0`.\n  uint16_t reserved;\n\n  //! \\brief ::RVA of a MinidumpByteArray to the data for the annotation.\n  RVA value;\n};\n\n//! \\brief A list of annotation objects.\nstruct alignas(4) PACKED MinidumpAnnotationList {\n  //! \\brief The number of annotation objects present.\n  uint32_t count;\n\n  //! \\brief A list of MinidumpAnnotation objects.\n  MinidumpAnnotation objects[0];\n};\n\n//! \\brief Additional Crashpad-specific information about a module carried\n//!     within a minidump file.\n//!\n//! This structure augments the information provided by MINIDUMP_MODULE. The\n//! minidump file must contain a module list stream\n//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear.\n//!\n//! This structure is versioned. When changing this structure, leave the\n//! existing structure intact so that earlier parsers will be able to understand\n//! the fields they are aware of, and make additions at the end of the\n//! structure. Revise #kVersion and document each field’s validity based on\n//! #version, so that newer parsers will be able to determine whether the added\n//! fields are valid or not.\n//!\n//! \\sa MinidumpModuleCrashpadInfoList\nstruct alignas(4) PACKED MinidumpModuleCrashpadInfo {\n  //! \\brief The structure’s currently-defined version number.\n  //!\n  //! \\sa version\n  static constexpr uint32_t kVersion = 1;\n\n  //! \\brief The structure’s version number.\n  //!\n  //! Readers can use this field to determine which other fields in the\n  //! structure are valid. Upon encountering a value greater than #kVersion, a\n  //! reader should assume that the structure’s layout is compatible with the\n  //! structure defined as having value #kVersion.\n  //!\n  //! Writers may produce values less than #kVersion in this field if there is\n  //! no need for any fields present in later versions.\n  uint32_t version;\n\n  //! \\brief A MinidumpRVAList pointing to MinidumpUTF8String objects. The\n  //!     module controls the data that appears here.\n  //!\n  //! These strings correspond to ModuleSnapshot::AnnotationsVector() and do not\n  //! duplicate anything in #simple_annotations or #annotation_objects.\n  //!\n  //! This field is present when #version is at least `1`.\n  MINIDUMP_LOCATION_DESCRIPTOR list_annotations;\n\n  //! \\brief A MinidumpSimpleStringDictionary pointing to strings interpreted as\n  //!     key-value pairs. The module controls the data that appears here.\n  //!\n  //! These key-value pairs correspond to\n  //! ModuleSnapshot::AnnotationsSimpleMap() and do not duplicate anything in\n  //! #list_annotations or #annotation_objects.\n  //!\n  //! This field is present when #version is at least `1`.\n  MINIDUMP_LOCATION_DESCRIPTOR simple_annotations;\n\n  //! \\brief A MinidumpAnnotationList object containing the annotation objects\n  //!     stored within the module. The module controls the data that appears\n  //!     here.\n  //!\n  //! These key-value pairs correspond to ModuleSnapshot::AnnotationObjects()\n  //! and do not duplicate anything in #list_annotations or #simple_annotations.\n  //!\n  //! This field may be present when #version is at least `1`.\n  MINIDUMP_LOCATION_DESCRIPTOR annotation_objects;\n};\n\n//! \\brief A link between a MINIDUMP_MODULE structure and additional\n//!     Crashpad-specific information about a module carried within a minidump\n//!     file.\nstruct alignas(4) PACKED MinidumpModuleCrashpadInfoLink {\n  //! \\brief A link to a MINIDUMP_MODULE structure in the module list stream.\n  //!\n  //! This field is an index into MINIDUMP_MODULE_LIST::Modules. This field’s\n  //! value must be in the range of MINIDUMP_MODULE_LIST::NumberOfEntries.\n  uint32_t minidump_module_list_index;\n\n  //! \\brief A link to a MinidumpModuleCrashpadInfo structure.\n  //!\n  //! MinidumpModuleCrashpadInfo structures are accessed indirectly through\n  //! MINIDUMP_LOCATION_DESCRIPTOR pointers to allow for future growth of the\n  //! MinidumpModuleCrashpadInfo structure.\n  MINIDUMP_LOCATION_DESCRIPTOR location;\n};\n\n//! \\brief Additional Crashpad-specific information about modules carried within\n//!     a minidump file.\n//!\n//! This structure augments the information provided by\n//! MINIDUMP_MODULE_LIST. The minidump file must contain a module list stream\n//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear.\n//!\n//! MinidumpModuleCrashpadInfoList::count may be less than the value of\n//! MINIDUMP_MODULE_LIST::NumberOfModules because not every MINIDUMP_MODULE\n//! structure carried within the minidump file will necessarily have\n//! Crashpad-specific information provided by a MinidumpModuleCrashpadInfo\n//! structure.\nstruct alignas(4) PACKED MinidumpModuleCrashpadInfoList {\n  //! \\brief The number of children present in the #modules array.\n  uint32_t count;\n\n  //! \\brief Crashpad-specific information about modules, along with links to\n  //!     MINIDUMP_MODULE structures that contain module information\n  //!     traditionally carried within minidump files.\n  MinidumpModuleCrashpadInfoLink modules[0];\n};\n\n//! \\brief Additional Crashpad-specific information carried within a minidump\n//!     file.\n//!\n//! This structure is versioned. When changing this structure, leave the\n//! existing structure intact so that earlier parsers will be able to understand\n//! the fields they are aware of, and make additions at the end of the\n//! structure. Revise #kVersion and document each field’s validity based on\n//! #version, so that newer parsers will be able to determine whether the added\n//! fields are valid or not.\nstruct alignas(4) PACKED MinidumpCrashpadInfo {\n  // UUID has a constructor, which makes it non-POD, which makes this structure\n  // non-POD. In order for the default constructor to zero-initialize other\n  // members, an explicit constructor must be provided.\n  MinidumpCrashpadInfo()\n      : version(),\n        report_id(),\n        client_id(),\n        simple_annotations(),\n        module_list(),\n        reserved(),\n        address_mask() {}\n\n  //! \\brief The structure’s currently-defined version number.\n  //!\n  //! \\sa version\n  static constexpr uint32_t kVersion = 1;\n\n  //! \\brief The structure’s version number.\n  //!\n  //! Readers can use this field to determine which other fields in the\n  //! structure are valid. Upon encountering a value greater than #kVersion, a\n  //! reader should assume that the structure’s layout is compatible with the\n  //! structure defined as having value #kVersion.\n  //!\n  //! Writers may produce values less than #kVersion in this field if there is\n  //! no need for any fields present in later versions.\n  uint32_t version;\n\n  //! \\brief A %UUID identifying an individual crash report.\n  //!\n  //! This provides a stable identifier for a crash even as the report is\n  //! converted to different formats, provided that all formats support storing\n  //! a crash report ID.\n  //!\n  //! If no identifier is available, this field will contain zeroes.\n  //!\n  //! This field is present when #version is at least `1`.\n  UUID report_id;\n\n  //! \\brief A %UUID identifying the client that crashed.\n  //!\n  //! Client identification is within the scope of the application, but it is\n  //! expected that the identifier will be unique for an instance of Crashpad\n  //! monitoring an application or set of applications for a user. The\n  //! identifier shall remain stable over time.\n  //!\n  //! If no identifier is available, this field will contain zeroes.\n  //!\n  //! This field is present when #version is at least `1`.\n  UUID client_id;\n\n  //! \\brief A MinidumpSimpleStringDictionary pointing to strings interpreted as\n  //!     key-value pairs.\n  //!\n  //! These key-value pairs correspond to\n  //! ProcessSnapshot::AnnotationsSimpleMap().\n  //!\n  //! This field is present when #version is at least `1`.\n  MINIDUMP_LOCATION_DESCRIPTOR simple_annotations;\n\n  //! \\brief A pointer to a MinidumpModuleCrashpadInfoList structure.\n  //!\n  //! This field is present when #version is at least `1`.\n  MINIDUMP_LOCATION_DESCRIPTOR module_list;\n\n  //! \\brief This field is always `0`.\n  uint32_t reserved;\n\n  //! \\brief A mask indicating the range of valid addresses for a pointer.\n  //!\n  //! ARM64 supports MTE, TBI and PAC masking, generally in the upper bits of\n  //! a pointer. This mask can be used by LLDB to mimic ptrauth_strip and strip\n  //! the pointer authentication codes. To recover `pointer` in userland on\n  //! Darwin, `pointer & (~mask)`. In the case of code running in high memory,\n  //! where bit 55 is set (indicating that all of the high bits should be set\n  //! to 1), `pointer | mask`. See ABIMacOSX_arm64::FixAddress for more details\n  //! here:\n  //! https://github.com/llvm/llvm-project/blob/001d18664f8bcf63af64f10688809f7681dfbf0b/lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp#L817-L830\n  //!\n  //! If the platform does not support pointer authentication, or the range of\n  //! valid addressees for a pointer was inaccessible, this field will be 0 and\n  //! should be ignored.\n  //!\n  //! This field is present when #version is at least `1`, if the size of the\n  //! structure is large enough to accommodate it.\n  uint64_t address_mask;\n};\n\n#if defined(COMPILER_MSVC)\n#pragma pack(pop)\n#pragma warning(pop)  // C4200\n#endif  // COMPILER_MSVC\n#undef PACKED\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_\n"
  },
  {
    "path": "minidump/minidump_file_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_file_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"minidump/minidump_crashpad_info_writer.h\"\n#include \"minidump/minidump_exception_writer.h\"\n#include \"minidump/minidump_handle_writer.h\"\n#include \"minidump/minidump_memory_info_writer.h\"\n#include \"minidump/minidump_memory_writer.h\"\n#include \"minidump/minidump_misc_info_writer.h\"\n#include \"minidump/minidump_module_writer.h\"\n#include \"minidump/minidump_system_info_writer.h\"\n#include \"minidump/minidump_thread_id_map.h\"\n#include \"minidump/minidump_thread_name_list_writer.h\"\n#include \"minidump/minidump_thread_writer.h\"\n#include \"minidump/minidump_unloaded_module_writer.h\"\n#include \"minidump/minidump_user_extension_stream_data_source.h\"\n#include \"minidump/minidump_user_stream_writer.h\"\n#include \"minidump/minidump_writer_util.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpFileWriter::MinidumpFileWriter()\n    : MinidumpWritable(), header_(), streams_(), stream_types_() {\n  // Don’t set the signature field right away. Leave it set to 0, so that a\n  // partially-written minidump file isn’t confused for a complete and valid\n  // one. The header will be rewritten in WriteToFile().\n  header_.Signature = 0;\n\n  header_.Version = MINIDUMP_VERSION;\n  header_.CheckSum = 0;\n  header_.Flags = MiniDumpNormal;\n}\n\nMinidumpFileWriter::~MinidumpFileWriter() {\n}\n\nvoid MinidumpFileWriter::InitializeFromSnapshot(\n    const ProcessSnapshot* process_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(header_.Signature, 0u);\n  DCHECK_EQ(header_.TimeDateStamp, 0u);\n  DCHECK_EQ(static_cast<MINIDUMP_TYPE>(header_.Flags), MiniDumpNormal);\n  DCHECK(streams_.empty());\n\n  // This time is truncated to an integer number of seconds, not rounded, for\n  // compatibility with the truncation of process_snapshot->ProcessStartTime()\n  // done by MinidumpMiscInfoWriter::InitializeFromSnapshot(). Handling both\n  // timestamps in the same way allows the highest-fidelity computation of\n  // process uptime as the difference between the two values.\n  timeval snapshot_time;\n  process_snapshot->SnapshotTime(&snapshot_time);\n  SetTimestamp(snapshot_time.tv_sec);\n\n  const SystemSnapshot* system_snapshot = process_snapshot->System();\n  auto system_info = std::make_unique<MinidumpSystemInfoWriter>();\n  system_info->InitializeFromSnapshot(system_snapshot);\n  bool add_stream_result = AddStream(std::move(system_info));\n  DCHECK(add_stream_result);\n\n  auto misc_info = std::make_unique<MinidumpMiscInfoWriter>();\n  misc_info->InitializeFromSnapshot(process_snapshot);\n  if (misc_info->HasXStateData())\n    header_.Flags = header_.Flags | MiniDumpWithAvxXStateContext;\n\n  add_stream_result = AddStream(std::move(misc_info));\n  DCHECK(add_stream_result);\n\n  auto memory_list = std::make_unique<MinidumpMemoryListWriter>();\n  auto thread_list = std::make_unique<MinidumpThreadListWriter>();\n  thread_list->SetMemoryListWriter(memory_list.get());\n  MinidumpThreadIDMap thread_id_map;\n  thread_list->InitializeFromSnapshot(process_snapshot->Threads(),\n                                      &thread_id_map);\n  add_stream_result = AddStream(std::move(thread_list));\n  DCHECK(add_stream_result);\n\n  bool has_thread_name = false;\n  for (const ThreadSnapshot* thread_snapshot : process_snapshot->Threads()) {\n    if (!thread_snapshot->ThreadName().empty()) {\n      has_thread_name = true;\n      break;\n    }\n  }\n  if (has_thread_name) {\n    auto thread_name_list = std::make_unique<MinidumpThreadNameListWriter>();\n    thread_name_list->InitializeFromSnapshot(process_snapshot->Threads(),\n                                             thread_id_map);\n    add_stream_result = AddStream(std::move(thread_name_list));\n    DCHECK(add_stream_result);\n  }\n\n  const ExceptionSnapshot* exception_snapshot = process_snapshot->Exception();\n  if (exception_snapshot) {\n    auto exception = std::make_unique<MinidumpExceptionWriter>();\n#if BUILDFLAG(IS_IOS)\n    // It's expected that iOS intermediate dumps can be written with missing\n    // information, but it's better to try and report as much as possible\n    // rather than drop the incomplete minidump.\n    constexpr bool allow_missing_thread_id_from_map = true;\n#else\n    constexpr bool allow_missing_thread_id_from_map = false;\n#endif\n    exception->InitializeFromSnapshot(\n        exception_snapshot, thread_id_map, allow_missing_thread_id_from_map);\n    add_stream_result = AddStream(std::move(exception));\n    DCHECK(add_stream_result);\n  }\n\n  auto module_list = std::make_unique<MinidumpModuleListWriter>();\n  module_list->InitializeFromSnapshot(process_snapshot->Modules());\n  add_stream_result = AddStream(std::move(module_list));\n  DCHECK(add_stream_result);\n\n  auto unloaded_modules = process_snapshot->UnloadedModules();\n  if (!unloaded_modules.empty()) {\n    auto unloaded_module_list =\n        std::make_unique<MinidumpUnloadedModuleListWriter>();\n    unloaded_module_list->InitializeFromSnapshot(unloaded_modules);\n    add_stream_result = AddStream(std::move(unloaded_module_list));\n    DCHECK(add_stream_result);\n  }\n\n  auto crashpad_info = std::make_unique<MinidumpCrashpadInfoWriter>();\n  crashpad_info->InitializeFromSnapshot(process_snapshot);\n\n  // Since the MinidumpCrashpadInfo stream is an extension, it’s safe to not add\n  // it to the minidump file if it wouldn’t carry any useful information.\n  if (crashpad_info->IsUseful()) {\n    add_stream_result = AddStream(std::move(crashpad_info));\n    DCHECK(add_stream_result);\n  }\n\n  std::vector<const MemoryMapRegionSnapshot*> memory_map_snapshot =\n      process_snapshot->MemoryMap();\n  if (!memory_map_snapshot.empty()) {\n    auto memory_info_list = std::make_unique<MinidumpMemoryInfoListWriter>();\n    memory_info_list->InitializeFromSnapshot(memory_map_snapshot);\n    add_stream_result = AddStream(std::move(memory_info_list));\n    DCHECK(add_stream_result);\n  }\n\n  std::vector<HandleSnapshot> handles_snapshot = process_snapshot->Handles();\n  if (!handles_snapshot.empty()) {\n    auto handle_data_writer = std::make_unique<MinidumpHandleDataWriter>();\n    handle_data_writer->InitializeFromSnapshot(handles_snapshot);\n    add_stream_result = AddStream(std::move(handle_data_writer));\n    DCHECK(add_stream_result);\n  }\n\n  memory_list->AddFromSnapshot(process_snapshot->ExtraMemory());\n  if (exception_snapshot) {\n    memory_list->AddFromSnapshot(exception_snapshot->ExtraMemory());\n  }\n\n  // These user streams must be added last. Otherwise, a user stream with the\n  // same type as a well-known stream could preempt the well-known stream. As it\n  // stands now, earlier-discovered user streams can still preempt\n  // later-discovered ones. The well-known memory list stream is added after\n  // these user streams, but only with a check here to avoid adding a user\n  // stream that would preempt the memory list stream.\n  for (const auto& module : process_snapshot->Modules()) {\n    for (const UserMinidumpStream* stream : module->CustomMinidumpStreams()) {\n      if (stream->stream_type() == kMinidumpStreamTypeMemoryList) {\n        LOG(WARNING) << \"discarding duplicate stream of type \"\n                     << stream->stream_type();\n        continue;\n      }\n      auto user_stream = std::make_unique<MinidumpUserStreamWriter>();\n      user_stream->InitializeFromSnapshot(stream);\n      AddStream(std::move(user_stream));\n    }\n  }\n\n  // The memory list stream should be added last. This keeps the “extra memory”\n  // at the end so that if the minidump file is truncated, other, more critical\n  // data is more likely to be preserved. Note that non-“extra” memory regions\n  // will not have to ride at the end of the file. Thread stack memory, for\n  // example, exists as a children of threads, and appears alongside them in the\n  // file, despite also being mentioned by the memory list stream.\n  add_stream_result = AddStream(std::move(memory_list));\n  DCHECK(add_stream_result);\n}\n\nvoid MinidumpFileWriter::SetTimestamp(time_t timestamp) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  internal::MinidumpWriterUtil::AssignTimeT(&header_.TimeDateStamp, timestamp);\n}\n\nbool MinidumpFileWriter::AddStream(\n    std::unique_ptr<internal::MinidumpStreamWriter> stream) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  MinidumpStreamType stream_type = stream->StreamType();\n\n  auto rv = stream_types_.insert(stream_type);\n  if (!rv.second) {\n    LOG(WARNING) << \"discarding duplicate stream of type \" << stream_type;\n    return false;\n  }\n\n  streams_.push_back(std::move(stream));\n\n  DCHECK_EQ(streams_.size(), stream_types_.size());\n  return true;\n}\n\nbool MinidumpFileWriter::AddUserExtensionStream(\n    std::unique_ptr<MinidumpUserExtensionStreamDataSource>\n        user_extension_stream_data) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  auto user_stream = std::make_unique<MinidumpUserStreamWriter>();\n  user_stream->InitializeFromUserExtensionStream(\n      std::move(user_extension_stream_data));\n\n  return AddStream(std::move(user_stream));\n}\n\nbool MinidumpFileWriter::WriteEverything(FileWriterInterface* file_writer) {\n  return WriteMinidump(file_writer, true);\n}\n\nbool MinidumpFileWriter::WriteMinidump(FileWriterInterface* file_writer,\n                                       bool allow_seek) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  FileOffset start_offset = -1;\n  if (allow_seek) {\n    start_offset = file_writer->Seek(0, SEEK_CUR);\n    if (start_offset < 0) {\n      return false;\n    }\n  } else {\n    header_.Signature = MINIDUMP_SIGNATURE;\n  }\n\n  if (!MinidumpWritable::WriteEverything(file_writer)) {\n    return false;\n  }\n\n  if (!allow_seek)\n    return true;\n\n  FileOffset end_offset = file_writer->Seek(0, SEEK_CUR);\n  if (end_offset < 0) {\n    return false;\n  }\n\n  // Now that the entire minidump file has been completely written, go back to\n  // the beginning and rewrite the header with the correct signature to identify\n  // it as a valid minidump file.\n  header_.Signature = MINIDUMP_SIGNATURE;\n\n  if (file_writer->Seek(start_offset, SEEK_SET) < 0) {\n    return false;\n  }\n\n  if (!file_writer->Write(&header_, sizeof(header_))) {\n    return false;\n  }\n\n  // Seek back to the end of the file, in case some non-minidump content will be\n  // written to the file after the minidump content.\n  return file_writer->Seek(end_offset, SEEK_SET) >= 0;\n}\n\nbool MinidumpFileWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  size_t stream_count = streams_.size();\n  CHECK_EQ(stream_count, stream_types_.size());\n\n  if (!AssignIfInRange(&header_.NumberOfStreams, stream_count)) {\n    LOG(ERROR) << \"stream_count \" << stream_count << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpFileWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK_EQ(streams_.size(), stream_types_.size());\n\n  return sizeof(header_) + streams_.size() * sizeof(MINIDUMP_DIRECTORY);\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpFileWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK_EQ(streams_.size(), stream_types_.size());\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& stream : streams_) {\n    children.push_back(stream.get());\n  }\n\n  return children;\n}\n\nbool MinidumpFileWriter::WillWriteAtOffsetImpl(FileOffset offset) {\n  DCHECK_EQ(state(), kStateFrozen);\n  DCHECK_EQ(offset, 0);\n  DCHECK_EQ(streams_.size(), stream_types_.size());\n\n  auto directory_offset = streams_.empty() ? 0 : offset + sizeof(header_);\n  if (!AssignIfInRange(&header_.StreamDirectoryRva, directory_offset)) {\n    LOG(ERROR) << \"offset \" << directory_offset << \" out of range\";\n    return false;\n  }\n\n  return MinidumpWritable::WillWriteAtOffsetImpl(offset);\n}\n\nbool MinidumpFileWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  DCHECK_EQ(streams_.size(), stream_types_.size());\n\n  WritableIoVec iov;\n  iov.iov_base = &header_;\n  iov.iov_len = sizeof(header_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  for (const auto& stream : streams_) {\n    iov.iov_base = stream->DirectoryListEntry();\n    iov.iov_len = sizeof(MINIDUMP_DIRECTORY);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_file_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_FILE_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_FILE_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <set>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_writable.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\n\nclass ProcessSnapshot;\nclass MinidumpUserExtensionStreamDataSource;\n\n//! \\brief The root-level object in a minidump file.\n//!\n//! This object writes a MINIDUMP_HEADER and list of MINIDUMP_DIRECTORY entries\n//! to a minidump file.\nclass MinidumpFileWriter final : public internal::MinidumpWritable {\n public:\n  MinidumpFileWriter();\n\n  MinidumpFileWriter(const MinidumpFileWriter&) = delete;\n  MinidumpFileWriter& operator=(const MinidumpFileWriter&) = delete;\n\n  ~MinidumpFileWriter() override;\n\n  //! \\brief Initializes the MinidumpFileWriter and populates it with\n  //!     appropriate child streams based on \\a process_snapshot.\n  //!\n  //! This method will add additional streams to the minidump file as children\n  //! of the MinidumpFileWriter object and as pointees of the top-level\n  //! MINIDUMP_DIRECTORY. To do so, it will obtain other snapshot information\n  //! from \\a process_snapshot, such as a SystemSnapshot, lists of\n  //! ThreadSnapshot and ModuleSnapshot objects, and, if available, an\n  //! ExceptionSnapshot.\n  //!\n  //! The streams are added in the order that they are expected to be most\n  //! useful to minidump readers, to improve data locality and minimize seeking.\n  //! The streams are added in this order:\n  //!  - kMinidumpStreamTypeSystemInfo\n  //!  - kMinidumpStreamTypeMiscInfo\n  //!  - kMinidumpStreamTypeThreadList\n  //!  - kMinidumpStreamTypeException (if present)\n  //!  - kMinidumpStreamTypeModuleList\n  //!  - kMinidumpStreamTypeUnloadedModuleList (if present)\n  //!  - kMinidumpStreamTypeCrashpadInfo (if present)\n  //!  - kMinidumpStreamTypeMemoryInfoList (if present)\n  //!  - kMinidumpStreamTypeHandleData (if present)\n  //!  - User streams (if present)\n  //!  - kMinidumpStreamTypeMemoryList\n  //!\n  //! \\param[in] process_snapshot The process snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot);\n\n  //! \\brief Sets MINIDUMP_HEADER::Timestamp.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetTimestamp(time_t timestamp);\n\n  //! \\brief Adds a stream to the minidump file and arranges for a\n  //!     MINIDUMP_DIRECTORY entry to point to it.\n  //!\n  //! This object takes ownership of \\a stream and becomes its parent in the\n  //! overall tree of internal::MinidumpWritable objects.\n  //!\n  //! At most one object of each stream type (as obtained from\n  //! internal::MinidumpStreamWriter::StreamType()) may be added to a\n  //! MinidumpFileWriter object. If an attempt is made to add a stream whose\n  //! type matches an existing stream’s type, this method discards the new\n  //! stream.\n  //!\n  //! \\note Valid in #kStateMutable.\n  //!\n  //! \\return `true` on success. `false` on failure, as occurs when an attempt\n  //!     is made to add a stream whose type matches an existing stream’s type,\n  //!     with a message logged.\n  bool AddStream(std::unique_ptr<internal::MinidumpStreamWriter> stream);\n\n  //! \\brief Adds a user extension stream to the minidump file and arranges for\n  //!     a MINIDUMP_DIRECTORY entry to point to it.\n  //!\n  //! This object takes ownership of \\a user_extension_stream_data.\n  //!\n  //! At most one object of each stream type (as obtained from\n  //! internal::MinidumpStreamWriter::StreamType()) may be added to a\n  //! MinidumpFileWriter object. If an attempt is made to add a stream whose\n  //! type matches an existing stream’s type, this method discards the new\n  //! stream.\n  //!\n  //! \\param[in] user_extension_stream_data The stream data to add to the\n  //!    minidump file. Note that the buffer this object points to must be valid\n  //!    through WriteEverything().\n  //!\n  //! \\note Valid in #kStateMutable.\n  //!\n  //! \\return `true` on success. `false` on failure, as occurs when an attempt\n  //!     is made to add a stream whose type matches an existing stream’s type,\n  //!     with a message logged.\n  bool AddUserExtensionStream(\n      std::unique_ptr<MinidumpUserExtensionStreamDataSource>\n          user_extension_stream_data);\n\n  // MinidumpWritable:\n\n  //! \\copydoc internal::MinidumpWritable::WriteEverything()\n  //!\n  //! This method does not initially write the final value for\n  //! MINIDUMP_HEADER::Signature. After all child objects have been written, it\n  //! rewinds to the beginning of the file and writes the correct value for this\n  //! field. This prevents incompletely-written minidump files from being\n  //! mistaken for valid ones.\n  bool WriteEverything(FileWriterInterface* file_writer) override;\n\n  //! \\brief Writes this object to a minidump file.\n  //!\n  //! Same as \\a WriteEverything, but give the option to disable the seek. It\n  //! is typically used to write to stream backed \\a FileWriterInterface which\n  //! doesn't support seek.\n  //!\n  //! \\param[in] file_writer The file writer to receive the minidump file’s\n  //!     content.\n  //!\n  //! \\param[in] allow_seek Whether seek is allowed.\n  //!\n  //! \\return `true` on success. `false` on failure, with an appropriate message\n  //!     logged.\n  bool WriteMinidump(FileWriterInterface* file_writer, bool allow_seek);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WillWriteAtOffsetImpl(FileOffset offset) override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  MINIDUMP_HEADER header_;\n  std::vector<std::unique_ptr<internal::MinidumpStreamWriter>> streams_;\n\n  // Protects against multiple streams with the same ID being added.\n  std::set<MinidumpStreamType> stream_types_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_file_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_file_writer.h\"\n\n#include <stdint.h>\n#include <string.h>\n\n#include <iterator>\n#include <string>\n#include <utility>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_user_extension_stream_data_source.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_user_extension_stream_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_cpu_context.h\"\n#include \"snapshot/test/test_exception_snapshot.h\"\n#include \"snapshot/test/test_memory_snapshot.h\"\n#include \"snapshot/test/test_module_snapshot.h\"\n#include \"snapshot/test/test_process_snapshot.h\"\n#include \"snapshot/test/test_system_snapshot.h\"\n#include \"snapshot/test/test_thread_snapshot.h\"\n#include \"test/gtest_death.h\"\n#include \"util/file/output_stream_file_writer.h\"\n#include \"util/file/string_file.h\"\n#include \"util/stream/output_stream_interface.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(MinidumpFileWriter, Empty) {\n  MinidumpFileWriter minidump_file;\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file.WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(), sizeof(MINIDUMP_HEADER));\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 0, 0));\n  EXPECT_FALSE(directory);\n}\n\nclass TestStream final : public internal::MinidumpStreamWriter {\n public:\n  TestStream(MinidumpStreamType stream_type,\n             size_t stream_size,\n             uint8_t stream_value)\n      : stream_data_(stream_size, stream_value), stream_type_(stream_type) {}\n\n  TestStream(const TestStream&) = delete;\n  TestStream& operator=(const TestStream&) = delete;\n\n  ~TestStream() override {}\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override {\n    return stream_type_;\n  }\n\n protected:\n  // MinidumpWritable:\n  size_t SizeOfObject() override {\n    EXPECT_GE(state(), kStateFrozen);\n    return stream_data_.size();\n  }\n\n  bool WriteObject(FileWriterInterface* file_writer) override {\n    EXPECT_EQ(kStateWritable, state());\n    return file_writer->Write(&stream_data_[0], stream_data_.size());\n  }\n\n private:\n  std::string stream_data_;\n  MinidumpStreamType stream_type_;\n};\n\nclass StringFileOutputStream : public OutputStreamInterface {\n public:\n  StringFileOutputStream() = default;\n\n  StringFileOutputStream(const StringFileOutputStream&) = delete;\n  StringFileOutputStream& operator=(const StringFileOutputStream&) = delete;\n\n  ~StringFileOutputStream() override = default;\n  bool Write(const uint8_t* data, size_t size) override {\n    return string_file_.Write(data, size);\n  }\n  bool Flush() override { return true; }\n  const StringFile& string_file() const { return string_file_; }\n\n private:\n  StringFile string_file_;\n};\n\nTEST(MinidumpFileWriter, OneStream) {\n  MinidumpFileWriter minidump_file;\n  constexpr time_t kTimestamp = 0x155d2fb8;\n  minidump_file.SetTimestamp(kTimestamp);\n\n  constexpr size_t kStreamSize = 5;\n  constexpr MinidumpStreamType kStreamType =\n      static_cast<MinidumpStreamType>(0x4d);\n  constexpr uint8_t kStreamValue = 0x5a;\n  auto stream =\n      std::make_unique<TestStream>(kStreamType, kStreamSize, kStreamValue);\n  ASSERT_TRUE(minidump_file.AddStream(std::move(stream)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file.WriteEverything(&string_file));\n\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kFileSize = kStreamOffset + kStreamSize;\n\n  ASSERT_EQ(string_file.string().size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kStreamType);\n  EXPECT_EQ(directory[0].Location.DataSize, kStreamSize);\n  EXPECT_EQ(directory[0].Location.Rva, kStreamOffset);\n\n  const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor<uint8_t>(\n      string_file.string(), directory[0].Location);\n  ASSERT_TRUE(stream_data);\n\n  std::string expected_stream(kStreamSize, kStreamValue);\n  EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStreamSize), 0);\n}\n\nTEST(MinidumpFileWriter, AddUserExtensionStream) {\n  MinidumpFileWriter minidump_file;\n  constexpr time_t kTimestamp = 0x155d2fb8;\n  minidump_file.SetTimestamp(kTimestamp);\n\n  static constexpr uint8_t kStreamData[] = \"Hello World!\";\n  constexpr size_t kStreamSize = std::size(kStreamData);\n  constexpr MinidumpStreamType kStreamType =\n      static_cast<MinidumpStreamType>(0x4d);\n\n  auto data_source = std::make_unique<test::BufferExtensionStreamDataSource>(\n      kStreamType, kStreamData, kStreamSize);\n  ASSERT_TRUE(minidump_file.AddUserExtensionStream(std::move(data_source)));\n\n  // Adding the same stream type a second time should fail.\n  data_source = std::make_unique<test::BufferExtensionStreamDataSource>(\n      kStreamType, kStreamData, kStreamSize);\n  ASSERT_FALSE(minidump_file.AddUserExtensionStream(std::move(data_source)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file.WriteEverything(&string_file));\n\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kFileSize = kStreamOffset + kStreamSize;\n\n  ASSERT_EQ(string_file.string().size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kStreamType);\n  EXPECT_EQ(directory[0].Location.DataSize, kStreamSize);\n  EXPECT_EQ(directory[0].Location.Rva, kStreamOffset);\n\n  const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor<uint8_t>(\n      string_file.string(), directory[0].Location);\n  ASSERT_TRUE(stream_data);\n\n  EXPECT_EQ(memcmp(stream_data, kStreamData, kStreamSize), 0);\n}\n\nTEST(MinidumpFileWriter, AddEmptyUserExtensionStream) {\n  MinidumpFileWriter minidump_file;\n  constexpr time_t kTimestamp = 0x155d2fb8;\n  minidump_file.SetTimestamp(kTimestamp);\n\n  constexpr MinidumpStreamType kStreamType =\n      static_cast<MinidumpStreamType>(0x4d);\n\n  auto data_source = std::make_unique<test::BufferExtensionStreamDataSource>(\n      kStreamType, nullptr, 0);\n  ASSERT_TRUE(minidump_file.AddUserExtensionStream(std::move(data_source)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file.WriteEverything(&string_file));\n\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kFileSize = kStreamOffset;\n\n  ASSERT_EQ(string_file.string().size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kStreamType);\n  EXPECT_EQ(directory[0].Location.DataSize, 0u);\n  EXPECT_EQ(directory[0].Location.Rva, kStreamOffset);\n}\n\nTEST(MinidumpFileWriter, ThreeStreams) {\n  MinidumpFileWriter minidump_file;\n  constexpr time_t kTimestamp = 0x155d2fb8;\n  minidump_file.SetTimestamp(kTimestamp);\n\n  constexpr size_t kStream0Size = 5;\n  constexpr MinidumpStreamType kStream0Type =\n      static_cast<MinidumpStreamType>(0x6d);\n  constexpr uint8_t kStream0Value = 0x5a;\n  auto stream0 =\n      std::make_unique<TestStream>(kStream0Type, kStream0Size, kStream0Value);\n  ASSERT_TRUE(minidump_file.AddStream(std::move(stream0)));\n\n  // Make the second stream’s type be a smaller quantity than the first stream’s\n  // to test that the streams show up in the order that they were added, not in\n  // numeric order.\n  constexpr size_t kStream1Size = 3;\n  constexpr MinidumpStreamType kStream1Type =\n      static_cast<MinidumpStreamType>(0x4d);\n  constexpr uint8_t kStream1Value = 0xa5;\n  auto stream1 =\n      std::make_unique<TestStream>(kStream1Type, kStream1Size, kStream1Value);\n  ASSERT_TRUE(minidump_file.AddStream(std::move(stream1)));\n\n  constexpr size_t kStream2Size = 1;\n  constexpr MinidumpStreamType kStream2Type =\n      static_cast<MinidumpStreamType>(0x7e);\n  constexpr uint8_t kStream2Value = 0x36;\n  auto stream2 =\n      std::make_unique<TestStream>(kStream2Type, kStream2Size, kStream2Value);\n  ASSERT_TRUE(minidump_file.AddStream(std::move(stream2)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file.WriteEverything(&string_file));\n\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kStream0Offset =\n      kDirectoryOffset + 3 * sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kStream1Padding = 3;\n  constexpr size_t kStream1Offset =\n      kStream0Offset + kStream0Size + kStream1Padding;\n  constexpr size_t kStream2Padding = 1;\n  constexpr size_t kStream2Offset =\n      kStream1Offset + kStream1Size + kStream2Padding;\n  constexpr size_t kFileSize = kStream2Offset + kStream2Size;\n\n  ASSERT_EQ(string_file.string().size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 3, kTimestamp));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kStream0Type);\n  EXPECT_EQ(directory[0].Location.DataSize, kStream0Size);\n  EXPECT_EQ(directory[0].Location.Rva, kStream0Offset);\n  EXPECT_EQ(directory[1].StreamType, kStream1Type);\n  EXPECT_EQ(directory[1].Location.DataSize, kStream1Size);\n  EXPECT_EQ(directory[1].Location.Rva, kStream1Offset);\n  EXPECT_EQ(directory[2].StreamType, kStream2Type);\n  EXPECT_EQ(directory[2].Location.DataSize, kStream2Size);\n  EXPECT_EQ(directory[2].Location.Rva, kStream2Offset);\n\n  const uint8_t* stream0_data = MinidumpWritableAtLocationDescriptor<uint8_t>(\n      string_file.string(), directory[0].Location);\n  ASSERT_TRUE(stream0_data);\n\n  std::string expected_stream0(kStream0Size, kStream0Value);\n  EXPECT_EQ(memcmp(stream0_data, expected_stream0.c_str(), kStream0Size), 0);\n\n  static constexpr int kZeroes[16] = {};\n  ASSERT_GE(sizeof(kZeroes), kStream1Padding);\n  EXPECT_EQ(memcmp(stream0_data + kStream0Size, kZeroes, kStream1Padding), 0);\n\n  const uint8_t* stream1_data = MinidumpWritableAtLocationDescriptor<uint8_t>(\n      string_file.string(), directory[1].Location);\n  ASSERT_TRUE(stream1_data);\n\n  std::string expected_stream1(kStream1Size, kStream1Value);\n  EXPECT_EQ(memcmp(stream1_data, expected_stream1.c_str(), kStream1Size), 0);\n\n  ASSERT_GE(sizeof(kZeroes), kStream2Padding);\n  EXPECT_EQ(memcmp(stream1_data + kStream1Size, kZeroes, kStream2Padding), 0);\n\n  const uint8_t* stream2_data = MinidumpWritableAtLocationDescriptor<uint8_t>(\n      string_file.string(), directory[2].Location);\n  ASSERT_TRUE(stream2_data);\n\n  std::string expected_stream2(kStream2Size, kStream2Value);\n  EXPECT_EQ(memcmp(stream2_data, expected_stream2.c_str(), kStream2Size), 0);\n}\n\nTEST(MinidumpFileWriter, ZeroLengthStream) {\n  MinidumpFileWriter minidump_file;\n\n  constexpr size_t kStreamSize = 0;\n  constexpr MinidumpStreamType kStreamType =\n      static_cast<MinidumpStreamType>(0x4d);\n  auto stream = std::make_unique<TestStream>(\n      kStreamType, kStreamSize, static_cast<uint8_t>(0));\n  ASSERT_TRUE(minidump_file.AddStream(std::move(stream)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file.WriteEverything(&string_file));\n\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kFileSize = kStreamOffset + kStreamSize;\n\n  ASSERT_EQ(string_file.string().size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kStreamType);\n  EXPECT_EQ(directory[0].Location.DataSize, kStreamSize);\n  EXPECT_EQ(directory[0].Location.Rva, kStreamOffset);\n}\n\nTEST(MinidumpFileWriter, InitializeFromSnapshot_Basic) {\n  constexpr uint32_t kSnapshotTime = 0x4976043c;\n  constexpr timeval kSnapshotTimeval = {static_cast<time_t>(kSnapshotTime), 0};\n\n  TestProcessSnapshot process_snapshot;\n  process_snapshot.SetSnapshotTime(kSnapshotTimeval);\n\n  auto system_snapshot = std::make_unique<TestSystemSnapshot>();\n  system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64);\n  system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);\n  process_snapshot.SetSystem(std::move(system_snapshot));\n\n  auto peb_snapshot = std::make_unique<TestMemorySnapshot>();\n  constexpr uint64_t kPebAddress = 0x07f90000;\n  peb_snapshot->SetAddress(kPebAddress);\n  constexpr size_t kPebSize = 0x280;\n  peb_snapshot->SetSize(kPebSize);\n  peb_snapshot->SetValue('p');\n  process_snapshot.AddExtraMemory(std::move(peb_snapshot));\n\n  MinidumpFileWriter minidump_file_writer;\n  minidump_file_writer.InitializeFromSnapshot(&process_snapshot);\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 5, kSnapshotTime));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(\n                  string_file.string(), directory[0].Location));\n\n  EXPECT_EQ(directory[1].StreamType, kMinidumpStreamTypeMiscInfo);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(\n                  string_file.string(), directory[1].Location));\n\n  EXPECT_EQ(directory[2].StreamType, kMinidumpStreamTypeThreadList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(\n                  string_file.string(), directory[2].Location));\n\n  EXPECT_EQ(directory[3].StreamType, kMinidumpStreamTypeModuleList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(\n                  string_file.string(), directory[3].Location));\n\n  EXPECT_EQ(directory[4].StreamType, kMinidumpStreamTypeMemoryList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(\n                  string_file.string(), directory[4].Location));\n\n  const MINIDUMP_MEMORY_LIST* memory_list =\n      MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(\n          string_file.string(), directory[4].Location);\n  EXPECT_EQ(memory_list->NumberOfMemoryRanges, 1u);\n  EXPECT_EQ(memory_list->MemoryRanges[0].StartOfMemoryRange, kPebAddress);\n  EXPECT_EQ(memory_list->MemoryRanges[0].Memory.DataSize, kPebSize);\n}\n\nTEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) {\n  // In a 32-bit environment, this will give a “timestamp out of range” warning,\n  // but the test should complete without failure.\n  constexpr uint32_t kSnapshotTime = 0xfd469ab8;\n  constexpr timeval kSnapshotTimeval = {\n#if BUILDFLAG(IS_WIN)\n    static_cast<long>(kSnapshotTime),\n#else\n    static_cast<time_t>(kSnapshotTime),\n#endif\n    0\n  };\n\n  TestProcessSnapshot process_snapshot;\n  process_snapshot.SetSnapshotTime(kSnapshotTimeval);\n\n  auto system_snapshot = std::make_unique<TestSystemSnapshot>();\n  system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64);\n  system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);\n  process_snapshot.SetSystem(std::move(system_snapshot));\n\n  auto thread_snapshot = std::make_unique<TestThreadSnapshot>();\n  InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5);\n  process_snapshot.AddThread(std::move(thread_snapshot));\n\n  auto exception_snapshot = std::make_unique<TestExceptionSnapshot>();\n  InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11);\n  process_snapshot.SetException(std::move(exception_snapshot));\n\n  // The module does not have anything that needs to be represented in a\n  // MinidumpModuleCrashpadInfo structure, so no such structure is expected to\n  // be present, which will in turn suppress the addition of a\n  // MinidumpCrashpadInfo stream.\n  auto module_snapshot = std::make_unique<TestModuleSnapshot>();\n  process_snapshot.AddModule(std::move(module_snapshot));\n\n  MinidumpFileWriter minidump_file_writer;\n  minidump_file_writer.InitializeFromSnapshot(&process_snapshot);\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 6, kSnapshotTime));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(\n                  string_file.string(), directory[0].Location));\n\n  EXPECT_EQ(directory[1].StreamType, kMinidumpStreamTypeMiscInfo);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(\n                  string_file.string(), directory[1].Location));\n\n  EXPECT_EQ(directory[2].StreamType, kMinidumpStreamTypeThreadList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(\n                  string_file.string(), directory[2].Location));\n\n  EXPECT_EQ(directory[3].StreamType, kMinidumpStreamTypeException);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>(\n                  string_file.string(), directory[3].Location));\n\n  EXPECT_EQ(directory[4].StreamType, kMinidumpStreamTypeModuleList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(\n                  string_file.string(), directory[4].Location));\n\n  EXPECT_EQ(directory[5].StreamType, kMinidumpStreamTypeMemoryList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(\n                  string_file.string(), directory[5].Location));\n}\n\nTEST(MinidumpFileWriter, InitializeFromSnapshot_CrashpadInfo) {\n  constexpr uint32_t kSnapshotTime = 0x15393bd3;\n  constexpr timeval kSnapshotTimeval = {static_cast<time_t>(kSnapshotTime), 0};\n\n  TestProcessSnapshot process_snapshot;\n  process_snapshot.SetSnapshotTime(kSnapshotTimeval);\n\n  auto system_snapshot = std::make_unique<TestSystemSnapshot>();\n  system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64);\n  system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);\n  process_snapshot.SetSystem(std::move(system_snapshot));\n\n  auto thread_snapshot = std::make_unique<TestThreadSnapshot>();\n  InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5);\n  process_snapshot.AddThread(std::move(thread_snapshot));\n\n  auto exception_snapshot = std::make_unique<TestExceptionSnapshot>();\n  InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11);\n  process_snapshot.SetException(std::move(exception_snapshot));\n\n  // The module needs an annotation for the MinidumpCrashpadInfo stream to be\n  // considered useful and be included.\n  auto module_snapshot = std::make_unique<TestModuleSnapshot>();\n  std::vector<std::string> annotations_list(1, std::string(\"annotation\"));\n  module_snapshot->SetAnnotationsVector(annotations_list);\n  process_snapshot.AddModule(std::move(module_snapshot));\n\n  MinidumpFileWriter minidump_file_writer;\n  minidump_file_writer.InitializeFromSnapshot(&process_snapshot);\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 7, kSnapshotTime));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(\n                  string_file.string(), directory[0].Location));\n\n  EXPECT_EQ(directory[1].StreamType, kMinidumpStreamTypeMiscInfo);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(\n                  string_file.string(), directory[1].Location));\n\n  EXPECT_EQ(directory[2].StreamType, kMinidumpStreamTypeThreadList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(\n                  string_file.string(), directory[2].Location));\n\n  EXPECT_EQ(directory[3].StreamType, kMinidumpStreamTypeException);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>(\n                  string_file.string(), directory[3].Location));\n\n  EXPECT_EQ(directory[4].StreamType, kMinidumpStreamTypeModuleList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(\n                  string_file.string(), directory[4].Location));\n\n  EXPECT_EQ(directory[5].StreamType, kMinidumpStreamTypeCrashpadInfo);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MinidumpCrashpadInfo>(\n                  string_file.string(), directory[5].Location));\n\n  EXPECT_EQ(directory[6].StreamType, kMinidumpStreamTypeMemoryList);\n  EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(\n                  string_file.string(), directory[6].Location));\n}\n\nTEST(MinidumpFileWriter, SameStreamType) {\n  MinidumpFileWriter minidump_file;\n\n  constexpr size_t kStream0Size = 3;\n  constexpr MinidumpStreamType kStreamType =\n      static_cast<MinidumpStreamType>(0x4d);\n  constexpr uint8_t kStream0Value = 0x5a;\n  auto stream0 =\n      std::make_unique<TestStream>(kStreamType, kStream0Size, kStream0Value);\n  ASSERT_TRUE(minidump_file.AddStream(std::move(stream0)));\n\n  // An attempt to add a second stream of the same type should fail.\n  constexpr size_t kStream1Size = 5;\n  constexpr uint8_t kStream1Value = 0xa5;\n  auto stream1 =\n      std::make_unique<TestStream>(kStreamType, kStream1Size, kStream1Value);\n  ASSERT_FALSE(minidump_file.AddStream(std::move(stream1)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file.WriteEverything(&string_file));\n\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kStream0Offset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kFileSize = kStream0Offset + kStream0Size;\n\n  ASSERT_EQ(string_file.string().size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kStreamType);\n  EXPECT_EQ(directory[0].Location.DataSize, kStream0Size);\n  EXPECT_EQ(directory[0].Location.Rva, kStream0Offset);\n\n  const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor<uint8_t>(\n      string_file.string(), directory[0].Location);\n  ASSERT_TRUE(stream_data);\n\n  std::string expected_stream(kStream0Size, kStream0Value);\n  EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStream0Size), 0);\n}\n\nTEST(MinidumpFileWriter, WriteMinidumpDisallowSeek) {\n  MinidumpFileWriter minidump_file;\n  constexpr time_t kTimestamp = 0x155d2fb8;\n  minidump_file.SetTimestamp(kTimestamp);\n\n  constexpr size_t kStreamSize = 5;\n  constexpr MinidumpStreamType kStreamType =\n      static_cast<MinidumpStreamType>(0x4d);\n  constexpr uint8_t kStreamValue = 0x5a;\n  auto stream =\n      std::make_unique<TestStream>(kStreamType, kStreamSize, kStreamValue);\n  ASSERT_TRUE(minidump_file.AddStream(std::move(stream)));\n\n  std::unique_ptr<StringFileOutputStream> string_file_output_stream =\n      std::make_unique<StringFileOutputStream>();\n  const StringFile& string_file = string_file_output_stream->string_file();\n  OutputStreamFileWriter output_stream(std::move(string_file_output_stream));\n  ASSERT_TRUE(minidump_file.WriteMinidump(&output_stream, false));\n  ASSERT_TRUE(output_stream.Flush());\n\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kFileSize = kStreamOffset + kStreamSize;\n\n  ASSERT_EQ(string_file.string().size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp));\n  ASSERT_TRUE(directory);\n\n  EXPECT_EQ(directory[0].StreamType, kStreamType);\n  EXPECT_EQ(directory[0].Location.DataSize, kStreamSize);\n  EXPECT_EQ(directory[0].Location.Rva, kStreamOffset);\n\n  const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor<uint8_t>(\n      string_file.string(), directory[0].Location);\n  ASSERT_TRUE(stream_data);\n\n  std::string expected_stream(kStreamSize, kStreamValue);\n  EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStreamSize), 0);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_handle_writer.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_handle_writer.h\"\n\n#include <string>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpHandleDataWriter::MinidumpHandleDataWriter()\n    : handle_data_stream_base_(), handle_descriptors_(), strings_() {\n}\n\nMinidumpHandleDataWriter::~MinidumpHandleDataWriter() {\n  for (auto& item : strings_)\n    delete item.second;\n}\n\nvoid MinidumpHandleDataWriter::InitializeFromSnapshot(\n    const std::vector<HandleSnapshot>& handle_snapshots) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  DCHECK(handle_descriptors_.empty());\n  // Because we RegisterRVA() on the string writer below, we preallocate and\n  // never resize the handle_descriptors_ vector.\n  handle_descriptors_.resize(handle_snapshots.size());\n  for (size_t i = 0; i < handle_snapshots.size(); ++i) {\n    const HandleSnapshot& handle_snapshot = handle_snapshots[i];\n    MINIDUMP_HANDLE_DESCRIPTOR& descriptor = handle_descriptors_[i];\n\n    descriptor.Handle = handle_snapshot.handle;\n\n    if (handle_snapshot.type_name.empty()) {\n      descriptor.TypeNameRva = 0;\n    } else {\n      auto it = strings_.lower_bound(handle_snapshot.type_name);\n      internal::MinidumpUTF16StringWriter* writer;\n      if (it != strings_.end() && it->first == handle_snapshot.type_name) {\n        writer = it->second;\n      } else {\n        writer = new internal::MinidumpUTF16StringWriter();\n        strings_.insert(it, std::make_pair(handle_snapshot.type_name, writer));\n        writer->SetUTF8(handle_snapshot.type_name);\n      }\n      writer->RegisterRVA(&descriptor.TypeNameRva);\n    }\n\n    descriptor.ObjectNameRva = 0;\n    descriptor.Attributes = handle_snapshot.attributes;\n    descriptor.GrantedAccess = handle_snapshot.granted_access;\n    descriptor.HandleCount = handle_snapshot.handle_count;\n    descriptor.PointerCount = handle_snapshot.pointer_count;\n  }\n}\n\nbool MinidumpHandleDataWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpStreamWriter::Freeze())\n    return false;\n\n  handle_data_stream_base_.SizeOfHeader = sizeof(handle_data_stream_base_);\n  handle_data_stream_base_.SizeOfDescriptor = sizeof(handle_descriptors_[0]);\n  const size_t handle_count = handle_descriptors_.size();\n  if (!AssignIfInRange(&handle_data_stream_base_.NumberOfDescriptors,\n                       handle_count)) {\n    LOG(ERROR) << \"handle_count \" << handle_count << \" out of range\";\n    return false;\n  }\n  handle_data_stream_base_.Reserved = 0;\n\n  return true;\n}\n\nsize_t MinidumpHandleDataWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n  return sizeof(handle_data_stream_base_) +\n         sizeof(handle_descriptors_[0]) * handle_descriptors_.size();\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpHandleDataWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& pair : strings_)\n    children.push_back(pair.second);\n  return children;\n}\n\nbool MinidumpHandleDataWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = &handle_data_stream_base_;\n  iov.iov_len = sizeof(handle_data_stream_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  for (const auto& descriptor : handle_descriptors_) {\n    iov.iov_base = &descriptor;\n    iov.iov_len = sizeof(descriptor);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpStreamType MinidumpHandleDataWriter::StreamType() const {\n  return kMinidumpStreamTypeHandleData;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_handle_writer.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_string_writer.h\"\n#include \"minidump/minidump_writable.h\"\n#include \"snapshot/handle_snapshot.h\"\n\nnamespace crashpad {\n\n//! \\brief The writer for a MINIDUMP_HANDLE_DATA_STREAM stream in a minidump\n//!     and its contained MINIDUMP_HANDLE_DESCRIPTOR s.\n//!\n//! As we currently do not track any data beyond what MINIDUMP_HANDLE_DESCRIPTOR\n//! supports, we only write that type of record rather than the newer\n//! MINIDUMP_HANDLE_DESCRIPTOR_2.\n//!\n//! Note that this writer writes both the header (MINIDUMP_HANDLE_DATA_STREAM)\n//! and the list of objects (MINIDUMP_HANDLE_DESCRIPTOR), which is different\n//! from some of the other list writers.\nclass MinidumpHandleDataWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpHandleDataWriter();\n\n  MinidumpHandleDataWriter(const MinidumpHandleDataWriter&) = delete;\n  MinidumpHandleDataWriter& operator=(const MinidumpHandleDataWriter&) = delete;\n\n  ~MinidumpHandleDataWriter() override;\n\n  //! \\brief Adds a MINIDUMP_HANDLE_DESCRIPTOR for each handle in \\a\n  //!     handle_snapshot to the MINIDUMP_HANDLE_DATA_STREAM.\n  //!\n  //! \\param[in] handle_snapshots The handle snapshots to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void InitializeFromSnapshot(\n      const std::vector<HandleSnapshot>& handle_snapshots);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  MINIDUMP_HANDLE_DATA_STREAM handle_data_stream_base_;\n  std::vector<MINIDUMP_HANDLE_DESCRIPTOR> handle_descriptors_;\n  std::map<std::string, internal::MinidumpUTF16StringWriter*> strings_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_handle_writer_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_handle_writer.h\"\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// The handle data stream is expected to be the only stream.\nvoid GetHandleDataStream(\n    const std::string& file_contents,\n    const MINIDUMP_HANDLE_DATA_STREAM** handle_data_stream) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kHandleDataStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  constexpr size_t kDirectoryIndex = 0;\n\n  ASSERT_EQ(directory[kDirectoryIndex].StreamType,\n            kMinidumpStreamTypeHandleData);\n  EXPECT_EQ(directory[kDirectoryIndex].Location.Rva, kHandleDataStreamOffset);\n\n  *handle_data_stream =\n      MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(\n          file_contents, directory[kDirectoryIndex].Location);\n  ASSERT_TRUE(*handle_data_stream);\n}\n\nTEST(MinidumpHandleDataWriter, Empty) {\n  MinidumpFileWriter minidump_file_writer;\n  auto handle_data_writer = std::make_unique<MinidumpHandleDataWriter>();\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(handle_data_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_HANDLE_DATA_STREAM));\n\n  const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetHandleDataStream(string_file.string(), &handle_data_stream));\n\n  EXPECT_EQ(handle_data_stream->NumberOfDescriptors, 0u);\n}\n\nTEST(MinidumpHandleDataWriter, OneHandle) {\n  MinidumpFileWriter minidump_file_writer;\n  auto handle_data_writer = std::make_unique<MinidumpHandleDataWriter>();\n\n  HandleSnapshot handle_snapshot;\n  handle_snapshot.handle = 0x1234;\n  handle_snapshot.type_name = \"Something\";\n  handle_snapshot.attributes = 0x12345678;\n  handle_snapshot.granted_access = 0x9abcdef0;\n  handle_snapshot.pointer_count = 4567;\n  handle_snapshot.handle_count = 9876;\n\n  std::vector<HandleSnapshot> snapshot;\n  snapshot.push_back(handle_snapshot);\n\n  handle_data_writer->InitializeFromSnapshot(snapshot);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(handle_data_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const size_t kTypeNameStringDataLength =\n      (handle_snapshot.type_name.size() + 1) * sizeof(char16_t);\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_HANDLE_DATA_STREAM) +\n                sizeof(MINIDUMP_HANDLE_DESCRIPTOR) + sizeof(MINIDUMP_STRING) +\n                kTypeNameStringDataLength);\n\n  const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetHandleDataStream(string_file.string(), &handle_data_stream));\n\n  EXPECT_EQ(handle_data_stream->NumberOfDescriptors, 1u);\n  MINIDUMP_HANDLE_DESCRIPTOR handle_descriptor;\n  memcpy(&handle_descriptor, &handle_data_stream[1], sizeof(handle_descriptor));\n  EXPECT_EQ(handle_descriptor.Handle, handle_snapshot.handle);\n  EXPECT_EQ(base::UTF16ToUTF8(MinidumpStringAtRVAAsString(\n                string_file.string(), handle_descriptor.TypeNameRva)),\n            handle_snapshot.type_name);\n  EXPECT_EQ(handle_descriptor.ObjectNameRva, 0u);\n  EXPECT_EQ(handle_descriptor.Attributes, handle_snapshot.attributes);\n  EXPECT_EQ(handle_descriptor.GrantedAccess, handle_snapshot.granted_access);\n  EXPECT_EQ(handle_descriptor.HandleCount, handle_snapshot.handle_count);\n  EXPECT_EQ(handle_descriptor.PointerCount, handle_snapshot.pointer_count);\n}\n\nTEST(MinidumpHandleDataWriter, RepeatedTypeName) {\n  MinidumpFileWriter minidump_file_writer;\n  auto handle_data_writer = std::make_unique<MinidumpHandleDataWriter>();\n\n  HandleSnapshot handle_snapshot;\n  handle_snapshot.handle = 0x1234;\n  handle_snapshot.type_name = \"Something\";\n  handle_snapshot.attributes = 0x12345678;\n  handle_snapshot.granted_access = 0x9abcdef0;\n  handle_snapshot.pointer_count = 4567;\n  handle_snapshot.handle_count = 9876;\n\n  HandleSnapshot handle_snapshot2;\n  handle_snapshot2.handle = 0x4321;\n  handle_snapshot2.type_name = \"Something\";  // Note: same as above.\n  handle_snapshot2.attributes = 0x87654321;\n  handle_snapshot2.granted_access = 0x0fedcba9;\n  handle_snapshot2.pointer_count = 7654;\n  handle_snapshot2.handle_count = 6789;\n\n  std::vector<HandleSnapshot> snapshot;\n  snapshot.push_back(handle_snapshot);\n  snapshot.push_back(handle_snapshot2);\n\n  handle_data_writer->InitializeFromSnapshot(snapshot);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(handle_data_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const size_t kTypeNameStringDataLength =\n      (handle_snapshot.type_name.size() + 1) * sizeof(char16_t);\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_HANDLE_DATA_STREAM) +\n                (sizeof(MINIDUMP_HANDLE_DESCRIPTOR) * 2) +\n                sizeof(MINIDUMP_STRING) + kTypeNameStringDataLength);\n\n  const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetHandleDataStream(string_file.string(), &handle_data_stream));\n\n  EXPECT_EQ(handle_data_stream->NumberOfDescriptors, 2u);\n  MINIDUMP_HANDLE_DESCRIPTOR handle_descriptor;\n  memcpy(&handle_descriptor, &handle_data_stream[1], sizeof(handle_descriptor));\n  EXPECT_EQ(handle_descriptor.Handle, handle_snapshot.handle);\n  EXPECT_EQ(base::UTF16ToUTF8(MinidumpStringAtRVAAsString(\n                string_file.string(), handle_descriptor.TypeNameRva)),\n            handle_snapshot.type_name);\n  EXPECT_EQ(handle_descriptor.ObjectNameRva, 0u);\n  EXPECT_EQ(handle_descriptor.Attributes, handle_snapshot.attributes);\n  EXPECT_EQ(handle_descriptor.GrantedAccess, handle_snapshot.granted_access);\n  EXPECT_EQ(handle_descriptor.HandleCount, handle_snapshot.handle_count);\n  EXPECT_EQ(handle_descriptor.PointerCount, handle_snapshot.pointer_count);\n\n  MINIDUMP_HANDLE_DESCRIPTOR handle_descriptor2;\n  memcpy(&handle_descriptor2,\n         reinterpret_cast<const unsigned char*>(&handle_data_stream[1]) +\n             sizeof(MINIDUMP_HANDLE_DESCRIPTOR),\n         sizeof(handle_descriptor2));\n  EXPECT_EQ(handle_descriptor2.Handle, handle_snapshot2.handle);\n  EXPECT_EQ(base::UTF16ToUTF8(MinidumpStringAtRVAAsString(\n                string_file.string(), handle_descriptor2.TypeNameRva)),\n            handle_snapshot2.type_name);\n  EXPECT_EQ(handle_descriptor2.ObjectNameRva, 0u);\n  EXPECT_EQ(handle_descriptor2.Attributes, handle_snapshot2.attributes);\n  EXPECT_EQ(handle_descriptor2.GrantedAccess, handle_snapshot2.granted_access);\n  EXPECT_EQ(handle_descriptor2.HandleCount, handle_snapshot2.handle_count);\n  EXPECT_EQ(handle_descriptor2.PointerCount, handle_snapshot2.pointer_count);\n\n  EXPECT_EQ(handle_descriptor2.TypeNameRva, handle_descriptor.TypeNameRva);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_memory_info_writer.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_memory_info_writer.h\"\n\n#include \"base/check_op.h\"\n#include \"snapshot/memory_map_region_snapshot.h\"\n#include \"util/file/file_writer.h\"\n\nnamespace crashpad {\n\nMinidumpMemoryInfoListWriter::MinidumpMemoryInfoListWriter()\n    : memory_info_list_base_(), items_() {\n}\n\nMinidumpMemoryInfoListWriter::~MinidumpMemoryInfoListWriter() {\n}\n\nvoid MinidumpMemoryInfoListWriter::InitializeFromSnapshot(\n    const std::vector<const MemoryMapRegionSnapshot*>& memory_map) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  DCHECK(items_.empty());\n  for (const auto& region : memory_map)\n    items_.push_back(region->AsMinidumpMemoryInfo());\n}\n\nbool MinidumpMemoryInfoListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpStreamWriter::Freeze())\n    return false;\n\n  memory_info_list_base_.SizeOfHeader = sizeof(MINIDUMP_MEMORY_INFO_LIST);\n  memory_info_list_base_.SizeOfEntry = sizeof(MINIDUMP_MEMORY_INFO);\n  memory_info_list_base_.NumberOfEntries = items_.size();\n\n  return true;\n}\n\nsize_t MinidumpMemoryInfoListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n  return sizeof(memory_info_list_base_) + sizeof(items_[0]) * items_.size();\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpMemoryInfoListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  return std::vector<internal::MinidumpWritable*>();\n}\n\nbool MinidumpMemoryInfoListWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = &memory_info_list_base_;\n  iov.iov_len = sizeof(memory_info_list_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  for (const auto& minidump_memory_info : items_) {\n    iov.iov_base = &minidump_memory_info;\n    iov.iov_len = sizeof(minidump_memory_info);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpStreamType MinidumpMemoryInfoListWriter::StreamType() const {\n  return kMinidumpStreamTypeMemoryInfoList;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_memory_info_writer.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <vector>\n\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\nclass MemoryMapRegionSnapshot;\nclass MinidumpContextWriter;\nclass MinidumpMemoryListWriter;\nclass MinidumpMemoryWriter;\n\n//! \\brief The writer for a MINIDUMP_MEMORY_INFO_LIST stream in a minidump file,\n//!     containing a list of MINIDUMP_MEMORY_INFO objects.\nclass MinidumpMemoryInfoListWriter final\n    : public internal::MinidumpStreamWriter {\n public:\n  MinidumpMemoryInfoListWriter();\n\n  MinidumpMemoryInfoListWriter(const MinidumpMemoryInfoListWriter&) = delete;\n  MinidumpMemoryInfoListWriter& operator=(const MinidumpMemoryInfoListWriter&) =\n      delete;\n\n  ~MinidumpMemoryInfoListWriter() override;\n\n  //! \\brief Initializes a MINIDUMP_MEMORY_INFO_LIST based on \\a memory_map.\n  //!\n  //! \\param[in] memory_map The vector of memory map region snapshots to use as\n  //!     source data.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void InitializeFromSnapshot(\n      const std::vector<const MemoryMapRegionSnapshot*>& memory_map);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<internal::MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  MINIDUMP_MEMORY_INFO_LIST memory_info_list_base_;\n  std::vector<MINIDUMP_MEMORY_INFO> items_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_memory_info_writer_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_memory_info_writer.h\"\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_memory_map_region_snapshot.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// The memory info list is expected to be the only stream.\nvoid GetMemoryInfoListStream(\n    const std::string& file_contents,\n    const MINIDUMP_MEMORY_INFO_LIST** memory_info_list) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kMemoryInfoListStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  constexpr size_t kDirectoryIndex = 0;\n\n  ASSERT_EQ(directory[kDirectoryIndex].StreamType,\n            kMinidumpStreamTypeMemoryInfoList);\n  EXPECT_EQ(directory[kDirectoryIndex].Location.Rva,\n            kMemoryInfoListStreamOffset);\n\n  *memory_info_list =\n      MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(\n          file_contents, directory[kDirectoryIndex].Location);\n  ASSERT_TRUE(*memory_info_list);\n}\n\nTEST(MinidumpMemoryInfoWriter, Empty) {\n  MinidumpFileWriter minidump_file_writer;\n  auto memory_info_list_writer =\n      std::make_unique<MinidumpMemoryInfoListWriter>();\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(memory_info_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MEMORY_INFO_LIST));\n\n  const MINIDUMP_MEMORY_INFO_LIST* memory_info_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryInfoListStream(string_file.string(), &memory_info_list));\n\n  uint64_t number_of_entries;\n  memcpy(&number_of_entries,\n         &memory_info_list->NumberOfEntries,\n         sizeof(number_of_entries));\n  EXPECT_EQ(number_of_entries, 0u);\n}\n\nTEST(MinidumpMemoryInfoWriter, OneRegion) {\n  MinidumpFileWriter minidump_file_writer;\n  auto memory_info_list_writer =\n      std::make_unique<MinidumpMemoryInfoListWriter>();\n\n  auto memory_map_region = std::make_unique<TestMemoryMapRegionSnapshot>();\n\n  MINIDUMP_MEMORY_INFO mmi;\n  mmi.BaseAddress = 0x12340000;\n  mmi.AllocationBase = 0x12000000;\n  mmi.AllocationProtect = PAGE_READWRITE;\n  mmi.__alignment1 = 0;\n  mmi.RegionSize = 0x6000;\n  mmi.State = MEM_COMMIT;\n  mmi.Protect = PAGE_NOACCESS;\n  mmi.Type = MEM_PRIVATE;\n  mmi.__alignment2 = 0;\n  memory_map_region->SetMindumpMemoryInfo(mmi);\n\n  std::vector<const MemoryMapRegionSnapshot*> memory_map;\n  memory_map.push_back(memory_map_region.get());\n  memory_info_list_writer->InitializeFromSnapshot(memory_map);\n\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(memory_info_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MEMORY_INFO_LIST) +\n                sizeof(MINIDUMP_MEMORY_INFO));\n\n  const MINIDUMP_MEMORY_INFO_LIST* memory_info_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryInfoListStream(string_file.string(), &memory_info_list));\n\n  uint64_t number_of_entries;\n  memcpy(&number_of_entries,\n         &memory_info_list->NumberOfEntries,\n         sizeof(number_of_entries));\n  EXPECT_EQ(number_of_entries, 1u);\n\n  MINIDUMP_MEMORY_INFO memory_info;\n  memcpy(&memory_info, &memory_info_list[1], sizeof(memory_info));\n  EXPECT_EQ(memory_info.BaseAddress, mmi.BaseAddress);\n  EXPECT_EQ(memory_info.AllocationBase, mmi.AllocationBase);\n  EXPECT_EQ(memory_info.AllocationProtect, mmi.AllocationProtect);\n  EXPECT_EQ(memory_info.RegionSize, mmi.RegionSize);\n  EXPECT_EQ(memory_info.State, mmi.State);\n  EXPECT_EQ(memory_info.Protect, mmi.Protect);\n  EXPECT_EQ(memory_info.Type, mmi.Type);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_memory_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_memory_writer.h\"\n\n#include <algorithm>\n#include <iterator>\n#include <utility>\n\n#include \"base/auto_reset.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nSnapshotMinidumpMemoryWriter::SnapshotMinidumpMemoryWriter(\n    const MemorySnapshot* memory_snapshot)\n    : internal::MinidumpWritable(),\n      MemorySnapshot::Delegate(),\n      memory_descriptor_(),\n      registered_memory_descriptors_(),\n      memory_snapshot_(memory_snapshot),\n      file_writer_(nullptr) {}\n\nSnapshotMinidumpMemoryWriter::~SnapshotMinidumpMemoryWriter() {}\n\nbool SnapshotMinidumpMemoryWriter::MemorySnapshotDelegateRead(void* data,\n                                                              size_t size) {\n  DCHECK_EQ(state(), kStateWritable);\n  DCHECK_EQ(size, UnderlyingSnapshot()->Size());\n  return file_writer_->Write(data, size);\n}\n\nbool SnapshotMinidumpMemoryWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  DCHECK(!file_writer_);\n\n  base::AutoReset<FileWriterInterface*> file_writer_reset(&file_writer_,\n                                                          file_writer);\n\n  // This will result in MemorySnapshotDelegateRead() being called.\n  if (!memory_snapshot_->Read(this)) {\n    // If the Read() fails (perhaps because the process' memory map has changed\n    // since it the range was captured), write an empty block of memory. It\n    // would be nice to instead not include this memory, but at this point in\n    // the writing process, it would be difficult to amend the minidump's\n    // structure. See https://crashpad.chromium.org/234 for background.\n    std::vector<uint8_t> empty(memory_snapshot_->Size(), 0xfe);\n    MemorySnapshotDelegateRead(empty.data(), empty.size());\n  }\n\n  return true;\n}\n\nconst MINIDUMP_MEMORY_DESCRIPTOR*\nSnapshotMinidumpMemoryWriter::MinidumpMemoryDescriptor() const {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return &memory_descriptor_;\n}\n\nvoid SnapshotMinidumpMemoryWriter::RegisterMemoryDescriptor(\n    MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor) {\n  DCHECK_LE(state(), kStateFrozen);\n\n  registered_memory_descriptors_.push_back(memory_descriptor);\n  RegisterLocationDescriptor(&memory_descriptor->Memory);\n}\n\nbool SnapshotMinidumpMemoryWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  RegisterMemoryDescriptor(&memory_descriptor_);\n\n  return true;\n}\n\nsize_t SnapshotMinidumpMemoryWriter::Alignment() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return 16;\n}\n\nsize_t SnapshotMinidumpMemoryWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return UnderlyingSnapshot()->Size();\n}\n\nbool SnapshotMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) {\n  DCHECK_EQ(state(), kStateFrozen);\n\n  // There will always be at least one registered descriptor, the one for this\n  // object’s own memory_descriptor_ field.\n  DCHECK_GE(registered_memory_descriptors_.size(), 1u);\n\n  uint64_t base_address = UnderlyingSnapshot()->Address();\n  decltype(registered_memory_descriptors_[0]->StartOfMemoryRange) local_address;\n  if (!AssignIfInRange(&local_address, base_address)) {\n    LOG(ERROR) << \"base_address \" << base_address << \" out of range\";\n    return false;\n  }\n\n  for (MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor :\n           registered_memory_descriptors_) {\n    memory_descriptor->StartOfMemoryRange = local_address;\n  }\n\n  return MinidumpWritable::WillWriteAtOffsetImpl(offset);\n}\n\ninternal::MinidumpWritable::Phase SnapshotMinidumpMemoryWriter::WritePhase() {\n  // Memory dumps are large and are unlikely to be consumed in their entirety.\n  // Data accesses are expected to be sparse and sporadic, and are expected to\n  // occur after all of the other structural and informational data from the\n  // minidump file has been read. Put memory dumps at the end of the minidump\n  // file to improve spatial locality.\n  return kPhaseLate;\n}\n\nMinidumpMemoryListWriter::MinidumpMemoryListWriter()\n    : MinidumpStreamWriter(),\n      non_owned_memory_writers_(),\n      children_(),\n      snapshots_created_during_merge_(),\n      all_memory_writers_(),\n      memory_list_base_() {}\n\nMinidumpMemoryListWriter::~MinidumpMemoryListWriter() {\n}\n\nvoid MinidumpMemoryListWriter::AddFromSnapshot(\n    const std::vector<const MemorySnapshot*>& memory_snapshots) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  for (const MemorySnapshot* memory_snapshot : memory_snapshots) {\n    std::unique_ptr<SnapshotMinidumpMemoryWriter> memory(\n        new SnapshotMinidumpMemoryWriter(memory_snapshot));\n    AddMemory(std::move(memory));\n  }\n}\n\nvoid MinidumpMemoryListWriter::AddMemory(\n    std::unique_ptr<SnapshotMinidumpMemoryWriter> memory_writer) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  children_.push_back(std::move(memory_writer));\n}\n\nvoid MinidumpMemoryListWriter::AddNonOwnedMemory(\n    SnapshotMinidumpMemoryWriter* memory_writer) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  non_owned_memory_writers_.push_back(memory_writer);\n}\n\nvoid MinidumpMemoryListWriter::CoalesceOwnedMemory() {\n  DropRangesThatOverlapNonOwned();\n\n  if (children_.empty())\n    return;\n\n  std::sort(children_.begin(),\n            children_.end(),\n            [](const std::unique_ptr<SnapshotMinidumpMemoryWriter>& a_ptr,\n               const std::unique_ptr<SnapshotMinidumpMemoryWriter>& b_ptr) {\n              const MemorySnapshot* a = a_ptr->UnderlyingSnapshot();\n              const MemorySnapshot* b = b_ptr->UnderlyingSnapshot();\n              if (a->Address() == b->Address()) {\n                return a->Size() < b->Size();\n              }\n              return a->Address() < b->Address();\n            });\n\n  // Remove any empty ranges.\n  children_.erase(\n      std::remove_if(children_.begin(),\n                     children_.end(),\n                     [](const auto& snapshot) {\n                       return snapshot->UnderlyingSnapshot()->Size() == 0;\n                     }),\n      children_.end());\n\n  std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> all_merged;\n  all_merged.push_back(std::move(children_.front()));\n  for (size_t i = 1; i < children_.size(); ++i) {\n    SnapshotMinidumpMemoryWriter* top = all_merged.back().get();\n    auto& child = children_[i];\n    if (!DetermineMergedRange(\n            child->UnderlyingSnapshot(), top->UnderlyingSnapshot(), nullptr)) {\n      // If it doesn't overlap with the current range, push it.\n      all_merged.push_back(std::move(child));\n    } else {\n      // Otherwise, merge and update the current element.\n      std::unique_ptr<const MemorySnapshot> merged(\n          top->UnderlyingSnapshot()->MergeWithOtherSnapshot(\n              child->UnderlyingSnapshot()));\n      top->SetSnapshot(merged.get());\n      snapshots_created_during_merge_.push_back(std::move(merged));\n    }\n  }\n  std::swap(children_, all_merged);\n}\n\nbool MinidumpMemoryListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  CoalesceOwnedMemory();\n\n  std::copy(non_owned_memory_writers_.begin(),\n            non_owned_memory_writers_.end(),\n            std::back_inserter(all_memory_writers_));\n  for (const auto& ptr : children_)\n    all_memory_writers_.push_back(ptr.get());\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  size_t memory_region_count = all_memory_writers_.size();\n  CHECK_LE(children_.size(), memory_region_count);\n\n  if (!AssignIfInRange(&memory_list_base_.NumberOfMemoryRanges,\n                       memory_region_count)) {\n    LOG(ERROR) << \"memory_region_count \" << memory_region_count\n               << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpMemoryListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK_LE(children_.size(), all_memory_writers_.size());\n\n  return sizeof(memory_list_base_) +\n         all_memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpMemoryListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK_LE(children_.size(), all_memory_writers_.size());\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& child : children_) {\n    children.push_back(child.get());\n  }\n\n  return children;\n}\n\nbool MinidumpMemoryListWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = &memory_list_base_;\n  iov.iov_len = sizeof(memory_list_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  for (const SnapshotMinidumpMemoryWriter* memory_writer :\n       all_memory_writers_) {\n    iov.iov_base = memory_writer->MinidumpMemoryDescriptor();\n    iov.iov_len = sizeof(MINIDUMP_MEMORY_DESCRIPTOR);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpStreamType MinidumpMemoryListWriter::StreamType() const {\n  return kMinidumpStreamTypeMemoryList;\n}\n\nvoid MinidumpMemoryListWriter::DropRangesThatOverlapNonOwned() {\n  std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> non_overlapping;\n  non_overlapping.reserve(children_.size());\n  for (auto& child_ptr : children_) {\n    bool overlaps = false;\n    for (const auto* non_owned : non_owned_memory_writers_) {\n      if (DetermineMergedRange(child_ptr->UnderlyingSnapshot(),\n                               non_owned->UnderlyingSnapshot(),\n                               nullptr)) {\n        overlaps = true;\n        break;\n      }\n    }\n    if (!overlaps)\n      non_overlapping.push_back(std::move(child_ptr));\n  }\n  std::swap(children_, non_overlapping);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_memory_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_writable.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\n\n//! \\brief The base class for writers of memory ranges pointed to by\n//!     MINIDUMP_MEMORY_DESCRIPTOR objects in a minidump file.\nclass SnapshotMinidumpMemoryWriter : public internal::MinidumpWritable,\n                                     public MemorySnapshot::Delegate {\n public:\n  explicit SnapshotMinidumpMemoryWriter(const MemorySnapshot* memory_snapshot);\n\n  SnapshotMinidumpMemoryWriter(const SnapshotMinidumpMemoryWriter&) = delete;\n  SnapshotMinidumpMemoryWriter& operator=(const SnapshotMinidumpMemoryWriter&) =\n      delete;\n\n  ~SnapshotMinidumpMemoryWriter() override;\n\n  //! \\brief Returns a MINIDUMP_MEMORY_DESCRIPTOR referencing the data that this\n  //!     object writes.\n  //!\n  //! This method is expected to be called by a MinidumpMemoryListWriter in\n  //! order to obtain a MINIDUMP_MEMORY_DESCRIPTOR to include in its list.\n  //!\n  //! \\note Valid in #kStateWritable.\n  const MINIDUMP_MEMORY_DESCRIPTOR* MinidumpMemoryDescriptor() const;\n\n  //! \\brief Registers a memory descriptor as one that should point to the\n  //!     object on which this method is called.\n  //!\n  //! This method is expected to be called by objects of other classes, when\n  //! those other classes have their own memory descriptors that need to point\n  //! to memory ranges within a minidump file. MinidumpThreadWriter is one such\n  //! class. This method is public for this reason, otherwise it would suffice\n  //! to be private.\n  //!\n  //! \\note Valid in #kStateFrozen or any preceding state.\n  void RegisterMemoryDescriptor(MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor);\n\n  //! \\brief Sets the underlying memory snapshot. Does not take ownership of \\a\n  //!     memory_snapshot.\n  void SetSnapshot(const MemorySnapshot* memory_snapshot) {\n    memory_snapshot_ = memory_snapshot;\n  }\n\n private:\n  friend class MinidumpMemoryListWriter;\n\n  // MemorySnapshot::Delegate:\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override;\n\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() final;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  //! \\brief Returns the object’s desired byte-boundary alignment.\n  //!\n  //! Memory regions are aligned to a 16-byte boundary. The actual alignment\n  //! requirements of any data within the memory region are unknown, and may be\n  //! more or less strict than this depending on the platform.\n  //!\n  //! \\return `16`.\n  //!\n  //! \\note Valid in #kStateFrozen or any subsequent state.\n  size_t Alignment() override;\n\n  bool WillWriteAtOffsetImpl(FileOffset offset) override;\n\n  //! \\brief Returns the object’s desired write phase.\n  //!\n  //! Memory regions are written at the end of minidump files, because it is\n  //! expected that unlike most other data in a minidump file, the contents of\n  //! memory regions will be accessed sparsely.\n  //!\n  //! \\return #kPhaseLate.\n  //!\n  //! \\note Valid in any state.\n  Phase WritePhase() final;\n\n  //! \\brief Gets the underlying memory snapshot that the memory writer will\n  //!     write to the minidump.\n  const MemorySnapshot* UnderlyingSnapshot() const { return memory_snapshot_; }\n\n  MINIDUMP_MEMORY_DESCRIPTOR memory_descriptor_;\n\n  // weak\n  std::vector<MINIDUMP_MEMORY_DESCRIPTOR*> registered_memory_descriptors_;\n  const MemorySnapshot* memory_snapshot_;\n  FileWriterInterface* file_writer_;\n};\n\n//! \\brief The writer for a MINIDUMP_MEMORY_LIST stream in a minidump file,\n//!     containing a list of MINIDUMP_MEMORY_DESCRIPTOR objects.\nclass MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpMemoryListWriter();\n\n  MinidumpMemoryListWriter(const MinidumpMemoryListWriter&) = delete;\n  MinidumpMemoryListWriter& operator=(const MinidumpMemoryListWriter&) = delete;\n\n  ~MinidumpMemoryListWriter() override;\n\n  //! \\brief Adds a concrete initialized SnapshotMinidumpMemoryWriter for each\n  //!     memory snapshot in \\a memory_snapshots to the MINIDUMP_MEMORY_LIST.\n  //!\n  //! Memory snapshots are added in the fashion of AddMemory().\n  //!\n  //! \\param[in] memory_snapshots The memory snapshots to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddFromSnapshot(\n      const std::vector<const MemorySnapshot*>& memory_snapshots);\n\n  //! \\brief Adds a SnapshotMinidumpMemoryWriter to the MINIDUMP_MEMORY_LIST.\n  //!\n  //! This object takes ownership of \\a memory_writer and becomes its parent in\n  //! the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddMemory(std::unique_ptr<SnapshotMinidumpMemoryWriter> memory_writer);\n\n  //! \\brief Adds a SnapshotMinidumpMemoryWriter that’s a child of another\n  //!     internal::MinidumpWritable object to the MINIDUMP_MEMORY_LIST.\n  //!\n  //! \\a memory_writer does not become a child of this object, but the\n  //! MINIDUMP_MEMORY_LIST will still contain a MINIDUMP_MEMORY_DESCRIPTOR for\n  //! it. \\a memory_writer must be a child of another object in the\n  //! internal::MinidumpWritable tree.\n  //!\n  //! This method exists to be called by objects that have their own\n  //! SnapshotMinidumpMemoryWriter children but wish for them to also appear in\n  //! the minidump file’s MINIDUMP_MEMORY_LIST. MinidumpThreadWriter, which has\n  //! a SnapshotMinidumpMemoryWriter for thread stack memory, is an example.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddNonOwnedMemory(SnapshotMinidumpMemoryWriter* memory_writer);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n  //! \\brief Merges any overlapping and abutting memory ranges that were added\n  //!     via AddFromSnapshot() and AddMemory() into single entries.\n  //!\n  //! This is expected to be called once just before writing, generally from\n  //! Freeze().\n  //!\n  //! This function has the side-effect of merging owned ranges, dropping any\n  //! owned ranges that overlap with non-owned ranges, removing empty ranges,\n  //! and sorting all ranges by address.\n  //!\n  //! Per its name, this coalesces owned memory, however, this is not a complete\n  //! solution for ensuring that no overlapping memory ranges are emitted in the\n  //! minidump. In particular, if AddNonOwnedMemory() is used to add multiple\n  //! overlapping ranges, then overlapping ranges will still be emitted to the\n  //! minidump. Currently, AddNonOwnedMemory() is used only for adding thread\n  //! stacks, so overlapping shouldn't be a problem in practice. For more\n  //! details see https://crashpad.chromium.org/bug/61 and\n  //! https://crrev.com/c/374539.\n  void CoalesceOwnedMemory();\n\n private:\n  //! \\brief Drops children_ ranges that overlap non_owned_memory_writers_.\n  void DropRangesThatOverlapNonOwned();\n\n  std::vector<SnapshotMinidumpMemoryWriter*> non_owned_memory_writers_;  // weak\n  std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> children_;\n  std::vector<std::unique_ptr<const MemorySnapshot>>\n      snapshots_created_during_merge_;\n  std::vector<SnapshotMinidumpMemoryWriter*> all_memory_writers_;  // weak\n  MINIDUMP_MEMORY_LIST memory_list_base_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_memory_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_memory_writer.h\"\n\n#include <iterator>\n#include <utility>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_memory_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_memory_snapshot.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr MinidumpStreamType kBogusStreamType =\n    static_cast<MinidumpStreamType>(1234);\n\n// expected_streams is the expected number of streams in the file. The memory\n// list must be the last stream. If there is another stream, it must come first,\n// have stream type kBogusStreamType, and have zero-length data.\nvoid GetMemoryListStream(const std::string& file_contents,\n                         const MINIDUMP_MEMORY_LIST** memory_list,\n                         const uint32_t expected_streams) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  const size_t kMemoryListStreamOffset =\n      kDirectoryOffset + expected_streams * sizeof(MINIDUMP_DIRECTORY);\n  const size_t kMemoryDescriptorsOffset =\n      kMemoryListStreamOffset + sizeof(MINIDUMP_MEMORY_LIST);\n\n  ASSERT_GE(file_contents.size(), kMemoryDescriptorsOffset);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, expected_streams, 0));\n  ASSERT_TRUE(directory);\n\n  size_t directory_index = 0;\n  if (expected_streams > 1) {\n    ASSERT_EQ(directory[directory_index].StreamType, kBogusStreamType);\n    ASSERT_EQ(directory[directory_index].Location.DataSize, 0u);\n    ASSERT_EQ(directory[directory_index].Location.Rva, kMemoryListStreamOffset);\n    ++directory_index;\n  }\n\n  ASSERT_EQ(directory[directory_index].StreamType,\n            kMinidumpStreamTypeMemoryList);\n  EXPECT_EQ(directory[directory_index].Location.Rva, kMemoryListStreamOffset);\n\n  *memory_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(\n      file_contents, directory[directory_index].Location);\n  ASSERT_TRUE(memory_list);\n}\n\nTEST(MinidumpMemoryWriter, EmptyMemoryList) {\n  MinidumpFileWriter minidump_file_writer;\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MEMORY_LIST));\n\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryListStream(string_file.string(), &memory_list, 1));\n\n  EXPECT_EQ(memory_list->NumberOfMemoryRanges, 0u);\n}\n\nTEST(MinidumpMemoryWriter, OneMemoryRegion) {\n  MinidumpFileWriter minidump_file_writer;\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n\n  constexpr uint64_t kBaseAddress = 0xfedcba9876543210;\n  constexpr size_t kSize = 0x1000;\n  constexpr uint8_t kValue = 'm';\n\n  auto memory_writer =\n      std::make_unique<TestMinidumpMemoryWriter>(kBaseAddress, kSize, kValue);\n  memory_list_writer->AddMemory(std::move(memory_writer));\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryListStream(string_file.string(), &memory_list, 1));\n\n  MINIDUMP_MEMORY_DESCRIPTOR expected;\n  expected.StartOfMemoryRange = kBaseAddress;\n  expected.Memory.DataSize = kSize;\n  expected.Memory.Rva =\n      sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n      sizeof(MINIDUMP_MEMORY_LIST) +\n      memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);\n  ExpectMinidumpMemoryDescriptorAndContents(&expected,\n                                            &memory_list->MemoryRanges[0],\n                                            string_file.string(),\n                                            kValue,\n                                            true);\n}\n\nTEST(MinidumpMemoryWriter, TwoMemoryRegions) {\n  MinidumpFileWriter minidump_file_writer;\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n\n  constexpr uint64_t kBaseAddress0 = 0xc0ffee;\n  constexpr size_t kSize0 = 0x0100;\n  constexpr uint8_t kValue0 = '6';\n  constexpr uint64_t kBaseAddress1 = 0xfac00fac;\n  constexpr size_t kSize1 = 0x0200;\n  constexpr uint8_t kValue1 = '!';\n\n  auto memory_writer_0 = std::make_unique<TestMinidumpMemoryWriter>(\n      kBaseAddress0, kSize0, kValue0);\n  memory_list_writer->AddMemory(std::move(memory_writer_0));\n  auto memory_writer_1 = std::make_unique<TestMinidumpMemoryWriter>(\n      kBaseAddress1, kSize1, kValue1);\n  memory_list_writer->AddMemory(std::move(memory_writer_1));\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryListStream(string_file.string(), &memory_list, 1));\n\n  EXPECT_EQ(memory_list->NumberOfMemoryRanges, 2u);\n\n  MINIDUMP_MEMORY_DESCRIPTOR expected;\n\n  {\n    SCOPED_TRACE(\"region 0\");\n\n    expected.StartOfMemoryRange = kBaseAddress0;\n    expected.Memory.DataSize = kSize0;\n    expected.Memory.Rva =\n        sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n        sizeof(MINIDUMP_MEMORY_LIST) +\n        memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);\n    ExpectMinidumpMemoryDescriptorAndContents(&expected,\n                                              &memory_list->MemoryRanges[0],\n                                              string_file.string(),\n                                              kValue0,\n                                              false);\n  }\n\n  {\n    SCOPED_TRACE(\"region 1\");\n\n    expected.StartOfMemoryRange = kBaseAddress1;\n    expected.Memory.DataSize = kSize1;\n    expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +\n                          memory_list->MemoryRanges[0].Memory.DataSize;\n    ExpectMinidumpMemoryDescriptorAndContents(&expected,\n                                              &memory_list->MemoryRanges[1],\n                                              string_file.string(),\n                                              kValue1,\n                                              true);\n  }\n}\n\nTEST(MinidumpMemoryWriter, RegionReadFails) {\n  MinidumpFileWriter minidump_file_writer;\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n\n  constexpr uint64_t kBaseAddress = 0xfedcba9876543210;\n  constexpr size_t kSize = 0x1000;\n  constexpr uint8_t kValue = 'm';\n\n  auto memory_writer =\n      std::make_unique<TestMinidumpMemoryWriter>(kBaseAddress, kSize, kValue);\n\n  // Make the read of that memory fail.\n  memory_writer->SetShouldFailRead(true);\n\n  memory_list_writer->AddMemory(std::move(memory_writer));\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryListStream(string_file.string(), &memory_list, 1));\n\n  MINIDUMP_MEMORY_DESCRIPTOR expected;\n  expected.StartOfMemoryRange = kBaseAddress;\n  expected.Memory.DataSize = kSize;\n  expected.Memory.Rva =\n      sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n      sizeof(MINIDUMP_MEMORY_LIST) +\n      memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);\n  ExpectMinidumpMemoryDescriptorAndContents(\n      &expected,\n      &memory_list->MemoryRanges[0],\n      string_file.string(),\n      0xfe,  // Not kValue ('m'), but the value that the implementation inserts\n             // if memory is unreadable.\n      true);\n}\n\nclass TestMemoryStream final : public internal::MinidumpStreamWriter {\n public:\n  TestMemoryStream(uint64_t base_address, size_t size, uint8_t value)\n      : MinidumpStreamWriter(), memory_(base_address, size, value) {}\n\n  TestMemoryStream(const TestMemoryStream&) = delete;\n  TestMemoryStream& operator=(const TestMemoryStream&) = delete;\n\n  ~TestMemoryStream() override {}\n\n  TestMinidumpMemoryWriter* memory() {\n    return &memory_;\n  }\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override {\n    return kBogusStreamType;\n  }\n\n protected:\n  // MinidumpWritable:\n  size_t SizeOfObject() override {\n    EXPECT_GE(state(), kStateFrozen);\n    return 0;\n  }\n\n  std::vector<MinidumpWritable*> Children() override {\n    EXPECT_GE(state(), kStateFrozen);\n    std::vector<MinidumpWritable*> children(1, memory());\n    return children;\n  }\n\n  bool WriteObject(FileWriterInterface* file_writer) override {\n    EXPECT_EQ(state(), kStateWritable);\n    return true;\n  }\n\n private:\n  TestMinidumpMemoryWriter memory_;\n};\n\nTEST(MinidumpMemoryWriter, ExtraMemory) {\n  // This tests MinidumpMemoryListWriter::AddExtraMemory(). That method adds\n  // a MinidumpMemoryWriter to the MinidumpMemoryListWriter without making the\n  // memory writer a child of the memory list writer.\n  MinidumpFileWriter minidump_file_writer;\n\n  constexpr uint64_t kBaseAddress0 = 0x1000;\n  constexpr size_t kSize0 = 0x0400;\n  constexpr uint8_t kValue0 = '1';\n  auto test_memory_stream =\n      std::make_unique<TestMemoryStream>(kBaseAddress0, kSize0, kValue0);\n\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n  memory_list_writer->AddNonOwnedMemory(test_memory_stream->memory());\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(test_memory_stream)));\n\n  constexpr uint64_t kBaseAddress1 = 0x2000;\n  constexpr size_t kSize1 = 0x0400;\n  constexpr uint8_t kValue1 = 'm';\n\n  auto memory_writer = std::make_unique<TestMinidumpMemoryWriter>(\n      kBaseAddress1, kSize1, kValue1);\n  memory_list_writer->AddMemory(std::move(memory_writer));\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryListStream(string_file.string(), &memory_list, 2));\n\n  EXPECT_EQ(memory_list->NumberOfMemoryRanges, 2u);\n\n  MINIDUMP_MEMORY_DESCRIPTOR expected;\n\n  {\n    SCOPED_TRACE(\"region 0\");\n\n    expected.StartOfMemoryRange = kBaseAddress0;\n    expected.Memory.DataSize = kSize0;\n    expected.Memory.Rva =\n        sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) +\n        sizeof(MINIDUMP_MEMORY_LIST) +\n        memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);\n    ExpectMinidumpMemoryDescriptorAndContents(&expected,\n                                              &memory_list->MemoryRanges[0],\n                                              string_file.string(),\n                                              kValue0,\n                                              false);\n  }\n\n  {\n    SCOPED_TRACE(\"region 1\");\n\n    expected.StartOfMemoryRange = kBaseAddress1;\n    expected.Memory.DataSize = kSize1;\n    expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +\n                          memory_list->MemoryRanges[0].Memory.DataSize;\n    ExpectMinidumpMemoryDescriptorAndContents(&expected,\n                                              &memory_list->MemoryRanges[1],\n                                              string_file.string(),\n                                              kValue1,\n                                              true);\n  }\n}\n\nTEST(MinidumpMemoryWriter, AddFromSnapshot) {\n  MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[3] = {};\n  uint8_t values[std::size(expect_memory_descriptors)] = {};\n\n  expect_memory_descriptors[0].StartOfMemoryRange = 0;\n  expect_memory_descriptors[0].Memory.DataSize = 0x1000;\n  values[0] = 0x01;\n\n  expect_memory_descriptors[1].StartOfMemoryRange = 0x2000;\n  expect_memory_descriptors[1].Memory.DataSize = 0x2000;\n  values[1] = 0xf4;\n\n  expect_memory_descriptors[2].StartOfMemoryRange = 0x7654321000000000;\n  expect_memory_descriptors[2].Memory.DataSize = 0x800;\n  values[2] = 0xa9;\n\n  std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;\n  std::vector<const MemorySnapshot*> memory_snapshots;\n  for (size_t index = 0; index < std::size(expect_memory_descriptors);\n       ++index) {\n    memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());\n    TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();\n    memory_snapshot->SetAddress(\n        expect_memory_descriptors[index].StartOfMemoryRange);\n    memory_snapshot->SetSize(expect_memory_descriptors[index].Memory.DataSize);\n    memory_snapshot->SetValue(values[index]);\n    memory_snapshots.push_back(memory_snapshot);\n  }\n\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n  memory_list_writer->AddFromSnapshot(memory_snapshots);\n\n  MinidumpFileWriter minidump_file_writer;\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryListStream(string_file.string(), &memory_list, 1));\n\n  ASSERT_EQ(memory_list->NumberOfMemoryRanges, 3u);\n\n  for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS, index));\n    ExpectMinidumpMemoryDescriptorAndContents(\n        &expect_memory_descriptors[index],\n        &memory_list->MemoryRanges[index],\n        string_file.string(),\n        values[index],\n        index == memory_list->NumberOfMemoryRanges - 1);\n  }\n}\n\nTEST(MinidumpMemoryWriter, CoalesceExplicitMultiple) {\n  MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[4] = {};\n  uint8_t values[std::size(expect_memory_descriptors)] = {};\n\n  expect_memory_descriptors[0].StartOfMemoryRange = 0;\n  expect_memory_descriptors[0].Memory.DataSize = 1000;\n  values[0] = 0x01;\n\n  expect_memory_descriptors[1].StartOfMemoryRange = 10000;\n  expect_memory_descriptors[1].Memory.DataSize = 2000;\n  values[1] = 0xf4;\n\n  expect_memory_descriptors[2].StartOfMemoryRange = 0x1111111111111111;\n  expect_memory_descriptors[2].Memory.DataSize = 1024;\n  values[2] = 0x99;\n\n  expect_memory_descriptors[3].StartOfMemoryRange = 0xfedcba9876543210;\n  expect_memory_descriptors[3].Memory.DataSize = 1024;\n  values[3] = 0x88;\n\n  struct {\n    uint64_t base;\n    size_t size;\n    uint8_t value;\n  } snapshots_to_add[] = {\n      // Various overlapping.\n      {0, 500, 0x01},\n      {0, 500, 0x01},\n      {250, 500, 0x01},\n      {600, 400, 0x01},\n\n      // Empty removed.\n      {0, 0, 0xbb},\n      {300, 0, 0xcc},\n      {1000, 0, 0xdd},\n      {12000, 0, 0xee},\n\n      // Abutting.\n      {10000, 500, 0xf4},\n      {10500, 500, 0xf4},\n      {11000, 1000, 0xf4},\n\n      // Large base addresses.\n      { 0xfedcba9876543210, 1024, 0x88 },\n      { 0x1111111111111111, 1024, 0x99 },\n  };\n\n  std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;\n  std::vector<const MemorySnapshot*> memory_snapshots;\n  for (const auto& to_add : snapshots_to_add) {\n    memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());\n    TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();\n    memory_snapshot->SetAddress(to_add.base);\n    memory_snapshot->SetSize(to_add.size);\n    memory_snapshot->SetValue(to_add.value);\n    memory_snapshots.push_back(memory_snapshot);\n  }\n\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n  memory_list_writer->AddFromSnapshot(memory_snapshots);\n\n  MinidumpFileWriter minidump_file_writer;\n  minidump_file_writer.AddStream(std::move(memory_list_writer));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryListStream(string_file.string(), &memory_list, 1));\n\n  ASSERT_EQ(4u, memory_list->NumberOfMemoryRanges);\n\n  for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS, index));\n    ExpectMinidumpMemoryDescriptorAndContents(\n        &expect_memory_descriptors[index],\n        &memory_list->MemoryRanges[index],\n        string_file.string(),\n        values[index],\n        index == memory_list->NumberOfMemoryRanges - 1);\n  }\n}\n\nstruct TestRange {\n  TestRange(uint64_t base, size_t size) : base(base), size(size) {}\n\n  uint64_t base;\n  size_t size;\n};\n\n// Parses a string spec to build a list of ranges suitable for CoalesceTest().\nstd::vector<TestRange> ParseCoalesceSpec(const char* spec) {\n  std::vector<TestRange> result;\n  enum { kNone, kSpace, kDot } state = kNone;\n  const char* range_started_at = nullptr;\n  for (const char* p = spec;; ++p) {\n    EXPECT_TRUE(*p == ' ' || *p == '.' || *p == 0);\n    if (*p == ' ' || *p == 0) {\n      if (state == kDot) {\n        result.push_back(\n            TestRange(range_started_at - spec, p - range_started_at));\n      }\n      state = kSpace;\n      range_started_at = nullptr;\n    } else if (*p == '.') {\n      if (state != kDot) {\n        range_started_at = p;\n        state = kDot;\n      }\n    }\n\n    if (*p == 0)\n      break;\n  }\n\n  return result;\n}\n\nTEST(MinidumpMemoryWriter, CoalesceSpecHelperParse) {\n  const auto empty = ParseCoalesceSpec(\"\");\n  ASSERT_EQ(empty.size(), 0u);\n\n  const auto a = ParseCoalesceSpec(\"...\");\n  ASSERT_EQ(a.size(), 1u);\n  EXPECT_EQ(a[0].base, 0u);\n  EXPECT_EQ(a[0].size, 3u);\n\n  const auto b = ParseCoalesceSpec(\"  ...\");\n  ASSERT_EQ(b.size(), 1u);\n  EXPECT_EQ(b[0].base, 2u);\n  EXPECT_EQ(b[0].size, 3u);\n\n  const auto c = ParseCoalesceSpec(\"  ...  \");\n  ASSERT_EQ(c.size(), 1u);\n  EXPECT_EQ(c[0].base, 2u);\n  EXPECT_EQ(c[0].size, 3u);\n\n  const auto d = ParseCoalesceSpec(\"  ...  ....\");\n  ASSERT_EQ(d.size(), 2u);\n  EXPECT_EQ(d[0].base, 2u);\n  EXPECT_EQ(d[0].size, 3u);\n  EXPECT_EQ(d[1].base, 7u);\n  EXPECT_EQ(d[1].size, 4u);\n\n  const auto e = ParseCoalesceSpec(\"  ...  ...... ... \");\n  ASSERT_EQ(e.size(), 3u);\n  EXPECT_EQ(e[0].base, 2u);\n  EXPECT_EQ(e[0].size, 3u);\n  EXPECT_EQ(e[1].base, 7u);\n  EXPECT_EQ(e[1].size, 6u);\n  EXPECT_EQ(e[2].base, 14u);\n  EXPECT_EQ(e[2].size, 3u);\n}\n\nconstexpr uint8_t kMemoryValue = 0xcd;\n\n// Builds a coalesce test out of specs of ' ' and '.'. Tests that when the two\n// ranges are added and coalesced, the result is equal to expected.\nvoid CoalesceTest(const char* r1_spec,\n                  const char* r2_spec,\n                  const char* expected_spec) {\n  auto r1 = ParseCoalesceSpec(r1_spec);\n  auto r2 = ParseCoalesceSpec(r2_spec);\n  auto expected = ParseCoalesceSpec(expected_spec);\n\n  std::vector<MINIDUMP_MEMORY_DESCRIPTOR> expect_memory_descriptors;\n  for (const auto& range : expected) {\n    MINIDUMP_MEMORY_DESCRIPTOR mmd = {};\n    mmd.StartOfMemoryRange = range.base;\n    mmd.Memory.DataSize = static_cast<uint32_t>(range.size);\n    expect_memory_descriptors.push_back(mmd);\n  }\n\n  std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;\n  std::vector<const MemorySnapshot*> memory_snapshots;\n\n  const auto add_test_memory_snapshots = [&memory_snapshots_owner,\n                                          &memory_snapshots](\n                                             std::vector<TestRange> ranges) {\n    for (const auto& r : ranges) {\n      memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());\n      TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();\n      memory_snapshot->SetAddress(r.base);\n      memory_snapshot->SetSize(r.size);\n      memory_snapshot->SetValue(kMemoryValue);\n      memory_snapshots.push_back(memory_snapshot);\n    }\n  };\n  add_test_memory_snapshots(r1);\n  add_test_memory_snapshots(r2);\n\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n  memory_list_writer->AddFromSnapshot(memory_snapshots);\n\n  MinidumpFileWriter minidump_file_writer;\n  minidump_file_writer.AddStream(std::move(memory_list_writer));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetMemoryListStream(string_file.string(), &memory_list, 1));\n\n  ASSERT_EQ(expected.size(), memory_list->NumberOfMemoryRanges);\n\n  for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS, index));\n    ExpectMinidumpMemoryDescriptorAndContents(\n        &expect_memory_descriptors[index],\n        &memory_list->MemoryRanges[index],\n        string_file.string(),\n        kMemoryValue,\n        index == memory_list->NumberOfMemoryRanges - 1);\n  }\n}\n\nTEST(MinidumpMemoryWriter, CoalescePairsVariousCases) {\n  // clang-format off\n\n  CoalesceTest(\"  .........\",\n               \"         .......\",\n  /* result */ \"  ..............\");\n\n  CoalesceTest(\"         .......\",\n               \"  .........\",\n  /* result */ \"  ..............\");\n\n  CoalesceTest(\"     ...\",\n               \"  .........\",\n  /* result */ \"  .........\");\n\n  CoalesceTest(\"  .........\",\n               \"    ......\",\n  /* result */ \"  .........\");\n\n  CoalesceTest(\"  ...\",\n               \"  ........\",\n  /* result */ \"  ........\");\n\n  CoalesceTest(\"  ........\",\n               \"  ...\",\n  /* result */ \"  ........\");\n\n  CoalesceTest(\"       ...\",\n               \"  ........\",\n  /* result */ \"  ........\");\n\n  CoalesceTest(\"  ........\",\n               \"       ...\",\n  /* result */ \"  ........\");\n\n  CoalesceTest(\"  ...     \",\n               \"       ...\",\n  /* result */ \"  ...  ...\");\n\n  CoalesceTest(\"       ...\",\n               \"  ...     \",\n  /* result */ \"  ...  ...\");\n\n  CoalesceTest(\"...\",\n               \".....\",\n  /* result */ \".....\");\n\n  CoalesceTest(\"...\",\n               \"   ..\",\n  /* result */ \".....\");\n\n  CoalesceTest(\"   .....\",\n               \" ..\",\n  /* result */ \" .......\");\n\n  CoalesceTest(\"  .........   ......\",\n               \"         .......\",\n  /* result */ \"  ..................\");\n\n  CoalesceTest(\"         .......\",\n               \"  .........   ......\",\n  /* result */ \"  ..................\");\n\n  CoalesceTest(\"      .....\",\n               \"  .........   ......\",\n  /* result */ \"  .........   ......\");\n\n  CoalesceTest(\"      .........      ....... ....  .\",\n               \"  .........    ......           ....\",\n  /* result */ \"  .......................... .......\");\n\n  // clang-format on\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_misc_info_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_misc_info_writer.h\"\n\n#include <iterator>\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"minidump/minidump_context_writer.h\"\n#include \"minidump/minidump_writer_util.h\"\n#include \"package.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/in_range_cast.h\"\n#include \"util/numeric/safe_assignment.h\"\n\n#if BUILDFLAG(IS_MAC)\n#include <Availability.h>\n#elif BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#endif\n\nnamespace crashpad {\nnamespace {\n\nuint32_t TimevalToRoundedSeconds(const timeval& tv) {\n  uint32_t seconds =\n      InRangeCast<uint32_t>(tv.tv_sec, std::numeric_limits<uint32_t>::max());\n  constexpr int kMicrosecondsPerSecond = static_cast<int>(1E6);\n  if (tv.tv_usec >= kMicrosecondsPerSecond / 2 &&\n      seconds != std::numeric_limits<uint32_t>::max()) {\n    ++seconds;\n  }\n  return seconds;\n}\n\n// For MINIDUMP_MISC_INFO_4::BuildString. dbghelp only places OS version\n// information here, but if a machine description is also available, this is the\n// only reasonable place in a minidump file to put it.\nstd::string BuildString(const SystemSnapshot* system_snapshot) {\n  std::string os_version_full = system_snapshot->OSVersionFull();\n  std::string machine_description = system_snapshot->MachineDescription();\n  if (!os_version_full.empty()) {\n    if (!machine_description.empty()) {\n      return base::StringPrintf(\n          \"%s; %s\", os_version_full.c_str(), machine_description.c_str());\n    }\n    return os_version_full;\n  }\n  return machine_description;\n}\n\n#if BUILDFLAG(IS_MAC)\n// Converts the value of the __MAC_OS_X_VERSION_MIN_REQUIRED or\n// __MAC_OS_X_VERSION_MAX_ALLOWED macro from <Availability.h> to a number\n// identifying the macOS version that it represents, in the same format used by\n// MacOSVersionNumber(). For example, with an argument of __MAC_10_15, this\n// function will return 10'15'00, which is incidentally the same as __MAC_10_15.\n// With an argument of __MAC_10_9, this function will return 10'09'00, different\n// from __MAC_10_9, which is 10'9'0.\nint AvailabilityVersionToMacOSVersionNumber(int availability) {\n#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_10\n  DCHECK_GE(availability, 10'0'0);\n\n  // Until __MAC_10_10, the format is major * 1'0'0 + minor * 1'0 + bugfix.\n  if (availability >= 10'0'0 && availability <= 10'9'9) {\n    int minor = (availability / 1'0) % 1'0;\n    int bugfix = availability % 1'0;\n    return 10'00'00 + minor * 1'00 + bugfix;\n  }\n#endif\n\n  // Since __MAC_10_10, the format is major * 1'00'00 + minor * 1'00 + bugfix.\n  DCHECK_GE(availability, 10'10'00);\n  DCHECK_LE(availability, 99'99'99);\n\n  return availability;\n}\n#endif  // BUILDFLAG(IS_MAC)\n\nbool MaybeSetXStateData(const ProcessSnapshot* process_snapshot,\n                        XSTATE_CONFIG_FEATURE_MSC_INFO* xstate) {\n  // Cannot set xstate data if there are no threads.\n  auto threads = process_snapshot->Threads();\n  if (threads.size() == 0)\n    return false;\n\n  // All threads should be the same as we request contexts in the same way.\n  auto context = threads.at(0)->Context();\n\n  // Only support AMD64.\n  if (context->architecture != kCPUArchitectureX86_64)\n    return false;\n\n  // If no extended features, then we will just write the standard context.\n  if (context->x86_64->xstate.enabled_features == 0)\n    return false;\n\n  xstate->SizeOfInfo = sizeof(*xstate);\n  // Needs to match the size of the context we'll write or the dump is invalid,\n  // so ask the first thread how large it will be.\n  auto context_writer = MinidumpContextWriter::CreateFromSnapshot(context);\n  xstate->ContextSize =\n      static_cast<uint32_t>(context_writer->FreezeAndGetSizeOfObject());\n  // Note: This isn't the same as xstateenabledfeatures!\n  xstate->EnabledFeatures =\n      context->x86_64->xstate.enabled_features | XSTATE_COMPACTION_ENABLE_MASK;\n\n  // Note: if other XSAVE entries are to be supported they will be in order,\n  // and may have different offsets depending on what is saved.\n  if (context->x86_64->xstate.enabled_features & XSTATE_MASK_CET_U) {\n    xstate->Features[XSTATE_CET_U].Offset = kXSaveAreaFirstOffset;\n    xstate->Features[XSTATE_CET_U].Size = sizeof(MinidumpAMD64XSaveFormatCetU);\n  }\n  return true;\n}\n\n}  // namespace\n\nnamespace internal {\n\n// For MINIDUMP_MISC_INFO_4::DbgBldStr. dbghelp produces strings like\n// “dbghelp.i386,6.3.9600.16520” and “dbghelp.amd64,6.3.9600.16520”. Mimic that\n// format, and add the OS that wrote the minidump along with any relevant\n// platform-specific data describing the compilation environment.\nstd::string MinidumpMiscInfoDebugBuildString() {\n  // Caution: the minidump file format only has room for 39 UTF-16 code units\n  // plus a UTF-16 NUL terminator. Don’t let strings get longer than this, or\n  // they will be truncated and a message will be logged.\n#if BUILDFLAG(IS_MAC)\n  static constexpr char kOS[] = \"mac\";\n#elif BUILDFLAG(IS_IOS)\n  static constexpr char kOS[] = \"ios\";\n#elif BUILDFLAG(IS_ANDROID)\n  static constexpr char kOS[] = \"android\";\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  static constexpr char kOS[] = \"linux\";\n#elif BUILDFLAG(IS_WIN)\n  static constexpr char kOS[] = \"win\";\n#elif BUILDFLAG(IS_FUCHSIA)\n  static constexpr char kOS[] = \"fuchsia\";\n#else\n#error define kOS for this operating system\n#endif\n\n#if defined(ARCH_CPU_X86)\n  static constexpr char kCPU[] = \"i386\";\n#elif defined(ARCH_CPU_X86_64)\n  static constexpr char kCPU[] = \"amd64\";\n#elif defined(ARCH_CPU_ARMEL)\n  static constexpr char kCPU[] = \"arm\";\n#elif defined(ARCH_CPU_ARM64)\n  static constexpr char kCPU[] = \"arm64\";\n#elif defined(ARCH_CPU_MIPSEL)\n  static constexpr char kCPU[] = \"mips\";\n#elif defined(ARCH_CPU_MIPS64EL)\n  static constexpr char kCPU[] = \"mips64\";\n#elif defined(ARCH_CPU_RISCV64)\n  static constexpr char kCPU[] = \"riscv64\";\n#else\n#error define kCPU for this CPU\n#endif\n\n  std::string debug_build_string = base::StringPrintf(\"%s.%s,%s,%s\",\n                                                      PACKAGE_TARNAME,\n                                                      kCPU,\n                                                      PACKAGE_VERSION,\n                                                      kOS);\n\n#if BUILDFLAG(IS_MAC)\n  debug_build_string += base::StringPrintf(\n      \",%d,%d\",\n      AvailabilityVersionToMacOSVersionNumber(__MAC_OS_X_VERSION_MIN_REQUIRED),\n      AvailabilityVersionToMacOSVersionNumber(__MAC_OS_X_VERSION_MAX_ALLOWED));\n#elif BUILDFLAG(IS_ANDROID)\n  debug_build_string += base::StringPrintf(\",%d\", __ANDROID_API__);\n#endif\n\n  return debug_build_string;\n}\n\n}  // namespace internal\n\nMinidumpMiscInfoWriter::MinidumpMiscInfoWriter()\n    : MinidumpStreamWriter(), misc_info_(), has_xstate_data_(false) {\n}\n\nMinidumpMiscInfoWriter::~MinidumpMiscInfoWriter() {\n}\n\nvoid MinidumpMiscInfoWriter::InitializeFromSnapshot(\n    const ProcessSnapshot* process_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(misc_info_.Flags1, 0u);\n\n  SetProcessID(InRangeCast<uint32_t>(process_snapshot->ProcessID(), 0));\n\n  const SystemSnapshot* system_snapshot = process_snapshot->System();\n\n  uint64_t current_hz;\n  uint64_t max_hz;\n  system_snapshot->CPUFrequency(&current_hz, &max_hz);\n  constexpr uint32_t kHzPerMHz = static_cast<const uint32_t>(1E6);\n  SetProcessorPowerInfo(\n      InRangeCast<uint32_t>(current_hz / kHzPerMHz,\n                            std::numeric_limits<uint32_t>::max()),\n      InRangeCast<uint32_t>(max_hz / kHzPerMHz,\n                            std::numeric_limits<uint32_t>::max()),\n      0,\n      0,\n      0);\n\n  timeval start_time;\n  process_snapshot->ProcessStartTime(&start_time);\n\n  timeval user_time;\n  timeval system_time;\n  process_snapshot->ProcessCPUTimes(&user_time, &system_time);\n\n  // Round the resource usage fields to the nearest second, because the minidump\n  // format only has one-second resolution. The start_time field is truncated\n  // instead of rounded so that the process uptime is reflected more accurately\n  // when the start time is compared to the snapshot time in the\n  // MINIDUMP_HEADER, which is also truncated, not rounded.\n  uint32_t user_seconds = TimevalToRoundedSeconds(user_time);\n  uint32_t system_seconds = TimevalToRoundedSeconds(system_time);\n\n  SetProcessTimes(start_time.tv_sec, user_seconds, system_seconds);\n\n  // This determines the system’s time zone, which may be different than the\n  // process’ notion of the time zone.\n  SystemSnapshot::DaylightSavingTimeStatus dst_status;\n  int standard_offset_seconds;\n  int daylight_offset_seconds;\n  std::string standard_name;\n  std::string daylight_name;\n  system_snapshot->TimeZone(&dst_status,\n                            &standard_offset_seconds,\n                            &daylight_offset_seconds,\n                            &standard_name,\n                            &daylight_name);\n\n  // standard_offset_seconds is seconds east of UTC, but the minidump file wants\n  // minutes west of UTC. daylight_offset_seconds is also seconds east of UTC,\n  // but the minidump file wants minutes west of the standard offset. The empty\n  // ({}) arguments are for the transition times in and out of daylight saving\n  // time. These are not determined because no API exists to do so, and the\n  // transition times may vary from year to year.\n  SetTimeZone(dst_status,\n              standard_offset_seconds / -60,\n              standard_name,\n              {},\n              0,\n              daylight_name,\n              {},\n              (standard_offset_seconds - daylight_offset_seconds) / 60);\n\n  SetBuildString(BuildString(system_snapshot),\n                 internal::MinidumpMiscInfoDebugBuildString());\n\n  XSTATE_CONFIG_FEATURE_MSC_INFO xstate{};\n  if (MaybeSetXStateData(process_snapshot, &xstate)) {\n    SetXStateData(xstate);\n  }\n}\n\nvoid MinidumpMiscInfoWriter::SetProcessID(uint32_t process_id) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.ProcessId = process_id;\n  misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_ID;\n}\n\nvoid MinidumpMiscInfoWriter::SetProcessTimes(time_t process_create_time,\n                                             uint32_t process_user_time,\n                                             uint32_t process_kernel_time) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  internal::MinidumpWriterUtil::AssignTimeT(&misc_info_.ProcessCreateTime,\n                                            process_create_time);\n\n  misc_info_.ProcessUserTime = process_user_time;\n  misc_info_.ProcessKernelTime = process_kernel_time;\n  misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_TIMES;\n}\n\nvoid MinidumpMiscInfoWriter::SetProcessorPowerInfo(\n    uint32_t processor_max_mhz,\n    uint32_t processor_current_mhz,\n    uint32_t processor_mhz_limit,\n    uint32_t processor_max_idle_state,\n    uint32_t processor_current_idle_state) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.ProcessorMaxMhz = processor_max_mhz;\n  misc_info_.ProcessorCurrentMhz = processor_current_mhz;\n  misc_info_.ProcessorMhzLimit = processor_mhz_limit;\n  misc_info_.ProcessorMaxIdleState = processor_max_idle_state;\n  misc_info_.ProcessorCurrentIdleState = processor_current_idle_state;\n  misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESSOR_POWER_INFO;\n}\n\nvoid MinidumpMiscInfoWriter::SetProcessIntegrityLevel(\n    uint32_t process_integrity_level) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.ProcessIntegrityLevel = process_integrity_level;\n  misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_INTEGRITY;\n}\n\nvoid MinidumpMiscInfoWriter::SetProcessExecuteFlags(\n    uint32_t process_execute_flags) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.ProcessExecuteFlags = process_execute_flags;\n  misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS;\n}\n\nvoid MinidumpMiscInfoWriter::SetProtectedProcess(uint32_t protected_process) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.ProtectedProcess = protected_process;\n  misc_info_.Flags1 |= MINIDUMP_MISC3_PROTECTED_PROCESS;\n}\n\nvoid MinidumpMiscInfoWriter::SetTimeZone(uint32_t time_zone_id,\n                                         int32_t bias,\n                                         const std::string& standard_name,\n                                         const SYSTEMTIME& standard_date,\n                                         int32_t standard_bias,\n                                         const std::string& daylight_name,\n                                         const SYSTEMTIME& daylight_date,\n                                         int32_t daylight_bias) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.TimeZoneId = time_zone_id;\n  misc_info_.TimeZone.Bias = bias;\n\n  internal::MinidumpWriterUtil::AssignUTF8ToUTF16(\n      AsU16CStr(misc_info_.TimeZone.StandardName),\n      std::size(misc_info_.TimeZone.StandardName),\n      standard_name);\n\n  misc_info_.TimeZone.StandardDate = standard_date;\n  misc_info_.TimeZone.StandardBias = standard_bias;\n\n  internal::MinidumpWriterUtil::AssignUTF8ToUTF16(\n      AsU16CStr(misc_info_.TimeZone.DaylightName),\n      std::size(misc_info_.TimeZone.DaylightName),\n      daylight_name);\n\n  misc_info_.TimeZone.DaylightDate = daylight_date;\n  misc_info_.TimeZone.DaylightBias = daylight_bias;\n\n  misc_info_.Flags1 |= MINIDUMP_MISC3_TIMEZONE;\n}\n\nvoid MinidumpMiscInfoWriter::SetBuildString(\n    const std::string& build_string,\n    const std::string& debug_build_string) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.Flags1 |= MINIDUMP_MISC4_BUILDSTRING;\n\n  internal::MinidumpWriterUtil::AssignUTF8ToUTF16(\n      AsU16CStr(misc_info_.BuildString),\n      std::size(misc_info_.BuildString),\n      build_string);\n  internal::MinidumpWriterUtil::AssignUTF8ToUTF16(\n      AsU16CStr(misc_info_.DbgBldStr),\n      std::size(misc_info_.DbgBldStr),\n      debug_build_string);\n}\n\nvoid MinidumpMiscInfoWriter::SetXStateData(\n    const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.XStateData = xstate_data;\n  has_xstate_data_ = true;\n}\n\nbool MinidumpMiscInfoWriter::HasXStateData() const {\n  return has_xstate_data_;\n}\n\nvoid MinidumpMiscInfoWriter::SetProcessCookie(uint32_t process_cookie) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_info_.ProcessCookie = process_cookie;\n  misc_info_.Flags1 |= MINIDUMP_MISC5_PROCESS_COOKIE;\n}\n\nbool MinidumpMiscInfoWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  size_t size = CalculateSizeOfObjectFromFlags();\n  if (!AssignIfInRange(&misc_info_.SizeOfInfo, size)) {\n    LOG(ERROR) << \"size \" << size << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpMiscInfoWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return CalculateSizeOfObjectFromFlags();\n}\n\nbool MinidumpMiscInfoWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return file_writer->Write(&misc_info_, CalculateSizeOfObjectFromFlags());\n}\n\nMinidumpStreamType MinidumpMiscInfoWriter::StreamType() const {\n  return kMinidumpStreamTypeMiscInfo;\n}\n\nsize_t MinidumpMiscInfoWriter::CalculateSizeOfObjectFromFlags() const {\n  DCHECK_GE(state(), kStateFrozen);\n\n  if (has_xstate_data_ || (misc_info_.Flags1 & MINIDUMP_MISC5_PROCESS_COOKIE)) {\n    return sizeof(MINIDUMP_MISC_INFO_5);\n  }\n  if (misc_info_.Flags1 & MINIDUMP_MISC4_BUILDSTRING) {\n    return sizeof(MINIDUMP_MISC_INFO_4);\n  }\n  if (misc_info_.Flags1 &\n      (MINIDUMP_MISC3_PROCESS_INTEGRITY | MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS |\n       MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC3_PROTECTED_PROCESS)) {\n    return sizeof(MINIDUMP_MISC_INFO_3);\n  }\n  if (misc_info_.Flags1 & MINIDUMP_MISC1_PROCESSOR_POWER_INFO) {\n    return sizeof(MINIDUMP_MISC_INFO_2);\n  }\n  return sizeof(MINIDUMP_MISC_INFO);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_misc_info_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <time.h>\n#include <wchar.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\nclass ProcessSnapshot;\n\nnamespace internal {\n\n//! \\brief Returns the string to set in MINIDUMP_MISC_INFO_4::DbgBldStr.\n//!\n//! dbghelp produces strings like `\"dbghelp.i386,6.3.9600.16520\"` and\n//! `\"dbghelp.amd64,6.3.9600.16520\"`. This function mimics that format, and adds\n//! the OS that wrote the minidump along with any relevant platform-specific\n//! data describing the compilation environment.\n//!\n//! This function is an implementation detail of\n//! MinidumpMiscInfoWriter::InitializeFromSnapshot() and is only exposed for\n//! testing purposes.\nstd::string MinidumpMiscInfoDebugBuildString();\n\n}  // namespace internal\n\n//! \\brief The writer for a stream in the MINIDUMP_MISC_INFO family in a\n//!     minidump file.\n//!\n//! The actual stream written will be a MINIDUMP_MISC_INFO,\n//! MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, or\n//! MINIDUMP_MISC_INFO_5 stream. Later versions of MINIDUMP_MISC_INFO are\n//! supersets of earlier versions. The earliest version that supports all of the\n//! information that an object of this class contains will be used.\nclass MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpMiscInfoWriter();\n\n  MinidumpMiscInfoWriter(const MinidumpMiscInfoWriter&) = delete;\n  MinidumpMiscInfoWriter& operator=(const MinidumpMiscInfoWriter&) = delete;\n\n  ~MinidumpMiscInfoWriter() override;\n\n  //! \\brief Initializes MINIDUMP_MISC_INFO_N based on \\a process_snapshot.\n  //!\n  //! \\param[in] process_snapshot The process snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot);\n\n  //! \\brief Sets the field referenced by #MINIDUMP_MISC1_PROCESS_ID.\n  void SetProcessID(uint32_t process_id);\n\n  //! \\brief Sets the fields referenced by #MINIDUMP_MISC1_PROCESS_TIMES.\n  void SetProcessTimes(time_t process_create_time,\n                       uint32_t process_user_time,\n                       uint32_t process_kernel_time);\n\n  //! \\brief Sets the fields referenced by #MINIDUMP_MISC1_PROCESSOR_POWER_INFO.\n  void SetProcessorPowerInfo(uint32_t processor_max_mhz,\n                             uint32_t processor_current_mhz,\n                             uint32_t processor_mhz_limit,\n                             uint32_t processor_max_idle_state,\n                             uint32_t processor_current_idle_state);\n\n  //! \\brief Sets the field referenced by #MINIDUMP_MISC3_PROCESS_INTEGRITY.\n  void SetProcessIntegrityLevel(uint32_t process_integrity_level);\n\n  //! \\brief Sets the field referenced by #MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS.\n  void SetProcessExecuteFlags(uint32_t process_execute_flags);\n\n  //! \\brief Sets the field referenced by #MINIDUMP_MISC3_PROTECTED_PROCESS.\n  void SetProtectedProcess(uint32_t protected_process);\n\n  //! \\brief Sets the fields referenced by #MINIDUMP_MISC3_TIMEZONE.\n  void SetTimeZone(uint32_t time_zone_id,\n                   int32_t bias,\n                   const std::string& standard_name,\n                   const SYSTEMTIME& standard_date,\n                   int32_t standard_bias,\n                   const std::string& daylight_name,\n                   const SYSTEMTIME& daylight_date,\n                   int32_t daylight_bias);\n\n  //! \\brief Sets the fields referenced by #MINIDUMP_MISC4_BUILDSTRING.\n  void SetBuildString(const std::string& build_string,\n                      const std::string& debug_build_string);\n\n  // TODO(mark): Provide a better interface than this. Don’t force callers to\n  // build their own XSTATE_CONFIG_FEATURE_MSC_INFO structure.\n  //\n  //! \\brief Sets MINIDUMP_MISC_INFO_5::XStateData.\n  void SetXStateData(const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data);\n\n  //! \\brief Will this write extended context information?\n  bool HasXStateData() const;\n\n  //! \\brief Sets the field referenced by #MINIDUMP_MISC5_PROCESS_COOKIE.\n  void SetProcessCookie(uint32_t process_cookie);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n  MinidumpStreamType StreamType() const override;\n\n private:\n  //! \\brief Returns the size of the object to be written based on\n  //!     MINIDUMP_MISC_INFO_N::Flags1.\n  //!\n  //! The smallest defined structure type in the MINIDUMP_MISC_INFO family that\n  //! can hold all of the data that has been populated will be used.\n  size_t CalculateSizeOfObjectFromFlags() const;\n\n  MINIDUMP_MISC_INFO_N misc_info_;\n  bool has_xstate_data_;\n};\n\n//! \\brief Conversion functions from a native UTF16 C-string to a char16_t\n//!     C-string. No-op where the native UTF16 string is std::u16string.\n#if defined(WCHAR_T_IS_16_BIT) || DOXYGEN\ninline const char16_t* AsU16CStr(const wchar_t* str) {\n  return reinterpret_cast<const char16_t*>(str);\n}\n\ninline char16_t* AsU16CStr(wchar_t* str) {\n  return reinterpret_cast<char16_t*>(str);\n}\n#else\ninline const char16_t* AsU16CStr(const char16_t* str) {\n  return str;\n}\n\ninline char16_t* AsU16CStr(char16_t* str) {\n  return str;\n}\n#endif\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_misc_info_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_misc_info_writer.h\"\n\n#include <string.h>\n\n#include <iterator>\n#include <string>\n#include <utility>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_process_snapshot.h\"\n#include \"snapshot/test/test_system_snapshot.h\"\n#include \"util/file/string_file.h\"\n#include \"util/stdlib/strlcpy.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\ntemplate <typename T>\nvoid GetMiscInfoStream(const std::string& file_contents, const T** misc_info) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kMiscInfoStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kMiscInfoStreamSize = sizeof(T);\n  constexpr size_t kFileSize = kMiscInfoStreamOffset + kMiscInfoStreamSize;\n\n  ASSERT_EQ(file_contents.size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeMiscInfo);\n  EXPECT_EQ(directory[0].Location.Rva, kMiscInfoStreamOffset);\n\n  *misc_info = MinidumpWritableAtLocationDescriptor<T>(file_contents,\n                                                       directory[0].Location);\n  ASSERT_TRUE(misc_info);\n}\n\nvoid ExpectNULPaddedString16Equal(const char16_t* expected,\n                                  const char16_t* observed,\n                                  size_t size) {\n  std::u16string expected_string(expected, size);\n  std::u16string observed_string(observed, size);\n  EXPECT_EQ(observed_string, expected_string);\n}\n\nvoid ExpectSystemTimeEqual(const SYSTEMTIME* expected,\n                           const SYSTEMTIME* observed) {\n  EXPECT_EQ(observed->wYear, expected->wYear);\n  EXPECT_EQ(observed->wMonth, expected->wMonth);\n  EXPECT_EQ(observed->wDayOfWeek, expected->wDayOfWeek);\n  EXPECT_EQ(observed->wDay, expected->wDay);\n  EXPECT_EQ(observed->wHour, expected->wHour);\n  EXPECT_EQ(observed->wMinute, expected->wMinute);\n  EXPECT_EQ(observed->wSecond, expected->wSecond);\n  EXPECT_EQ(observed->wMilliseconds, expected->wMilliseconds);\n}\n\ntemplate <typename T>\nvoid ExpectMiscInfoEqual(const T* expected, const T* observed);\n\ntemplate <>\nvoid ExpectMiscInfoEqual<MINIDUMP_MISC_INFO>(\n    const MINIDUMP_MISC_INFO* expected,\n    const MINIDUMP_MISC_INFO* observed) {\n  EXPECT_EQ(observed->Flags1, expected->Flags1);\n  EXPECT_EQ(observed->ProcessId, expected->ProcessId);\n  EXPECT_EQ(observed->ProcessCreateTime, expected->ProcessCreateTime);\n  EXPECT_EQ(observed->ProcessUserTime, expected->ProcessUserTime);\n  EXPECT_EQ(observed->ProcessKernelTime, expected->ProcessKernelTime);\n}\n\ntemplate <>\nvoid ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_2>(\n    const MINIDUMP_MISC_INFO_2* expected,\n    const MINIDUMP_MISC_INFO_2* observed) {\n  ExpectMiscInfoEqual<MINIDUMP_MISC_INFO>(\n      reinterpret_cast<const MINIDUMP_MISC_INFO*>(expected),\n      reinterpret_cast<const MINIDUMP_MISC_INFO*>(observed));\n  EXPECT_EQ(observed->ProcessorMaxMhz, expected->ProcessorMaxMhz);\n  EXPECT_EQ(observed->ProcessorCurrentMhz, expected->ProcessorCurrentMhz);\n  EXPECT_EQ(observed->ProcessorMhzLimit, expected->ProcessorMhzLimit);\n  EXPECT_EQ(observed->ProcessorMaxIdleState, expected->ProcessorMaxIdleState);\n  EXPECT_EQ(observed->ProcessorCurrentIdleState,\n            expected->ProcessorCurrentIdleState);\n}\n\ntemplate <>\nvoid ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_3>(\n    const MINIDUMP_MISC_INFO_3* expected,\n    const MINIDUMP_MISC_INFO_3* observed) {\n  ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_2>(\n      reinterpret_cast<const MINIDUMP_MISC_INFO_2*>(expected),\n      reinterpret_cast<const MINIDUMP_MISC_INFO_2*>(observed));\n  EXPECT_EQ(observed->ProcessIntegrityLevel, expected->ProcessIntegrityLevel);\n  EXPECT_EQ(observed->ProcessExecuteFlags, expected->ProcessExecuteFlags);\n  EXPECT_EQ(observed->ProtectedProcess, expected->ProtectedProcess);\n  EXPECT_EQ(observed->TimeZoneId, expected->TimeZoneId);\n  EXPECT_EQ(observed->TimeZone.Bias, expected->TimeZone.Bias);\n  {\n    SCOPED_TRACE(\"Standard\");\n    ExpectNULPaddedString16Equal(AsU16CStr(expected->TimeZone.StandardName),\n                                 AsU16CStr(observed->TimeZone.StandardName),\n                                 std::size(expected->TimeZone.StandardName));\n    ExpectSystemTimeEqual(&expected->TimeZone.StandardDate,\n                          &observed->TimeZone.StandardDate);\n    EXPECT_EQ(observed->TimeZone.StandardBias, expected->TimeZone.StandardBias);\n  }\n  {\n    SCOPED_TRACE(\"Daylight\");\n    ExpectNULPaddedString16Equal(AsU16CStr(expected->TimeZone.DaylightName),\n                                 AsU16CStr(observed->TimeZone.DaylightName),\n                                 std::size(expected->TimeZone.DaylightName));\n    ExpectSystemTimeEqual(&expected->TimeZone.DaylightDate,\n                          &observed->TimeZone.DaylightDate);\n    EXPECT_EQ(observed->TimeZone.DaylightBias, expected->TimeZone.DaylightBias);\n  }\n}\n\ntemplate <>\nvoid ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_4>(\n    const MINIDUMP_MISC_INFO_4* expected,\n    const MINIDUMP_MISC_INFO_4* observed) {\n  ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_3>(\n      reinterpret_cast<const MINIDUMP_MISC_INFO_3*>(expected),\n      reinterpret_cast<const MINIDUMP_MISC_INFO_3*>(observed));\n  {\n    SCOPED_TRACE(\"BuildString\");\n    ExpectNULPaddedString16Equal(AsU16CStr(expected->BuildString),\n                                 AsU16CStr(observed->BuildString),\n                                 std::size(expected->BuildString));\n  }\n  {\n    SCOPED_TRACE(\"DbgBldStr\");\n    ExpectNULPaddedString16Equal(AsU16CStr(expected->DbgBldStr),\n                                 AsU16CStr(observed->DbgBldStr),\n                                 std::size(expected->DbgBldStr));\n  }\n}\n\ntemplate <>\nvoid ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_5>(\n    const MINIDUMP_MISC_INFO_5* expected,\n    const MINIDUMP_MISC_INFO_5* observed) {\n  ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_4>(\n      reinterpret_cast<const MINIDUMP_MISC_INFO_4*>(expected),\n      reinterpret_cast<const MINIDUMP_MISC_INFO_4*>(observed));\n\n  MINIDUMP_MISC_INFO_5 expected_misc_info, observed_misc_info;\n  memcpy(&expected_misc_info, expected, sizeof(expected_misc_info));\n  memcpy(&observed_misc_info, observed, sizeof(observed_misc_info));\n\n  EXPECT_EQ(observed_misc_info.XStateData.SizeOfInfo,\n            expected_misc_info.XStateData.SizeOfInfo);\n  EXPECT_EQ(observed_misc_info.XStateData.ContextSize,\n            expected_misc_info.XStateData.ContextSize);\n  // `EnabledFeatures` is underaligned and `EXPECT_EQ` internally takes\n  // arguments by reference. Copy it into a temporary before comparing to avoid\n  // undefined behavior.\n  EXPECT_EQ(uint64_t{observed_misc_info.XStateData.EnabledFeatures},\n            uint64_t{expected_misc_info.XStateData.EnabledFeatures});\n  for (size_t feature_index = 0;\n       feature_index < std::size(observed_misc_info.XStateData.Features);\n       ++feature_index) {\n    SCOPED_TRACE(base::StringPrintf(\"feature_index %\" PRIuS, feature_index));\n    EXPECT_EQ(observed_misc_info.XStateData.Features[feature_index].Offset,\n              expected_misc_info.XStateData.Features[feature_index].Offset);\n    EXPECT_EQ(observed_misc_info.XStateData.Features[feature_index].Size,\n              expected_misc_info.XStateData.Features[feature_index].Size);\n  }\n  EXPECT_EQ(observed_misc_info.ProcessCookie, expected_misc_info.ProcessCookie);\n}\n\n// Bypass restrictions on conversion of compile-time constants (added for\n// https://crbug.com/1189439).\nconst char* Crbug1189439Cast(const char str[]) {\n  return str;\n}\n\nTEST(MinidumpMiscInfoWriter, Empty) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO expected = {};\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, ProcessId) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kProcessId = 12345;\n\n  misc_info_writer->SetProcessID(kProcessId);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO expected = {};\n  expected.Flags1 = MINIDUMP_MISC1_PROCESS_ID;\n  expected.ProcessId = kProcessId;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, ProcessTimes) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr time_t kProcessCreateTime = 0x15252f00;\n  constexpr uint32_t kProcessUserTime = 10;\n  constexpr uint32_t kProcessKernelTime = 5;\n\n  misc_info_writer->SetProcessTimes(\n      kProcessCreateTime, kProcessUserTime, kProcessKernelTime);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO expected = {};\n  expected.Flags1 = MINIDUMP_MISC1_PROCESS_TIMES;\n  expected.ProcessCreateTime = kProcessCreateTime;\n  expected.ProcessUserTime = kProcessUserTime;\n  expected.ProcessKernelTime = kProcessKernelTime;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, ProcessorPowerInfo) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kProcessorMaxMhz = 2800;\n  constexpr uint32_t kProcessorCurrentMhz = 2300;\n  constexpr uint32_t kProcessorMhzLimit = 3300;\n  constexpr uint32_t kProcessorMaxIdleState = 5;\n  constexpr uint32_t kProcessorCurrentIdleState = 1;\n\n  misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz,\n                                          kProcessorCurrentMhz,\n                                          kProcessorMhzLimit,\n                                          kProcessorMaxIdleState,\n                                          kProcessorCurrentIdleState);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_2* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_2 expected = {};\n  expected.Flags1 = MINIDUMP_MISC1_PROCESSOR_POWER_INFO;\n  expected.ProcessorMaxMhz = kProcessorMaxMhz;\n  expected.ProcessorCurrentMhz = kProcessorCurrentMhz;\n  expected.ProcessorMhzLimit = kProcessorMhzLimit;\n  expected.ProcessorMaxIdleState = kProcessorMaxIdleState;\n  expected.ProcessorCurrentIdleState = kProcessorCurrentIdleState;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, ProcessIntegrityLevel) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kProcessIntegrityLevel = 0x2000;\n\n  misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_3* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_3 expected = {};\n  expected.Flags1 = MINIDUMP_MISC3_PROCESS_INTEGRITY;\n  expected.ProcessIntegrityLevel = kProcessIntegrityLevel;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, ProcessExecuteFlags) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kProcessExecuteFlags = 0x13579bdf;\n\n  misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_3* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_3 expected = {};\n  expected.Flags1 = MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS;\n  expected.ProcessExecuteFlags = kProcessExecuteFlags;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, ProtectedProcess) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kProtectedProcess = 1;\n\n  misc_info_writer->SetProtectedProcess(kProtectedProcess);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_3* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_3 expected = {};\n  expected.Flags1 = MINIDUMP_MISC3_PROTECTED_PROCESS;\n  expected.ProtectedProcess = kProtectedProcess;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, TimeZone) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kTimeZoneId = 2;\n  constexpr int32_t kBias = 300;\n  static constexpr char kStandardName[] = \"EST\";\n  constexpr SYSTEMTIME kStandardDate = {0, 11, 1, 0, 2, 0, 0, 0};\n  constexpr int32_t kStandardBias = 0;\n  static constexpr char kDaylightName[] = \"EDT\";\n  constexpr SYSTEMTIME kDaylightDate = {0, 3, 2, 0, 2, 0, 0, 0};\n  constexpr int32_t kDaylightBias = -60;\n\n  misc_info_writer->SetTimeZone(kTimeZoneId,\n                                kBias,\n                                kStandardName,\n                                kStandardDate,\n                                kStandardBias,\n                                kDaylightName,\n                                kDaylightDate,\n                                kDaylightBias);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_3* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_3 expected = {};\n  expected.Flags1 = MINIDUMP_MISC3_TIMEZONE;\n  expected.TimeZoneId = kTimeZoneId;\n  expected.TimeZone.Bias = kBias;\n  std::u16string standard_name_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kStandardName));\n  c16lcpy(AsU16CStr(expected.TimeZone.StandardName),\n          standard_name_utf16.c_str(),\n          std::size(expected.TimeZone.StandardName));\n  memcpy(&expected.TimeZone.StandardDate,\n         &kStandardDate,\n         sizeof(expected.TimeZone.StandardDate));\n  expected.TimeZone.StandardBias = kStandardBias;\n  std::u16string daylight_name_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kDaylightName));\n  c16lcpy(AsU16CStr(expected.TimeZone.DaylightName),\n          daylight_name_utf16.c_str(),\n          std::size(expected.TimeZone.DaylightName));\n  memcpy(&expected.TimeZone.DaylightDate,\n         &kDaylightDate,\n         sizeof(expected.TimeZone.DaylightDate));\n  expected.TimeZone.DaylightBias = kDaylightBias;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, TimeZoneStringsOverflow) {\n  // This test makes sure that the time zone name strings are truncated properly\n  // to the widths of their fields.\n\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kTimeZoneId = 2;\n  constexpr int32_t kBias = 300;\n  [[maybe_unused]] MINIDUMP_MISC_INFO_N tmp;\n  std::string standard_name(std::size(tmp.TimeZone.StandardName) + 1, 's');\n  constexpr int32_t kStandardBias = 0;\n  std::string daylight_name(std::size(tmp.TimeZone.DaylightName), 'd');\n  constexpr int32_t kDaylightBias = -60;\n\n  // Test using kSystemTimeZero, because not all platforms will be able to\n  // provide daylight saving time transition times.\n  constexpr SYSTEMTIME kSystemTimeZero = {};\n\n  misc_info_writer->SetTimeZone(kTimeZoneId,\n                                kBias,\n                                standard_name,\n                                kSystemTimeZero,\n                                kStandardBias,\n                                daylight_name,\n                                kSystemTimeZero,\n                                kDaylightBias);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_3* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_3 expected = {};\n  expected.Flags1 = MINIDUMP_MISC3_TIMEZONE;\n  expected.TimeZoneId = kTimeZoneId;\n  expected.TimeZone.Bias = kBias;\n  std::u16string standard_name_utf16 = base::UTF8ToUTF16(standard_name);\n  c16lcpy(AsU16CStr(expected.TimeZone.StandardName),\n          standard_name_utf16.c_str(),\n          std::size(expected.TimeZone.StandardName));\n  memcpy(&expected.TimeZone.StandardDate,\n         &kSystemTimeZero,\n         sizeof(expected.TimeZone.StandardDate));\n  expected.TimeZone.StandardBias = kStandardBias;\n  std::u16string daylight_name_utf16 = base::UTF8ToUTF16(daylight_name);\n  c16lcpy(AsU16CStr(expected.TimeZone.DaylightName),\n          daylight_name_utf16.c_str(),\n          std::size(expected.TimeZone.DaylightName));\n  memcpy(&expected.TimeZone.DaylightDate,\n         &kSystemTimeZero,\n         sizeof(expected.TimeZone.DaylightDate));\n  expected.TimeZone.DaylightBias = kDaylightBias;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, BuildStrings) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  static constexpr char kBuildString[] = \"build string\";\n  static constexpr char kDebugBuildString[] = \"debug build string\";\n\n  misc_info_writer->SetBuildString(kBuildString, kDebugBuildString);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_4* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_4 expected = {};\n  expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING;\n  std::u16string build_string_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kBuildString));\n  c16lcpy(AsU16CStr(expected.BuildString),\n          build_string_utf16.c_str(),\n          std::size(expected.BuildString));\n  std::u16string debug_build_string_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kDebugBuildString));\n  c16lcpy(AsU16CStr(expected.DbgBldStr),\n          debug_build_string_utf16.c_str(),\n          std::size(expected.DbgBldStr));\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, BuildStringsOverflow) {\n  // This test makes sure that the build strings are truncated properly to the\n  // widths of their fields.\n\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  [[maybe_unused]] MINIDUMP_MISC_INFO_N tmp;\n  std::string build_string(std::size(tmp.BuildString) + 1, 'B');\n  std::string debug_build_string(std::size(tmp.DbgBldStr), 'D');\n\n  misc_info_writer->SetBuildString(build_string, debug_build_string);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_4* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_4 expected = {};\n  expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING;\n  std::u16string build_string_utf16 = base::UTF8ToUTF16(build_string);\n  c16lcpy(AsU16CStr(expected.BuildString),\n          build_string_utf16.c_str(),\n          std::size(expected.BuildString));\n  std::u16string debug_build_string_utf16 =\n      base::UTF8ToUTF16(debug_build_string);\n  c16lcpy(AsU16CStr(expected.DbgBldStr),\n          debug_build_string_utf16.c_str(),\n          std::size(expected.DbgBldStr));\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, XStateData) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr XSTATE_CONFIG_FEATURE_MSC_INFO kXStateData = {\n      sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO),\n      1024,\n      0x000000000000005f,\n      {\n          {0, 512},\n          {512, 256},\n          {768, 128},\n          {896, 64},\n          {960, 32},\n          {0, 0},\n          {992, 32},\n      }};\n\n  misc_info_writer->SetXStateData(kXStateData);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_5* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_5 expected = {};\n  expected.XStateData = kXStateData;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, ProcessCookie) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kProcessCookie = 0x12345678;\n\n  misc_info_writer->SetProcessCookie(kProcessCookie);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_5* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_5 expected = {};\n  expected.Flags1 = MINIDUMP_MISC5_PROCESS_COOKIE;\n  expected.ProcessCookie = kProcessCookie;\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, Everything) {\n  MinidumpFileWriter minidump_file_writer;\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n\n  constexpr uint32_t kProcessId = 12345;\n  constexpr time_t kProcessCreateTime = 0x15252f00;\n  constexpr uint32_t kProcessUserTime = 10;\n  constexpr uint32_t kProcessKernelTime = 5;\n  constexpr uint32_t kProcessorMaxMhz = 2800;\n  constexpr uint32_t kProcessorCurrentMhz = 2300;\n  constexpr uint32_t kProcessorMhzLimit = 3300;\n  constexpr uint32_t kProcessorMaxIdleState = 5;\n  constexpr uint32_t kProcessorCurrentIdleState = 1;\n  constexpr uint32_t kProcessIntegrityLevel = 0x2000;\n  constexpr uint32_t kProcessExecuteFlags = 0x13579bdf;\n  constexpr uint32_t kProtectedProcess = 1;\n  constexpr uint32_t kTimeZoneId = 2;\n  constexpr int32_t kBias = 300;\n  static constexpr char kStandardName[] = \"EST\";\n  constexpr int32_t kStandardBias = 0;\n  static constexpr char kDaylightName[] = \"EDT\";\n  constexpr int32_t kDaylightBias = -60;\n  constexpr SYSTEMTIME kSystemTimeZero = {};\n  static constexpr char kBuildString[] = \"build string\";\n  static constexpr char kDebugBuildString[] = \"debug build string\";\n\n  misc_info_writer->SetProcessID(kProcessId);\n  misc_info_writer->SetProcessTimes(\n      kProcessCreateTime, kProcessUserTime, kProcessKernelTime);\n  misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz,\n                                          kProcessorCurrentMhz,\n                                          kProcessorMhzLimit,\n                                          kProcessorMaxIdleState,\n                                          kProcessorCurrentIdleState);\n  misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel);\n  misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags);\n  misc_info_writer->SetProtectedProcess(kProtectedProcess);\n  misc_info_writer->SetTimeZone(kTimeZoneId,\n                                kBias,\n                                kStandardName,\n                                kSystemTimeZero,\n                                kStandardBias,\n                                kDaylightName,\n                                kSystemTimeZero,\n                                kDaylightBias);\n  misc_info_writer->SetBuildString(kBuildString, kDebugBuildString);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_4* observed = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed));\n\n  MINIDUMP_MISC_INFO_4 expected = {};\n  expected.Flags1 =\n      MINIDUMP_MISC1_PROCESS_ID | MINIDUMP_MISC1_PROCESS_TIMES |\n      MINIDUMP_MISC1_PROCESSOR_POWER_INFO | MINIDUMP_MISC3_PROCESS_INTEGRITY |\n      MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS | MINIDUMP_MISC3_PROTECTED_PROCESS |\n      MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC4_BUILDSTRING;\n  expected.ProcessId = kProcessId;\n  expected.ProcessCreateTime = kProcessCreateTime;\n  expected.ProcessUserTime = kProcessUserTime;\n  expected.ProcessKernelTime = kProcessKernelTime;\n  expected.ProcessorMaxMhz = kProcessorMaxMhz;\n  expected.ProcessorCurrentMhz = kProcessorCurrentMhz;\n  expected.ProcessorMhzLimit = kProcessorMhzLimit;\n  expected.ProcessorMaxIdleState = kProcessorMaxIdleState;\n  expected.ProcessorCurrentIdleState = kProcessorCurrentIdleState;\n  expected.ProcessIntegrityLevel = kProcessIntegrityLevel;\n  expected.ProcessExecuteFlags = kProcessExecuteFlags;\n  expected.ProtectedProcess = kProtectedProcess;\n  expected.TimeZoneId = kTimeZoneId;\n  expected.TimeZone.Bias = kBias;\n  std::u16string standard_name_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kStandardName));\n  c16lcpy(AsU16CStr(expected.TimeZone.StandardName),\n          standard_name_utf16.c_str(),\n          std::size(expected.TimeZone.StandardName));\n  memcpy(&expected.TimeZone.StandardDate,\n         &kSystemTimeZero,\n         sizeof(expected.TimeZone.StandardDate));\n  expected.TimeZone.StandardBias = kStandardBias;\n  std::u16string daylight_name_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kDaylightName));\n  c16lcpy(AsU16CStr(expected.TimeZone.DaylightName),\n          daylight_name_utf16.c_str(),\n          std::size(expected.TimeZone.DaylightName));\n  memcpy(&expected.TimeZone.DaylightDate,\n         &kSystemTimeZero,\n         sizeof(expected.TimeZone.DaylightDate));\n  expected.TimeZone.DaylightBias = kDaylightBias;\n  std::u16string build_string_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kBuildString));\n  c16lcpy(AsU16CStr(expected.BuildString),\n          build_string_utf16.c_str(),\n          std::size(expected.BuildString));\n  std::u16string debug_build_string_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kDebugBuildString));\n  c16lcpy(AsU16CStr(expected.DbgBldStr),\n          debug_build_string_utf16.c_str(),\n          std::size(expected.DbgBldStr));\n\n  ExpectMiscInfoEqual(&expected, observed);\n}\n\nTEST(MinidumpMiscInfoWriter, InitializeFromSnapshot) {\n  MINIDUMP_MISC_INFO_4 expect_misc_info = {};\n\n  static constexpr char kStandardTimeName[] = \"EST\";\n  static constexpr char kDaylightTimeName[] = \"EDT\";\n  static constexpr char kOSVersionFull[] =\n      \"Mac OS X 10.9.5 (13F34); \"\n      \"Darwin 13.4.0 Darwin Kernel Version 13.4.0: \"\n      \"Sun Aug 17 19:50:11 PDT 2014; \"\n      \"root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64\";\n  static constexpr char kMachineDescription[] =\n      \"MacBookPro11,3 (Mac-2BD1B31983FE1663)\";\n  std::u16string standard_time_name_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kStandardTimeName));\n  std::u16string daylight_time_name_utf16 =\n      base::UTF8ToUTF16(Crbug1189439Cast(kDaylightTimeName));\n  std::u16string build_string_utf16 = base::UTF8ToUTF16(\n      std::string(kOSVersionFull) + \"; \" + kMachineDescription);\n  std::string debug_build_string = internal::MinidumpMiscInfoDebugBuildString();\n  EXPECT_FALSE(debug_build_string.empty());\n  std::u16string debug_build_string_utf16 =\n      base::UTF8ToUTF16(debug_build_string);\n\n  expect_misc_info.SizeOfInfo = sizeof(expect_misc_info);\n  expect_misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_ID |\n                            MINIDUMP_MISC1_PROCESS_TIMES |\n                            MINIDUMP_MISC1_PROCESSOR_POWER_INFO |\n                            MINIDUMP_MISC3_TIMEZONE |\n                            MINIDUMP_MISC4_BUILDSTRING;\n  expect_misc_info.ProcessId = 12345;\n  expect_misc_info.ProcessCreateTime = 0x555c7740;\n  expect_misc_info.ProcessUserTime = 60;\n  expect_misc_info.ProcessKernelTime = 15;\n  expect_misc_info.ProcessorCurrentMhz = 2800;\n  expect_misc_info.ProcessorMaxMhz = 2800;\n  expect_misc_info.TimeZoneId = 1;\n  expect_misc_info.TimeZone.Bias = 300;\n  c16lcpy(AsU16CStr(expect_misc_info.TimeZone.StandardName),\n          standard_time_name_utf16.c_str(),\n          std::size(expect_misc_info.TimeZone.StandardName));\n  expect_misc_info.TimeZone.StandardBias = 0;\n  c16lcpy(AsU16CStr(expect_misc_info.TimeZone.DaylightName),\n          daylight_time_name_utf16.c_str(),\n          std::size(expect_misc_info.TimeZone.DaylightName));\n  expect_misc_info.TimeZone.DaylightBias = -60;\n  c16lcpy(AsU16CStr(expect_misc_info.BuildString),\n          build_string_utf16.c_str(),\n          std::size(expect_misc_info.BuildString));\n  c16lcpy(AsU16CStr(expect_misc_info.DbgBldStr),\n          debug_build_string_utf16.c_str(),\n          std::size(expect_misc_info.DbgBldStr));\n\n  const timeval kStartTime = {\n      static_cast<long>(expect_misc_info.ProcessCreateTime), 0};\n  const timeval kUserCPUTime = {\n      static_cast<long>(expect_misc_info.ProcessUserTime), 0};\n  const timeval kSystemCPUTime = {\n      static_cast<long>(expect_misc_info.ProcessKernelTime), 0};\n\n  TestProcessSnapshot process_snapshot;\n  process_snapshot.SetProcessID(expect_misc_info.ProcessId);\n  process_snapshot.SetProcessStartTime(kStartTime);\n  process_snapshot.SetProcessCPUTimes(kUserCPUTime, kSystemCPUTime);\n\n  auto system_snapshot = std::make_unique<TestSystemSnapshot>();\n  constexpr uint64_t kHzPerMHz = static_cast<uint64_t>(1E6);\n  system_snapshot->SetCPUFrequency(\n      expect_misc_info.ProcessorCurrentMhz * kHzPerMHz,\n      expect_misc_info.ProcessorMaxMhz * kHzPerMHz);\n  system_snapshot->SetTimeZone(SystemSnapshot::kObservingStandardTime,\n                               expect_misc_info.TimeZone.Bias * -60,\n                               (expect_misc_info.TimeZone.Bias +\n                                expect_misc_info.TimeZone.DaylightBias) * -60,\n                               kStandardTimeName,\n                               kDaylightTimeName);\n  system_snapshot->SetOSVersionFull(kOSVersionFull);\n  system_snapshot->SetMachineDescription(kMachineDescription);\n\n  process_snapshot.SetSystem(std::move(system_snapshot));\n\n  auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();\n  misc_info_writer->InitializeFromSnapshot(&process_snapshot);\n\n  MinidumpFileWriter minidump_file_writer;\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MISC_INFO_4* misc_info = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &misc_info));\n\n  ExpectMiscInfoEqual(&expect_misc_info, misc_info);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_module_crashpad_info_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_module_crashpad_info_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"minidump/minidump_annotation_writer.h\"\n#include \"minidump/minidump_simple_string_dictionary_writer.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpModuleCrashpadInfoWriter::MinidumpModuleCrashpadInfoWriter()\n    : MinidumpWritable(),\n      module_(),\n      list_annotations_(),\n      simple_annotations_(),\n      annotation_objects_() {\n  module_.version = MinidumpModuleCrashpadInfo::kVersion;\n}\n\nMinidumpModuleCrashpadInfoWriter::~MinidumpModuleCrashpadInfoWriter() {\n}\n\nvoid MinidumpModuleCrashpadInfoWriter::InitializeFromSnapshot(\n    const ModuleSnapshot* module_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!list_annotations_);\n  DCHECK(!simple_annotations_);\n\n  auto list_annotations = std::make_unique<MinidumpUTF8StringListWriter>();\n  list_annotations->InitializeFromVector(module_snapshot->AnnotationsVector());\n  if (list_annotations->IsUseful()) {\n    SetListAnnotations(std::move(list_annotations));\n  }\n\n  auto simple_annotations =\n      std::make_unique<MinidumpSimpleStringDictionaryWriter>();\n  simple_annotations->InitializeFromMap(\n      module_snapshot->AnnotationsSimpleMap());\n  if (simple_annotations->IsUseful()) {\n    SetSimpleAnnotations(std::move(simple_annotations));\n  }\n\n  auto annotation_objects = std::make_unique<MinidumpAnnotationListWriter>();\n  annotation_objects->InitializeFromList(module_snapshot->AnnotationObjects());\n  if (annotation_objects->IsUseful()) {\n    SetAnnotationObjects(std::move(annotation_objects));\n  }\n}\n\nvoid MinidumpModuleCrashpadInfoWriter::SetListAnnotations(\n    std::unique_ptr<MinidumpUTF8StringListWriter> list_annotations) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  list_annotations_ = std::move(list_annotations);\n}\n\nvoid MinidumpModuleCrashpadInfoWriter::SetSimpleAnnotations(\n    std::unique_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  simple_annotations_ = std::move(simple_annotations);\n}\n\nvoid MinidumpModuleCrashpadInfoWriter::SetAnnotationObjects(\n    std::unique_ptr<MinidumpAnnotationListWriter> annotation_objects) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  annotation_objects_ = std::move(annotation_objects);\n}\n\nbool MinidumpModuleCrashpadInfoWriter::IsUseful() const {\n  return list_annotations_ || simple_annotations_ || annotation_objects_;\n}\n\nbool MinidumpModuleCrashpadInfoWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  if (list_annotations_) {\n    list_annotations_->RegisterLocationDescriptor(&module_.list_annotations);\n  }\n\n  if (simple_annotations_) {\n    simple_annotations_->RegisterLocationDescriptor(\n        &module_.simple_annotations);\n  }\n\n  if (annotation_objects_) {\n    annotation_objects_->RegisterLocationDescriptor(\n        &module_.annotation_objects);\n  }\n\n  return true;\n}\n\nsize_t MinidumpModuleCrashpadInfoWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(module_);\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpModuleCrashpadInfoWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children;\n  if (list_annotations_) {\n    children.push_back(list_annotations_.get());\n  }\n  if (simple_annotations_) {\n    children.push_back(simple_annotations_.get());\n  }\n  if (annotation_objects_) {\n    children.push_back(annotation_objects_.get());\n  }\n\n  return children;\n}\n\nbool MinidumpModuleCrashpadInfoWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return file_writer->Write(&module_, sizeof(module_));\n}\n\nMinidumpModuleCrashpadInfoListWriter::MinidumpModuleCrashpadInfoListWriter()\n    : MinidumpWritable(),\n      module_crashpad_infos_(),\n      module_crashpad_info_links_(),\n      module_crashpad_info_list_base_() {\n}\n\nMinidumpModuleCrashpadInfoListWriter::~MinidumpModuleCrashpadInfoListWriter() {\n}\n\nvoid MinidumpModuleCrashpadInfoListWriter::InitializeFromSnapshot(\n    const std::vector<const ModuleSnapshot*>& module_snapshots) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(module_crashpad_infos_.empty());\n  DCHECK(module_crashpad_info_links_.empty());\n\n  size_t count = module_snapshots.size();\n  for (size_t index = 0; index < count; ++index) {\n    const ModuleSnapshot* module_snapshot = module_snapshots[index];\n\n    auto module = std::make_unique<MinidumpModuleCrashpadInfoWriter>();\n    module->InitializeFromSnapshot(module_snapshot);\n    if (module->IsUseful()) {\n      AddModule(std::move(module), index);\n    }\n  }\n}\n\nvoid MinidumpModuleCrashpadInfoListWriter::AddModule(\n    std::unique_ptr<MinidumpModuleCrashpadInfoWriter> module_crashpad_info,\n    size_t minidump_module_list_index) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());\n\n  MinidumpModuleCrashpadInfoLink module_crashpad_info_link = {};\n  if (!AssignIfInRange(&module_crashpad_info_link.minidump_module_list_index,\n                       minidump_module_list_index)) {\n    LOG(ERROR) << \"minidump_module_list_index \" << minidump_module_list_index\n               << \" out of range\";\n    return;\n  }\n\n  module_crashpad_info_links_.push_back(module_crashpad_info_link);\n  module_crashpad_infos_.push_back(std::move(module_crashpad_info));\n}\n\nbool MinidumpModuleCrashpadInfoListWriter::IsUseful() const {\n  DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());\n  return !module_crashpad_infos_.empty();\n}\n\nbool MinidumpModuleCrashpadInfoListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n  CHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  size_t module_count = module_crashpad_infos_.size();\n  if (!AssignIfInRange(&module_crashpad_info_list_base_.count, module_count)) {\n    LOG(ERROR) << \"module_count \" << module_count << \" out of range\";\n    return false;\n  }\n\n  for (size_t index = 0; index < module_count; ++index) {\n    module_crashpad_infos_[index]->RegisterLocationDescriptor(\n        &module_crashpad_info_links_[index].location);\n  }\n\n  return true;\n}\n\nsize_t MinidumpModuleCrashpadInfoListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());\n\n  return sizeof(module_crashpad_info_list_base_) +\n         module_crashpad_info_links_.size() *\n             sizeof(module_crashpad_info_links_[0]);\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpModuleCrashpadInfoListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& module : module_crashpad_infos_) {\n    children.push_back(module.get());\n  }\n\n  return children;\n}\n\nbool MinidumpModuleCrashpadInfoListWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size());\n\n  WritableIoVec iov;\n  iov.iov_base = &module_crashpad_info_list_base_;\n  iov.iov_len = sizeof(module_crashpad_info_list_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  if (!module_crashpad_info_links_.empty()) {\n    iov.iov_base = &module_crashpad_info_links_[0];\n    iov.iov_len = module_crashpad_info_links_.size() *\n                  sizeof(module_crashpad_info_links_[0]);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_module_crashpad_info_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_string_writer.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\nclass MinidumpAnnotationListWriter;\nclass MinidumpSimpleStringDictionaryWriter;\nclass ModuleSnapshot;\n\n//! \\brief The writer for a MinidumpModuleCrashpadInfo object in a minidump\n//!     file.\nclass MinidumpModuleCrashpadInfoWriter final\n    : public internal::MinidumpWritable {\n public:\n  MinidumpModuleCrashpadInfoWriter();\n\n  MinidumpModuleCrashpadInfoWriter(const MinidumpModuleCrashpadInfoWriter&) =\n      delete;\n  MinidumpModuleCrashpadInfoWriter& operator=(\n      const MinidumpModuleCrashpadInfoWriter&) = delete;\n\n  ~MinidumpModuleCrashpadInfoWriter() override;\n\n  //! \\brief Initializes MinidumpModuleCrashpadInfo based on \\a module_snapshot.\n  //!\n  //! Only data in \\a module_snapshot that is considered useful will be\n  //! included. For simple annotations, usefulness is determined by\n  //! MinidumpSimpleStringDictionaryWriter::IsUseful().\n  //!\n  //! \\param[in] module_snapshot The module snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot);\n\n  //! \\brief Arranges for MinidumpModuleCrashpadInfo::list_annotations to point\n  //!     to the internal::MinidumpUTF8StringListWriter object to be written by\n  //!     \\a list_annotations.\n  //!\n  //! This object takes ownership of \\a simple_annotations and becomes its\n  //! parent in the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetListAnnotations(\n      std::unique_ptr<MinidumpUTF8StringListWriter> list_annotations);\n\n  //! \\brief Arranges for MinidumpModuleCrashpadInfo::simple_annotations to\n  //!     point to the MinidumpSimpleStringDictionaryWriter object to be written\n  //!     by \\a simple_annotations.\n  //!\n  //! This object takes ownership of \\a simple_annotations and becomes its\n  //! parent in the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetSimpleAnnotations(\n      std::unique_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations);\n\n  //! \\brief Arranges for MinidumpModuleCrashpadInfo::annotation_objects to\n  //!     point to the MinidumpAnnotationListWriter object to be written by\n  //!     \\a annotation_objects.\n  //!\n  //! This object takes ownership of \\a annotation_objects and becomes its\n  //! parent in the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetAnnotationObjects(\n      std::unique_ptr<MinidumpAnnotationListWriter> annotation_objects);\n\n  //! \\brief Determines whether the object is useful.\n  //!\n  //! A useful object is one that carries data that makes a meaningful\n  //! contribution to a minidump file. An object carrying list annotations or\n  //! simple annotations would be considered useful.\n  //!\n  //! \\return `true` if the object is useful, `false` otherwise.\n  bool IsUseful() const;\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  MinidumpModuleCrashpadInfo module_;\n  std::unique_ptr<MinidumpUTF8StringListWriter> list_annotations_;\n  std::unique_ptr<MinidumpSimpleStringDictionaryWriter> simple_annotations_;\n  std::unique_ptr<MinidumpAnnotationListWriter> annotation_objects_;\n};\n\n//! \\brief The writer for a MinidumpModuleCrashpadInfoList object in a minidump\n//!     file, containing a list of MinidumpModuleCrashpadInfo objects.\nclass MinidumpModuleCrashpadInfoListWriter final\n    : public internal::MinidumpWritable {\n public:\n  MinidumpModuleCrashpadInfoListWriter();\n\n  MinidumpModuleCrashpadInfoListWriter(\n      const MinidumpModuleCrashpadInfoListWriter&) = delete;\n  MinidumpModuleCrashpadInfoListWriter& operator=(\n      const MinidumpModuleCrashpadInfoListWriter&) = delete;\n\n  ~MinidumpModuleCrashpadInfoListWriter() override;\n\n  //! \\brief Adds an initialized MinidumpModuleCrashpadInfo for modules in \\a\n  //!     module_snapshots to the MinidumpModuleCrashpadInfoList.\n  //!\n  //! Only modules in \\a module_snapshots that would produce a useful\n  //! MinidumpModuleCrashpadInfo structure are included. Usefulness is\n  //! determined by MinidumpModuleCrashpadInfoWriter::IsUseful().\n  //!\n  //! \\param[in] module_snapshots The module snapshots to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. AddModule() may not be called before this\n  //!     method, and it is not normally necessary to call AddModule() after\n  //!     this method.\n  void InitializeFromSnapshot(\n      const std::vector<const ModuleSnapshot*>& module_snapshots);\n\n  //! \\brief Adds a MinidumpModuleCrashpadInfo to the\n  //!     MinidumpModuleCrashpadInfoList.\n  //!\n  //! \\param[in] module_crashpad_info Extended Crashpad-specific information\n  //!     about the module. This object takes ownership of \\a\n  //!     module_crashpad_info and becomes its parent in the overall tree of\n  //!     internal::MinidumpWritable objects.\n  //! \\param[in] minidump_module_list_index The index of the MINIDUMP_MODULE in\n  //!     the minidump file’s MINIDUMP_MODULE_LIST stream that corresponds to \\a\n  //!     module_crashpad_info.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddModule(\n      std::unique_ptr<MinidumpModuleCrashpadInfoWriter> module_crashpad_info,\n      size_t minidump_module_list_index);\n\n  //! \\brief Determines whether the object is useful.\n  //!\n  //! A useful object is one that carries data that makes a meaningful\n  //! contribution to a minidump file. An object carrying children would be\n  //! considered useful.\n  //!\n  //! \\return `true` if the object is useful, `false` otherwise.\n  bool IsUseful() const;\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  std::vector<std::unique_ptr<MinidumpModuleCrashpadInfoWriter>>\n      module_crashpad_infos_;\n  std::vector<MinidumpModuleCrashpadInfoLink> module_crashpad_info_links_;\n  MinidumpModuleCrashpadInfoList module_crashpad_info_list_base_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_module_crashpad_info_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_module_crashpad_info_writer.h\"\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include <iterator>\n#include <utility>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_annotation_writer.h\"\n#include \"minidump/minidump_simple_string_dictionary_writer.h\"\n#include \"minidump/test/minidump_byte_array_writer_test_util.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_module_snapshot.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconst MinidumpModuleCrashpadInfoList* MinidumpModuleCrashpadInfoListAtStart(\n    const std::string& file_contents,\n    size_t count) {\n  MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;\n  location_descriptor.DataSize =\n      static_cast<uint32_t>(sizeof(MinidumpModuleCrashpadInfoList) +\n                            count * sizeof(MinidumpModuleCrashpadInfoLink));\n  location_descriptor.Rva = 0;\n\n  const MinidumpModuleCrashpadInfoList* list =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>(\n          file_contents, location_descriptor);\n  if (!list) {\n    return nullptr;\n  }\n\n  if (list->count != count) {\n    EXPECT_EQ(list->count, count);\n    return nullptr;\n  }\n\n  return list;\n}\n\nTEST(MinidumpModuleCrashpadInfoWriter, EmptyList) {\n  StringFile string_file;\n\n  auto module_list_writer =\n      std::make_unique<MinidumpModuleCrashpadInfoListWriter>();\n  EXPECT_FALSE(module_list_writer->IsUseful());\n\n  EXPECT_TRUE(module_list_writer->WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpModuleCrashpadInfoList));\n\n  const MinidumpModuleCrashpadInfoList* module_list =\n      MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 0);\n  ASSERT_TRUE(module_list);\n}\n\nTEST(MinidumpModuleCrashpadInfoWriter, EmptyModule) {\n  StringFile string_file;\n\n  auto module_list_writer =\n      std::make_unique<MinidumpModuleCrashpadInfoListWriter>();\n  auto module_writer = std::make_unique<MinidumpModuleCrashpadInfoWriter>();\n  EXPECT_FALSE(module_writer->IsUseful());\n  module_list_writer->AddModule(std::move(module_writer), 0);\n\n  EXPECT_TRUE(module_list_writer->IsUseful());\n\n  EXPECT_TRUE(module_list_writer->WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpModuleCrashpadInfoList) +\n                sizeof(MinidumpModuleCrashpadInfoLink) +\n                sizeof(MinidumpModuleCrashpadInfo));\n\n  const MinidumpModuleCrashpadInfoList* module_list =\n      MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1);\n  ASSERT_TRUE(module_list);\n\n  EXPECT_EQ(module_list->modules[0].minidump_module_list_index, 0u);\n  const MinidumpModuleCrashpadInfo* module =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[0].location);\n  ASSERT_TRUE(module);\n\n  EXPECT_EQ(module->version, MinidumpModuleCrashpadInfo::kVersion);\n  EXPECT_EQ(module->list_annotations.DataSize, 0u);\n  EXPECT_EQ(module->list_annotations.Rva, 0u);\n  EXPECT_EQ(module->simple_annotations.DataSize, 0u);\n  EXPECT_EQ(module->simple_annotations.Rva, 0u);\n  EXPECT_EQ(module->annotation_objects.DataSize, 0u);\n  EXPECT_EQ(module->annotation_objects.Rva, 0u);\n}\n\nTEST(MinidumpModuleCrashpadInfoWriter, FullModule) {\n  constexpr uint32_t kMinidumpModuleListIndex = 1;\n  static constexpr char kKey[] = \"key\";\n  static constexpr char kValue[] = \"value\";\n  static constexpr char kEntry[] = \"entry\";\n  std::vector<std::string> vector(1, std::string(kEntry));\n  const AnnotationSnapshot annotation(\"one\", 42, {'t', 'e', 's', 't'});\n\n  StringFile string_file;\n\n  auto module_list_writer =\n      std::make_unique<MinidumpModuleCrashpadInfoListWriter>();\n\n  auto module_writer = std::make_unique<MinidumpModuleCrashpadInfoWriter>();\n  auto string_list_writer = std::make_unique<MinidumpUTF8StringListWriter>();\n  string_list_writer->InitializeFromVector(vector);\n  module_writer->SetListAnnotations(std::move(string_list_writer));\n  auto simple_string_dictionary_writer =\n      std::make_unique<MinidumpSimpleStringDictionaryWriter>();\n  auto simple_string_dictionary_entry_writer =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  simple_string_dictionary_entry_writer->SetKeyValue(kKey, kValue);\n  simple_string_dictionary_writer->AddEntry(\n      std::move(simple_string_dictionary_entry_writer));\n  module_writer->SetSimpleAnnotations(\n      std::move(simple_string_dictionary_writer));\n  auto annotation_list_writer =\n      std::make_unique<MinidumpAnnotationListWriter>();\n  annotation_list_writer->InitializeFromList({annotation});\n  module_writer->SetAnnotationObjects(std::move(annotation_list_writer));\n  EXPECT_TRUE(module_writer->IsUseful());\n  module_list_writer->AddModule(std::move(module_writer),\n                                kMinidumpModuleListIndex);\n\n  EXPECT_TRUE(module_list_writer->IsUseful());\n\n  EXPECT_TRUE(module_list_writer->WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpModuleCrashpadInfoList) +\n                sizeof(MinidumpModuleCrashpadInfoLink) +\n                sizeof(MinidumpModuleCrashpadInfo) + sizeof(MinidumpRVAList) +\n                sizeof(RVA) + sizeof(MinidumpSimpleStringDictionary) +\n                sizeof(MinidumpSimpleStringDictionaryEntry) +\n                sizeof(MinidumpAnnotationList) + 2 +  // padding\n                sizeof(MinidumpAnnotation) + sizeof(MinidumpUTF8String) +\n                std::size(kEntry) + 2 +  // padding\n                sizeof(MinidumpUTF8String) + std::size(kKey) +\n                sizeof(MinidumpUTF8String) + std::size(kValue) +\n                sizeof(MinidumpUTF8String) + annotation.name.size() + 1 +\n                sizeof(MinidumpByteArray) + annotation.value.size());\n\n  const MinidumpModuleCrashpadInfoList* module_list =\n      MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1);\n  ASSERT_TRUE(module_list);\n\n  EXPECT_EQ(module_list->modules[0].minidump_module_list_index,\n            kMinidumpModuleListIndex);\n  const MinidumpModuleCrashpadInfo* module =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[0].location);\n  ASSERT_TRUE(module);\n\n  EXPECT_EQ(module->version, MinidumpModuleCrashpadInfo::kVersion);\n  EXPECT_NE(module->list_annotations.DataSize, 0u);\n  EXPECT_NE(module->list_annotations.Rva, 0u);\n  EXPECT_NE(module->simple_annotations.DataSize, 0u);\n  EXPECT_NE(module->simple_annotations.Rva, 0u);\n  EXPECT_NE(module->annotation_objects.DataSize, 0u);\n  EXPECT_NE(module->annotation_objects.Rva, 0u);\n\n  const MinidumpRVAList* list_annotations =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          string_file.string(), module->list_annotations);\n  ASSERT_TRUE(list_annotations);\n\n  ASSERT_EQ(list_annotations->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            list_annotations->children[0]),\n            kEntry);\n\n  const MinidumpSimpleStringDictionary* simple_annotations =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module->simple_annotations);\n  ASSERT_TRUE(simple_annotations);\n\n  ASSERT_EQ(simple_annotations->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            simple_annotations->entries[0].key),\n            kKey);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations->entries[0].value),\n            kValue);\n\n  const MinidumpAnnotationList* annotation_objects =\n      MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n          string_file.string(), module->annotation_objects);\n  ASSERT_TRUE(annotation_objects);\n\n  ASSERT_EQ(annotation_objects->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), annotation_objects->objects[0].name),\n            annotation.name);\n  EXPECT_EQ(annotation_objects->objects[0].type, 42u);\n  EXPECT_EQ(annotation_objects->objects[0].reserved, 0u);\n  EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(),\n                                   annotation_objects->objects[0].value),\n            annotation.value);\n}\n\nTEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) {\n  constexpr uint32_t kMinidumpModuleListIndex0 = 0;\n  static constexpr char kKey0[] = \"key\";\n  static constexpr char kValue0[] = \"value\";\n  const AnnotationSnapshot annotation0(\"name\", 0x8FFF, {'t', '\\0', 't'});\n  constexpr uint32_t kMinidumpModuleListIndex1 = 2;\n  constexpr uint32_t kMinidumpModuleListIndex2 = 5;\n  static constexpr char kKey2A[] = \"K\";\n  static constexpr char kValue2A[] = \"VVV\";\n  static constexpr char kKey2B[] = \"river\";\n  static constexpr char kValue2B[] = \"hudson\";\n  const AnnotationSnapshot annotation2a(\"A2\", 0xDDDD, {2, 4, 6, 8});\n  const AnnotationSnapshot annotation2b(\"A3\", 0xDDDF, {'m', 'o', 'o'});\n\n  StringFile string_file;\n\n  auto module_list_writer =\n      std::make_unique<MinidumpModuleCrashpadInfoListWriter>();\n\n  auto module_writer_0 = std::make_unique<MinidumpModuleCrashpadInfoWriter>();\n  auto simple_string_dictionary_writer_0 =\n      std::make_unique<MinidumpSimpleStringDictionaryWriter>();\n  auto simple_string_dictionary_entry_writer_0 =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  simple_string_dictionary_entry_writer_0->SetKeyValue(kKey0, kValue0);\n  simple_string_dictionary_writer_0->AddEntry(\n      std::move(simple_string_dictionary_entry_writer_0));\n  module_writer_0->SetSimpleAnnotations(\n      std::move(simple_string_dictionary_writer_0));\n  auto annotation_list_writer_0 =\n      std::make_unique<MinidumpAnnotationListWriter>();\n  auto annotation_writer_0 = std::make_unique<MinidumpAnnotationWriter>();\n  annotation_writer_0->InitializeFromSnapshot(annotation0);\n  annotation_list_writer_0->AddObject(std::move(annotation_writer_0));\n  module_writer_0->SetAnnotationObjects(std::move(annotation_list_writer_0));\n  EXPECT_TRUE(module_writer_0->IsUseful());\n  module_list_writer->AddModule(std::move(module_writer_0),\n                                kMinidumpModuleListIndex0);\n\n  auto module_writer_1 = std::make_unique<MinidumpModuleCrashpadInfoWriter>();\n  EXPECT_FALSE(module_writer_1->IsUseful());\n  module_list_writer->AddModule(std::move(module_writer_1),\n                                kMinidumpModuleListIndex1);\n\n  auto module_writer_2 = std::make_unique<MinidumpModuleCrashpadInfoWriter>();\n  auto simple_string_dictionary_writer_2 =\n      std::make_unique<MinidumpSimpleStringDictionaryWriter>();\n  auto simple_string_dictionary_entry_writer_2a =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  simple_string_dictionary_entry_writer_2a->SetKeyValue(kKey2A, kValue2A);\n  simple_string_dictionary_writer_2->AddEntry(\n      std::move(simple_string_dictionary_entry_writer_2a));\n  auto simple_string_dictionary_entry_writer_2b =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  simple_string_dictionary_entry_writer_2b->SetKeyValue(kKey2B, kValue2B);\n  simple_string_dictionary_writer_2->AddEntry(\n      std::move(simple_string_dictionary_entry_writer_2b));\n  module_writer_2->SetSimpleAnnotations(\n      std::move(simple_string_dictionary_writer_2));\n  auto annotation_list_writer_2 =\n      std::make_unique<MinidumpAnnotationListWriter>();\n  auto annotation_writer_2a = std::make_unique<MinidumpAnnotationWriter>();\n  annotation_writer_2a->InitializeFromSnapshot(annotation2a);\n  auto annotation_writer_2b = std::make_unique<MinidumpAnnotationWriter>();\n  annotation_writer_2b->InitializeFromSnapshot(annotation2b);\n  annotation_list_writer_2->AddObject(std::move(annotation_writer_2a));\n  annotation_list_writer_2->AddObject(std::move(annotation_writer_2b));\n  module_writer_2->SetAnnotationObjects(std::move(annotation_list_writer_2));\n  EXPECT_TRUE(module_writer_2->IsUseful());\n  module_list_writer->AddModule(std::move(module_writer_2),\n                                kMinidumpModuleListIndex2);\n\n  EXPECT_TRUE(module_list_writer->IsUseful());\n\n  EXPECT_TRUE(module_list_writer->WriteEverything(&string_file));\n\n  const MinidumpModuleCrashpadInfoList* module_list =\n      MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 3);\n  ASSERT_TRUE(module_list);\n\n  EXPECT_EQ(module_list->modules[0].minidump_module_list_index,\n            kMinidumpModuleListIndex0);\n  const MinidumpModuleCrashpadInfo* module_0 =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[0].location);\n  ASSERT_TRUE(module_0);\n\n  EXPECT_EQ(module_0->version, MinidumpModuleCrashpadInfo::kVersion);\n\n  const MinidumpRVAList* list_annotations_0 =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          string_file.string(), module_0->list_annotations);\n  EXPECT_FALSE(list_annotations_0);\n\n  const MinidumpSimpleStringDictionary* simple_annotations_0 =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module_0->simple_annotations);\n  ASSERT_TRUE(simple_annotations_0);\n\n  ASSERT_EQ(simple_annotations_0->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_0->entries[0].key),\n            kKey0);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_0->entries[0].value),\n            kValue0);\n\n  const MinidumpAnnotationList* annotation_list_0 =\n      MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n          string_file.string(), module_0->annotation_objects);\n  ASSERT_TRUE(annotation_list_0);\n\n  ASSERT_EQ(annotation_list_0->count, 1u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            annotation_list_0->objects[0].name),\n            annotation0.name);\n  EXPECT_EQ(annotation_list_0->objects[0].type, annotation0.type);\n  EXPECT_EQ(annotation_list_0->objects[0].reserved, 0u);\n  EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(),\n                                   annotation_list_0->objects[0].value),\n            annotation0.value);\n\n  EXPECT_EQ(module_list->modules[1].minidump_module_list_index,\n            kMinidumpModuleListIndex1);\n  const MinidumpModuleCrashpadInfo* module_1 =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[1].location);\n  ASSERT_TRUE(module_1);\n\n  EXPECT_EQ(module_1->version, MinidumpModuleCrashpadInfo::kVersion);\n\n  const MinidumpRVAList* list_annotations_1 =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          string_file.string(), module_1->list_annotations);\n  EXPECT_FALSE(list_annotations_1);\n\n  const MinidumpSimpleStringDictionary* simple_annotations_1 =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module_1->simple_annotations);\n  EXPECT_FALSE(simple_annotations_1);\n\n  const MinidumpAnnotationList* annotation_list_1 =\n      MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n          string_file.string(), module_1->annotation_objects);\n  EXPECT_FALSE(annotation_list_1);\n\n  EXPECT_EQ(module_list->modules[2].minidump_module_list_index,\n            kMinidumpModuleListIndex2);\n  const MinidumpModuleCrashpadInfo* module_2 =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[2].location);\n  ASSERT_TRUE(module_2);\n\n  EXPECT_EQ(module_2->version, MinidumpModuleCrashpadInfo::kVersion);\n\n  const MinidumpRVAList* list_annotations_2 =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          string_file.string(), module_2->list_annotations);\n  EXPECT_FALSE(list_annotations_2);\n\n  const MinidumpSimpleStringDictionary* simple_annotations_2 =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module_2->simple_annotations);\n  ASSERT_TRUE(simple_annotations_2);\n\n  ASSERT_EQ(simple_annotations_2->count, 2u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_2->entries[0].key),\n            kKey2A);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_2->entries[0].value),\n            kValue2A);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_2->entries[1].key),\n            kKey2B);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_2->entries[1].value),\n            kValue2B);\n\n  const MinidumpAnnotationList* annotation_list_2 =\n      MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n          string_file.string(), module_2->annotation_objects);\n  ASSERT_TRUE(annotation_list_2);\n\n  ASSERT_EQ(annotation_list_2->count, 2u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            annotation_list_2->objects[0].name),\n            annotation2a.name);\n  EXPECT_EQ(annotation_list_2->objects[0].type, annotation2a.type);\n  EXPECT_EQ(annotation_list_2->objects[0].reserved, 0u);\n  EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(),\n                                   annotation_list_2->objects[0].value),\n            annotation2a.value);\n\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            annotation_list_2->objects[1].name),\n            annotation2b.name);\n  EXPECT_EQ(annotation_list_2->objects[1].type, annotation2b.type);\n  EXPECT_EQ(annotation_list_2->objects[1].reserved, 0u);\n  EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(),\n                                   annotation_list_2->objects[1].value),\n            annotation2b.value);\n}\n\nTEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) {\n  static constexpr char kKey0A[] = \"k\";\n  static constexpr char kValue0A[] = \"value\";\n  static constexpr char kKey0B[] = \"hudson\";\n  static constexpr char kValue0B[] = \"estuary\";\n  static constexpr char kKey2[] = \"k\";\n  static constexpr char kValue2[] = \"different_value\";\n  static constexpr char kEntry3A[] = \"list\";\n  static constexpr char kEntry3B[] = \"erine\";\n  const AnnotationSnapshot annotation(\n      \"market\", 1, {'2', '3', 'r', 'd', ' ', 'S', 't', '.'});\n\n  std::vector<const ModuleSnapshot*> module_snapshots;\n\n  TestModuleSnapshot module_snapshot_0;\n  std::map<std::string, std::string> annotations_simple_map_0;\n  annotations_simple_map_0[kKey0A] = kValue0A;\n  annotations_simple_map_0[kKey0B] = kValue0B;\n  module_snapshot_0.SetAnnotationsSimpleMap(annotations_simple_map_0);\n  module_snapshots.push_back(&module_snapshot_0);\n\n  // module_snapshot_1 is not expected to be written because it would not carry\n  // any MinidumpModuleCrashpadInfo data.\n  TestModuleSnapshot module_snapshot_1;\n  module_snapshots.push_back(&module_snapshot_1);\n\n  TestModuleSnapshot module_snapshot_2;\n  std::map<std::string, std::string> annotations_simple_map_2;\n  annotations_simple_map_2[kKey2] = kValue2;\n  module_snapshot_2.SetAnnotationsSimpleMap(annotations_simple_map_2);\n  module_snapshots.push_back(&module_snapshot_2);\n\n  TestModuleSnapshot module_snapshot_3;\n  std::vector<std::string> annotations_vector_3;\n  annotations_vector_3.push_back(kEntry3A);\n  annotations_vector_3.push_back(kEntry3B);\n  module_snapshot_3.SetAnnotationsVector(annotations_vector_3);\n  module_snapshots.push_back(&module_snapshot_3);\n\n  TestModuleSnapshot module_snapshot_4;\n  module_snapshot_4.SetAnnotationObjects({annotation});\n  module_snapshots.push_back(&module_snapshot_4);\n\n  auto module_list_writer =\n      std::make_unique<MinidumpModuleCrashpadInfoListWriter>();\n  module_list_writer->InitializeFromSnapshot(module_snapshots);\n  EXPECT_TRUE(module_list_writer->IsUseful());\n\n  StringFile string_file;\n  ASSERT_TRUE(module_list_writer->WriteEverything(&string_file));\n\n  const MinidumpModuleCrashpadInfoList* module_list =\n      MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 4);\n  ASSERT_TRUE(module_list);\n\n  EXPECT_EQ(module_list->modules[0].minidump_module_list_index, 0u);\n  const MinidumpModuleCrashpadInfo* module_0 =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[0].location);\n  ASSERT_TRUE(module_0);\n\n  EXPECT_EQ(module_0->version, MinidumpModuleCrashpadInfo::kVersion);\n\n  const MinidumpRVAList* list_annotations_0 =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          string_file.string(), module_0->list_annotations);\n  EXPECT_FALSE(list_annotations_0);\n\n  const MinidumpSimpleStringDictionary* simple_annotations_0 =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module_0->simple_annotations);\n  ASSERT_TRUE(simple_annotations_0);\n\n  ASSERT_EQ(simple_annotations_0->count, annotations_simple_map_0.size());\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_0->entries[0].key),\n            kKey0B);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_0->entries[0].value),\n            kValue0B);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_0->entries[1].key),\n            kKey0A);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_0->entries[1].value),\n            kValue0A);\n\n  EXPECT_FALSE(MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n      string_file.string(), module_0->annotation_objects));\n\n  EXPECT_EQ(module_list->modules[1].minidump_module_list_index, 2u);\n  const MinidumpModuleCrashpadInfo* module_2 =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[1].location);\n  ASSERT_TRUE(module_2);\n\n  EXPECT_EQ(module_2->version, MinidumpModuleCrashpadInfo::kVersion);\n\n  const MinidumpRVAList* list_annotations_2 =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          string_file.string(), module_2->list_annotations);\n  EXPECT_FALSE(list_annotations_2);\n\n  const MinidumpSimpleStringDictionary* simple_annotations_2 =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module_2->simple_annotations);\n  ASSERT_TRUE(simple_annotations_2);\n\n  ASSERT_EQ(simple_annotations_2->count, annotations_simple_map_2.size());\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_2->entries[0].key),\n            kKey2);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(\n                string_file.string(), simple_annotations_2->entries[0].value),\n            kValue2);\n\n  EXPECT_FALSE(MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n      string_file.string(), module_2->annotation_objects));\n\n  EXPECT_EQ(module_list->modules[2].minidump_module_list_index, 3u);\n  const MinidumpModuleCrashpadInfo* module_3 =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[2].location);\n  ASSERT_TRUE(module_3);\n\n  EXPECT_EQ(module_3->version, MinidumpModuleCrashpadInfo::kVersion);\n\n  const MinidumpRVAList* list_annotations_3 =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          string_file.string(), module_3->list_annotations);\n  ASSERT_TRUE(list_annotations_3);\n\n  ASSERT_EQ(list_annotations_3->count, annotations_vector_3.size());\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            list_annotations_3->children[0]),\n            kEntry3A);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            list_annotations_3->children[1]),\n            kEntry3B);\n\n  const MinidumpSimpleStringDictionary* simple_annotations_3 =\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module_3->simple_annotations);\n  EXPECT_FALSE(simple_annotations_3);\n\n  EXPECT_FALSE(MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n      string_file.string(), module_3->annotation_objects));\n\n  EXPECT_EQ(module_list->modules[3].minidump_module_list_index, 4u);\n  const MinidumpModuleCrashpadInfo* module_4 =\n      MinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfo>(\n          string_file.string(), module_list->modules[3].location);\n  ASSERT_TRUE(module_4);\n\n  EXPECT_EQ(module_4->version, MinidumpModuleCrashpadInfo::kVersion);\n\n  EXPECT_FALSE(MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n      string_file.string(), module_4->list_annotations));\n  EXPECT_FALSE(\n      MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n          string_file.string(), module_4->simple_annotations));\n\n  auto* annotation_list_4 =\n      MinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n          string_file.string(), module_4->annotation_objects);\n  ASSERT_TRUE(annotation_list_4);\n\n  ASSERT_EQ(annotation_list_4->count, 1u);\n\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            annotation_list_4->objects[0].name),\n            annotation.name);\n  EXPECT_EQ(annotation_list_4->objects[0].type, annotation.type);\n  EXPECT_EQ(annotation_list_4->objects[0].reserved, 0u);\n  EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(),\n                                   annotation_list_4->objects[0].value),\n            annotation.value);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_module_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_module_writer.h\"\n\n#include <stddef.h>\n\n#include <limits>\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"minidump/minidump_string_writer.h\"\n#include \"minidump/minidump_writer_util.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/numeric/in_range_cast.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {}\n\nnamespace internal {\n\ntemplate <typename CodeViewRecordType>\nMinidumpModuleCodeViewRecordPDBLinkWriter<\n    CodeViewRecordType>::MinidumpModuleCodeViewRecordPDBLinkWriter()\n    : MinidumpModuleCodeViewRecordWriter(), codeview_record_(), pdb_name_() {\n  codeview_record_.signature = CodeViewRecordType::kSignature;\n}\n\ntemplate <typename CodeViewRecordType>\nMinidumpModuleCodeViewRecordPDBLinkWriter<\n    CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {}\n\ntemplate <typename CodeViewRecordType>\nsize_t\nMinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // NUL-terminate.\n  return offsetof(decltype(codeview_record_), pdb_name) +\n         (pdb_name_.size() + 1) * sizeof(pdb_name_[0]);\n}\n\ntemplate <typename CodeViewRecordType>\nbool MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = &codeview_record_;\n  iov.iov_len = offsetof(decltype(codeview_record_), pdb_name);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  // NUL-terminate.\n  iov.iov_base = &pdb_name_[0];\n  iov.iov_len = (pdb_name_.size() + 1) * sizeof(pdb_name_[0]);\n  iovecs.push_back(iov);\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\n}  // namespace internal\n\ntemplate class internal::MinidumpModuleCodeViewRecordPDBLinkWriter<\n    CodeViewRecordPDB20>;\n\nMinidumpModuleCodeViewRecordPDB20Writer::\n    ~MinidumpModuleCodeViewRecordPDB20Writer() {}\n\nvoid MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge(\n    time_t timestamp,\n    uint32_t age) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  internal::MinidumpWriterUtil::AssignTimeT(&codeview_record()->timestamp,\n                                            timestamp);\n\n  codeview_record()->age = age;\n}\n\ntemplate class internal::MinidumpModuleCodeViewRecordPDBLinkWriter<\n    CodeViewRecordPDB70>;\n\nMinidumpModuleCodeViewRecordPDB70Writer::\n    ~MinidumpModuleCodeViewRecordPDB70Writer() {}\n\nvoid MinidumpModuleCodeViewRecordPDB70Writer::InitializeFromSnapshot(\n    const ModuleSnapshot* module_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  SetPDBName(module_snapshot->DebugFileName());\n\n  UUID uuid;\n  uint32_t age;\n  module_snapshot->UUIDAndAge(&uuid, &age);\n  SetUUIDAndAge(uuid, age);\n}\n\nMinidumpModuleCodeViewRecordBuildIDWriter::\n    MinidumpModuleCodeViewRecordBuildIDWriter()\n    : MinidumpModuleCodeViewRecordWriter(), build_id_() {}\n\nMinidumpModuleCodeViewRecordBuildIDWriter::\n    ~MinidumpModuleCodeViewRecordBuildIDWriter() {}\n\nsize_t MinidumpModuleCodeViewRecordBuildIDWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n  return offsetof(CodeViewRecordBuildID, build_id) + build_id_.size();\n}\n\nvoid MinidumpModuleCodeViewRecordBuildIDWriter::SetBuildID(\n    const std::vector<uint8_t>& build_id) {\n  DCHECK_EQ(state(), kStateMutable);\n  build_id_ = build_id;\n}\n\nbool MinidumpModuleCodeViewRecordBuildIDWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  CodeViewRecordBuildID cv;\n  cv.signature = CodeViewRecordBuildID::kSignature;\n\n  WritableIoVec iov;\n  iov.iov_base = &cv;\n  iov.iov_len = offsetof(CodeViewRecordBuildID, build_id);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  if (!build_id_.empty()) {\n    iov.iov_base = build_id_.data();\n    iov.iov_len = build_id_.size();\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter()\n    : internal::MinidumpWritable(),\n      image_debug_misc_(),\n      data_(),\n      data_utf16_() {}\n\nMinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() {}\n\nvoid MinidumpModuleMiscDebugRecordWriter::SetData(const std::string& data,\n                                                  bool utf16) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!utf16) {\n    data_utf16_.clear();\n    image_debug_misc_.Unicode = 0;\n    data_ = data;\n  } else {\n    data_.clear();\n    image_debug_misc_.Unicode = 1;\n    data_utf16_ = internal::MinidumpWriterUtil::ConvertUTF8ToUTF16(data);\n  }\n}\n\nbool MinidumpModuleMiscDebugRecordWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  // NUL-terminate.\n  if (!image_debug_misc_.Unicode) {\n    DCHECK(data_utf16_.empty());\n    image_debug_misc_.Length = base::checked_cast<uint32_t>(\n        offsetof(decltype(image_debug_misc_), Data) +\n        (data_.size() + 1) * sizeof(data_[0]));\n  } else {\n    DCHECK(data_.empty());\n    image_debug_misc_.Length = base::checked_cast<uint32_t>(\n        offsetof(decltype(image_debug_misc_), Data) +\n        (data_utf16_.size() + 1) * sizeof(data_utf16_[0]));\n  }\n\n  return true;\n}\n\nsize_t MinidumpModuleMiscDebugRecordWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return image_debug_misc_.Length;\n}\n\nbool MinidumpModuleMiscDebugRecordWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  const size_t base_length = offsetof(decltype(image_debug_misc_), Data);\n\n  WritableIoVec iov;\n  iov.iov_base = &image_debug_misc_;\n  iov.iov_len = base_length;\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  if (!image_debug_misc_.Unicode) {\n    DCHECK(data_utf16_.empty());\n    iov.iov_base = &data_[0];\n  } else {\n    DCHECK(data_.empty());\n    iov.iov_base = &data_utf16_[0];\n  }\n  iov.iov_len = image_debug_misc_.Length - base_length;\n  iovecs.push_back(iov);\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpModuleWriter::MinidumpModuleWriter()\n    : MinidumpWritable(),\n      module_(),\n      name_(),\n      codeview_record_(nullptr),\n      misc_debug_record_(nullptr) {\n  module_.VersionInfo.dwSignature = VS_FFI_SIGNATURE;\n  module_.VersionInfo.dwStrucVersion = VS_FFI_STRUCVERSION;\n}\n\nMinidumpModuleWriter::~MinidumpModuleWriter() {}\n\nvoid MinidumpModuleWriter::InitializeFromSnapshot(\n    const ModuleSnapshot* module_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!name_);\n  DCHECK(!codeview_record_);\n  DCHECK(!misc_debug_record_);\n\n  SetName(module_snapshot->Name());\n\n  SetImageBaseAddress(module_snapshot->Address());\n  SetImageSize(InRangeCast<uint32_t>(module_snapshot->Size(),\n                                     std::numeric_limits<uint32_t>::max()));\n  SetTimestamp(module_snapshot->Timestamp());\n\n  uint16_t v[4];\n  module_snapshot->FileVersion(&v[0], &v[1], &v[2], &v[3]);\n  SetFileVersion(v[0], v[1], v[2], v[3]);\n\n  module_snapshot->SourceVersion(&v[0], &v[1], &v[2], &v[3]);\n  SetProductVersion(v[0], v[1], v[2], v[3]);\n\n  uint32_t file_type;\n  switch (module_snapshot->GetModuleType()) {\n    case ModuleSnapshot::kModuleTypeExecutable:\n      file_type = VFT_APP;\n      break;\n    case ModuleSnapshot::kModuleTypeSharedLibrary:\n    case ModuleSnapshot::kModuleTypeLoadableModule:\n      file_type = VFT_DLL;\n      break;\n    default:\n      file_type = VFT_UNKNOWN;\n      break;\n  }\n  SetFileTypeAndSubtype(file_type, VFT2_UNKNOWN);\n\n  auto build_id = module_snapshot->BuildID();\n\n  std::unique_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record;\n  if (!build_id.empty()) {\n    auto cv_record_build_id =\n        std::make_unique<MinidumpModuleCodeViewRecordBuildIDWriter>();\n    cv_record_build_id->SetBuildID(build_id);\n    codeview_record = std::move(cv_record_build_id);\n  } else {\n    auto cv_record_pdb70 =\n        std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();\n    cv_record_pdb70->InitializeFromSnapshot(module_snapshot);\n    codeview_record = std::move(cv_record_pdb70);\n  }\n\n  SetCodeViewRecord(std::move(codeview_record));\n}\n\nconst MINIDUMP_MODULE* MinidumpModuleWriter::MinidumpModule() const {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return &module_;\n}\n\nvoid MinidumpModuleWriter::SetName(const std::string& name) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!name_) {\n    name_.reset(new internal::MinidumpUTF16StringWriter());\n  }\n  name_->SetUTF8(name);\n}\n\nvoid MinidumpModuleWriter::SetCodeViewRecord(\n    std::unique_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  codeview_record_ = std::move(codeview_record);\n}\n\nvoid MinidumpModuleWriter::SetMiscDebugRecord(\n    std::unique_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  misc_debug_record_ = std::move(misc_debug_record);\n}\n\nvoid MinidumpModuleWriter::SetTimestamp(time_t timestamp) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  internal::MinidumpWriterUtil::AssignTimeT(&module_.TimeDateStamp, timestamp);\n}\n\nvoid MinidumpModuleWriter::SetFileVersion(uint16_t version_0,\n                                          uint16_t version_1,\n                                          uint16_t version_2,\n                                          uint16_t version_3) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  module_.VersionInfo.dwFileVersionMS =\n      (implicit_cast<uint32_t>(version_0) << 16) | version_1;\n  module_.VersionInfo.dwFileVersionLS =\n      (implicit_cast<uint32_t>(version_2) << 16) | version_3;\n}\n\nvoid MinidumpModuleWriter::SetProductVersion(uint16_t version_0,\n                                             uint16_t version_1,\n                                             uint16_t version_2,\n                                             uint16_t version_3) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  module_.VersionInfo.dwProductVersionMS =\n      (implicit_cast<uint32_t>(version_0) << 16) | version_1;\n  module_.VersionInfo.dwProductVersionLS =\n      (implicit_cast<uint32_t>(version_2) << 16) | version_3;\n}\n\nvoid MinidumpModuleWriter::SetFileFlagsAndMask(uint32_t file_flags,\n                                               uint32_t file_flags_mask) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_EQ(file_flags & ~file_flags_mask, 0u);\n\n  module_.VersionInfo.dwFileFlags = file_flags;\n  module_.VersionInfo.dwFileFlagsMask = file_flags_mask;\n}\n\nbool MinidumpModuleWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n  CHECK(name_);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  name_->RegisterRVA(&module_.ModuleNameRva);\n\n  if (codeview_record_) {\n    codeview_record_->RegisterLocationDescriptor(&module_.CvRecord);\n  }\n\n  if (misc_debug_record_) {\n    misc_debug_record_->RegisterLocationDescriptor(&module_.MiscRecord);\n  }\n\n  return true;\n}\n\nsize_t MinidumpModuleWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is\n  // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children\n  // are responsible for writing themselves.\n  return 0;\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpModuleWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK(name_);\n\n  std::vector<MinidumpWritable*> children;\n  children.push_back(name_.get());\n  if (codeview_record_) {\n    children.push_back(codeview_record_.get());\n  }\n  if (misc_debug_record_) {\n    children.push_back(misc_debug_record_.get());\n  }\n\n  return children;\n}\n\nbool MinidumpModuleWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is\n  // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children\n  // are responsible for writing themselves.\n  return true;\n}\n\nMinidumpModuleListWriter::MinidumpModuleListWriter()\n    : MinidumpStreamWriter(), modules_(), module_list_base_() {}\n\nMinidumpModuleListWriter::~MinidumpModuleListWriter() {}\n\nvoid MinidumpModuleListWriter::InitializeFromSnapshot(\n    const std::vector<const ModuleSnapshot*>& module_snapshots) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(modules_.empty());\n\n  for (const ModuleSnapshot* module_snapshot : module_snapshots) {\n    auto module = std::make_unique<MinidumpModuleWriter>();\n    module->InitializeFromSnapshot(module_snapshot);\n    AddModule(std::move(module));\n  }\n}\n\nvoid MinidumpModuleListWriter::AddModule(\n    std::unique_ptr<MinidumpModuleWriter> module) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  modules_.push_back(std::move(module));\n}\n\nbool MinidumpModuleListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  size_t module_count = modules_.size();\n  if (!AssignIfInRange(&module_list_base_.NumberOfModules, module_count)) {\n    LOG(ERROR) << \"module_count \" << module_count << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpModuleListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(module_list_base_) + modules_.size() * sizeof(MINIDUMP_MODULE);\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpModuleListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& module : modules_) {\n    children.push_back(module.get());\n  }\n\n  return children;\n}\n\nbool MinidumpModuleListWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = &module_list_base_;\n  iov.iov_len = sizeof(module_list_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  for (const auto& module : modules_) {\n    iov.iov_base = module->MinidumpModule();\n    iov.iov_len = sizeof(MINIDUMP_MODULE);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpStreamType MinidumpModuleListWriter::StreamType() const {\n  return kMinidumpStreamTypeModuleList;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_module_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <time.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\nclass ModuleSnapshot;\n\nnamespace internal {\nclass MinidumpUTF16StringWriter;\n}  // namespace internal\n\n//! \\brief The base class for writers of CodeView records referenced by\n//!     MINIDUMP_MODULE::CvRecord in minidump files.\nclass MinidumpModuleCodeViewRecordWriter : public internal::MinidumpWritable {\n public:\n  MinidumpModuleCodeViewRecordWriter(\n      const MinidumpModuleCodeViewRecordWriter&) = delete;\n  MinidumpModuleCodeViewRecordWriter& operator=(\n      const MinidumpModuleCodeViewRecordWriter&) = delete;\n\n  ~MinidumpModuleCodeViewRecordWriter() override;\n\n protected:\n  MinidumpModuleCodeViewRecordWriter() : MinidumpWritable() {}\n};\n\nnamespace internal {\n\n//! \\brief The base class for writers of CodeView records that serve as links to\n//!     `.pdb` (program database) files.\ntemplate <typename CodeViewRecordType>\nclass MinidumpModuleCodeViewRecordPDBLinkWriter\n    : public MinidumpModuleCodeViewRecordWriter {\n public:\n  MinidumpModuleCodeViewRecordPDBLinkWriter(\n      const MinidumpModuleCodeViewRecordPDBLinkWriter&) = delete;\n  MinidumpModuleCodeViewRecordPDBLinkWriter& operator=(\n      const MinidumpModuleCodeViewRecordPDBLinkWriter&) = delete;\n\n  //! \\brief Sets the name of the `.pdb` file being linked to.\n  void SetPDBName(const std::string& pdb_name) { pdb_name_ = pdb_name; }\n\n protected:\n  MinidumpModuleCodeViewRecordPDBLinkWriter();\n  ~MinidumpModuleCodeViewRecordPDBLinkWriter() override;\n\n  // MinidumpWritable:\n  size_t SizeOfObject() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  //! \\brief Returns a pointer to the raw CodeView record’s data.\n  //!\n  //! Subclasses can use this to set fields in their codeview records other than\n  //! the `pdb_name` field.\n  CodeViewRecordType* codeview_record() { return &codeview_record_; }\n\n private:\n  CodeViewRecordType codeview_record_;\n  std::string pdb_name_;\n};\n\n}  // namespace internal\n\n//! \\brief The writer for a CodeViewRecordPDB20 object in a minidump file.\n//!\n//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer or\n//! MinidumpModuleCodeViewRecordBuildIDWriter instead.\nclass MinidumpModuleCodeViewRecordPDB20Writer final\n    : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<\n          CodeViewRecordPDB20> {\n public:\n  MinidumpModuleCodeViewRecordPDB20Writer()\n      : internal::MinidumpModuleCodeViewRecordPDBLinkWriter<\n            CodeViewRecordPDB20>() {}\n\n  MinidumpModuleCodeViewRecordPDB20Writer(\n      const MinidumpModuleCodeViewRecordPDB20Writer&) = delete;\n  MinidumpModuleCodeViewRecordPDB20Writer& operator=(\n      const MinidumpModuleCodeViewRecordPDB20Writer&) = delete;\n\n  ~MinidumpModuleCodeViewRecordPDB20Writer() override;\n\n  //! \\brief Sets CodeViewRecordPDB20::timestamp and CodeViewRecordPDB20::age.\n  void SetTimestampAndAge(time_t timestamp, uint32_t age);\n};\n\n//! \\brief The writer for a CodeViewRecordPDB70 object in a minidump file.\nclass MinidumpModuleCodeViewRecordPDB70Writer final\n    : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<\n          CodeViewRecordPDB70> {\n public:\n  MinidumpModuleCodeViewRecordPDB70Writer()\n      : internal::MinidumpModuleCodeViewRecordPDBLinkWriter<\n            CodeViewRecordPDB70>() {}\n\n  MinidumpModuleCodeViewRecordPDB70Writer(\n      const MinidumpModuleCodeViewRecordPDB70Writer&) = delete;\n  MinidumpModuleCodeViewRecordPDB70Writer& operator=(\n      const MinidumpModuleCodeViewRecordPDB70Writer&) = delete;\n\n  ~MinidumpModuleCodeViewRecordPDB70Writer() override;\n\n  //! \\brief Initializes the CodeViewRecordPDB70 based on \\a module_snapshot.\n  //!\n  //! \\param[in] module_snapshot The module snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot);\n\n  //! \\brief Sets CodeViewRecordPDB70::uuid and CodeViewRecordPDB70::age.\n  void SetUUIDAndAge(const UUID& uuid, uint32_t age) {\n    codeview_record()->uuid = uuid;\n    codeview_record()->age = age;\n  }\n};\n\n//! \\brief The writer for a CodeViewRecordBuildID object in a minidump file.\nclass MinidumpModuleCodeViewRecordBuildIDWriter final\n    : public MinidumpModuleCodeViewRecordWriter {\n public:\n  MinidumpModuleCodeViewRecordBuildIDWriter();\n\n  MinidumpModuleCodeViewRecordBuildIDWriter(\n      const MinidumpModuleCodeViewRecordBuildIDWriter&) = delete;\n  MinidumpModuleCodeViewRecordBuildIDWriter& operator=(\n      const MinidumpModuleCodeViewRecordBuildIDWriter&) = delete;\n\n  ~MinidumpModuleCodeViewRecordBuildIDWriter() override;\n\n  //! \\brief Sets the build ID used for symbol lookup.\n  void SetBuildID(const std::vector<uint8_t>& build_id);\n\n private:\n  // MinidumpWritable:\n  size_t SizeOfObject() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  std::vector<uint8_t> build_id_;\n};\n\n//! \\brief The writer for an IMAGE_DEBUG_MISC object in a minidump file.\n//!\n//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.\nclass MinidumpModuleMiscDebugRecordWriter final\n    : public internal::MinidumpWritable {\n public:\n  MinidumpModuleMiscDebugRecordWriter();\n\n  MinidumpModuleMiscDebugRecordWriter(\n      const MinidumpModuleMiscDebugRecordWriter&) = delete;\n  MinidumpModuleMiscDebugRecordWriter& operator=(\n      const MinidumpModuleMiscDebugRecordWriter&) = delete;\n\n  ~MinidumpModuleMiscDebugRecordWriter() override;\n\n  //! \\brief Sets IMAGE_DEBUG_MISC::DataType.\n  void SetDataType(uint32_t data_type) {\n    image_debug_misc_.DataType = data_type;\n  }\n\n  //! \\brief Sets IMAGE_DEBUG_MISC::Data, IMAGE_DEBUG_MISC::Length, and\n  //!     IMAGE_DEBUG_MISC::Unicode.\n  //!\n  //! If \\a utf16 is `true`, \\a data will be treated as UTF-8 data and will be\n  //! converted to UTF-16, and IMAGE_DEBUG_MISC::Unicode will be set to `1`.\n  //! Otherwise, \\a data will be used as-is and IMAGE_DEBUG_MISC::Unicode will\n  //! be set to `0`.\n  void SetData(const std::string& data, bool utf16);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  IMAGE_DEBUG_MISC image_debug_misc_;\n  std::string data_;\n  std::u16string data_utf16_;\n};\n\n//! \\brief The writer for a MINIDUMP_MODULE object in a minidump file.\n//!\n//! Because MINIDUMP_MODULE objects only appear as elements of\n//! MINIDUMP_MODULE_LIST objects, this class does not write any data on its own.\n//! It makes its MINIDUMP_MODULE data available to its MinidumpModuleListWriter\n//! parent, which writes it as part of a MINIDUMP_MODULE_LIST.\nclass MinidumpModuleWriter final : public internal::MinidumpWritable {\n public:\n  MinidumpModuleWriter();\n\n  MinidumpModuleWriter(const MinidumpModuleWriter&) = delete;\n  MinidumpModuleWriter& operator=(const MinidumpModuleWriter&) = delete;\n\n  ~MinidumpModuleWriter() override;\n\n  //! \\brief Initializes the MINIDUMP_MODULE based on \\a module_snapshot.\n  //!\n  //! \\param[in] module_snapshot The module snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot);\n\n  //! \\brief Returns a MINIDUMP_MODULE referencing this object’s data.\n  //!\n  //! This method is expected to be called by a MinidumpModuleListWriter in\n  //! order to obtain a MINIDUMP_MODULE to include in its list.\n  //!\n  //! \\note Valid in #kStateWritable.\n  const MINIDUMP_MODULE* MinidumpModule() const;\n\n  //! \\brief Arranges for MINIDUMP_MODULE::ModuleNameRva to point to a\n  //!     MINIDUMP_STRING containing \\a name.\n  //!\n  //! A name is required in all MINIDUMP_MODULE objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetName(const std::string& name);\n\n  //! \\brief Arranges for MINIDUMP_MODULE::CvRecord to point to a CodeView\n  //!     record to be written by \\a codeview_record.\n  //!\n  //! This object takes ownership of \\a codeview_record and becomes its parent\n  //! in the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetCodeViewRecord(\n      std::unique_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record);\n\n  //! \\brief Arranges for MINIDUMP_MODULE::MiscRecord to point to an\n  //!     IMAGE_DEBUG_MISC object to be written by \\a misc_debug_record.\n  //!\n  //! This object takes ownership of \\a misc_debug_record and becomes its parent\n  //! in the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetMiscDebugRecord(\n      std::unique_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record);\n\n  //! \\brief Sets IMAGE_DEBUG_MISC::BaseOfImage.\n  void SetImageBaseAddress(uint64_t image_base_address) {\n    module_.BaseOfImage = image_base_address;\n  }\n\n  //! \\brief Sets IMAGE_DEBUG_MISC::SizeOfImage.\n  void SetImageSize(uint32_t image_size) { module_.SizeOfImage = image_size; }\n\n  //! \\brief Sets IMAGE_DEBUG_MISC::CheckSum.\n  void SetChecksum(uint32_t checksum) { module_.CheckSum = checksum; }\n\n  //! \\brief Sets IMAGE_DEBUG_MISC::TimeDateStamp.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetTimestamp(time_t timestamp);\n\n  //! \\brief Sets \\ref VS_FIXEDFILEINFO::dwFileVersionMS\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionMS\" and \\ref\n  //!     VS_FIXEDFILEINFO::dwFileVersionLS\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionLS\".\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetFileVersion(uint16_t version_0,\n                      uint16_t version_1,\n                      uint16_t version_2,\n                      uint16_t version_3);\n\n  //! \\brief Sets \\ref VS_FIXEDFILEINFO::dwProductVersionMS\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionMS\" and \\ref\n  //!     VS_FIXEDFILEINFO::dwProductVersionLS\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionLS\".\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetProductVersion(uint16_t version_0,\n                         uint16_t version_1,\n                         uint16_t version_2,\n                         uint16_t version_3);\n\n  //! \\brief Sets \\ref VS_FIXEDFILEINFO::dwFileFlags\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwFileFlags\" and \\ref\n  //!     VS_FIXEDFILEINFO::dwFileFlagsMask\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwFileFlagsMask\".\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetFileFlagsAndMask(uint32_t file_flags, uint32_t file_flags_mask);\n\n  //! \\brief Sets \\ref VS_FIXEDFILEINFO::dwFileOS\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwFileOS\".\n  void SetFileOS(uint32_t file_os) { module_.VersionInfo.dwFileOS = file_os; }\n\n  //! \\brief Sets \\ref VS_FIXEDFILEINFO::dwFileType\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwFileType\" and \\ref\n  //!     VS_FIXEDFILEINFO::dwFileSubtype\n  //!     \"IMAGE_DEBUG_MISC::VersionInfo::dwFileSubtype\".\n  void SetFileTypeAndSubtype(uint32_t file_type, uint32_t file_subtype) {\n    module_.VersionInfo.dwFileType = file_type;\n    module_.VersionInfo.dwFileSubtype = file_subtype;\n  }\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  MINIDUMP_MODULE module_;\n  std::unique_ptr<internal::MinidumpUTF16StringWriter> name_;\n  std::unique_ptr<MinidumpModuleCodeViewRecordWriter> codeview_record_;\n  std::unique_ptr<MinidumpModuleMiscDebugRecordWriter> misc_debug_record_;\n};\n\n//! \\brief The writer for a MINIDUMP_MODULE_LIST stream in a minidump file,\n//!     containing a list of MINIDUMP_MODULE objects.\nclass MinidumpModuleListWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpModuleListWriter();\n\n  MinidumpModuleListWriter(const MinidumpModuleListWriter&) = delete;\n  MinidumpModuleListWriter& operator=(const MinidumpModuleListWriter&) = delete;\n\n  ~MinidumpModuleListWriter() override;\n\n  //! \\brief Adds an initialized MINIDUMP_MODULE for each module in \\a\n  //!     module_snapshots to the MINIDUMP_MODULE_LIST.\n  //!\n  //! \\param[in] module_snapshots The module snapshots to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. AddModule() may not be called before this\n  //!     method, and it is not normally necessary to call AddModule() after\n  //!     this method.\n  void InitializeFromSnapshot(\n      const std::vector<const ModuleSnapshot*>& module_snapshots);\n\n  //! \\brief Adds a MinidumpModuleWriter to the MINIDUMP_MODULE_LIST.\n  //!\n  //! This object takes ownership of \\a module and becomes its parent in the\n  //! overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddModule(std::unique_ptr<MinidumpModuleWriter> module);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  std::vector<std::unique_ptr<MinidumpModuleWriter>> modules_;\n  MINIDUMP_MODULE_LIST module_list_base_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_module_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_module_writer.h\"\n\n#include <stddef.h>\n#include <string.h>\n\n#include <iterator>\n#include <utility>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_module_snapshot.h\"\n#include \"test/gtest_death.h\"\n#include \"util/file/string_file.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid GetModuleListStream(const std::string& file_contents,\n                         const MINIDUMP_MODULE_LIST** module_list) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kModuleListStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kModulesOffset =\n      kModuleListStreamOffset + sizeof(MINIDUMP_MODULE_LIST);\n\n  ASSERT_GE(file_contents.size(), kModulesOffset);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeModuleList);\n  EXPECT_EQ(directory[0].Location.Rva, kModuleListStreamOffset);\n\n  *module_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(\n      file_contents, directory[0].Location);\n  ASSERT_TRUE(module_list);\n}\n\nTEST(MinidumpModuleWriter, EmptyModuleList) {\n  MinidumpFileWriter minidump_file_writer;\n  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MODULE_LIST));\n\n  const MINIDUMP_MODULE_LIST* module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetModuleListStream(string_file.string(), &module_list));\n\n  EXPECT_EQ(module_list->NumberOfModules, 0u);\n}\n\n// If |expected_pdb_name| is not nullptr, |codeview_record| is used to locate a\n// CodeView record in |file_contents|, and its fields are compared against the\n// |expected_pdb_*| values. If |expected_pdb_uuid| is supplied, the CodeView\n// record must be a PDB 7.0 link, otherwise, it must be a PDB 2.0 link. If\n// |expected_pdb_name| is nullptr, |codeview_record| must not point to anything.\nvoid ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record,\n                          const std::string& file_contents,\n                          const char* expected_pdb_name,\n                          const UUID* expected_pdb_uuid,\n                          time_t expected_pdb_timestamp,\n                          uint32_t expected_pdb_age) {\n  if (expected_pdb_name) {\n    EXPECT_NE(codeview_record->Rva, 0u);\n\n    std::string observed_pdb_name;\n    if (expected_pdb_uuid) {\n      // The CodeView record should be a PDB 7.0 link.\n      const CodeViewRecordPDB70* codeview_pdb70_record =\n          MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(\n              file_contents, *codeview_record);\n      ASSERT_TRUE(codeview_pdb70_record);\n      EXPECT_EQ(memcmp(expected_pdb_uuid,\n                       &codeview_pdb70_record->uuid,\n                       sizeof(codeview_pdb70_record->uuid)),\n                0);\n      EXPECT_EQ(codeview_pdb70_record->age, expected_pdb_age);\n\n      observed_pdb_name.assign(\n          reinterpret_cast<const char*>(&codeview_pdb70_record->pdb_name[0]),\n          codeview_record->DataSize - offsetof(CodeViewRecordPDB70, pdb_name));\n    } else {\n      // The CodeView record should be a PDB 2.0 link.\n      const CodeViewRecordPDB20* codeview_pdb20_record =\n          MinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(\n              file_contents, *codeview_record);\n      ASSERT_TRUE(codeview_pdb20_record);\n      EXPECT_EQ(codeview_pdb20_record->timestamp,\n                static_cast<uint32_t>(expected_pdb_timestamp));\n      EXPECT_EQ(codeview_pdb20_record->age, expected_pdb_age);\n\n      observed_pdb_name.assign(\n          reinterpret_cast<const char*>(&codeview_pdb20_record->pdb_name[0]),\n          codeview_record->DataSize - offsetof(CodeViewRecordPDB20, pdb_name));\n    }\n\n    // Check for, and then remove, the NUL terminator.\n    EXPECT_EQ(observed_pdb_name[observed_pdb_name.size() - 1], '\\0');\n    observed_pdb_name.resize(observed_pdb_name.size() - 1);\n\n    EXPECT_EQ(observed_pdb_name, expected_pdb_name);\n  } else {\n    // There should be no CodeView record.\n    EXPECT_EQ(codeview_record->DataSize, 0u);\n    EXPECT_EQ(codeview_record->Rva, 0u);\n  }\n}\n\n// If |expected_debug_name| is not nullptr, |misc_record| is used to locate a\n// miscellanous debugging record in |file_contents|, and its fields are compared\n// against the the |expected_debug_*| values. If |expected_debug_name| is\n// nullptr, |misc_record| must not point to anything.\nvoid ExpectMiscellaneousDebugRecord(\n    const MINIDUMP_LOCATION_DESCRIPTOR* misc_record,\n    const std::string& file_contents,\n    const char* expected_debug_name,\n    uint32_t expected_debug_type,\n    bool expected_debug_utf16) {\n  if (expected_debug_name) {\n    EXPECT_NE(misc_record->Rva, 0u);\n    const IMAGE_DEBUG_MISC* misc_debug_record =\n        MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(file_contents,\n                                                               *misc_record);\n    ASSERT_TRUE(misc_debug_record);\n    EXPECT_EQ(misc_debug_record->DataType, expected_debug_type);\n    EXPECT_EQ(misc_debug_record->Unicode != 0, expected_debug_utf16);\n    EXPECT_EQ(misc_debug_record->Reserved[0], 0u);\n    EXPECT_EQ(misc_debug_record->Reserved[1], 0u);\n    EXPECT_EQ(misc_debug_record->Reserved[2], 0u);\n\n    // Check for the NUL terminator.\n    size_t bytes_available =\n        misc_debug_record->Length - offsetof(IMAGE_DEBUG_MISC, Data);\n    EXPECT_EQ(misc_debug_record->Data[bytes_available - 1], '\\0');\n    std::string observed_data(\n        reinterpret_cast<const char*>(misc_debug_record->Data));\n\n    size_t bytes_used;\n    if (misc_debug_record->Unicode) {\n      std::u16string observed_data_utf16(\n          reinterpret_cast<const char16_t*>(misc_debug_record->Data));\n      bytes_used = (observed_data_utf16.size() + 1) * sizeof(char16_t);\n      observed_data = base::UTF16ToUTF8(observed_data_utf16);\n    } else {\n      observed_data = reinterpret_cast<const char*>(misc_debug_record->Data);\n      bytes_used = (observed_data.size() + 1) * sizeof(char);\n    }\n    EXPECT_LE(bytes_used, bytes_available);\n\n    // Make sure that any padding bytes after the first NUL are also NUL.\n    for (size_t index = bytes_used; index < bytes_available; ++index) {\n      EXPECT_EQ(misc_debug_record->Data[index], '\\0');\n    }\n\n    EXPECT_EQ(observed_data, expected_debug_name);\n  } else {\n    // There should be no miscellaneous debugging record.\n    EXPECT_EQ(misc_record->DataSize, 0u);\n    EXPECT_EQ(misc_record->Rva, 0u);\n  }\n}\n\n// ExpectModule() verifies that |expected| matches |observed|. Fields that are\n// supposed to contain constant magic numbers are verified against the expected\n// constants instead of |expected|. Reserved fields are verified to be 0. RVA\n// and MINIDUMP_LOCATION_DESCRIPTOR fields are not verified against |expected|.\n// Instead, |ModuleNameRva| is used to locate the module name, which is compared\n// against |expected_module_name|. ExpectCodeViewRecord() and\n// ExpectMiscellaneousDebugRecord() are used to verify the |CvRecord| and\n// |MiscRecord| fields against |expected_pdb_*| and |expected_debug_*|\n// parameters, respectively.\nvoid ExpectModule(const MINIDUMP_MODULE* expected,\n                  const MINIDUMP_MODULE* observed,\n                  const std::string& file_contents,\n                  const std::string& expected_module_name,\n                  const char* expected_pdb_name,\n                  const UUID* expected_pdb_uuid,\n                  time_t expected_pdb_timestamp,\n                  uint32_t expected_pdb_age,\n                  const char* expected_debug_name,\n                  uint32_t expected_debug_type,\n                  bool expected_debug_utf16) {\n  MINIDUMP_MODULE expected_module, observed_module;\n  memcpy(&expected_module, expected, sizeof(expected_module));\n  memcpy(&observed_module, observed, sizeof(observed_module));\n\n  EXPECT_EQ(observed_module.BaseOfImage, expected_module.BaseOfImage);\n  EXPECT_EQ(observed_module.SizeOfImage, expected_module.SizeOfImage);\n  EXPECT_EQ(observed_module.CheckSum, expected_module.CheckSum);\n  EXPECT_EQ(observed_module.TimeDateStamp, expected_module.TimeDateStamp);\n  EXPECT_EQ(observed_module.VersionInfo.dwSignature,\n            implicit_cast<uint32_t>(VS_FFI_SIGNATURE));\n  EXPECT_EQ(observed_module.VersionInfo.dwStrucVersion,\n            implicit_cast<uint32_t>(VS_FFI_STRUCVERSION));\n  EXPECT_EQ(observed_module.VersionInfo.dwFileVersionMS,\n            expected_module.VersionInfo.dwFileVersionMS);\n  EXPECT_EQ(observed_module.VersionInfo.dwFileVersionLS,\n            expected_module.VersionInfo.dwFileVersionLS);\n  EXPECT_EQ(observed_module.VersionInfo.dwProductVersionMS,\n            expected_module.VersionInfo.dwProductVersionMS);\n  EXPECT_EQ(observed_module.VersionInfo.dwProductVersionLS,\n            expected_module.VersionInfo.dwProductVersionLS);\n  EXPECT_EQ(observed_module.VersionInfo.dwFileFlagsMask,\n            expected_module.VersionInfo.dwFileFlagsMask);\n  EXPECT_EQ(observed_module.VersionInfo.dwFileFlags,\n            expected_module.VersionInfo.dwFileFlags);\n  EXPECT_EQ(observed_module.VersionInfo.dwFileOS,\n            expected_module.VersionInfo.dwFileOS);\n  EXPECT_EQ(observed_module.VersionInfo.dwFileType,\n            expected_module.VersionInfo.dwFileType);\n  EXPECT_EQ(observed_module.VersionInfo.dwFileSubtype,\n            expected_module.VersionInfo.dwFileSubtype);\n  EXPECT_EQ(observed_module.VersionInfo.dwFileDateMS,\n            expected_module.VersionInfo.dwFileDateMS);\n  EXPECT_EQ(observed_module.VersionInfo.dwFileDateLS,\n            expected_module.VersionInfo.dwFileDateLS);\n\n  uint64_t reserved0, reserved1;\n  memcpy(&reserved0, &observed_module.Reserved0, sizeof(reserved0));\n  memcpy(&reserved1, &observed_module.Reserved1, sizeof(reserved1));\n\n  EXPECT_EQ(reserved0, 0u);\n  EXPECT_EQ(reserved1, 0u);\n\n  EXPECT_NE(observed_module.ModuleNameRva, 0u);\n  std::u16string observed_module_name_utf16 =\n      MinidumpStringAtRVAAsString(file_contents, observed_module.ModuleNameRva);\n  std::u16string expected_module_name_utf16 =\n      base::UTF8ToUTF16(expected_module_name);\n  EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16);\n\n  ASSERT_NO_FATAL_FAILURE(ExpectCodeViewRecord(&observed_module.CvRecord,\n                                               file_contents,\n                                               expected_pdb_name,\n                                               expected_pdb_uuid,\n                                               expected_pdb_timestamp,\n                                               expected_pdb_age));\n\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectMiscellaneousDebugRecord(&observed_module.MiscRecord,\n                                     file_contents,\n                                     expected_debug_name,\n                                     expected_debug_type,\n                                     expected_debug_utf16));\n}\n\n// ExpectModuleWithBuildIDCv() is like ExpectModule( but expects the module to\n// have a BuildID CodeView Record.\nvoid ExpectModuleWithBuildIDCv(const MINIDUMP_MODULE* expected,\n                               const MINIDUMP_MODULE* observed,\n                               const std::string& file_contents,\n                               const std::string& expected_module_name,\n                               const std::vector<uint8_t>& expected_build_id) {\n  EXPECT_EQ(observed->BaseOfImage, expected->BaseOfImage);\n  EXPECT_EQ(observed->SizeOfImage, expected->SizeOfImage);\n  EXPECT_EQ(observed->CheckSum, expected->CheckSum);\n  EXPECT_EQ(observed->TimeDateStamp, expected->TimeDateStamp);\n  EXPECT_EQ(observed->VersionInfo.dwSignature,\n            implicit_cast<uint32_t>(VS_FFI_SIGNATURE));\n  EXPECT_EQ(observed->VersionInfo.dwStrucVersion,\n            implicit_cast<uint32_t>(VS_FFI_STRUCVERSION));\n  EXPECT_EQ(observed->VersionInfo.dwFileVersionMS,\n            expected->VersionInfo.dwFileVersionMS);\n  EXPECT_EQ(observed->VersionInfo.dwFileVersionLS,\n            expected->VersionInfo.dwFileVersionLS);\n  EXPECT_EQ(observed->VersionInfo.dwProductVersionMS,\n            expected->VersionInfo.dwProductVersionMS);\n  EXPECT_EQ(observed->VersionInfo.dwProductVersionLS,\n            expected->VersionInfo.dwProductVersionLS);\n  EXPECT_EQ(observed->VersionInfo.dwFileFlagsMask,\n            expected->VersionInfo.dwFileFlagsMask);\n  EXPECT_EQ(observed->VersionInfo.dwFileFlags,\n            expected->VersionInfo.dwFileFlags);\n  EXPECT_EQ(observed->VersionInfo.dwFileOS, expected->VersionInfo.dwFileOS);\n  EXPECT_EQ(observed->VersionInfo.dwFileType, expected->VersionInfo.dwFileType);\n  EXPECT_EQ(observed->VersionInfo.dwFileSubtype,\n            expected->VersionInfo.dwFileSubtype);\n  EXPECT_EQ(observed->VersionInfo.dwFileDateMS,\n            expected->VersionInfo.dwFileDateMS);\n  EXPECT_EQ(observed->VersionInfo.dwFileDateLS,\n            expected->VersionInfo.dwFileDateLS);\n\n  uint64_t reserved0, reserved1;\n  memcpy(&reserved0, &observed->Reserved0, sizeof(reserved0));\n  memcpy(&reserved1, &observed->Reserved1, sizeof(reserved1));\n\n  EXPECT_EQ(reserved0, 0u);\n  EXPECT_EQ(reserved1, 0u);\n\n  EXPECT_NE(observed->ModuleNameRva, 0u);\n  std::u16string observed_module_name_utf16 =\n      MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva);\n  std::u16string expected_module_name_utf16 =\n      base::UTF8ToUTF16(expected_module_name);\n  EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16);\n\n  const CodeViewRecordBuildID* codeview_build_id_record =\n      MinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(\n          file_contents, observed->CvRecord);\n  ASSERT_TRUE(codeview_build_id_record);\n  EXPECT_EQ(memcmp(expected_build_id.data(),\n                   &codeview_build_id_record->build_id,\n                   expected_build_id.size()),\n            0);\n}\n\nTEST(MinidumpModuleWriter, EmptyModule) {\n  MinidumpFileWriter minidump_file_writer;\n  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();\n\n  static constexpr char kModuleName[] = \"test_executable\";\n\n  auto module_writer = std::make_unique<MinidumpModuleWriter>();\n  module_writer->SetName(kModuleName);\n\n  module_list_writer->AddModule(std::move(module_writer));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));\n\n  const MINIDUMP_MODULE_LIST* module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetModuleListStream(string_file.string(), &module_list));\n\n  EXPECT_EQ(module_list->NumberOfModules, 1u);\n\n  MINIDUMP_MODULE expected = {};\n  ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,\n                                       &module_list->Modules[0],\n                                       string_file.string(),\n                                       kModuleName,\n                                       nullptr,\n                                       nullptr,\n                                       0,\n                                       0,\n                                       nullptr,\n                                       0,\n                                       false));\n}\n\nTEST(MinidumpModuleWriter, OneModule) {\n  MinidumpFileWriter minidump_file_writer;\n  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();\n\n  static constexpr char kModuleName[] = \"statically_linked\";\n  constexpr uint64_t kModuleBase = 0x10da69000;\n  constexpr uint32_t kModuleSize = 0x1000;\n  constexpr uint32_t kChecksum = 0x76543210;\n  constexpr time_t kTimestamp = 0x386d4380;\n  constexpr uint32_t kFileVersionMS = 0x00010002;\n  constexpr uint32_t kFileVersionLS = 0x00030004;\n  constexpr uint32_t kProductVersionMS = 0x00050006;\n  constexpr uint32_t kProductVersionLS = 0x00070008;\n  constexpr uint32_t kFileFlagsMask = VS_FF_DEBUG | VS_FF_PRERELEASE |\n                                      VS_FF_PATCHED | VS_FF_PRIVATEBUILD |\n                                      VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD;\n  constexpr uint32_t kFileFlags = VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD;\n  constexpr uint32_t kFileOS = VOS_DOS;\n  constexpr uint32_t kFileType = VFT_DRV;\n  constexpr uint32_t kFileSubtype = VFT2_DRV_KEYBOARD;\n  static constexpr char kPDBName[] = \"statical.pdb\";\n  static constexpr uint8_t kPDBUUIDBytes[16] = {0xfe,\n                                                0xdc,\n                                                0xba,\n                                                0x98,\n                                                0x76,\n                                                0x54,\n                                                0x32,\n                                                0x10,\n                                                0x08,\n                                                0x19,\n                                                0x2a,\n                                                0x3b,\n                                                0x4c,\n                                                0x5d,\n                                                0x6e,\n                                                0x7f};\n  UUID pdb_uuid;\n  pdb_uuid.InitializeFromBytes(kPDBUUIDBytes);\n  constexpr uint32_t kPDBAge = 1;\n  constexpr uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME;\n  static constexpr char kDebugName[] = \"statical.dbg\";\n  constexpr bool kDebugUTF16 = false;\n\n  auto module_writer = std::make_unique<MinidumpModuleWriter>();\n  module_writer->SetName(kModuleName);\n  module_writer->SetImageBaseAddress(kModuleBase);\n  module_writer->SetImageSize(kModuleSize);\n  module_writer->SetChecksum(kChecksum);\n  module_writer->SetTimestamp(kTimestamp);\n  module_writer->SetFileVersion(kFileVersionMS >> 16,\n                                kFileVersionMS & 0xffff,\n                                kFileVersionLS >> 16,\n                                kFileVersionLS & 0xffff);\n  module_writer->SetProductVersion(kProductVersionMS >> 16,\n                                   kProductVersionMS & 0xffff,\n                                   kProductVersionLS >> 16,\n                                   kProductVersionLS & 0xffff);\n  module_writer->SetFileFlagsAndMask(kFileFlags, kFileFlagsMask);\n  module_writer->SetFileOS(kFileOS);\n  module_writer->SetFileTypeAndSubtype(kFileType, kFileSubtype);\n\n  auto codeview_pdb70_writer =\n      std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();\n  codeview_pdb70_writer->SetPDBName(kPDBName);\n  codeview_pdb70_writer->SetUUIDAndAge(pdb_uuid, kPDBAge);\n  module_writer->SetCodeViewRecord(std::move(codeview_pdb70_writer));\n\n  auto misc_debug_writer =\n      std::make_unique<MinidumpModuleMiscDebugRecordWriter>();\n  misc_debug_writer->SetDataType(kDebugType);\n  misc_debug_writer->SetData(kDebugName, kDebugUTF16);\n  module_writer->SetMiscDebugRecord(std::move(misc_debug_writer));\n\n  module_list_writer->AddModule(std::move(module_writer));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));\n\n  const MINIDUMP_MODULE_LIST* module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetModuleListStream(string_file.string(), &module_list));\n\n  EXPECT_EQ(module_list->NumberOfModules, 1u);\n\n  MINIDUMP_MODULE expected = {};\n  expected.BaseOfImage = kModuleBase;\n  expected.SizeOfImage = kModuleSize;\n  expected.CheckSum = kChecksum;\n  expected.TimeDateStamp = kTimestamp;\n  expected.VersionInfo.dwFileVersionMS = kFileVersionMS;\n  expected.VersionInfo.dwFileVersionLS = kFileVersionLS;\n  expected.VersionInfo.dwProductVersionMS = kProductVersionMS;\n  expected.VersionInfo.dwProductVersionLS = kProductVersionLS;\n  expected.VersionInfo.dwFileFlagsMask = kFileFlagsMask;\n  expected.VersionInfo.dwFileFlags = kFileFlags;\n  expected.VersionInfo.dwFileOS = kFileOS;\n  expected.VersionInfo.dwFileType = kFileType;\n  expected.VersionInfo.dwFileSubtype = kFileSubtype;\n\n  ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,\n                                       &module_list->Modules[0],\n                                       string_file.string(),\n                                       kModuleName,\n                                       kPDBName,\n                                       &pdb_uuid,\n                                       0,\n                                       kPDBAge,\n                                       kDebugName,\n                                       kDebugType,\n                                       kDebugUTF16));\n}\n\nTEST(MinidumpModuleWriter, OneModule_CodeViewUsesPDB20_MiscUsesUTF16) {\n  // MinidumpModuleWriter.OneModule tested with a PDB 7.0 link as the CodeView\n  // record and an IMAGE_DEBUG_MISC record in UTF-8. This test exercises the\n  // alternatives, a PDB 2.0 link as the CodeView record and an IMAGE_DEBUG_MISC\n  // record with UTF-16 data.\n  MinidumpFileWriter minidump_file_writer;\n  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();\n\n  static constexpr char kModuleName[] = \"dinosaur\";\n  static constexpr char kPDBName[] = \"d1n05.pdb\";\n  constexpr time_t kPDBTimestamp = 0x386d4380;\n  constexpr uint32_t kPDBAge = 1;\n  constexpr uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME;\n  static constexpr char kDebugName[] = \"d1n05.dbg\";\n  constexpr bool kDebugUTF16 = true;\n\n  auto module_writer = std::make_unique<MinidumpModuleWriter>();\n  module_writer->SetName(kModuleName);\n\n  auto codeview_pdb20_writer =\n      std::make_unique<MinidumpModuleCodeViewRecordPDB20Writer>();\n  codeview_pdb20_writer->SetPDBName(kPDBName);\n  codeview_pdb20_writer->SetTimestampAndAge(kPDBTimestamp, kPDBAge);\n  module_writer->SetCodeViewRecord(std::move(codeview_pdb20_writer));\n\n  auto misc_debug_writer =\n      std::make_unique<MinidumpModuleMiscDebugRecordWriter>();\n  misc_debug_writer->SetDataType(kDebugType);\n  misc_debug_writer->SetData(kDebugName, kDebugUTF16);\n  module_writer->SetMiscDebugRecord(std::move(misc_debug_writer));\n\n  module_list_writer->AddModule(std::move(module_writer));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));\n\n  const MINIDUMP_MODULE_LIST* module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetModuleListStream(string_file.string(), &module_list));\n\n  EXPECT_EQ(module_list->NumberOfModules, 1u);\n\n  MINIDUMP_MODULE expected = {};\n\n  ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,\n                                       &module_list->Modules[0],\n                                       string_file.string(),\n                                       kModuleName,\n                                       kPDBName,\n                                       nullptr,\n                                       kPDBTimestamp,\n                                       kPDBAge,\n                                       kDebugName,\n                                       kDebugType,\n                                       kDebugUTF16));\n}\n\nTEST(MinidumpModuleWriter, OneModule_CodeViewBuildID) {\n  // MinidumpModuleWriter.OneModule tested with a BuildID CodeView\n  MinidumpFileWriter minidump_file_writer;\n  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();\n\n  static constexpr char kModuleName[] = \"dinosaur\";\n  static constexpr char kBuildID[] =\n      \"averylonghashcodeormaybeitsjustrandomnumbershardtosay\";\n\n  std::vector<uint8_t> build_id_data(kBuildID, kBuildID + 53);\n\n  auto module_writer = std::make_unique<MinidumpModuleWriter>();\n  module_writer->SetName(kModuleName);\n\n  auto codeview_build_id_writer =\n      std::make_unique<MinidumpModuleCodeViewRecordBuildIDWriter>();\n  codeview_build_id_writer->SetBuildID(build_id_data);\n  module_writer->SetCodeViewRecord(std::move(codeview_build_id_writer));\n\n  module_list_writer->AddModule(std::move(module_writer));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));\n\n  const MINIDUMP_MODULE_LIST* module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetModuleListStream(string_file.string(), &module_list));\n\n  EXPECT_EQ(module_list->NumberOfModules, 1u);\n\n  MINIDUMP_MODULE expected = {};\n\n  ASSERT_NO_FATAL_FAILURE(ExpectModuleWithBuildIDCv(&expected,\n                                                    &module_list->Modules[0],\n                                                    string_file.string(),\n                                                    kModuleName,\n                                                    build_id_data));\n}\n\nTEST(MinidumpModuleWriter, ThreeModules) {\n  // As good exercise, this test uses three modules, one with a PDB 7.0 link as\n  // its CodeView record, one with no CodeView record, and one with a PDB 2.0\n  // link as its CodeView record.\n  MinidumpFileWriter minidump_file_writer;\n  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();\n\n  static constexpr char kModuleName0[] = \"main\";\n  constexpr uint64_t kModuleBase0 = 0x100101000;\n  constexpr uint32_t kModuleSize0 = 0xf000;\n  static constexpr char kPDBName0[] = \"main\";\n  static constexpr uint8_t kPDBUUIDBytes0[16] = {0xaa,\n                                                 0xbb,\n                                                 0xcc,\n                                                 0xdd,\n                                                 0xee,\n                                                 0xff,\n                                                 0x00,\n                                                 0x11,\n                                                 0x22,\n                                                 0x33,\n                                                 0x44,\n                                                 0x55,\n                                                 0x66,\n                                                 0x77,\n                                                 0x88,\n                                                 0x99};\n  UUID pdb_uuid_0;\n  pdb_uuid_0.InitializeFromBytes(kPDBUUIDBytes0);\n  constexpr uint32_t kPDBAge0 = 0;\n\n  static constexpr char kModuleName1[] = \"ld.so\";\n  constexpr uint64_t kModuleBase1 = 0x200202000;\n  constexpr uint32_t kModuleSize1 = 0x1e000;\n\n  static constexpr char kModuleName2[] = \"libc.so\";\n  constexpr uint64_t kModuleBase2 = 0x300303000;\n  constexpr uint32_t kModuleSize2 = 0x2d000;\n  static constexpr char kPDBName2[] = \"libc.so\";\n  constexpr time_t kPDBTimestamp2 = 0x386d4380;\n  constexpr uint32_t kPDBAge2 = 2;\n\n  auto module_writer_0 = std::make_unique<MinidumpModuleWriter>();\n  module_writer_0->SetName(kModuleName0);\n  module_writer_0->SetImageBaseAddress(kModuleBase0);\n  module_writer_0->SetImageSize(kModuleSize0);\n\n  auto codeview_pdb70_writer_0 =\n      std::make_unique<MinidumpModuleCodeViewRecordPDB70Writer>();\n  codeview_pdb70_writer_0->SetPDBName(kPDBName0);\n  codeview_pdb70_writer_0->SetUUIDAndAge(pdb_uuid_0, kPDBAge0);\n  module_writer_0->SetCodeViewRecord(std::move(codeview_pdb70_writer_0));\n\n  module_list_writer->AddModule(std::move(module_writer_0));\n\n  auto module_writer_1 = std::make_unique<MinidumpModuleWriter>();\n  module_writer_1->SetName(kModuleName1);\n  module_writer_1->SetImageBaseAddress(kModuleBase1);\n  module_writer_1->SetImageSize(kModuleSize1);\n\n  module_list_writer->AddModule(std::move(module_writer_1));\n\n  auto module_writer_2 = std::make_unique<MinidumpModuleWriter>();\n  module_writer_2->SetName(kModuleName2);\n  module_writer_2->SetImageBaseAddress(kModuleBase2);\n  module_writer_2->SetImageSize(kModuleSize2);\n\n  auto codeview_pdb70_writer_2 =\n      std::make_unique<MinidumpModuleCodeViewRecordPDB20Writer>();\n  codeview_pdb70_writer_2->SetPDBName(kPDBName2);\n  codeview_pdb70_writer_2->SetTimestampAndAge(kPDBTimestamp2, kPDBAge2);\n  module_writer_2->SetCodeViewRecord(std::move(codeview_pdb70_writer_2));\n\n  module_list_writer->AddModule(std::move(module_writer_2));\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));\n\n  const MINIDUMP_MODULE_LIST* module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetModuleListStream(string_file.string(), &module_list));\n\n  EXPECT_EQ(module_list->NumberOfModules, 3u);\n\n  MINIDUMP_MODULE expected = {};\n\n  {\n    SCOPED_TRACE(\"module 0\");\n\n    expected.BaseOfImage = kModuleBase0;\n    expected.SizeOfImage = kModuleSize0;\n\n    ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,\n                                         &module_list->Modules[0],\n                                         string_file.string(),\n                                         kModuleName0,\n                                         kPDBName0,\n                                         &pdb_uuid_0,\n                                         0,\n                                         kPDBAge0,\n                                         nullptr,\n                                         0,\n                                         false));\n  }\n\n  {\n    SCOPED_TRACE(\"module 1\");\n\n    expected.BaseOfImage = kModuleBase1;\n    expected.SizeOfImage = kModuleSize1;\n\n    ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,\n                                         &module_list->Modules[1],\n                                         string_file.string(),\n                                         kModuleName1,\n                                         nullptr,\n                                         nullptr,\n                                         0,\n                                         0,\n                                         nullptr,\n                                         0,\n                                         false));\n  }\n\n  {\n    SCOPED_TRACE(\"module 2\");\n\n    expected.BaseOfImage = kModuleBase2;\n    expected.SizeOfImage = kModuleSize2;\n\n    ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected,\n                                         &module_list->Modules[2],\n                                         string_file.string(),\n                                         kModuleName2,\n                                         kPDBName2,\n                                         nullptr,\n                                         kPDBTimestamp2,\n                                         kPDBAge2,\n                                         nullptr,\n                                         0,\n                                         false));\n  }\n}\n\nvoid InitializeTestModuleSnapshotFromMinidumpModule(\n    TestModuleSnapshot* module_snapshot,\n    const MINIDUMP_MODULE& minidump_module,\n    const std::string& name,\n    const std::string& pdb_name,\n    const crashpad::UUID& uuid,\n    uint32_t age) {\n  module_snapshot->SetName(name);\n\n  module_snapshot->SetAddressAndSize(minidump_module.BaseOfImage,\n                                     minidump_module.SizeOfImage);\n  module_snapshot->SetTimestamp(minidump_module.TimeDateStamp);\n  module_snapshot->SetFileVersion(\n      minidump_module.VersionInfo.dwFileVersionMS >> 16,\n      minidump_module.VersionInfo.dwFileVersionMS & 0xffff,\n      minidump_module.VersionInfo.dwFileVersionLS >> 16,\n      minidump_module.VersionInfo.dwFileVersionLS & 0xffff);\n  module_snapshot->SetSourceVersion(\n      minidump_module.VersionInfo.dwProductVersionMS >> 16,\n      minidump_module.VersionInfo.dwProductVersionMS & 0xffff,\n      minidump_module.VersionInfo.dwProductVersionLS >> 16,\n      minidump_module.VersionInfo.dwProductVersionLS & 0xffff);\n\n  ModuleSnapshot::ModuleType module_type;\n  switch (minidump_module.VersionInfo.dwFileType) {\n    case VFT_APP:\n      module_type = ModuleSnapshot::kModuleTypeExecutable;\n      break;\n    case VFT_DLL:\n      module_type = ModuleSnapshot::kModuleTypeSharedLibrary;\n      break;\n    default:\n      module_type = ModuleSnapshot::kModuleTypeUnknown;\n      break;\n  }\n  module_snapshot->SetModuleType(module_type);\n\n  module_snapshot->SetUUIDAndAge(uuid, age);\n  module_snapshot->SetDebugFileName(pdb_name);\n}\n\nTEST(MinidumpModuleWriter, InitializeFromSnapshot) {\n  MINIDUMP_MODULE expect_modules[3] = {};\n  const char* module_paths[std::size(expect_modules)] = {};\n  const char* module_pdbs[std::size(expect_modules)] = {};\n  UUID uuids[std::size(expect_modules)] = {};\n  uint32_t ages[std::size(expect_modules)] = {};\n\n  expect_modules[0].BaseOfImage = 0x100101000;\n  expect_modules[0].SizeOfImage = 0xf000;\n  expect_modules[0].TimeDateStamp = 0x01234567;\n  expect_modules[0].VersionInfo.dwFileVersionMS = 0x00010002;\n  expect_modules[0].VersionInfo.dwFileVersionLS = 0x00030004;\n  expect_modules[0].VersionInfo.dwProductVersionMS = 0x00050006;\n  expect_modules[0].VersionInfo.dwProductVersionLS = 0x00070008;\n  expect_modules[0].VersionInfo.dwFileType = VFT_APP;\n  module_paths[0] = \"/usr/bin/true\";\n  module_pdbs[0] = \"true\";\n  static constexpr uint8_t kUUIDBytes0[16] = {0x00,\n                                              0x11,\n                                              0x22,\n                                              0x33,\n                                              0x44,\n                                              0x55,\n                                              0x66,\n                                              0x77,\n                                              0x88,\n                                              0x99,\n                                              0xaa,\n                                              0xbb,\n                                              0xcc,\n                                              0xdd,\n                                              0xee,\n                                              0xff};\n  uuids[0].InitializeFromBytes(kUUIDBytes0);\n  ages[0] = 10;\n\n  expect_modules[1].BaseOfImage = 0x200202000;\n  expect_modules[1].SizeOfImage = 0x1e1000;\n  expect_modules[1].TimeDateStamp = 0x89abcdef;\n  expect_modules[1].VersionInfo.dwFileVersionMS = 0x0009000a;\n  expect_modules[1].VersionInfo.dwFileVersionLS = 0x000b000c;\n  expect_modules[1].VersionInfo.dwProductVersionMS = 0x000d000e;\n  expect_modules[1].VersionInfo.dwProductVersionLS = 0x000f0000;\n  expect_modules[1].VersionInfo.dwFileType = VFT_DLL;\n  module_paths[1] = \"/usr/lib/libSystem.B.dylib\";\n  module_pdbs[1] = \"libSystem.B.dylib.pdb\";\n  static constexpr uint8_t kUUIDBytes1[16] = {0x00,\n                                              0x01,\n                                              0x02,\n                                              0x03,\n                                              0x04,\n                                              0x05,\n                                              0x06,\n                                              0x07,\n                                              0x08,\n                                              0x09,\n                                              0x0a,\n                                              0x0b,\n                                              0x0c,\n                                              0x0d,\n                                              0x0e,\n                                              0x0f};\n  uuids[1].InitializeFromBytes(kUUIDBytes1);\n  ages[1] = 20;\n\n  expect_modules[2].BaseOfImage = 0x300303000;\n  expect_modules[2].SizeOfImage = 0x2d000;\n  expect_modules[2].TimeDateStamp = 0x76543210;\n  expect_modules[2].VersionInfo.dwFileVersionMS = 0x11112222;\n  expect_modules[2].VersionInfo.dwFileVersionLS = 0x33334444;\n  expect_modules[2].VersionInfo.dwProductVersionMS = 0x9999aaaa;\n  expect_modules[2].VersionInfo.dwProductVersionLS = 0xbbbbcccc;\n  expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN;\n  module_paths[2] = \"/usr/lib/dyld\";\n  module_pdbs[2] = \"/usr/lib/dyld.pdb\";\n  static constexpr uint8_t kUUIDBytes2[16] = {0xff,\n                                              0xfe,\n                                              0xfd,\n                                              0xfc,\n                                              0xfb,\n                                              0xfa,\n                                              0xf9,\n                                              0xf8,\n                                              0xf7,\n                                              0xf6,\n                                              0xf5,\n                                              0xf4,\n                                              0xf3,\n                                              0xf2,\n                                              0xf1,\n                                              0xf0};\n  uuids[2].InitializeFromBytes(kUUIDBytes2);\n  ages[2] = 30;\n\n  std::vector<std::unique_ptr<TestModuleSnapshot>> module_snapshots_owner;\n  std::vector<const ModuleSnapshot*> module_snapshots;\n  for (size_t index = 0; index < std::size(expect_modules); ++index) {\n    module_snapshots_owner.push_back(std::make_unique<TestModuleSnapshot>());\n    TestModuleSnapshot* module_snapshot = module_snapshots_owner.back().get();\n    InitializeTestModuleSnapshotFromMinidumpModule(module_snapshot,\n                                                   expect_modules[index],\n                                                   module_paths[index],\n                                                   module_pdbs[index],\n                                                   uuids[index],\n                                                   ages[index]);\n    module_snapshots.push_back(module_snapshot);\n  }\n\n  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();\n  module_list_writer->InitializeFromSnapshot(module_snapshots);\n\n  MinidumpFileWriter minidump_file_writer;\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_MODULE_LIST* module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetModuleListStream(string_file.string(), &module_list));\n\n  ASSERT_EQ(module_list->NumberOfModules, 3u);\n\n  for (size_t index = 0; index < module_list->NumberOfModules; ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS, index));\n    ASSERT_NO_FATAL_FAILURE(ExpectModule(&expect_modules[index],\n                                         &module_list->Modules[index],\n                                         string_file.string(),\n                                         module_paths[index],\n                                         module_pdbs[index],\n                                         &uuids[index],\n                                         0,\n                                         ages[index],\n                                         nullptr,\n                                         0,\n                                         false));\n  }\n}\n\nTEST(MinidumpModuleWriterDeathTest, NoModuleName) {\n  MinidumpFileWriter minidump_file_writer;\n  auto module_list_writer = std::make_unique<MinidumpModuleListWriter>();\n  auto module_writer = std::make_unique<MinidumpModuleWriter>();\n  module_list_writer->AddModule(std::move(module_writer));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),\n                     \"name_\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_rva_list_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_rva_list_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nMinidumpRVAListWriter::MinidumpRVAListWriter()\n    : MinidumpWritable(),\n      rva_list_base_(new MinidumpRVAList()),\n      children_(),\n      child_rvas_() {\n}\n\nMinidumpRVAListWriter::~MinidumpRVAListWriter() {\n}\n\nvoid MinidumpRVAListWriter::AddChild(std::unique_ptr<MinidumpWritable> child) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  children_.push_back(std::move(child));\n}\n\nbool MinidumpRVAListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(child_rvas_.empty());\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  size_t child_count = children_.size();\n  if (!AssignIfInRange(&rva_list_base_->count, child_count)) {\n    LOG(ERROR) << \"child_count \" << child_count << \" out of range\";\n    return false;\n  }\n\n  child_rvas_.resize(child_count);\n  for (size_t index = 0; index < child_count; ++index) {\n    children_[index]->RegisterRVA(&child_rvas_[index]);\n  }\n\n  return true;\n}\n\nsize_t MinidumpRVAListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(*rva_list_base_) + children_.size() * sizeof(RVA);\n}\n\nstd::vector<MinidumpWritable*> MinidumpRVAListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& child : children_) {\n    children.push_back(child.get());\n  }\n\n  return children;\n}\n\nbool MinidumpRVAListWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n  DCHECK_EQ(children_.size(), child_rvas_.size());\n\n  WritableIoVec iov;\n  iov.iov_base = rva_list_base_.get();\n  iov.iov_len = sizeof(*rva_list_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  if (!child_rvas_.empty()) {\n    iov.iov_base = &child_rvas_[0];\n    iov.iov_len = child_rvas_.size() * sizeof(RVA);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_rva_list_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_\n#define CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief The writer for a MinidumpRVAList object in a minidump file,\n//!     containing a list of ::RVA pointers.\nclass MinidumpRVAListWriter : public MinidumpWritable {\n protected:\n  MinidumpRVAListWriter();\n\n  MinidumpRVAListWriter(const MinidumpRVAListWriter&) = delete;\n  MinidumpRVAListWriter& operator=(const MinidumpRVAListWriter&) = delete;\n\n  ~MinidumpRVAListWriter() override;\n\n  //! \\brief Adds an ::RVA referencing an MinidumpWritable to the\n  //!     MinidumpRVAList.\n  //!\n  //! This object takes ownership of \\a child and becomes its parent in the\n  //! overall tree of MinidumpWritable objects.\n  //!\n  //! To provide type-correctness, subclasses are expected to provide a public\n  //! method that accepts a `scoped_ptr`-wrapped argument of the proper\n  //! MinidumpWritable subclass, and call this method with that argument.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddChild(std::unique_ptr<MinidumpWritable> child);\n\n  //! \\brief Returns `true` if no child objects have been added by AddChild(),\n  //!     and `false` if child objects are present.\n  bool IsEmpty() const { return children_.empty(); }\n\n  //! \\brief Returns an object’s ::RVA objects referencing its children.\n  //!\n  //! \\note The returned vector will be empty until the object advances to\n  //!     #kStateFrozen or beyond.\n  const std::vector<RVA>& child_rvas() const { return child_rvas_; }\n\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  std::unique_ptr<MinidumpRVAList> rva_list_base_;\n  std::vector<std::unique_ptr<MinidumpWritable>> children_;\n  std::vector<RVA> child_rvas_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_rva_list_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_rva_list_writer.h\"\n\n#include <iterator>\n#include <utility>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/test/minidump_rva_list_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass TestMinidumpRVAListWriter final : public internal::MinidumpRVAListWriter {\n public:\n  TestMinidumpRVAListWriter() : MinidumpRVAListWriter() {}\n\n  TestMinidumpRVAListWriter(const TestMinidumpRVAListWriter&) = delete;\n  TestMinidumpRVAListWriter& operator=(const TestMinidumpRVAListWriter&) =\n      delete;\n\n  ~TestMinidumpRVAListWriter() override {}\n\n  void AddChild(uint32_t value) {\n    auto child = std::make_unique<TestUInt32MinidumpWritable>(value);\n    MinidumpRVAListWriter::AddChild(std::move(child));\n  }\n};\n\nTEST(MinidumpRVAListWriter, Empty) {\n  TestMinidumpRVAListWriter list_writer;\n\n  StringFile string_file;\n\n  ASSERT_TRUE(list_writer.WriteEverything(&string_file));\n  EXPECT_EQ(string_file.string().size(), sizeof(MinidumpRVAList));\n\n  const MinidumpRVAList* list = MinidumpRVAListAtStart(string_file.string(), 0);\n  ASSERT_TRUE(list);\n}\n\nTEST(MinidumpRVAListWriter, OneChild) {\n  TestMinidumpRVAListWriter list_writer;\n\n  constexpr uint32_t kValue = 0;\n  list_writer.AddChild(kValue);\n\n  StringFile string_file;\n\n  ASSERT_TRUE(list_writer.WriteEverything(&string_file));\n\n  const MinidumpRVAList* list = MinidumpRVAListAtStart(string_file.string(), 1);\n  ASSERT_TRUE(list);\n\n  const uint32_t* child = MinidumpWritableAtRVA<uint32_t>(\n      string_file.string(), list->children[0]);\n  ASSERT_TRUE(child);\n  EXPECT_EQ(*child, kValue);\n}\n\nTEST(MinidumpRVAListWriter, ThreeChildren) {\n  TestMinidumpRVAListWriter list_writer;\n\n  static constexpr uint32_t kValues[] = {0x80000000, 0x55555555, 0x66006600};\n\n  list_writer.AddChild(kValues[0]);\n  list_writer.AddChild(kValues[1]);\n  list_writer.AddChild(kValues[2]);\n\n  StringFile string_file;\n\n  ASSERT_TRUE(list_writer.WriteEverything(&string_file));\n\n  const MinidumpRVAList* list =\n      MinidumpRVAListAtStart(string_file.string(), std::size(kValues));\n  ASSERT_TRUE(list);\n\n  for (size_t index = 0; index < std::size(kValues); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS, index));\n\n    const uint32_t* child = MinidumpWritableAtRVA<uint32_t>(\n        string_file.string(), list->children[index]);\n    ASSERT_TRUE(child);\n    EXPECT_EQ(*child, kValues[index]);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_simple_string_dictionary_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_simple_string_dictionary_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpSimpleStringDictionaryEntryWriter::\n    MinidumpSimpleStringDictionaryEntryWriter()\n    : MinidumpWritable(), entry_(), key_(), value_() {\n}\n\nMinidumpSimpleStringDictionaryEntryWriter::\n    ~MinidumpSimpleStringDictionaryEntryWriter() {\n}\n\nconst MinidumpSimpleStringDictionaryEntry*\nMinidumpSimpleStringDictionaryEntryWriter::\n    GetMinidumpSimpleStringDictionaryEntry() const {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return &entry_;\n}\n\nvoid MinidumpSimpleStringDictionaryEntryWriter::SetKeyValue(\n    const std::string& key,\n    const std::string& value) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  key_.SetUTF8(key);\n  value_.SetUTF8(value);\n}\n\nbool MinidumpSimpleStringDictionaryEntryWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  key_.RegisterRVA(&entry_.key);\n  value_.RegisterRVA(&entry_.value);\n\n  return true;\n}\n\nsize_t MinidumpSimpleStringDictionaryEntryWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // This object doesn’t directly write anything itself. Its\n  // MinidumpSimpleStringDictionaryEntry is written by its parent as part of a\n  // MinidumpSimpleStringDictionary, and its children are responsible for\n  // writing themselves.\n  return 0;\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpSimpleStringDictionaryEntryWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children(1, &key_);\n  children.push_back(&value_);\n  return children;\n}\n\nbool MinidumpSimpleStringDictionaryEntryWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  // This object doesn’t directly write anything itself. Its\n  // MinidumpSimpleStringDictionaryEntry is written by its parent as part of a\n  // MinidumpSimpleStringDictionary, and its children are responsible for\n  // writing themselves.\n  return true;\n}\n\nMinidumpSimpleStringDictionaryWriter::MinidumpSimpleStringDictionaryWriter()\n    : MinidumpWritable(),\n      entries_(),\n      simple_string_dictionary_base_(new MinidumpSimpleStringDictionary()) {\n}\n\nMinidumpSimpleStringDictionaryWriter::~MinidumpSimpleStringDictionaryWriter() {\n  for (auto& item : entries_)\n    delete item.second;\n}\n\nvoid MinidumpSimpleStringDictionaryWriter::InitializeFromMap(\n    const std::map<std::string, std::string>& map) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(entries_.empty());\n\n  for (const auto& iterator : map) {\n    auto entry = std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n    entry->SetKeyValue(iterator.first, iterator.second);\n    AddEntry(std::move(entry));\n  }\n}\n\nvoid MinidumpSimpleStringDictionaryWriter::AddEntry(\n    std::unique_ptr<MinidumpSimpleStringDictionaryEntryWriter> entry) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  const std::string& key = entry->Key();\n  auto iterator = entries_.find(key);\n  if (iterator != entries_.end()) {\n    delete iterator->second;\n    iterator->second = entry.release();\n  } else {\n    entries_[key] = entry.release();\n  }\n}\n\nbool MinidumpSimpleStringDictionaryWriter::IsUseful() const {\n  return !entries_.empty();\n}\n\nbool MinidumpSimpleStringDictionaryWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  size_t entry_count = entries_.size();\n  if (!AssignIfInRange(&simple_string_dictionary_base_->count, entry_count)) {\n    LOG(ERROR) << \"entry_count \" << entry_count << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpSimpleStringDictionaryWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(*simple_string_dictionary_base_) +\n         entries_.size() * sizeof(MinidumpSimpleStringDictionaryEntry);\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpSimpleStringDictionaryWriter::Children() {\n  DCHECK_GE(state(), kStateMutable);\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& key_entry : entries_) {\n    children.push_back(key_entry.second);\n  }\n\n  return children;\n}\n\nbool MinidumpSimpleStringDictionaryWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_GE(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = simple_string_dictionary_base_.get();\n  iov.iov_len = sizeof(*simple_string_dictionary_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  for (const auto& key_entry : entries_) {\n    iov.iov_base = key_entry.second->GetMinidumpSimpleStringDictionaryEntry();\n    iov.iov_len = sizeof(MinidumpSimpleStringDictionaryEntry);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_simple_string_dictionary_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_\n\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_string_writer.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\n//! \\brief The writer for a MinidumpSimpleStringDictionaryEntry object in a\n//!     minidump file.\n//!\n//! Because MinidumpSimpleStringDictionaryEntry objects only appear as elements\n//! of MinidumpSimpleStringDictionary objects, this class does not write any\n//! data on its own. It makes its MinidumpSimpleStringDictionaryEntry data\n//! available to its MinidumpSimpleStringDictionaryWriter parent, which writes\n//! it as part of a MinidumpSimpleStringDictionary.\nclass MinidumpSimpleStringDictionaryEntryWriter final\n    : public internal::MinidumpWritable {\n public:\n  MinidumpSimpleStringDictionaryEntryWriter();\n\n  MinidumpSimpleStringDictionaryEntryWriter(\n      const MinidumpSimpleStringDictionaryEntryWriter&) = delete;\n  MinidumpSimpleStringDictionaryEntryWriter& operator=(\n      const MinidumpSimpleStringDictionaryEntryWriter&) = delete;\n\n  ~MinidumpSimpleStringDictionaryEntryWriter() override;\n\n  //! \\brief Returns a MinidumpSimpleStringDictionaryEntry referencing this\n  //!     object’s data.\n  //!\n  //! This method is expected to be called by a\n  //! MinidumpSimpleStringDictionaryWriter in order to obtain a\n  //! MinidumpSimpleStringDictionaryEntry to include in its list.\n  //!\n  //! \\note Valid in #kStateWritable.\n  const MinidumpSimpleStringDictionaryEntry*\n  GetMinidumpSimpleStringDictionaryEntry() const;\n\n  //! \\brief Sets the strings to be written as the entry object’s key and value.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetKeyValue(const std::string& key, const std::string& value);\n\n  //! \\brief Retrieves the key to be written.\n  //!\n  //! \\note Valid in any state.\n  const std::string& Key() const { return key_.UTF8(); }\n\n protected:\n  // MinidumpWritable:\n\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  struct MinidumpSimpleStringDictionaryEntry entry_;\n  internal::MinidumpUTF8StringWriter key_;\n  internal::MinidumpUTF8StringWriter value_;\n};\n\n//! \\brief The writer for a MinidumpSimpleStringDictionary object in a minidump\n//!     file, containing a list of MinidumpSimpleStringDictionaryEntry objects.\n//!\n//! Because this class writes a representatin of a dictionary, the order of\n//! entries is insignificant. Entries may be written in any order.\nclass MinidumpSimpleStringDictionaryWriter final\n    : public internal::MinidumpWritable {\n public:\n  MinidumpSimpleStringDictionaryWriter();\n\n  MinidumpSimpleStringDictionaryWriter(\n      const MinidumpSimpleStringDictionaryWriter&) = delete;\n  MinidumpSimpleStringDictionaryWriter& operator=(\n      const MinidumpSimpleStringDictionaryWriter&) = delete;\n\n  ~MinidumpSimpleStringDictionaryWriter() override;\n\n  //! \\brief Adds an initialized MinidumpSimpleStringDictionaryEntryWriter for\n  //!     each key-value pair in \\a map to the MinidumpSimpleStringDictionary.\n  //!\n  //! \\param[in] map The map to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromMap(const std::map<std::string, std::string>& map);\n\n  //! \\brief Adds a MinidumpSimpleStringDictionaryEntryWriter to the\n  //!     MinidumpSimpleStringDictionary.\n  //!\n  //! This object takes ownership of \\a entry and becomes its parent in the\n  //! overall tree of internal::MinidumpWritable objects.\n  //!\n  //! If the key contained in \\a entry duplicates the key of an entry already\n  //! present in the MinidumpSimpleStringDictionary, the new \\a entry will\n  //! replace the previous one.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddEntry(\n      std::unique_ptr<MinidumpSimpleStringDictionaryEntryWriter> entry);\n\n  //! \\brief Determines whether the object is useful.\n  //!\n  //! A useful object is one that carries data that makes a meaningful\n  //! contribution to a minidump file. An object carrying entries would be\n  //! considered useful.\n  //!\n  //! \\return `true` if the object is useful, `false` otherwise.\n  bool IsUseful() const;\n\n protected:\n  // MinidumpWritable:\n\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  // This object owns the MinidumpSimpleStringDictionaryEntryWriter objects.\n  std::map<std::string, MinidumpSimpleStringDictionaryEntryWriter*> entries_;\n\n  std::unique_ptr<MinidumpSimpleStringDictionary>\n      simple_string_dictionary_base_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_simple_string_dictionary_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_simple_string_dictionary_writer.h\"\n\n#include <stdint.h>\n\n#include <utility>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconst MinidumpSimpleStringDictionary* MinidumpSimpleStringDictionaryAtStart(\n    const std::string& file_contents,\n    size_t count) {\n  MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;\n  location_descriptor.DataSize = static_cast<uint32_t>(\n      sizeof(MinidumpSimpleStringDictionary) +\n      count * sizeof(MinidumpSimpleStringDictionaryEntry));\n  location_descriptor.Rva = 0;\n  return MinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n      file_contents, location_descriptor);\n}\n\nTEST(MinidumpSimpleStringDictionaryWriter, EmptySimpleStringDictionary) {\n  StringFile string_file;\n\n  MinidumpSimpleStringDictionaryWriter dictionary_writer;\n\n  EXPECT_FALSE(dictionary_writer.IsUseful());\n\n  EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpSimpleStringDictionary));\n\n  const MinidumpSimpleStringDictionary* dictionary =\n      MinidumpSimpleStringDictionaryAtStart(string_file.string(), 0);\n  ASSERT_TRUE(dictionary);\n  EXPECT_EQ(dictionary->count, 0u);\n}\n\nTEST(MinidumpSimpleStringDictionaryWriter, EmptyKeyValue) {\n  StringFile string_file;\n\n  MinidumpSimpleStringDictionaryWriter dictionary_writer;\n  auto entry_writer =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  dictionary_writer.AddEntry(std::move(entry_writer));\n\n  EXPECT_TRUE(dictionary_writer.IsUseful());\n\n  EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpSimpleStringDictionary) +\n                sizeof(MinidumpSimpleStringDictionaryEntry) +\n                2 * sizeof(MinidumpUTF8String) + 1 + 3 + 1);  // 3 for padding\n\n  const MinidumpSimpleStringDictionary* dictionary =\n      MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1);\n  ASSERT_TRUE(dictionary);\n  EXPECT_EQ(dictionary->count, 1u);\n  EXPECT_EQ(dictionary->entries[0].key, 12u);\n  EXPECT_EQ(dictionary->entries[0].value, 20u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].key),\n            \"\");\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].value),\n            \"\");\n}\n\nTEST(MinidumpSimpleStringDictionaryWriter, OneKeyValue) {\n  StringFile string_file;\n\n  char kKey[] = \"key\";\n  char kValue[] = \"value\";\n\n  MinidumpSimpleStringDictionaryWriter dictionary_writer;\n  auto entry_writer =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  entry_writer->SetKeyValue(kKey, kValue);\n  dictionary_writer.AddEntry(std::move(entry_writer));\n\n  EXPECT_TRUE(dictionary_writer.IsUseful());\n\n  EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpSimpleStringDictionary) +\n                sizeof(MinidumpSimpleStringDictionaryEntry) +\n                2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + sizeof(kValue));\n\n  const MinidumpSimpleStringDictionary* dictionary =\n      MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1);\n  ASSERT_TRUE(dictionary);\n  EXPECT_EQ(dictionary->count, 1u);\n  EXPECT_EQ(dictionary->entries[0].key, 12u);\n  EXPECT_EQ(dictionary->entries[0].value, 20u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].key),\n            kKey);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].value),\n            kValue);\n}\n\nTEST(MinidumpSimpleStringDictionaryWriter, ThreeKeysValues) {\n  StringFile string_file;\n\n  char kKey0[] = \"m0\";\n  char kValue0[] = \"value0\";\n  char kKey1[] = \"zzz1\";\n  char kValue1[] = \"v1\";\n  char kKey2[] = \"aa2\";\n  char kValue2[] = \"val2\";\n\n  MinidumpSimpleStringDictionaryWriter dictionary_writer;\n  auto entry_writer_0 =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  entry_writer_0->SetKeyValue(kKey0, kValue0);\n  dictionary_writer.AddEntry(std::move(entry_writer_0));\n  auto entry_writer_1 =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  entry_writer_1->SetKeyValue(kKey1, kValue1);\n  dictionary_writer.AddEntry(std::move(entry_writer_1));\n  auto entry_writer_2 =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  entry_writer_2->SetKeyValue(kKey2, kValue2);\n  dictionary_writer.AddEntry(std::move(entry_writer_2));\n\n  EXPECT_TRUE(dictionary_writer.IsUseful());\n\n  EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpSimpleStringDictionary) +\n                3 * sizeof(MinidumpSimpleStringDictionaryEntry) +\n                6 * sizeof(MinidumpUTF8String) + sizeof(kKey2) +\n                sizeof(kValue2) + 3 + sizeof(kKey0) + 1 + sizeof(kValue0) + 1 +\n                sizeof(kKey1) + 3 + sizeof(kValue1));\n\n  const MinidumpSimpleStringDictionary* dictionary =\n      MinidumpSimpleStringDictionaryAtStart(string_file.string(), 3);\n  ASSERT_TRUE(dictionary);\n  EXPECT_EQ(dictionary->count, 3u);\n  EXPECT_EQ(dictionary->entries[0].key, 28u);\n  EXPECT_EQ(dictionary->entries[0].value, 36u);\n  EXPECT_EQ(dictionary->entries[1].key, 48u);\n  EXPECT_EQ(dictionary->entries[1].value, 56u);\n  EXPECT_EQ(dictionary->entries[2].key, 68u);\n  EXPECT_EQ(dictionary->entries[2].value, 80u);\n\n  // The entries don’t appear in the order they were added. The current\n  // implementation uses a std::map and sorts keys, so the entires appear in\n  // alphabetical order. However, this is an implementation detail, and it’s OK\n  // if the writer stops sorting in this order. Testing for a specific order is\n  // just the easiest way to write this test while the writer will output things\n  // in a known order.\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].key),\n            kKey2);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].value),\n            kValue2);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[1].key),\n            kKey0);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[1].value),\n            kValue0);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[2].key),\n            kKey1);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[2].value),\n            kValue1);\n}\n\nTEST(MinidumpSimpleStringDictionaryWriter, DuplicateKeyValue) {\n  StringFile string_file;\n\n  char kKey[] = \"key\";\n  char kValue0[] = \"fake_value\";\n  char kValue1[] = \"value\";\n\n  MinidumpSimpleStringDictionaryWriter dictionary_writer;\n  auto entry_writer_0 =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  entry_writer_0->SetKeyValue(kKey, kValue0);\n  dictionary_writer.AddEntry(std::move(entry_writer_0));\n  auto entry_writer_1 =\n      std::make_unique<MinidumpSimpleStringDictionaryEntryWriter>();\n  entry_writer_1->SetKeyValue(kKey, kValue1);\n  dictionary_writer.AddEntry(std::move(entry_writer_1));\n\n  EXPECT_TRUE(dictionary_writer.IsUseful());\n\n  EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file));\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MinidumpSimpleStringDictionary) +\n                sizeof(MinidumpSimpleStringDictionaryEntry) +\n                2 * sizeof(MinidumpUTF8String) + sizeof(kKey) +\n                sizeof(kValue1));\n\n  const MinidumpSimpleStringDictionary* dictionary =\n      MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1);\n  ASSERT_TRUE(dictionary);\n  EXPECT_EQ(dictionary->count, 1u);\n  EXPECT_EQ(dictionary->entries[0].key, 12u);\n  EXPECT_EQ(dictionary->entries[0].value, 20u);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].key),\n            kKey);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].value),\n            kValue1);\n}\n\nTEST(MinidumpSimpleStringDictionaryWriter, InitializeFromMap) {\n  char kKey0[] = \"Dictionaries\";\n  char kValue0[] = \"USEFUL*\";\n  char kKey1[] = \"#1 Key!\";\n  char kValue1[] = \"\";\n  char kKey2[] = \"key two\";\n  char kValue2[] = \"value two\";\n\n  std::map<std::string, std::string> map;\n  map[kKey0] = kValue0;\n  map[kKey1] = kValue1;\n  map[kKey2] = kValue2;\n\n  MinidumpSimpleStringDictionaryWriter dictionary_writer;\n  dictionary_writer.InitializeFromMap(map);\n\n  EXPECT_TRUE(dictionary_writer.IsUseful());\n\n  StringFile string_file;\n  ASSERT_TRUE(dictionary_writer.WriteEverything(&string_file));\n\n  const MinidumpSimpleStringDictionary* dictionary =\n      MinidumpSimpleStringDictionaryAtStart(string_file.string(), map.size());\n  ASSERT_TRUE(dictionary);\n  ASSERT_EQ(dictionary->count, 3u);\n\n  // The entries don’t appear in the order they were added. The current\n  // implementation uses a std::map and sorts keys, so the entires appear in\n  // alphabetical order. However, this is an implementation detail, and it’s OK\n  // if the writer stops sorting in this order. Testing for a specific order is\n  // just the easiest way to write this test while the writer will output things\n  // in a known order.\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].key),\n            kKey1);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[0].value),\n            kValue1);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[1].key),\n            kKey0);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[1].value),\n            kValue0);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[2].key),\n            kKey2);\n  EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(),\n                                            dictionary->entries[2].value),\n            kValue2);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_stream_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_stream_writer.h\"\n\n#include \"base/check_op.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nMinidumpStreamWriter::~MinidumpStreamWriter() {\n}\n\nconst MINIDUMP_DIRECTORY* MinidumpStreamWriter::DirectoryListEntry() const {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return &directory_list_entry_;\n}\n\nMinidumpStreamWriter::MinidumpStreamWriter()\n    : MinidumpWritable(), directory_list_entry_() {\n}\n\nbool MinidumpStreamWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  directory_list_entry_.StreamType = StreamType();\n  RegisterLocationDescriptor(&directory_list_entry_.Location);\n\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_stream_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STREAM_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_STREAM_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief The base class for all second-level objects (“streams”) in a minidump\n//!     file.\n//!\n//! Instances of subclasses of this class are children of the root-level\n//! MinidumpFileWriter object.\nclass MinidumpStreamWriter : public MinidumpWritable {\n public:\n  MinidumpStreamWriter(const MinidumpStreamWriter&) = delete;\n  MinidumpStreamWriter& operator=(const MinidumpStreamWriter&) = delete;\n\n  ~MinidumpStreamWriter() override;\n\n  //! \\brief Returns an object’s stream type.\n  //!\n  //! \\note Valid in any state.\n  virtual MinidumpStreamType StreamType() const = 0;\n\n  //! \\brief Returns a MINIDUMP_DIRECTORY entry that serves as a pointer to this\n  //!     stream.\n  //!\n  //! This method is provided for MinidumpFileWriter, which calls it in order to\n  //! obtain the directory entry for a stream.\n  //!\n  //! \\note Valid only in #kStateWritable.\n  const MINIDUMP_DIRECTORY* DirectoryListEntry() const;\n\n protected:\n  MinidumpStreamWriter();\n\n  // MinidumpWritable:\n  bool Freeze() override;\n\n private:\n  MINIDUMP_DIRECTORY directory_list_entry_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_STREAM_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_string_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_string_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"minidump/minidump_writer_util.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\nnamespace internal {\n\ntemplate <typename Traits>\nMinidumpStringWriter<Traits>::MinidumpStringWriter()\n    : MinidumpWritable(), string_base_(new MinidumpStringType()), string_() {\n}\n\ntemplate <typename Traits>\nMinidumpStringWriter<Traits>::~MinidumpStringWriter() {\n}\n\ntemplate <typename Traits>\nbool MinidumpStringWriter<Traits>::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  size_t string_bytes = string_.size() * sizeof(string_[0]);\n  if (!AssignIfInRange(&string_base_->Length, string_bytes)) {\n    LOG(ERROR) << \"string_bytes \" << string_bytes << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\ntemplate <typename Traits>\nsize_t MinidumpStringWriter<Traits>::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // Include the NUL terminator.\n  return sizeof(*string_base_) + (string_.size() + 1) * sizeof(string_[0]);\n}\n\ntemplate <typename Traits>\nbool MinidumpStringWriter<Traits>::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  // The string’s length is stored in string_base_, and its data is stored in\n  // string_. Write them both.\n  WritableIoVec iov;\n  iov.iov_base = string_base_.get();\n  iov.iov_len = sizeof(*string_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  // Include the NUL terminator.\n  iov.iov_base = &string_[0];\n  iov.iov_len = (string_.size() + 1) * sizeof(string_[0]);\n  iovecs.push_back(iov);\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\n// Explicit template instantiation of the forms of MinidumpStringWriter<> used\n// as base classes.\ntemplate class MinidumpStringWriter<MinidumpStringWriterUTF16Traits>;\ntemplate class MinidumpStringWriter<MinidumpStringWriterUTF8Traits>;\n\nMinidumpUTF16StringWriter::~MinidumpUTF16StringWriter() {\n}\n\nvoid MinidumpUTF16StringWriter::SetUTF8(const std::string& string_utf8) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  set_string(MinidumpWriterUtil::ConvertUTF8ToUTF16(string_utf8));\n}\n\nMinidumpUTF8StringWriter::~MinidumpUTF8StringWriter() {\n}\n\ntemplate <typename MinidumpStringWriterType>\nMinidumpStringListWriter<MinidumpStringWriterType>::MinidumpStringListWriter()\n    : MinidumpRVAListWriter() {\n}\n\ntemplate <typename MinidumpStringWriterType>\nMinidumpStringListWriter<\n    MinidumpStringWriterType>::~MinidumpStringListWriter() {\n}\n\ntemplate <typename MinidumpStringWriterType>\nvoid MinidumpStringListWriter<MinidumpStringWriterType>::InitializeFromVector(\n    const std::vector<std::string>& vector) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(IsEmpty());\n\n  for (const std::string& string : vector) {\n    AddStringUTF8(string);\n  }\n}\n\ntemplate <typename MinidumpStringWriterType>\nvoid MinidumpStringListWriter<MinidumpStringWriterType>::AddStringUTF8(\n    const std::string& string_utf8) {\n  auto string_writer = std::make_unique<MinidumpStringWriterType>();\n  string_writer->SetUTF8(string_utf8);\n  AddChild(std::move(string_writer));\n}\n\ntemplate <typename MinidumpStringWriterType>\nbool MinidumpStringListWriter<MinidumpStringWriterType>::IsUseful() const {\n  return !IsEmpty();\n}\n\n// Explicit template instantiation of the forms of MinidumpStringListWriter<>\n// used as type aliases.\ntemplate class MinidumpStringListWriter<MinidumpUTF16StringWriter>;\ntemplate class MinidumpStringListWriter<MinidumpUTF8StringWriter>;\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_string_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_rva_list_writer.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\cond\n\nstruct MinidumpStringWriterUTF16Traits {\n  using StringType = std::u16string;\n  using MinidumpStringType = MINIDUMP_STRING;\n};\n\nstruct MinidumpStringWriterUTF8Traits {\n  using StringType = std::string;\n  using MinidumpStringType = MinidumpUTF8String;\n};\n\n//! \\endcond\n\n//! \\brief Writes a variable-length string to a minidump file in accordance with\n//!     the string type’s characteristics.\n//!\n//! MinidumpStringWriter objects should not be instantiated directly. To write\n//! strings to minidump file, use the MinidumpUTF16StringWriter and\n//! MinidumpUTF8StringWriter subclasses instead.\ntemplate <typename Traits>\nclass MinidumpStringWriter : public MinidumpWritable {\n public:\n  MinidumpStringWriter();\n\n  MinidumpStringWriter(const MinidumpStringWriter&) = delete;\n  MinidumpStringWriter& operator=(const MinidumpStringWriter&) = delete;\n\n  ~MinidumpStringWriter() override;\n\n protected:\n  using MinidumpStringType = typename Traits::MinidumpStringType;\n  using StringType = typename Traits::StringType;\n\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  //! \\brief Sets the string to be written.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void set_string(const StringType& string) { string_.assign(string); }\n\n  //! \\brief Retrieves the string to be written.\n  //!\n  //! \\note Valid in any state.\n  const StringType& string() const { return string_; }\n\n private:\n  std::unique_ptr<MinidumpStringType> string_base_;\n  StringType string_;\n};\n\n//! \\brief Writes a variable-length UTF-16-encoded MINIDUMP_STRING to a minidump\n//!     file.\n//!\n//! MinidumpUTF16StringWriter objects should not be instantiated directly\n//! outside of the MinidumpWritable family of classes.\nclass MinidumpUTF16StringWriter final\n    : public MinidumpStringWriter<MinidumpStringWriterUTF16Traits> {\n public:\n  MinidumpUTF16StringWriter() : MinidumpStringWriter() {}\n\n  MinidumpUTF16StringWriter(const MinidumpUTF16StringWriter&) = delete;\n  MinidumpUTF16StringWriter& operator=(const MinidumpUTF16StringWriter&) =\n      delete;\n\n  ~MinidumpUTF16StringWriter() override;\n\n  //! \\brief Converts a UTF-8 string to UTF-16 and sets it as the string to be\n  //!     written.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetUTF8(const std::string& string_utf8);\n};\n\n//! \\brief Writes a variable-length UTF-8-encoded MinidumpUTF8String to a\n//!     minidump file.\n//!\n//! MinidumpUTF8StringWriter objects should not be instantiated directly outside\n//! of the MinidumpWritable family of classes.\nclass MinidumpUTF8StringWriter final\n    : public MinidumpStringWriter<MinidumpStringWriterUTF8Traits> {\n public:\n  MinidumpUTF8StringWriter() : MinidumpStringWriter() {}\n\n  MinidumpUTF8StringWriter(const MinidumpUTF8StringWriter&) = delete;\n  MinidumpUTF8StringWriter& operator=(const MinidumpUTF8StringWriter&) = delete;\n\n  ~MinidumpUTF8StringWriter() override;\n\n  //! \\brief Sets the string to be written.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetUTF8(const std::string& string_utf8) { set_string(string_utf8); }\n\n  //! \\brief Retrieves the string to be written.\n  //!\n  //! \\note Valid in any state.\n  const std::string& UTF8() const { return string(); }\n};\n\n//! \\brief The writer for a MinidumpRVAList object in a minidump file,\n//!     containing a list of \\a MinidumpStringWriterType objects.\ntemplate <typename MinidumpStringWriterType>\nclass MinidumpStringListWriter final : public MinidumpRVAListWriter {\n public:\n  MinidumpStringListWriter();\n\n  MinidumpStringListWriter(const MinidumpStringListWriter&) = delete;\n  MinidumpStringListWriter& operator=(const MinidumpStringListWriter&) = delete;\n\n  ~MinidumpStringListWriter() override;\n\n  //! \\brief Adds a new \\a Traits::MinidumpStringWriterType for each element in\n  //!     \\a vector to the MinidumpRVAList.\n  //!\n  //! \\param[in] vector The vector to use as source data. Each string in the\n  //!     vector is treated as a UTF-8 string, and a new string writer will be\n  //!     created for each one and made a child of the MinidumpStringListWriter.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromVector(const std::vector<std::string>& vector);\n\n  //! \\brief Creates a new \\a Traits::MinidumpStringWriterType object and adds\n  //!     it to the MinidumpRVAList.\n  //!\n  //! This object creates a new string writer with string value \\a string_utf8,\n  //! takes ownership of it, and becomes its parent in the overall tree of\n  //! MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddStringUTF8(const std::string& string_utf8);\n\n  //! \\brief Determines whether the object is useful.\n  //!\n  //! A useful object is one that carries data that makes a meaningful\n  //! contribution to a minidump file. An object carrying entries would be\n  //! considered useful.\n  //!\n  //! \\return `true` if the object is useful, `false` otherwise.\n  bool IsUseful() const;\n};\n\n}  // namespace internal\n\nusing MinidumpUTF16StringListWriter = internal::MinidumpStringListWriter<\n    internal::MinidumpUTF16StringWriter>;\nusing MinidumpUTF8StringListWriter = internal::MinidumpStringListWriter<\n    internal::MinidumpUTF8StringWriter>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_string_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_string_writer.h\"\n\n#include <iterator>\n#include <string>\n#include <type_traits>\n\n#include \"base/format_macros.h\"\n#include \"base/notreached.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/test/minidump_rva_list_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass TestTypeNames {\n public:\n  template <typename T>\n  static std::string GetName(int) {\n    static_assert(std::is_same<T, RVA>() || std::is_same<T, RVA64>());\n    return std::is_same<T, RVA>() ? \"RVA\" : \"RVA64\";\n  }\n};\n\ntemplate <typename RVAType>\nclass MinidumpStringWriter : public ::testing::Test {};\n\nusing RVATypes = ::testing::Types<RVA, RVA64>;\nTYPED_TEST_SUITE(MinidumpStringWriter, RVATypes, TestTypeNames);\n\nTYPED_TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) {\n  StringFile string_file;\n\n  {\n    SCOPED_TRACE(\"unset\");\n    string_file.Reset();\n    crashpad::internal::MinidumpUTF16StringWriter string_writer;\n    EXPECT_TRUE(string_writer.WriteEverything(&string_file));\n    ASSERT_EQ(string_file.string().size(), 6u);\n\n    const MINIDUMP_STRING* minidump_string =\n        MinidumpStringAtRVA(string_file.string(), TypeParam(0));\n    EXPECT_TRUE(minidump_string);\n    EXPECT_EQ(MinidumpStringAtRVAAsString(string_file.string(), TypeParam(0)),\n              std::u16string());\n  }\n\n  static constexpr struct {\n    size_t input_length;\n    const char* input_string;\n    size_t output_length;\n    char16_t output_string[10];\n  } kTestData[] = {\n      {0, \"\", 0, {}},\n      {1, \"a\", 1, {'a'}},\n      {2, \"\\0b\", 2, {0, 'b'}},\n      {3, \"cde\", 3, {'c', 'd', 'e'}},\n      {9, \"Hi world!\", 9, {'H', 'i', ' ', 'w', 'o', 'r', 'l', 'd', '!'}},\n      {7, \"ret\\nurn\", 7, {'r', 'e', 't', '\\n', 'u', 'r', 'n'}},\n      {2, \"\\303\\251\", 1, {0x00e9}},  // é\n\n      // oóöőo\n      {8, \"o\\303\\263\\303\\266\\305\\221o\", 5, {'o', 0x00f3, 0x00f6, 0x151, 'o'}},\n      {4, \"\\360\\220\\204\\202\", 2, {0xd800, 0xdd02}},  // 𐄂 (non-BMP)\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %\" PRIuS \", input %s\", index, kTestData[index].input_string));\n\n    // Make sure that the expected output string with its NUL terminator fits in\n    // the space provided.\n    ASSERT_EQ(kTestData[index]\n                  .output_string[std::size(kTestData[index].output_string) - 1],\n              0);\n\n    string_file.Reset();\n    crashpad::internal::MinidumpUTF16StringWriter string_writer;\n    string_writer.SetUTF8(std::string(kTestData[index].input_string,\n                                      kTestData[index].input_length));\n    EXPECT_TRUE(string_writer.WriteEverything(&string_file));\n\n    const size_t expected_utf16_units_with_nul =\n        kTestData[index].output_length + 1;\n    [[maybe_unused]] MINIDUMP_STRING* tmp;\n    const size_t expected_utf16_bytes =\n        expected_utf16_units_with_nul * sizeof(tmp->Buffer[0]);\n    ASSERT_EQ(string_file.string().size(), sizeof(*tmp) + expected_utf16_bytes);\n\n    const MINIDUMP_STRING* minidump_string =\n        MinidumpStringAtRVA(string_file.string(), TypeParam(0));\n    EXPECT_TRUE(minidump_string);\n    std::u16string expect_string = std::u16string(\n        kTestData[index].output_string, kTestData[index].output_length);\n    EXPECT_EQ(MinidumpStringAtRVAAsString(string_file.string(), TypeParam(0)),\n              expect_string);\n  }\n}\n\nTYPED_TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) {\n  StringFile string_file;\n\n  static constexpr const char* kTestData[] = {\n      \"\\200\",  // continuation byte\n      \"\\300\",  // start byte followed by EOF\n      \"\\310\\177\",  // start byte without continuation\n      \"\\340\\200\",  // EOF in middle of 3-byte sequence\n      \"\\340\\200\\115\",  // invalid 3-byte sequence\n      \"\\303\\0\\251\",  // NUL in middle of valid sequence\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %\" PRIuS \", input %s\", index, kTestData[index]));\n    string_file.Reset();\n    crashpad::internal::MinidumpUTF16StringWriter string_writer;\n    string_writer.SetUTF8(kTestData[index]);\n    EXPECT_TRUE(string_writer.WriteEverything(&string_file));\n\n    // The requirements for conversion of invalid UTF-8 input are lax. Make sure\n    // that at least enough data was written for a string that has one unit and\n    // a NUL terminator, make sure that the length field matches the length of\n    // data written, and make sure that at least one U+FFFD replacement\n    // character was written.\n    const MINIDUMP_STRING* minidump_string =\n        MinidumpStringAtRVA(string_file.string(), TypeParam(0));\n    EXPECT_TRUE(minidump_string);\n    [[maybe_unused]] MINIDUMP_STRING* tmp;\n    EXPECT_EQ(\n        minidump_string->Length,\n        string_file.string().size() - sizeof(*tmp) - sizeof(tmp->Buffer[0]));\n    std::u16string output_string =\n        MinidumpStringAtRVAAsString(string_file.string(), TypeParam(0));\n    EXPECT_FALSE(output_string.empty());\n    EXPECT_NE(output_string.find(0xfffd), std::u16string::npos);\n  }\n}\n\nTYPED_TEST(MinidumpStringWriter, MinidumpUTF8StringWriter) {\n  StringFile string_file;\n\n  {\n    SCOPED_TRACE(\"unset\");\n    string_file.Reset();\n    crashpad::internal::MinidumpUTF8StringWriter string_writer;\n    EXPECT_TRUE(string_writer.WriteEverything(&string_file));\n    ASSERT_EQ(string_file.string().size(), 5u);\n\n    const MinidumpUTF8String* minidump_string =\n        MinidumpUTF8StringAtRVA(string_file.string(), TypeParam(0));\n    EXPECT_TRUE(minidump_string);\n    EXPECT_EQ(\n        MinidumpUTF8StringAtRVAAsString(string_file.string(), TypeParam(0)),\n        std::string());\n  }\n\n  static constexpr struct {\n    size_t length;\n    const char* string;\n  } kTestData[] = {\n      {0, \"\"},\n      {1, \"a\"},\n      {2, \"\\0b\"},\n      {3, \"cde\"},\n      {9, \"Hi world!\"},\n      {7, \"ret\\nurn\"},\n      {2, \"\\303\\251\"},  // é\n\n      // oóöőo\n      {8, \"o\\303\\263\\303\\266\\305\\221o\"},\n      {4, \"\\360\\220\\204\\202\"},  // 𐄂 (non-BMP)\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %\" PRIuS \", input %s\", index, kTestData[index].string));\n\n    string_file.Reset();\n    crashpad::internal::MinidumpUTF8StringWriter string_writer;\n    std::string test_string(kTestData[index].string, kTestData[index].length);\n    string_writer.SetUTF8(test_string);\n    EXPECT_EQ(string_writer.UTF8(), test_string);\n    EXPECT_TRUE(string_writer.WriteEverything(&string_file));\n\n    const size_t expected_utf8_bytes_with_nul = kTestData[index].length + 1;\n    ASSERT_EQ(string_file.string().size(),\n              sizeof(MinidumpUTF8String) + expected_utf8_bytes_with_nul);\n\n    const MinidumpUTF8String* minidump_string =\n        MinidumpUTF8StringAtRVA(string_file.string(), TypeParam(0));\n    EXPECT_TRUE(minidump_string);\n    EXPECT_EQ(\n        MinidumpUTF8StringAtRVAAsString(string_file.string(), TypeParam(0)),\n        test_string);\n  }\n}\n\nstruct MinidumpUTF16StringListWriterTraits {\n  using MinidumpStringListWriterType = MinidumpUTF16StringListWriter;\n  static std::u16string ExpectationForUTF8(const std::string& utf8) {\n    return base::UTF8ToUTF16(utf8);\n  }\n  static std::u16string ObservationAtRVA(const std::string& file_contents,\n                                         RVA rva) {\n    return MinidumpStringAtRVAAsString(file_contents, rva);\n  }\n};\n\nstruct MinidumpUTF8StringListWriterTraits {\n  using MinidumpStringListWriterType = MinidumpUTF8StringListWriter;\n  static std::string ExpectationForUTF8(const std::string& utf8) {\n    return utf8;\n  }\n  static std::string ObservationAtRVA(const std::string& file_contents,\n                                      RVA rva) {\n    return MinidumpUTF8StringAtRVAAsString(file_contents, rva);\n  }\n};\n\ntemplate <typename Traits>\nvoid MinidumpStringListTest() {\n  std::vector<std::string> strings;\n  strings.push_back(std::string(\"One\"));\n  strings.push_back(std::string());\n  strings.push_back(std::string(\"3\"));\n  strings.push_back(std::string(\"\\360\\237\\222\\251\"));\n\n  typename Traits::MinidumpStringListWriterType string_list_writer;\n  EXPECT_FALSE(string_list_writer.IsUseful());\n  string_list_writer.InitializeFromVector(strings);\n  EXPECT_TRUE(string_list_writer.IsUseful());\n\n  StringFile string_file;\n\n  ASSERT_TRUE(string_list_writer.WriteEverything(&string_file));\n\n  const MinidumpRVAList* list =\n      MinidumpRVAListAtStart(string_file.string(), strings.size());\n  ASSERT_TRUE(list);\n\n  for (size_t index = 0; index < strings.size(); ++index) {\n    EXPECT_EQ(\n        Traits::ObservationAtRVA(string_file.string(), list->children[index]),\n        Traits::ExpectationForUTF8(strings[index]));\n  }\n}\n\nTYPED_TEST(MinidumpStringWriter, MinidumpUTF16StringList) {\n  MinidumpStringListTest<MinidumpUTF16StringListWriterTraits>();\n}\n\nTYPED_TEST(MinidumpStringWriter, MinidumpUTF8StringList) {\n  MinidumpStringListTest<MinidumpUTF8StringListWriterTraits>();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_system_info_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_system_info_writer.h\"\n\n#include <string.h>\n\n#include <iterator>\n\n#include \"base/check_op.h\"\n#include \"base/notreached.h\"\n#include \"minidump/minidump_string_writer.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/misc/arraysize.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nuint64_t AMD64FeaturesFromSystemSnapshot(\n    const SystemSnapshot* system_snapshot) {\n#define ADD_FEATURE(minidump_bit) (UINT64_C(1) << (minidump_bit))\n\n  // Features for which no cpuid bits are present, but that always exist on\n  // x86_64. cmpxchg is supported on 486 and later.\n  uint64_t minidump_features = ADD_FEATURE(PF_COMPARE_EXCHANGE_DOUBLE);\n\n#define MAP_FEATURE(features, cpuid_bit, minidump_bit)                        \\\n  do {                                                                        \\\n    if ((features) & (implicit_cast<decltype(features)>(1) << (cpuid_bit))) { \\\n      minidump_features |= ADD_FEATURE(minidump_bit);                         \\\n    }                                                                         \\\n  } while (false)\n\n#define F_TSC 4\n#define F_PAE 6\n#define F_MMX 23\n#define F_SSE 25\n#define F_SSE2 26\n#define F_SSE3 32\n#define F_CX16 45\n#define F_XSAVE 58\n#define F_RDRAND 62\n\n  uint64_t cpuid_features = system_snapshot->CPUX86Features();\n\n  MAP_FEATURE(cpuid_features, F_TSC, PF_RDTSC_INSTRUCTION_AVAILABLE);\n  MAP_FEATURE(cpuid_features, F_PAE, PF_PAE_ENABLED);\n  MAP_FEATURE(cpuid_features, F_MMX, PF_MMX_INSTRUCTIONS_AVAILABLE);\n  MAP_FEATURE(cpuid_features, F_SSE, PF_XMMI_INSTRUCTIONS_AVAILABLE);\n  MAP_FEATURE(cpuid_features, F_SSE2, PF_XMMI64_INSTRUCTIONS_AVAILABLE);\n  MAP_FEATURE(cpuid_features, F_SSE3, PF_SSE3_INSTRUCTIONS_AVAILABLE);\n  MAP_FEATURE(cpuid_features, F_CX16, PF_COMPARE_EXCHANGE128);\n  MAP_FEATURE(cpuid_features, F_XSAVE, PF_XSAVE_ENABLED);\n  MAP_FEATURE(cpuid_features, F_RDRAND, PF_RDRAND_INSTRUCTION_AVAILABLE);\n\n#define FX_XD 20\n#define FX_RDTSCP 27\n#define FX_3DNOW 31\n\n  uint64_t extended_features = system_snapshot->CPUX86ExtendedFeatures();\n\n  MAP_FEATURE(extended_features, FX_RDTSCP, PF_RDTSCP_INSTRUCTION_AVAILABLE);\n  MAP_FEATURE(extended_features, FX_3DNOW, PF_3DNOW_INSTRUCTIONS_AVAILABLE);\n\n#define F7_FSGSBASE 0\n\n  uint32_t leaf7_features = system_snapshot->CPUX86Leaf7Features();\n\n  MAP_FEATURE(leaf7_features, F7_FSGSBASE, PF_RDWRFSGSBASE_AVAILABLE);\n\n  // This feature bit should be set if NX (XD, DEP) is enabled, not just if it’s\n  // available on the CPU as indicated by the FX_XD bit.\n  if (system_snapshot->NXEnabled()) {\n    minidump_features |= ADD_FEATURE(PF_NX_ENABLED);\n  }\n\n  if (system_snapshot->CPUX86SupportsDAZ()) {\n    minidump_features |= ADD_FEATURE(PF_SSE_DAZ_MODE_AVAILABLE);\n  }\n\n  // PF_SECOND_LEVEL_ADDRESS_TRANSLATION can’t be determined without consulting\n  // model-specific registers, a privileged operation. The exact use of\n  // PF_VIRT_FIRMWARE_ENABLED is unknown. PF_FASTFAIL_AVAILABLE is irrelevant\n  // outside of Windows.\n\n#undef MAP_FEATURE\n#undef ADD_FEATURE\n\n  return minidump_features;\n}\n\n}  // namespace\n\nMinidumpSystemInfoWriter::MinidumpSystemInfoWriter()\n    : MinidumpStreamWriter(), system_info_(), csd_version_() {\n  system_info_.ProcessorArchitecture = kMinidumpCPUArchitectureUnknown;\n}\n\nMinidumpSystemInfoWriter::~MinidumpSystemInfoWriter() {\n}\n\nvoid MinidumpSystemInfoWriter::InitializeFromSnapshot(\n    const SystemSnapshot* system_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!csd_version_);\n\n  MinidumpCPUArchitecture cpu_architecture;\n  switch (system_snapshot->GetCPUArchitecture()) {\n    case kCPUArchitectureX86:\n      cpu_architecture = kMinidumpCPUArchitectureX86;\n      break;\n    case kCPUArchitectureX86_64:\n      cpu_architecture = kMinidumpCPUArchitectureAMD64;\n      break;\n    case kCPUArchitectureARM:\n      cpu_architecture = kMinidumpCPUArchitectureARM;\n      break;\n    case kCPUArchitectureARM64:\n      cpu_architecture = kMinidumpCPUArchitectureARM64;\n      break;\n    case kCPUArchitectureRISCV64:\n      cpu_architecture = kMinidumpCPUArchitectureRISCV64Breakpad;\n      break;\n    default:\n      NOTREACHED();\n  }\n  SetCPUArchitecture(cpu_architecture);\n\n  uint32_t cpu_revision = system_snapshot->CPURevision();\n  SetCPULevelAndRevision((cpu_revision & 0xffff0000) >> 16,\n                         cpu_revision & 0x0000ffff);\n  SetCPUCount(system_snapshot->CPUCount());\n\n  if (cpu_architecture == kMinidumpCPUArchitectureX86) {\n    std::string cpu_vendor = system_snapshot->CPUVendor();\n    SetCPUX86VendorString(cpu_vendor);\n\n    // The minidump file format only has room for the bottom 32 bits of CPU\n    // features and extended CPU features.\n    SetCPUX86VersionAndFeatures(system_snapshot->CPUX86Signature(),\n                                system_snapshot->CPUX86Features() & 0xffffffff);\n\n    if (cpu_vendor == \"AuthenticAMD\" || cpu_vendor == \"HygonGenuine\") {\n      SetCPUX86AMDExtendedFeatures(\n          system_snapshot->CPUX86ExtendedFeatures() & 0xffffffff);\n    }\n  } else if (cpu_architecture == kMinidumpCPUArchitectureAMD64) {\n    SetCPUOtherFeatures(AMD64FeaturesFromSystemSnapshot(system_snapshot), 0);\n  }\n\n  MinidumpOS operating_system;\n  switch (system_snapshot->GetOperatingSystem()) {\n    case SystemSnapshot::kOperatingSystemMacOSX:\n      operating_system = kMinidumpOSMacOSX;\n      break;\n    case SystemSnapshot::kOperatingSystemWindows:\n      operating_system = kMinidumpOSWin32NT;\n      break;\n    case SystemSnapshot::kOperatingSystemLinux:\n      operating_system = kMinidumpOSLinux;\n      break;\n    case SystemSnapshot::kOperatingSystemAndroid:\n      operating_system = kMinidumpOSAndroid;\n      break;\n    case SystemSnapshot::kOperatingSystemFuchsia:\n      operating_system = kMinidumpOSFuchsia;\n      break;\n    case SystemSnapshot::kOperatingSystemIOS:\n      operating_system = kMinidumpOSIOS;\n      break;\n    default:\n      NOTREACHED();\n  }\n  SetOS(operating_system);\n\n  SetOSType(system_snapshot->OSServer() ? kMinidumpOSTypeServer\n                                        : kMinidumpOSTypeWorkstation);\n\n  int major;\n  int minor;\n  int bugfix;\n  std::string build;\n  system_snapshot->OSVersion(&major, &minor, &bugfix, &build);\n  SetOSVersion(major, minor, bugfix);\n  SetCSDVersion(build);\n}\n\nvoid MinidumpSystemInfoWriter::SetCSDVersion(const std::string& csd_version) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!csd_version_) {\n    csd_version_.reset(new internal::MinidumpUTF16StringWriter());\n  }\n\n  csd_version_->SetUTF8(csd_version);\n}\n\nvoid MinidumpSystemInfoWriter::SetCPUX86Vendor(uint32_t ebx,\n                                               uint32_t edx,\n                                               uint32_t ecx) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 ||\n         system_info_.ProcessorArchitecture ==\n             kMinidumpCPUArchitectureX86Win64);\n\n  static_assert(ArraySize(system_info_.Cpu.X86CpuInfo.VendorId) == 3,\n                \"VendorId must have 3 elements\");\n\n  system_info_.Cpu.X86CpuInfo.VendorId[0] = ebx;\n  system_info_.Cpu.X86CpuInfo.VendorId[1] = edx;\n  system_info_.Cpu.X86CpuInfo.VendorId[2] = ecx;\n}\n\nvoid MinidumpSystemInfoWriter::SetCPUX86VendorString(\n    const std::string& vendor) {\n  DCHECK_EQ(state(), kStateMutable);\n  CHECK_EQ(vendor.size(), sizeof(system_info_.Cpu.X86CpuInfo.VendorId));\n\n  uint32_t registers[3];\n  static_assert(\n      sizeof(registers) == sizeof(system_info_.Cpu.X86CpuInfo.VendorId),\n      \"VendorId sizes must be equal\");\n\n  for (size_t index = 0; index < std::size(registers); ++index) {\n    memcpy(&registers[index],\n           &vendor[index * sizeof(*registers)],\n           sizeof(*registers));\n  }\n\n  SetCPUX86Vendor(registers[0], registers[1], registers[2]);\n}\n\nvoid MinidumpSystemInfoWriter::SetCPUX86VersionAndFeatures(uint32_t version,\n                                                           uint32_t features) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 ||\n         system_info_.ProcessorArchitecture ==\n             kMinidumpCPUArchitectureX86Win64);\n\n  system_info_.Cpu.X86CpuInfo.VersionInformation = version;\n  system_info_.Cpu.X86CpuInfo.FeatureInformation = features;\n}\n\nvoid MinidumpSystemInfoWriter::SetCPUX86AMDExtendedFeatures(\n    uint32_t extended_features) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 ||\n         system_info_.ProcessorArchitecture ==\n             kMinidumpCPUArchitectureX86Win64);\n  DCHECK(system_info_.Cpu.X86CpuInfo.VendorId[0] == 'htuA' &&\n         system_info_.Cpu.X86CpuInfo.VendorId[1] == 'itne' &&\n         system_info_.Cpu.X86CpuInfo.VendorId[2] == 'DMAc');\n\n  system_info_.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = extended_features;\n}\n\nvoid MinidumpSystemInfoWriter::SetCPUOtherFeatures(uint64_t features_0,\n                                                   uint64_t features_1) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(system_info_.ProcessorArchitecture != kMinidumpCPUArchitectureX86 &&\n         system_info_.ProcessorArchitecture !=\n             kMinidumpCPUArchitectureX86Win64);\n\n  static_assert(ArraySize(system_info_.Cpu.OtherCpuInfo.ProcessorFeatures) == 2,\n                \"ProcessorFeatures must have 2 elements\");\n\n  system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[0] = features_0;\n  system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[1] = features_1;\n}\n\nbool MinidumpSystemInfoWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n  CHECK(csd_version_);\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  csd_version_->RegisterRVA(&system_info_.CSDVersionRva);\n\n  return true;\n}\n\nsize_t MinidumpSystemInfoWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(system_info_);\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpSystemInfoWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK(csd_version_);\n\n  std::vector<MinidumpWritable*> children(1, csd_version_.get());\n  return children;\n}\n\nbool MinidumpSystemInfoWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return file_writer->Write(&system_info_, sizeof(system_info_));\n}\n\nMinidumpStreamType MinidumpSystemInfoWriter::StreamType() const {\n  return kMinidumpStreamTypeSystemInfo;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_system_info_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\nclass SystemSnapshot;\n\nnamespace internal {\nclass MinidumpUTF16StringWriter;\n}  // namespace internal\n\n//! \\brief The writer for a MINIDUMP_SYSTEM_INFO stream in a minidump file.\nclass MinidumpSystemInfoWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpSystemInfoWriter();\n\n  MinidumpSystemInfoWriter(const MinidumpSystemInfoWriter&) = delete;\n  MinidumpSystemInfoWriter& operator=(const MinidumpSystemInfoWriter&) = delete;\n\n  ~MinidumpSystemInfoWriter() override;\n\n  //! \\brief Initializes MINIDUMP_SYSTEM_INFO based on \\a system_snapshot.\n  //!\n  //! \\param[in] system_snapshot The system snapshot to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const SystemSnapshot* system_snapshot);\n\n  //! \\brief Sets MINIDUMP_SYSTEM_INFO::ProcessorArchitecture.\n  void SetCPUArchitecture(MinidumpCPUArchitecture processor_architecture) {\n    system_info_.ProcessorArchitecture = processor_architecture;\n  }\n\n  //! \\brief Sets MINIDUMP_SYSTEM_INFO::ProcessorLevel and\n  //!     MINIDUMP_SYSTEM_INFO::ProcessorRevision.\n  void SetCPULevelAndRevision(uint16_t processor_level,\n                              uint16_t processor_revision) {\n    system_info_.ProcessorLevel = processor_level;\n    system_info_.ProcessorRevision = processor_revision;\n  }\n\n  //! \\brief Sets MINIDUMP_SYSTEM_INFO::NumberOfProcessors.\n  void SetCPUCount(uint8_t number_of_processors) {\n    system_info_.NumberOfProcessors = number_of_processors;\n  }\n\n  //! \\brief Sets MINIDUMP_SYSTEM_INFO::PlatformId.\n  void SetOS(MinidumpOS platform_id) { system_info_.PlatformId = platform_id; }\n\n  //! \\brief Sets MINIDUMP_SYSTEM_INFO::ProductType.\n  void SetOSType(MinidumpOSType product_type) {\n    system_info_.ProductType = product_type;\n  }\n\n  //! \\brief Sets MINIDUMP_SYSTEM_INFO::MajorVersion,\n  //!     MINIDUMP_SYSTEM_INFO::MinorVersion, and\n  //!     MINIDUMP_SYSTEM_INFO::BuildNumber.\n  void SetOSVersion(uint32_t major_version,\n                    uint32_t minor_version,\n                    uint32_t build_number) {\n    system_info_.MajorVersion = major_version;\n    system_info_.MinorVersion = minor_version;\n    system_info_.BuildNumber = build_number;\n  }\n\n  //! \\brief Arranges for MINIDUMP_SYSTEM_INFO::CSDVersionRva to point to a\n  //!     MINIDUMP_STRING containing the supplied string.\n  //!\n  //! This method must be called prior to Freeze(). A CSD version is required\n  //! in all MINIDUMP_SYSTEM_INFO streams. An empty string is an acceptable\n  //! value.\n  void SetCSDVersion(const std::string& csd_version);\n\n  //! \\brief Sets MINIDUMP_SYSTEM_INFO::SuiteMask.\n  void SetSuiteMask(uint16_t suite_mask) {\n    system_info_.SuiteMask = suite_mask;\n  }\n\n  //! \\brief Sets \\ref CPU_INFORMATION::VendorId\n  //!     \"MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId\".\n  //!\n  //! This is only valid if SetCPUArchitecture() has been used to set the CPU\n  //! architecture to #kMinidumpCPUArchitectureX86 or\n  //! #kMinidumpCPUArchitectureX86Win64.\n  //!\n  //! \\param[in] ebx The first 4 bytes of the CPU vendor string, the value\n  //!     reported in `cpuid 0` `ebx`.\n  //! \\param[in] edx The middle 4 bytes of the CPU vendor string, the value\n  //!     reported in `cpuid 0` `edx`.\n  //! \\param[in] ecx The last 4 bytes of the CPU vendor string, the value\n  //!     reported by `cpuid 0` `ecx`.\n  //!\n  //! \\note Do not call this method if SetCPUArchitecture() has been used to set\n  //!     the CPU architecture to #kMinidumpCPUArchitectureAMD64.\n  //!\n  //! \\sa SetCPUX86VendorString()\n  void SetCPUX86Vendor(uint32_t ebx, uint32_t edx, uint32_t ecx);\n\n  //! \\brief Sets \\ref CPU_INFORMATION::VendorId\n  //!     \"MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId\".\n  //!\n  //! This is only valid if SetCPUArchitecture() has been used to set the CPU\n  //! architecture to #kMinidumpCPUArchitectureX86 or\n  //! #kMinidumpCPUArchitectureX86Win64.\n  //!\n  //! \\param[in] vendor The entire CPU vendor string, which must be exactly 12\n  //!     bytes long.\n  //!\n  //! \\note Do not call this method if SetCPUArchitecture() has been used to set\n  //!     the CPU architecture to #kMinidumpCPUArchitectureAMD64.\n  //!\n  //! \\sa SetCPUX86Vendor()\n  void SetCPUX86VendorString(const std::string& vendor);\n\n  //! \\brief Sets \\ref CPU_INFORMATION::VersionInformation\n  //!     \"MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VersionInformation\" and\n  //!     \\ref CPU_INFORMATION::FeatureInformation\n  //!     \"MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::FeatureInformation\".\n  //!\n  //! This is only valid if SetCPUArchitecture() has been used to set the CPU\n  //! architecture to #kMinidumpCPUArchitectureX86 or\n  //! #kMinidumpCPUArchitectureX86Win64.\n  //!\n  //! \\note Do not call this method if SetCPUArchitecture() has been used to set\n  //!     the CPU architecture to #kMinidumpCPUArchitectureAMD64.\n  void SetCPUX86VersionAndFeatures(uint32_t version, uint32_t features);\n\n  //! \\brief Sets \\ref CPU_INFORMATION::AMDExtendedCpuFeatures\n  //!     \"MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::AMDExtendedCPUFeatures\".\n  //!\n  //! This is only valid if SetCPUArchitecture() has been used to set the CPU\n  //! architecture to #kMinidumpCPUArchitectureX86 or\n  //! #kMinidumpCPUArchitectureX86Win64, and if SetCPUX86Vendor() or\n  //! SetCPUX86VendorString() has been used to set the CPU vendor to\n  //! “AuthenticAMD”.\n  //!\n  //! \\note Do not call this method if SetCPUArchitecture() has been used to set\n  //!     the CPU architecture to #kMinidumpCPUArchitectureAMD64.\n  void SetCPUX86AMDExtendedFeatures(uint32_t extended_features);\n\n  //! \\brief Sets \\ref CPU_INFORMATION::ProcessorFeatures\n  //!     \"MINIDUMP_SYSTEM_INFO::Cpu::OtherCpuInfo::ProcessorFeatures\".\n  //!\n  //! This is only valid if SetCPUArchitecture() has been used to set the CPU\n  //! architecture to an architecture other than #kMinidumpCPUArchitectureX86\n  //! or #kMinidumpCPUArchitectureX86Win64.\n  //!\n  //! \\note This method may be called if SetCPUArchitecture() has been used to\n  //!     set the CPU architecture to #kMinidumpCPUArchitectureAMD64.\n  void SetCPUOtherFeatures(uint64_t features_0, uint64_t features_1);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  MINIDUMP_SYSTEM_INFO system_info_;\n  std::unique_ptr<internal::MinidumpUTF16StringWriter> csd_version_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_system_info_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_system_info_writer.h\"\n\n#include <string.h>\n\n#include <algorithm>\n#include <string>\n#include <utility>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_system_snapshot.h\"\n#include \"test/gtest_death.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid GetSystemInfoStream(const std::string& file_contents,\n                         size_t csd_version_length,\n                         const MINIDUMP_SYSTEM_INFO** system_info,\n                         const MINIDUMP_STRING** csd_version) {\n  // The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer.\n  [[maybe_unused]] MINIDUMP_STRING* tmp;\n  const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp->Buffer[0]);\n  const size_t kCSDVersionBytesWithNUL =\n      kCSDVersionBytes + sizeof(tmp->Buffer[0]);\n\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kSystemInfoStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kCSDVersionOffset =\n      kSystemInfoStreamOffset + sizeof(MINIDUMP_SYSTEM_INFO);\n  const size_t kFileSize =\n      kCSDVersionOffset + sizeof(MINIDUMP_STRING) + kCSDVersionBytesWithNUL;\n\n  ASSERT_EQ(file_contents.size(), kFileSize);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo);\n  EXPECT_EQ(directory[0].Location.Rva, kSystemInfoStreamOffset);\n\n  *system_info = MinidumpWritableAtLocationDescriptor<MINIDUMP_SYSTEM_INFO>(\n      file_contents, directory[0].Location);\n  ASSERT_TRUE(system_info);\n\n  EXPECT_EQ((*system_info)->CSDVersionRva, kCSDVersionOffset);\n\n  *csd_version =\n      MinidumpStringAtRVA(file_contents, (*system_info)->CSDVersionRva);\n  EXPECT_EQ((*csd_version)->Length, kCSDVersionBytes);\n}\n\nTEST(MinidumpSystemInfoWriter, Empty) {\n  MinidumpFileWriter minidump_file_writer;\n  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();\n\n  system_info_writer->SetCSDVersion(std::string());\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;\n  const MINIDUMP_STRING* csd_version = nullptr;\n\n  ASSERT_NO_FATAL_FAILURE(\n      GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version));\n\n  EXPECT_EQ(system_info->ProcessorArchitecture,\n            kMinidumpCPUArchitectureUnknown);\n  EXPECT_EQ(system_info->ProcessorLevel, 0u);\n  EXPECT_EQ(system_info->ProcessorRevision, 0u);\n  EXPECT_EQ(system_info->NumberOfProcessors, 0u);\n  EXPECT_EQ(system_info->ProductType, 0u);\n  EXPECT_EQ(system_info->MajorVersion, 0u);\n  EXPECT_EQ(system_info->MinorVersion, 0u);\n  EXPECT_EQ(system_info->BuildNumber, 0u);\n  EXPECT_EQ(system_info->PlatformId, 0u);\n  EXPECT_EQ(system_info->SuiteMask, 0u);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], 0u);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], 0u);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], 0u);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, 0u);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation, 0u);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures, 0u);\n\n  CPU_INFORMATION other_cpu_info;\n  memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info));\n  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0], 0u);\n  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1], 0u);\n\n  EXPECT_EQ(csd_version->Buffer[0], '\\0');\n}\n\nTEST(MinidumpSystemInfoWriter, X86_Win) {\n  MinidumpFileWriter minidump_file_writer;\n  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();\n\n  constexpr MinidumpCPUArchitecture kCPUArchitecture =\n      kMinidumpCPUArchitectureX86;\n  constexpr uint16_t kCPULevel = 0x0010;\n  constexpr uint16_t kCPURevision = 0x0602;\n  constexpr uint8_t kCPUCount = 1;\n  constexpr MinidumpOS kOS = kMinidumpOSWin32NT;\n  constexpr MinidumpOSType kOSType = kMinidumpOSTypeWorkstation;\n  constexpr uint32_t kOSVersionMajor = 6;\n  constexpr uint32_t kOSVersionMinor = 1;\n  constexpr uint32_t kOSVersionBuild = 7601;\n  static constexpr char kCSDVersion[] = \"Service Pack 1\";\n  constexpr uint16_t kSuiteMask = VER_SUITE_SINGLEUSERTS;\n  static constexpr char kCPUVendor[] = \"AuthenticAMD\";\n  constexpr uint32_t kCPUVersion = 0x00100f62;\n  constexpr uint32_t kCPUFeatures = 0x078bfbff;\n  constexpr uint32_t kAMDFeatures = 0xefd3fbff;\n\n  uint32_t cpu_vendor_registers[3];\n  ASSERT_EQ(strlen(kCPUVendor), sizeof(cpu_vendor_registers));\n  memcpy(cpu_vendor_registers, kCPUVendor, sizeof(cpu_vendor_registers));\n\n  system_info_writer->SetCPUArchitecture(kCPUArchitecture);\n  system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision);\n  system_info_writer->SetCPUCount(kCPUCount);\n  system_info_writer->SetOS(kOS);\n  system_info_writer->SetOSType(kMinidumpOSTypeWorkstation);\n  system_info_writer->SetOSVersion(\n      kOSVersionMajor, kOSVersionMinor, kOSVersionBuild);\n  system_info_writer->SetCSDVersion(kCSDVersion);\n  system_info_writer->SetSuiteMask(kSuiteMask);\n  system_info_writer->SetCPUX86VendorString(kCPUVendor);\n  system_info_writer->SetCPUX86VersionAndFeatures(kCPUVersion, kCPUFeatures);\n  system_info_writer->SetCPUX86AMDExtendedFeatures(kAMDFeatures);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;\n  const MINIDUMP_STRING* csd_version = nullptr;\n\n  ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(\n      string_file.string(), strlen(kCSDVersion), &system_info, &csd_version));\n\n  EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture);\n  EXPECT_EQ(system_info->ProcessorLevel, kCPULevel);\n  EXPECT_EQ(system_info->ProcessorRevision, kCPURevision);\n  EXPECT_EQ(system_info->NumberOfProcessors, kCPUCount);\n  EXPECT_EQ(system_info->ProductType, kOSType);\n  EXPECT_EQ(system_info->MajorVersion, kOSVersionMajor);\n  EXPECT_EQ(system_info->MinorVersion, kOSVersionMinor);\n  EXPECT_EQ(system_info->BuildNumber, kOSVersionBuild);\n  EXPECT_EQ(system_info->PlatformId, kOS);\n  EXPECT_EQ(system_info->SuiteMask, kSuiteMask);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], cpu_vendor_registers[0]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], cpu_vendor_registers[1]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], cpu_vendor_registers[2]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, kCPUVersion);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation, kCPUFeatures);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures, kAMDFeatures);\n\n  for (size_t index = 0; index < strlen(kCSDVersion); ++index) {\n    EXPECT_EQ(csd_version->Buffer[index], kCSDVersion[index]) << index;\n  }\n}\n\nTEST(MinidumpSystemInfoWriter, AMD64_Mac) {\n  MinidumpFileWriter minidump_file_writer;\n  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();\n\n  constexpr MinidumpCPUArchitecture kCPUArchitecture =\n      kMinidumpCPUArchitectureAMD64;\n  constexpr uint16_t kCPULevel = 0x0006;\n  constexpr uint16_t kCPURevision = 0x3a09;\n  constexpr uint8_t kCPUCount = 8;\n  constexpr MinidumpOS kOS = kMinidumpOSMacOSX;\n  constexpr MinidumpOSType kOSType = kMinidumpOSTypeWorkstation;\n  constexpr uint32_t kOSVersionMajor = 10;\n  constexpr uint32_t kOSVersionMinor = 9;\n  constexpr uint32_t kOSVersionBuild = 4;\n  static constexpr char kCSDVersion[] = \"13E28\";\n  static constexpr uint64_t kCPUFeatures[2] = {0x10427f4c, 0x00000000};\n\n  system_info_writer->SetCPUArchitecture(kCPUArchitecture);\n  system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision);\n  system_info_writer->SetCPUCount(kCPUCount);\n  system_info_writer->SetOS(kOS);\n  system_info_writer->SetOSType(kMinidumpOSTypeWorkstation);\n  system_info_writer->SetOSVersion(\n      kOSVersionMajor, kOSVersionMinor, kOSVersionBuild);\n  system_info_writer->SetCSDVersion(kCSDVersion);\n  system_info_writer->SetCPUOtherFeatures(kCPUFeatures[0], kCPUFeatures[1]);\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;\n  const MINIDUMP_STRING* csd_version;\n\n  ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(\n      string_file.string(), strlen(kCSDVersion), &system_info, &csd_version));\n\n  EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture);\n  EXPECT_EQ(system_info->ProcessorLevel, kCPULevel);\n  EXPECT_EQ(system_info->ProcessorRevision, kCPURevision);\n  EXPECT_EQ(system_info->NumberOfProcessors, kCPUCount);\n  EXPECT_EQ(system_info->ProductType, kOSType);\n  EXPECT_EQ(system_info->MajorVersion, kOSVersionMajor);\n  EXPECT_EQ(system_info->MinorVersion, kOSVersionMinor);\n  EXPECT_EQ(system_info->BuildNumber, kOSVersionBuild);\n  EXPECT_EQ(system_info->PlatformId, kOS);\n  EXPECT_EQ(system_info->SuiteMask, 0u);\n\n  CPU_INFORMATION other_cpu_info;\n  memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info));\n  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0], kCPUFeatures[0]);\n  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1], kCPUFeatures[1]);\n}\n\nTEST(MinidumpSystemInfoWriter, X86_CPUVendorFromRegisters) {\n  // MinidumpSystemInfoWriter.X86_Win already tested SetCPUX86VendorString().\n  // This test exercises SetCPUX86Vendor() to set the vendor from register\n  // values.\n  MinidumpFileWriter minidump_file_writer;\n  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();\n\n  constexpr MinidumpCPUArchitecture kCPUArchitecture =\n      kMinidumpCPUArchitectureX86;\n  static constexpr uint32_t kCPUVendor[] = {'uneG', 'Ieni', 'letn'};\n\n  system_info_writer->SetCPUArchitecture(kCPUArchitecture);\n  system_info_writer->SetCPUX86Vendor(\n      kCPUVendor[0], kCPUVendor[1], kCPUVendor[2]);\n  system_info_writer->SetCSDVersion(std::string());\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;\n  const MINIDUMP_STRING* csd_version;\n\n  ASSERT_NO_FATAL_FAILURE(\n      GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version));\n\n  EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture);\n  EXPECT_EQ(system_info->ProcessorLevel, 0u);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], kCPUVendor[0]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], kCPUVendor[1]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], kCPUVendor[2]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, 0u);\n}\n\nTEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) {\n  MINIDUMP_SYSTEM_INFO expect_system_info = {};\n\n  constexpr uint16_t kCPUFamily = 6;\n  constexpr uint8_t kCPUModel = 70;\n  constexpr uint8_t kCPUStepping = 1;\n\n  const uint8_t kCPUBasicFamily =\n      static_cast<uint8_t>(std::min(kCPUFamily, static_cast<uint16_t>(15)));\n  const uint8_t kCPUExtendedFamily = kCPUFamily - kCPUBasicFamily;\n\n  // These checks ensure that even if the constants above change, they represent\n  // something that can legitimately be encoded in the form used by cpuid 1 eax.\n  EXPECT_LE(kCPUFamily, 270);\n  EXPECT_LE(kCPUStepping, 15);\n  EXPECT_TRUE(kCPUBasicFamily == 6 || kCPUBasicFamily == 15 || kCPUModel <= 15);\n\n  constexpr uint8_t kCPUBasicModel = kCPUModel & 0xf;\n  constexpr uint8_t kCPUExtendedModel = kCPUModel >> 4;\n  const uint32_t kCPUSignature =\n      (kCPUExtendedFamily << 20) | (kCPUExtendedModel << 16) |\n      (kCPUBasicFamily << 8) | (kCPUBasicModel << 4) | kCPUStepping;\n  constexpr uint64_t kCPUX86Features = 0x7ffafbffbfebfbff;\n  expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86;\n  expect_system_info.ProcessorLevel = kCPUFamily;\n  expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping;\n  expect_system_info.NumberOfProcessors = 8;\n  expect_system_info.ProductType = kMinidumpOSTypeServer;\n  expect_system_info.MajorVersion = 10;\n  expect_system_info.MinorVersion = 9;\n  expect_system_info.BuildNumber = 5;\n  expect_system_info.PlatformId = kMinidumpOSMacOSX;\n  expect_system_info.SuiteMask = 0;\n  expect_system_info.Cpu.X86CpuInfo.VendorId[0] = 'uneG';\n  expect_system_info.Cpu.X86CpuInfo.VendorId[1] = 'Ieni';\n  expect_system_info.Cpu.X86CpuInfo.VendorId[2] = 'letn';\n  expect_system_info.Cpu.X86CpuInfo.VersionInformation = kCPUSignature;\n  expect_system_info.Cpu.X86CpuInfo.FeatureInformation =\n      kCPUX86Features & 0xffffffff;\n  static constexpr char kCPUVendor[] = \"GenuineIntel\";\n  static constexpr char kOSVersionBuild[] = \"13F34\";\n\n  TestSystemSnapshot system_snapshot;\n  system_snapshot.SetCPUArchitecture(kCPUArchitectureX86);\n  system_snapshot.SetCPURevision(\n      (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping);\n  system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors);\n  system_snapshot.SetCPUVendor(kCPUVendor);\n  system_snapshot.SetCPUX86Signature(kCPUSignature);\n  system_snapshot.SetCPUX86Features(kCPUX86Features);\n  system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);\n  system_snapshot.SetOSServer(true);\n  system_snapshot.SetOSVersion(expect_system_info.MajorVersion,\n                               expect_system_info.MinorVersion,\n                               expect_system_info.BuildNumber,\n                               kOSVersionBuild);\n\n  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();\n  system_info_writer->InitializeFromSnapshot(&system_snapshot);\n\n  MinidumpFileWriter minidump_file_writer;\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;\n  const MINIDUMP_STRING* csd_version = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(),\n                                              strlen(kOSVersionBuild),\n                                              &system_info,\n                                              &csd_version));\n\n  EXPECT_EQ(system_info->ProcessorArchitecture,\n            expect_system_info.ProcessorArchitecture);\n  EXPECT_EQ(system_info->ProcessorLevel, expect_system_info.ProcessorLevel);\n  EXPECT_EQ(system_info->ProcessorRevision,\n            expect_system_info.ProcessorRevision);\n  EXPECT_EQ(system_info->NumberOfProcessors,\n            expect_system_info.NumberOfProcessors);\n  EXPECT_EQ(system_info->ProductType, expect_system_info.ProductType);\n  EXPECT_EQ(system_info->MajorVersion, expect_system_info.MajorVersion);\n  EXPECT_EQ(system_info->MinorVersion, expect_system_info.MinorVersion);\n  EXPECT_EQ(system_info->BuildNumber, expect_system_info.BuildNumber);\n  EXPECT_EQ(system_info->PlatformId, expect_system_info.PlatformId);\n  EXPECT_EQ(system_info->SuiteMask, expect_system_info.SuiteMask);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0],\n            expect_system_info.Cpu.X86CpuInfo.VendorId[0]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1],\n            expect_system_info.Cpu.X86CpuInfo.VendorId[1]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2],\n            expect_system_info.Cpu.X86CpuInfo.VendorId[2]);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation,\n            expect_system_info.Cpu.X86CpuInfo.VersionInformation);\n  EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation,\n            expect_system_info.Cpu.X86CpuInfo.FeatureInformation);\n\n  for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) {\n    EXPECT_EQ(csd_version->Buffer[index], kOSVersionBuild[index]) << index;\n  }\n}\n\nTEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_AMD64) {\n  MINIDUMP_SYSTEM_INFO expect_system_info = {};\n\n  constexpr uint8_t kCPUFamily = 6;\n  constexpr uint8_t kCPUModel = 70;\n  constexpr uint8_t kCPUStepping = 1;\n  expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64;\n  expect_system_info.ProcessorLevel = kCPUFamily;\n  expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping;\n  expect_system_info.NumberOfProcessors = 8;\n  expect_system_info.ProductType = kMinidumpOSTypeServer;\n  expect_system_info.MajorVersion = 10;\n  expect_system_info.MinorVersion = 9;\n  expect_system_info.BuildNumber = 5;\n  expect_system_info.PlatformId = kMinidumpOSMacOSX;\n  expect_system_info.SuiteMask = 0;\n  expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0] =\n      (1 << PF_COMPARE_EXCHANGE_DOUBLE) |\n      (1 << PF_MMX_INSTRUCTIONS_AVAILABLE) |\n      (1 << PF_XMMI_INSTRUCTIONS_AVAILABLE) |\n      (1 << PF_RDTSC_INSTRUCTION_AVAILABLE) |\n      (1 << PF_PAE_ENABLED) |\n      (1 << PF_XMMI64_INSTRUCTIONS_AVAILABLE) |\n      (1 << PF_SSE_DAZ_MODE_AVAILABLE) |\n      (1 << PF_NX_ENABLED) |\n      (1 << PF_SSE3_INSTRUCTIONS_AVAILABLE) |\n      (1 << PF_COMPARE_EXCHANGE128) |\n      (1 << PF_XSAVE_ENABLED) |\n      (1 << PF_RDWRFSGSBASE_AVAILABLE) |\n      (1 << PF_RDRAND_INSTRUCTION_AVAILABLE) |\n      (UINT64_C(1) << PF_RDTSCP_INSTRUCTION_AVAILABLE);\n  expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0;\n  static constexpr char kOSVersionBuild[] = \"13F34\";\n\n  TestSystemSnapshot system_snapshot;\n  system_snapshot.SetCPUArchitecture(kCPUArchitectureX86_64);\n  system_snapshot.SetCPURevision(\n      (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping);\n  system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors);\n  system_snapshot.SetCPUX86Features(0x7ffafbffbfebfbff);\n  system_snapshot.SetCPUX86ExtendedFeatures(0x000000212c100900);\n  system_snapshot.SetCPUX86Leaf7Features(0x00002fbb);\n  system_snapshot.SetCPUX86SupportsDAZ(true);\n  system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX);\n  system_snapshot.SetOSServer(true);\n  system_snapshot.SetOSVersion(expect_system_info.MajorVersion,\n                               expect_system_info.MinorVersion,\n                               expect_system_info.BuildNumber,\n                               kOSVersionBuild);\n  system_snapshot.SetNXEnabled(true);\n\n  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();\n  system_info_writer->InitializeFromSnapshot(&system_snapshot);\n\n  MinidumpFileWriter minidump_file_writer;\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_SYSTEM_INFO* system_info = nullptr;\n  const MINIDUMP_STRING* csd_version = nullptr;\n  ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(),\n                                              strlen(kOSVersionBuild),\n                                              &system_info,\n                                              &csd_version));\n\n  EXPECT_EQ(system_info->ProcessorArchitecture,\n            expect_system_info.ProcessorArchitecture);\n  EXPECT_EQ(system_info->ProcessorLevel, expect_system_info.ProcessorLevel);\n  EXPECT_EQ(system_info->ProcessorRevision,\n            expect_system_info.ProcessorRevision);\n  EXPECT_EQ(system_info->NumberOfProcessors,\n            expect_system_info.NumberOfProcessors);\n  EXPECT_EQ(system_info->ProductType, expect_system_info.ProductType);\n  EXPECT_EQ(system_info->MajorVersion, expect_system_info.MajorVersion);\n  EXPECT_EQ(system_info->MinorVersion, expect_system_info.MinorVersion);\n  EXPECT_EQ(system_info->BuildNumber, expect_system_info.BuildNumber);\n  EXPECT_EQ(system_info->PlatformId, expect_system_info.PlatformId);\n  EXPECT_EQ(system_info->SuiteMask, expect_system_info.SuiteMask);\n\n  CPU_INFORMATION other_cpu_info;\n  memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info));\n  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0],\n            expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0]);\n  EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1],\n            expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1]);\n\n  for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) {\n    EXPECT_EQ(csd_version->Buffer[index], kOSVersionBuild[index]) << index;\n  }\n}\n\nTEST(MinidumpSystemInfoWriterDeathTest, NoCSDVersion) {\n  MinidumpFileWriter minidump_file_writer;\n  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));\n\n  StringFile string_file;\n  ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),\n                     \"csd_version_\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_thread_id_map.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_thread_id_map.h\"\n\n#include <limits>\n#include <set>\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"snapshot/thread_snapshot.h\"\n\nnamespace crashpad {\n\nvoid BuildMinidumpThreadIDMap(\n    const std::vector<const ThreadSnapshot*>& thread_snapshots,\n    MinidumpThreadIDMap* thread_id_map) {\n  DCHECK(thread_id_map->empty());\n\n  // First, try truncating each 64-bit thread ID to 32 bits. If that’s possible\n  // for each unique 64-bit thread ID, then this will be used as the mapping.\n  // This preserves as much of the original thread ID as possible when feasible.\n  bool collision = false;\n  std::set<uint32_t> thread_ids_32;\n  for (const ThreadSnapshot* thread_snapshot : thread_snapshots) {\n    uint64_t thread_id_64 = thread_snapshot->ThreadID();\n    if (thread_id_map->find(thread_id_64) == thread_id_map->end()) {\n      uint32_t thread_id_32 = static_cast<uint32_t>(thread_id_64);\n      if (!thread_ids_32.insert(thread_id_32).second) {\n        collision = true;\n        break;\n      }\n      thread_id_map->insert(std::make_pair(thread_id_64, thread_id_32));\n    }\n  }\n\n  if (collision) {\n    // Since there was a collision, go back and assign each unique 64-bit thread\n    // ID its own sequential 32-bit equivalent. The 32-bit thread IDs will not\n    // bear any resemblance to the original 64-bit thread IDs.\n    thread_id_map->clear();\n    for (const ThreadSnapshot* thread_snapshot : thread_snapshots) {\n      uint64_t thread_id_64 = thread_snapshot->ThreadID();\n      if (thread_id_map->find(thread_id_64) == thread_id_map->end()) {\n        uint32_t thread_id_32 =\n            base::checked_cast<uint32_t>(thread_id_map->size());\n        thread_id_map->insert(std::make_pair(thread_id_64, thread_id_32));\n      }\n    }\n\n    DCHECK_LE(thread_id_map->size(), std::numeric_limits<uint32_t>::max());\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_thread_id_map.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_\n\n#include <stdint.h>\n\n#include <map>\n#include <vector>\n\nnamespace crashpad {\n\nclass ThreadSnapshot;\n\n//! \\brief A map that connects 64-bit snapshot thread IDs to 32-bit minidump\n//!     thread IDs.\n//!\n//! 64-bit snapshot thread IDs are obtained from ThreadSnapshot::ThreadID().\n//! 32-bit minidump thread IDs are stored in MINIDUMP_THREAD::ThreadId.\n//!\n//! A ThreadIDMap ensures that there are no collisions among the set of 32-bit\n//! minidump thread IDs.\nusing MinidumpThreadIDMap = std::map<uint64_t, uint32_t>;\n\n//! \\brief Builds a MinidumpThreadIDMap for a group of ThreadSnapshot objects.\n//!\n//! \\param[in] thread_snapshots The thread snapshots to use as source data.\n//! \\param[out] thread_id_map A MinidumpThreadIDMap to be built by this method.\n//!     This map must be empty when this function is called.\n//!\n//! The map ensures that for any unique 64-bit thread ID found in a\n//! ThreadSnapshot, the 32-bit thread ID used in a minidump file will also be\n//! unique.\nvoid BuildMinidumpThreadIDMap(\n    const std::vector<const ThreadSnapshot*>& thread_snapshots,\n    MinidumpThreadIDMap* thread_id_map);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_\n"
  },
  {
    "path": "minidump/minidump_thread_id_map_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_thread_id_map.h\"\n\n#include <sys/types.h>\n\n#include <iterator>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n#include \"snapshot/test/test_thread_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass MinidumpThreadIDMapTest : public testing::Test {\n public:\n  MinidumpThreadIDMapTest()\n      : Test(),\n        thread_snapshots_(),\n        test_thread_snapshots_() {\n  }\n\n  MinidumpThreadIDMapTest(const MinidumpThreadIDMapTest&) = delete;\n  MinidumpThreadIDMapTest& operator=(const MinidumpThreadIDMapTest&) = delete;\n\n  ~MinidumpThreadIDMapTest() override {}\n\n  // testing::Test:\n  void SetUp() override {\n    for (size_t index = 0; index < std::size(test_thread_snapshots_); ++index) {\n      thread_snapshots_.push_back(&test_thread_snapshots_[index]);\n    }\n  }\n\n protected:\n  static bool MapHasKeyValue(\n      const MinidumpThreadIDMap* map, uint64_t key, uint32_t expected_value) {\n    auto iterator = map->find(key);\n    if (iterator == map->end()) {\n      EXPECT_NE(iterator, map->end());\n      return false;\n    }\n    if (iterator->second != expected_value) {\n      EXPECT_EQ(iterator->second, expected_value);\n      return false;\n    }\n    return true;\n  }\n\n  void SetThreadID(size_t index, uint64_t thread_id) {\n    ASSERT_LT(index, std::size(test_thread_snapshots_));\n    test_thread_snapshots_[index].SetThreadID(thread_id);\n  }\n\n  const std::vector<const ThreadSnapshot*>& thread_snapshots() const {\n    return thread_snapshots_;\n  }\n\n private:\n  std::vector<const ThreadSnapshot*> thread_snapshots_;\n  TestThreadSnapshot test_thread_snapshots_[5];\n};\n\nTEST_F(MinidumpThreadIDMapTest, NoThreads) {\n  // Don’t use thread_snapshots(), because it’s got some threads in it, and the\n  // point of this test is to make sure that BuildMinidumpThreadIDMap() works\n  // with no threads.\n  std::vector<const ThreadSnapshot*> thread_snapshots;\n  MinidumpThreadIDMap thread_id_map;\n  BuildMinidumpThreadIDMap(thread_snapshots, &thread_id_map);\n\n  EXPECT_TRUE(thread_id_map.empty());\n}\n\nTEST_F(MinidumpThreadIDMapTest, SimpleMapping) {\n  SetThreadID(0, 1);\n  SetThreadID(1, 3);\n  SetThreadID(2, 5);\n  SetThreadID(3, 7);\n  SetThreadID(4, 9);\n\n  MinidumpThreadIDMap thread_id_map;\n  BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);\n\n  EXPECT_EQ(thread_id_map.size(), 5u);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 1, 1);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 3, 3);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 5, 5);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 7, 7);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 9, 9);\n}\n\nTEST_F(MinidumpThreadIDMapTest, Truncation) {\n  SetThreadID(0, 0x0000000000000000);\n  SetThreadID(1, 0x9999999900000001);\n  SetThreadID(2, 0x9999999980000001);\n  SetThreadID(3, 0x99999999fffffffe);\n  SetThreadID(4, 0x99999999ffffffff);\n\n  MinidumpThreadIDMap thread_id_map;\n  BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);\n\n  EXPECT_EQ(thread_id_map.size(), 5u);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000000, 0x00000000);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x9999999900000001, 0x00000001);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x9999999980000001, 0x80000001);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x99999999fffffffe, 0xfffffffe);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x99999999ffffffff, 0xffffffff);\n}\n\nTEST_F(MinidumpThreadIDMapTest, DuplicateThreadID) {\n  SetThreadID(0, 2);\n  SetThreadID(1, 4);\n  SetThreadID(2, 4);\n  SetThreadID(3, 6);\n  SetThreadID(4, 8);\n\n  MinidumpThreadIDMap thread_id_map;\n  BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);\n\n  EXPECT_EQ(thread_id_map.size(), 4u);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 2, 2);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 4, 4);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 6, 6);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 8, 8);\n}\n\nTEST_F(MinidumpThreadIDMapTest, Collision) {\n  SetThreadID(0, 0x0000000000000010);\n  SetThreadID(1, 0x0000000000000020);\n  SetThreadID(2, 0x0000000000000030);\n  SetThreadID(3, 0x0000000000000040);\n  SetThreadID(4, 0x0000000100000010);\n\n  MinidumpThreadIDMap thread_id_map;\n  BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);\n\n  EXPECT_EQ(thread_id_map.size(), 5u);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000010, 0);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000020, 1);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000030, 2);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000040, 3);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000100000010, 4);\n}\n\nTEST_F(MinidumpThreadIDMapTest, DuplicateAndCollision) {\n  SetThreadID(0, 0x0000000100000010);\n  SetThreadID(1, 0x0000000000000010);\n  SetThreadID(2, 0x0000000000000020);\n  SetThreadID(3, 0x0000000000000030);\n  SetThreadID(4, 0x0000000000000020);\n\n  MinidumpThreadIDMap thread_id_map;\n  BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);\n\n  EXPECT_EQ(thread_id_map.size(), 4u);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000100000010, 0);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000010, 1);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000020, 2);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000030, 3);\n}\n\nTEST_F(MinidumpThreadIDMapTest, AllDuplicates) {\n  SetThreadID(0, 6);\n  SetThreadID(1, 6);\n  SetThreadID(2, 6);\n  SetThreadID(3, 6);\n  SetThreadID(4, 6);\n\n  MinidumpThreadIDMap thread_id_map;\n  BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map);\n\n  EXPECT_EQ(thread_id_map.size(), 1u);\n  EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 6, 6);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_thread_name_list_writer.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_thread_name_list_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"minidump/minidump_thread_id_map.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpThreadNameWriter::MinidumpThreadNameWriter()\n    : MinidumpWritable(), rva_of_thread_name_(), thread_id_(), name_() {}\n\nMinidumpThreadNameWriter::~MinidumpThreadNameWriter() {}\n\nvoid MinidumpThreadNameWriter::InitializeFromSnapshot(\n    const ThreadSnapshot* thread_snapshot,\n    const MinidumpThreadIDMap& thread_id_map) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  const auto it = thread_id_map.find(thread_snapshot->ThreadID());\n  DCHECK(it != thread_id_map.end());\n  SetThreadId(it->second);\n  SetThreadName(thread_snapshot->ThreadName());\n}\n\nRVA64 MinidumpThreadNameWriter::RvaOfThreadName() const {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return rva_of_thread_name_;\n}\n\nuint32_t MinidumpThreadNameWriter::ThreadId() const {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return thread_id_;\n}\n\nbool MinidumpThreadNameWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  name_->RegisterRVA(&rva_of_thread_name_);\n\n  return MinidumpWritable::Freeze();\n}\n\nvoid MinidumpThreadNameWriter::SetThreadName(const std::string& name) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!name_) {\n    name_.reset(new internal::MinidumpUTF16StringWriter());\n  }\n  name_->SetUTF8(name);\n}\n\nsize_t MinidumpThreadNameWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // This object doesn’t directly write anything itself. Its parent writes the\n  // MINIDUMP_THREAD_NAME objects as part of a MINIDUMP_THREAD_NAME_LIST, and\n  // its children are responsible for writing themselves.\n  return 0;\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpThreadNameWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK(name_);\n\n  std::vector<MinidumpWritable*> children;\n  children.emplace_back(name_.get());\n\n  return children;\n}\n\nbool MinidumpThreadNameWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  // This object doesn’t directly write anything itself. Its\n  // MINIDUMP_THREAD_NAME is written by its parent as part of a\n  // MINIDUMP_THREAD_NAME_LIST, and its children are responsible for writing\n  // themselves.\n  return true;\n}\n\nMinidumpThreadNameListWriter::MinidumpThreadNameListWriter()\n    : MinidumpStreamWriter(), thread_names_() {}\n\nMinidumpThreadNameListWriter::~MinidumpThreadNameListWriter() {}\n\nvoid MinidumpThreadNameListWriter::InitializeFromSnapshot(\n    const std::vector<const ThreadSnapshot*>& thread_snapshots,\n    const MinidumpThreadIDMap& thread_id_map) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(thread_names_.empty());\n\n  for (const ThreadSnapshot* thread_snapshot : thread_snapshots) {\n    auto thread = std::make_unique<MinidumpThreadNameWriter>();\n    thread->InitializeFromSnapshot(thread_snapshot, thread_id_map);\n    AddThreadName(std::move(thread));\n  }\n}\n\nvoid MinidumpThreadNameListWriter::AddThreadName(\n    std::unique_ptr<MinidumpThreadNameWriter> thread_name) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  thread_names_.emplace_back(std::move(thread_name));\n}\n\nbool MinidumpThreadNameListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  size_t thread_name_count = thread_names_.size();\n  if (!AssignIfInRange(&thread_name_list_.NumberOfThreadNames,\n                       thread_name_count)) {\n    LOG(ERROR) << \"thread_name_count \" << thread_name_count << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpThreadNameListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(thread_name_list_) +\n         thread_names_.size() * sizeof(MINIDUMP_THREAD_NAME);\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpThreadNameListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children;\n  children.reserve(thread_names_.size());\n  for (const auto& thread_name : thread_names_) {\n    children.emplace_back(thread_name.get());\n  }\n\n  return children;\n}\n\nbool MinidumpThreadNameListWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = &thread_name_list_;\n  iov.iov_len = sizeof(thread_name_list_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n  iovecs.reserve(thread_names_.size() + 1);\n\n  std::vector<MINIDUMP_THREAD_NAME> minidump_thread_names;\n  minidump_thread_names.reserve(thread_names_.size());\n  for (const auto& thread_name : thread_names_) {\n    auto& minidump_thread_name = minidump_thread_names.emplace_back();\n    minidump_thread_name.ThreadId = thread_name->ThreadId();\n    minidump_thread_name.RvaOfThreadName = thread_name->RvaOfThreadName();\n    iov.iov_base = &minidump_thread_name;\n    iov.iov_len = sizeof(minidump_thread_name);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpStreamType MinidumpThreadNameListWriter::StreamType() const {\n  return kMinidumpStreamTypeThreadNameList;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_thread_name_list_writer.h",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_NAME_LIST_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_NAME_LIST_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_string_writer.h\"\n#include \"minidump/minidump_thread_id_map.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\n//! \\brief The writer for a MINIDUMP_THREAD_NAME object in a minidump file.\n//!\n//! Because MINIDUMP_THREAD_NAME objects only appear as elements of\n//! MINIDUMP_THREAD_NAME_LIST objects, this class does not write any data on its\n//! own. It makes its MINIDUMP_THREAD_NAME data available to its\n//! MinidumpThreadNameListWriter parent, which writes it as part of a\n//! MINIDUMP_THREAD_NAME_LIST.\nclass MinidumpThreadNameWriter final : public internal::MinidumpWritable {\n public:\n  MinidumpThreadNameWriter();\n\n  MinidumpThreadNameWriter(const MinidumpThreadNameWriter&) = delete;\n  MinidumpThreadNameWriter& operator=(const MinidumpThreadNameWriter&) = delete;\n\n  ~MinidumpThreadNameWriter() override;\n\n  //! \\brief Initializes the MINIDUMP_THREAD_NAME based on \\a thread_snapshot.\n  //!\n  //! \\param[in] thread_snapshot The thread snapshot to use as source data.\n  //! \\param[in] thread_id_map A MinidumpThreadIDMap to be consulted to\n  //!     determine the 32-bit minidump thread ID to use for \\a thread_snapshot.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void InitializeFromSnapshot(const ThreadSnapshot* thread_snapshot,\n                              const MinidumpThreadIDMap& thread_id_map);\n\n  //! \\brief Sets the ThreadId for MINIDUMP_THREAD_NAME::ThreadId.\n  void SetThreadId(uint32_t thread_id) { thread_id_ = thread_id; }\n\n  //! \\brief Gets the ThreadId for MINIDUMP_THREAD_NAME::ThreadId.\n  //!\n  //! \\note Valid in #kStateWritable.\n  uint32_t ThreadId() const;\n\n  //! \\brief Sets MINIDUMP_THREAD_NAME::RvaOfThreadName.\n  void SetThreadName(const std::string& thread_name);\n\n  //! \\brief Returns an RVA64 which has been updated with the relative address\n  //!    of the thread name.\n  //!\n  //! This method is expected to be called by a MinidumpThreadNameListWriter in\n  //! order to obtain the RVA64 of the thread name.\n  //!\n  //! \\note Valid in #kStateWritable.\n  RVA64 RvaOfThreadName() const;\n\n private:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // This exists as a separate field so MinidumpWritable::RegisterRVA() can be\n  // used on a guaranteed-aligned pointer (MINIDUMP_THREAD_NAME::RvaOfThreadName\n  // is not 64-bit aligned, causing issues on ARM).\n  RVA64 rva_of_thread_name_;\n\n  // Although this class manages the data for a MINIDUMP_THREAD_NAME, it does\n  // not directly hold a MINIDUMP_THREAD_NAME, as that struct contains a\n  // non-aligned RVA64 field which prevents it use with\n  // MinidumpWritable::RegisterRVA().\n  //\n  // Instead, this class individually holds the fields of the\n  // MINIDUMP_THREAD_NAME which are fetched by MinidumpThreadNameListWriter.\n  uint32_t thread_id_;\n\n  std::unique_ptr<internal::MinidumpUTF16StringWriter> name_;\n};\n\n//! \\brief The writer for a MINIDUMP_THREAD_NAME_LIST stream in a minidump file,\n//!     containing a list of MINIDUMP_THREAD_NAME objects.\nclass MinidumpThreadNameListWriter final\n    : public internal::MinidumpStreamWriter {\n public:\n  MinidumpThreadNameListWriter();\n\n  MinidumpThreadNameListWriter(const MinidumpThreadNameListWriter&) = delete;\n  MinidumpThreadNameListWriter& operator=(const MinidumpThreadNameListWriter&) =\n      delete;\n\n  ~MinidumpThreadNameListWriter() override;\n\n  //! \\brief Adds an initialized MINIDUMP_THREAD_NAME for each thread in \\a\n  //!     thread_snapshots to the MINIDUMP_THREAD_NAME_LIST.\n  //!\n  //! \\param[in] thread_snapshots The thread snapshots to use as source data.\n  //! \\param[in] thread_id_map A MinidumpThreadIDMap previously built by\n  //!     MinidumpThreadListWriter::InitializeFromSnapshot().\n  //!\n  //! \\note Valid in #kStateMutable.\n  void InitializeFromSnapshot(\n      const std::vector<const ThreadSnapshot*>& thread_snapshots,\n      const MinidumpThreadIDMap& thread_id_map);\n\n  //! \\brief Adds a MinidumpThreadNameWriter to the MINIDUMP_THREAD_LIST.\n  //!\n  //! This object takes ownership of \\a thread_name and becomes its parent in\n  //! the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddThreadName(std::unique_ptr<MinidumpThreadNameWriter> thread_name);\n\n private:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n  std::vector<std::unique_ptr<MinidumpThreadNameWriter>> thread_names_;\n  MINIDUMP_THREAD_NAME_LIST thread_name_list_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_NAME_LIST_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_thread_name_list_writer_test.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_thread_name_list_writer.h\"\n\n#include <iterator>\n#include <string>\n#include <utility>\n\n#include \"base/compiler_specific.h\"\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/minidump_system_info_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"test/gtest_death.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// This returns the MINIDUMP_THREAD_NAME_LIST stream in |thread_name_list|.\nvoid GetThreadNameListStream(\n    const std::string& file_contents,\n    const MINIDUMP_THREAD_NAME_LIST** thread_name_list) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  const uint32_t kExpectedStreams = 1;\n  const size_t kThreadNameListStreamOffset =\n      kDirectoryOffset + kExpectedStreams * sizeof(MINIDUMP_DIRECTORY);\n  const size_t kThreadNameListOffset =\n      kThreadNameListStreamOffset + sizeof(MINIDUMP_THREAD_NAME_LIST);\n\n  ASSERT_GE(file_contents.size(), kThreadNameListOffset);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0));\n  ASSERT_TRUE(directory);\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeThreadNameList);\n  EXPECT_EQ(directory[0].Location.Rva, kThreadNameListStreamOffset);\n\n  *thread_name_list =\n      MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_NAME_LIST>(\n          file_contents, directory[0].Location);\n  ASSERT_TRUE(thread_name_list);\n}\n\nTEST(MinidumpThreadNameListWriter, EmptyThreadNameList) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_name_list_writer =\n      std::make_unique<MinidumpThreadNameListWriter>();\n\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(thread_name_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_THREAD_NAME_LIST));\n\n  const MINIDUMP_THREAD_NAME_LIST* thread_name_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadNameListStream(string_file.string(), &thread_name_list));\n\n  EXPECT_EQ(thread_name_list->NumberOfThreadNames, 0u);\n}\n\n// The MINIDUMP_THREAD_NAMEs |expected| and |observed| are compared against\n// each other using Google Test assertions.\nvoid ExpectThreadName(const MINIDUMP_THREAD_NAME* expected,\n                      const MINIDUMP_THREAD_NAME* observed,\n                      const std::string& file_contents,\n                      const std::string& expected_thread_name) {\n  // Copy RvaOfThreadName into a local variable because\n  // |MINIDUMP_THREAD_NAME::RvaOfThreadName| requires 8-byte alignment but the\n  // struct itself is 4-byte algined.\n  const auto rva_of_thread_name = [&observed] {\n    RVA64 data = 0;\n    memcpy(&data, &observed->RvaOfThreadName, sizeof(RVA64));\n    return data;\n  }();\n\n  EXPECT_EQ(observed->ThreadId, expected->ThreadId);\n  EXPECT_NE(rva_of_thread_name, 0u);\n  const std::string observed_thread_name = base::UTF16ToUTF8(\n      MinidumpStringAtRVAAsString(file_contents, rva_of_thread_name));\n  EXPECT_EQ(observed_thread_name, expected_thread_name);\n}\n\nTEST(MinidumpThreadNameListWriter, OneThread) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();\n\n  constexpr uint32_t kThreadID = 0x11111111;\n  const std::string kThreadName = \"ariadne\";\n\n  auto thread_name_list_writer =\n      std::make_unique<MinidumpThreadNameListWriter>();\n  auto thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();\n  thread_name_writer->SetThreadId(kThreadID);\n  thread_name_writer->SetThreadName(kThreadName);\n  thread_name_list_writer->AddThreadName(std::move(thread_name_writer));\n\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(thread_name_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_THREAD_NAME_LIST) +\n                1 * sizeof(MINIDUMP_THREAD_NAME));\n\n  const MINIDUMP_THREAD_NAME_LIST* thread_name_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadNameListStream(string_file.string(), &thread_name_list));\n\n  EXPECT_EQ(thread_name_list->NumberOfThreadNames, 1u);\n\n  MINIDUMP_THREAD_NAME expected = {};\n  expected.ThreadId = kThreadID;\n\n  ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,\n                                           &thread_name_list->ThreadNames[0],\n                                           string_file.string(),\n                                           kThreadName));\n}\n\nTEST(MinidumpThreadNameListWriter, OneThreadWithLeadingPadding) {\n  MinidumpFileWriter minidump_file_writer;\n\n  // Add a stream before the MINIDUMP_THREAD_NAME_LIST to ensure the thread name\n  // MINIDUMP_STRING requires leading padding to align to a 4-byte boundary.\n  auto system_info_writer = std::make_unique<MinidumpSystemInfoWriter>();\n  system_info_writer->SetCSDVersion(\"\");\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer)));\n\n  auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();\n\n  constexpr uint32_t kThreadID = 0x11111111;\n  const std::string kThreadName = \"ariadne\";\n\n  auto thread_name_list_writer =\n      std::make_unique<MinidumpThreadNameListWriter>();\n  auto thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();\n  thread_name_writer->SetThreadId(kThreadID);\n  thread_name_writer->SetThreadName(kThreadName);\n  thread_name_list_writer->AddThreadName(std::move(thread_name_writer));\n\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(thread_name_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_THREAD_NAME_LIST) +\n                1 * sizeof(MINIDUMP_THREAD_NAME));\n\n  const uint32_t kExpectedStreams = 2;\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(string_file.string(), &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0));\n  ASSERT_TRUE(directory);\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo);\n  ASSERT_EQ(directory[1].StreamType, kMinidumpStreamTypeThreadNameList);\n\n  const MINIDUMP_THREAD_NAME_LIST* thread_name_list =\n      MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_NAME_LIST>(\n          string_file.string(), directory[1].Location);\n  ASSERT_TRUE(thread_name_list);\n\n  EXPECT_EQ(thread_name_list->NumberOfThreadNames, 1u);\n\n  MINIDUMP_THREAD_NAME expected = {};\n  expected.ThreadId = kThreadID;\n\n  ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,\n                                           &thread_name_list->ThreadNames[0],\n                                           string_file.string(),\n                                           kThreadName));\n}\n\nTEST(MinidumpThreadNameListWriter, TwoThreads_DifferentNames) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();\n\n  constexpr uint32_t kFirstThreadID = 0x11111111;\n  const std::string kFirstThreadName = \"ariadne\";\n\n  constexpr uint32_t kSecondThreadID = 0x22222222;\n  const std::string kSecondThreadName = \"theseus\";\n\n  auto thread_name_list_writer =\n      std::make_unique<MinidumpThreadNameListWriter>();\n  auto first_thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();\n  first_thread_name_writer->SetThreadId(kFirstThreadID);\n  first_thread_name_writer->SetThreadName(kFirstThreadName);\n  thread_name_list_writer->AddThreadName(std::move(first_thread_name_writer));\n\n  auto second_thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();\n  second_thread_name_writer->SetThreadId(kSecondThreadID);\n  second_thread_name_writer->SetThreadName(kSecondThreadName);\n  thread_name_list_writer->AddThreadName(std::move(second_thread_name_writer));\n\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(thread_name_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_THREAD_NAME_LIST) +\n                2 * sizeof(MINIDUMP_THREAD_NAME));\n\n  const MINIDUMP_THREAD_NAME_LIST* thread_name_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadNameListStream(string_file.string(), &thread_name_list));\n\n  EXPECT_EQ(thread_name_list->NumberOfThreadNames, 2u);\n\n  MINIDUMP_THREAD_NAME expected = {};\n  expected.ThreadId = kFirstThreadID;\n\n  ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,\n                                           &thread_name_list->ThreadNames[0],\n                                           string_file.string(),\n                                           kFirstThreadName));\n\n  expected.ThreadId = kSecondThreadID;\n\n  ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,\n                                           &thread_name_list->ThreadNames[1],\n                                           string_file.string(),\n                                           kSecondThreadName));\n}\n\nTEST(MinidumpThreadNameListWriter, TwoThreads_SameNames) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_list_writer = std::make_unique<MinidumpThreadNameListWriter>();\n\n  constexpr uint32_t kFirstThreadID = 0x11111111;\n  const std::string kThreadName = \"ariadne\";\n\n  constexpr uint32_t kSecondThreadID = 0x22222222;\n\n  auto thread_name_list_writer =\n      std::make_unique<MinidumpThreadNameListWriter>();\n  auto first_thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();\n  first_thread_name_writer->SetThreadId(kFirstThreadID);\n  first_thread_name_writer->SetThreadName(kThreadName);\n  thread_name_list_writer->AddThreadName(std::move(first_thread_name_writer));\n\n  auto second_thread_name_writer = std::make_unique<MinidumpThreadNameWriter>();\n  second_thread_name_writer->SetThreadId(kSecondThreadID);\n  second_thread_name_writer->SetThreadName(kThreadName);\n  thread_name_list_writer->AddThreadName(std::move(second_thread_name_writer));\n\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(thread_name_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_THREAD_NAME_LIST) +\n                2 * sizeof(MINIDUMP_THREAD_NAME));\n\n  const MINIDUMP_THREAD_NAME_LIST* thread_name_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadNameListStream(string_file.string(), &thread_name_list));\n\n  EXPECT_EQ(thread_name_list->NumberOfThreadNames, 2u);\n\n  MINIDUMP_THREAD_NAME expected = {};\n  expected.ThreadId = kFirstThreadID;\n\n  ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,\n                                           &thread_name_list->ThreadNames[0],\n                                           string_file.string(),\n                                           kThreadName));\n\n  expected.ThreadId = kSecondThreadID;\n\n  ASSERT_NO_FATAL_FAILURE(ExpectThreadName(&expected,\n                                           &thread_name_list->ThreadNames[1],\n                                           string_file.string(),\n                                           kThreadName));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_thread_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_thread_writer.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"minidump/minidump_context_writer.h\"\n#include \"minidump/minidump_memory_writer.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpThreadWriter::MinidumpThreadWriter()\n    : MinidumpWritable(), thread_(), stack_(nullptr), context_(nullptr) {\n}\n\nMinidumpThreadWriter::~MinidumpThreadWriter() {\n}\n\nvoid MinidumpThreadWriter::InitializeFromSnapshot(\n    const ThreadSnapshot* thread_snapshot,\n    const MinidumpThreadIDMap* thread_id_map) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!stack_);\n  DCHECK(!context_);\n\n  auto thread_id_it = thread_id_map->find(thread_snapshot->ThreadID());\n  DCHECK(thread_id_it != thread_id_map->end());\n  SetThreadID(thread_id_it->second);\n\n  SetSuspendCount(thread_snapshot->SuspendCount());\n  SetPriority(thread_snapshot->Priority());\n  SetTEB(thread_snapshot->ThreadSpecificDataAddress());\n\n  const MemorySnapshot* stack_snapshot = thread_snapshot->Stack();\n  if (stack_snapshot && stack_snapshot->Size() > 0) {\n    std::unique_ptr<SnapshotMinidumpMemoryWriter> stack(\n        new SnapshotMinidumpMemoryWriter(stack_snapshot));\n    SetStack(std::move(stack));\n  }\n\n  std::unique_ptr<MinidumpContextWriter> context =\n      MinidumpContextWriter::CreateFromSnapshot(thread_snapshot->Context());\n  SetContext(std::move(context));\n}\n\nconst MINIDUMP_THREAD* MinidumpThreadWriter::MinidumpThread() const {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return &thread_;\n}\n\nvoid MinidumpThreadWriter::SetStack(\n    std::unique_ptr<SnapshotMinidumpMemoryWriter> stack) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  stack_ = std::move(stack);\n}\n\nvoid MinidumpThreadWriter::SetContext(\n    std::unique_ptr<MinidumpContextWriter> context) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  context_ = std::move(context);\n}\n\nbool MinidumpThreadWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n  CHECK(context_);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  if (stack_) {\n    stack_->RegisterMemoryDescriptor(&thread_.Stack);\n  }\n\n  context_->RegisterLocationDescriptor(&thread_.ThreadContext);\n\n  return true;\n}\n\nsize_t MinidumpThreadWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is\n  // written by its parent as part of a MINIDUMP_THREAD_LIST, and its children\n  // are responsible for writing themselves.\n  return 0;\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpThreadWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK(context_);\n\n  std::vector<MinidumpWritable*> children;\n  if (stack_) {\n    children.push_back(stack_.get());\n  }\n  children.push_back(context_.get());\n\n  return children;\n}\n\nbool MinidumpThreadWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  // This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is\n  // written by its parent as part of a MINIDUMP_THREAD_LIST, and its children\n  // are responsible for writing themselves.\n  return true;\n}\n\nMinidumpThreadListWriter::MinidumpThreadListWriter()\n    : MinidumpStreamWriter(),\n      threads_(),\n      memory_list_writer_(nullptr),\n      thread_list_base_() {\n}\n\nMinidumpThreadListWriter::~MinidumpThreadListWriter() {\n}\n\nvoid MinidumpThreadListWriter::InitializeFromSnapshot(\n    const std::vector<const ThreadSnapshot*>& thread_snapshots,\n    MinidumpThreadIDMap* thread_id_map) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(threads_.empty());\n\n  BuildMinidumpThreadIDMap(thread_snapshots, thread_id_map);\n\n  for (const ThreadSnapshot* thread_snapshot : thread_snapshots) {\n    auto thread = std::make_unique<MinidumpThreadWriter>();\n    thread->InitializeFromSnapshot(thread_snapshot, thread_id_map);\n    AddThread(std::move(thread));\n  }\n\n  // Do this in a separate loop to keep the thread stacks earlier in the dump,\n  // and together.\n  for (const ThreadSnapshot* thread_snapshot : thread_snapshots)\n    memory_list_writer_->AddFromSnapshot(thread_snapshot->ExtraMemory());\n}\n\nvoid MinidumpThreadListWriter::SetMemoryListWriter(\n    MinidumpMemoryListWriter* memory_list_writer) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(threads_.empty());\n\n  memory_list_writer_ = memory_list_writer;\n}\n\nvoid MinidumpThreadListWriter::AddThread(\n    std::unique_ptr<MinidumpThreadWriter> thread) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (memory_list_writer_) {\n    SnapshotMinidumpMemoryWriter* stack = thread->Stack();\n    if (stack) {\n      memory_list_writer_->AddNonOwnedMemory(stack);\n    }\n  }\n\n  threads_.push_back(std::move(thread));\n}\n\nbool MinidumpThreadListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  size_t thread_count = threads_.size();\n  if (!AssignIfInRange(&thread_list_base_.NumberOfThreads, thread_count)) {\n    LOG(ERROR) << \"thread_count \" << thread_count << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpThreadListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(thread_list_base_) + threads_.size() * sizeof(MINIDUMP_THREAD);\n}\n\nstd::vector<internal::MinidumpWritable*> MinidumpThreadListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& thread : threads_) {\n    children.push_back(thread.get());\n  }\n\n  return children;\n}\n\nbool MinidumpThreadListWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = &thread_list_base_;\n  iov.iov_len = sizeof(thread_list_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  for (const auto& thread : threads_) {\n    iov.iov_base = thread->MinidumpThread();\n    iov.iov_len = sizeof(MINIDUMP_THREAD);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpStreamType MinidumpThreadListWriter::StreamType() const {\n  return kMinidumpStreamTypeThreadList;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_thread_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_thread_id_map.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\nclass MinidumpContextWriter;\nclass MinidumpMemoryListWriter;\nclass SnapshotMinidumpMemoryWriter;\nclass ThreadSnapshot;\n\n//! \\brief The writer for a MINIDUMP_THREAD object in a minidump file.\n//!\n//! Because MINIDUMP_THREAD objects only appear as elements of\n//! MINIDUMP_THREAD_LIST objects, this class does not write any data on its own.\n//! It makes its MINIDUMP_THREAD data available to its MinidumpThreadListWriter\n//! parent, which writes it as part of a MINIDUMP_THREAD_LIST.\nclass MinidumpThreadWriter final : public internal::MinidumpWritable {\n public:\n  MinidumpThreadWriter();\n\n  MinidumpThreadWriter(const MinidumpThreadWriter&) = delete;\n  MinidumpThreadWriter& operator=(const MinidumpThreadWriter&) = delete;\n\n  ~MinidumpThreadWriter() override;\n\n  //! \\brief Initializes the MINIDUMP_THREAD based on \\a thread_snapshot.\n  //!\n  //! \\param[in] thread_snapshot The thread snapshot to use as source data.\n  //! \\param[in] thread_id_map A MinidumpThreadIDMap to be consulted to\n  //!     determine the 32-bit minidump thread ID to use for \\a thread_snapshot.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(const ThreadSnapshot* thread_snapshot,\n                              const MinidumpThreadIDMap* thread_id_map);\n\n  //! \\brief Returns a MINIDUMP_THREAD referencing this object’s data.\n  //!\n  //! This method is expected to be called by a MinidumpThreadListWriter in\n  //! order to obtain a MINIDUMP_THREAD to include in its list.\n  //!\n  //! \\note Valid in #kStateWritable.\n  const MINIDUMP_THREAD* MinidumpThread() const;\n\n  //! \\brief Returns a SnapshotMinidumpMemoryWriter that will write the memory\n  //!     region corresponding to this object’s stack.\n  //!\n  //! If the thread does not have a stack, or its stack could not be determined,\n  //! this will return `nullptr`.\n  //!\n  //! This method is provided so that MinidumpThreadListWriter can obtain thread\n  //! stack memory regions for the purposes of adding them to a\n  //! MinidumpMemoryListWriter (configured by calling\n  //! MinidumpThreadListWriter::SetMemoryListWriter()) by calling\n  //! MinidumpMemoryListWriter::AddExtraMemory().\n  //!\n  //! \\note Valid in any state.\n  SnapshotMinidumpMemoryWriter* Stack() const { return stack_.get(); }\n\n  //! \\brief Arranges for MINIDUMP_THREAD::Stack to point to the MINIDUMP_MEMORY\n  //!     object to be written by \\a stack.\n  //!\n  //! This object takes ownership of \\a stack and becomes its parent in the\n  //! overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetStack(std::unique_ptr<SnapshotMinidumpMemoryWriter> stack);\n\n  //! \\brief Arranges for MINIDUMP_THREAD::ThreadContext to point to the CPU\n  //!     context to be written by \\a context.\n  //!\n  //! A context is required in all MINIDUMP_THREAD objects.\n  //!\n  //! This object takes ownership of \\a context and becomes its parent in the\n  //! overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetContext(std::unique_ptr<MinidumpContextWriter> context);\n\n  //! \\brief Sets MINIDUMP_THREAD::ThreadId.\n  void SetThreadID(uint32_t thread_id) { thread_.ThreadId = thread_id; }\n\n  //! \\brief Sets MINIDUMP_THREAD::SuspendCount.\n  void SetSuspendCount(uint32_t suspend_count) {\n    thread_.SuspendCount = suspend_count;\n  }\n\n  //! \\brief Sets MINIDUMP_THREAD::PriorityClass.\n  void SetPriorityClass(uint32_t priority_class) {\n    thread_.PriorityClass = priority_class;\n  }\n\n  //! \\brief Sets MINIDUMP_THREAD::Priority.\n  void SetPriority(uint32_t priority) { thread_.Priority = priority; }\n\n  //! \\brief Sets MINIDUMP_THREAD::Teb.\n  void SetTEB(uint64_t teb) { thread_.Teb = teb; }\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  MINIDUMP_THREAD thread_;\n  std::unique_ptr<SnapshotMinidumpMemoryWriter> stack_;\n  std::unique_ptr<MinidumpContextWriter> context_;\n};\n\n//! \\brief The writer for a MINIDUMP_THREAD_LIST stream in a minidump file,\n//!     containing a list of MINIDUMP_THREAD objects.\nclass MinidumpThreadListWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpThreadListWriter();\n\n  MinidumpThreadListWriter(const MinidumpThreadListWriter&) = delete;\n  MinidumpThreadListWriter& operator=(const MinidumpThreadListWriter&) = delete;\n\n  ~MinidumpThreadListWriter() override;\n\n  //! \\brief Adds an initialized MINIDUMP_THREAD for each thread in \\a\n  //!     thread_snapshots to the MINIDUMP_THREAD_LIST.\n  //!\n  //! \\param[in] thread_snapshots The thread snapshots to use as source data.\n  //! \\param[out] thread_id_map A MinidumpThreadIDMap to be built by this\n  //!     method. This map must be empty when this method is called.\n  //!\n  //! \\note Valid in #kStateMutable. AddThread() may not be called before this\n  //!     method, and it is not normally necessary to call AddThread() after\n  //!     this method.\n  void InitializeFromSnapshot(\n      const std::vector<const ThreadSnapshot*>& thread_snapshots,\n      MinidumpThreadIDMap* thread_id_map);\n\n  //! \\brief Sets the MinidumpMemoryListWriter that each thread’s stack memory\n  //!     region should be added to as extra memory.\n  //!\n  //! Each MINIDUMP_THREAD object can contain a reference to a\n  //! SnapshotMinidumpMemoryWriter object that contains a snapshot of its stac\n  //! memory. In the overall tree of internal::MinidumpWritable objects, these\n  //! SnapshotMinidumpMemoryWriter objects are considered children of their\n  //! MINIDUMP_THREAD, and are referenced by a MINIDUMP_MEMORY_DESCRIPTOR\n  //! contained in the MINIDUMP_THREAD. It is also possible for the same memory\n  //! regions to have MINIDUMP_MEMORY_DESCRIPTOR objects present in a\n  //! MINIDUMP_MEMORY_LIST stream. This is accomplished by calling this method,\n  //! which informs a MinidumpThreadListWriter that it should call\n  //! MinidumpMemoryListWriter::AddExtraMemory() for each extant thread stack\n  //! while the thread is being added in AddThread(). When this is done, the\n  //! MinidumpMemoryListWriter will contain a MINIDUMP_MEMORY_DESCRIPTOR\n  //! pointing to the thread’s stack memory in its MINIDUMP_MEMORY_LIST. Note\n  //! that the actual contents of the memory is only written once, as a child of\n  //! the MinidumpThreadWriter. The MINIDUMP_MEMORY_DESCRIPTOR objects in both\n  //! the MINIDUMP_THREAD and MINIDUMP_MEMORY_LIST will point to the same copy\n  //! of the memory’s contents.\n  //!\n  //! \\note This method must be called before AddThread() is called. Threads\n  //!     added by AddThread() prior to this method being called will not have\n  //!     their stacks added to \\a memory_list_writer as extra memory.\n  //! \\note Valid in #kStateMutable.\n  void SetMemoryListWriter(MinidumpMemoryListWriter* memory_list_writer);\n\n  //! \\brief Adds a MinidumpThreadWriter to the MINIDUMP_THREAD_LIST.\n  //!\n  //! This object takes ownership of \\a thread and becomes its parent in the\n  //! overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddThread(std::unique_ptr<MinidumpThreadWriter> thread);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  std::vector<std::unique_ptr<MinidumpThreadWriter>> threads_;\n  MinidumpMemoryListWriter* memory_list_writer_;  // weak\n  MINIDUMP_THREAD_LIST thread_list_base_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_thread_writer_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_thread_writer.h\"\n\n#include <iterator>\n#include <string>\n#include <utility>\n\n#include \"base/compiler_specific.h\"\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_context_writer.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/minidump_memory_writer.h\"\n#include \"minidump/test/minidump_context_test_util.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_memory_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_cpu_context.h\"\n#include \"snapshot/test/test_memory_snapshot.h\"\n#include \"snapshot/test/test_thread_snapshot.h\"\n#include \"test/gtest_death.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// This returns the MINIDUMP_THREAD_LIST stream in |thread_list|. If\n// |memory_list| is not nullptr, a MINIDUMP_MEMORY_LIST stream is also expected\n// in |file_contents|, and that stream will be returned in |memory_list|.\nvoid GetThreadListStream(const std::string& file_contents,\n                         const MINIDUMP_THREAD_LIST** thread_list,\n                         const MINIDUMP_MEMORY_LIST** memory_list) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  const uint32_t kExpectedStreams = memory_list ? 2 : 1;\n  const size_t kThreadListStreamOffset =\n      kDirectoryOffset + kExpectedStreams * sizeof(MINIDUMP_DIRECTORY);\n  const size_t kThreadsOffset =\n      kThreadListStreamOffset + sizeof(MINIDUMP_THREAD_LIST);\n\n  ASSERT_GE(file_contents.size(), kThreadsOffset);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0));\n  ASSERT_TRUE(directory);\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeThreadList);\n  EXPECT_EQ(directory[0].Location.Rva, kThreadListStreamOffset);\n\n  *thread_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(\n      file_contents, directory[0].Location);\n  ASSERT_TRUE(thread_list);\n\n  if (memory_list) {\n    ASSERT_EQ(directory[1].StreamType, kMinidumpStreamTypeMemoryList);\n\n    *memory_list = MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(\n        file_contents, directory[1].Location);\n    ASSERT_TRUE(*memory_list);\n  }\n}\n\nTEST(MinidumpThreadWriter, EmptyThreadList) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_list_writer = std::make_unique<MinidumpThreadListWriter>();\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_THREAD_LIST));\n\n  const MINIDUMP_THREAD_LIST* thread_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadListStream(string_file.string(), &thread_list, nullptr));\n\n  EXPECT_EQ(thread_list->NumberOfThreads, 0u);\n}\n\n// The MINIDUMP_THREADs |expected| and |observed| are compared against each\n// other using Google Test assertions. If |stack| is not nullptr, |observed| is\n// expected to contain a populated MINIDUMP_MEMORY_DESCRIPTOR in its Stack\n// field, otherwise, its Stack field is expected to be zeroed out. The memory\n// descriptor will be placed in |stack|. |observed| must contain a populated\n// ThreadContext field. The context will be recovered from |file_contents| and\n// stored in |context_base|.\nvoid ExpectThread(const MINIDUMP_THREAD* expected,\n                  const MINIDUMP_THREAD* observed,\n                  const std::string& file_contents,\n                  const MINIDUMP_MEMORY_DESCRIPTOR** stack,\n                  const void** context_base) {\n  MINIDUMP_THREAD expected_thread, observed_thread;\n  memcpy(&expected_thread, expected, sizeof(expected_thread));\n  memcpy(&observed_thread, observed, sizeof(observed_thread));\n\n  EXPECT_EQ(observed_thread.ThreadId, expected_thread.ThreadId);\n  EXPECT_EQ(observed_thread.SuspendCount, expected_thread.SuspendCount);\n  EXPECT_EQ(observed_thread.PriorityClass, expected_thread.PriorityClass);\n  EXPECT_EQ(observed_thread.Priority, expected_thread.Priority);\n  EXPECT_EQ(observed_thread.Teb, expected_thread.Teb);\n\n  EXPECT_EQ(observed_thread.Stack.StartOfMemoryRange,\n            expected_thread.Stack.StartOfMemoryRange);\n  EXPECT_EQ(observed_thread.Stack.Memory.DataSize,\n            expected_thread.Stack.Memory.DataSize);\n  if (stack) {\n    ASSERT_NE(observed_thread.Stack.Memory.DataSize, 0u);\n    ASSERT_NE(observed_thread.Stack.Memory.Rva, 0u);\n    ASSERT_GE(file_contents.size(),\n              observed_thread.Stack.Memory.Rva +\n                  observed_thread.Stack.Memory.DataSize);\n    *stack = &observed->Stack;\n  } else {\n    EXPECT_EQ(observed_thread.Stack.StartOfMemoryRange, 0u);\n    EXPECT_EQ(observed_thread.Stack.Memory.DataSize, 0u);\n    EXPECT_EQ(observed_thread.Stack.Memory.Rva, 0u);\n  }\n\n  EXPECT_EQ(observed_thread.ThreadContext.DataSize,\n            expected_thread.ThreadContext.DataSize);\n  ASSERT_NE(observed_thread.ThreadContext.DataSize, 0u);\n  ASSERT_NE(observed_thread.ThreadContext.Rva, 0u);\n  ASSERT_GE(file_contents.size(),\n            observed_thread.ThreadContext.Rva +\n                expected_thread.ThreadContext.DataSize);\n  *context_base = &file_contents[observed_thread.ThreadContext.Rva];\n}\n\nTEST(MinidumpThreadWriter, OneThread_x86_NoStack) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_list_writer = std::make_unique<MinidumpThreadListWriter>();\n\n  constexpr uint32_t kThreadID = 0x11111111;\n  constexpr uint32_t kSuspendCount = 1;\n  constexpr uint32_t kPriorityClass = 0x20;\n  constexpr uint32_t kPriority = 10;\n  constexpr uint64_t kTEB = 0x55555555;\n  constexpr uint32_t kSeed = 123;\n\n  auto thread_writer = std::make_unique<MinidumpThreadWriter>();\n  thread_writer->SetThreadID(kThreadID);\n  thread_writer->SetSuspendCount(kSuspendCount);\n  thread_writer->SetPriorityClass(kPriorityClass);\n  thread_writer->SetPriority(kPriority);\n  thread_writer->SetTEB(kTEB);\n\n  auto context_x86_writer = std::make_unique<MinidumpContextX86Writer>();\n  InitializeMinidumpContextX86(context_x86_writer->context(), kSeed);\n  thread_writer->SetContext(std::move(context_x86_writer));\n\n  thread_list_writer->AddThread(std::move(thread_writer));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) +\n                1 * sizeof(MinidumpContextX86));\n\n  const MINIDUMP_THREAD_LIST* thread_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadListStream(string_file.string(), &thread_list, nullptr));\n\n  EXPECT_EQ(thread_list->NumberOfThreads, 1u);\n\n  MINIDUMP_THREAD expected = {};\n  expected.ThreadId = kThreadID;\n  expected.SuspendCount = kSuspendCount;\n  expected.PriorityClass = kPriorityClass;\n  expected.Priority = kPriority;\n  expected.Teb = kTEB;\n  expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);\n\n  const MinidumpContextX86* observed_context = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectThread(&expected,\n                   &thread_list->Threads[0],\n                   string_file.string(),\n                   nullptr,\n                   reinterpret_cast<const void**>(&observed_context)));\n\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectMinidumpContextX86(kSeed, observed_context, false));\n}\n\nTEST(MinidumpThreadWriter, OneThread_AMD64_Stack) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_list_writer = std::make_unique<MinidumpThreadListWriter>();\n\n  constexpr uint32_t kThreadID = 0x22222222;\n  constexpr uint32_t kSuspendCount = 2;\n  constexpr uint32_t kPriorityClass = 0x30;\n  constexpr uint32_t kPriority = 20;\n  constexpr uint64_t kTEB = 0x5555555555555555;\n  constexpr uint64_t kMemoryBase = 0x765432100000;\n  constexpr size_t kMemorySize = 32;\n  constexpr uint8_t kMemoryValue = 99;\n  constexpr uint32_t kSeed = 456;\n\n  auto thread_writer = std::make_unique<MinidumpThreadWriter>();\n  thread_writer->SetThreadID(kThreadID);\n  thread_writer->SetSuspendCount(kSuspendCount);\n  thread_writer->SetPriorityClass(kPriorityClass);\n  thread_writer->SetPriority(kPriority);\n  thread_writer->SetTEB(kTEB);\n\n  auto memory_writer = std::make_unique<TestMinidumpMemoryWriter>(\n      kMemoryBase, kMemorySize, kMemoryValue);\n  thread_writer->SetStack(std::move(memory_writer));\n\n  auto context_amd64_writer = std::make_unique<MinidumpContextAMD64Writer>();\n  InitializeMinidumpContextAMD64(context_amd64_writer->context(), kSeed);\n  thread_writer->SetContext(std::move(context_amd64_writer));\n\n  thread_list_writer->AddThread(std::move(thread_writer));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) +\n                1 * sizeof(MinidumpContextAMD64) + kMemorySize);\n\n  const MINIDUMP_THREAD_LIST* thread_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadListStream(string_file.string(), &thread_list, nullptr));\n\n  EXPECT_EQ(thread_list->NumberOfThreads, 1u);\n\n  MINIDUMP_THREAD expected = {};\n  expected.ThreadId = kThreadID;\n  expected.SuspendCount = kSuspendCount;\n  expected.PriorityClass = kPriorityClass;\n  expected.Priority = kPriority;\n  expected.Teb = kTEB;\n  expected.Stack.StartOfMemoryRange = kMemoryBase;\n  expected.Stack.Memory.DataSize = kMemorySize;\n  expected.ThreadContext.DataSize = sizeof(MinidumpContextAMD64);\n\n  const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;\n  const MinidumpContextAMD64* observed_context = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectThread(&expected,\n                   &thread_list->Threads[0],\n                   string_file.string(),\n                   &observed_stack,\n                   reinterpret_cast<const void**>(&observed_context)));\n\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,\n                                                observed_stack,\n                                                string_file.string(),\n                                                kMemoryValue,\n                                                true));\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectMinidumpContextAMD64(kSeed, observed_context, false));\n}\n\nTEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_list_writer = std::make_unique<MinidumpThreadListWriter>();\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n  thread_list_writer->SetMemoryListWriter(memory_list_writer.get());\n\n  constexpr uint32_t kThreadID0 = 1111111;\n  constexpr uint32_t kSuspendCount0 = 111111;\n  constexpr uint32_t kPriorityClass0 = 11111;\n  constexpr uint32_t kPriority0 = 1111;\n  constexpr uint64_t kTEB0 = 111;\n  constexpr uint64_t kMemoryBase0 = 0x1110;\n  constexpr size_t kMemorySize0 = 16;\n  constexpr uint8_t kMemoryValue0 = 11;\n  constexpr uint32_t kSeed0 = 1;\n\n  auto thread_writer_0 = std::make_unique<MinidumpThreadWriter>();\n  thread_writer_0->SetThreadID(kThreadID0);\n  thread_writer_0->SetSuspendCount(kSuspendCount0);\n  thread_writer_0->SetPriorityClass(kPriorityClass0);\n  thread_writer_0->SetPriority(kPriority0);\n  thread_writer_0->SetTEB(kTEB0);\n\n  auto memory_writer_0 = std::make_unique<TestMinidumpMemoryWriter>(\n      kMemoryBase0, kMemorySize0, kMemoryValue0);\n  thread_writer_0->SetStack(std::move(memory_writer_0));\n\n  auto context_x86_writer_0 = std::make_unique<MinidumpContextX86Writer>();\n  InitializeMinidumpContextX86(context_x86_writer_0->context(), kSeed0);\n  thread_writer_0->SetContext(std::move(context_x86_writer_0));\n\n  thread_list_writer->AddThread(std::move(thread_writer_0));\n\n  constexpr uint32_t kThreadID1 = 2222222;\n  constexpr uint32_t kSuspendCount1 = 222222;\n  constexpr uint32_t kPriorityClass1 = 22222;\n  constexpr uint32_t kPriority1 = 2222;\n  constexpr uint64_t kTEB1 = 222;\n  constexpr uint64_t kMemoryBase1 = 0x2220;\n  constexpr size_t kMemorySize1 = 32;\n  constexpr uint8_t kMemoryValue1 = 22;\n  constexpr uint32_t kSeed1 = 2;\n\n  auto thread_writer_1 = std::make_unique<MinidumpThreadWriter>();\n  thread_writer_1->SetThreadID(kThreadID1);\n  thread_writer_1->SetSuspendCount(kSuspendCount1);\n  thread_writer_1->SetPriorityClass(kPriorityClass1);\n  thread_writer_1->SetPriority(kPriority1);\n  thread_writer_1->SetTEB(kTEB1);\n\n  auto memory_writer_1 = std::make_unique<TestMinidumpMemoryWriter>(\n      kMemoryBase1, kMemorySize1, kMemoryValue1);\n  thread_writer_1->SetStack(std::move(memory_writer_1));\n\n  auto context_x86_writer_1 = std::make_unique<MinidumpContextX86Writer>();\n  InitializeMinidumpContextX86(context_x86_writer_1->context(), kSeed1);\n  thread_writer_1->SetContext(std::move(context_x86_writer_1));\n\n  thread_list_writer->AddThread(std::move(thread_writer_1));\n\n  constexpr uint32_t kThreadID2 = 3333333;\n  constexpr uint32_t kSuspendCount2 = 333333;\n  constexpr uint32_t kPriorityClass2 = 33333;\n  constexpr uint32_t kPriority2 = 3333;\n  constexpr uint64_t kTEB2 = 333;\n  constexpr uint64_t kMemoryBase2 = 0x3330;\n  constexpr size_t kMemorySize2 = 48;\n  constexpr uint8_t kMemoryValue2 = 33;\n  constexpr uint32_t kSeed2 = 3;\n\n  auto thread_writer_2 = std::make_unique<MinidumpThreadWriter>();\n  thread_writer_2->SetThreadID(kThreadID2);\n  thread_writer_2->SetSuspendCount(kSuspendCount2);\n  thread_writer_2->SetPriorityClass(kPriorityClass2);\n  thread_writer_2->SetPriority(kPriority2);\n  thread_writer_2->SetTEB(kTEB2);\n\n  auto memory_writer_2 = std::make_unique<TestMinidumpMemoryWriter>(\n      kMemoryBase2, kMemorySize2, kMemoryValue2);\n  thread_writer_2->SetStack(std::move(memory_writer_2));\n\n  auto context_x86_writer_2 = std::make_unique<MinidumpContextX86Writer>();\n  InitializeMinidumpContextX86(context_x86_writer_2->context(), kSeed2);\n  thread_writer_2->SetContext(std::move(context_x86_writer_2));\n\n  thread_list_writer->AddThread(std::move(thread_writer_2));\n\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer)));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(\n      string_file.string().size(),\n      sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) +\n          sizeof(MINIDUMP_THREAD_LIST) + 3 * sizeof(MINIDUMP_THREAD) +\n          sizeof(MINIDUMP_MEMORY_LIST) +\n          3 * sizeof(MINIDUMP_MEMORY_DESCRIPTOR) +\n          3 * sizeof(MinidumpContextX86) + kMemorySize0 + kMemorySize1 +\n          kMemorySize2 + 12);  // 12 for alignment\n\n  const MINIDUMP_THREAD_LIST* thread_list = nullptr;\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadListStream(string_file.string(), &thread_list, &memory_list));\n\n  EXPECT_EQ(thread_list->NumberOfThreads, 3u);\n  EXPECT_EQ(memory_list->NumberOfMemoryRanges, 3u);\n\n  {\n    SCOPED_TRACE(\"thread 0\");\n\n    MINIDUMP_THREAD expected = {};\n    expected.ThreadId = kThreadID0;\n    expected.SuspendCount = kSuspendCount0;\n    expected.PriorityClass = kPriorityClass0;\n    expected.Priority = kPriority0;\n    expected.Teb = kTEB0;\n    expected.Stack.StartOfMemoryRange = kMemoryBase0;\n    expected.Stack.Memory.DataSize = kMemorySize0;\n    expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);\n\n    const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;\n    const MinidumpContextX86* observed_context = nullptr;\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectThread(&expected,\n                     &thread_list->Threads[0],\n                     string_file.string(),\n                     &observed_stack,\n                     reinterpret_cast<const void**>(&observed_context)));\n\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,\n                                                  observed_stack,\n                                                  string_file.string(),\n                                                  kMemoryValue0,\n                                                  false));\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectMinidumpContextX86(kSeed0, observed_context, false));\n    ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(\n        observed_stack, &memory_list->MemoryRanges[0]));\n  }\n\n  {\n    SCOPED_TRACE(\"thread 1\");\n\n    MINIDUMP_THREAD expected = {};\n    expected.ThreadId = kThreadID1;\n    expected.SuspendCount = kSuspendCount1;\n    expected.PriorityClass = kPriorityClass1;\n    expected.Priority = kPriority1;\n    expected.Teb = kTEB1;\n    expected.Stack.StartOfMemoryRange = kMemoryBase1;\n    expected.Stack.Memory.DataSize = kMemorySize1;\n    expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);\n\n    const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;\n    const MinidumpContextX86* observed_context = nullptr;\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectThread(&expected,\n                     &thread_list->Threads[1],\n                     string_file.string(),\n                     &observed_stack,\n                     reinterpret_cast<const void**>(&observed_context)));\n\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,\n                                                  observed_stack,\n                                                  string_file.string(),\n                                                  kMemoryValue1,\n                                                  false));\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectMinidumpContextX86(kSeed1, observed_context, false));\n    ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(\n        observed_stack, &memory_list->MemoryRanges[1]));\n  }\n\n  {\n    SCOPED_TRACE(\"thread 2\");\n\n    MINIDUMP_THREAD expected = {};\n    expected.ThreadId = kThreadID2;\n    expected.SuspendCount = kSuspendCount2;\n    expected.PriorityClass = kPriorityClass2;\n    expected.Priority = kPriority2;\n    expected.Teb = kTEB2;\n    expected.Stack.StartOfMemoryRange = kMemoryBase2;\n    expected.Stack.Memory.DataSize = kMemorySize2;\n    expected.ThreadContext.DataSize = sizeof(MinidumpContextX86);\n\n    const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;\n    const MinidumpContextX86* observed_context = nullptr;\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectThread(&expected,\n                     &thread_list->Threads[2],\n                     string_file.string(),\n                     &observed_stack,\n                     reinterpret_cast<const void**>(&observed_context)));\n\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack,\n                                                  observed_stack,\n                                                  string_file.string(),\n                                                  kMemoryValue2,\n                                                  true));\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectMinidumpContextX86(kSeed2, observed_context, false));\n    ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(\n        observed_stack, &memory_list->MemoryRanges[2]));\n  }\n}\n\nstruct InitializeFromSnapshotX86Traits {\n  using MinidumpContextType = MinidumpContextX86;\n  static void InitializeCPUContext(CPUContext* context, uint32_t seed) {\n    return InitializeCPUContextX86(context, seed);\n  }\n  static void ExpectMinidumpContext(\n      uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot) {\n    return ExpectMinidumpContextX86(expect_seed, observed, snapshot);\n  }\n};\n\nstruct InitializeFromSnapshotAMD64Traits {\n  using MinidumpContextType = MinidumpContextAMD64;\n  static void InitializeCPUContext(CPUContext* context, uint32_t seed) {\n    return InitializeCPUContextX86_64(context, seed);\n  }\n  static void ExpectMinidumpContext(uint32_t expect_seed,\n                                    const MinidumpContextAMD64* observed,\n                                    bool snapshot) {\n    return ExpectMinidumpContextAMD64(expect_seed, observed, snapshot);\n  }\n};\n\nstruct InitializeFromSnapshotNoContextTraits {\n  using MinidumpContextType = MinidumpContextX86;\n  static void InitializeCPUContext(CPUContext* context, uint32_t seed) {\n    context->architecture = kCPUArchitectureUnknown;\n  }\n  static void ExpectMinidumpContext(uint32_t expect_seed,\n                                    const MinidumpContextX86* observed,\n                                    bool snapshot) {\n    FAIL();\n  }\n};\n\ntemplate <typename Traits>\nvoid RunInitializeFromSnapshotTest(bool thread_id_collision) {\n  using MinidumpContextType = typename Traits::MinidumpContextType;\n  MINIDUMP_THREAD expect_threads[3] = {};\n  uint64_t thread_ids[std::size(expect_threads)] = {};\n  uint8_t memory_values[std::size(expect_threads)] = {};\n  uint32_t context_seeds[std::size(expect_threads)] = {};\n  MINIDUMP_MEMORY_DESCRIPTOR tebs[std::size(expect_threads)] = {};\n\n  constexpr size_t kTebSize = 1024;\n\n  expect_threads[0].ThreadId = 1;\n  expect_threads[0].SuspendCount = 2;\n  expect_threads[0].Priority = 3;\n  expect_threads[0].Teb = 0x0123456789abcdef;\n  expect_threads[0].Stack.StartOfMemoryRange = 0x1000;\n  expect_threads[0].Stack.Memory.DataSize = 0x100;\n  expect_threads[0].ThreadContext.DataSize = sizeof(MinidumpContextType);\n  memory_values[0] = 'A';\n  context_seeds[0] = 0x80000000;\n  tebs[0].StartOfMemoryRange = expect_threads[0].Teb;\n  tebs[0].Memory.DataSize = kTebSize;\n\n  // The thread at index 1 has no stack.\n  expect_threads[1].ThreadId = 11;\n  expect_threads[1].SuspendCount = 12;\n  expect_threads[1].Priority = 13;\n  expect_threads[1].Teb = 0x1111111111111111;\n  expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType);\n  context_seeds[1] = 0x40000001;\n  tebs[1].StartOfMemoryRange = expect_threads[1].Teb;\n  tebs[1].Memory.DataSize = kTebSize;\n\n  expect_threads[2].ThreadId = 21;\n  expect_threads[2].SuspendCount = 22;\n  expect_threads[2].Priority = 23;\n  expect_threads[2].Teb = 0xfedcba9876543210;\n  expect_threads[2].Stack.StartOfMemoryRange = 0x3000;\n  expect_threads[2].Stack.Memory.DataSize = 0x300;\n  expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType);\n  memory_values[2] = 'd';\n  context_seeds[2] = 0x20000002;\n  tebs[2].StartOfMemoryRange = expect_threads[2].Teb;\n  tebs[2].Memory.DataSize = kTebSize;\n\n  if (thread_id_collision) {\n    thread_ids[0] = 0x0123456700000001;\n    thread_ids[1] = 0x89abcdef00000001;\n    thread_ids[2] = 4;\n    expect_threads[0].ThreadId = 0;\n    expect_threads[1].ThreadId = 1;\n    expect_threads[2].ThreadId = 2;\n  } else {\n    thread_ids[0] = 1;\n    thread_ids[1] = 11;\n    thread_ids[2] = 22;\n    expect_threads[0].ThreadId = static_cast<uint32_t>(thread_ids[0]);\n    expect_threads[1].ThreadId = static_cast<uint32_t>(thread_ids[1]);\n    expect_threads[2].ThreadId = static_cast<uint32_t>(thread_ids[2]);\n  }\n\n  std::vector<std::unique_ptr<TestThreadSnapshot>> thread_snapshots_owner;\n  std::vector<const ThreadSnapshot*> thread_snapshots;\n  for (size_t index = 0; index < std::size(expect_threads); ++index) {\n    thread_snapshots_owner.push_back(std::make_unique<TestThreadSnapshot>());\n    TestThreadSnapshot* thread_snapshot = thread_snapshots_owner.back().get();\n\n    thread_snapshot->SetThreadID(thread_ids[index]);\n    thread_snapshot->SetSuspendCount(expect_threads[index].SuspendCount);\n    thread_snapshot->SetPriority(expect_threads[index].Priority);\n    thread_snapshot->SetThreadSpecificDataAddress(expect_threads[index].Teb);\n\n    if (expect_threads[index].Stack.Memory.DataSize) {\n      auto memory_snapshot = std::make_unique<TestMemorySnapshot>();\n      memory_snapshot->SetAddress(\n          expect_threads[index].Stack.StartOfMemoryRange);\n      memory_snapshot->SetSize(expect_threads[index].Stack.Memory.DataSize);\n      memory_snapshot->SetValue(memory_values[index]);\n      thread_snapshot->SetStack(std::move(memory_snapshot));\n    }\n\n    Traits::InitializeCPUContext(thread_snapshot->MutableContext(),\n                                 context_seeds[index]);\n\n    auto teb_snapshot = std::make_unique<TestMemorySnapshot>();\n    teb_snapshot->SetAddress(expect_threads[index].Teb);\n    teb_snapshot->SetSize(kTebSize);\n    teb_snapshot->SetValue(static_cast<char>('t' + index));\n    thread_snapshot->AddExtraMemory(std::move(teb_snapshot));\n\n    thread_snapshots.push_back(thread_snapshot);\n  }\n\n  auto thread_list_writer = std::make_unique<MinidumpThreadListWriter>();\n  auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();\n  thread_list_writer->SetMemoryListWriter(memory_list_writer.get());\n  MinidumpThreadIDMap thread_id_map;\n  thread_list_writer->InitializeFromSnapshot(thread_snapshots, &thread_id_map);\n\n  MinidumpFileWriter minidump_file_writer;\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer)));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  const MINIDUMP_THREAD_LIST* thread_list = nullptr;\n  const MINIDUMP_MEMORY_LIST* memory_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetThreadListStream(string_file.string(), &thread_list, &memory_list));\n\n  ASSERT_EQ(thread_list->NumberOfThreads, 3u);\n  ASSERT_EQ(memory_list->NumberOfMemoryRanges, 5u);\n\n  size_t memory_index = 0;\n  for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS, index));\n\n    const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr;\n    const MINIDUMP_MEMORY_DESCRIPTOR** observed_stack_p =\n        expect_threads[index].Stack.Memory.DataSize ? &observed_stack : nullptr;\n    const MinidumpContextType* observed_context = nullptr;\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectThread(&expect_threads[index],\n                     &thread_list->Threads[index],\n                     string_file.string(),\n                     observed_stack_p,\n                     reinterpret_cast<const void**>(&observed_context)));\n\n    ASSERT_NO_FATAL_FAILURE(Traits::ExpectMinidumpContext(\n        context_seeds[index], observed_context, true));\n\n    if (observed_stack_p) {\n      ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptorAndContents(\n          &expect_threads[index].Stack,\n          observed_stack,\n          string_file.string(),\n          memory_values[index],\n          false));\n\n      ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor(\n          observed_stack, &memory_list->MemoryRanges[memory_index]));\n\n      ++memory_index;\n    }\n  }\n\n  for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) {\n    const MINIDUMP_MEMORY_DESCRIPTOR* memory =\n        &memory_list->MemoryRanges[memory_index];\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectMinidumpMemoryDescriptor(&tebs[index], memory));\n    std::string expected_data(kTebSize, static_cast<char>('t' + index));\n    std::string observed_data(&string_file.string()[memory->Memory.Rva],\n                              memory->Memory.DataSize);\n    EXPECT_EQ(observed_data, expected_data);\n    ++memory_index;\n  }\n}\n\nTEST(MinidumpThreadWriter, InitializeFromSnapshot_x86) {\n  RunInitializeFromSnapshotTest<InitializeFromSnapshotX86Traits>(false);\n}\n\nTEST(MinidumpThreadWriter, InitializeFromSnapshot_AMD64) {\n  RunInitializeFromSnapshotTest<InitializeFromSnapshotAMD64Traits>(false);\n}\n\nTEST(MinidumpThreadWriter, InitializeFromSnapshot_ThreadIDCollision) {\n  RunInitializeFromSnapshotTest<InitializeFromSnapshotX86Traits>(true);\n}\n\nTEST(MinidumpThreadWriterDeathTest, NoContext) {\n  MinidumpFileWriter minidump_file_writer;\n  auto thread_list_writer = std::make_unique<MinidumpThreadListWriter>();\n\n  auto thread_writer = std::make_unique<MinidumpThreadWriter>();\n\n  thread_list_writer->AddThread(std::move(thread_writer));\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer)));\n\n  StringFile string_file;\n  ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file),\n                     \"context_\");\n}\n\nTEST(MinidumpThreadWriterDeathTest, InitializeFromSnapshot_NoContext) {\n  ASSERT_DEATH_CHECK(\n      RunInitializeFromSnapshotTest<InitializeFromSnapshotNoContextTraits>(\n          false), \"context_\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_unloaded_module_writer.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_unloaded_module_writer.h\"\n\n#include <limits>\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"minidump/minidump_writer_util.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/in_range_cast.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nMinidumpUnloadedModuleWriter::MinidumpUnloadedModuleWriter()\n    : MinidumpWritable(), unloaded_module_(), name_() {}\n\nMinidumpUnloadedModuleWriter::~MinidumpUnloadedModuleWriter() {\n}\n\nvoid MinidumpUnloadedModuleWriter::InitializeFromSnapshot(\n    const UnloadedModuleSnapshot& unloaded_module_snapshot) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!name_);\n\n  SetName(unloaded_module_snapshot.Name());\n\n  SetImageBaseAddress(unloaded_module_snapshot.Address());\n  SetImageSize(InRangeCast<uint32_t>(unloaded_module_snapshot.Size(),\n                                     std::numeric_limits<uint32_t>::max()));\n  SetTimestamp(unloaded_module_snapshot.Timestamp());\n  SetChecksum(unloaded_module_snapshot.Checksum());\n}\n\nconst MINIDUMP_UNLOADED_MODULE*\nMinidumpUnloadedModuleWriter::MinidumpUnloadedModule() const {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return &unloaded_module_;\n}\n\nvoid MinidumpUnloadedModuleWriter::SetName(const std::string& name) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!name_) {\n    name_.reset(new internal::MinidumpUTF16StringWriter());\n  }\n  name_->SetUTF8(name);\n}\n\nvoid MinidumpUnloadedModuleWriter::SetTimestamp(time_t timestamp) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  internal::MinidumpWriterUtil::AssignTimeT(&unloaded_module_.TimeDateStamp,\n                                            timestamp);\n}\n\nbool MinidumpUnloadedModuleWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n  CHECK(name_);\n\n  if (!MinidumpWritable::Freeze()) {\n    return false;\n  }\n\n  name_->RegisterRVA(&unloaded_module_.ModuleNameRva);\n\n  return true;\n}\n\nsize_t MinidumpUnloadedModuleWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  // This object doesn’t directly write anything itself. Its\n  // MINIDUMP_UNLOADED_MODULE is written by its parent as part of a\n  // MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing\n  // themselves.\n  return 0;\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpUnloadedModuleWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  DCHECK(name_);\n\n  std::vector<MinidumpWritable*> children(1, name_.get());\n  return children;\n}\n\nbool MinidumpUnloadedModuleWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  // This object doesn’t directly write anything itself. Its\n  // MINIDUMP_UNLOADED_MODULE is written by its parent as part of a\n  // MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing\n  // themselves.\n  return true;\n}\n\nMinidumpUnloadedModuleListWriter::MinidumpUnloadedModuleListWriter()\n    : MinidumpStreamWriter(),\n      unloaded_modules_(),\n      unloaded_module_list_base_() {}\n\nMinidumpUnloadedModuleListWriter::~MinidumpUnloadedModuleListWriter() {\n}\n\nvoid MinidumpUnloadedModuleListWriter::InitializeFromSnapshot(\n    const std::vector<UnloadedModuleSnapshot>& unloaded_module_snapshots) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(unloaded_modules_.empty());\n\n  for (const UnloadedModuleSnapshot& unloaded_module_snapshot :\n       unloaded_module_snapshots) {\n    auto unloaded_module = std::make_unique<MinidumpUnloadedModuleWriter>();\n    unloaded_module->InitializeFromSnapshot(unloaded_module_snapshot);\n    AddUnloadedModule(std::move(unloaded_module));\n  }\n}\n\nvoid MinidumpUnloadedModuleListWriter::AddUnloadedModule(\n    std::unique_ptr<MinidumpUnloadedModuleWriter> unloaded_module) {\n  DCHECK_EQ(state(), kStateMutable);\n\n  unloaded_modules_.push_back(std::move(unloaded_module));\n}\n\nbool MinidumpUnloadedModuleListWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n\n  if (!MinidumpStreamWriter::Freeze()) {\n    return false;\n  }\n\n  unloaded_module_list_base_.SizeOfHeader =\n      sizeof(MINIDUMP_UNLOADED_MODULE_LIST);\n  unloaded_module_list_base_.SizeOfEntry = sizeof(MINIDUMP_UNLOADED_MODULE);\n\n  size_t unloaded_module_count = unloaded_modules_.size();\n  if (!AssignIfInRange(&unloaded_module_list_base_.NumberOfEntries,\n                       unloaded_module_count)) {\n    LOG(ERROR) << \"unloaded_module_count \" << unloaded_module_count\n               << \" out of range\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t MinidumpUnloadedModuleListWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return sizeof(unloaded_module_list_base_) +\n         unloaded_modules_.size() * sizeof(MINIDUMP_UNLOADED_MODULE);\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpUnloadedModuleListWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  std::vector<MinidumpWritable*> children;\n  for (const auto& unloaded_module : unloaded_modules_) {\n    children.push_back(unloaded_module.get());\n  }\n\n  return children;\n}\n\nbool MinidumpUnloadedModuleListWriter::WriteObject(\n    FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  WritableIoVec iov;\n  iov.iov_base = &unloaded_module_list_base_;\n  iov.iov_len = sizeof(unloaded_module_list_base_);\n  std::vector<WritableIoVec> iovecs(1, iov);\n\n  for (const auto& unloaded_module : unloaded_modules_) {\n    iov.iov_base = unloaded_module->MinidumpUnloadedModule();\n    iov.iov_len = sizeof(MINIDUMP_UNLOADED_MODULE);\n    iovecs.push_back(iov);\n  }\n\n  return file_writer->WriteIoVec(&iovecs);\n}\n\nMinidumpStreamType MinidumpUnloadedModuleListWriter::StreamType() const {\n  return kMinidumpStreamTypeUnloadedModuleList;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_unloaded_module_writer.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_string_writer.h\"\n#include \"minidump/minidump_writable.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n\nnamespace crashpad {\n\n//! \\brief The writer for a MINIDUMP_UNLOADED_MODULE object in a minidump file.\n//!\n//! Because MINIDUMP_UNLOADED_MODULE objects only appear as elements of\n//! MINIDUMP_UNLOADED_MODULE_LIST objects, this class does not write any data on\n//! its own. It makes its MINIDUMP_UNLOADED_MODULE data available to its\n//! MinidumpUnloadedModuleListWriter parent, which writes it as part of a\n//! MINIDUMP_UNLOADED_MODULE_LIST.\nclass MinidumpUnloadedModuleWriter final : public internal::MinidumpWritable {\n public:\n  MinidumpUnloadedModuleWriter();\n\n  MinidumpUnloadedModuleWriter(const MinidumpUnloadedModuleWriter&) = delete;\n  MinidumpUnloadedModuleWriter& operator=(const MinidumpUnloadedModuleWriter&) =\n      delete;\n\n  ~MinidumpUnloadedModuleWriter() override;\n\n  //! \\brief Initializes the MINIDUMP_UNLOADED_MODULE based on \\a\n  //! unloaded_module_snapshot.\n  //!\n  //! \\param[in] unloaded_module_snapshot The unloaded module snapshot to use as\n  //!     source data.\n  //!\n  //! \\note Valid in #kStateMutable. No mutator methods may be called before\n  //!     this method, and it is not normally necessary to call any mutator\n  //!     methods after this method.\n  void InitializeFromSnapshot(\n      const UnloadedModuleSnapshot& unloaded_module_snapshot);\n\n  //! \\brief Returns a MINIDUMP_UNLOADED_MODULE referencing this object’s data.\n  //!\n  //! This method is expected to be called by a MinidumpUnloadedModuleListWriter\n  //! in order to obtain a MINIDUMP_UNLOADED_MODULE to include in its list.\n  //!\n  //! \\note Valid in #kStateWritable.\n  const MINIDUMP_UNLOADED_MODULE* MinidumpUnloadedModule() const;\n\n  //! \\brief Arranges for MINIDUMP_UNLOADED_MODULE::ModuleNameRva to point to a\n  //!     MINIDUMP_STRING containing \\a name.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetName(const std::string& name);\n\n  //! \\brief Sets MINIDUMP_UNLOADED_MODULE::BaseOfImage.\n  void SetImageBaseAddress(uint64_t image_base_address) {\n    unloaded_module_.BaseOfImage = image_base_address;\n  }\n\n  //! \\brief Sets MINIDUMP_UNLOADED_MODULE::SizeOfImage.\n  void SetImageSize(uint32_t image_size) {\n    unloaded_module_.SizeOfImage = image_size;\n  }\n\n  //! \\brief Sets MINIDUMP_UNLOADED_MODULE::CheckSum.\n  void SetChecksum(uint32_t checksum) { unloaded_module_.CheckSum = checksum; }\n\n  //! \\brief Sets MINIDUMP_UNLOADED_MODULE::TimeDateStamp.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void SetTimestamp(time_t timestamp);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  MINIDUMP_UNLOADED_MODULE unloaded_module_;\n  std::unique_ptr<internal::MinidumpUTF16StringWriter> name_;\n};\n\n//! \\brief The writer for a MINIDUMP_UNLOADED_MODULE_LIST stream in a minidump\n//!     file, containing a list of MINIDUMP_UNLOADED_MODULE objects.\nclass MinidumpUnloadedModuleListWriter final\n    : public internal::MinidumpStreamWriter {\n public:\n  MinidumpUnloadedModuleListWriter();\n\n  MinidumpUnloadedModuleListWriter(const MinidumpUnloadedModuleListWriter&) =\n      delete;\n  MinidumpUnloadedModuleListWriter& operator=(\n      const MinidumpUnloadedModuleListWriter&) = delete;\n\n  ~MinidumpUnloadedModuleListWriter() override;\n\n  //! \\brief Adds an initialized MINIDUMP_UNLOADED_MODULE for each unloaded\n  //!     module in \\a unloaded_module_snapshots to the\n  //!     MINIDUMP_UNLOADED_MODULE_LIST.\n  //!\n  //! \\param[in] unloaded_module_snapshots The unloaded module snapshots to use\n  //!     as source data.\n  //!\n  //! \\note Valid in #kStateMutable. AddUnloadedModule() may not be called\n  //!     before this this method, and it is not normally necessary to call\n  //!     AddUnloadedModule() after this method.\n  void InitializeFromSnapshot(\n      const std::vector<UnloadedModuleSnapshot>& unloaded_module_snapshots);\n\n  //! \\brief Adds a MinidumpUnloadedModuleWriter to the\n  //!     MINIDUMP_UNLOADED_MODULE_LIST.\n  //!\n  //! This object takes ownership of \\a unloaded_module and becomes its parent\n  //! in the overall tree of internal::MinidumpWritable objects.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void AddUnloadedModule(\n      std::unique_ptr<MinidumpUnloadedModuleWriter> unloaded_module);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  std::vector<std::unique_ptr<MinidumpUnloadedModuleWriter>> unloaded_modules_;\n  MINIDUMP_UNLOADED_MODULE_LIST unloaded_module_list_base_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_unloaded_module_writer_test.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_unloaded_module_writer.h\"\n\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid ExpectUnloadedModule(const MINIDUMP_UNLOADED_MODULE* expected,\n                          const MINIDUMP_UNLOADED_MODULE* observed,\n                          const std::string& file_contents,\n                          const std::string& expected_module_name) {\n  EXPECT_EQ(observed->BaseOfImage, expected->BaseOfImage);\n  EXPECT_EQ(observed->SizeOfImage, expected->SizeOfImage);\n  EXPECT_EQ(observed->CheckSum, expected->CheckSum);\n  EXPECT_EQ(observed->TimeDateStamp, expected->TimeDateStamp);\n  EXPECT_NE(observed->ModuleNameRva, 0u);\n  std::u16string observed_module_name_utf16 =\n      MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva);\n  std::u16string expected_module_name_utf16 =\n      base::UTF8ToUTF16(expected_module_name);\n  EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16);\n}\n\nvoid GetUnloadedModuleListStream(\n    const std::string& file_contents,\n    const MINIDUMP_UNLOADED_MODULE_LIST** unloaded_module_list) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kUnloadedModuleListStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n  constexpr size_t kUnloadedModulesOffset =\n      kUnloadedModuleListStreamOffset + sizeof(MINIDUMP_UNLOADED_MODULE_LIST);\n\n  ASSERT_GE(file_contents.size(), kUnloadedModulesOffset);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeUnloadedModuleList);\n  EXPECT_EQ(directory[0].Location.Rva, kUnloadedModuleListStreamOffset);\n\n  *unloaded_module_list =\n      MinidumpWritableAtLocationDescriptor<MINIDUMP_UNLOADED_MODULE_LIST>(\n          file_contents, directory[0].Location);\n  ASSERT_TRUE(unloaded_module_list);\n}\n\nTEST(MinidumpUnloadedModuleWriter, EmptyModule) {\n  MinidumpFileWriter minidump_file_writer;\n  auto unloaded_module_list_writer =\n      std::make_unique<MinidumpUnloadedModuleListWriter>();\n\n  static constexpr char kModuleName[] = \"test_dll\";\n\n  auto unloaded_module_writer =\n      std::make_unique<MinidumpUnloadedModuleWriter>();\n  unloaded_module_writer->SetName(kModuleName);\n\n  unloaded_module_list_writer->AddUnloadedModule(\n      std::move(unloaded_module_writer));\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(unloaded_module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_UNLOADED_MODULE_LIST) +\n                1 * sizeof(MINIDUMP_UNLOADED_MODULE));\n\n  const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list));\n\n  EXPECT_EQ(unloaded_module_list->NumberOfEntries, 1u);\n\n  MINIDUMP_UNLOADED_MODULE expected = {};\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectUnloadedModule(&expected,\n                           reinterpret_cast<const MINIDUMP_UNLOADED_MODULE*>(\n                               &unloaded_module_list[1]),\n                           string_file.string(),\n                           kModuleName));\n}\n\nTEST(MinidumpUnloadedModuleWriter, OneModule) {\n  MinidumpFileWriter minidump_file_writer;\n  auto unloaded_module_list_writer =\n      std::make_unique<MinidumpUnloadedModuleListWriter>();\n\n  static constexpr char kModuleName[] = \"statically_linked\";\n  constexpr uint64_t kModuleBase = 0x10da69000;\n  constexpr uint32_t kModuleSize = 0x1000;\n  constexpr uint32_t kChecksum = 0x76543210;\n  constexpr time_t kTimestamp = 0x386d4380;\n\n  auto unloaded_module_writer =\n      std::make_unique<MinidumpUnloadedModuleWriter>();\n  unloaded_module_writer->SetName(kModuleName);\n  unloaded_module_writer->SetImageBaseAddress(kModuleBase);\n  unloaded_module_writer->SetImageSize(kModuleSize);\n  unloaded_module_writer->SetChecksum(kChecksum);\n  unloaded_module_writer->SetTimestamp(kTimestamp);\n\n  unloaded_module_list_writer->AddUnloadedModule(\n      std::move(unloaded_module_writer));\n  ASSERT_TRUE(\n      minidump_file_writer.AddStream(std::move(unloaded_module_list_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_GT(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +\n                sizeof(MINIDUMP_UNLOADED_MODULE_LIST) +\n                1 * sizeof(MINIDUMP_UNLOADED_MODULE));\n\n  const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr;\n  ASSERT_NO_FATAL_FAILURE(\n      GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list));\n\n  EXPECT_EQ(unloaded_module_list->NumberOfEntries, 1u);\n\n  MINIDUMP_UNLOADED_MODULE expected = {};\n  expected.BaseOfImage = kModuleBase;\n  expected.SizeOfImage = kModuleSize;\n  expected.CheckSum = kChecksum;\n  expected.TimeDateStamp = kTimestamp;\n\n  ASSERT_NO_FATAL_FAILURE(\n      ExpectUnloadedModule(&expected,\n                           reinterpret_cast<const MINIDUMP_UNLOADED_MODULE*>(\n                               &unloaded_module_list[1]),\n                           string_file.string(),\n                           kModuleName));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_user_extension_stream_data_source.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_user_extension_stream_data_source.h\"\n\nnamespace crashpad {\n\nMinidumpUserExtensionStreamDataSource::MinidumpUserExtensionStreamDataSource(\n    uint32_t stream_type)\n    : stream_type_(static_cast<MinidumpStreamType>(stream_type)) {}\n\nMinidumpUserExtensionStreamDataSource::\n    ~MinidumpUserExtensionStreamDataSource() {}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_user_extension_stream_data_source.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_USER_EXTENSION_STREAM_DATA_SOURCE_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_USER_EXTENSION_STREAM_DATA_SOURCE_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n\n#include \"minidump/minidump_extensions.h\"\n\nnamespace crashpad {\n\n//! \\brief Describes a user extension data stream in a minidump.\nclass MinidumpUserExtensionStreamDataSource {\n public:\n  //! \\brief An interface implemented by readers of\n  //!     MinidumpUserExtensionStreamDataSource.\n  class Delegate {\n   public:\n    //! \\brief Called by  MinidumpUserExtensionStreamDataSource::Read() to\n    //!     provide data requested by a call to that method.\n    //!\n    //! \\param[in] data A pointer to the data that was read. The callee does not\n    //!     take ownership of this data. This data is only valid for the\n    //!     duration of the call to this method. This parameter may be `nullptr`\n    //!     if \\a size is `0`.\n    //! \\param[in] size The size of the data that was read.\n    //!\n    //! \\return `true` on success, `false` on failure.\n    //!     MinidumpUserExtensionStreamDataSource::ReadStreamData() will use\n    //!     this as its own return value.\n    virtual bool ExtensionStreamDataSourceRead(const void* data,\n                                               size_t size) = 0;\n\n   protected:\n    ~Delegate() {}\n  };\n\n  //! \\brief Constructs a MinidumpUserExtensionStreamDataSource.\n  //!\n  //! \\param[in] stream_type The type of the user extension stream.\n  explicit MinidumpUserExtensionStreamDataSource(uint32_t stream_type);\n\n  MinidumpUserExtensionStreamDataSource(\n      const MinidumpUserExtensionStreamDataSource&) = delete;\n  MinidumpUserExtensionStreamDataSource& operator=(\n      const MinidumpUserExtensionStreamDataSource&) = delete;\n\n  virtual ~MinidumpUserExtensionStreamDataSource();\n\n  MinidumpStreamType stream_type() const { return stream_type_; }\n\n  //! \\brief The size of this data stream.\n  virtual size_t StreamDataSize() = 0;\n\n  //! \\brief Calls Delegate::UserStreamDataSourceRead(), providing it with\n  //!     the stream data.\n  //!\n  //! Implementations do not necessarily compute the stream data prior to\n  //! this method being called. The stream data may be computed or loaded\n  //! lazily and may be discarded after being passed to the delegate.\n  //!\n  //! \\return `false` on failure, otherwise, the return value of\n  //!     Delegate::ExtensionStreamDataSourceRead(), which should be `true` on\n  //!     success and `false` on failure.\n  virtual bool ReadStreamData(Delegate* delegate) = 0;\n\n private:\n  MinidumpStreamType stream_type_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_USER_EXTENSION_STREAM_DATA_SOURCE_H_\n"
  },
  {
    "path": "minidump/minidump_user_stream_writer.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_user_stream_writer.h\"\n\n#include \"base/check_op.h\"\n#include \"util/file/file_writer.h\"\n\nnamespace crashpad {\n\nclass MinidumpUserStreamWriter::ContentsWriter {\n public:\n  virtual ~ContentsWriter() {}\n  virtual bool WriteContents(FileWriterInterface* writer) = 0;\n  virtual size_t GetSize() const = 0;\n};\n\nclass MinidumpUserStreamWriter::SnapshotContentsWriter final\n    : public MinidumpUserStreamWriter::ContentsWriter,\n      public MemorySnapshot::Delegate {\n public:\n  explicit SnapshotContentsWriter(const MemorySnapshot* snapshot)\n      : snapshot_(snapshot), writer_(nullptr) {}\n\n  SnapshotContentsWriter(const SnapshotContentsWriter&) = delete;\n  SnapshotContentsWriter& operator=(const SnapshotContentsWriter&) = delete;\n\n  bool WriteContents(FileWriterInterface* writer) override {\n    DCHECK(!writer_);\n\n    writer_ = writer;\n    if (!snapshot_)\n      return true;\n\n    return snapshot_->Read(this);\n  }\n\n  size_t GetSize() const override { return snapshot_ ? snapshot_->Size() : 0; }\n\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override {\n    return writer_->Write(data, size);\n  }\n\n private:\n  const MemorySnapshot* snapshot_;\n  FileWriterInterface* writer_;\n};\n\nclass MinidumpUserStreamWriter::ExtensionStreamContentsWriter final\n    : public MinidumpUserStreamWriter::ContentsWriter,\n      public MinidumpUserExtensionStreamDataSource::Delegate {\n public:\n  explicit ExtensionStreamContentsWriter(\n      std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source)\n      : data_source_(std::move(data_source)), writer_(nullptr) {}\n\n  ExtensionStreamContentsWriter(const ExtensionStreamContentsWriter&) = delete;\n  ExtensionStreamContentsWriter& operator=(\n      const ExtensionStreamContentsWriter&) = delete;\n\n  bool WriteContents(FileWriterInterface* writer) override {\n    DCHECK(!writer_);\n\n    writer_ = writer;\n    return data_source_->ReadStreamData(this);\n  }\n\n  size_t GetSize() const override { return data_source_->StreamDataSize(); }\n\n  bool ExtensionStreamDataSourceRead(const void* data, size_t size) override {\n    return writer_->Write(data, size);\n  }\n\n private:\n  std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source_;\n  FileWriterInterface* writer_;\n};\n\nMinidumpUserStreamWriter::MinidumpUserStreamWriter() : stream_type_() {}\n\nMinidumpUserStreamWriter::~MinidumpUserStreamWriter() {\n}\n\nvoid MinidumpUserStreamWriter::InitializeFromSnapshot(\n    const UserMinidumpStream* stream) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!contents_writer_.get());\n\n  stream_type_ = static_cast<MinidumpStreamType>(stream->stream_type());\n  contents_writer_ = std::make_unique<SnapshotContentsWriter>(stream->memory());\n}\n\nvoid MinidumpUserStreamWriter::InitializeFromUserExtensionStream(\n    std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source) {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK(!contents_writer_.get());\n\n  stream_type_ = data_source->stream_type();\n  contents_writer_ =\n      std::make_unique<ExtensionStreamContentsWriter>(std::move(data_source));\n}\n\nbool MinidumpUserStreamWriter::Freeze() {\n  DCHECK_EQ(state(), kStateMutable);\n  DCHECK_NE(stream_type_, 0u);\n  return MinidumpStreamWriter::Freeze();\n}\n\nsize_t MinidumpUserStreamWriter::SizeOfObject() {\n  DCHECK_GE(state(), kStateFrozen);\n\n  return contents_writer_->GetSize();\n}\n\nstd::vector<internal::MinidumpWritable*>\nMinidumpUserStreamWriter::Children() {\n  DCHECK_GE(state(), kStateFrozen);\n  return std::vector<internal::MinidumpWritable*>();\n}\n\nbool MinidumpUserStreamWriter::WriteObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state(), kStateWritable);\n\n  return contents_writer_->WriteContents(file_writer);\n}\n\nMinidumpStreamType MinidumpUserStreamWriter::StreamType() const {\n  return static_cast<MinidumpStreamType>(stream_type_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_user_stream_writer.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n\n#include <memory>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_stream_writer.h\"\n#include \"minidump/minidump_writable.h\"\n#include \"minidump/minidump_user_extension_stream_data_source.h\"\n#include \"snapshot/module_snapshot.h\"\n\nnamespace crashpad {\n\n//! \\brief The writer for a MINIDUMP_USER_STREAM in a minidump file.\nclass MinidumpUserStreamWriter final : public internal::MinidumpStreamWriter {\n public:\n  MinidumpUserStreamWriter();\n\n  MinidumpUserStreamWriter(const MinidumpUserStreamWriter&) = delete;\n  MinidumpUserStreamWriter& operator=(const MinidumpUserStreamWriter&) = delete;\n\n  ~MinidumpUserStreamWriter() override;\n\n  //! \\brief Initializes a MINIDUMP_USER_STREAM based on \\a stream.\n  //!\n  //! \\param[in] stream The memory and stream type to use as source data.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void InitializeFromSnapshot(const UserMinidumpStream* stream);\n\n  //! \\brief Initializes a MINIDUMP_USER_STREAM based on \\a data_source.\n  //!\n  //! \\param[in] data_source The content and type of the stream.\n  //!\n  //! \\note Valid in #kStateMutable.\n  void InitializeFromUserExtensionStream(\n      std::unique_ptr<MinidumpUserExtensionStreamDataSource> data_source);\n\n protected:\n  // MinidumpWritable:\n  bool Freeze() override;\n  size_t SizeOfObject() override;\n  std::vector<internal::MinidumpWritable*> Children() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n  // MinidumpStreamWriter:\n  MinidumpStreamType StreamType() const override;\n\n private:\n  class ContentsWriter;\n  class SnapshotContentsWriter;\n  class ExtensionStreamContentsWriter;\n\n  std::unique_ptr<ContentsWriter> contents_writer_;\n\n  MinidumpStreamType stream_type_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_\n"
  },
  {
    "path": "minidump/minidump_user_stream_writer_test.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_user_stream_writer.h\"\n\n#include <string>\n#include <utility>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n#include \"minidump/test/minidump_user_extension_stream_util.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n#include \"snapshot/test/test_memory_snapshot.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// The user stream is expected to be the only stream.\nvoid GetUserStream(const std::string& file_contents,\n                   MINIDUMP_LOCATION_DESCRIPTOR* user_stream_location,\n                   uint32_t stream_type,\n                   size_t stream_size) {\n  constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);\n  constexpr size_t kUserStreamOffset =\n      kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);\n\n  const MINIDUMP_DIRECTORY* directory;\n  const MINIDUMP_HEADER* header =\n      MinidumpHeaderAtStart(file_contents, &directory);\n  ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));\n  ASSERT_TRUE(directory);\n\n  constexpr size_t kDirectoryIndex = 0;\n\n  ASSERT_EQ(directory[kDirectoryIndex].StreamType, stream_type);\n  EXPECT_EQ(directory[kDirectoryIndex].Location.Rva, kUserStreamOffset);\n  EXPECT_EQ(directory[kDirectoryIndex].Location.DataSize, stream_size);\n  *user_stream_location = directory[kDirectoryIndex].Location;\n}\n\nconstexpr MinidumpStreamType kTestStreamId =\n    static_cast<MinidumpStreamType>(0x123456);\n\nTEST(MinidumpUserStreamWriter, InitializeFromSnapshotNoData) {\n  MinidumpFileWriter minidump_file_writer;\n  auto user_stream_writer = std::make_unique<MinidumpUserStreamWriter>();\n  auto stream = std::make_unique<UserMinidumpStream>(kTestStreamId, nullptr);\n  user_stream_writer->InitializeFromSnapshot(stream.get());\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(user_stream_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY));\n\n  MINIDUMP_LOCATION_DESCRIPTOR user_stream_location;\n  ASSERT_NO_FATAL_FAILURE(GetUserStream(\n      string_file.string(), &user_stream_location, kTestStreamId, 0u));\n}\n\nTEST(MinidumpUserStreamWriter, InitializeFromUserExtensionStreamNoData) {\n  MinidumpFileWriter minidump_file_writer;\n  auto data_source = std::make_unique<test::BufferExtensionStreamDataSource>(\n      kTestStreamId, nullptr, 0);\n  auto user_stream_writer = std::make_unique<MinidumpUserStreamWriter>();\n  user_stream_writer->InitializeFromUserExtensionStream(std::move(data_source));\n  minidump_file_writer.AddStream(std::move(user_stream_writer));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY));\n\n  MINIDUMP_LOCATION_DESCRIPTOR user_stream_location;\n  ASSERT_NO_FATAL_FAILURE(GetUserStream(\n      string_file.string(), &user_stream_location, kTestStreamId, 0u));\n}\n\nTEST(MinidumpUserStreamWriter, InitializeFromSnapshotOneStream) {\n  MinidumpFileWriter minidump_file_writer;\n  auto user_stream_writer = std::make_unique<MinidumpUserStreamWriter>();\n\n  TestMemorySnapshot* test_data = new TestMemorySnapshot();\n  test_data->SetAddress(97865);\n  constexpr size_t kStreamSize = 128;\n  test_data->SetSize(kStreamSize);\n  test_data->SetValue('c');\n  auto stream = std::make_unique<UserMinidumpStream>(kTestStreamId, test_data);\n  user_stream_writer->InitializeFromSnapshot(stream.get());\n  ASSERT_TRUE(minidump_file_writer.AddStream(std::move(user_stream_writer)));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + kStreamSize);\n\n  MINIDUMP_LOCATION_DESCRIPTOR user_stream_location = {};\n  ASSERT_NO_FATAL_FAILURE(GetUserStream(\n      string_file.string(), &user_stream_location, kTestStreamId, kStreamSize));\n  const std::string stream_data = string_file.string().substr(\n      user_stream_location.Rva, user_stream_location.DataSize);\n  EXPECT_EQ(stream_data, std::string(kStreamSize, 'c'));\n}\n\nTEST(MinidumpUserStreamWriter, InitializeFromBufferOneStream) {\n  MinidumpFileWriter minidump_file_writer;\n\n  constexpr size_t kStreamSize = 128;\n  std::vector<uint8_t> data(kStreamSize, 'c');\n  auto data_source = std::make_unique<test::BufferExtensionStreamDataSource>(\n      kTestStreamId, &data[0], data.size());\n  auto user_stream_writer = std::make_unique<MinidumpUserStreamWriter>();\n  user_stream_writer->InitializeFromUserExtensionStream(std::move(data_source));\n  minidump_file_writer.AddStream(std::move(user_stream_writer));\n\n  StringFile string_file;\n  ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));\n\n  ASSERT_EQ(string_file.string().size(),\n            sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + kStreamSize);\n\n  MINIDUMP_LOCATION_DESCRIPTOR user_stream_location = {};\n  ASSERT_NO_FATAL_FAILURE(GetUserStream(\n      string_file.string(), &user_stream_location, kTestStreamId, kStreamSize));\n  const std::string stream_data = string_file.string().substr(\n      user_stream_location.Rva, user_stream_location.DataSize);\n  EXPECT_EQ(stream_data, std::string(kStreamSize, 'c'));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_writable.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_writable.h\"\n\n#include <stdint.h>\n\n#include <iterator>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace {\n\nconstexpr size_t kMaximumAlignment = 16;\n\n}  // namespace\n\nnamespace crashpad {\nnamespace internal {\n\nMinidumpWritable::~MinidumpWritable() {\n}\n\nbool MinidumpWritable::WriteEverything(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state_, kStateMutable);\n\n  if (!Freeze()) {\n    return false;\n  }\n\n  DCHECK_EQ(state_, kStateFrozen);\n\n  FileOffset offset = 0;\n  std::vector<MinidumpWritable*> write_sequence;\n  size_t size = WillWriteAtOffset(kPhaseEarly, &offset, &write_sequence);\n  if (size == kInvalidSize) {\n    return false;\n  }\n\n  offset += size;\n  if (WillWriteAtOffset(kPhaseLate, &offset, &write_sequence) == kInvalidSize) {\n    return false;\n  }\n\n  DCHECK_EQ(state_, kStateWritable);\n  DCHECK_EQ(write_sequence.front(), this);\n\n  for (MinidumpWritable* writable : write_sequence) {\n    if (!writable->WritePaddingAndObject(file_writer)) {\n      return false;\n    }\n  }\n\n  DCHECK_EQ(state_, kStateWritten);\n\n  return true;\n}\n\nvoid MinidumpWritable::RegisterRVA(RVA* rva) {\n  DCHECK_LE(state_, kStateFrozen);\n\n  registered_rvas_.push_back(rva);\n}\n\nvoid MinidumpWritable::RegisterRVA(RVA64* rva64) {\n  DCHECK_LE(state_, kStateFrozen);\n\n  registered_rva64s_.push_back(rva64);\n}\n\nvoid MinidumpWritable::RegisterLocationDescriptor(\n    MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor) {\n  DCHECK_LE(state_, kStateFrozen);\n\n  registered_location_descriptors_.push_back(location_descriptor);\n}\n\nvoid MinidumpWritable::RegisterLocationDescriptor(\n    MINIDUMP_LOCATION_DESCRIPTOR64* location_descriptor64) {\n  DCHECK_LE(state_, kStateFrozen);\n\n  registered_location_descriptor64s_.push_back(location_descriptor64);\n}\n\nMinidumpWritable::MinidumpWritable()\n    : registered_rvas_(),\n      registered_rva64s_(),\n      registered_location_descriptors_(),\n      registered_location_descriptor64s_(),\n      leading_pad_bytes_(0),\n      state_(kStateMutable) {}\n\nbool MinidumpWritable::Freeze() {\n  DCHECK_EQ(state_, kStateMutable);\n  state_ = kStateFrozen;\n\n  std::vector<MinidumpWritable*> children = Children();\n  for (MinidumpWritable* child : children) {\n    if (!child->Freeze()) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nsize_t MinidumpWritable::Alignment() {\n  DCHECK_GE(state_, kStateFrozen);\n\n  return 4;\n}\n\nstd::vector<MinidumpWritable*> MinidumpWritable::Children() {\n  DCHECK_GE(state_, kStateFrozen);\n\n  return std::vector<MinidumpWritable*>();\n}\n\nMinidumpWritable::Phase MinidumpWritable::WritePhase() {\n  return kPhaseEarly;\n}\n\nsize_t MinidumpWritable::WillWriteAtOffset(\n    Phase phase,\n    FileOffset* offset,\n    std::vector<MinidumpWritable*>* write_sequence) {\n  FileOffset local_offset = *offset;\n  CHECK_GE(local_offset, 0);\n\n  size_t leading_pad_bytes_this_phase;\n  size_t size;\n  if (phase == WritePhase()) {\n    DCHECK_EQ(state_, kStateFrozen);\n\n    // Add this object to the sequence of MinidumpWritable objects to be\n    // written.\n    write_sequence->push_back(this);\n\n    size = SizeOfObject();\n\n    if (size > 0) {\n      // Honor this object’s request to be aligned to a specific byte boundary.\n      // Once the alignment is corrected, this object knows exactly what file\n      // offset it will be written at.\n      size_t alignment = Alignment();\n      CHECK_LE(alignment, kMaximumAlignment);\n\n      leading_pad_bytes_this_phase =\n          (alignment - (local_offset % alignment)) % alignment;\n      local_offset += leading_pad_bytes_this_phase;\n      *offset = local_offset;\n    } else {\n      // If the object is size 0, alignment is of no concern.\n      leading_pad_bytes_this_phase = 0;\n    }\n    leading_pad_bytes_ = leading_pad_bytes_this_phase;\n\n    // Now that the file offset that this object will be written at is known,\n    // let the subclass implementation know in case it’s interested.\n    if (!WillWriteAtOffsetImpl(local_offset)) {\n      return kInvalidSize;\n    }\n\n    // Populate the 32-bit RVA fields in other objects that have registered to\n    // point to this one. Typically, a parent object will have registered to\n    // point to its children, but this can also occur where no parent-child\n    // relationship exists.\n    if (!registered_rvas_.empty() ||\n        !registered_location_descriptors_.empty()) {\n      RVA local_rva;\n      if (!AssignIfInRange(&local_rva, local_offset)) {\n        LOG(ERROR) << \"offset \" << local_offset << \" out of range\";\n        return kInvalidSize;\n      }\n\n      for (RVA* rva : registered_rvas_) {\n        *rva = local_rva;\n      }\n\n      if (!registered_location_descriptors_.empty()) {\n        decltype(registered_location_descriptors_[0]->DataSize) local_size;\n        if (!AssignIfInRange(&local_size, size)) {\n          LOG(ERROR) << \"size \" << size << \" out of range\";\n          return kInvalidSize;\n        }\n\n        for (MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor :\n                 registered_location_descriptors_) {\n          location_descriptor->DataSize = local_size;\n          location_descriptor->Rva = local_rva;\n        }\n      }\n    }\n\n    // Populate the 64-bit RVA fields in other objects that have registered to\n    // point to this one. Typically, a parent object will have registered to\n    // point to its children, but this can also occur where no parent-child\n    // relationship exists.\n    if (!registered_rva64s_.empty() ||\n        !registered_location_descriptor64s_.empty()) {\n      RVA64 local_rva64;\n      if (!AssignIfInRange(&local_rva64, local_offset)) {\n        LOG(ERROR) << \"offset \" << local_offset << \" out of range\";\n        return kInvalidSize;\n      }\n\n      for (RVA64* rva64 : registered_rva64s_) {\n        *rva64 = local_rva64;\n      }\n\n      if (!registered_location_descriptor64s_.empty()) {\n        decltype(registered_location_descriptor64s_[0]->DataSize) local_size;\n        if (!AssignIfInRange(&local_size, size)) {\n          LOG(ERROR) << \"size \" << size << \" out of range\";\n          return kInvalidSize;\n        }\n\n        for (MINIDUMP_LOCATION_DESCRIPTOR64* location_descriptor :\n             registered_location_descriptor64s_) {\n          location_descriptor->DataSize = local_size;\n          location_descriptor->Rva = local_rva64;\n        }\n      }\n    }\n\n    // This object is now considered writable. However, if it contains RVA/RVA64\n    // or MINIDUMP_LOCATION_DESCRIPTOR/MINIDUMP_LOCATION_DESCRIPTOR64 fields,\n    // they may not be fully updated yet, because it’s the repsonsibility of\n    // these fields’ pointees to update them. Once WillWriteAtOffset has\n    // completed running for both phases on an entire tree, and the entire tree\n    // has moved into kStateFrozen, all RVA/RVA64 and\n    // MINIDUMP_LOCATION_DESCRIPTOR/MINIDUMP_LOCATION_DESCRIPTOR64 fields within\n    // that tree will be populated.\n    state_ = kStateWritable;\n  } else {\n    if (phase == kPhaseEarly) {\n      DCHECK_EQ(state_, kStateFrozen);\n    } else {\n      DCHECK_EQ(state_, kStateWritable);\n    }\n\n    size = 0;\n    leading_pad_bytes_this_phase = 0;\n  }\n\n  // Loop over children regardless of whether this object itself will write\n  // during this phase. An object’s children are not required to be written\n  // during the same phase as their parent.\n  std::vector<MinidumpWritable*> children = Children();\n  for (MinidumpWritable* child : children) {\n    // Use “auto” here because it’s impossible to know whether size_t (size) or\n    // FileOffset (local_offset) is the wider type, and thus what type the\n    // result of adding these two variables will have.\n    auto unaligned_child_offset = local_offset + size;\n    FileOffset child_offset;\n    if (!AssignIfInRange(&child_offset, unaligned_child_offset)) {\n      LOG(ERROR) << \"offset \" << unaligned_child_offset << \" out of range\";\n      return kInvalidSize;\n    }\n\n    size_t child_size =\n        child->WillWriteAtOffset(phase, &child_offset, write_sequence);\n    if (child_size == kInvalidSize) {\n      return kInvalidSize;\n    }\n\n    size += child_size;\n  }\n\n  return leading_pad_bytes_this_phase + size;\n}\n\nbool MinidumpWritable::WillWriteAtOffsetImpl(FileOffset offset) {\n  return true;\n}\n\nbool MinidumpWritable::WritePaddingAndObject(FileWriterInterface* file_writer) {\n  DCHECK_EQ(state_, kStateWritable);\n\n  // The number of elements in kZeroes must be at least one less than the\n  // maximum Alignment() ever encountered.\n  static constexpr uint8_t kZeroes[kMaximumAlignment - 1] = {};\n  DCHECK_LE(leading_pad_bytes_, std::size(kZeroes));\n\n  if (leading_pad_bytes_) {\n    if (!file_writer->Write(&kZeroes, leading_pad_bytes_)) {\n      return false;\n    }\n  }\n\n  if (!WriteObject(file_writer)) {\n    return false;\n  }\n\n  state_ = kStateWritten;\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_writable.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <sys/types.h>\n\n#include <limits>\n#include <vector>\n\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\n\nclass FileWriterInterface;\n\nnamespace internal {\n\n//! \\brief The base class for all content that might be written to a minidump\n//!     file.\nclass MinidumpWritable {\n public:\n  MinidumpWritable(const MinidumpWritable&) = delete;\n  MinidumpWritable& operator=(const MinidumpWritable&) = delete;\n\n  virtual ~MinidumpWritable();\n\n  //! \\brief Writes an object and all of its children to a minidump file.\n  //!\n  //! Use this on the root object of a tree of MinidumpWritable objects,\n  //! typically on a MinidumpFileWriter object.\n  //!\n  //! \\param[in] file_writer The file writer to receive the minidump file’s\n  //!     content.\n  //!\n  //! \\return `true` on success. `false` on failure, with an appropriate message\n  //!     logged.\n  //!\n  //! \\note Valid in #kStateMutable, and transitions the object and the entire\n  //!     tree beneath it through all states to #kStateWritten.\n  //!\n  //! \\note This method should rarely be overridden.\n  virtual bool WriteEverything(FileWriterInterface* file_writer);\n\n  //! \\brief Registers a file offset pointer as one that should point to the\n  //!     object on which this method is called.\n  //!\n  //! Once the file offset at which an object will be written is known (when it\n  //! enters #kStateWritable), registered RVA pointers will be updated.\n  //!\n  //! \\param[in] rva A pointer to storage for the file offset that should\n  //!     contain this object’s writable file offset, once it is known.\n  //!\n  //! \\note Valid in #kStateFrozen or any preceding state.\n  //\n  // This is public instead of protected because objects of derived classes need\n  // to be able to register their own pointers with distinct objects.\n  void RegisterRVA(RVA* rva);\n  void RegisterRVA(RVA64* rva);\n\n  //! \\brief Registers a location descriptor as one that should point to the\n  //!     object on which this method is called.\n  //!\n  //! Once an object’s size and the file offset at it will be written is known\n  //! (when it enters #kStateFrozen), the relevant data in registered location\n  //! descriptors will be updated.\n  //!\n  //! \\param[in] location_descriptor A pointer to a location descriptor that\n  //!     should contain this object’s writable size and file offset, once they\n  //!     are known.\n  //!\n  //! \\note Valid in #kStateFrozen or any preceding state.\n  //\n  // This is public instead of protected because objects of derived classes need\n  // to be able to register their own pointers with distinct objects.\n  void RegisterLocationDescriptor(\n      MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor);\n  void RegisterLocationDescriptor(\n      MINIDUMP_LOCATION_DESCRIPTOR64* location_descriptor64);\n\n protected:\n  //! \\brief Identifies the state of an object.\n  //!\n  //! Objects will normally transition through each of these states as they are\n  //! created, populated with data, and then written to a minidump file.\n  enum State {\n    //! \\brief The object’s properties can be modified.\n    kStateMutable = 0,\n\n    //! \\brief The object is “frozen”.\n    //!\n    //! Its properties cannot be modified. Pointers to file offsets of other\n    //! structures may not yet be valid.\n    kStateFrozen,\n\n    //! \\brief The object is writable.\n    //!\n    //! The file offset at which it will be written is known. Pointers to file\n    //! offsets of other structures are valid when all objects in a tree are in\n    //! this state.\n    kStateWritable,\n\n    //! \\brief The object has been written to a minidump file.\n    kStateWritten,\n  };\n\n  //! \\brief Identifies the phase during which an object will be written to a\n  //!     minidump file.\n  enum Phase {\n    //! \\brief Objects that are written to a minidump file “early”.\n    //!\n    //! The normal sequence is for an object to write itself and then write all\n    //! of its children.\n    kPhaseEarly = 0,\n\n    //! \\brief Objects that are written to a minidump file “late”.\n    //!\n    //! Some objects, such as those capturing memory region snapshots, are\n    //! written to minidump files after all other objects. This “late” phase\n    //! identifies such objects. This is useful to improve spatial locality in\n    //! minidump files in accordance with expected access patterns: unlike most\n    //! other data, memory snapshots are large and do not usually need to be\n    //! consulted in their entirety in order to process a minidump file.\n    kPhaseLate,\n  };\n\n  //! \\brief A size value used to signal failure by methods that return\n  //!     `size_t`.\n  static constexpr size_t kInvalidSize = std::numeric_limits<size_t>::max();\n\n  MinidumpWritable();\n\n  //! \\brief The state of the object.\n  State state() const { return state_; }\n\n  //! \\brief Transitions the object from #kStateMutable to #kStateFrozen.\n  //!\n  //! The default implementation marks the object as frozen and recursively\n  //! calls Freeze() on all of its children. Subclasses may override this method\n  //! to perform processing that should only be done once callers have finished\n  //! populating an object with data. Typically, a subclass implementation would\n  //! call RegisterRVA() or RegisterLocationDescriptor() on other objects as\n  //! appropriate, because at the time Freeze() runs, the in-memory locations of\n  //! RVAs and location descriptors are known and will not change for the\n  //! remaining duration of an object’s lifetime.\n  //!\n  //! \\return `true` on success. `false` on failure, with an appropriate message\n  //!     logged.\n  virtual bool Freeze();\n\n  //! \\brief Returns the amount of space that this object will consume when\n  //!     written to a minidump file, in bytes, not including any leading or\n  //!     trailing padding necessary to maintain proper alignment.\n  //!\n  //! \\note Valid in #kStateFrozen or any subsequent state.\n  virtual size_t SizeOfObject() = 0;\n\n  //! \\brief Returns the object’s desired byte-boundary alignment.\n  //!\n  //! The default implementation returns `4`. Subclasses may override this as\n  //! needed.\n  //!\n  //! \\note Valid in #kStateFrozen or any subsequent state.\n  virtual size_t Alignment();\n\n  //! \\brief Returns the object’s children.\n  //!\n  //! \\note Valid in #kStateFrozen or any subsequent state.\n  virtual std::vector<MinidumpWritable*> Children();\n\n  //! \\brief Returns the object’s desired write phase.\n  //!\n  //! The default implementation returns #kPhaseEarly. Subclasses may override\n  //! this method to alter their write phase.\n  //!\n  //! \\note Valid in any state.\n  virtual Phase WritePhase();\n\n  //! \\brief Prepares the object to be written at a known file offset,\n  //!     transitioning it from #kStateFrozen to #kStateWritable.\n  //!\n  //! This method is responsible for determining the final file offset of the\n  //! object, which may be increased from \\a offset to meet alignment\n  //! requirements. It calls WillWriteAtOffsetImpl() for the benefit of\n  //! subclasses. It populates all RVAs and location descriptors registered with\n  //! it via RegisterRVA() and RegisterLocationDescriptor(). It also recurses\n  //! into all known children.\n  //!\n  //! \\param[in] phase The phase during which the object will be written. If\n  //!     this does not match Phase(), processing is suppressed, although\n  //!     recursive processing will still occur on all children. This addresses\n  //!     the case where parents and children do not write in the same phase.\n  //! \\param[in] offset The file offset at which the object will be written. The\n  //!     offset may need to be adjusted for alignment.\n  //! \\param[out] write_sequence This object will append itself to this list,\n  //!     such that on return from a recursive tree of WillWriteAtOffset()\n  //!     calls, elements of the vector will be organized in the sequence that\n  //!     the objects will be written to the minidump file.\n  //!\n  //! \\return The file size consumed by this object and all children, including\n  //!     any padding inserted to meet alignment requirements. On failure,\n  //!     #kInvalidSize, with an appropriate message logged.\n  //!\n  //! \\note This method cannot be overridden. Subclasses that need to perform\n  //!     processing when an object transitions to #kStateWritable should\n  //!     implement WillWriteAtOffsetImpl(), which is called by this method.\n  size_t WillWriteAtOffset(Phase phase,\n                           FileOffset* offset,\n                           std::vector<MinidumpWritable*>* write_sequence);\n\n  //! \\brief Called once an object’s writable file offset is determined, as it\n  //!     transitions into #kStateWritable.\n  //!\n  //! Subclasses can override this method if they need to provide additional\n  //! processing once their writable file offset is known. Typically, this will\n  //! be done by subclasses that handle certain RVAs themselves instead of using\n  //! the RegisterRVA() interface.\n  //!\n  //! \\param[in] offset The file offset at which the object will be written. The\n  //!     value passed to this method will already have been adjusted to meet\n  //!     alignment requirements.\n  //!\n  //! \\return `true` on success. `false` on error, indicating that the minidump\n  //!     file should not be written.\n  //!\n  //! \\note Valid in #kStateFrozen. The object will transition to\n  //!     #kStateWritable after this method returns.\n  virtual bool WillWriteAtOffsetImpl(FileOffset offset);\n\n  //! \\brief Writes the object, transitioning it from #kStateWritable to\n  //!     #kStateWritten.\n  //!\n  //! Writes any padding necessary to meet alignment requirements, and then\n  //! calls WriteObject() to write the object’s content.\n  //!\n  //! \\param[in] file_writer The file writer to receive the object’s content.\n  //!\n  //! \\return `true` on success. `false` on error with an appropriate message\n  //!     logged.\n  //!\n  //! \\note This method cannot be overridden. Subclasses must override\n  //!     WriteObject().\n  bool WritePaddingAndObject(FileWriterInterface* file_writer);\n\n  //! \\brief Writes the object’s content.\n  //!\n  //! \\param[in] file_writer The file writer to receive the object’s content.\n  //!\n  //! \\return `true` on success. `false` on error, indicating that the content\n  //!     could not be written to the minidump file.\n  //!\n  //! \\note Valid in #kStateWritable. The object will transition to\n  //!     #kStateWritten after this method returns.\n  virtual bool WriteObject(FileWriterInterface* file_writer) = 0;\n\n private:\n  std::vector<RVA*> registered_rvas_;  // weak\n\n  std::vector<RVA64*> registered_rva64s_;  // weak\n\n  // weak\n  std::vector<MINIDUMP_LOCATION_DESCRIPTOR*> registered_location_descriptors_;\n\n  // weak\n  std::vector<MINIDUMP_LOCATION_DESCRIPTOR64*>\n      registered_location_descriptor64s_;\n\n  size_t leading_pad_bytes_;\n  State state_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_\n"
  },
  {
    "path": "minidump/minidump_writable_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_writable.h\"\n\n#include <string>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass BaseTestMinidumpWritable : public crashpad::internal::MinidumpWritable {\n public:\n  BaseTestMinidumpWritable()\n      : MinidumpWritable(),\n        children_(),\n        expected_offset_(-1),\n        alignment_(0),\n        phase_(kPhaseEarly),\n        has_alignment_(false),\n        has_phase_(false),\n        verified_(false) {}\n\n  BaseTestMinidumpWritable(const BaseTestMinidumpWritable&) = delete;\n  BaseTestMinidumpWritable& operator=(const BaseTestMinidumpWritable&) = delete;\n\n  ~BaseTestMinidumpWritable() { EXPECT_TRUE(verified_); }\n\n  void SetAlignment(size_t alignment) {\n    alignment_ = alignment;\n    has_alignment_ = true;\n  }\n\n  void AddChild(BaseTestMinidumpWritable* child) { children_.push_back(child); }\n\n  void SetPhaseLate() {\n    phase_ = kPhaseLate;\n    has_phase_ = true;\n  }\n\n  void Verify() {\n    verified_ = true;\n    EXPECT_EQ(state(), kStateWritten);\n    for (BaseTestMinidumpWritable* child : children_) {\n      child->Verify();\n    }\n  }\n\n protected:\n  bool Freeze() override {\n    EXPECT_EQ(state(), kStateMutable);\n    bool rv = MinidumpWritable::Freeze();\n    EXPECT_TRUE(rv);\n    EXPECT_EQ(state(), kStateFrozen);\n    return rv;\n  }\n\n  size_t Alignment() override {\n    EXPECT_GE(state(), kStateFrozen);\n    return has_alignment_ ? alignment_ : MinidumpWritable::Alignment();\n  }\n\n  std::vector<MinidumpWritable*> Children() override {\n    EXPECT_GE(state(), kStateFrozen);\n    if (!children_.empty()) {\n      std::vector<MinidumpWritable*> children;\n      for (BaseTestMinidumpWritable* child : children_) {\n        children.push_back(child);\n      }\n      return children;\n    }\n    return MinidumpWritable::Children();\n  }\n\n  Phase WritePhase() override {\n    return has_phase_ ? phase_ : MinidumpWritable::Phase();\n  }\n\n  bool WillWriteAtOffsetImpl(FileOffset offset) override {\n    EXPECT_EQ(kStateFrozen, state());\n    expected_offset_ = offset;\n    bool rv = MinidumpWritable::WillWriteAtOffsetImpl(offset);\n    EXPECT_TRUE(rv);\n    return rv;\n  }\n\n  bool WriteObject(FileWriterInterface* file_writer) override {\n    EXPECT_EQ(kStateWritable, state());\n    EXPECT_EQ(file_writer->Seek(0, SEEK_CUR), expected_offset_);\n\n    // Subclasses must override this.\n    return false;\n  }\n\n private:\n  std::vector<BaseTestMinidumpWritable*> children_;\n  FileOffset expected_offset_;\n  size_t alignment_;\n  Phase phase_;\n  bool has_alignment_;\n  bool has_phase_;\n  bool verified_;\n};\n\nclass TestStringMinidumpWritable final : public BaseTestMinidumpWritable {\n public:\n  TestStringMinidumpWritable() : BaseTestMinidumpWritable(), data_() {}\n\n  TestStringMinidumpWritable(const TestStringMinidumpWritable&) = delete;\n  TestStringMinidumpWritable& operator=(const TestStringMinidumpWritable&) =\n      delete;\n\n  ~TestStringMinidumpWritable() {}\n\n  void SetData(const std::string& string) { data_ = string; }\n\n protected:\n  size_t SizeOfObject() override {\n    EXPECT_GE(state(), kStateFrozen);\n    return data_.size();\n  }\n\n  bool WriteObject(FileWriterInterface* file_writer) override {\n    BaseTestMinidumpWritable::WriteObject(file_writer);\n    bool rv = file_writer->Write(&data_[0], data_.size());\n    EXPECT_TRUE(rv);\n    return rv;\n  }\n\n private:\n  std::string data_;\n};\n\nTEST(MinidumpWritable, MinidumpWritable) {\n  StringFile string_file;\n\n  {\n    SCOPED_TRACE(\"empty\");\n    string_file.Reset();\n    TestStringMinidumpWritable string_writable;\n    EXPECT_TRUE(string_writable.WriteEverything(&string_file));\n    EXPECT_TRUE(string_file.string().empty());\n    string_writable.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"childless\");\n    string_file.Reset();\n    TestStringMinidumpWritable string_writable;\n    string_writable.SetData(\"a\");\n    EXPECT_TRUE(string_writable.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 1u);\n    EXPECT_EQ(string_file.string(), \"a\");\n    string_writable.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"parent-child\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"b\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"c\");\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 5u);\n    EXPECT_EQ(string_file.string(), std::string(\"b\\0\\0\\0c\", 5));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"base alignment 2\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"de\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"f\");\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 5u);\n    EXPECT_EQ(string_file.string(), std::string(\"de\\0\\0f\", 5));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"base alignment 3\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"ghi\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"j\");\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 5u);\n    EXPECT_EQ(string_file.string(), std::string(\"ghi\\0j\", 5));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"base alignment 4\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"klmn\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"o\");\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 5u);\n    EXPECT_EQ(string_file.string(), \"klmno\");\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"base alignment 5\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"pqrst\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"u\");\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 9u);\n    EXPECT_EQ(string_file.string(), std::string(\"pqrst\\0\\0\\0u\", 9));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"two children\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"parent\");\n    TestStringMinidumpWritable child_0;\n    child_0.SetData(\"child_0\");\n    parent.AddChild(&child_0);\n    TestStringMinidumpWritable child_1;\n    child_1.SetData(\"child_1\");\n    parent.AddChild(&child_1);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 23u);\n    EXPECT_EQ(string_file.string(),\n              std::string(\"parent\\0\\0child_0\\0child_1\", 23));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"grandchild\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"parent\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"child\");\n    parent.AddChild(&child);\n    TestStringMinidumpWritable grandchild;\n    grandchild.SetData(\"grandchild\");\n    child.AddChild(&grandchild);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 26u);\n    EXPECT_EQ(string_file.string(),\n              std::string(\"parent\\0\\0child\\0\\0\\0grandchild\", 26));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"grandchild with empty parent\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    TestStringMinidumpWritable child;\n    child.SetData(\"child\");\n    parent.AddChild(&child);\n    TestStringMinidumpWritable grandchild;\n    grandchild.SetData(\"grandchild\");\n    child.AddChild(&grandchild);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 18u);\n    EXPECT_EQ(string_file.string(), std::string(\"child\\0\\0\\0grandchild\", 18));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"grandchild with empty child\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"parent\");\n    TestStringMinidumpWritable child;\n    parent.AddChild(&child);\n    TestStringMinidumpWritable grandchild;\n    grandchild.SetData(\"grandchild\");\n    child.AddChild(&grandchild);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 18u);\n    EXPECT_EQ(string_file.string(), std::string(\"parent\\0\\0grandchild\", 18));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"grandchild with empty grandchild\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"parent\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"child\");\n    parent.AddChild(&child);\n    TestStringMinidumpWritable grandchild;\n    child.AddChild(&grandchild);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 13u);\n    EXPECT_EQ(string_file.string(), std::string(\"parent\\0\\0child\", 13));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"grandchild with late-phase grandchild\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"parent\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"child\");\n    parent.AddChild(&child);\n    TestStringMinidumpWritable grandchild;\n    grandchild.SetData(\"grandchild\");\n    grandchild.SetPhaseLate();\n    child.AddChild(&grandchild);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 26u);\n    EXPECT_EQ(string_file.string(),\n              std::string(\"parent\\0\\0child\\0\\0\\0grandchild\", 26));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"grandchild with late-phase child\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"parent\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"child\");\n    child.SetPhaseLate();\n    parent.AddChild(&child);\n    TestStringMinidumpWritable grandchild;\n    grandchild.SetData(\"grandchild\");\n    child.AddChild(&grandchild);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 25u);\n    EXPECT_EQ(string_file.string(),\n              std::string(\"parent\\0\\0grandchild\\0\\0child\", 25));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"family tree\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"P..\");\n    TestStringMinidumpWritable child_0;\n    child_0.SetData(\"C0.\");\n    parent.AddChild(&child_0);\n    TestStringMinidumpWritable child_1;\n    child_1.SetData(\"C1.\");\n    parent.AddChild(&child_1);\n    TestStringMinidumpWritable grandchild_00;\n    grandchild_00.SetData(\"G00\");\n    child_0.AddChild(&grandchild_00);\n    TestStringMinidumpWritable grandchild_01;\n    grandchild_01.SetData(\"G01\");\n    child_0.AddChild(&grandchild_01);\n    TestStringMinidumpWritable grandchild_10;\n    grandchild_10.SetData(\"G10\");\n    child_1.AddChild(&grandchild_10);\n    TestStringMinidumpWritable grandchild_11;\n    grandchild_11.SetData(\"G11\");\n    child_1.AddChild(&grandchild_11);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 27u);\n    EXPECT_EQ(string_file.string(),\n              std::string(\"P..\\0C0.\\0G00\\0G01\\0C1.\\0G10\\0G11\", 27));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"family tree with C0 late\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"P..\");\n    TestStringMinidumpWritable child_0;\n    child_0.SetData(\"C0.\");\n    child_0.SetPhaseLate();\n    parent.AddChild(&child_0);\n    TestStringMinidumpWritable child_1;\n    child_1.SetData(\"C1.\");\n    parent.AddChild(&child_1);\n    TestStringMinidumpWritable grandchild_00;\n    grandchild_00.SetData(\"G00\");\n    child_0.AddChild(&grandchild_00);\n    TestStringMinidumpWritable grandchild_01;\n    grandchild_01.SetData(\"G01\");\n    child_0.AddChild(&grandchild_01);\n    TestStringMinidumpWritable grandchild_10;\n    grandchild_10.SetData(\"G10\");\n    child_1.AddChild(&grandchild_10);\n    TestStringMinidumpWritable grandchild_11;\n    grandchild_11.SetData(\"G11\");\n    child_1.AddChild(&grandchild_11);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 27u);\n    EXPECT_EQ(string_file.string(),\n              std::string(\"P..\\0G00\\0G01\\0C1.\\0G10\\0G11\\0C0.\", 27));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"family tree with G0 late\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"P..\");\n    TestStringMinidumpWritable child_0;\n    child_0.SetData(\"C0.\");\n    parent.AddChild(&child_0);\n    TestStringMinidumpWritable child_1;\n    child_1.SetData(\"C1.\");\n    parent.AddChild(&child_1);\n    TestStringMinidumpWritable grandchild_00;\n    grandchild_00.SetData(\"G00\");\n    grandchild_00.SetPhaseLate();\n    child_0.AddChild(&grandchild_00);\n    TestStringMinidumpWritable grandchild_01;\n    grandchild_01.SetData(\"G01\");\n    grandchild_01.SetPhaseLate();\n    child_0.AddChild(&grandchild_01);\n    TestStringMinidumpWritable grandchild_10;\n    grandchild_10.SetData(\"G10\");\n    child_1.AddChild(&grandchild_10);\n    TestStringMinidumpWritable grandchild_11;\n    grandchild_11.SetData(\"G11\");\n    child_1.AddChild(&grandchild_11);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 27u);\n    EXPECT_EQ(string_file.string(),\n              std::string(\"P..\\0C0.\\0C1.\\0G10\\0G11\\0G00\\0G01\", 27));\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"align 1\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"p\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"c\");\n    child.SetAlignment(1);\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 2u);\n    EXPECT_EQ(string_file.string(), \"pc\");\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"align 2\");\n    string_file.Reset();\n    TestStringMinidumpWritable parent;\n    parent.SetData(\"p\");\n    TestStringMinidumpWritable child;\n    child.SetData(\"c\");\n    child.SetAlignment(2);\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n    EXPECT_EQ(string_file.string().size(), 3u);\n    EXPECT_EQ(string_file.string(), std::string(\"p\\0c\", 3));\n    parent.Verify();\n  }\n}\n\ntemplate <typename RVAType>\nclass TTestRVAMinidumpWritable final : public BaseTestMinidumpWritable {\n public:\n  TTestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {}\n\n  TTestRVAMinidumpWritable(const TTestRVAMinidumpWritable&) = delete;\n  TTestRVAMinidumpWritable& operator=(const TTestRVAMinidumpWritable&) = delete;\n\n  ~TTestRVAMinidumpWritable() {}\n\n  void SetRVA(MinidumpWritable* other) { other->RegisterRVA(&rva_); }\n\n protected:\n  size_t SizeOfObject() override {\n    EXPECT_GE(state(), kStateFrozen);\n    return sizeof(rva_);\n  }\n\n  bool WriteObject(FileWriterInterface* file_writer) override {\n    BaseTestMinidumpWritable::WriteObject(file_writer);\n    EXPECT_TRUE(file_writer->Write(&rva_, sizeof(rva_)));\n    return true;\n  }\n\n private:\n  RVAType rva_;\n};\n\ntemplate <typename RVAType>\nRVAType TRVAAtIndex(const std::string& string, size_t index) {\n  return *reinterpret_cast<const RVAType*>(&string[index * sizeof(RVAType)]);\n}\n\ntemplate <typename T>\nstruct TestNames {};\n\ntemplate <>\nstruct TestNames<RVA> {\n  static constexpr char kName[] = \"RVA\";\n};\n\ntemplate <>\nstruct TestNames<RVA64> {\n  static constexpr char kName[] = \"RVA64\";\n};\n\nclass TestTypeNames {\n public:\n  template <typename T>\n  static std::string GetName(int) {\n    return std::string(TestNames<T>::kName);\n  }\n};\n\ntemplate <typename RVAType>\nclass MinidumpWritable : public ::testing::Test {};\n\nusing RVATypes = ::testing::Types<RVA, RVA64>;\nTYPED_TEST_SUITE(MinidumpWritable, RVATypes, TestTypeNames);\n\nTYPED_TEST(MinidumpWritable, RVA) {\n  StringFile string_file;\n  using TestRVAMinidumpWritable = TTestRVAMinidumpWritable<TypeParam>;\n  const auto RVAAtIndex = TRVAAtIndex<TypeParam>;\n  constexpr size_t kRVASize = sizeof(TypeParam);\n\n  {\n    SCOPED_TRACE(\"unset\");\n    string_file.Reset();\n    TestRVAMinidumpWritable rva_writable;\n    EXPECT_TRUE(rva_writable.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize);\n    rva_writable.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"self\");\n    string_file.Reset();\n    TestRVAMinidumpWritable rva_writable;\n    rva_writable.SetRVA(&rva_writable);\n    EXPECT_TRUE(rva_writable.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize);\n    rva_writable.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"parent-child self\");\n    string_file.Reset();\n    TestRVAMinidumpWritable parent;\n    parent.SetRVA(&parent);\n    TestRVAMinidumpWritable child;\n    child.SetRVA(&child);\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), 2 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 1 * kRVASize);\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"parent-child only\");\n    string_file.Reset();\n    TestRVAMinidumpWritable parent;\n    TestRVAMinidumpWritable child;\n    parent.SetRVA(&child);\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), 2 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize);\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"parent-child circular\");\n    string_file.Reset();\n    TestRVAMinidumpWritable parent;\n    TestRVAMinidumpWritable child;\n    parent.SetRVA(&child);\n    child.SetRVA(&parent);\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), 2 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize);\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"grandchildren\");\n    string_file.Reset();\n    TestRVAMinidumpWritable parent;\n    TestRVAMinidumpWritable child;\n    parent.SetRVA(&child);\n    parent.AddChild(&child);\n    TestRVAMinidumpWritable grandchild_0;\n    grandchild_0.SetRVA(&child);\n    child.AddChild(&grandchild_0);\n    TestRVAMinidumpWritable grandchild_1;\n    grandchild_1.SetRVA(&child);\n    child.AddChild(&grandchild_1);\n    TestRVAMinidumpWritable grandchild_2;\n    grandchild_2.SetRVA(&child);\n    child.AddChild(&grandchild_2);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), 5 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 2), 1 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 3), 1 * kRVASize);\n    EXPECT_EQ(RVAAtIndex(string_file.string(), 4), 1 * kRVASize);\n    parent.Verify();\n  }\n}\n\ntemplate <typename MinidumpLocationDescriptorType>\nclass TTestLocationDescriptorMinidumpWritable final\n    : public BaseTestMinidumpWritable {\n public:\n  TTestLocationDescriptorMinidumpWritable()\n      : BaseTestMinidumpWritable(), location_descriptor_(), string_() {}\n\n  TTestLocationDescriptorMinidumpWritable(\n      const TTestLocationDescriptorMinidumpWritable&) = delete;\n  TTestLocationDescriptorMinidumpWritable& operator=(\n      const TTestLocationDescriptorMinidumpWritable&) = delete;\n\n  ~TTestLocationDescriptorMinidumpWritable() {}\n\n  void SetLocationDescriptor(MinidumpWritable* other) {\n    other->RegisterLocationDescriptor(&location_descriptor_);\n  }\n\n  void SetString(const std::string& string) { string_ = string; }\n\n protected:\n  size_t SizeOfObject() override {\n    EXPECT_GE(state(), kStateFrozen);\n    // NUL-terminate.\n    return sizeof(location_descriptor_) + string_.size() + 1;\n  }\n\n  bool WriteObject(FileWriterInterface* file_writer) override {\n    BaseTestMinidumpWritable::WriteObject(file_writer);\n    WritableIoVec iov;\n    iov.iov_base = &location_descriptor_;\n    iov.iov_len = sizeof(location_descriptor_);\n    std::vector<WritableIoVec> iovecs(1, iov);\n    // NUL-terminate.\n    iov.iov_base = &string_[0];\n    iov.iov_len = string_.size() + 1;\n    iovecs.push_back(iov);\n    EXPECT_TRUE(file_writer->WriteIoVec(&iovecs));\n    return true;\n  }\n\n private:\n  MinidumpLocationDescriptorType location_descriptor_;\n  std::string string_;\n};\n\ntemplate <typename MinidumpLocationDescriptorType>\nstruct TLocationDescriptorAndData {\n  MinidumpLocationDescriptorType location_descriptor;\n  const char* string;\n};\n\ntemplate <typename MinidumpLocationDescriptorType>\nTLocationDescriptorAndData<MinidumpLocationDescriptorType> TLDDAtIndex(\n    const std::string& str,\n    size_t index) {\n  const MinidumpLocationDescriptorType* location_descriptor =\n      reinterpret_cast<const MinidumpLocationDescriptorType*>(&str[index]);\n\n  const char* string = reinterpret_cast<const char*>(\n      &str[index] +\n      offsetof(TLocationDescriptorAndData<MinidumpLocationDescriptorType>,\n               string));\n\n  return TLocationDescriptorAndData<MinidumpLocationDescriptorType>{\n      *location_descriptor,\n      string,\n  };\n}\n\ntemplate <typename MinidumpLocationDescriptorType>\nclass MinidumpWritableLocationDescriptor : public ::testing::Test {};\n\ntemplate <>\nstruct TestNames<MINIDUMP_LOCATION_DESCRIPTOR> {\n  static constexpr char kName[] = \"MINIDUMP_LOCATION_DESCRIPTOR\";\n};\n\ntemplate <>\nstruct TestNames<MINIDUMP_LOCATION_DESCRIPTOR64> {\n  static constexpr char kName[] = \"MINIDUMP_LOCATION_DESCRIPTOR64\";\n};\n\nusing MinidumpLocationDescriptorTypes =\n    ::testing::Types<MINIDUMP_LOCATION_DESCRIPTOR,\n                     MINIDUMP_LOCATION_DESCRIPTOR64>;\nTYPED_TEST_SUITE(MinidumpWritableLocationDescriptor,\n                 MinidumpLocationDescriptorTypes,\n                 TestTypeNames);\n\nTYPED_TEST(MinidumpWritableLocationDescriptor, LocationDescriptor) {\n  StringFile string_file;\n\n  using LocationDescriptorAndData = TLocationDescriptorAndData<TypeParam>;\n  const auto LDDAtIndex = TLDDAtIndex<TypeParam>;\n  using TestLocationDescriptorMinidumpWritable =\n      TTestLocationDescriptorMinidumpWritable<TypeParam>;\n  constexpr size_t kMinidumpLocationDescriptorSize = sizeof(TypeParam);\n\n  {\n    SCOPED_TRACE(\"unset\");\n    string_file.Reset();\n    TestLocationDescriptorMinidumpWritable location_descriptor_writable;\n    EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 1);\n    LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0);\n    EXPECT_EQ(ldd.location_descriptor.DataSize, 0u);\n    EXPECT_EQ(ldd.location_descriptor.Rva, 0u);\n    location_descriptor_writable.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"self\");\n    string_file.Reset();\n    TestLocationDescriptorMinidumpWritable location_descriptor_writable;\n    location_descriptor_writable.SetLocationDescriptor(\n        &location_descriptor_writable);\n    EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 1);\n    LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0);\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 1);\n    EXPECT_EQ(ldd.location_descriptor.Rva, 0u);\n    location_descriptor_writable.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"self with data\");\n    string_file.Reset();\n    TestLocationDescriptorMinidumpWritable location_descriptor_writable;\n    location_descriptor_writable.SetLocationDescriptor(\n        &location_descriptor_writable);\n    location_descriptor_writable.SetString(\"zz\");\n    EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 3);\n    LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0);\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 3);\n    EXPECT_EQ(ldd.location_descriptor.Rva, 0u);\n    EXPECT_STREQ(\"zz\", ldd.string);\n    location_descriptor_writable.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"parent-child self\");\n    string_file.Reset();\n    TestLocationDescriptorMinidumpWritable parent;\n    parent.SetLocationDescriptor(&parent);\n    parent.SetString(\"yy\");\n    TestLocationDescriptorMinidumpWritable child;\n    child.SetLocationDescriptor(&child);\n    child.SetString(\"x\");\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(),\n              kMinidumpLocationDescriptorSize * 2 + 6);\n    LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0);\n\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 3);\n    EXPECT_EQ(ldd.location_descriptor.Rva, 0u);\n    EXPECT_STREQ(\"yy\", ldd.string);\n    ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4);\n\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 2);\n\n    EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4);\n    EXPECT_STREQ(\"x\", ldd.string);\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"parent-child only\");\n    string_file.Reset();\n    TestLocationDescriptorMinidumpWritable parent;\n    TestLocationDescriptorMinidumpWritable child;\n    parent.SetLocationDescriptor(&child);\n    parent.SetString(\"www\");\n    child.SetString(\"vv\");\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(),\n              kMinidumpLocationDescriptorSize * 2 + 7);\n    LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0);\n\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 3);\n\n    EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4);\n    EXPECT_STREQ(\"www\", ldd.string);\n    ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4);\n\n    EXPECT_EQ(ldd.location_descriptor.DataSize, 0u);\n    EXPECT_EQ(ldd.location_descriptor.Rva, 0u);\n    EXPECT_STREQ(\"vv\", ldd.string);\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"parent-child circular\");\n    string_file.Reset();\n    TestLocationDescriptorMinidumpWritable parent;\n    TestLocationDescriptorMinidumpWritable child;\n    parent.SetLocationDescriptor(&child);\n    parent.SetString(\"uuuu\");\n    child.SetLocationDescriptor(&parent);\n    child.SetString(\"tttt\");\n    parent.AddChild(&child);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(),\n              kMinidumpLocationDescriptorSize * 2 + 13);\n    LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0);\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 5);\n    EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 8);\n    EXPECT_STREQ(\"uuuu\", ldd.string);\n    ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 8);\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 5);\n    EXPECT_EQ(ldd.location_descriptor.Rva, 0u);\n    EXPECT_STREQ(\"tttt\", ldd.string);\n    parent.Verify();\n  }\n\n  {\n    SCOPED_TRACE(\"grandchildren\");\n    string_file.Reset();\n    TestLocationDescriptorMinidumpWritable parent;\n    TestLocationDescriptorMinidumpWritable child;\n    parent.SetLocationDescriptor(&child);\n    parent.SetString(\"s\");\n    parent.AddChild(&child);\n    child.SetString(\"r\");\n    TestLocationDescriptorMinidumpWritable grandchild_0;\n    grandchild_0.SetLocationDescriptor(&child);\n    grandchild_0.SetString(\"q\");\n    child.AddChild(&grandchild_0);\n    TestLocationDescriptorMinidumpWritable grandchild_1;\n    grandchild_1.SetLocationDescriptor(&child);\n    grandchild_1.SetString(\"p\");\n    child.AddChild(&grandchild_1);\n    TestLocationDescriptorMinidumpWritable grandchild_2;\n    grandchild_2.SetLocationDescriptor(&child);\n    grandchild_2.SetString(\"o\");\n    child.AddChild(&grandchild_2);\n    EXPECT_TRUE(parent.WriteEverything(&string_file));\n\n    ASSERT_EQ(string_file.string().size(),\n              kMinidumpLocationDescriptorSize * 5 + 18);\n    LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0);\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 2);\n    EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4);\n    EXPECT_STREQ(\"s\", ldd.string);\n    ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4);\n    EXPECT_EQ(ldd.location_descriptor.DataSize, 0u);\n    EXPECT_EQ(ldd.location_descriptor.Rva, 0u);\n    EXPECT_STREQ(\"r\", ldd.string);\n    ldd = LDDAtIndex(string_file.string(),\n                     kMinidumpLocationDescriptorSize * 2 + 8);\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 2);\n    EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4);\n    EXPECT_STREQ(\"q\", ldd.string);\n    ldd = LDDAtIndex(string_file.string(),\n                     kMinidumpLocationDescriptorSize * 3 + 12);\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 2);\n    EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4);\n    EXPECT_STREQ(\"p\", ldd.string);\n    ldd = LDDAtIndex(string_file.string(),\n                     kMinidumpLocationDescriptorSize * 4 + 16);\n    EXPECT_EQ(ldd.location_descriptor.DataSize,\n              kMinidumpLocationDescriptorSize + 2);\n    EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4);\n    EXPECT_STREQ(\"o\", ldd.string);\n    parent.Verify();\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_writer_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/minidump_writer_util.h\"\n\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"util/stdlib/strlcpy.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n// static\nvoid MinidumpWriterUtil::AssignTimeT(uint32_t* destination, time_t source) {\n  if (!base::IsValueInRangeForNumericType<uint32_t>(source)) {\n    LOG(WARNING) << \"timestamp \" << source << \" out of range\";\n  }\n\n  *destination = static_cast<uint32_t>(source);\n}\n\n// static\nstd::u16string MinidumpWriterUtil::ConvertUTF8ToUTF16(const std::string& utf8) {\n  std::u16string utf16;\n  if (!base::UTF8ToUTF16(utf8.data(), utf8.length(), &utf16)) {\n    LOG(WARNING) << \"string \" << utf8\n                 << \" cannot be converted to UTF-16 losslessly\";\n  }\n  return utf16;\n}\n\n// static\nvoid MinidumpWriterUtil::AssignUTF8ToUTF16(char16_t* destination,\n                                           size_t destination_size,\n                                           const std::string& source) {\n  std::u16string source_utf16 = ConvertUTF8ToUTF16(source);\n  if (source_utf16.size() > destination_size - 1) {\n    LOG(WARNING) << \"string \" << source << \" UTF-16 length \"\n                 << source_utf16.size()\n                 << \" will be truncated to UTF-16 length \"\n                 << destination_size - 1;\n  }\n\n  source_utf16.resize(destination_size - 1);\n  c16lcpy(destination, source_utf16.c_str(), destination_size);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/minidump_writer_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_\n#define CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <time.h>\n\n#include <string>\n\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A collection of utility functions used by the MinidumpWritable family\n//!     of classes.\nclass MinidumpWriterUtil final {\n public:\n  MinidumpWriterUtil() = delete;\n  MinidumpWriterUtil(const MinidumpWriterUtil&) = delete;\n  MinidumpWriterUtil& operator=(const MinidumpWriterUtil&) = delete;\n\n  //! \\brief Assigns a `time_t` value, logging a warning if the result overflows\n  //!     the destination buffer and will be truncated.\n  //!\n  //! \\param[out] destination A pointer to the variable to be assigned to.\n  //! \\param[in] source The value to assign.\n  //!\n  //! The minidump format uses `uint32_t` for many timestamp values, but\n  //! `time_t` may be wider than this. These year 2038 bugs are a limitation of\n  //! the minidump format. An out-of-range error will be noted with a warning,\n  //! but is not considered fatal. \\a source will be truncated and assigned to\n  //! \\a destination in this case.\n  //!\n  //! For `time_t` values with nonfatal overflow semantics, this function is\n  //! used in preference to AssignIfInRange(), which fails without performing an\n  //! assignment when an out-of-range condition is detected.\n  static void AssignTimeT(uint32_t* destination, time_t source);\n\n  //! \\brief Converts a UTF-8 string to UTF-16 and returns it. If the string\n  //!     cannot be converted losslessly, indicating that the input is not\n  //!     well-formed UTF-8, a warning is logged.\n  //!\n  //! \\param[in] utf8 The UTF-8-encoded string to convert.\n  //!\n  //! \\return The \\a utf8 string, converted to UTF-16 encoding. If the\n  //!     conversion is lossy, U+FFFD “replacement characters” will be\n  //!     introduced.\n  static std::u16string ConvertUTF8ToUTF16(const std::string& utf8);\n\n  //! \\brief Converts a UTF-8 string to UTF-16 and places it into a buffer of\n  //!     fixed size, taking care to `NUL`-terminate the buffer and not to\n  //!     overflow it. If the string will be truncated or if it cannot be\n  //!     converted losslessly, a warning is logged.\n  //!\n  //! Any unused portion of the \\a destination buffer that is not written to by\n  //! the converted string will be overwritten with `NUL` UTF-16 code units,\n  //! thus, this function always writes \\a destination_size `char16_t` units.\n  //!\n  //! If the conversion is lossy, U+FFFD “replacement characters” will be\n  //! introduced.\n  //!\n  //! \\param[out] destination A pointer to the destination buffer, where the\n  //!     UTF-16-encoded string will be written.\n  //! \\param[in] destination_size The size of \\a destination in `char16_t`\n  //!     units, including space used by a `NUL` terminator.\n  //! \\param[in] source The UTF-8-encoded input string.\n  static void AssignUTF8ToUTF16(char16_t* destination,\n                                size_t destination_size,\n                                const std::string& source);\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_\n"
  },
  {
    "path": "minidump/test/minidump_byte_array_writer_test_util.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/test/minidump_byte_array_writer_test_util.h\"\n\n#include \"minidump/test/minidump_writable_test_util.h\"\n\nnamespace crashpad {\nnamespace test {\n\nstd::vector<uint8_t> MinidumpByteArrayAtRVA(const std::string& file_contents,\n                                            RVA rva) {\n  auto* minidump_byte_array =\n      MinidumpWritableAtRVA<MinidumpByteArray>(file_contents, rva);\n  if (!minidump_byte_array) {\n    return {};\n  }\n  auto* data = static_cast<const uint8_t*>(minidump_byte_array->data);\n  const uint8_t* data_end = data + minidump_byte_array->length;\n  return std::vector<uint8_t>(data, data_end);\n}\n\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/test/minidump_byte_array_writer_test_util.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_\n#define MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n\n#include <string>\n#include <vector>\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Returns the bytes referenced by a MinidumpByteArray object located\n//!     in a minidump file at the specified RVA.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] rva The offset in the minidump file of the MinidumpByteArray.\n//!\n//! \\return The MinidumpByteArray::data referenced by the \\a rva. Note that\n//!       this function does not check that the data are within the bounds of\n//!       the \\a file_contents.\nstd::vector<uint8_t> MinidumpByteArrayAtRVA(const std::string& file_contents,\n                                            RVA rva);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_\n"
  },
  {
    "path": "minidump/test/minidump_context_test_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/test/minidump_context_test_util.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/test/test_cpu_context.h\"\n#include \"test/hex_string.h\"\n\nnamespace crashpad {\nnamespace test {\n\nvoid InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed) {\n  if (seed == 0) {\n    memset(context, 0, sizeof(*context));\n    context->context_flags = kMinidumpContextX86;\n    return;\n  }\n\n  context->context_flags = kMinidumpContextX86All;\n\n  uint32_t value = seed;\n\n  context->eax = value++;\n  context->ebx = value++;\n  context->ecx = value++;\n  context->edx = value++;\n  context->edi = value++;\n  context->esi = value++;\n  context->ebp = value++;\n  context->esp = value++;\n  context->eip = value++;\n  context->eflags = value++;\n  context->cs = value++ & 0xffff;\n  context->ds = value++ & 0xffff;\n  context->es = value++ & 0xffff;\n  context->fs = value++ & 0xffff;\n  context->gs = value++ & 0xffff;\n  context->ss = value++ & 0xffff;\n\n  InitializeCPUContextX86Fxsave(&context->fxsave, &value);\n  CPUContextX86::FxsaveToFsave(context->fxsave, &context->fsave);\n\n  context->dr0 = value++;\n  context->dr1 = value++;\n  context->dr2 = value++;\n  context->dr3 = value++;\n  value += 2;  // Minidumps don’t carry dr4 or dr5.\n  context->dr6 = value++;\n  context->dr7 = value++;\n\n  // Set this field last, because it has no analogue in CPUContextX86.\n  context->float_save.spare_0 = value++;\n}\n\nvoid InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,\n                                    uint32_t seed) {\n  if (seed == 0) {\n    memset(context, 0, sizeof(*context));\n    context->context_flags = kMinidumpContextAMD64;\n    return;\n  }\n\n  context->context_flags = kMinidumpContextAMD64All;\n\n  uint32_t value = seed;\n\n  context->rax = value++;\n  context->rbx = value++;\n  context->rcx = value++;\n  context->rdx = value++;\n  context->rdi = value++;\n  context->rsi = value++;\n  context->rbp = value++;\n  context->rsp = value++;\n  context->r8 = value++;\n  context->r9 = value++;\n  context->r10 = value++;\n  context->r11 = value++;\n  context->r12 = value++;\n  context->r13 = value++;\n  context->r14 = value++;\n  context->r15 = value++;\n  context->rip = value++;\n  context->eflags = value++;\n  context->cs = static_cast<uint16_t>(value++);\n  context->fs = static_cast<uint16_t>(value++);\n  context->gs = static_cast<uint16_t>(value++);\n\n  InitializeCPUContextX86_64Fxsave(&context->fxsave, &value);\n\n  // mxcsr appears twice, and the two values should be aliased.\n  context->mx_csr = context->fxsave.mxcsr;\n\n  context->dr0 = value++;\n  context->dr1 = value++;\n  context->dr2 = value++;\n  context->dr3 = value++;\n  value += 2;  // Minidumps don’t carry dr4 or dr5.\n  context->dr6 = value++;\n  context->dr7 = value++;\n\n  // Set these fields last, because they have no analogues in CPUContextX86_64.\n  context->p1_home = value++;\n  context->p2_home = value++;\n  context->p3_home = value++;\n  context->p4_home = value++;\n  context->p5_home = value++;\n  context->p6_home = value++;\n  context->ds = static_cast<uint16_t>(value++);\n  context->es = static_cast<uint16_t>(value++);\n  context->ss = static_cast<uint16_t>(value++);\n  for (size_t index = 0; index < std::size(context->vector_register); ++index) {\n    context->vector_register[index].lo = value++;\n    context->vector_register[index].hi = value++;\n  }\n  context->vector_control = value++;\n  context->debug_control = value++;\n  context->last_branch_to_rip = value++;\n  context->last_branch_from_rip = value++;\n  context->last_exception_to_rip = value++;\n  context->last_exception_from_rip = value++;\n}\n\nvoid InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed) {\n  if (seed == 0) {\n    memset(context, 0, sizeof(*context));\n    context->context_flags = kMinidumpContextARM;\n    return;\n  }\n\n  context->context_flags = kMinidumpContextARMAll;\n\n  uint32_t value = seed;\n\n  for (size_t index = 0; index < std::size(context->regs); ++index) {\n    context->regs[index] = value++;\n  }\n  context->fp = value++;\n  context->ip = value++;\n  context->ip = value++;\n  context->sp = value++;\n  context->lr = value++;\n  context->pc = value++;\n  context->cpsr = value++;\n\n  for (size_t index = 0; index < std::size(context->vfp); ++index) {\n    context->vfp[index] = value++;\n  }\n  context->fpscr = value++;\n}\n\nvoid InitializeMinidumpContextARM64(MinidumpContextARM64* context,\n                                    uint32_t seed) {\n  if (seed == 0) {\n    memset(context, 0, sizeof(*context));\n    context->context_flags = kMinidumpContextARM64;\n    return;\n  }\n\n  context->context_flags = kMinidumpContextARM64Full;\n\n  uint32_t value = seed;\n\n  for (size_t index = 0; index < std::size(context->regs); ++index) {\n    context->regs[index] = value++;\n  }\n  context->fp = value++;\n  context->lr = value++;\n  context->sp = value++;\n  context->pc = value++;\n  context->cpsr = value++;\n\n  for (size_t index = 0; index < std::size(context->fpsimd); ++index) {\n    context->fpsimd[index].lo = value++;\n    context->fpsimd[index].hi = value++;\n  }\n  context->fpsr = value++;\n  context->fpcr = value++;\n}\n\nvoid InitializeMinidumpContextMIPS(MinidumpContextMIPS* context,\n                                   uint32_t seed) {\n  if (seed == 0) {\n    memset(context, 0, sizeof(*context));\n    context->context_flags = kMinidumpContextMIPS;\n    return;\n  }\n\n  context->context_flags = kMinidumpContextMIPSAll;\n\n  uint32_t value = seed;\n\n  for (size_t index = 0; index < std::size(context->regs); ++index) {\n    context->regs[index] = value++;\n  }\n\n  context->mdlo = value++;\n  context->mdhi = value++;\n  context->epc = value++;\n  context->badvaddr = value++;\n  context->status = value++;\n  context->cause = value++;\n\n  for (size_t index = 0; index < std::size(context->fpregs.fregs); ++index) {\n    context->fpregs.fregs[index]._fp_fregs = static_cast<float>(value++);\n  }\n\n  context->fpcsr = value++;\n  context->fir = value++;\n\n  for (size_t index = 0; index < 3; ++index) {\n    context->hi[index] = value++;\n    context->lo[index] = value++;\n  }\n\n  context->dsp_control = value++;\n}\n\nvoid InitializeMinidumpContextMIPS64(MinidumpContextMIPS64* context,\n                                     uint32_t seed) {\n  if (seed == 0) {\n    memset(context, 0, sizeof(*context));\n    context->context_flags = kMinidumpContextMIPS64;\n    return;\n  }\n\n  context->context_flags = kMinidumpContextMIPS64All;\n\n  uint64_t value = seed;\n\n  for (size_t index = 0; index < std::size(context->regs); ++index) {\n    context->regs[index] = value++;\n  }\n\n  context->mdlo = value++;\n  context->mdhi = value++;\n  context->epc = value++;\n  context->badvaddr = value++;\n  context->status = value++;\n  context->cause = value++;\n\n  for (size_t index = 0; index < std::size(context->fpregs.dregs); ++index) {\n    context->fpregs.dregs[index] = static_cast<double>(value++);\n  }\n  context->fpcsr = value++;\n  context->fir = value++;\n\n  for (size_t index = 0; index < 3; ++index) {\n    context->hi[index] = value++;\n    context->lo[index] = value++;\n  }\n  context->dsp_control = value++;\n}\n\nvoid InitializeMinidumpContextRISCV64(MinidumpContextRISCV64* context,\n                                      uint32_t seed) {\n  if (seed == 0) {\n    memset(context, 0, sizeof(*context));\n    context->context_flags = kMinidumpContextRISCV64;\n    context->version = MinidumpContextRISCV64::kVersion;\n    return;\n  }\n\n  context->context_flags = kMinidumpContextRISCV64All;\n  context->version = MinidumpContextRISCV64::kVersion;\n\n  uint32_t value = seed;\n\n  context->pc = value++;\n  for (size_t index = 0; index < std::size(context->regs); ++index) {\n    context->regs[index] = value++;\n  }\n\n  for (size_t index = 0; index < std::size(context->fpregs); ++index) {\n    context->fpregs[index] = value++;\n  }\n  context->fcsr = value++;\n}\n\nnamespace {\n\n// Using Google Test assertions, compares |expected| to |observed|. This is\n// templatized because the CPUContextX86::Fxsave and CPUContextX86_64::Fxsave\n// are nearly identical but have different sizes for the members |xmm|,\n// |reserved_4|, and |available|.\ntemplate <typename FxsaveType>\nvoid ExpectMinidumpContextFxsave(const FxsaveType* expected,\n                                 const FxsaveType* observed) {\n  EXPECT_EQ(observed->fcw, expected->fcw);\n  EXPECT_EQ(observed->fsw, expected->fsw);\n  EXPECT_EQ(observed->ftw, expected->ftw);\n  EXPECT_EQ(observed->reserved_1, expected->reserved_1);\n  EXPECT_EQ(observed->fop, expected->fop);\n  EXPECT_EQ(observed->fpu_ip, expected->fpu_ip);\n  EXPECT_EQ(observed->fpu_cs, expected->fpu_cs);\n  EXPECT_EQ(observed->reserved_2, expected->reserved_2);\n  EXPECT_EQ(observed->fpu_dp, expected->fpu_dp);\n  EXPECT_EQ(observed->fpu_ds, expected->fpu_ds);\n  EXPECT_EQ(observed->reserved_3, expected->reserved_3);\n  EXPECT_EQ(observed->mxcsr, expected->mxcsr);\n  EXPECT_EQ(observed->mxcsr_mask, expected->mxcsr_mask);\n  for (size_t st_mm_index = 0; st_mm_index < std::size(expected->st_mm);\n       ++st_mm_index) {\n    SCOPED_TRACE(base::StringPrintf(\"st_mm_index %\" PRIuS, st_mm_index));\n    EXPECT_EQ(BytesToHexString(observed->st_mm[st_mm_index].st,\n                               std::size(observed->st_mm[st_mm_index].st)),\n              BytesToHexString(expected->st_mm[st_mm_index].st,\n                               std::size(expected->st_mm[st_mm_index].st)));\n    EXPECT_EQ(\n        BytesToHexString(observed->st_mm[st_mm_index].st_reserved,\n                         std::size(observed->st_mm[st_mm_index].st_reserved)),\n        BytesToHexString(expected->st_mm[st_mm_index].st_reserved,\n                         std::size(expected->st_mm[st_mm_index].st_reserved)));\n  }\n  for (size_t xmm_index = 0; xmm_index < std::size(expected->xmm);\n       ++xmm_index) {\n    EXPECT_EQ(BytesToHexString(observed->xmm[xmm_index],\n                               std::size(observed->xmm[xmm_index])),\n              BytesToHexString(expected->xmm[xmm_index],\n                               std::size(expected->xmm[xmm_index])))\n        << \"xmm_index \" << xmm_index;\n  }\n  EXPECT_EQ(\n      BytesToHexString(observed->reserved_4, std::size(observed->reserved_4)),\n      BytesToHexString(expected->reserved_4, std::size(expected->reserved_4)));\n  EXPECT_EQ(\n      BytesToHexString(observed->available, std::size(observed->available)),\n      BytesToHexString(expected->available, std::size(expected->available)));\n}\n\n}  // namespace\n\nvoid ExpectMinidumpContextX86(\n    uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot) {\n  MinidumpContextX86 expected;\n  InitializeMinidumpContextX86(&expected, expect_seed);\n\n  EXPECT_EQ(observed->context_flags, expected.context_flags);\n  EXPECT_EQ(observed->dr0, expected.dr0);\n  EXPECT_EQ(observed->dr1, expected.dr1);\n  EXPECT_EQ(observed->dr2, expected.dr2);\n  EXPECT_EQ(observed->dr3, expected.dr3);\n  EXPECT_EQ(observed->dr6, expected.dr6);\n  EXPECT_EQ(observed->dr7, expected.dr7);\n\n  EXPECT_EQ(observed->fsave.fcw, expected.fsave.fcw);\n  EXPECT_EQ(observed->fsave.fsw, expected.fsave.fsw);\n  EXPECT_EQ(observed->fsave.ftw, expected.fsave.ftw);\n  EXPECT_EQ(observed->fsave.fpu_ip, expected.fsave.fpu_ip);\n  EXPECT_EQ(observed->fsave.fpu_cs, expected.fsave.fpu_cs);\n  EXPECT_EQ(observed->fsave.fpu_dp, expected.fsave.fpu_dp);\n  EXPECT_EQ(observed->fsave.fpu_ds, expected.fsave.fpu_ds);\n  for (size_t index = 0; index < std::size(expected.fsave.st); ++index) {\n    EXPECT_EQ(BytesToHexString(observed->fsave.st[index],\n                               std::size(observed->fsave.st[index])),\n              BytesToHexString(expected.fsave.st[index],\n                               std::size(expected.fsave.st[index])))\n        << \"index \" << index;\n  }\n  if (snapshot) {\n    EXPECT_EQ(observed->float_save.spare_0, 0u);\n  } else {\n    EXPECT_EQ(observed->float_save.spare_0, expected.float_save.spare_0);\n  }\n\n  EXPECT_EQ(observed->gs, expected.gs);\n  EXPECT_EQ(observed->fs, expected.fs);\n  EXPECT_EQ(observed->es, expected.es);\n  EXPECT_EQ(observed->ds, expected.ds);\n  EXPECT_EQ(observed->edi, expected.edi);\n  EXPECT_EQ(observed->esi, expected.esi);\n  EXPECT_EQ(observed->ebx, expected.ebx);\n  EXPECT_EQ(observed->edx, expected.edx);\n  EXPECT_EQ(observed->ecx, expected.ecx);\n  EXPECT_EQ(observed->eax, expected.eax);\n  EXPECT_EQ(observed->ebp, expected.ebp);\n  EXPECT_EQ(observed->eip, expected.eip);\n  EXPECT_EQ(observed->cs, expected.cs);\n  EXPECT_EQ(observed->eflags, expected.eflags);\n  EXPECT_EQ(observed->esp, expected.esp);\n  EXPECT_EQ(observed->ss, expected.ss);\n\n  ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave);\n}\n\nvoid ExpectMinidumpContextAMD64(\n    uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot) {\n  MinidumpContextAMD64 expected;\n  InitializeMinidumpContextAMD64(&expected, expect_seed);\n\n  // Allow context_flags to include xstate bit - this is added if we will write\n  // an extended context, but is not generated in the fixed context for testing.\n  if ((observed->context_flags & kMinidumpContextAMD64Xstate) ==\n      kMinidumpContextAMD64Xstate) {\n    EXPECT_EQ(observed->context_flags,\n              (expected.context_flags | kMinidumpContextAMD64Xstate));\n  } else {\n    EXPECT_EQ(observed->context_flags, expected.context_flags);\n  }\n\n  if (snapshot) {\n    EXPECT_EQ(observed->p1_home, 0u);\n    EXPECT_EQ(observed->p2_home, 0u);\n    EXPECT_EQ(observed->p3_home, 0u);\n    EXPECT_EQ(observed->p4_home, 0u);\n    EXPECT_EQ(observed->p5_home, 0u);\n    EXPECT_EQ(observed->p6_home, 0u);\n  } else {\n    EXPECT_EQ(observed->p1_home, expected.p1_home);\n    EXPECT_EQ(observed->p2_home, expected.p2_home);\n    EXPECT_EQ(observed->p3_home, expected.p3_home);\n    EXPECT_EQ(observed->p4_home, expected.p4_home);\n    EXPECT_EQ(observed->p5_home, expected.p5_home);\n    EXPECT_EQ(observed->p6_home, expected.p6_home);\n  }\n\n  EXPECT_EQ(observed->mx_csr, expected.mx_csr);\n\n  EXPECT_EQ(observed->cs, expected.cs);\n  if (snapshot) {\n    EXPECT_EQ(observed->ds, 0u);\n    EXPECT_EQ(observed->es, 0u);\n  } else {\n    EXPECT_EQ(observed->ds, expected.ds);\n    EXPECT_EQ(observed->es, expected.es);\n  }\n  EXPECT_EQ(observed->fs, expected.fs);\n  EXPECT_EQ(observed->gs, expected.gs);\n  if (snapshot) {\n    EXPECT_EQ(observed->ss, 0u);\n  } else {\n    EXPECT_EQ(observed->ss, expected.ss);\n  }\n\n  EXPECT_EQ(observed->eflags, expected.eflags);\n\n  EXPECT_EQ(observed->dr0, expected.dr0);\n  EXPECT_EQ(observed->dr1, expected.dr1);\n  EXPECT_EQ(observed->dr2, expected.dr2);\n  EXPECT_EQ(observed->dr3, expected.dr3);\n  EXPECT_EQ(observed->dr6, expected.dr6);\n  EXPECT_EQ(observed->dr7, expected.dr7);\n\n  EXPECT_EQ(observed->rax, expected.rax);\n  EXPECT_EQ(observed->rcx, expected.rcx);\n  EXPECT_EQ(observed->rdx, expected.rdx);\n  EXPECT_EQ(observed->rbx, expected.rbx);\n  EXPECT_EQ(observed->rsp, expected.rsp);\n  EXPECT_EQ(observed->rbp, expected.rbp);\n  EXPECT_EQ(observed->rsi, expected.rsi);\n  EXPECT_EQ(observed->rdi, expected.rdi);\n  EXPECT_EQ(observed->r8, expected.r8);\n  EXPECT_EQ(observed->r9, expected.r9);\n  EXPECT_EQ(observed->r10, expected.r10);\n  EXPECT_EQ(observed->r11, expected.r11);\n  EXPECT_EQ(observed->r12, expected.r12);\n  EXPECT_EQ(observed->r13, expected.r13);\n  EXPECT_EQ(observed->r14, expected.r14);\n  EXPECT_EQ(observed->r15, expected.r15);\n  EXPECT_EQ(observed->rip, expected.rip);\n\n  ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave);\n\n  for (size_t index = 0; index < std::size(expected.vector_register); ++index) {\n    if (snapshot) {\n      EXPECT_EQ(observed->vector_register[index].lo, 0u) << \"index \" << index;\n      EXPECT_EQ(observed->vector_register[index].hi, 0u) << \"index \" << index;\n    } else {\n      EXPECT_EQ(observed->vector_register[index].lo,\n                expected.vector_register[index].lo)\n          << \"index \" << index;\n      EXPECT_EQ(observed->vector_register[index].hi,\n                expected.vector_register[index].hi)\n          << \"index \" << index;\n    }\n  }\n\n  if (snapshot) {\n    EXPECT_EQ(observed->vector_control, 0u);\n    EXPECT_EQ(observed->debug_control, 0u);\n    EXPECT_EQ(observed->last_branch_to_rip, 0u);\n    EXPECT_EQ(observed->last_branch_from_rip, 0u);\n    EXPECT_EQ(observed->last_exception_to_rip, 0u);\n    EXPECT_EQ(observed->last_exception_from_rip, 0u);\n  } else {\n    EXPECT_EQ(observed->vector_control, expected.vector_control);\n    EXPECT_EQ(observed->debug_control, expected.debug_control);\n    EXPECT_EQ(observed->last_branch_to_rip, expected.last_branch_to_rip);\n    EXPECT_EQ(observed->last_branch_from_rip, expected.last_branch_from_rip);\n    EXPECT_EQ(observed->last_exception_to_rip, expected.last_exception_to_rip);\n    EXPECT_EQ(observed->last_exception_from_rip,\n              expected.last_exception_from_rip);\n  }\n}\n\nvoid ExpectMinidumpContextARM(uint32_t expect_seed,\n                              const MinidumpContextARM* observed,\n                              bool snapshot) {\n  MinidumpContextARM expected;\n  InitializeMinidumpContextARM(&expected, expect_seed);\n\n  EXPECT_EQ(observed->context_flags, expected.context_flags);\n\n  for (size_t index = 0; index < std::size(expected.regs); ++index) {\n    EXPECT_EQ(observed->regs[index], expected.regs[index]);\n  }\n  EXPECT_EQ(observed->fp, expected.fp);\n  EXPECT_EQ(observed->ip, expected.ip);\n  EXPECT_EQ(observed->sp, expected.sp);\n  EXPECT_EQ(observed->lr, expected.lr);\n  EXPECT_EQ(observed->pc, expected.pc);\n  EXPECT_EQ(observed->cpsr, expected.cpsr);\n\n  EXPECT_EQ(observed->fpscr, expected.fpscr);\n  for (size_t index = 0; index < std::size(expected.vfp); ++index) {\n    EXPECT_EQ(observed->vfp[index], expected.vfp[index]);\n  }\n  for (size_t index = 0; index < std::size(expected.extra); ++index) {\n    EXPECT_EQ(observed->extra[index], snapshot ? 0 : expected.extra[index]);\n  }\n}\n\nvoid ExpectMinidumpContextARM64(uint32_t expect_seed,\n                                const MinidumpContextARM64* observed,\n                                bool snapshot) {\n  MinidumpContextARM64 expected;\n  InitializeMinidumpContextARM64(&expected, expect_seed);\n\n  EXPECT_EQ(observed->context_flags, expected.context_flags);\n\n  for (size_t index = 0; index < std::size(expected.regs); ++index) {\n    EXPECT_EQ(observed->regs[index], expected.regs[index]);\n  }\n  EXPECT_EQ(observed->cpsr, expected.cpsr);\n\n  EXPECT_EQ(observed->fpsr, expected.fpsr);\n  EXPECT_EQ(observed->fpcr, expected.fpcr);\n  for (size_t index = 0; index < std::size(expected.fpsimd); ++index) {\n    EXPECT_EQ(observed->fpsimd[index].lo, expected.fpsimd[index].lo);\n    EXPECT_EQ(observed->fpsimd[index].hi, expected.fpsimd[index].hi);\n  }\n}\n\nvoid ExpectMinidumpContextMIPS(uint32_t expect_seed,\n                               const MinidumpContextMIPS* observed,\n                               bool snapshot) {\n  MinidumpContextMIPS expected;\n  InitializeMinidumpContextMIPS(&expected, expect_seed);\n\n  EXPECT_EQ(observed->context_flags, expected.context_flags);\n\n  for (size_t index = 0; index < std::size(expected.regs); ++index) {\n    EXPECT_EQ(observed->regs[index], expected.regs[index]);\n  }\n\n  EXPECT_EQ(observed->mdlo, expected.mdlo);\n  EXPECT_EQ(observed->mdhi, expected.mdhi);\n  EXPECT_EQ(observed->epc, expected.epc);\n  EXPECT_EQ(observed->badvaddr, expected.badvaddr);\n  EXPECT_EQ(observed->status, expected.status);\n  EXPECT_EQ(observed->cause, expected.cause);\n\n  for (size_t index = 0; index < std::size(expected.fpregs.fregs); ++index) {\n    EXPECT_EQ(observed->fpregs.fregs[index]._fp_fregs,\n              expected.fpregs.fregs[index]._fp_fregs);\n  }\n  EXPECT_EQ(observed->fpcsr, expected.fpcsr);\n  EXPECT_EQ(observed->fir, expected.fir);\n\n  for (size_t index = 0; index < 3; ++index) {\n    EXPECT_EQ(observed->hi[index], expected.hi[index]);\n    EXPECT_EQ(observed->lo[index], expected.lo[index]);\n  }\n  EXPECT_EQ(observed->dsp_control, expected.dsp_control);\n}\n\nvoid ExpectMinidumpContextMIPS64(uint32_t expect_seed,\n                                 const MinidumpContextMIPS64* observed,\n                                 bool snapshot) {\n  MinidumpContextMIPS64 expected;\n  InitializeMinidumpContextMIPS64(&expected, expect_seed);\n\n  EXPECT_EQ(observed->context_flags, expected.context_flags);\n\n  for (size_t index = 0; index < std::size(expected.regs); ++index) {\n    EXPECT_EQ(observed->regs[index], expected.regs[index]);\n  }\n\n  EXPECT_EQ(observed->mdlo, expected.mdlo);\n  EXPECT_EQ(observed->mdhi, expected.mdhi);\n  EXPECT_EQ(observed->epc, expected.epc);\n  EXPECT_EQ(observed->badvaddr, expected.badvaddr);\n  EXPECT_EQ(observed->status, expected.status);\n  EXPECT_EQ(observed->cause, expected.cause);\n\n  for (size_t index = 0; index < std::size(expected.fpregs.dregs); ++index) {\n    EXPECT_EQ(observed->fpregs.dregs[index], expected.fpregs.dregs[index]);\n  }\n  EXPECT_EQ(observed->fpcsr, expected.fpcsr);\n  EXPECT_EQ(observed->fir, expected.fir);\n\n  for (size_t index = 0; index < 3; ++index) {\n    EXPECT_EQ(observed->hi[index], expected.hi[index]);\n    EXPECT_EQ(observed->lo[index], expected.lo[index]);\n  }\n  EXPECT_EQ(observed->dsp_control, expected.dsp_control);\n}\n\nvoid ExpectMinidumpContextRISCV64(uint32_t expect_seed,\n                                  const MinidumpContextRISCV64* observed,\n                                  bool snapshot) {\n  MinidumpContextRISCV64 expected;\n  InitializeMinidumpContextRISCV64(&expected, expect_seed);\n\n  EXPECT_EQ(observed->context_flags, expected.context_flags);\n  EXPECT_EQ(observed->version, expected.version);\n\n  for (size_t index = 0; index < std::size(expected.regs); ++index) {\n    EXPECT_EQ(observed->regs[index], expected.regs[index]);\n  }\n\n  for (size_t index = 0; index < std::size(expected.fpregs); ++index) {\n    EXPECT_EQ(observed->fpregs[index], expected.fpregs[index]);\n  }\n  EXPECT_EQ(observed->fcsr, expected.fcsr);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/test/minidump_context_test_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_\n#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_\n\n#include <stdint.h>\n\n#include \"minidump/minidump_context.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Initializes a context structure for testing.\n//!\n//! Initialization is compatible with the initialization used by CPUContext test\n//! initialization functions such as InitializeCPUContextX86() and\n//! InitializeCPUContextX86_64() for identical \\a seed values.\n//!\n//! \\param[out] context The structure to initialize.\n//! \\param[in] seed The seed value. Initializing two context structures of the\n//!     same type with identical seed values should produce identical context\n//!     structures. Initialization with a different seed value should produce\n//!     a different context structure. If \\a seed is `0`, \\a context is zeroed\n//!     out entirely except for the flags field, which will identify the context\n//!     type. If \\a seed is nonzero, \\a context will be populated entirely with\n//!     nonzero values.\n//!\n//! \\{\nvoid InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed);\nvoid InitializeMinidumpContextAMD64(MinidumpContextAMD64* context,\n                                    uint32_t seed);\nvoid InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed);\nvoid InitializeMinidumpContextARM64(MinidumpContextARM64* context,\n                                    uint32_t seed);\nvoid InitializeMinidumpContextMIPS(MinidumpContextMIPS* context, uint32_t seed);\nvoid InitializeMinidumpContextMIPS64(MinidumpContextMIPS* context,\n                                     uint32_t seed);\nvoid InitializeMinidumpContextRISCV64(MinidumpContextRISCV64* context,\n                                      uint32_t seed);\n//! \\}\n\n//! \\brief Verifies, via Google Test assertions, that a context structure\n//!     contains expected values.\n//!\n//! \\param[in] expect_seed The seed value used to initialize a context\n//!     structure. This is the seed value used with\n//!     InitializeMinidumpContext*().\n//! \\param[in] observed The context structure to check. All fields of this\n//!     structure will be compared against the expected context structure, one\n//!     initialized with \\a expect_seed.\n//! \\param[in] snapshot If `true`, compare \\a observed to a context structure\n//!     expected to be produced from a CPUContext snapshot. If `false`, compare\n//!     \\a observed to a native minidump context structure. CPUContext snapshot\n//!     structures may carry different sets of data than native minidump context\n//!     structures in meaningless ways. When `true`, fields not found in\n//!     CPUContext structures are expected to be `0`. When `false`, all fields\n//!     are compared. This makes it possible to test both that these fields are\n//!     passed through correctly by the native minidump writer and are zeroed\n//!     out when creating a minidump context structure from a CPUContext\n//!     structure.\n//! \\{\nvoid ExpectMinidumpContextX86(\n    uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot);\nvoid ExpectMinidumpContextAMD64(\n    uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot);\nvoid ExpectMinidumpContextARM(uint32_t expect_seed,\n                              const MinidumpContextARM* observed,\n                              bool snapshot);\nvoid ExpectMinidumpContextARM64(uint32_t expect_seed,\n                                const MinidumpContextARM64* observed,\n                                bool snapshot);\nvoid ExpectMinidumpContextMIPS(uint32_t expect_seed,\n                               const MinidumpContextMIPS* observed,\n                               bool snapshot);\nvoid ExpectMinidumpContextMIPS64(uint32_t expect_seed,\n                                 const MinidumpContextMIPS64* observed,\n                                 bool snapshot);\nvoid ExpectMinidumpContextRISCV64(uint32_t expect_seed,\n                                  const MinidumpContextRISCV64* observed,\n                                  bool snapshot);\n//! \\}\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_\n"
  },
  {
    "path": "minidump/test/minidump_file_writer_test_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/test/minidump_file_writer_test_util.h\"\n\n#include \"gtest/gtest.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n\nnamespace crashpad {\nnamespace test {\n\nconst MINIDUMP_HEADER* MinidumpHeaderAtStart(\n    const std::string& file_contents,\n    const MINIDUMP_DIRECTORY** directory) {\n  MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;\n  location_descriptor.DataSize = sizeof(MINIDUMP_HEADER);\n  location_descriptor.Rva = 0;\n\n  const MINIDUMP_HEADER* header =\n      MinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>(\n          file_contents, location_descriptor);\n\n  if (header) {\n    location_descriptor.DataSize =\n        header->NumberOfStreams * sizeof(MINIDUMP_DIRECTORY);\n    location_descriptor.Rva = header->StreamDirectoryRva;\n    *directory = MinidumpWritableAtLocationDescriptor<MINIDUMP_DIRECTORY>(\n        file_contents, location_descriptor);\n  } else {\n    *directory = nullptr;\n  }\n\n  return header;\n}\n\nvoid VerifyMinidumpHeader(const MINIDUMP_HEADER* header,\n                          uint32_t streams,\n                          uint32_t timestamp) {\n  ASSERT_TRUE(header);\n  ASSERT_EQ(header->NumberOfStreams, streams);\n  ASSERT_EQ(header->StreamDirectoryRva, streams ? sizeof(MINIDUMP_HEADER) : 0u);\n  EXPECT_EQ(header->CheckSum, 0u);\n  EXPECT_EQ(header->TimeDateStamp, timestamp);\n  EXPECT_EQ(static_cast<MINIDUMP_TYPE>(header->Flags), MiniDumpNormal);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/test/minidump_file_writer_test_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_\n#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n\n#include <string>\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Returns the MINIDUMP_HEADER at the start of a minidump file, along\n//!     with the MINIDUMP_DIRECTORY it references.\n//!\n//! This function validates the MINIDUMP_HEADER::Signature and\n//! MINIDUMP_HEADER::Version fields.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[out] directory The MINIDUMP_DIRECTORY referenced by the\n//!     MINIDUMP_HEADER. If the MINIDUMP_HEADER does not reference a\n//!     MINIDUMP_DIRECTORY, `nullptr` without raising a Google Test assertion.\n//!     If the referenced MINIDUMP_DIRECTORY is not valid, `nullptr` with a\n//!     Google Test assertion raised. On failure, `nullptr`.\n//!\n//! \\return On success, the MINIDUMP_HEADER at the beginning of the minidump\n//!     file. On failure, raises a Google Test assertion and returns `nullptr`.\nconst MINIDUMP_HEADER* MinidumpHeaderAtStart(\n    const std::string& file_contents,\n    const MINIDUMP_DIRECTORY** directory);\n\n//! \\brief Verifies, via Google Test assertions, that a MINIDUMP_HEADER contains\n//!     expected values.\n//!\n//! All fields in the MINIDUMP_HEADER will be evaluated except for the Signature\n//! and Version fields, because those are checked by MinidumpHeaderAtStart().\n//! Most other fields are are compared to their correct default values.\n//! MINIDUMP_HEADER::NumberOfStreams is compared to \\a streams, and\n//! MINIDUMP_HEADER::TimeDateStamp is compared to \\a timestamp. Most fields are\n//! checked with nonfatal EXPECT-style assertions, but\n//! MINIDUMP_HEADER::NumberOfStreams and MINIDUMP_HEADER::StreamDirectoryRva are\n//! checked with fatal ASSERT-style assertions, because they must be correct in\n//! order for processing of the minidump to continue.\nvoid VerifyMinidumpHeader(const MINIDUMP_HEADER* header,\n                          uint32_t streams,\n                          uint32_t timestamp);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_\n"
  },
  {
    "path": "minidump/test/minidump_memory_writer_test_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/test/minidump_memory_writer_test_util.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\n\nTestMinidumpMemoryWriter::TestMinidumpMemoryWriter(uint64_t base_address,\n                                                   size_t size,\n                                                   uint8_t value)\n    : SnapshotMinidumpMemoryWriter(&test_snapshot_) {\n  test_snapshot_.SetAddress(base_address);\n  test_snapshot_.SetSize(size);\n  test_snapshot_.SetValue(value);\n}\n\nTestMinidumpMemoryWriter::~TestMinidumpMemoryWriter() {\n}\n\nvoid TestMinidumpMemoryWriter::SetShouldFailRead(bool should_fail) {\n  test_snapshot_.SetShouldFailRead(should_fail);\n}\n\nvoid ExpectMinidumpMemoryDescriptor(\n    const MINIDUMP_MEMORY_DESCRIPTOR* expected,\n    const MINIDUMP_MEMORY_DESCRIPTOR* observed) {\n  MINIDUMP_MEMORY_DESCRIPTOR expected_descriptor;\n  MINIDUMP_MEMORY_DESCRIPTOR observed_descriptor;\n\n  memcpy(&expected_descriptor, expected, sizeof(expected_descriptor));\n  memcpy(&observed_descriptor, observed, sizeof(observed_descriptor));\n\n  EXPECT_EQ(observed_descriptor.StartOfMemoryRange,\n            expected_descriptor.StartOfMemoryRange);\n  EXPECT_EQ(observed_descriptor.Memory.DataSize,\n            expected_descriptor.Memory.DataSize);\n  if (expected_descriptor.Memory.Rva != 0) {\n    constexpr uint32_t kMemoryAlignment = 16;\n    EXPECT_EQ(observed_descriptor.Memory.Rva,\n              (expected_descriptor.Memory.Rva + kMemoryAlignment - 1) &\n                  ~(kMemoryAlignment - 1));\n  }\n}\n\nvoid ExpectMinidumpMemoryDescriptorAndContents(\n    const MINIDUMP_MEMORY_DESCRIPTOR* expected,\n    const MINIDUMP_MEMORY_DESCRIPTOR* observed,\n    const std::string& file_contents,\n    uint8_t value,\n    bool at_eof) {\n  ExpectMinidumpMemoryDescriptor(expected, observed);\n\n  if (at_eof) {\n    EXPECT_EQ(observed->Memory.Rva + observed->Memory.DataSize,\n              file_contents.size());\n  } else {\n    EXPECT_GE(file_contents.size(),\n              observed->Memory.Rva + observed->Memory.DataSize);\n  }\n\n  std::string expected_data(expected->Memory.DataSize, value);\n  std::string observed_data(&file_contents[observed->Memory.Rva],\n                            observed->Memory.DataSize);\n  EXPECT_EQ(observed_data, expected_data);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/test/minidump_memory_writer_test_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_\n#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_\n\n#include \"minidump/minidump_memory_writer.h\"\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <string>\n\n#include \"snapshot/test/test_memory_snapshot.h\"\n#include \"util/file/file_writer.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A SnapshotMinidumpMemoryWriter implementation used for testing.\n//!\n//! TestMinidumpMemoryWriter objects are created with a fixed base address and\n//! size, and will write the same byte (\\a value) repeatedly, \\a size times.\nclass TestMinidumpMemoryWriter final : public SnapshotMinidumpMemoryWriter {\n public:\n  TestMinidumpMemoryWriter(uint64_t base_address, size_t size, uint8_t value);\n\n  TestMinidumpMemoryWriter(const TestMinidumpMemoryWriter&) = delete;\n  TestMinidumpMemoryWriter& operator=(const TestMinidumpMemoryWriter&) = delete;\n\n  ~TestMinidumpMemoryWriter();\n\n  void SetShouldFailRead(bool should_fail);\n\n private:\n  TestMemorySnapshot test_snapshot_;\n};\n\n//! \\brief Verifies, via Google Test assertions, that a\n//!     MINIDUMP_MEMORY_DESCRIPTOR structure contains expected values.\n//!\n//! In \\a expected and \\a observed,\n//! MINIDUMP_MEMORY_DESCRIPTOR::StartOfMemoryRange and\n//! MINIDUMP_LOCATION_DESCRIPTOR::DataSize are compared and must match. If\n//! MINIDUMP_LOCATION_DESCRIPTOR::Rva is nonzero in \\a expected, the same field\n//! in \\a observed must match it, subject to a 16-byte alignment augmentation.\n//!\n//! \\param[in] expected A MINIDUMP_MEMORY_DESCRIPTOR structure containing\n//!     expected values.\n//! \\param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing\n//!     observed values.\nvoid ExpectMinidumpMemoryDescriptor(const MINIDUMP_MEMORY_DESCRIPTOR* expected,\n                                    const MINIDUMP_MEMORY_DESCRIPTOR* observed);\n\n//! \\brief Verifies, via Google Test assertions, that a\n//!     MINIDUMP_MEMORY_DESCRIPTOR structure contains expected values, and that\n//!     the memory region it points to contains expected values assuming it was\n//!     written by a TestMinidumpMemoryWriter object.\n//!\n//! \\a expected and \\a observed are compared by\n//! ExpectMinidumpMemoryDescriptor().\n//!\n//! \\param[in] expected A MINIDUMP_MEMORY_DESCRIPTOR structure containing\n//!     expected values.\n//! \\param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing\n//!     observed values.\n//! \\param[in] file_contents The contents of the minidump file in which \\a\n//!     observed was found. The memory region referenced by \\a observed will be\n//!     read from this string.\n//! \\param[in] value The \\a value used to create a TestMinidumpMemoryWriter.\n//!     Each byte of memory in the region referenced by \\a observed must be this\n//!     value.\n//! \\param[in] at_eof If `true`, the region referenced by \\a observed must\n//!     appear at the end of \\a file_contents, without any data following it.\nvoid ExpectMinidumpMemoryDescriptorAndContents(\n    const MINIDUMP_MEMORY_DESCRIPTOR* expected,\n    const MINIDUMP_MEMORY_DESCRIPTOR* observed,\n    const std::string& file_contents,\n    uint8_t value,\n    bool at_eof);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_\n"
  },
  {
    "path": "minidump/test/minidump_rva_list_test_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/test/minidump_rva_list_test_util.h\"\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n\nnamespace crashpad {\nnamespace test {\n\nconst MinidumpRVAList* MinidumpRVAListAtStart(const std::string& file_contents,\n                                              size_t count) {\n  MINIDUMP_LOCATION_DESCRIPTOR location_descriptor;\n  location_descriptor.DataSize =\n      static_cast<uint32_t>(sizeof(MinidumpRVAList) + count * sizeof(RVA));\n  location_descriptor.Rva = 0;\n\n  const MinidumpRVAList* list =\n      MinidumpWritableAtLocationDescriptor<MinidumpRVAList>(\n          file_contents, location_descriptor);\n  if (!list) {\n    return nullptr;\n  }\n\n  if (list->count != count) {\n    EXPECT_EQ(list->count, count);\n    return nullptr;\n  }\n\n  return list;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/test/minidump_rva_list_test_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_\n#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_\n\n#include <sys/types.h>\n\n#include <string>\n\nnamespace crashpad {\n\nstruct MinidumpRVAList;\n\nnamespace test {\n\n//! \\brief Returns the MinidumpRVAList at the start of a minidump file.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] count The number of ::RVA objects expected in the\n//!     MinidumpRVAList. This function will only be successful if exactly this\n//!     many objects are present, and if space for them exists in \\a\n//!     file_contents.\n//!\n//! \\return On success, the MinidumpRVAList at the beginning of the file. On\n//!     failure, raises a Google Test assertion and returns `nullptr`.\nconst MinidumpRVAList* MinidumpRVAListAtStart(const std::string& file_contents,\n                                              size_t count);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_\n"
  },
  {
    "path": "minidump/test/minidump_string_writer_test_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/test/minidump_string_writer_test_util.h\"\n\n#include <sys/types.h>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\ntemplate <\n    typename T,\n    typename RVAType = RVA,\n    typename MinidumpLocationDescriptorType = MINIDUMP_LOCATION_DESCRIPTOR>\nconst T* TMinidumpStringAtRVA(const std::string& file_contents, RVAType rva) {\n  const T* string_base = MinidumpWritableAtRVA<T>(file_contents, rva);\n  if (!string_base) {\n    return nullptr;\n  }\n\n  // |Length| must indicate the ability to store an integral number of code\n  // units.\n  const size_t kCodeUnitSize = sizeof(string_base->Buffer[0]);\n  if (string_base->Length % kCodeUnitSize != 0) {\n    EXPECT_EQ(string_base->Length % kCodeUnitSize, 0u);\n    return nullptr;\n  }\n\n  // |Length| does not include space for the required NUL terminator.\n  MinidumpLocationDescriptorType location;\n  location.DataSize =\n      sizeof(*string_base) + string_base->Length + kCodeUnitSize;\n  location.Rva = rva;\n  const T* string =\n      MinidumpWritableAtLocationDescriptor<T>(file_contents, location);\n  if (!string) {\n    return nullptr;\n  }\n\n  EXPECT_EQ(string, string_base);\n\n  // Require the NUL terminator to be NUL.\n  if (string->Buffer[string->Length / kCodeUnitSize] != '\\0') {\n    EXPECT_EQ(string->Buffer[string->Length / kCodeUnitSize], '\\0');\n    return nullptr;\n  }\n\n  return string;\n}\n\ntemplate <typename StringType,\n          typename MinidumpStringType,\n          typename RVAType,\n          typename MinidumpLocationDescriptorType>\nStringType TMinidumpStringAtRVAAsString(const std::string& file_contents,\n                                        RVAType rva) {\n  const MinidumpStringType* minidump_string =\n      TMinidumpStringAtRVA<MinidumpStringType,\n                           RVAType,\n                           MinidumpLocationDescriptorType>(file_contents, rva);\n  if (!minidump_string) {\n    return StringType();\n  }\n\n  StringType minidump_string_data(\n      reinterpret_cast<const typename StringType::value_type*>(\n          &minidump_string->Buffer[0]),\n      minidump_string->Length / sizeof(minidump_string->Buffer[0]));\n  return minidump_string_data;\n}\n\n}  // namespace\n\nconst MINIDUMP_STRING* MinidumpStringAtRVA(const std::string& file_contents,\n                                           RVA rva) {\n  return TMinidumpStringAtRVA<MINIDUMP_STRING,\n                              RVA,\n                              MINIDUMP_LOCATION_DESCRIPTOR>(file_contents, rva);\n}\n\nconst MINIDUMP_STRING* MinidumpStringAtRVA(const std::string& file_contents,\n                                           RVA64 rva) {\n  return TMinidumpStringAtRVA<MINIDUMP_STRING,\n                              RVA64,\n                              MINIDUMP_LOCATION_DESCRIPTOR64>(file_contents,\n                                                              rva);\n}\n\nconst MinidumpUTF8String* MinidumpUTF8StringAtRVA(\n    const std::string& file_contents,\n    RVA rva) {\n  return TMinidumpStringAtRVA<MinidumpUTF8String,\n                              RVA,\n                              MINIDUMP_LOCATION_DESCRIPTOR>(file_contents, rva);\n}\n\nconst MinidumpUTF8String* MinidumpUTF8StringAtRVA(\n    const std::string& file_contents,\n    RVA64 rva) {\n  return TMinidumpStringAtRVA<MinidumpUTF8String,\n                              RVA64,\n                              MINIDUMP_LOCATION_DESCRIPTOR64>(file_contents,\n                                                              rva);\n}\n\nstd::u16string MinidumpStringAtRVAAsString(const std::string& file_contents,\n                                           RVA rva) {\n  return TMinidumpStringAtRVAAsString<std::u16string,\n                                      MINIDUMP_STRING,\n                                      RVA,\n                                      MINIDUMP_LOCATION_DESCRIPTOR>(\n      file_contents, rva);\n}\n\nstd::u16string MinidumpStringAtRVAAsString(const std::string& file_contents,\n                                           RVA64 rva) {\n  return TMinidumpStringAtRVAAsString<std::u16string,\n                                      MINIDUMP_STRING,\n                                      RVA64,\n                                      MINIDUMP_LOCATION_DESCRIPTOR64>(\n      file_contents, rva);\n}\n\nstd::string MinidumpUTF8StringAtRVAAsString(const std::string& file_contents,\n                                            RVA rva) {\n  return TMinidumpStringAtRVAAsString<std::string,\n                                      MinidumpUTF8String,\n                                      RVA,\n                                      MINIDUMP_LOCATION_DESCRIPTOR>(\n      file_contents, rva);\n}\n\nstd::string MinidumpUTF8StringAtRVAAsString(const std::string& file_contents,\n                                            RVA64 rva) {\n  return TMinidumpStringAtRVAAsString<std::string,\n                                      MinidumpUTF8String,\n                                      RVA64,\n                                      MINIDUMP_LOCATION_DESCRIPTOR64>(\n      file_contents, rva);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/test/minidump_string_writer_test_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_\n#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include <string>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/test/minidump_writable_test_util.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\n\nstruct MinidumpUTF8String;\n\nnamespace test {\n\n//! \\brief Returns a MINIDUMP_STRING located within a minidump file’s contents.\n//!\n//! If \\a rva points outside of the range of \\a file_contents, if the string has\n//! an incorrect length or is not `NUL`-terminated, or if any of the string data\n//! would lie outside of the range of \\a file_contents, this function will fail.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] rva The offset within the minidump file of the desired\n//!     MINIDUMP_STRING.\n//!\n//! \\return On success, a pointer to the MINIDUMP_STRING in \\a file_contents. On\n//!     failure, raises a Google Test assertion and returns `nullptr`.\n//!\n//! \\sa MinidumpStringAtRVAAsString()\n//! \\sa MinidumpUTF8StringAtRVA()\nconst MINIDUMP_STRING* MinidumpStringAtRVA(const std::string& file_contents,\n                                           RVA rva);\n\n//! \\brief 64-bit specialization of MinidumpStringAtRVA.\nconst MINIDUMP_STRING* MinidumpStringAtRVA(const std::string& file_contents,\n                                           RVA64 rva);\n\n//! \\brief Returns a MinidumpUTF8String located within a minidump file’s\n//!     contents.\n//!\n//! If \\a rva points outside of the range of \\a file_contents, if the string has\n//! an incorrect length or is not `NUL`-terminated, or if any of the string data\n//! would lie outside of the range of \\a file_contents, this function will fail.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] rva The offset within the minidump file of the desired\n//!     MinidumpUTF8String.\n//!\n//! \\return On success, a pointer to the MinidumpUTF8String in \\a file_contents.\n//!     On failure, raises a Google Test assertion and returns `nullptr`.\n//!\n//! \\sa MinidumpUTF8StringAtRVAAsString()\n//! \\sa MinidumpStringAtRVA()\nconst MinidumpUTF8String* MinidumpUTF8StringAtRVA(\n    const std::string& file_contents,\n    RVA rva);\n\n//! \\brief 64-bit specialization of MinidumpUTF8StringAtRVA.\nconst MinidumpUTF8String* MinidumpUTF8StringAtRVA(\n    const std::string& file_contents,\n    RVA64 rva);\n\n//! \\brief Returns the contents of a MINIDUMP_STRING as a `std::u16string`.\n//!\n//! This function uses MinidumpStringAtRVA() to obtain a MINIDUMP_STRING, and\n//! returns the string data as a `std::u16string`.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] rva The offset within the minidump file of the desired\n//!     MINIDUMP_STRING.\n//!\n//! \\return On success, the string read from \\a file_writer at offset \\a rva. On\n//!     failure, raises a Google Test assertion and returns an empty string.\n//!\n//! \\sa MinidumpUTF8StringAtRVAAsString()\nstd::u16string MinidumpStringAtRVAAsString(const std::string& file_contents,\n                                           RVA rva);\n\n//! \\brief 64-bit specialization of MinidumpStringAtRVAAsString.\nstd::u16string MinidumpStringAtRVAAsString(const std::string& file_contents,\n                                           RVA64 rva);\n\n//! \\brief Returns the contents of a MinidumpUTF8String as a `std::string`.\n//!\n//! This function uses MinidumpUTF8StringAtRVA() to obtain a MinidumpUTF8String,\n//! and returns the string data as a `std::string`.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] rva The offset within the minidump file of the desired\n//!     MinidumpUTF8String.\n//!\n//! \\return On success, the string read from \\a file_writer at offset \\a rva. On\n//!     failure, raises a Google Test assertion and returns an empty string.\n//!\n//! \\sa MinidumpStringAtRVAAsString()\nstd::string MinidumpUTF8StringAtRVAAsString(const std::string& file_contents,\n                                            RVA rva);\n\n//! \\brief 64-bit specialization of MinidumpUTF8StringAtRVAAsString.\nstd::string MinidumpUTF8StringAtRVAAsString(const std::string& file_contents,\n                                            RVA64 rva);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_\n"
  },
  {
    "path": "minidump/test/minidump_user_extension_stream_util.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/test/minidump_user_extension_stream_util.h\"\n\n#include <string.h>\n\nnamespace crashpad {\nnamespace test {\n\nBufferExtensionStreamDataSource::BufferExtensionStreamDataSource(\n    uint32_t stream_type,\n    const void* data,\n    size_t data_size)\n    : MinidumpUserExtensionStreamDataSource(stream_type) {\n  data_.resize(data_size);\n\n  if (data_size)\n    memcpy(data_.data(), data, data_size);\n}\n\nsize_t BufferExtensionStreamDataSource::StreamDataSize() {\n  return data_.size();\n}\n\nbool BufferExtensionStreamDataSource::ReadStreamData(Delegate* delegate) {\n  return delegate->ExtensionStreamDataSourceRead(\n      data_.size() ? data_.data() : nullptr, data_.size());\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/test/minidump_user_extension_stream_util.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_\n#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_\n\n#include \"minidump/minidump_user_extension_stream_data_source.h\"\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <vector>\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A user extension data source that wraps a buffer.\nclass BufferExtensionStreamDataSource final\n    : public MinidumpUserExtensionStreamDataSource {\n public:\n  //! \\brief Creates a data source with \\a stream_type.\n  //!\n  //! param[in] stream_type The type of the stream.\n  //! param[in] data The data of the stream.\n  //! param[in] data_size The length of \\a data.\n  BufferExtensionStreamDataSource(uint32_t stream_type,\n                                  const void* data,\n                                  size_t data_size);\n\n  BufferExtensionStreamDataSource(const BufferExtensionStreamDataSource&) =\n      delete;\n  BufferExtensionStreamDataSource& operator=(\n      const BufferExtensionStreamDataSource&) = delete;\n\n  size_t StreamDataSize() override;\n  bool ReadStreamData(Delegate* delegate) override;\n\n private:\n  std::vector<uint8_t> data_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_\n"
  },
  {
    "path": "minidump/test/minidump_writable_test_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"minidump/test/minidump_writable_test_util.h\"\n\n#include <stddef.h>\n\n#include <string>\n\n#include \"gtest/gtest.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/numeric/in_range_cast.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\n//! \\brief Returns an untyped minidump object located within a minidump file’s\n//!     contents, where the offset of the object is known.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] rva The offset within the minidump file of the desired object.\n//!\n//! \\return If \\a rva is within the range of \\a file_contents, returns a pointer\n//!     into \\a file_contents at offset \\a rva. Otherwise, raises a Google Test\n//!     assertion failure and returns `nullptr`.\n//!\n//! Do not call this function. Use the typed version, MinidumpWritableAtRVA<>(),\n//! or another type-specific function.\ntemplate <typename RVAType>\nconst void* MinidumpWritableAtRVAInternal(const std::string& file_contents,\n                                          RVAType rva) {\n  const auto rva_offset = crashpad::InRangeCast(rva, file_contents.size());\n  if (rva_offset >= file_contents.size()) {\n    EXPECT_LT(rva_offset, file_contents.size());\n    return nullptr;\n  }\n\n  return &file_contents[rva_offset];\n}\n\ntemplate <typename RVAType, typename MinidumpLocationDescriptorType>\nconst void* TMinidumpWritableAtLocationDescriptorInternal(\n    const std::string& file_contents,\n    const MinidumpLocationDescriptorType& location,\n    size_t expected_size,\n    bool allow_oversized_data) {\n  if (location.DataSize == 0) {\n    EXPECT_EQ(location.Rva, RVAType(0));\n    return nullptr;\n  }\n\n  if (allow_oversized_data) {\n    if (location.DataSize < expected_size) {\n      EXPECT_GE(location.DataSize, expected_size);\n      return nullptr;\n    }\n  } else if (location.DataSize != expected_size) {\n    EXPECT_EQ(location.DataSize, expected_size);\n    return nullptr;\n  }\n\n  RVAType end = location.Rva + location.DataSize;\n  if (end > file_contents.size()) {\n    EXPECT_LE(end, file_contents.size());\n    return nullptr;\n  }\n\n  const void* rv =\n      MinidumpWritableAtRVAInternal<RVAType>(file_contents, location.Rva);\n\n  return rv;\n}\n\n}  // namespace\n\nconst void* MinidumpWritableAtLocationDescriptorInternal(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location,\n    size_t expected_size,\n    bool allow_oversized_data) {\n  return TMinidumpWritableAtLocationDescriptorInternal<\n      RVA,\n      MINIDUMP_LOCATION_DESCRIPTOR>(\n      file_contents, location, expected_size, allow_oversized_data);\n}\n\nconst void* MinidumpWritableAtLocationDescriptorInternal(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR64& location,\n    size_t expected_size,\n    bool allow_oversized_data) {\n  return TMinidumpWritableAtLocationDescriptorInternal<\n      RVA64,\n      MINIDUMP_LOCATION_DESCRIPTOR64>(\n      file_contents, location, expected_size, allow_oversized_data);\n}\n\ntemplate <>\nconst IMAGE_DEBUG_MISC* MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  const IMAGE_DEBUG_MISC* misc =\n      TMinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(file_contents,\n                                                              location);\n  if (!misc) {\n    return nullptr;\n  }\n\n  if (misc->DataType != IMAGE_DEBUG_MISC_EXENAME) {\n    EXPECT_EQ(misc->DataType,\n              implicit_cast<uint32_t>(IMAGE_DEBUG_MISC_EXENAME));\n    return nullptr;\n  }\n\n  if (misc->Length != location.DataSize) {\n    EXPECT_EQ(misc->Length, location.DataSize);\n    return nullptr;\n  }\n\n  if (misc->Unicode == 0) {\n    size_t string_length = misc->Length - offsetof(IMAGE_DEBUG_MISC, Data) - 1;\n    if (misc->Data[string_length] != '\\0') {\n      EXPECT_EQ(misc->Data[string_length], '\\0');\n      return nullptr;\n    }\n  } else if (misc->Unicode == 1) {\n    if (misc->Length % sizeof(char16_t) != 0) {\n      EXPECT_EQ(misc->Length % sizeof(char16_t), 0u);\n      return nullptr;\n    }\n\n    size_t string_length =\n        (misc->Length - offsetof(IMAGE_DEBUG_MISC, Data)) / sizeof(char16_t) -\n        1;\n    const char16_t* data16 = reinterpret_cast<const char16_t*>(misc->Data);\n    if (data16[string_length] != '\\0') {\n      EXPECT_EQ(data16[string_length], '\\0');\n      return nullptr;\n    }\n  } else {\n    ADD_FAILURE() << \"misc->Unicode \" << misc->Unicode;\n    return nullptr;\n  }\n\n  return misc;\n}\n\ntemplate <>\nconst MINIDUMP_HEADER* MinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  const MINIDUMP_HEADER* header =\n      TMinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>(file_contents,\n                                                             location);\n  if (!header) {\n    return nullptr;\n  }\n\n  if (header->Signature != MINIDUMP_SIGNATURE) {\n    EXPECT_EQ(header->Signature, implicit_cast<uint32_t>(MINIDUMP_SIGNATURE));\n    return nullptr;\n  }\n  if (header->Version != MINIDUMP_VERSION) {\n    EXPECT_EQ(header->Version, implicit_cast<uint32_t>(MINIDUMP_VERSION));\n    return nullptr;\n  }\n\n  return header;\n}\n\nnamespace {\n\nstruct MinidumpMemoryListTraits {\n  using ListType = MINIDUMP_MEMORY_LIST;\n  enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_DESCRIPTOR) };\n  static size_t ElementCount(const ListType* list) {\n    return list->NumberOfMemoryRanges;\n  }\n};\n\nstruct MinidumpModuleListTraits {\n  using ListType = MINIDUMP_MODULE_LIST;\n  enum : size_t { kElementSize = sizeof(MINIDUMP_MODULE) };\n  static size_t ElementCount(const ListType* list) {\n    return list->NumberOfModules;\n  }\n};\n\nstruct MinidumpUnloadedModuleListTraits {\n  using ListType = MINIDUMP_UNLOADED_MODULE_LIST;\n  enum : size_t { kElementSize = sizeof(MINIDUMP_UNLOADED_MODULE) };\n  static size_t ElementCount(const ListType* list) {\n    return list->NumberOfEntries;\n  }\n};\n\nstruct MinidumpThreadListTraits {\n  using ListType = MINIDUMP_THREAD_LIST;\n  enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD) };\n  static size_t ElementCount(const ListType* list) {\n    return list->NumberOfThreads;\n  }\n};\n\nstruct MinidumpThreadNameListTraits {\n  using ListType = MINIDUMP_THREAD_NAME_LIST;\n  enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD_NAME) };\n  static size_t ElementCount(const ListType* list) {\n    return list->NumberOfThreadNames;\n  }\n};\n\nstruct MinidumpHandleDataStreamTraits {\n  using ListType = MINIDUMP_HANDLE_DATA_STREAM;\n  enum : size_t { kElementSize = sizeof(MINIDUMP_HANDLE_DESCRIPTOR) };\n  static size_t ElementCount(const ListType* list) {\n    return static_cast<size_t>(list->NumberOfDescriptors);\n  }\n};\n\nstruct MinidumpMemoryInfoListTraits {\n  using ListType = MINIDUMP_MEMORY_INFO_LIST;\n  enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_INFO) };\n  static size_t ElementCount(const ListType* list) {\n    return static_cast<size_t>(list->NumberOfEntries);\n  }\n};\n\nstruct MinidumpModuleCrashpadInfoListTraits {\n  using ListType = MinidumpModuleCrashpadInfoList;\n  enum : size_t { kElementSize = sizeof(MinidumpModuleCrashpadInfoLink) };\n  static size_t ElementCount(const ListType* list) { return list->count; }\n};\n\nstruct MinidumpSimpleStringDictionaryListTraits {\n  using ListType = MinidumpSimpleStringDictionary;\n  enum : size_t { kElementSize = sizeof(MinidumpSimpleStringDictionaryEntry) };\n  static size_t ElementCount(const ListType* list) { return list->count; }\n};\n\nstruct MinidumpAnnotationListObjectsTraits {\n  using ListType = MinidumpAnnotationList;\n  enum : size_t { kElementSize = sizeof(MinidumpAnnotation) };\n  static size_t ElementCount(const ListType* list) { return list->count; }\n};\n\ntemplate <typename T>\nconst typename T::ListType* MinidumpListAtLocationDescriptor(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  const typename T::ListType* list =\n      TMinidumpWritableAtLocationDescriptor<typename T::ListType>(file_contents,\n                                                                  location);\n  if (!list) {\n    return nullptr;\n  }\n\n  size_t expected_size =\n      sizeof(typename T::ListType) + T::ElementCount(list) * T::kElementSize;\n  if (location.DataSize != expected_size) {\n    EXPECT_EQ(location.DataSize, expected_size);\n    return nullptr;\n  }\n\n  return list;\n}\n\n}  // namespace\n\ntemplate <>\nconst MINIDUMP_MEMORY_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpMemoryListTraits>(\n      file_contents, location);\n}\n\ntemplate <>\nconst MINIDUMP_MODULE_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpModuleListTraits>(\n      file_contents, location);\n}\n\ntemplate <>\nconst MINIDUMP_UNLOADED_MODULE_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_UNLOADED_MODULE_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpUnloadedModuleListTraits>(\n      file_contents, location);\n}\n\ntemplate <>\nconst MINIDUMP_THREAD_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpThreadListTraits>(\n      file_contents, location);\n}\n\ntemplate <>\nconst MINIDUMP_THREAD_NAME_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_NAME_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpThreadNameListTraits>(\n      file_contents, location);\n}\n\ntemplate <>\nconst MINIDUMP_HANDLE_DATA_STREAM*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpHandleDataStreamTraits>(\n      file_contents, location);\n}\n\ntemplate <>\nconst MINIDUMP_MEMORY_INFO_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpMemoryInfoListTraits>(\n      file_contents, location);\n}\n\ntemplate <>\nconst MinidumpModuleCrashpadInfoList*\nMinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpModuleCrashpadInfoListTraits>(\n      file_contents, location);\n}\n\ntemplate <>\nconst MinidumpSimpleStringDictionary*\nMinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<\n      MinidumpSimpleStringDictionaryListTraits>(file_contents, location);\n}\n\ntemplate <>\nconst MinidumpAnnotationList*\nMinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpListAtLocationDescriptor<MinidumpAnnotationListObjectsTraits>(\n      file_contents, location);\n}\n\nnamespace {\n\ntemplate <typename T>\nconst T* MinidumpCVPDBAtLocationDescriptor(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  const T* cv_pdb =\n      TMinidumpWritableAtLocationDescriptor<T>(file_contents, location);\n  if (!cv_pdb) {\n    return nullptr;\n  }\n\n  if (cv_pdb->signature != T::kSignature) {\n    EXPECT_EQ(cv_pdb->signature, T::kSignature);\n    return nullptr;\n  }\n\n  size_t string_length = location.DataSize - offsetof(T, pdb_name) - 1;\n  if (cv_pdb->pdb_name[string_length] != '\\0') {\n    EXPECT_EQ(cv_pdb->pdb_name[string_length], '\\0');\n    return nullptr;\n  }\n\n  return cv_pdb;\n}\n\n}  // namespace\n\ntemplate <>\nconst CodeViewRecordPDB20*\nMinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB20>(file_contents,\n                                                                location);\n}\n\ntemplate <>\nconst CodeViewRecordPDB70*\nMinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return MinidumpCVPDBAtLocationDescriptor<CodeViewRecordPDB70>(file_contents,\n                                                                location);\n}\n\ntemplate <>\nconst CodeViewRecordBuildID*\nMinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  const CodeViewRecordBuildID* cv =\n      reinterpret_cast<const CodeViewRecordBuildID*>(\n          MinidumpWritableAtLocationDescriptorInternal(\n              file_contents,\n              location,\n              offsetof(CodeViewRecordBuildID, build_id),\n              true));\n\n  if (!cv) {\n    return nullptr;\n  }\n\n  if (cv->signature != CodeViewRecordBuildID::kSignature) {\n    return nullptr;\n  }\n\n  return cv;\n}\n\nTestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value)\n    : MinidumpWritable(), value_(value) {}\n\nTestUInt32MinidumpWritable::~TestUInt32MinidumpWritable() {}\n\nsize_t TestUInt32MinidumpWritable::SizeOfObject() {\n  return sizeof(value_);\n}\n\nbool TestUInt32MinidumpWritable::WriteObject(FileWriterInterface* file_writer) {\n  return file_writer->Write(&value_, sizeof(value_));\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "minidump/test/minidump_writable_test_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_\n#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <string>\n\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"minidump/minidump_writable.h\"\n\nnamespace crashpad {\n\nclass FileWriterInterface;\n\nnamespace test {\n\n//! \\brief Returns an untyped minidump object located within a minidump file’s\n//!     contents, where the offset and size of the object are known.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within\n//!     the minidump file of the desired object, as well as its size.\n//! \\param[in] expected_size The expected size of the object. If \\a\n//!     allow_oversized_data is `true`, \\a expected_size is treated as the\n//!     minimum size of \\a location, but it is permitted to be larger. If \\a\n//!     allow_oversized_data is `false`, the size of \\a location must match\n//!     \\a expected_size exactly.\n//! \\param[in] allow_oversized_data Controls whether \\a expected_size is a\n//!     minimum limit (`true`) or an exact match is required (`false`).\n//!\n//! \\return If the size of \\a location is agrees with \\a expected_size, and if\n//!     \\a location is within the range of \\a file_contents, returns a pointer\n//!     into \\a file_contents at offset \\a rva. Otherwise, raises a Google Test\n//!     assertion failure and returns `nullptr`.\n//!\n//! Do not call this function. Use the typed version,\n//! MinidumpWritableAtLocationDescriptor<>(), or another type-specific function.\nconst void* MinidumpWritableAtLocationDescriptorInternal(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location,\n    size_t expected_size,\n    bool allow_oversized_data);\n\n//! \\brief 64-bit specialization of\n//! MinidumpWritableAtLocationDescriptorInternal.\n//!\n//! Do not call this function. Use the typed version,\n//! MinidumpWritableAtLocationDescriptor<>(), or another type-specific function.\nconst void* MinidumpWritableAtLocationDescriptorInternal(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR64& location,\n    size_t expected_size,\n    bool allow_oversized_data);\n\n//! \\brief A traits class defining whether a minidump object type is required to\n//!     appear only as a fixed-size object or if it is variable-sized.\n//!\n//! Variable-sized data is data referenced by a MINIDUMP_LOCATION_DESCRIPTOR\n//! whose DataSize field may be larger than the size of the basic object type’s\n//! structure. This can happen for types that appear only as variable-sized\n//! lists, or types whose final fields are variable-sized lists or other\n//! variable-sized data.\ntemplate <typename T>\nstruct MinidumpWritableTraits {\n  //! \\brief `true` if \\a T should be treated as a variable-sized data type,\n  //!     where its base size is used solely as a minimum bound. `false` if \\a\n  //!     T is a fixed-sized type, which should only appear at its base size.\n  static const bool kAllowOversizedData = false;\n};\n\n#define MINIDUMP_ALLOW_OVERSIZED_DATA(x)          \\\n  template <>                                     \\\n  struct MinidumpWritableTraits<x> {              \\\n    static const bool kAllowOversizedData = true; \\\n  }\n\n// This type appears only as a variable-sized list.\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY);\n\n// These types are permitted to be oversized because their final fields are\n// variable-sized lists.\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_UNLOADED_MODULE_LIST);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_NAME_LIST);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_HANDLE_DATA_STREAM);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpAnnotationList);\n\n// These types have final fields carrying variable-sized data (typically string\n// data).\nMINIDUMP_ALLOW_OVERSIZED_DATA(IMAGE_DEBUG_MISC);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_STRING);\nMINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB20);\nMINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB70);\nMINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordBuildID);\nMINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpUTF8String);\n\n// minidump_file_writer_test accesses its variable-sized test streams via a\n// uint8_t*.\nMINIDUMP_ALLOW_OVERSIZED_DATA(uint8_t);\n\n#undef MINIDUMP_ALLOW_OVERSIZED_DATA\n\n//! \\brief Returns a typed minidump object located within a minidump file’s\n//!     contents, where the offset and size of the object are known.\n//!\n//! This function is similar to MinidumpWritableAtLocationDescriptor<>() and is\n//! used to implement that function. It exists independently so that template\n//! specializations are able to call this function, which provides the default\n//! implementation.\n//!\n//! Do not call this function directly. Use\n//! MinidumpWritableAtLocationDescriptor<>() instead.\ntemplate <typename T>\nconst T* TMinidumpWritableAtLocationDescriptor(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return reinterpret_cast<const T*>(\n      MinidumpWritableAtLocationDescriptorInternal(\n          file_contents,\n          location,\n          sizeof(T),\n          MinidumpWritableTraits<T>::kAllowOversizedData));\n}\n\n//! \\brief 64-bit specialization of TMinidumpWritableAtLocationDescriptor.\n//!\n//! Do not call this function directly. Use\n//! MinidumpWritableAtLocationDescriptor<>() instead.\ntemplate <typename T>\nconst T* TMinidumpWritableAtLocationDescriptor(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR64& location) {\n  return reinterpret_cast<const T*>(\n      MinidumpWritableAtLocationDescriptorInternal(\n          file_contents,\n          location,\n          sizeof(T),\n          MinidumpWritableTraits<T>::kAllowOversizedData));\n}\n\n//! \\brief Returns a typed minidump object located within a minidump file’s\n//!     contents, where the offset and size of the object are known.\n//!\n//! This function has template specializations that perform more stringent\n//! checking than the default implementation:\n//!  - With a MINIDUMP_HEADER template parameter, a template specialization\n//!    ensures that the structure’s magic number and version fields are correct.\n//!  - With a MINIDUMP_MEMORY_LIST, MINIDUMP_THREAD_LIST,\n//!    MINIDUMP_THREAD_NAME_LIST, MINIDUMP_MODULE_LIST,\n//!    MINIDUMP_MEMORY_INFO_LIST, MinidumpSimpleStringDictionary, or\n//!    MinidumpAnnotationList template parameter, template specializations\n//!    ensure that the size given by \\a location matches the size expected of a\n//!    stream containing the number of elements it claims to have.\n//!  - With an IMAGE_DEBUG_MISC, CodeViewRecordPDB20, or CodeViewRecordPDB70\n//!    template parameter, template specializations ensure that the structure\n//!    has the expected format including any magic number and the `NUL`-\n//!    terminated string.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within\n//!     the minidump file of the desired object, as well as its size.\n//!\n//! \\return If the size of \\a location is at least as big as the size of the\n//!     requested object, and if \\a location is within the range of \\a\n//!     file_contents, returns a pointer into \\a file_contents at offset \\a rva.\n//!     Otherwise, raises a Google Test assertion failure and returns `nullptr`.\n//!\n//! \\sa MinidumpWritableAtRVA()\ntemplate <typename T>\nconst T* MinidumpWritableAtLocationDescriptor(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location) {\n  return TMinidumpWritableAtLocationDescriptor<T>(file_contents, location);\n}\n\n//! \\brief 64-bit specialization of MinidumpWritableAtLocationDescriptor.\ntemplate <typename T>\nconst T* MinidumpWritableAtLocationDescriptor(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR64& location) {\n  return TMinidumpWritableAtLocationDescriptor<T>(file_contents, location);\n}\n\ntemplate <>\nconst IMAGE_DEBUG_MISC* MinidumpWritableAtLocationDescriptor<IMAGE_DEBUG_MISC>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MINIDUMP_HEADER* MinidumpWritableAtLocationDescriptor<MINIDUMP_HEADER>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MINIDUMP_MEMORY_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MINIDUMP_MODULE_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MINIDUMP_UNLOADED_MODULE_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_UNLOADED_MODULE_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MINIDUMP_THREAD_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MINIDUMP_THREAD_NAME_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_NAME_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MINIDUMP_HANDLE_DATA_STREAM*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MINIDUMP_MEMORY_INFO_LIST*\nMinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst CodeViewRecordPDB20*\nMinidumpWritableAtLocationDescriptor<CodeViewRecordPDB20>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst CodeViewRecordPDB70*\nMinidumpWritableAtLocationDescriptor<CodeViewRecordPDB70>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst CodeViewRecordBuildID*\nMinidumpWritableAtLocationDescriptor<CodeViewRecordBuildID>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MinidumpModuleCrashpadInfoList*\nMinidumpWritableAtLocationDescriptor<MinidumpModuleCrashpadInfoList>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MinidumpSimpleStringDictionary*\nMinidumpWritableAtLocationDescriptor<MinidumpSimpleStringDictionary>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\ntemplate <>\nconst MinidumpAnnotationList*\nMinidumpWritableAtLocationDescriptor<MinidumpAnnotationList>(\n    const std::string& file_contents,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location);\n\n//! \\brief Returns a typed minidump object located within a minidump file’s\n//!     contents, where the offset of the object is known.\n//!\n//! \\param[in] file_contents The contents of the minidump file.\n//! \\param[in] rva The offset within the minidump file of the desired object.\n//!\n//! \\return If \\a rva plus the size of an object of type \\a T is within the\n//!     range of \\a file_contents, returns a pointer into \\a file_contents at\n//!     offset \\a rva. Otherwise, raises a Google Test assertion failure and\n//!     returns `nullptr`.\n//!\n//! \\sa MinidumpWritableAtLocationDescriptor<>()\ntemplate <typename T>\nconst T* MinidumpWritableAtRVA(const std::string& file_contents, RVA rva) {\n  MINIDUMP_LOCATION_DESCRIPTOR location;\n  location.DataSize = sizeof(T);\n  location.Rva = rva;\n  return MinidumpWritableAtLocationDescriptor<T>(file_contents, location);\n}\n\n//! \\brief 64-bit specialization of MinidumpWritableAtRVA.\ntemplate <typename T>\nconst T* MinidumpWritableAtRVA(const std::string& file_contents, RVA64 rva) {\n  MINIDUMP_LOCATION_DESCRIPTOR64 location;\n  location.DataSize = sizeof(T);\n  location.Rva = rva;\n  return MinidumpWritableAtLocationDescriptor<T>(file_contents, location);\n}\n\n//! \\brief An internal::MinidumpWritable that carries a `uint32_t` for testing.\nclass TestUInt32MinidumpWritable final : public internal::MinidumpWritable {\n public:\n  //! \\brief Constructs the object to write a `uint32_t` with value \\a value.\n  explicit TestUInt32MinidumpWritable(uint32_t value);\n\n  TestUInt32MinidumpWritable(const TestUInt32MinidumpWritable&) = delete;\n  TestUInt32MinidumpWritable& operator=(const TestUInt32MinidumpWritable&) =\n      delete;\n\n  ~TestUInt32MinidumpWritable() override;\n\n protected:\n  // MinidumpWritable:\n  size_t SizeOfObject() override;\n  bool WriteObject(FileWriterInterface* file_writer) override;\n\n private:\n  uint32_t value_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_\n"
  },
  {
    "path": "navbar.md",
    "content": "<!--\nCopyright 2016 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# Crashpad\n\n * [Home][home]\n * [Developing](/doc/developing.md)\n * [Interface Docs](https://crashpad.chromium.org/doxygen/)\n * [Man Pages](/doc/man.md)\n * [Source Code](https://chromium.googlesource.com/crashpad/crashpad/)\n\n[home]: /README.md\n"
  },
  {
    "path": "package.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_PACKAGE_H_\n#define CRASHPAD_PACKAGE_H_\n\n#define PACKAGE_BUGREPORT \"https://crashpad.chromium.org/bug/new\"\n#define PACKAGE_COPYRIGHT \\\n  \"Copyright \" PACKAGE_COPYRIGHT_YEAR \" \" PACKAGE_COPYRIGHT_OWNER\n#define PACKAGE_COPYRIGHT_OWNER \"The Crashpad Authors\"\n#define PACKAGE_COPYRIGHT_YEAR \"2018\"\n#define PACKAGE_NAME \"Crashpad\"\n#define PACKAGE_STRING PACKAGE_NAME \" \" PACKAGE_VERSION\n#define PACKAGE_TARNAME \"crashpad\"\n#define PACKAGE_VERSION \"0.8.0\"\n#define PACKAGE_URL \"https://crashpad.chromium.org/\"\n\n#endif  // CRASHPAD_PACKAGE_H_\n"
  },
  {
    "path": "snapshot/BUILD.gn",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../build/crashpad_buildconfig.gni\")\nimport(\"../build/crashpad_fuzzer_test.gni\")\n\nif (crashpad_is_in_chromium) {\n  import(\"//build/config/compiler/compiler.gni\")\n}\n\ncrashpad_static_library(\"snapshot\") {\n  sources = [\n    \"annotation_snapshot.cc\",\n    \"annotation_snapshot.h\",\n    \"capture_memory.cc\",\n    \"capture_memory.h\",\n    \"crashpad_info_client_options.cc\",\n    \"crashpad_info_client_options.h\",\n    \"exception_snapshot.h\",\n    \"handle_snapshot.cc\",\n    \"handle_snapshot.h\",\n    \"memory_snapshot.cc\",\n    \"memory_snapshot.h\",\n    \"memory_snapshot_generic.h\",\n    \"minidump/exception_snapshot_minidump.cc\",\n    \"minidump/exception_snapshot_minidump.h\",\n    \"minidump/memory_snapshot_minidump.cc\",\n    \"minidump/memory_snapshot_minidump.h\",\n    \"minidump/minidump_annotation_reader.cc\",\n    \"minidump/minidump_annotation_reader.h\",\n    \"minidump/minidump_context_converter.cc\",\n    \"minidump/minidump_context_converter.h\",\n    \"minidump/minidump_simple_string_dictionary_reader.cc\",\n    \"minidump/minidump_simple_string_dictionary_reader.h\",\n    \"minidump/minidump_stream.h\",\n    \"minidump/minidump_string_list_reader.cc\",\n    \"minidump/minidump_string_list_reader.h\",\n    \"minidump/minidump_string_reader.cc\",\n    \"minidump/minidump_string_reader.h\",\n    \"minidump/module_snapshot_minidump.cc\",\n    \"minidump/module_snapshot_minidump.h\",\n    \"minidump/process_snapshot_minidump.cc\",\n    \"minidump/process_snapshot_minidump.h\",\n    \"minidump/system_snapshot_minidump.cc\",\n    \"minidump/system_snapshot_minidump.h\",\n    \"minidump/thread_snapshot_minidump.cc\",\n    \"minidump/thread_snapshot_minidump.h\",\n    \"module_snapshot.h\",\n    \"process_snapshot.h\",\n    \"snapshot_constants.h\",\n    \"system_snapshot.h\",\n    \"thread_snapshot.h\",\n    \"unloaded_module_snapshot.cc\",\n    \"unloaded_module_snapshot.h\",\n  ]\n\n  if (crashpad_is_posix || crashpad_is_fuchsia) {\n    sources += [\n      \"posix/timezone.cc\",\n      \"posix/timezone.h\",\n    ]\n  }\n\n  if (crashpad_is_mac) {\n    sources += [\n      \"mac/cpu_context_mac.cc\",\n      \"mac/cpu_context_mac.h\",\n      \"mac/exception_snapshot_mac.cc\",\n      \"mac/exception_snapshot_mac.h\",\n      \"mac/mach_o_image_annotations_reader.cc\",\n      \"mac/mach_o_image_annotations_reader.h\",\n      \"mac/mach_o_image_reader.cc\",\n      \"mac/mach_o_image_reader.h\",\n      \"mac/mach_o_image_segment_reader.cc\",\n      \"mac/mach_o_image_segment_reader.h\",\n      \"mac/mach_o_image_symbol_table_reader.cc\",\n      \"mac/mach_o_image_symbol_table_reader.h\",\n      \"mac/module_snapshot_mac.cc\",\n      \"mac/module_snapshot_mac.h\",\n      \"mac/process_reader_mac.cc\",\n      \"mac/process_reader_mac.h\",\n      \"mac/process_snapshot_mac.cc\",\n      \"mac/process_snapshot_mac.h\",\n      \"mac/process_types.cc\",\n      \"mac/process_types.h\",\n      \"mac/process_types/custom.cc\",\n      \"mac/process_types/flavors.h\",\n      \"mac/process_types/internal.h\",\n      \"mac/process_types/traits.h\",\n      \"mac/system_snapshot_mac.cc\",\n      \"mac/system_snapshot_mac.h\",\n      \"mac/thread_snapshot_mac.cc\",\n      \"mac/thread_snapshot_mac.h\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    sources += [\n      \"ios/exception_snapshot_ios_intermediate_dump.cc\",\n      \"ios/exception_snapshot_ios_intermediate_dump.h\",\n      \"ios/intermediate_dump_reader_util.cc\",\n      \"ios/intermediate_dump_reader_util.h\",\n      \"ios/memory_snapshot_ios_intermediate_dump.cc\",\n      \"ios/memory_snapshot_ios_intermediate_dump.h\",\n      \"ios/module_snapshot_ios_intermediate_dump.cc\",\n      \"ios/module_snapshot_ios_intermediate_dump.h\",\n      \"ios/process_snapshot_ios_intermediate_dump.cc\",\n      \"ios/process_snapshot_ios_intermediate_dump.h\",\n      \"ios/system_snapshot_ios_intermediate_dump.cc\",\n      \"ios/system_snapshot_ios_intermediate_dump.h\",\n      \"ios/thread_snapshot_ios_intermediate_dump.cc\",\n      \"ios/thread_snapshot_ios_intermediate_dump.h\",\n      \"mac/cpu_context_mac.cc\",\n      \"mac/cpu_context_mac.h\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    sources += [\n      \"linux/capture_memory_delegate_linux.cc\",\n      \"linux/capture_memory_delegate_linux.h\",\n      \"linux/cpu_context_linux.cc\",\n      \"linux/cpu_context_linux.h\",\n      \"linux/debug_rendezvous.cc\",\n      \"linux/debug_rendezvous.h\",\n      \"linux/exception_snapshot_linux.cc\",\n      \"linux/exception_snapshot_linux.h\",\n      \"linux/process_reader_linux.cc\",\n      \"linux/process_reader_linux.h\",\n      \"linux/process_snapshot_linux.cc\",\n      \"linux/process_snapshot_linux.h\",\n      \"linux/signal_context.h\",\n      \"linux/system_snapshot_linux.cc\",\n      \"linux/system_snapshot_linux.h\",\n      \"linux/thread_snapshot_linux.cc\",\n      \"linux/thread_snapshot_linux.h\",\n      \"sanitized/memory_snapshot_sanitized.cc\",\n      \"sanitized/memory_snapshot_sanitized.h\",\n      \"sanitized/module_snapshot_sanitized.cc\",\n      \"sanitized/module_snapshot_sanitized.h\",\n      \"sanitized/process_snapshot_sanitized.cc\",\n      \"sanitized/process_snapshot_sanitized.h\",\n      \"sanitized/sanitization_information.cc\",\n      \"sanitized/sanitization_information.h\",\n      \"sanitized/thread_snapshot_sanitized.cc\",\n      \"sanitized/thread_snapshot_sanitized.h\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia ||\n      crashpad_is_win) {\n    sources += [\n      \"crashpad_types/crashpad_info_reader.cc\",\n      \"crashpad_types/crashpad_info_reader.h\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {\n    sources += [\n      \"crashpad_types/image_annotation_reader.cc\",\n      \"crashpad_types/image_annotation_reader.h\",\n      \"elf/elf_dynamic_array_reader.cc\",\n      \"elf/elf_dynamic_array_reader.h\",\n      \"elf/elf_image_reader.cc\",\n      \"elf/elf_image_reader.h\",\n      \"elf/elf_symbol_table_reader.cc\",\n      \"elf/elf_symbol_table_reader.h\",\n      \"elf/module_snapshot_elf.cc\",\n      \"elf/module_snapshot_elf.h\",\n    ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [\n      \"win/capture_memory_delegate_win.cc\",\n      \"win/capture_memory_delegate_win.h\",\n      \"win/cpu_context_win.cc\",\n      \"win/cpu_context_win.h\",\n      \"win/exception_snapshot_win.cc\",\n      \"win/exception_snapshot_win.h\",\n      \"win/memory_map_region_snapshot_win.cc\",\n      \"win/memory_map_region_snapshot_win.h\",\n      \"win/module_snapshot_win.cc\",\n      \"win/module_snapshot_win.h\",\n      \"win/pe_image_annotations_reader.cc\",\n      \"win/pe_image_annotations_reader.h\",\n      \"win/pe_image_reader.cc\",\n      \"win/pe_image_reader.h\",\n      \"win/pe_image_resource_reader.cc\",\n      \"win/pe_image_resource_reader.h\",\n      \"win/process_reader_win.cc\",\n      \"win/process_reader_win.h\",\n      \"win/process_snapshot_win.cc\",\n      \"win/process_snapshot_win.h\",\n      \"win/process_subrange_reader.cc\",\n      \"win/process_subrange_reader.h\",\n      \"win/system_snapshot_win.cc\",\n      \"win/system_snapshot_win.h\",\n      \"win/thread_snapshot_win.cc\",\n      \"win/thread_snapshot_win.h\",\n    ]\n  }\n\n  if (crashpad_is_fuchsia) {\n    sources += [\n      \"fuchsia/cpu_context_fuchsia.cc\",\n      \"fuchsia/cpu_context_fuchsia.h\",\n      \"fuchsia/exception_snapshot_fuchsia.cc\",\n      \"fuchsia/exception_snapshot_fuchsia.h\",\n      \"fuchsia/memory_map_fuchsia.cc\",\n      \"fuchsia/memory_map_fuchsia.h\",\n      \"fuchsia/memory_map_region_snapshot_fuchsia.cc\",\n      \"fuchsia/memory_map_region_snapshot_fuchsia.h\",\n      \"fuchsia/process_reader_fuchsia.cc\",\n      \"fuchsia/process_reader_fuchsia.h\",\n      \"fuchsia/process_snapshot_fuchsia.cc\",\n      \"fuchsia/process_snapshot_fuchsia.h\",\n      \"fuchsia/system_snapshot_fuchsia.cc\",\n      \"fuchsia/system_snapshot_fuchsia.h\",\n      \"fuchsia/thread_snapshot_fuchsia.cc\",\n      \"fuchsia/thread_snapshot_fuchsia.h\",\n    ]\n  }\n\n  if (current_cpu == \"x86\" || current_cpu == \"x64\" ||\n      (crashpad_is_mac && current_cpu == \"mac_universal\")) {\n    sources += [\n      \"x86/cpuid_reader.cc\",\n      \"x86/cpuid_reader.h\",\n    ]\n  }\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  public_deps = [ \":context\" ]\n\n  deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../client:common\",\n    \"../compat\",\n    \"../minidump:format\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_win) {\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n    libs = [ \"powrprof.lib\" ]\n  }\n}\n\n# :context is the only part of snapshot that minidump may depend on.\nstatic_library(\"context\") {\n  sources = [\n    \"cpu_architecture.h\",\n    \"cpu_context.cc\",\n    \"cpu_context.h\",\n  ]\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_win) {\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n  }\n}\n\nif (crashpad_is_linux) {\n  crashpad_fuzzer_test(\"elf_image_reader_fuzzer\") {\n    sources = [ \"elf/elf_image_reader_fuzzer.cc\" ]\n\n    deps = [\n      \":snapshot\",\n      \"$mini_chromium_source_parent:base\",\n      \"../util\",\n    ]\n    seed_corpus = \"elf/elf_image_reader_fuzzer_corpus\"\n  }\n}\n\nstatic_library(\"test_support\") {\n  testonly = true\n\n  sources = [\n    \"test/test_cpu_context.cc\",\n    \"test/test_cpu_context.h\",\n    \"test/test_exception_snapshot.cc\",\n    \"test/test_exception_snapshot.h\",\n    \"test/test_memory_map_region_snapshot.cc\",\n    \"test/test_memory_map_region_snapshot.h\",\n    \"test/test_memory_snapshot.cc\",\n    \"test/test_memory_snapshot.h\",\n    \"test/test_module_snapshot.cc\",\n    \"test/test_module_snapshot.h\",\n    \"test/test_process_snapshot.cc\",\n    \"test/test_process_snapshot.h\",\n    \"test/test_system_snapshot.cc\",\n    \"test/test_system_snapshot.h\",\n    \"test/test_thread_snapshot.cc\",\n    \"test/test_thread_snapshot.h\",\n  ]\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  public_deps = [ \":snapshot\" ]\n\n  deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../compat\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_win) {\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n  }\n}\n\nconfig(\"snapshot_test_link\") {\n  visibility = [ \":*\" ]\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {\n    # There’s no way to make the link depend on this file. “inputs” doesn’t have\n    # the intended effect in a config. https://crbug.com/781858,\n    # https://crbug.com/796187.\n    inputs = [ \"elf/test_exported_symbols.sym\" ]\n    ldflags = [ \"-Wl,--dynamic-list,\" + rebase_path(inputs[0], root_build_dir) ]\n  }\n}\n\nsource_set(\"snapshot_test\") {\n  testonly = true\n\n  sources = [\n    \"cpu_context_test.cc\",\n    \"memory_snapshot_test.cc\",\n    \"minidump/process_snapshot_minidump_test.cc\",\n  ]\n\n  if (crashpad_is_mac) {\n    sources += [\n      \"mac/cpu_context_mac_test.cc\",\n      \"mac/mach_o_image_annotations_reader_test.cc\",\n      \"mac/mach_o_image_reader_test.cc\",\n      \"mac/mach_o_image_segment_reader_test.cc\",\n      \"mac/process_reader_mac_test.cc\",\n      \"mac/process_types_test.cc\",\n      \"mac/system_snapshot_mac_test.cc\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    sources += [\n      \"ios/memory_snapshot_ios_intermediate_dump_test.cc\",\n      \"ios/process_snapshot_ios_intermediate_dump_test.cc\",\n      \"mac/cpu_context_mac_test.cc\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    sources += [\n      \"linux/debug_rendezvous_test.cc\",\n      \"linux/exception_snapshot_linux_test.cc\",\n      \"linux/process_reader_linux_test.cc\",\n      \"linux/system_snapshot_linux_test.cc\",\n      \"linux/test_modules.cc\",\n      \"linux/test_modules.h\",\n      \"sanitized/process_snapshot_sanitized_test.cc\",\n      \"sanitized/sanitization_information_test.cc\",\n    ]\n  } else if (!crashpad_is_ios) {\n    sources += [ \"crashpad_info_client_options_test.cc\" ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia ||\n      crashpad_is_win) {\n    sources += [ \"crashpad_types/crashpad_info_reader_test.cc\" ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {\n    sources += [\n      \"crashpad_types/image_annotation_reader_test.cc\",\n      \"elf/elf_image_reader_test.cc\",\n      \"elf/elf_image_reader_test_note.S\",\n    ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [\n      \"win/cpu_context_win_test.cc\",\n      \"win/exception_snapshot_win_test.cc\",\n      \"win/extra_memory_ranges_test.cc\",\n      \"win/module_snapshot_win_test.cc\",\n      \"win/pe_image_reader_test.cc\",\n      \"win/process_reader_win_test.cc\",\n      \"win/process_snapshot_win_test.cc\",\n      \"win/system_snapshot_win_test.cc\",\n    ]\n  } else if (!crashpad_is_fuchsia) {\n    # Timezones are currently non-functional on Fuchsia:\n    # https://fuchsia.googlesource.com/zircon/+/master/third_party/ulib/musl/src/time/__tz.c#9\n    # https://crashpad.chromium.org/bug/196. Relevant upstream bugs are ZX-337\n    # and ZX-1731.\n    sources += [ \"posix/timezone_test.cc\" ]\n  }\n\n  if (crashpad_is_fuchsia) {\n    sources += [\n      \"fuchsia/process_reader_fuchsia_test.cc\",\n      \"fuchsia/process_snapshot_fuchsia_test.cc\",\n    ]\n  }\n\n  # public_configs isn’t quite right. snapshot_test_link sets ldflags, and\n  # what’s really needed is a way to push ldflags to dependent targets that\n  # produce linker output. Luckily in this case, all dependents do produce\n  # linker output. https://crbug.com/796183.\n  public_configs = [ \":snapshot_test_link\" ]\n\n  deps = [\n    \":test_support\",\n    \"$mini_chromium_source_parent:base\",\n    \"../client:common\",\n    \"../compat\",\n    \"../minidump:format\",\n    \"../test\",\n    \"../third_party/googletest\",\n    \"../third_party/googletest:googlemock\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_ios) {\n    deps += [\n      \":snapshot_test_ios_data\",\n      \"../minidump\",\n    ]\n  }\n\n  data_deps = [\n    \":crashpad_snapshot_test_module\",\n    \":crashpad_snapshot_test_module_large\",\n    \":crashpad_snapshot_test_module_small\",\n  ]\n\n  if (crashpad_is_mac) {\n    frameworks = [ \"OpenCL.framework\" ]\n\n    data_deps += [\n      \":crashpad_snapshot_test_module_crashy_initializer\",\n      \":crashpad_snapshot_test_no_op\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    libs = [ \"dl\" ]\n  }\n\n  if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) &&\n      target_cpu != \"mipsel\" && target_cpu != \"mips64el\") {\n    data_deps += [ \":crashpad_snapshot_test_both_dt_hash_styles\" ]\n  }\n\n  if (crashpad_is_win) {\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n\n    data_deps += [\n      \":crashpad_snapshot_test_annotations\",\n      \":crashpad_snapshot_test_crashing_child\",\n      \":crashpad_snapshot_test_dump_without_crashing\",\n      \":crashpad_snapshot_test_extra_memory_ranges\",\n      \":crashpad_snapshot_test_image_reader\",\n      \":crashpad_snapshot_test_image_reader_module\",\n    ]\n  }\n}\n\nbundle_data(\"snapshot_test_ios_data\") {\n  testonly = true\n\n  sources = [\n    \"ios/testdata/crash-1fa088dda0adb41459d063078a0f384a0bb8eefa\",\n    \"ios/testdata/crash-5726011582644224\",\n    \"ios/testdata/crash-6605504629637120\",\n    \"ios/testdata/crash-c44acfcbccd8c7a8\",\n  ]\n\n  outputs = [ \"{{bundle_resources_dir}}/crashpad_test_data/\" +\n              \"{{source_root_relative_dir}}/{{source_file_part}}\" ]\n}\ncrashpad_loadable_module(\"crashpad_snapshot_test_module\") {\n  testonly = true\n  sources = [ \"crashpad_info_client_options_test_module.cc\" ]\n  deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../client\",\n  ]\n  if (crashpad_is_in_fuchsia) {\n    # TODO(fxbug.dev/42059784): Remove this once the underlying issue is\n    # addressed.\n    exclude_toolchain_tags = [ \"hwasan\" ]\n  }\n}\n\ncrashpad_loadable_module(\"crashpad_snapshot_test_module_large\") {\n  testonly = true\n  sources = [ \"crashpad_info_size_test_module.cc\" ]\n\n  deps = []\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {\n    sources += [ \"crashpad_info_size_test_note.S\" ]\n    deps += [ \"../util\" ]\n  }\n\n  defines = [ \"CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE\" ]\n  deps += [ \"$mini_chromium_source_parent:base\" ]\n\n  if (crashpad_is_in_fuchsia) {\n    # TODO(fxbug.dev/42059784): Remove this once the underlying issue is\n    # addressed.\n    exclude_toolchain_tags = [ \"hwasan\" ]\n  }\n}\n\ncrashpad_loadable_module(\"crashpad_snapshot_test_module_small\") {\n  testonly = true\n  sources = [ \"crashpad_info_size_test_module.cc\" ]\n\n  deps = []\n  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {\n    sources += [ \"crashpad_info_size_test_note.S\" ]\n    deps += [ \"../util\" ]\n  }\n\n  defines = [ \"CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL\" ]\n  deps += [ \"$mini_chromium_source_parent:base\" ]\n\n  if (crashpad_is_in_fuchsia) {\n    # TODO(fxbug.dev/42059784): Remove this once the underlying issue is\n    # addressed.\n    exclude_toolchain_tags = [ \"hwasan\" ]\n  }\n}\n\nif ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) &&\n    target_cpu != \"mipsel\" && target_cpu != \"mips64el\") {\n  crashpad_loadable_module(\"crashpad_snapshot_test_both_dt_hash_styles\") {\n    testonly = true\n    sources = [ \"hash_types_test.cc\" ]\n\n    # This makes `ld` emit both .hash and .gnu.hash sections.\n    ldflags = [ \"-Wl,--hash-style=both\" ]\n\n    if (crashpad_is_in_fuchsia) {\n      # TODO(fxbug.dev/42059784): Remove this once the underlying issue is\n      # addressed.\n      exclude_toolchain_tags = [ \"hwasan\" ]\n    }\n  }\n}\n\nif (crashpad_is_apple) {\n  crashpad_loadable_module(\"crashpad_snapshot_test_module_crashy_initializer\") {\n    testonly = true\n    sources = [\n      \"mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc\",\n    ]\n  }\n\n  crashpad_executable(\"crashpad_snapshot_test_no_op\") {\n    testonly = true\n    sources = [ \"mac/mach_o_image_annotations_reader_test_no_op.cc\" ]\n  }\n}\n\nif (crashpad_is_win) {\n  crashpad_executable(\"crashpad_snapshot_test_annotations\") {\n    testonly = true\n    sources = [ \"win/crashpad_snapshot_test_annotations.cc\" ]\n    deps = [\n      \"$mini_chromium_source_parent:base\",\n      \"../client\",\n      \"../compat\",\n    ]\n  }\n\n  crashpad_executable(\"crashpad_snapshot_test_crashing_child\") {\n    testonly = true\n    sources = [ \"win/crashpad_snapshot_test_crashing_child.cc\" ]\n    deps = [\n      \"$mini_chromium_source_parent:base\",\n      \"../client\",\n      \"../compat\",\n      \"../util\",\n    ]\n  }\n\n  crashpad_executable(\"crashpad_snapshot_test_dump_without_crashing\") {\n    testonly = true\n    sources = [ \"win/crashpad_snapshot_test_dump_without_crashing.cc\" ]\n    deps = [\n      \"$mini_chromium_source_parent:base\",\n      \"../client\",\n      \"../compat\",\n      \"../util\",\n    ]\n  }\n\n  crashpad_executable(\"crashpad_snapshot_test_extra_memory_ranges\") {\n    testonly = true\n    sources = [ \"win/crashpad_snapshot_test_extra_memory_ranges.cc\" ]\n    deps = [\n      \"$mini_chromium_source_parent:base\",\n      \"../client\",\n      \"../compat\",\n    ]\n  }\n\n  crashpad_executable(\"crashpad_snapshot_test_image_reader\") {\n    testonly = true\n    sources = [ \"win/crashpad_snapshot_test_image_reader.cc\" ]\n    deps = [\n      \"$mini_chromium_source_parent:base\",\n      \"../client\",\n      \"../compat\",\n      \"../util\",\n    ]\n    if (crashpad_is_in_chromium) {\n      if (symbol_level == 0) {\n        # The tests that use this executable rely on at least minimal debug\n        # info.\n        remove_configs = [ \"//build/config/compiler:default_symbols\" ]\n        configs = [ \"//build/config/compiler:minimal_symbols\" ]\n      }\n    }\n  }\n\n  crashpad_loadable_module(\"crashpad_snapshot_test_image_reader_module\") {\n    testonly = true\n    sources = [ \"win/crashpad_snapshot_test_image_reader_module.cc\" ]\n    deps = [\n      \"$mini_chromium_source_parent:base\",\n      \"../client\",\n    ]\n    if (crashpad_is_in_chromium) {\n      if (symbol_level == 0) {\n        # The tests that use this module rely on at least minimal debug info.\n        remove_configs = [ \"//build/config/compiler:default_symbols\" ]\n        configs = [ \"//build/config/compiler:minimal_symbols\" ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "snapshot/annotation_snapshot.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/annotation_snapshot.h\"\n\nnamespace crashpad {\n\nAnnotationSnapshot::AnnotationSnapshot() : name(), type(0), value() {}\n\nAnnotationSnapshot::AnnotationSnapshot(const std::string& name,\n                                       uint16_t type,\n                                       const std::vector<uint8_t>& value)\n    : name(name), type(type), value(value) {}\n\nAnnotationSnapshot::~AnnotationSnapshot() = default;\n\nbool AnnotationSnapshot::operator==(const AnnotationSnapshot& other) const {\n  return name == other.name && type == other.type && value == other.value;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/annotation_snapshot.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_ANNOTATION_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_ANNOTATION_SNAPSHOT_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <vector>\n\nnamespace crashpad {\n\n// \\!brief The snapshot representation of a client's Annotation.\nstruct AnnotationSnapshot {\n  AnnotationSnapshot();\n  AnnotationSnapshot(const std::string& name,\n                     uint16_t type,\n                     const std::vector<uint8_t>& value);\n  ~AnnotationSnapshot();\n\n  bool operator==(const AnnotationSnapshot& other) const;\n  bool operator!=(const AnnotationSnapshot& other) const {\n    return !(*this == other);\n  }\n\n  //! \\brief A non-unique name by which this annotation can be identified.\n  std::string name;\n\n  //! \\brief The Annotation::Type of data stored in the annotation. This value\n  //!     may be client-supplied and need not correspond to a Crashpad-defined\n  //!     type.\n  uint16_t type;\n\n  //! \\brief The data for the annotation. Guranteed to be non-empty, since\n  //!     empty annotations are skipped. The representation of the data should\n  //!     be interpreted as \\a #type.\n  std::vector<uint8_t> value;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_ANNOTATION_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/capture_memory.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/capture_memory.h\"\n\n#include <stdint.h>\n#include <windows.h>\n\n// dbghelp must be after windows.h.\n#include <dbghelp.h>\n\n#include <iterator>\n#include <limits>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"snapshot/memory_snapshot.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\nvoid MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate,\n                              uint64_t address) {\n  constexpr uint64_t non_address_offset = 0x10000;\n  if (address < non_address_offset)\n    return;\n\n  const uint64_t max_address = delegate->Is64Bit() ?\n      std::numeric_limits<uint64_t>::max() :\n      std::numeric_limits<uint32_t>::max();\n  if (address > max_address - non_address_offset)\n    return;\n\n  constexpr uint64_t kRegisterByteOffset = 128;\n  const uint64_t target = address - kRegisterByteOffset;\n  constexpr uint64_t size = 512;\n  static_assert(kRegisterByteOffset <= size / 2,\n                \"negative offset too large\");\n  auto ranges =\n      delegate->GetReadableRanges(CheckedRange<uint64_t>(target, size));\n  for (const auto& range : ranges) {\n    delegate->AddNewMemorySnapshot(range);\n  }\n}\n\ntemplate <class T>\nvoid CaptureAtPointersInRange(uint8_t* buffer,\n                              uint64_t buffer_size,\n                              CaptureMemory::Delegate* delegate) {\n  for (uint64_t address_offset = 0; address_offset < buffer_size;\n       address_offset += sizeof(T)) {\n    uint64_t target_address = *reinterpret_cast<T*>(&buffer[address_offset]);\n    MaybeCaptureMemoryAround(delegate, target_address);\n  }\n}\n\n}  // namespace\n\n// static\nvoid CaptureMemory::PointedToByContext(const CPUContext& context,\n                                       Delegate* delegate) {\n#if defined(ARCH_CPU_X86_FAMILY)\n  if (context.architecture == kCPUArchitectureX86_64) {\n    MaybeCaptureMemoryAround(delegate, context.x86_64->rip);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->rax);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->rbx);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->rcx);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->rdx);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->rdi);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->rsi);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->rbp);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->r8);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->r9);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->r10);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->r11);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->r12);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->r13);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->r14);\n    MaybeCaptureMemoryAround(delegate, context.x86_64->r15);\n    // Note: Shadow stack region is directly captured.\n  } else {\n    MaybeCaptureMemoryAround(delegate, context.x86->eip);\n    MaybeCaptureMemoryAround(delegate, context.x86->eax);\n    MaybeCaptureMemoryAround(delegate, context.x86->ebx);\n    MaybeCaptureMemoryAround(delegate, context.x86->ecx);\n    MaybeCaptureMemoryAround(delegate, context.x86->edx);\n    MaybeCaptureMemoryAround(delegate, context.x86->edi);\n    MaybeCaptureMemoryAround(delegate, context.x86->esi);\n    MaybeCaptureMemoryAround(delegate, context.x86->ebp);\n  }\n#elif defined(ARCH_CPU_ARM_FAMILY)\n  if (context.architecture == kCPUArchitectureARM64) {\n    MaybeCaptureMemoryAround(delegate, context.arm64->pc);\n    for (size_t i = 0; i < std::size(context.arm64->regs); ++i) {\n      MaybeCaptureMemoryAround(delegate, context.arm64->regs[i]);\n    }\n  } else {\n    MaybeCaptureMemoryAround(delegate, context.arm->pc);\n    for (size_t i = 0; i < std::size(context.arm->regs); ++i) {\n      MaybeCaptureMemoryAround(delegate, context.arm->regs[i]);\n    }\n  }\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  for (size_t i = 0; i < std::size(context.mipsel->regs); ++i) {\n    MaybeCaptureMemoryAround(delegate, context.mipsel->regs[i]);\n  }\n#elif defined(ARCH_CPU_RISCV64)\n  MaybeCaptureMemoryAround(delegate, context.riscv64->pc);\n  for (size_t i = 0; i < std::size(context.riscv64->regs); ++i) {\n    MaybeCaptureMemoryAround(delegate, context.riscv64->regs[i]);\n  }\n#else\n#error Port.\n#endif\n}\n\n// static\nvoid CaptureMemory::PointedToByMemoryRange(const MemorySnapshot& memory,\n                                           Delegate* delegate) {\n  if (memory.Size() == 0)\n    return;\n\n  const size_t alignment =\n      delegate->Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t);\n  if (memory.Address() % alignment != 0 || memory.Size() % alignment != 0) {\n    LOG(ERROR) << \"unaligned range\";\n    return;\n  }\n\n  auto buffer = base::HeapArray<uint8_t>::Uninit(memory.Size());\n  if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.data())) {\n    LOG(ERROR) << \"ReadMemory\";\n    return;\n  }\n\n  if (delegate->Is64Bit())\n    CaptureAtPointersInRange<uint64_t>(buffer.data(), buffer.size(), delegate);\n  else\n    CaptureAtPointersInRange<uint32_t>(buffer.data(), buffer.size(), delegate);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/capture_memory.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_\n#define CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_\n\n#include <stdint.h>\n\n#include <vector>\n\n#include \"snapshot/cpu_context.h\"\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\n\nclass MemorySnapshot;\n\nnamespace internal {\n\nclass CaptureMemory {\n public:\n  //! \\brief An interface to a platform-specific process reader.\n  class Delegate {\n   public:\n    virtual ~Delegate() {}\n\n    //! \\return `true` if the target process is a 64-bit process.\n    virtual bool Is64Bit() const = 0;\n\n    //! \\brief Attempts to read \\a num_bytes bytes from the target process\n    //!     starting at address \\a at into \\a into.\n    //!\n    //! \\return `true` if the entire region could be read, or `false` with an\n    //!     error logged.\n    virtual bool ReadMemory(uint64_t at,\n                            uint64_t num_bytes,\n                            void* into) const = 0;\n\n    //! \\brief Given a range to be read from the target process, returns a\n    //! vector\n    //!     of ranges, representing the readable portions of the original range.\n    //!\n    //! \\param[in] range The range being identified.\n    //!\n    //! \\return A vector of ranges corresponding to the portion of \\a range that\n    //!     is readable.\n    virtual std::vector<CheckedRange<uint64_t>> GetReadableRanges(\n        const CheckedRange<uint64_t, uint64_t>& range) const = 0;\n\n    //! \\brief Adds the given range representing a memory snapshot in the target\n    //!     process to the result.\n    virtual void AddNewMemorySnapshot(\n        const CheckedRange<uint64_t, uint64_t>& range) = 0;\n  };\n\n  CaptureMemory() = delete;\n  CaptureMemory(const CaptureMemory&) = delete;\n  CaptureMemory& operator=(const CaptureMemory&) = delete;\n\n  //! \\brief For all registers that appear to be pointer-like in \\a context,\n  //!     captures a small amount of memory near their pointed to location.\n  //!\n  //! \"Pointer-like\" in this context means not too close to zero (signed or\n  //! unsigned) so that there's a reasonable chance that the value is a pointer.\n  //!\n  //! \\param[in] context The context to inspect.\n  //! \\param[in] delegate A Delegate that handles reading from the target\n  //!     process and adding new ranges.\n  static void PointedToByContext(const CPUContext& context, Delegate* delegate);\n\n  //! \\brief For all pointer-like values in a memory range of the target\n  //! process,\n  //!     captures a small amount of memory near the pointed to location.\n  //!\n  //! \\param[in] memory An existing MemorySnapshot of the range to search. The\n  //!     base address and size must be pointer-aligned and an integral number\n  //!     of\n  //!     pointers long.\n  //! \\param[in] delegate A Delegate that handles reading from the target\n  //!     process and adding new ranges.\n  static void PointedToByMemoryRange(const MemorySnapshot& memory,\n                                     Delegate* delegate);\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_\n"
  },
  {
    "path": "snapshot/cpu_architecture.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_\n#define CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_\n\nnamespace crashpad {\n\n//! \\brief A system’s CPU architecture.\n//!\n//! This can be used to represent the CPU architecture of an entire system\n//! as in SystemSnapshot::CPUArchitecture(). It can also be used to represent\n//! the architecture of a CPUContext structure in its CPUContext::architecture\n//! field without reference to external data.\nenum CPUArchitecture {\n  //! \\brief The CPU architecture is unknown.\n  kCPUArchitectureUnknown = 0,\n\n  //! \\brief 32-bit x86.\n  kCPUArchitectureX86,\n\n  //! \\brief x86_64.\n  kCPUArchitectureX86_64,\n\n  //! \\brief 32-bit ARM.\n  kCPUArchitectureARM,\n\n  //! \\brief 64-bit ARM.\n  kCPUArchitectureARM64,\n\n  //! \\brief 32-bit MIPSEL.\n  kCPUArchitectureMIPSEL,\n\n  //! \\brief 64-bit MIPSEL.\n  kCPUArchitectureMIPS64EL,\n\n  //! \\brief 64-bit RISC-V.\n  kCPUArchitectureRISCV64,\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_\n"
  },
  {
    "path": "snapshot/cpu_context.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/cpu_context.h\"\n\n#include <stddef.h>\n#include <string.h>\n\n#include <iterator>\n\n#include \"base/notreached.h\"\n#include \"cpu_architecture.h\"\n#include \"util/misc/arraysize.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Sanity-check complex structures to ensure interoperability.\nstatic_assert(sizeof(CPUContextX86::Fsave) == 108, \"CPUContextX86::Fsave size\");\nstatic_assert(sizeof(CPUContextX86::Fxsave) == 512,\n              \"CPUContextX86::Fxsave size\");\nstatic_assert(sizeof(CPUContextX86_64::Fxsave) == 512,\n              \"CPUContextX86_64::Fxsave size\");\n\nenum {\n  kX87TagValid = 0,\n  kX87TagZero,\n  kX87TagSpecial,\n  kX87TagEmpty,\n};\n\n}  // namespace\n\n// static\nvoid CPUContextX86::FxsaveToFsave(const Fxsave& fxsave, Fsave* fsave) {\n  fsave->fcw = fxsave.fcw;\n  fsave->reserved_1 = 0;\n  fsave->fsw = fxsave.fsw;\n  fsave->reserved_2 = 0;\n  fsave->ftw = FxsaveToFsaveTagWord(fxsave.fsw, fxsave.ftw, fxsave.st_mm);\n  fsave->reserved_3 = 0;\n  fsave->fpu_ip = fxsave.fpu_ip;\n  fsave->fpu_cs = fxsave.fpu_cs;\n  fsave->fop = fxsave.fop;\n  fsave->fpu_dp = fxsave.fpu_dp;\n  fsave->fpu_ds = fxsave.fpu_ds;\n  fsave->reserved_4 = 0;\n  static_assert(ArraySize(fsave->st) == ArraySize(fxsave.st_mm),\n                \"FPU stack registers must be equivalent\");\n  for (size_t index = 0; index < std::size(fsave->st); ++index) {\n    memcpy(fsave->st[index], fxsave.st_mm[index].st, sizeof(fsave->st[index]));\n  }\n}\n\n// static\nvoid CPUContextX86::FsaveToFxsave(const Fsave& fsave, Fxsave* fxsave) {\n  fxsave->fcw = fsave.fcw;\n  fxsave->fsw = fsave.fsw;\n  fxsave->ftw = FsaveToFxsaveTagWord(fsave.ftw);\n  fxsave->reserved_1 = 0;\n  fxsave->fop = fsave.fop;\n  fxsave->fpu_ip = fsave.fpu_ip;\n  fxsave->fpu_cs = fsave.fpu_cs;\n  fxsave->reserved_2 = 0;\n  fxsave->fpu_dp = fsave.fpu_dp;\n  fxsave->fpu_ds = fsave.fpu_ds;\n  fxsave->reserved_3 = 0;\n  fxsave->mxcsr = 0;\n  fxsave->mxcsr_mask = 0;\n  static_assert(ArraySize(fxsave->st_mm) == ArraySize(fsave.st),\n                \"FPU stack registers must be equivalent\");\n  for (size_t index = 0; index < std::size(fsave.st); ++index) {\n    memcpy(fxsave->st_mm[index].st, fsave.st[index], sizeof(fsave.st[index]));\n    memset(fxsave->st_mm[index].st_reserved,\n           0,\n           sizeof(fxsave->st_mm[index].st_reserved));\n  }\n  memset(fxsave->xmm, 0, sizeof(*fxsave) - offsetof(Fxsave, xmm));\n}\n\n// static\nuint16_t CPUContextX86::FxsaveToFsaveTagWord(\n    uint16_t fsw,\n    uint8_t fxsave_tag,\n    const CPUContextX86::X87OrMMXRegister st_mm[8]) {\n  // The x87 tag word (in both abridged and full form) identifies physical\n  // registers, but |st_mm| is arranged in logical stack order. In order to map\n  // physical tag word bits to the logical stack registers they correspond to,\n  // the “stack top” value from the x87 status word is necessary.\n  int stack_top = (fsw >> 11) & 0x7;\n\n  uint16_t fsave_tag = 0;\n  for (int physical_index = 0; physical_index < 8; ++physical_index) {\n    bool fxsave_bit = (fxsave_tag & (1 << physical_index)) != 0;\n    uint8_t fsave_bits;\n\n    if (fxsave_bit) {\n      int st_index = (physical_index + 8 - stack_top) % 8;\n      const CPUContextX86::X87Register& st = st_mm[st_index].st;\n\n      uint32_t exponent = ((st[9] & 0x7f) << 8) | st[8];\n      if (exponent == 0x7fff) {\n        // Infinity, NaN, pseudo-infinity, or pseudo-NaN. If it was important to\n        // distinguish between these, the J bit and the M bit (the most\n        // significant bit of |fraction|) could be consulted.\n        fsave_bits = kX87TagSpecial;\n      } else {\n        // The integer bit the “J bit”.\n        bool integer_bit = (st[7] & 0x80) != 0;\n        if (exponent == 0) {\n          uint64_t fraction = ((implicit_cast<uint64_t>(st[7]) & 0x7f) << 56) |\n                              (implicit_cast<uint64_t>(st[6]) << 48) |\n                              (implicit_cast<uint64_t>(st[5]) << 40) |\n                              (implicit_cast<uint64_t>(st[4]) << 32) |\n                              (implicit_cast<uint32_t>(st[3]) << 24) |\n                              (st[2] << 16) | (st[1] << 8) | st[0];\n          if (!integer_bit && fraction == 0) {\n            fsave_bits = kX87TagZero;\n          } else {\n            // Denormal (if the J bit is clear) or pseudo-denormal.\n            fsave_bits = kX87TagSpecial;\n          }\n        } else if (integer_bit) {\n          fsave_bits = kX87TagValid;\n        } else {\n          // Unnormal.\n          fsave_bits = kX87TagSpecial;\n        }\n      }\n    } else {\n      fsave_bits = kX87TagEmpty;\n    }\n\n    fsave_tag |= (fsave_bits << (physical_index * 2));\n  }\n\n  return fsave_tag;\n}\n\n// static\nuint8_t CPUContextX86::FsaveToFxsaveTagWord(uint16_t fsave_tag) {\n  uint8_t fxsave_tag = 0;\n  for (int physical_index = 0; physical_index < 8; ++physical_index) {\n    const uint8_t fsave_bits = (fsave_tag >> (physical_index * 2)) & 0x3;\n    const bool fxsave_bit = fsave_bits != kX87TagEmpty;\n    fxsave_tag |= fxsave_bit << physical_index;\n  }\n  return fxsave_tag;\n}\n\nuint64_t CPUContext::InstructionPointer() const {\n  switch (architecture) {\n    case kCPUArchitectureX86:\n      return x86->eip;\n    case kCPUArchitectureX86_64:\n      return x86_64->rip;\n    case kCPUArchitectureARM:\n      return arm->pc;\n    case kCPUArchitectureARM64:\n      return arm64->pc;\n    case kCPUArchitectureRISCV64:\n      return riscv64->pc;\n    default:\n      NOTREACHED();\n  }\n}\n\nuint64_t CPUContext::StackPointer() const {\n  switch (architecture) {\n    case kCPUArchitectureX86:\n      return x86->esp;\n    case kCPUArchitectureX86_64:\n      return x86_64->rsp;\n    case kCPUArchitectureARM:\n      return arm->sp;\n    case kCPUArchitectureARM64:\n      return arm64->sp;\n    case kCPUArchitectureRISCV64:\n      return riscv64->regs[1];\n    default:\n      NOTREACHED();\n  }\n}\n\nuint64_t CPUContext::ShadowStackPointer() const {\n  switch (architecture) {\n    case kCPUArchitectureX86:\n    case kCPUArchitectureARM:\n    case kCPUArchitectureARM64:\n      NOTREACHED();\n    case kCPUArchitectureX86_64:\n      return x86_64->xstate.cet_u.ssp;\n    default:\n      NOTREACHED();\n  }\n}\n\nbool CPUContext::HasShadowStack() const {\n  switch (architecture) {\n    case kCPUArchitectureX86:\n    case kCPUArchitectureARM:\n    case kCPUArchitectureARM64:\n      return false;\n    case kCPUArchitectureX86_64:\n      return x86_64->xstate.cet_u.cetmsr != 0;\n    default:\n      NOTREACHED();\n  }\n}\n\nbool CPUContext::Is64Bit() const {\n  switch (architecture) {\n    case kCPUArchitectureX86_64:\n    case kCPUArchitectureARM64:\n    case kCPUArchitectureMIPS64EL:\n    case kCPUArchitectureRISCV64:\n      return true;\n    case kCPUArchitectureX86:\n    case kCPUArchitectureARM:\n    case kCPUArchitectureMIPSEL:\n      return false;\n    default:\n      NOTREACHED();\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/cpu_context.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_\n#define CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_\n\n#include <stdint.h>\n\n#include \"snapshot/cpu_architecture.h\"\n#include \"util/numeric/int128.h\"\n\nnamespace crashpad {\n\n//! \\brief A context structure carrying 32-bit x86 CPU state.\nstruct CPUContextX86 {\n  using X87Register = uint8_t[10];\n\n  struct Fsave {\n    uint16_t fcw;  // FPU control word\n    uint16_t reserved_1;\n    uint16_t fsw;  // FPU status word\n    uint16_t reserved_2;\n    uint16_t ftw;  // full FPU tag word\n    uint16_t reserved_3;\n    uint32_t fpu_ip;  // FPU instruction pointer offset\n    uint16_t fpu_cs;  // FPU instruction pointer segment selector\n    uint16_t fop;  // FPU opcode\n    uint32_t fpu_dp;  // FPU data pointer offset\n    uint16_t fpu_ds;  // FPU data pointer segment selector\n    uint16_t reserved_4;\n    X87Register st[8];\n  };\n\n  union X87OrMMXRegister {\n    struct {\n      X87Register st;\n      uint8_t st_reserved[6];\n    };\n    struct {\n      uint8_t mm_value[8];\n      uint8_t mm_reserved[8];\n    };\n  };\n\n  using XMMRegister = uint8_t[16];\n\n  struct Fxsave {\n    uint16_t fcw;  // FPU control word\n    uint16_t fsw;  // FPU status word\n    uint8_t ftw;  // abridged FPU tag word\n    uint8_t reserved_1;\n    uint16_t fop;  // FPU opcode\n    uint32_t fpu_ip;  // FPU instruction pointer offset\n    uint16_t fpu_cs;  // FPU instruction pointer segment selector\n    uint16_t reserved_2;\n    uint32_t fpu_dp;  // FPU data pointer offset\n    uint16_t fpu_ds;  // FPU data pointer segment selector\n    uint16_t reserved_3;\n    uint32_t mxcsr;  // multimedia extensions status and control register\n    uint32_t mxcsr_mask;  // valid bits in mxcsr\n    X87OrMMXRegister st_mm[8];\n    XMMRegister xmm[8];\n    uint8_t reserved_4[176];\n    uint8_t available[48];\n  };\n\n  //! \\brief Converts an `fxsave` area to an `fsave` area.\n  //!\n  //! `fsave` state is restricted to the x87 FPU, while `fxsave` state includes\n  //! state related to the x87 FPU as well as state specific to SSE.\n  //!\n  //! As the `fxsave` format is a superset of the `fsave` format, this operation\n  //! fully populates the `fsave` area. `fsave` uses the full 16-bit form for\n  //! the x87 floating-point tag word, so FxsaveToFsaveTagWord() is used to\n  //! derive Fsave::ftw from the abridged 8-bit form used by `fxsave`. Reserved\n  //! fields in \\a fsave are set to `0`.\n  //!\n  //! \\param[in] fxsave The `fxsave` area to convert.\n  //! \\param[out] fsave The `fsave` area to populate.\n  //!\n  //! \\sa FsaveToFxsave()\n  static void FxsaveToFsave(const Fxsave& fxsave, Fsave* fsave);\n\n  //! \\brief Converts an `fsave` area to an `fxsave` area.\n  //!\n  //! `fsave` state is restricted to the x87 FPU, while `fxsave` state includes\n  //! state related to the x87 FPU as well as state specific to SSE.\n  //!\n  //! As the `fsave` format is a subset of the `fxsave` format, this operation\n  //! cannot fully populate the `fxsave` area. Fields in \\a fxsave that have no\n  //! equivalent in \\a fsave are set to `0`, including Fxsave::mxcsr,\n  //! Fxsave::mxcsr_mask, Fxsave::xmm, and Fxsave::available.\n  //! FsaveToFxsaveTagWord() is used to derive Fxsave::ftw from the full 16-bit\n  //! form used by `fsave`. Reserved fields in \\a fxsave are set to `0`.\n  //!\n  //! \\param[in] fsave The `fsave` area to convert.\n  //! \\param[out] fxsave The `fxsave` area to populate.\n  //!\n  //! \\sa FxsaveToFsave()\n  static void FsaveToFxsave(const Fsave& fsave, Fxsave* fxsave);\n\n  //! \\brief Converts x87 floating-point tag words from `fxsave` (abridged,\n  //!     8-bit) to `fsave` (full, 16-bit) form.\n  //!\n  //! `fxsave` stores the x87 floating-point tag word in abridged 8-bit form,\n  //! and `fsave` stores it in full 16-bit form. Some users, notably\n  //! CPUContextX86::Fsave::ftw, require the full 16-bit form, where most other\n  //! contemporary code uses `fxsave` and thus the abridged 8-bit form found in\n  //! CPUContextX86::Fxsave::ftw.\n  //!\n  //! This function converts an abridged tag word to the full version by using\n  //! the abridged tag word and the contents of the registers it describes. See\n  //! Intel Software Developer’s Manual, Volume 2A: Instruction Set Reference\n  //! A-M (253666-052), 3.2 “FXSAVE”, specifically, the notes on the abridged\n  //! FTW and recreating the FSAVE format, and AMD Architecture Programmer’s\n  //! Manual, Volume 2: System Programming (24593-3.24), “FXSAVE Format for x87\n  //! Tag Word”.\n  //!\n  //! \\sa FsaveToFxsaveTagWord()\n  //!\n  //! \\param[in] fsw The FPU status word, used to map logical \\a st_mm registers\n  //!     to their physical counterparts. This can be taken from\n  //!     CPUContextX86::Fxsave::fsw.\n  //! \\param[in] fxsave_tag The abridged FPU tag word. This can be taken from\n  //!     CPUContextX86::Fxsave::ftw.\n  //! \\param[in] st_mm The floating-point registers in logical order. This can\n  //!     be taken from CPUContextX86::Fxsave::st_mm.\n  //!\n  //! \\return The full FPU tag word.\n  static uint16_t FxsaveToFsaveTagWord(\n      uint16_t fsw, uint8_t fxsave_tag, const X87OrMMXRegister st_mm[8]);\n\n  //! \\brief Converts x87 floating-point tag words from `fsave` (full, 16-bit)\n  //!     to `fxsave` (abridged, 8-bit) form.\n  //!\n  //! This function performs the inverse operation of FxsaveToFsaveTagWord().\n  //!\n  //! \\param[in] fsave_tag The full FPU tag word.\n  //!\n  //! \\return The abridged FPU tag word.\n  static uint8_t FsaveToFxsaveTagWord(uint16_t fsave_tag);\n\n  // Integer registers.\n  uint32_t eax;\n  uint32_t ebx;\n  uint32_t ecx;\n  uint32_t edx;\n  uint32_t edi;  // destination index\n  uint32_t esi;  // source index\n  uint32_t ebp;  // base pointer\n  uint32_t esp;  // stack pointer\n  uint32_t eip;  // instruction pointer\n  uint32_t eflags;\n  uint16_t cs;  // code segment selector\n  uint16_t ds;  // data segment selector\n  uint16_t es;  // extra segment selector\n  uint16_t fs;\n  uint16_t gs;\n  uint16_t ss;  // stack segment selector\n\n  // Floating-point and vector registers.\n  Fxsave fxsave;\n\n  // Debug registers.\n  uint32_t dr0;\n  uint32_t dr1;\n  uint32_t dr2;\n  uint32_t dr3;\n  uint32_t dr4;  // obsolete, normally an alias for dr6\n  uint32_t dr5;  // obsolete, normally an alias for dr7\n  uint32_t dr6;\n  uint32_t dr7;\n};\n\n//! \\brief A context structure carrying x86_64 CPU state.\nstruct CPUContextX86_64 {\n  using X87Register = CPUContextX86::X87Register;\n  using X87OrMMXRegister = CPUContextX86::X87OrMMXRegister;\n  using XMMRegister = CPUContextX86::XMMRegister;\n\n  struct Fxsave {\n    uint16_t fcw;  // FPU control word\n    uint16_t fsw;  // FPU status word\n    uint8_t ftw;  // abridged FPU tag word\n    uint8_t reserved_1;\n    uint16_t fop;  // FPU opcode\n    union {\n      // The expression of these union members is determined by the use of\n      // fxsave/fxrstor or fxsave64/fxrstor64 (fxsaveq/fxrstorq). macOS and\n      // Windows systems use the traditional fxsave/fxrstor structure.\n      struct {\n        // fxsave/fxrstor\n        uint32_t fpu_ip;  // FPU instruction pointer offset\n        uint16_t fpu_cs;  // FPU instruction pointer segment selector\n        uint16_t reserved_2;\n        uint32_t fpu_dp;  // FPU data pointer offset\n        uint16_t fpu_ds;  // FPU data pointer segment selector\n        uint16_t reserved_3;\n      };\n      struct {\n        // fxsave64/fxrstor64 (fxsaveq/fxrstorq)\n        uint64_t fpu_ip_64;  // FPU instruction pointer\n        uint64_t fpu_dp_64;  // FPU data pointer\n      };\n    };\n    uint32_t mxcsr;  // multimedia extensions status and control register\n    uint32_t mxcsr_mask;  // valid bits in mxcsr\n    X87OrMMXRegister st_mm[8];\n    XMMRegister xmm[16];\n    uint8_t reserved_4[48];\n    uint8_t available[48];\n  };\n\n  // Integer registers.\n  uint64_t rax;\n  uint64_t rbx;\n  uint64_t rcx;\n  uint64_t rdx;\n  uint64_t rdi;  // destination index\n  uint64_t rsi;  // source index\n  uint64_t rbp;  // base pointer\n  uint64_t rsp;  // stack pointer\n  uint64_t r8;\n  uint64_t r9;\n  uint64_t r10;\n  uint64_t r11;\n  uint64_t r12;\n  uint64_t r13;\n  uint64_t r14;\n  uint64_t r15;\n  uint64_t rip;  // instruction pointer\n  uint64_t rflags;\n  uint16_t cs;  // code segment selector\n  uint16_t fs;\n  uint16_t gs;\n\n  // Floating-point and vector registers.\n  Fxsave fxsave;\n\n  // Debug registers.\n  uint64_t dr0;\n  uint64_t dr1;\n  uint64_t dr2;\n  uint64_t dr3;\n  uint64_t dr4;  // obsolete, normally an alias for dr6\n  uint64_t dr5;  // obsolete, normally an alias for dr7\n  uint64_t dr6;\n  uint64_t dr7;\n\n  struct {\n    // If 0 then none of the xsave areas are valid.\n    uint64_t enabled_features;\n    // CET_U registers if XSTATE_CET_U bit is set in enabled_features.\n    struct {\n      uint64_t cetmsr;\n      uint64_t ssp;\n    } cet_u;\n  } xstate;\n};\n\n//! \\brief A context structure carrying ARM CPU state.\nstruct CPUContextARM {\n  uint32_t regs[11];\n  uint32_t fp;  // r11\n  uint32_t ip;  // r12\n  uint32_t sp;  // r13\n  uint32_t lr;  // r14\n  uint32_t pc;  // r15\n  uint32_t cpsr;\n\n  struct {\n    struct fp_reg {\n      uint32_t sign1 : 1;\n      uint32_t unused : 15;\n      uint32_t sign2 : 1;\n      uint32_t exponent : 14;\n      uint32_t j : 1;\n      uint32_t mantissa1 : 31;\n      uint32_t mantisss0 : 32;\n    } fpregs[8];\n    uint32_t fpsr : 32;\n    uint32_t fpcr : 32;\n    uint8_t type[8];\n    uint32_t init_flag;\n  } fpa_regs;\n\n  struct {\n    uint64_t vfp[32];\n    uint32_t fpscr;\n  } vfp_regs;\n\n  bool have_fpa_regs;\n  bool have_vfp_regs;\n};\n\n//! \\brief A context structure carrying ARM64 CPU state.\nstruct CPUContextARM64 {\n  uint64_t regs[31];\n  uint64_t sp;\n  uint64_t pc;\n  uint32_t spsr;\n\n  uint128_struct fpsimd[32];\n  uint32_t fpsr;\n  uint32_t fpcr;\n};\n\n//! \\brief A context structure carrying MIPS CPU state.\nstruct CPUContextMIPS {\n  uint64_t regs[32];\n  uint32_t mdlo;\n  uint32_t mdhi;\n  uint32_t cp0_epc;\n  uint32_t cp0_badvaddr;\n  uint32_t cp0_status;\n  uint32_t cp0_cause;\n  uint32_t hi[3];\n  uint32_t lo[3];\n  uint32_t dsp_control;\n  union {\n    double dregs[32];\n    struct {\n      float _fp_fregs;\n      uint32_t _fp_pad;\n    } fregs[32];\n  } fpregs;\n  uint32_t fpcsr;\n  uint32_t fir;\n};\n\n//! \\brief A context structure carrying MIPS64 CPU state.\nstruct CPUContextMIPS64 {\n  uint64_t regs[32];\n  uint64_t mdlo;\n  uint64_t mdhi;\n  uint64_t cp0_epc;\n  uint64_t cp0_badvaddr;\n  uint64_t cp0_status;\n  uint64_t cp0_cause;\n  uint64_t hi[3];\n  uint64_t lo[3];\n  uint64_t dsp_control;\n  union {\n    double dregs[32];\n    struct {\n      float _fp_fregs;\n      uint32_t _fp_pad;\n    } fregs[32];\n  } fpregs;\n  uint64_t fpcsr;\n  uint64_t fir;\n};\n\n//! \\brief A context structure carrying RISCV64 CPU state.\nstruct CPUContextRISCV64 {\n  uint64_t pc;\n  uint64_t regs[31];\n\n  uint64_t fpregs[32];\n  uint32_t fcsr;\n};\n\n//! \\brief A context structure capable of carrying the context of any supported\n//!     CPU architecture.\nstruct CPUContext {\n  //! \\brief Returns the instruction pointer value from the context structure.\n  //!\n  //! This is a CPU architecture-independent method that is capable of\n  //! recovering the instruction pointer from any supported CPU architecture’s\n  //! context structure.\n  uint64_t InstructionPointer() const;\n\n  //! \\brief Returns the stack pointer value from the context structure.\n  //!\n  //! This is a CPU architecture-independent method that is capable of\n  //! recovering the stack pointer from any supported CPU architecture’s\n  //! context structure.\n  uint64_t StackPointer() const;\n\n  //! \\brief Returns the shadow stack pointer value from the context structure.\n  //!\n  //! This is a CPU architecture-independent method that is capable of\n  //! recovering the shadow stack pointer from any supported CPU architecture’s\n  //! context structure.\n  uint64_t ShadowStackPointer() const;\n\n  //! \\brief Returns `true` if this context is for a 64-bit architecture.\n  bool Is64Bit() const;\n\n  //! \\brief Returns `true` if this context has an active shadow stack pointer.\n  bool HasShadowStack() const;\n\n  //! \\brief The CPU architecture of a context structure. This field controls\n  //!     the expression of the union.\n  CPUArchitecture architecture;\n  union {\n    CPUContextX86* x86;\n    CPUContextX86_64* x86_64;\n    CPUContextARM* arm;\n    CPUContextARM64* arm64;\n    CPUContextMIPS* mipsel;\n    CPUContextMIPS64* mips64;\n    CPUContextRISCV64* riscv64;\n  };\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_\n"
  },
  {
    "path": "snapshot/cpu_context_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/cpu_context.h\"\n\n#include <stddef.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"gtest/gtest.h\"\n#include \"test/hex_string.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nenum ExponentValue {\n  kExponentAllZero = 0,\n  kExponentAllOne,\n  kExponentNormal,\n};\n\nenum FractionValue {\n  kFractionAllZero = 0,\n  kFractionNormal,\n};\n\n//! \\brief Initializes an x87 register to a known bit pattern.\n//!\n//! \\param[out] st_mm The x87 register to initialize. The reserved portion of\n//!     the register is always zeroed out.\n//! \\param[in] exponent_value The bit pattern to use for the exponent. If this\n//!     is kExponentAllZero, the sign bit will be set to `1`, and if this is\n//!     kExponentAllOne, the sign bit will be set to `0`. This tests that the\n//!     implementation doesn’t erroneously consider the sign bit to be part of\n//!     the exponent. This may also be kExponentNormal, indicating that the\n//!     exponent shall neither be all zeroes nor all ones.\n//! \\param[in] j_bit The value to use for the “J bit” (“integer bit”).\n//! \\param[in] fraction_value If kFractionAllZero, the fraction will be zeroed\n//!     out. If kFractionNormal, the fraction will not be all zeroes.\nvoid SetX87Register(CPUContextX86::X87Register* st,\n                    ExponentValue exponent_value,\n                    bool j_bit,\n                    FractionValue fraction_value) {\n  switch (exponent_value) {\n    case kExponentAllZero:\n      (*st)[9] = 0x80;\n      (*st)[8] = 0;\n      break;\n    case kExponentAllOne:\n      (*st)[9] = 0x7f;\n      (*st)[8] = 0xff;\n      break;\n    case kExponentNormal:\n      (*st)[9] = 0x55;\n      (*st)[8] = 0x55;\n      break;\n  }\n\n  uint8_t fraction_pattern = fraction_value == kFractionAllZero ? 0 : 0x55;\n  memset(st, fraction_pattern, 8);\n\n  if (j_bit) {\n    (*st)[7] |= 0x80;\n  } else {\n    (*st)[7] &= ~0x80;\n  }\n}\n\n//! \\brief Initializes an x87 register to a known bit pattern.\n//!\n//! This behaves as SetX87Register() but also clears the reserved portion of the\n//! field as used in the `fxsave` format.\nvoid SetX87OrMMXRegister(CPUContextX86::X87OrMMXRegister* st_mm,\n                         ExponentValue exponent_value,\n                         bool j_bit,\n                         FractionValue fraction_value) {\n  SetX87Register(&st_mm->st, exponent_value, j_bit, fraction_value);\n  memset(st_mm->st_reserved, 0, sizeof(st_mm->st_reserved));\n}\n\nTEST(CPUContextX86, FxsaveToFsave) {\n  // Establish a somewhat plausible fxsave state. Use nonzero values for\n  // reserved fields and things that aren’t present in fsave.\n  CPUContextX86::Fxsave fxsave;\n  fxsave.fcw = 0x027f;  // mask exceptions, 53-bit precision, round to nearest\n  fxsave.fsw = 1 << 11;  // top = 1: logical 0-7 maps to physical 1-7, 0\n  fxsave.ftw = 0x1f;  // physical 5-7 (logical 4-6) empty\n  fxsave.reserved_1 = 0x5a;\n  fxsave.fop = 0x1fe;  // fsin\n  fxsave.fpu_ip = 0x76543210;\n  fxsave.fpu_cs = 0x0007;\n  fxsave.reserved_2 = 0x5a5a;\n  fxsave.fpu_dp = 0xfedcba98;\n  fxsave.fpu_ds = 0x000f;\n  fxsave.reserved_3 = 0x5a5a;\n  fxsave.mxcsr = 0x1f80;\n  fxsave.mxcsr_mask = 0xffff;\n  SetX87Register(\n      &fxsave.st_mm[0].st, kExponentNormal, true, kFractionAllZero);  // valid\n  SetX87Register(\n      &fxsave.st_mm[1].st, kExponentAllZero, false, kFractionAllZero);  // zero\n  SetX87Register(\n      &fxsave.st_mm[2].st, kExponentAllOne, true, kFractionAllZero);  // spec.\n  SetX87Register(\n      &fxsave.st_mm[3].st, kExponentAllOne, true, kFractionNormal);  // spec.\n  SetX87Register(\n      &fxsave.st_mm[4].st, kExponentAllZero, false, kFractionAllZero);\n  SetX87Register(\n      &fxsave.st_mm[5].st, kExponentAllZero, false, kFractionAllZero);\n  SetX87Register(\n      &fxsave.st_mm[6].st, kExponentAllZero, false, kFractionAllZero);\n  SetX87Register(\n      &fxsave.st_mm[7].st, kExponentNormal, true, kFractionNormal);  // valid\n  for (size_t index = 0; index < std::size(fxsave.st_mm); ++index) {\n    memset(&fxsave.st_mm[index].st_reserved,\n           0x5a,\n           sizeof(fxsave.st_mm[index].st_reserved));\n  }\n  memset(&fxsave.xmm, 0x5a, sizeof(fxsave) - offsetof(decltype(fxsave), xmm));\n\n  CPUContextX86::Fsave fsave;\n  CPUContextX86::FxsaveToFsave(fxsave, &fsave);\n\n  // Everything should have come over from fxsave. Reserved fields should be\n  // zero.\n  EXPECT_EQ(fsave.fcw, fxsave.fcw);\n  EXPECT_EQ(fsave.reserved_1, 0);\n  EXPECT_EQ(fsave.fsw, fxsave.fsw);\n  EXPECT_EQ(fsave.reserved_2, 0);\n  EXPECT_EQ(fsave.ftw, 0xfe90);  // FxsaveToFsaveTagWord\n  EXPECT_EQ(fsave.reserved_3, 0);\n  EXPECT_EQ(fsave.fpu_ip, fxsave.fpu_ip);\n  EXPECT_EQ(fsave.fpu_cs, fxsave.fpu_cs);\n  EXPECT_EQ(fsave.fop, fxsave.fop);\n  EXPECT_EQ(fsave.fpu_dp, fxsave.fpu_dp);\n  EXPECT_EQ(fsave.fpu_ds, fxsave.fpu_ds);\n  EXPECT_EQ(fsave.reserved_4, 0);\n  for (size_t index = 0; index < std::size(fsave.st); ++index) {\n    EXPECT_EQ(BytesToHexString(fsave.st[index], std::size(fsave.st[index])),\n              BytesToHexString(fxsave.st_mm[index].st,\n                               std::size(fxsave.st_mm[index].st)))\n        << \"index \" << index;\n  }\n}\n\nTEST(CPUContextX86, FsaveToFxsave) {\n  // Establish a somewhat plausible fsave state. Use nonzero values for\n  // reserved fields.\n  CPUContextX86::Fsave fsave;\n  fsave.fcw = 0x0300;  // unmask exceptions, 64-bit precision, round to nearest\n  fsave.reserved_1 = 0xa5a5;\n  fsave.fsw = 2 << 11;  // top = 2: logical 0-7 maps to physical 2-7, 0-1\n  fsave.reserved_2 = 0xa5a5;\n  fsave.ftw = 0xa9ff;  // physical 0-3 (logical 6-7, 0-1) empty; physical 4\n                       // (logical 2) zero; physical 5-7 (logical 3-5) special\n  fsave.reserved_3 = 0xa5a5;\n  fsave.fpu_ip = 0x456789ab;\n  fsave.fpu_cs = 0x1013;\n  fsave.fop = 0x01ee;  // fldz\n  fsave.fpu_dp = 0x0123cdef;\n  fsave.fpu_ds = 0x2017;\n  fsave.reserved_4 = 0xa5a5;\n  SetX87Register(&fsave.st[0], kExponentAllZero, false, kFractionNormal);\n  SetX87Register(&fsave.st[1], kExponentAllZero, true, kFractionNormal);\n  SetX87Register(\n      &fsave.st[2], kExponentAllZero, false, kFractionAllZero);  // zero\n  SetX87Register(\n      &fsave.st[3], kExponentAllZero, true, kFractionAllZero);  // spec.\n  SetX87Register(\n      &fsave.st[4], kExponentAllZero, false, kFractionNormal);  // spec.\n  SetX87Register(\n      &fsave.st[5], kExponentAllZero, true, kFractionNormal);  // spec.\n  SetX87Register(&fsave.st[6], kExponentAllZero, false, kFractionAllZero);\n  SetX87Register(&fsave.st[7], kExponentAllZero, true, kFractionAllZero);\n\n  CPUContextX86::Fxsave fxsave;\n  CPUContextX86::FsaveToFxsave(fsave, &fxsave);\n\n  // Everything in fsave should have come over from there. Fields not present in\n  // fsave and reserved fields should be zero.\n  EXPECT_EQ(fxsave.fcw, fsave.fcw);\n  EXPECT_EQ(fxsave.fsw, fsave.fsw);\n  EXPECT_EQ(fxsave.ftw, 0xf0);  // FsaveToFxsaveTagWord\n  EXPECT_EQ(fxsave.reserved_1, 0);\n  EXPECT_EQ(fxsave.fop, fsave.fop);\n  EXPECT_EQ(fxsave.fpu_ip, fsave.fpu_ip);\n  EXPECT_EQ(fxsave.fpu_cs, fsave.fpu_cs);\n  EXPECT_EQ(fxsave.reserved_2, 0);\n  EXPECT_EQ(fxsave.fpu_dp, fsave.fpu_dp);\n  EXPECT_EQ(fxsave.fpu_ds, fsave.fpu_ds);\n  EXPECT_EQ(fxsave.reserved_3, 0);\n  EXPECT_EQ(fxsave.mxcsr, 0u);\n  EXPECT_EQ(fxsave.mxcsr_mask, 0u);\n  for (size_t index = 0; index < std::size(fxsave.st_mm); ++index) {\n    EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st,\n                               std::size(fxsave.st_mm[index].st)),\n              BytesToHexString(fsave.st[index], std::size(fsave.st[index])))\n        << \"index \" << index;\n    EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st_reserved,\n                               std::size(fxsave.st_mm[index].st_reserved)),\n              std::string(std::size(fxsave.st_mm[index].st_reserved) * 2, '0'))\n        << \"index \" << index;\n  }\n  size_t unused_len = sizeof(fxsave) - offsetof(decltype(fxsave), xmm);\n  EXPECT_EQ(BytesToHexString(fxsave.xmm, unused_len),\n            std::string(unused_len * 2, '0'));\n\n  // Since the fsave format is a subset of the fxsave format, fsave-fxsave-fsave\n  // should round-trip cleanly.\n  CPUContextX86::Fsave fsave_2;\n  CPUContextX86::FxsaveToFsave(fxsave, &fsave_2);\n\n  // Clear the reserved fields in the original fsave structure, since they’re\n  // expected to be clear in the copy.\n  fsave.reserved_1 = 0;\n  fsave.reserved_2 = 0;\n  fsave.reserved_3 = 0;\n  fsave.reserved_4 = 0;\n  EXPECT_EQ(memcmp(&fsave, &fsave_2, sizeof(fsave)), 0);\n}\n\nTEST(CPUContextX86, FxsaveToFsaveTagWord) {\n  // The fsave tag word uses bit pattern 00 for valid, 01 for zero, 10 for\n  // “special”, and 11 for empty. Like the fxsave tag word, it is arranged by\n  // physical register. The fxsave tag word determines whether a register is\n  // empty, and analysis of the x87 register content distinguishes between\n  // valid, zero, and special. In the initializations below, comments show\n  // whether a register is expected to be considered valid, zero, or special,\n  // except where the tag word is expected to indicate that it is empty. Each\n  // combination appears twice: once where the fxsave tag word indicates a\n  // nonempty register, and once again where it indicates an empty register.\n\n  uint16_t fsw = 0 << 11;  // top = 0: logical 0-7 maps to physical 0-7\n  uint8_t fxsave_tag = 0x0f;  // physical 4-7 (logical 4-7) empty\n  CPUContextX86::X87OrMMXRegister st_mm[8];\n  SetX87OrMMXRegister(\n      &st_mm[0], kExponentNormal, false, kFractionNormal);  // spec.\n  SetX87OrMMXRegister(\n      &st_mm[1], kExponentNormal, true, kFractionNormal);  // valid\n  SetX87OrMMXRegister(\n      &st_mm[2], kExponentNormal, false, kFractionAllZero);  // spec.\n  SetX87OrMMXRegister(\n      &st_mm[3], kExponentNormal, true, kFractionAllZero);  // valid\n  SetX87OrMMXRegister(&st_mm[4], kExponentNormal, false, kFractionNormal);\n  SetX87OrMMXRegister(&st_mm[5], kExponentNormal, true, kFractionNormal);\n  SetX87OrMMXRegister(&st_mm[6], kExponentNormal, false, kFractionAllZero);\n  SetX87OrMMXRegister(&st_mm[7], kExponentNormal, true, kFractionAllZero);\n  EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm),\n            0xff22);\n\n  fsw = 2 << 11;  // top = 2: logical 0-7 maps to physical 2-7, 0-1\n  fxsave_tag = 0xf0;  // physical 0-3 (logical 6-7, 0-1) empty\n  SetX87OrMMXRegister(&st_mm[0], kExponentAllZero, false, kFractionNormal);\n  SetX87OrMMXRegister(&st_mm[1], kExponentAllZero, true, kFractionNormal);\n  SetX87OrMMXRegister(\n      &st_mm[2], kExponentAllZero, false, kFractionAllZero);  // zero\n  SetX87OrMMXRegister(\n      &st_mm[3], kExponentAllZero, true, kFractionAllZero);  // spec.\n  SetX87OrMMXRegister(\n      &st_mm[4], kExponentAllZero, false, kFractionNormal);  // spec.\n  SetX87OrMMXRegister(\n      &st_mm[5], kExponentAllZero, true, kFractionNormal);  // spec.\n  SetX87OrMMXRegister(&st_mm[6], kExponentAllZero, false, kFractionAllZero);\n  SetX87OrMMXRegister(&st_mm[7], kExponentAllZero, true, kFractionAllZero);\n  EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm),\n            0xa9ff);\n\n  fsw = 5 << 11;  // top = 5: logical 0-7 maps to physical 5-7, 0-4\n  fxsave_tag = 0x5a;  // physical 0, 2, 5, and 7 (logical 5, 0, 2, and 3) empty\n  SetX87OrMMXRegister(&st_mm[0], kExponentAllOne, false, kFractionNormal);\n  SetX87OrMMXRegister(\n      &st_mm[1], kExponentAllOne, true, kFractionNormal);  // spec.\n  SetX87OrMMXRegister(&st_mm[2], kExponentAllOne, false, kFractionAllZero);\n  SetX87OrMMXRegister(&st_mm[3], kExponentAllOne, true, kFractionAllZero);\n  SetX87OrMMXRegister(\n      &st_mm[4], kExponentAllOne, false, kFractionNormal);  // spec.\n  SetX87OrMMXRegister(&st_mm[5], kExponentAllOne, true, kFractionNormal);\n  SetX87OrMMXRegister(\n      &st_mm[6], kExponentAllOne, false, kFractionAllZero);  // spec.\n  SetX87OrMMXRegister(\n      &st_mm[7], kExponentAllOne, true, kFractionAllZero);  // spec.\n  EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm),\n            0xeebb);\n\n  // This set set is just a mix of all of the possible tag types in a single\n  // register file.\n  fsw = 1 << 11;  // top = 1: logical 0-7 maps to physical 1-7, 0\n  fxsave_tag = 0x1f;  // physical 5-7 (logical 4-6) empty\n  SetX87OrMMXRegister(\n      &st_mm[0], kExponentNormal, true, kFractionAllZero);  // valid\n  SetX87OrMMXRegister(\n      &st_mm[1], kExponentAllZero, false, kFractionAllZero);  // zero\n  SetX87OrMMXRegister(\n      &st_mm[2], kExponentAllOne, true, kFractionAllZero);  // spec.\n  SetX87OrMMXRegister(\n      &st_mm[3], kExponentAllOne, true, kFractionNormal);  // spec.\n  SetX87OrMMXRegister(&st_mm[4], kExponentAllZero, false, kFractionAllZero);\n  SetX87OrMMXRegister(&st_mm[5], kExponentAllZero, false, kFractionAllZero);\n  SetX87OrMMXRegister(&st_mm[6], kExponentAllZero, false, kFractionAllZero);\n  SetX87OrMMXRegister(\n      &st_mm[7], kExponentNormal, true, kFractionNormal);  // valid\n  EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm),\n            0xfe90);\n\n  // In this set, everything is valid.\n  fsw = 0 << 11;  // top = 0: logical 0-7 maps to physical 0-7\n  fxsave_tag = 0xff;  // nothing empty\n  for (size_t index = 0; index < std::size(st_mm); ++index) {\n    SetX87OrMMXRegister(&st_mm[index], kExponentNormal, true, kFractionAllZero);\n  }\n  EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), 0);\n\n  // In this set, everything is empty. The registers shouldn’t be consulted at\n  // all, so they’re left alone from the previous set.\n  fsw = 0 << 11;  // top = 0: logical 0-7 maps to physical 0-7\n  fxsave_tag = 0;  // everything empty\n  EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm),\n            0xffff);\n}\n\nTEST(CPUContextX86, FsaveToFxsaveTagWord) {\n  // The register sets that these x87 tag words might apply to are given in the\n  // FxsaveToFsaveTagWord test above.\n  EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xff22), 0x0f);\n  EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xa9ff), 0xf0);\n  EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xeebb), 0x5a);\n  EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xfe90), 0x1f);\n  EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0x0000), 0xff);\n  EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xffff), 0x00);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/crashpad_info_client_options.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/crashpad_info_client_options.h\"\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\n// static\nTriState CrashpadInfoClientOptions::TriStateFromCrashpadInfo(\n    uint8_t crashpad_info_tri_state) {\n  switch (crashpad_info_tri_state) {\n    case static_cast<uint8_t>(TriState::kUnset):\n      return TriState::kUnset;\n    case static_cast<uint8_t>(TriState::kEnabled):\n      return TriState::kEnabled;\n    case static_cast<uint8_t>(TriState::kDisabled):\n      return TriState::kDisabled;\n    default:\n      LOG(WARNING) << \"unknown TriState \"\n                   << static_cast<int>(crashpad_info_tri_state);\n      return TriState::kUnset;\n  }\n}\n\nCrashpadInfoClientOptions::CrashpadInfoClientOptions()\n    : crashpad_handler_behavior(TriState::kUnset),\n      system_crash_reporter_forwarding(TriState::kUnset),\n      gather_indirectly_referenced_memory(TriState::kUnset),\n      indirectly_referenced_memory_cap(0) {\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/crashpad_info_client_options.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_\n#define CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_\n\n#include <stdint.h>\n\n#include \"util/misc/tri_state.h\"\n\nnamespace crashpad {\n\n//! \\brief Options represented in a client’s CrashpadInfo structure.\n//!\n//! The CrashpadInfo structure is not suitable to expose client options in a\n//! generic way at the snapshot level. This structure duplicates option-related\n//! fields from the client structure for general use within the snapshot layer\n//! and by users of this layer.\n//!\n//! For objects of this type corresponding to a module, option values are taken\n//! from the module’s CrashpadInfo structure directly. If the module has no such\n//! structure, option values appear unset.\n//!\n//! For objects of this type corresponding to an entire process, option values\n//! are taken from the CrashpadInfo structures of modules within the process.\n//! The first module found with a set value (enabled or disabled) will provide\n//! an option value for the process. Different modules may provide values for\n//! different options. If no module in the process sets a value for an option,\n//! the option will appear unset for the process. If no module in the process\n//! has a CrashpadInfo structure, all option values will appear unset.\nstruct CrashpadInfoClientOptions {\n public:\n  //! \\brief Converts `uint8_t` value to a TriState value.\n  //!\n  //! The process_types layer exposes TriState as a `uint8_t` rather than an\n  //! enum type. This function converts these values into the equivalent enum\n  //! values used in the snapshot layer.\n  //!\n  //! \\return The TriState equivalent of \\a crashpad_info_tri_state, if it is a\n  //!     valid TriState value. Otherwise, logs a warning and returns\n  //!     TriState::kUnset.\n  static TriState TriStateFromCrashpadInfo(uint8_t crashpad_info_tri_state);\n\n  CrashpadInfoClientOptions();\n\n  //! \\sa CrashpadInfo::set_crashpad_handler_behavior()\n  TriState crashpad_handler_behavior;\n\n  //! \\sa CrashpadInfo::set_system_crash_reporter_forwarding()\n  TriState system_crash_reporter_forwarding;\n\n  //! \\sa CrashpadInfo::set_gather_indirectly_referenced_memory()\n  TriState gather_indirectly_referenced_memory;\n\n  //! \\sa CrashpadInfo::set_gather_indirectly_referenced_memory()\n  uint32_t indirectly_referenced_memory_cap;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_\n"
  },
  {
    "path": "snapshot/crashpad_info_client_options_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/crashpad_info_client_options.h\"\n\n#include \"base/auto_reset.h\"\n#include \"base/files/file_path.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"client/crashpad_info.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_module_handle.h\"\n#include \"test/test_paths.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <dlfcn.h>\n#include \"snapshot/mac/process_snapshot_mac.h\"\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#include \"snapshot/win/process_snapshot_win.h\"\n#elif BUILDFLAG(IS_FUCHSIA)\n#include <lib/zx/process.h>\n#include \"snapshot/fuchsia/process_snapshot_fuchsia.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(CrashpadInfoClientOptions, TriStateFromCrashpadInfo) {\n  EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0),\n            TriState::kUnset);\n  EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(1),\n            TriState::kEnabled);\n  EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(2),\n            TriState::kDisabled);\n\n  // These will produce log messages but should result in kUnset being returned.\n  EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(3),\n            TriState::kUnset);\n  EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(4),\n            TriState::kUnset);\n  EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0xff),\n            TriState::kUnset);\n}\n\nclass ScopedUnsetCrashpadInfoOptions {\n public:\n  explicit ScopedUnsetCrashpadInfoOptions(CrashpadInfo* crashpad_info)\n      : crashpad_info_(crashpad_info) {\n  }\n\n  ScopedUnsetCrashpadInfoOptions(const ScopedUnsetCrashpadInfoOptions&) =\n      delete;\n  ScopedUnsetCrashpadInfoOptions& operator=(\n      const ScopedUnsetCrashpadInfoOptions&) = delete;\n\n  ~ScopedUnsetCrashpadInfoOptions() {\n    crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset);\n    crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset);\n    crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset,\n                                                            0);\n  }\n\n private:\n  CrashpadInfo* crashpad_info_;\n};\n\nCrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() {\n#if BUILDFLAG(IS_APPLE)\n  ProcessSnapshotMac process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(mach_task_self()));\n#elif BUILDFLAG(IS_WIN)\n  ProcessSnapshotWin process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(\n      GetCurrentProcess(), ProcessSuspensionState::kRunning, 0, 0));\n#elif BUILDFLAG(IS_FUCHSIA)\n  ProcessSnapshotFuchsia process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(*zx::process::self()));\n#else\n#error Port.\n#endif  // BUILDFLAG(IS_APPLE)\n\n  CrashpadInfoClientOptions options;\n  process_snapshot.GetCrashpadOptions(&options);\n  return options;\n}\n\nTEST(CrashpadInfoClientOptions, OneModule) {\n  // Make sure that the initial state has all values unset.\n  auto options = SelfProcessSnapshotAndGetCrashpadOptions();\n\n  EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset);\n  EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset);\n  EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n  EXPECT_EQ(options.indirectly_referenced_memory_cap, 0u);\n\n  CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();\n  ASSERT_TRUE(crashpad_info);\n\n  {\n    ScopedUnsetCrashpadInfoOptions unset(crashpad_info);\n\n    crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);\n\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n    EXPECT_EQ(options.indirectly_referenced_memory_cap, 0u);\n  }\n\n  {\n    ScopedUnsetCrashpadInfoOptions unset(crashpad_info);\n\n    crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled);\n\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kDisabled);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n    EXPECT_EQ(options.indirectly_referenced_memory_cap, 0u);\n  }\n\n  {\n    ScopedUnsetCrashpadInfoOptions unset(crashpad_info);\n\n    crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled,\n                                                           1234);\n\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kEnabled);\n    EXPECT_LE(options.indirectly_referenced_memory_cap, 1234u);\n  }\n}\n\nTEST(CrashpadInfoClientOptions, TwoModules) {\n  // Open the module, which has its own CrashpadInfo structure.\n  base::FilePath module_path =\n      TestPaths::BuildArtifact(FILE_PATH_LITERAL(\"snapshot\"),\n                               FILE_PATH_LITERAL(\"module\"),\n                               TestPaths::FileType::kLoadableModule);\n#if BUILDFLAG(IS_POSIX)\n  ScopedModuleHandle module(\n      dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));\n  ASSERT_TRUE(module.valid()) << \"dlopen \" << module_path.value() << \": \"\n                              << dlerror();\n#elif BUILDFLAG(IS_WIN)\n  ScopedModuleHandle module(LoadLibrary(module_path.value().c_str()));\n  ASSERT_TRUE(module.valid())\n      << \"LoadLibrary \" << base::WideToUTF8(module_path.value()) << \": \"\n      << ErrorMessage();\n#else\n#error Port.\n#endif  // BUILDFLAG(IS_POSIX)\n\n  // Get the function pointer from the module. This wraps GetCrashpadInfo(), but\n  // because it runs in the module, it returns the remote module’s CrashpadInfo\n  // structure.\n  CrashpadInfo* (*TestModule_GetCrashpadInfo)() =\n      module.LookUpSymbol<CrashpadInfo* (*)()>(\"TestModule_GetCrashpadInfo\");\n  ASSERT_TRUE(TestModule_GetCrashpadInfo);\n\n  auto options = SelfProcessSnapshotAndGetCrashpadOptions();\n\n  // Make sure that the initial state has all values unset.\n  EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset);\n  EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset);\n  EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n\n  // Get both CrashpadInfo structures.\n  CrashpadInfo* local_crashpad_info = CrashpadInfo::GetCrashpadInfo();\n  ASSERT_TRUE(local_crashpad_info);\n\n  CrashpadInfo* remote_crashpad_info = TestModule_GetCrashpadInfo();\n  ASSERT_TRUE(remote_crashpad_info);\n\n  {\n    ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info);\n    ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info);\n\n    // When only one module sets a value, it applies to the entire process.\n    remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);\n\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n\n    // When more than one module sets a value, the first one in the module list\n    // applies to the process. The local module should appear before the remote\n    // module, because the local module loaded the remote module.\n    local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled);\n\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kDisabled);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n  }\n\n  {\n    ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info);\n    ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info);\n\n    // When only one module sets a value, it applies to the entire process.\n    remote_crashpad_info->set_system_crash_reporter_forwarding(\n        TriState::kDisabled);\n\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kDisabled);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n\n    // When more than one module sets a value, the first one in the module list\n    // applies to the process. The local module should appear before the remote\n    // module, because the local module loaded the remote module.\n    local_crashpad_info->set_system_crash_reporter_forwarding(\n        TriState::kEnabled);\n\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kEnabled);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n  }\n}\n\nclass CrashpadInfoSizes_ClientOptions\n    : public testing::TestWithParam<base::FilePath::StringType> {};\n\n// UBSan detects a function type mismatch when calling\n// TestModule_GetCrashpadInfo since the expected function signature should\n// return a CrashpadInfo* but the actual TestModule_GetCrashpadInfo defined for\n// the test returns a TestCrashpadInfo*. CrashpadInfo is a struct with its\n// members set as private and TestCrashpadInfo is a POD meant to replicate the\n// layout of CrashpadInfo byte-for-byte. Note this is intentional since the\n// whole point of the test is to exercise the snapshot reader’s ability to\n// handle CrashpadInfo.\n#if defined(__clang__)\n[[clang::no_sanitize(\"function\")]]\n#endif\ninline CrashpadInfo*\nCallGetCrashpadInfo(CrashpadInfo* (*func)()) {\n  return func();\n}\n\nTEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) {\n  base::FilePath::StringType artifact(FILE_PATH_LITERAL(\"module_\"));\n  artifact += GetParam();\n\n  // Open the module, which has a CrashpadInfo-like structure that’s smaller or\n  // larger than the current version’s CrashpadInfo structure defined in the\n  // client library.\n  base::FilePath module_path =\n      TestPaths::BuildArtifact(FILE_PATH_LITERAL(\"snapshot\"),\n                               artifact,\n                               TestPaths::FileType::kLoadableModule);\n#if BUILDFLAG(IS_POSIX)\n  ScopedModuleHandle module(\n      dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));\n  ASSERT_TRUE(module.valid())\n      << \"dlopen \" << module_path.value() << \": \" << dlerror();\n#elif BUILDFLAG(IS_WIN)\n  ScopedModuleHandle module(LoadLibrary(module_path.value().c_str()));\n  ASSERT_TRUE(module.valid())\n      << \"LoadLibrary \" << base::WideToUTF8(module_path.value()) << \": \"\n      << ErrorMessage();\n#else\n#error Port.\n#endif  // BUILDFLAG(IS_POSIX)\n\n  // Get the function pointer from the module.\n  CrashpadInfo* (*TestModule_GetCrashpadInfo)() =\n      module.LookUpSymbol<CrashpadInfo* (*)()>(\"TestModule_GetCrashpadInfo\");\n  ASSERT_TRUE(TestModule_GetCrashpadInfo);\n\n  auto options = SelfProcessSnapshotAndGetCrashpadOptions();\n\n  // Make sure that the initial state has all values unset.\n  EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset);\n  EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset);\n  EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n\n  // Get the remote CrashpadInfo structure.\n  CrashpadInfo* remote_crashpad_info =\n      CallGetCrashpadInfo(TestModule_GetCrashpadInfo);\n  ASSERT_TRUE(remote_crashpad_info);\n\n  {\n    ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info);\n\n    // Make sure that a change in the remote structure can be read back out,\n    // even though it’s a different size.\n    remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);\n    remote_crashpad_info->set_system_crash_reporter_forwarding(\n        TriState::kDisabled);\n\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kDisabled);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n  }\n\n  {\n    ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info);\n\n    // Make sure that the portion of the remote structure lying beyond its\n    // declared size reads as zero.\n\n    // 4 = offsetof(CrashpadInfo, size_), but it’s private.\n    uint32_t* size = reinterpret_cast<uint32_t*>(\n        reinterpret_cast<char*>(remote_crashpad_info) + 4);\n\n    // 21 = offsetof(CrashpadInfo, system_crash_reporter_forwarding_, but it’s\n    // private.\n    base::AutoReset<uint32_t> reset_size(size, 21);\n\n    // system_crash_reporter_forwarding_ is now beyond the struct’s declared\n    // size. Storage has actually been allocated for it, so it’s safe to set\n    // here.\n    remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);\n    remote_crashpad_info->set_system_crash_reporter_forwarding(\n        TriState::kDisabled);\n\n    // Since system_crash_reporter_forwarding_ is beyond the struct’s declared\n    // size, it should read as 0 (TriState::kUnset), even though it was set to\n    // a different value above.\n    options = SelfProcessSnapshotAndGetCrashpadOptions();\n    EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled);\n    EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset);\n    EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset);\n  }\n}\n\nINSTANTIATE_TEST_SUITE_P(CrashpadInfoSizes_ClientOptions,\n                         CrashpadInfoSizes_ClientOptions,\n                         testing::Values(FILE_PATH_LITERAL(\"small\"),\n                                         FILE_PATH_LITERAL(\"large\")));\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/crashpad_info_client_options_test_module.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"build/build_config.h\"\n#include \"client/crashpad_info.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#define EXPORT __attribute__((visibility(\"default\")))\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#define EXPORT __declspec(dllexport)\n#endif  // BUILDFLAG(IS_POSIX)\n\nextern \"C\" {\n\n// Returns the module’s CrashpadInfo structure. Assuming that this file is built\n// into a loadable_module with a distinct static copy of the Crashpad client\n// library from the copy built into the loader of this loadable_module, this\n// will return a different CrashpadInfo structure than the one that the loader\n// uses. Having an extra CrashpadInfo structure makes it possible to test\n// behaviors that are relevant in the presence of multiple Crashpad\n// client-enabled modules.\n//\n// This function is used by the CrashpadInfoClientOptions.TwoModules test in\n// crashpad_info_client_options_test.cc.\nEXPORT crashpad::CrashpadInfo* TestModule_GetCrashpadInfo() {\n  return crashpad::CrashpadInfo::GetCrashpadInfo();\n}\n\n}  // extern \"C\"\n\n#if BUILDFLAG(IS_WIN)\nBOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) {\n  return TRUE;\n}\n#endif  // BUILDFLAG(IS_WIN)\n"
  },
  {
    "path": "snapshot/crashpad_info_size_test_module.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdint.h>\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <mach-o/loader.h>\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif  // BUILDFLAG(IS_APPLE)\n\nnamespace crashpad {\n\n#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) == \\\n    defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE)\n#error Define exactly one of these macros\n#endif\n\n// This module contains a CrashpadInfo structure that’s either smaller or larger\n// than the one defined in the client library, depending on which macro is\n// defined when it’s compiled. This tests the snapshot layer’s ability to read\n// smaller structures (as might be found in modules built with older versions of\n// the client library than a handler’s snapshot library) and larger ones (the\n// “vice-versa” situation). This needs to be done without taking a dependency on\n// the client library, which would bring with it a correct copy of the\n// CrashpadInfo structure. As a result, all types have been simplified to\n// fixed-size integers and void* pointers.\nstruct TestCrashpadInfo {\n  uint32_t signature_;\n  uint32_t size_;\n  uint32_t version_;\n  uint32_t indirectly_referenced_memory_cap_;\n  uint32_t padding_0_;\n  uint8_t crashpad_handler_behavior_;\n  uint8_t system_crash_reporter_forwarding_;\n  uint8_t gather_indirectly_referenced_memory_;\n  uint8_t padding_1_;\n  void* extra_memory_ranges_;\n  void* simple_annotations_;\n#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL)\n  void* user_data_minidump_stream_head_;\n  void* annotations_list_;\n#if BUILDFLAG(IS_IOS)\n  void* intermediate_dump_extra_memory_ranges_;\n#endif  // BUILDFLAG(IS_IOS)\n#endif  // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL\n#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE)\n  uint8_t trailer_[64 * 1024];\n#endif  // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE\n};\n\n// Put it in the correct section.\n//\n// The initializer also duplicates constants from the client library, sufficient\n// to get this test version to be interpreted as a genuine CrashpadInfo\n// structure. The size is set to the actual size of this structure (that’s kind\n// of the point of this test).\n#if BUILDFLAG(IS_POSIX)\n__attribute__((\n#if BUILDFLAG(IS_APPLE)\n    section(SEG_DATA \",crashpad_info\"),\n#endif\n#if defined(ADDRESS_SANITIZER)\n    aligned(64),\n#endif  // defined(ADDRESS_SANITIZER)\n    visibility(\"hidden\"),\n    used))\n#elif BUILDFLAG(IS_WIN)\n#pragma section(\"CPADinfo\", read, write)\n__declspec(allocate(\"CPADinfo\"))\n#else  // !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_WIN)\n#error Port\n#endif  // !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_WIN)\nTestCrashpadInfo g_test_crashpad_info = {'CPad',\n                                         sizeof(TestCrashpadInfo),\n                                         1,\n                                         0,\n                                         0,\n                                         0,\n                                         0,\n                                         0,\n                                         0,\n                                         nullptr,\n                                         nullptr,\n#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL)\n                                         nullptr,\n                                         nullptr,\n#if BUILDFLAG(IS_IOS)\n                                         nullptr,\n#endif  // BUILDFLAG(IS_IOS)\n\n#endif  // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL\n#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE)\n                                         {}\n#endif  // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE\n};\n\n}  // namespace crashpad\n\nextern \"C\" {\n\n#if BUILDFLAG(IS_POSIX)\n__attribute__((visibility(\"default\")))\n#elif BUILDFLAG(IS_WIN)\n__declspec(dllexport)\n#else\n#error Port\n#endif  // BUILDFLAG(IS_POSIX)\ncrashpad::TestCrashpadInfo*\nTestModule_GetCrashpadInfo() {\n  // Note that there's no need to do the back-reference here to the note on\n  // POSIX like CrashpadInfo::GetCrashpadInfo() because the note .S file is\n  // directly included into this test binary.\n  return &crashpad::g_test_crashpad_info;\n}\n\n}  // extern \"C\"\n\n#if BUILDFLAG(IS_WIN)\nBOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) {\n  return TRUE;\n}\n#endif  // BUILDFLAG(IS_WIN)\n"
  },
  {
    "path": "snapshot/crashpad_info_size_test_note.S",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This note section is used on ELF platforms to give ElfImageReader a method\n// of finding the instance of CrashpadInfo g_crashpad_info without requiring\n// that symbol to be in the dynamic symbol table.\n\n#include \"util/misc/elf_note_types.h\"\n#include \"util/misc/arm64_pac_bti.S\"\n\n// namespace crashpad {\n// CrashpadInfo g_test_crashpad_info;\n// }  // namespace crashpad\n#define TEST_CRASHPAD_INFO_SYMBOL _ZN8crashpad20g_test_crashpad_infoE\n\n#define NOTE_ALIGN 4\n\n  // This section must be \"a\"llocated so that it appears in the final binary at\n  // runtime. The reference to TEST_CRASHPAD_INFO_SYMBOL uses an offset relative\n  // to this note to avoid making this note writable, which triggers a bug in\n  // GNU ld, or adding text relocations which require the target system to allow\n  // making text segments writable. https://crbug.com/crashpad/260.\n  .section .note.crashpad.info,\"a\",%note\n  .balign NOTE_ALIGN\n  .type info_size_test_note, %object\ninfo_size_test_note:\n  .long name_end - name  // namesz\n  .long desc_end - desc  // descsz\n  .long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO  // type\nname:\n  .asciz CRASHPAD_ELF_NOTE_NAME\nname_end:\n  .balign NOTE_ALIGN\ndesc:\n#if defined(__LP64__)\n  .quad TEST_CRASHPAD_INFO_SYMBOL - desc\n#else\n  .long TEST_CRASHPAD_INFO_SYMBOL - desc\n#endif  // __LP64__\ndesc_end:\n  .size info_size_test_note, .-info_size_test_note\n"
  },
  {
    "path": "snapshot/crashpad_types/crashpad_info_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/crashpad_types/crashpad_info_reader.h\"\n\n#include <type_traits>\n\n#include \"build/build_config.h\"\n#include \"client/crashpad_info.h\"\n#include \"util/misc/as_underlying_type.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include \"util/win/traits.h\"\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include \"util/linux/traits.h\"\n#elif BUILDFLAG(IS_FUCHSIA)\n#include \"util/fuchsia/traits.h\"\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\nvoid UnsetIfNotValidTriState(TriState* value) {\n  switch (AsUnderlyingType(*value)) {\n    case AsUnderlyingType(TriState::kUnset):\n    case AsUnderlyingType(TriState::kEnabled):\n    case AsUnderlyingType(TriState::kDisabled):\n      return;\n  }\n  LOG(WARNING) << \"Unsetting invalid TriState \" << AsUnderlyingType(*value);\n  *value = TriState::kUnset;\n}\n\n}  // namespace\n\nclass CrashpadInfoReader::InfoContainer {\n public:\n  virtual ~InfoContainer() = default;\n\n  virtual bool Read(const ProcessMemoryRange* memory, VMAddress address) = 0;\n\n protected:\n  InfoContainer() = default;\n};\n\ntemplate <class Traits>\nclass CrashpadInfoReader::InfoContainerSpecific : public InfoContainer {\n public:\n  InfoContainerSpecific() : InfoContainer() {}\n  ~InfoContainerSpecific() override = default;\n\n  bool Read(const ProcessMemoryRange* memory, VMAddress address) override {\n    if (!memory->Read(address,\n                      offsetof(decltype(info), size) + sizeof(info.size),\n                      &info)) {\n      return false;\n    }\n\n    if (info.signature != CrashpadInfo::kSignature) {\n      LOG(ERROR) << \"invalid signature 0x\" << std::hex << info.signature;\n      return false;\n    }\n\n    if (!memory->Read(\n            address, std::min<VMSize>(info.size, sizeof(info)), &info)) {\n      return false;\n    }\n\n    if (info.size > sizeof(info)) {\n      LOG(INFO) << \"large crashpad info size \" << info.size;\n    }\n\n    if (info.version != 1) {\n      LOG(ERROR) << \"unexpected version \" << info.version;\n      return false;\n    }\n\n    if (sizeof(info) > info.size) {\n      memset(reinterpret_cast<char*>(&info) + info.size,\n             0,\n             sizeof(info) - info.size);\n    }\n\n    UnsetIfNotValidTriState(&info.crashpad_handler_behavior);\n    UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding);\n    UnsetIfNotValidTriState(&info.gather_indirectly_referenced_memory);\n\n    return true;\n  }\n\n  struct {\n    uint32_t signature;\n    uint32_t size;\n    uint32_t version;\n    uint32_t indirectly_referenced_memory_cap;\n    uint32_t padding_0;\n    TriState crashpad_handler_behavior;\n    TriState system_crash_reporter_forwarding;\n    TriState gather_indirectly_referenced_memory;\n    uint8_t padding_1;\n    typename Traits::Address extra_memory_ranges;\n    typename Traits::Address simple_annotations;\n    typename Traits::Address user_data_minidump_stream_head;\n    typename Traits::Address annotations_list;\n  } info;\n\n#if defined(ARCH_CPU_64_BITS)\n#define NATIVE_TRAITS Traits64\n#else\n#define NATIVE_TRAITS Traits32\n#endif\n  static_assert(!std::is_same<Traits, NATIVE_TRAITS>::value ||\n                    sizeof(decltype(info)) == sizeof(CrashpadInfo),\n                \"CrashpadInfo size mismtach\");\n#undef NATIVE_TRAITS\n};\n\nCrashpadInfoReader::CrashpadInfoReader()\n    : container_(), is_64_bit_(false), initialized_() {}\n\nCrashpadInfoReader::~CrashpadInfoReader() = default;\n\nbool CrashpadInfoReader::Initialize(const ProcessMemoryRange* memory,\n                                    VMAddress address) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  is_64_bit_ = memory->Is64Bit();\n\n  std::unique_ptr<InfoContainer> new_container;\n  if (is_64_bit_) {\n    new_container = std::make_unique<InfoContainerSpecific<Traits64>>();\n  } else {\n    new_container = std::make_unique<InfoContainerSpecific<Traits32>>();\n  }\n\n  if (!new_container->Read(memory, address)) {\n    return false;\n  }\n  container_ = std::move(new_container);\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\n#define GET_MEMBER(name)                                                      \\\n  (is_64_bit_                                                                 \\\n       ? reinterpret_cast<InfoContainerSpecific<Traits64>*>(container_.get()) \\\n             ->info.name                                                      \\\n       : reinterpret_cast<InfoContainerSpecific<Traits32>*>(container_.get()) \\\n             ->info.name)\n\n#define DEFINE_GETTER(type, method, member)          \\\n  type CrashpadInfoReader::method() {                \\\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_); \\\n    return GET_MEMBER(member);                       \\\n  }\n\nDEFINE_GETTER(TriState, CrashpadHandlerBehavior, crashpad_handler_behavior)\n\nDEFINE_GETTER(TriState,\n              SystemCrashReporterForwarding,\n              system_crash_reporter_forwarding)\n\nDEFINE_GETTER(TriState,\n              GatherIndirectlyReferencedMemory,\n              gather_indirectly_referenced_memory)\n\nDEFINE_GETTER(uint32_t,\n              IndirectlyReferencedMemoryCap,\n              indirectly_referenced_memory_cap)\n\nDEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges)\n\nDEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations)\n\nDEFINE_GETTER(VMAddress, AnnotationsList, annotations_list)\n\nDEFINE_GETTER(VMAddress,\n              UserDataMinidumpStreamHead,\n              user_data_minidump_stream_head)\n\n#undef DEFINE_GETTER\n#undef GET_MEMBER\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/crashpad_types/crashpad_info_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_\n#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_\n\n#include <stdint.h>\n\n#include <memory>\n\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/tri_state.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n//! \\brief Reads CrashpadInfo structs from another process via a\n//!     ProcessMemoryRange.\nclass CrashpadInfoReader {\n public:\n  CrashpadInfoReader();\n\n  CrashpadInfoReader(const CrashpadInfoReader&) = delete;\n  CrashpadInfoReader& operator=(const CrashpadInfoReader&) = delete;\n\n  ~CrashpadInfoReader();\n\n  //! \\brief Initializes this object.\n  //!\n  //! This method must be successfully called bfore any other method in this\n  //! class.\n  //!\n  //! \\param[in] memory The reader for the remote process.\n  //! \\param[in] address The address in the remote process' address space of a\n  //!     CrashpadInfo struct.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Initialize(const ProcessMemoryRange* memory, VMAddress address);\n\n  //! \\{\n  //! \\see CrashpadInfo\n  TriState CrashpadHandlerBehavior();\n  TriState SystemCrashReporterForwarding();\n  TriState GatherIndirectlyReferencedMemory();\n  uint32_t IndirectlyReferencedMemoryCap();\n  VMAddress ExtraMemoryRanges();\n  VMAddress SimpleAnnotations();\n  VMAddress AnnotationsList();\n  VMAddress UserDataMinidumpStreamHead();\n  //! \\}\n\n private:\n  class InfoContainer;\n\n  template <typename Traits>\n  class InfoContainerSpecific;\n\n  std::unique_ptr<InfoContainer> container_;\n  bool is_64_bit_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_\n"
  },
  {
    "path": "snapshot/crashpad_types/crashpad_info_reader_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/crashpad_types/crashpad_info_reader.h\"\n\n#include <sys/types.h>\n\n#include <memory>\n\n#include \"build/build_config.h\"\n#include \"client/annotation_list.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simple_address_range_bag.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"gtest/gtest.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"test/process_type.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/process/process_memory_native.h\"\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#include \"test/linux/fake_ptrace_connection.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr TriState kCrashpadHandlerBehavior = TriState::kEnabled;\nconstexpr TriState kSystemCrashReporterForwarding = TriState::kDisabled;\nconstexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset;\n\nconstexpr uint32_t kIndirectlyReferencedMemoryCap = 42;\n\nclass ScopedUnsetCrashpadInfo {\n public:\n  explicit ScopedUnsetCrashpadInfo(CrashpadInfo* crashpad_info)\n      : crashpad_info_(crashpad_info) {}\n\n  ScopedUnsetCrashpadInfo(const ScopedUnsetCrashpadInfo&) = delete;\n  ScopedUnsetCrashpadInfo& operator=(const ScopedUnsetCrashpadInfo&) = delete;\n\n  ~ScopedUnsetCrashpadInfo() {\n    crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset);\n    crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset);\n    crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset,\n                                                            0);\n    crashpad_info_->set_extra_memory_ranges(nullptr);\n    crashpad_info_->set_simple_annotations(nullptr);\n    crashpad_info_->set_annotations_list(nullptr);\n  }\n\n private:\n  CrashpadInfo* crashpad_info_;\n};\n\nclass CrashpadInfoTestDataSetup {\n public:\n  CrashpadInfoTestDataSetup() {\n    CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo();\n    unset_.reset(new ScopedUnsetCrashpadInfo(info));\n\n    info->set_extra_memory_ranges(&extra_memory_);\n    info->set_simple_annotations(&simple_annotations_);\n    info->set_annotations_list(&annotation_list_);\n    info->set_crashpad_handler_behavior(kCrashpadHandlerBehavior);\n    info->set_system_crash_reporter_forwarding(kSystemCrashReporterForwarding);\n    info->set_gather_indirectly_referenced_memory(\n        kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap);\n  }\n\n  CrashpadInfoTestDataSetup(const CrashpadInfoTestDataSetup&) = delete;\n  CrashpadInfoTestDataSetup& operator=(const CrashpadInfoTestDataSetup&) =\n      delete;\n\n  void GetAddresses(VMAddress* info_address,\n                    VMAddress* extra_memory_address,\n                    VMAddress* simple_annotations_address,\n                    VMAddress* annotations_list_address) {\n    *info_address = FromPointerCast<VMAddress>(CrashpadInfo::GetCrashpadInfo());\n    *extra_memory_address = FromPointerCast<VMAddress>(&extra_memory_);\n    *simple_annotations_address =\n        FromPointerCast<VMAddress>(&simple_annotations_);\n    *annotations_list_address = FromPointerCast<VMAddress>(&annotation_list_);\n  }\n\n private:\n  std::unique_ptr<ScopedUnsetCrashpadInfo> unset_;\n  SimpleAddressRangeBag extra_memory_;\n  SimpleStringDictionary simple_annotations_;\n  AnnotationList annotation_list_;\n};\n\nvoid ExpectCrashpadInfo(ProcessType process,\n                        bool is_64_bit,\n                        VMAddress info_address,\n                        VMAddress extra_memory_address,\n                        VMAddress simple_annotations_address,\n                        VMAddress annotations_list_address) {\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(process));\n  ProcessMemoryLinux memory(&connection);\n#else\n  ProcessMemoryNative memory;\n  ASSERT_TRUE(memory.Initialize(process));\n#endif\n\n  ProcessMemoryRange range;\n  ASSERT_TRUE(range.Initialize(&memory, is_64_bit));\n\n  CrashpadInfoReader reader;\n  ASSERT_TRUE(reader.Initialize(&range, info_address));\n  EXPECT_EQ(reader.CrashpadHandlerBehavior(), kCrashpadHandlerBehavior);\n  EXPECT_EQ(reader.SystemCrashReporterForwarding(),\n            kSystemCrashReporterForwarding);\n  EXPECT_EQ(reader.GatherIndirectlyReferencedMemory(),\n            kGatherIndirectlyReferencedMemory);\n  EXPECT_EQ(reader.IndirectlyReferencedMemoryCap(),\n            kIndirectlyReferencedMemoryCap);\n  EXPECT_EQ(reader.ExtraMemoryRanges(), extra_memory_address);\n  EXPECT_EQ(reader.SimpleAnnotations(), simple_annotations_address);\n  EXPECT_EQ(reader.AnnotationsList(), annotations_list_address);\n}\n\nTEST(CrashpadInfoReader, ReadFromSelf) {\n#if defined(ARCH_CPU_64_BITS)\n  constexpr bool am_64_bit = true;\n#else\n  constexpr bool am_64_bit = false;\n#endif\n\n  CrashpadInfoTestDataSetup test_data_setup;\n  VMAddress info_address;\n  VMAddress extra_memory_address;\n  VMAddress simple_annotations_address;\n  VMAddress annotations_list_address;\n  test_data_setup.GetAddresses(&info_address,\n                               &extra_memory_address,\n                               &simple_annotations_address,\n                               &annotations_list_address);\n  ExpectCrashpadInfo(GetSelfProcess(),\n                     am_64_bit,\n                     info_address,\n                     extra_memory_address,\n                     simple_annotations_address,\n                     annotations_list_address);\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ReadFromChildTestMain) {\n  CrashpadInfoTestDataSetup test_data_setup;\n  VMAddress info_address;\n  VMAddress extra_memory_address;\n  VMAddress simple_annotations_address;\n  VMAddress annotations_list_address;\n  test_data_setup.GetAddresses(&info_address,\n                               &extra_memory_address,\n                               &simple_annotations_address,\n                               &annotations_list_address);\n\n  FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);\n  CheckedWriteFile(out, &info_address, sizeof(info_address));\n  CheckedWriteFile(out, &extra_memory_address, sizeof(extra_memory_address));\n  CheckedWriteFile(\n      out, &simple_annotations_address, sizeof(simple_annotations_address));\n  CheckedWriteFile(\n      out, &annotations_list_address, sizeof(annotations_list_address));\n  CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));\n  return 0;\n}\n\nclass ReadFromChildTest : public MultiprocessExec {\n public:\n  ReadFromChildTest() : MultiprocessExec() {\n    SetChildTestMainFunction(\"ReadFromChildTestMain\");\n  }\n\n  ReadFromChildTest(const ReadFromChildTest&) = delete;\n  ReadFromChildTest& operator=(const ReadFromChildTest&) = delete;\n\n  ~ReadFromChildTest() = default;\n\n private:\n  void MultiprocessParent() {\n#if defined(ARCH_CPU_64_BITS)\n    constexpr bool am_64_bit = true;\n#else\n    constexpr bool am_64_bit = false;\n#endif\n\n    VMAddress info_address;\n    VMAddress extra_memory_address;\n    VMAddress simple_annotations_address;\n    VMAddress annotations_list_address;\n    ASSERT_TRUE(\n        ReadFileExactly(ReadPipeHandle(), &info_address, sizeof(info_address)));\n    ASSERT_TRUE(ReadFileExactly(\n        ReadPipeHandle(), &extra_memory_address, sizeof(extra_memory_address)));\n    ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(),\n                                &simple_annotations_address,\n                                sizeof(simple_annotations_address)));\n    ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(),\n                                &annotations_list_address,\n                                sizeof(annotations_list_address)));\n    ExpectCrashpadInfo(ChildProcess(),\n                       am_64_bit,\n                       info_address,\n                       extra_memory_address,\n                       simple_annotations_address,\n                       annotations_list_address);\n  }\n};\n\nTEST(CrashpadInfoReader, ReadFromChild) {\n  ReadFromChildTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/crashpad_types/image_annotation_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/crashpad_types/image_annotation_reader.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <utility>\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"client/annotation.h\"\n#include \"client/annotation_list.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"snapshot/snapshot_constants.h\"\n#if BUILDFLAG(IS_FUCHSIA)\n#include \"util/fuchsia/traits.h\"\n#else\n#include \"util/linux/traits.h\"\n#endif\n\nnamespace crashpad {\n\nnamespace process_types {\n\ntemplate <class Traits>\nstruct Annotation {\n  typename Traits::Address link_node;\n  typename Traits::Address name;\n  typename Traits::Address value;\n  uint32_t size;\n  uint16_t type;\n  crashpad::Annotation::ConcurrentAccessGuardMode concurrent_access_guard_mode;\n  bool spin_guard_state;\n};\n\ntemplate <class Traits>\nstruct AnnotationList {\n  typename Traits::Address tail_pointer;\n  Annotation<Traits> head;\n  Annotation<Traits> tail;\n};\n\n}  // namespace process_types\n\n#if defined(ARCH_CPU_64_BITS)\n#define NATIVE_TRAITS Traits64\n#else\n#define NATIVE_TRAITS Traits32\n#endif  // ARCH_CPU_64_BITS\n\nstatic_assert(sizeof(process_types::Annotation<NATIVE_TRAITS>) ==\n                  sizeof(Annotation),\n              \"Annotation size mismatch\");\n\nstatic_assert(sizeof(process_types::AnnotationList<NATIVE_TRAITS>) ==\n                  sizeof(AnnotationList),\n              \"AnnotationList size mismatch\");\n\n#undef NATIVE_TRAITS\n\nImageAnnotationReader::ImageAnnotationReader(const ProcessMemoryRange* memory)\n    : memory_(memory) {}\n\nImageAnnotationReader::~ImageAnnotationReader() = default;\n\nbool ImageAnnotationReader::SimpleMap(\n    VMAddress address,\n    std::map<std::string, std::string>* annotations) const {\n  std::vector<SimpleStringDictionary::Entry> simple_annotations(\n      SimpleStringDictionary::num_entries);\n\n  if (!memory_->Read(address,\n                     simple_annotations.size() * sizeof(simple_annotations[0]),\n                     &simple_annotations[0])) {\n    return false;\n  }\n\n  for (const auto& entry : simple_annotations) {\n    size_t key_length = strnlen(entry.key, sizeof(entry.key));\n    if (key_length) {\n      std::string key(entry.key, key_length);\n      std::string value(entry.value, strnlen(entry.value, sizeof(entry.value)));\n      if (!annotations->insert(std::make_pair(key, value)).second) {\n        LOG(WARNING) << \"duplicate simple annotation \" << key << \" \" << value;\n      }\n    }\n  }\n  return true;\n}\n\nbool ImageAnnotationReader::AnnotationsList(\n    VMAddress address,\n    std::vector<AnnotationSnapshot>* annotations) const {\n  return memory_->Is64Bit()\n             ? ReadAnnotationList<Traits64>(address, annotations)\n             : ReadAnnotationList<Traits32>(address, annotations);\n}\n\ntemplate <class Traits>\nbool ImageAnnotationReader::ReadAnnotationList(\n    VMAddress address,\n    std::vector<AnnotationSnapshot>* annotations) const {\n  process_types::AnnotationList<Traits> annotation_list;\n  if (!memory_->Read(address, sizeof(annotation_list), &annotation_list)) {\n    LOG(ERROR) << \"could not read annotation list\";\n    return false;\n  }\n\n  process_types::Annotation<Traits> current = annotation_list.head;\n  for (size_t index = 0; current.link_node != annotation_list.tail_pointer &&\n                         index < kMaxNumberOfAnnotations;\n       ++index) {\n    if (!memory_->Read(current.link_node, sizeof(current), &current)) {\n      LOG(ERROR) << \"could not read annotation at index \" << index;\n      return false;\n    }\n\n    if (current.size == 0) {\n      continue;\n    }\n\n    AnnotationSnapshot snapshot;\n    snapshot.type = current.type;\n\n    if (!memory_->ReadCStringSizeLimited(\n            current.name, Annotation::kNameMaxLength, &snapshot.name)) {\n      LOG(WARNING) << \"could not read annotation name at index \" << index;\n      continue;\n    }\n\n    size_t value_length =\n        std::min(static_cast<size_t>(current.size), Annotation::kValueMaxSize);\n    snapshot.value.resize(value_length);\n    if (!memory_->Read(current.value, value_length, snapshot.value.data())) {\n      LOG(WARNING) << \"could not read annotation value at index \" << index;\n      continue;\n    }\n\n    annotations->push_back(std::move(snapshot));\n  }\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/crashpad_types/image_annotation_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_\n#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"snapshot/annotation_snapshot.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n//! \\brief Reads Annotations from another process via a ProcessMemoryRange.\n//!\n//! These annotations are stored for the benefit of crash reporters, and provide\n//! information thought to be potentially useful for crash analysis.\nclass ImageAnnotationReader {\n public:\n  //! \\brief Constructs the object.\n  //!\n  //! \\param[in] memory A memory reader for the remote process.\n  explicit ImageAnnotationReader(const ProcessMemoryRange* memory);\n\n  ImageAnnotationReader(const ImageAnnotationReader&) = delete;\n  ImageAnnotationReader& operator=(const ImageAnnotationReader&) = delete;\n\n  ~ImageAnnotationReader();\n\n  //! \\brief Reads annotations that are organized as key-value pairs, where all\n  //!     keys and values are strings.\n  //!\n  //! \\param[in] address The address in the target process' address space of a\n  //!     SimpleStringDictionary containing the annotations to read.\n  //! \\param[out] annotations The annotations read, valid if this method\n  //!     returns `true`.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool SimpleMap(VMAddress address,\n                 std::map<std::string, std::string>* annotations) const;\n\n  //! \\brief Reads the module's annotations that are organized as a list of\n  //!     typed annotation objects.\n  //!\n  //! \\param[in] address The address in the target process' address space of an\n  //!     AnnotationList.\n  //! \\param[out] annotations The annotations read, valid if this method returns\n  //!     `true`.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool AnnotationsList(VMAddress,\n                       std::vector<AnnotationSnapshot>* annotations) const;\n\n private:\n  template <class Traits>\n  bool ReadAnnotationList(VMAddress address,\n                          std::vector<AnnotationSnapshot>* annotations) const;\n\n  const ProcessMemoryRange* memory_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_\n"
  },
  {
    "path": "snapshot/crashpad_types/image_annotation_reader_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/crashpad_types/image_annotation_reader.h\"\n\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n\n#include \"build/build_config.h\"\n#include \"client/annotation.h\"\n#include \"client/annotation_list.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"gtest/gtest.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"test/process_type.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/as_underlying_type.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/process/process_memory_native.h\"\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#include \"test/linux/fake_ptrace_connection.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid ExpectSimpleMap(const std::map<std::string, std::string>& map,\n                     const SimpleStringDictionary& expected_map) {\n  EXPECT_EQ(map.size(), expected_map.GetCount());\n  for (const auto& pair : map) {\n    EXPECT_EQ(pair.second, expected_map.GetValueForKey(pair.first));\n  }\n}\n\nvoid ExpectAnnotationList(const std::vector<AnnotationSnapshot>& list,\n                          AnnotationList& expected_list) {\n  size_t index = 0;\n  for (const Annotation* expected_annotation : expected_list) {\n    const AnnotationSnapshot& annotation = list[index++];\n    EXPECT_EQ(annotation.name, expected_annotation->name());\n    EXPECT_EQ(annotation.type, AsUnderlyingType(expected_annotation->type()));\n    EXPECT_EQ(annotation.value.size(), expected_annotation->size());\n    EXPECT_EQ(memcmp(annotation.value.data(),\n                     expected_annotation->value(),\n                     std::min(VMSize{annotation.value.size()},\n                              VMSize{expected_annotation->size()})),\n              0);\n  }\n}\n\nvoid BuildTestStructures(\n    std::vector<std::unique_ptr<Annotation>>* annotations_storage,\n    SimpleStringDictionary* into_map,\n    AnnotationList* into_annotation_list) {\n  into_map->SetKeyValue(\"key\", \"value\");\n  into_map->SetKeyValue(\"key2\", \"value2\");\n\n  static constexpr char kAnnotationName[] = \"test annotation\";\n  static constexpr char kAnnotationValue[] = \"test annotation value\";\n  annotations_storage->push_back(std::make_unique<Annotation>(\n      Annotation::Type::kString,\n      kAnnotationName,\n      reinterpret_cast<void*>(const_cast<char*>(kAnnotationValue))));\n  annotations_storage->back()->SetSize(sizeof(kAnnotationValue));\n  into_annotation_list->Add(annotations_storage->back().get());\n\n  static constexpr char kAnnotationName2[] = \"test annotation2\";\n  static constexpr char kAnnotationValue2[] = \"test annotation value2\";\n  annotations_storage->push_back(std::make_unique<Annotation>(\n      Annotation::Type::kString,\n      kAnnotationName2,\n      reinterpret_cast<void*>(const_cast<char*>(kAnnotationValue2))));\n  annotations_storage->back()->SetSize(sizeof(kAnnotationValue2));\n  into_annotation_list->Add(annotations_storage->back().get());\n}\n\nvoid ExpectAnnotations(ProcessType process,\n                       bool is_64_bit,\n                       VMAddress simple_map_address,\n                       VMAddress annotation_list_address) {\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(process));\n  ProcessMemoryLinux memory(&connection);\n#else\n  ProcessMemoryNative memory;\n  ASSERT_TRUE(memory.Initialize(process));\n#endif\n\n  ProcessMemoryRange range;\n  ASSERT_TRUE(range.Initialize(&memory, is_64_bit));\n\n  SimpleStringDictionary expected_simple_map;\n  std::vector<std::unique_ptr<Annotation>> storage;\n  AnnotationList expected_annotations;\n  BuildTestStructures(&storage, &expected_simple_map, &expected_annotations);\n\n  ImageAnnotationReader reader(&range);\n\n  std::map<std::string, std::string> simple_map;\n  ASSERT_TRUE(reader.SimpleMap(simple_map_address, &simple_map));\n  ExpectSimpleMap(simple_map, expected_simple_map);\n\n  std::vector<AnnotationSnapshot> annotation_list;\n  ASSERT_TRUE(\n      reader.AnnotationsList(annotation_list_address, &annotation_list));\n  ExpectAnnotationList(annotation_list, expected_annotations);\n}\n\nTEST(ImageAnnotationReader, ReadFromSelf) {\n  SimpleStringDictionary map;\n  std::vector<std::unique_ptr<Annotation>> storage;\n  AnnotationList annotations;\n  BuildTestStructures(&storage, &map, &annotations);\n\n#if defined(ARCH_CPU_64_BITS)\n  constexpr bool am_64_bit = true;\n#else\n  constexpr bool am_64_bit = false;\n#endif\n\n  ExpectAnnotations(GetSelfProcess(),\n                    am_64_bit,\n                    FromPointerCast<VMAddress>(&map),\n                    FromPointerCast<VMAddress>(&annotations));\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ReadAnnotationsFromChildTestMain) {\n  SimpleStringDictionary map;\n  std::vector<std::unique_ptr<Annotation>> storage;\n  AnnotationList annotations;\n  BuildTestStructures(&storage, &map, &annotations);\n\n  VMAddress simple_map_address = FromPointerCast<VMAddress>(&map);\n  VMAddress annotations_address = FromPointerCast<VMAddress>(&annotations);\n  FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);\n  CheckedWriteFile(out, &simple_map_address, sizeof(simple_map_address));\n  CheckedWriteFile(out, &annotations_address, sizeof(annotations_address));\n\n  CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));\n  return 0;\n}\n\nclass ReadFromChildTest : public MultiprocessExec {\n public:\n  ReadFromChildTest() : MultiprocessExec() {\n    SetChildTestMainFunction(\"ReadAnnotationsFromChildTestMain\");\n  }\n\n  ReadFromChildTest(const ReadFromChildTest&) = delete;\n  ReadFromChildTest& operator=(const ReadFromChildTest&) = delete;\n\n  ~ReadFromChildTest() = default;\n\n private:\n  void MultiprocessParent() {\n#if defined(ARCH_CPU_64_BITS)\n    constexpr bool am_64_bit = true;\n#else\n    constexpr bool am_64_bit = false;\n#endif\n\n    VMAddress simple_map_address;\n    VMAddress annotations_address;\n    ASSERT_TRUE(ReadFileExactly(\n        ReadPipeHandle(), &simple_map_address, sizeof(simple_map_address)));\n    ASSERT_TRUE(ReadFileExactly(\n        ReadPipeHandle(), &annotations_address, sizeof(annotations_address)));\n    ExpectAnnotations(\n        ChildProcess(), am_64_bit, simple_map_address, annotations_address);\n  }\n};\n\nTEST(ImageAnnotationReader, ReadFromChild) {\n  ReadFromChildTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/elf/elf_dynamic_array_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/elf/elf_dynamic_array_reader.h\"\n\n#include <elf.h>\n\n#include <type_traits>\n\n#include \"util/stdlib/map_insert.h\"\n\nnamespace crashpad {\n\nnamespace {\n\ntemplate <typename DynType>\nbool Read(const ProcessMemoryRange& memory,\n          VMAddress address,\n          VMSize size,\n          std::map<uint64_t, uint64_t>* values) {\n  std::map<uint64_t, uint64_t> local_values;\n\n  while (size > 0) {\n    DynType entry;\n    if (!memory.Read(address, sizeof(entry), &entry)) {\n      return false;\n    }\n    size -= sizeof(entry);\n    address += sizeof(entry);\n\n    switch (entry.d_tag) {\n      case DT_NULL:\n        values->swap(local_values);\n        return true;\n      case DT_NEEDED:\n        // Skip these entries for now.\n        break;\n      default:\n        static_assert(std::is_unsigned<decltype(entry.d_un.d_ptr)>::value,\n                      \"type must be unsigned\");\n        static_assert(static_cast<void*>(&entry.d_un.d_ptr) ==\n                              static_cast<void*>(&entry.d_un.d_val) &&\n                          sizeof(entry.d_un.d_ptr) == sizeof(entry.d_un.d_val),\n                      \"d_ptr and d_val must be aliases\");\n        if (!MapInsertOrReplace(\n                &local_values, entry.d_tag, entry.d_un.d_ptr, nullptr)) {\n          LOG(ERROR) << \"duplicate dynamic array entry\";\n          return false;\n        }\n    }\n  }\n  LOG(ERROR) << \"missing DT_NULL\";\n  return false;\n}\n\n}  // namespace\n\nElfDynamicArrayReader::ElfDynamicArrayReader() : values_() {}\n\nElfDynamicArrayReader::~ElfDynamicArrayReader() {}\n\nbool ElfDynamicArrayReader::Initialize(const ProcessMemoryRange& memory,\n                                       VMAddress address,\n                                       VMSize size) {\n  return memory.Is64Bit() ? Read<Elf64_Dyn>(memory, address, size, &values_)\n                          : Read<Elf32_Dyn>(memory, address, size, &values_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/elf/elf_dynamic_array_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_ELF_ELF_DYNAMIC_ARRAY_READER_H_\n#define CRASHPAD_SNAPSHOT_ELF_ELF_DYNAMIC_ARRAY_READER_H_\n\n#include <stdint.h>\n\n#include <map>\n\n#include \"base/logging.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/reinterpret_bytes.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n//! \\brief A reader for ELF dynamic arrays mapped into another process.\nclass ElfDynamicArrayReader {\n public:\n  ElfDynamicArrayReader();\n\n  ElfDynamicArrayReader(const ElfDynamicArrayReader&) = delete;\n  ElfDynamicArrayReader& operator=(const ElfDynamicArrayReader&) = delete;\n\n  ~ElfDynamicArrayReader();\n\n  //! \\brief Initializes the reader.\n  //!\n  //! This method must be called once on an object and must be successfully\n  //! called before any other method in this class may be called.\n  //!\n  //! \\param[in] memory A memory reader for the remote process.\n  //! \\param[in] address The address in the remote process' address space where\n  //!     the ELF dynamic table is loaded.\n  //! \\param[in] size The maximum number of bytes to read.\n  bool Initialize(const ProcessMemoryRange& memory,\n                  VMAddress address,\n                  VMSize size);\n\n  //! \\brief Retrieve a value from the array.\n  //!\n  //! \\param[in] tag Specifies which value should be retrieved. The possible\n  //!     values for this parameter are the `DT_*` values from `<elf.h>`.\n  //! \\param[in] log Specifies whether an error should be logged if \\a tag is\n  //!     not found.\n  //! \\param[out] value The value, casted to an appropriate type, if found.\n  //! \\return `true` if the value is found.\n  template <typename V>\n  bool GetValue(uint64_t tag, bool log, V* value) {\n    auto iter = values_.find(tag);\n    if (iter == values_.end()) {\n      LOG_IF(ERROR, log) << \"tag not found\";\n      return false;\n    }\n    return ReinterpretBytes(iter->second, value);\n  }\n\n private:\n  std::map<uint64_t, uint64_t> values_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_ELF_ELF_DYNAMIC_ARRAY_READER_H_\n"
  },
  {
    "path": "snapshot/elf/elf_image_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/elf/elf_image_reader.h\"\n\n#include <stddef.h>\n\n#include <algorithm>\n#include <limits>\n#include <utility>\n#include <vector>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_math.h\"\n#include \"build/build_config.h\"\n#include \"util/numeric/checked_vm_address_range.h\"\n\nnamespace crashpad {\n\nclass ElfImageReader::ProgramHeaderTable {\n public:\n  virtual ~ProgramHeaderTable() {}\n\n  virtual bool VerifyLoadSegments(bool verbose) const = 0;\n  virtual size_t Size() const = 0;\n  virtual bool GetDynamicSegment(VMAddress* address, VMSize* size) const = 0;\n  virtual bool GetPreferredElfHeaderAddress(VMAddress* address,\n                                            bool verbose) const = 0;\n  virtual bool GetPreferredLoadedMemoryRange(VMAddress* address,\n                                             VMSize* size,\n                                             bool verbose) const = 0;\n\n  // Locate the next PT_NOTE segment starting at segment index start_index. If a\n  // PT_NOTE segment is found, start_index is set to the next index after the\n  // found segment.\n  virtual bool GetNoteSegment(size_t* start_index,\n                              VMAddress* address,\n                              VMSize* size) const = 0;\n\n protected:\n  ProgramHeaderTable() {}\n};\n\ntemplate <typename PhdrType>\nclass ElfImageReader::ProgramHeaderTableSpecific\n    : public ElfImageReader::ProgramHeaderTable {\n public:\n  ProgramHeaderTableSpecific() {}\n\n  ProgramHeaderTableSpecific(\n      const ProgramHeaderTableSpecific<PhdrType>&) = delete;\n  ProgramHeaderTableSpecific<PhdrType>& operator=(\n      const ProgramHeaderTableSpecific<PhdrType>&) = delete;\n\n  ~ProgramHeaderTableSpecific() {}\n\n  bool Initialize(const ProcessMemoryRange& memory,\n                  VMAddress address,\n                  VMSize num_segments,\n                  bool verbose) {\n    INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n    table_.resize(num_segments);\n    if (!memory.Read(address, sizeof(PhdrType) * num_segments, table_.data())) {\n      return false;\n    }\n\n    if (!VerifyLoadSegments(verbose)) {\n      return false;\n    }\n\n    INITIALIZATION_STATE_SET_VALID(initialized_);\n    return true;\n  }\n\n  bool VerifyLoadSegments(bool verbose) const override {\n    constexpr bool is_64_bit = std::is_same<PhdrType, Elf64_Phdr>::value;\n    VMAddress last_vaddr;\n    bool load_found = false;\n    for (const auto& header : table_) {\n      if (header.p_type == PT_LOAD) {\n        CheckedVMAddressRange load_range(\n            is_64_bit, header.p_vaddr, header.p_memsz);\n\n        if (!load_range.IsValid()) {\n          LOG_IF(ERROR, verbose) << \"bad load range\";\n          return false;\n        }\n\n        if (load_found && header.p_vaddr <= last_vaddr) {\n          LOG_IF(ERROR, verbose) << \"out of order load segments\";\n          return false;\n        }\n        load_found = true;\n        last_vaddr = header.p_vaddr;\n      }\n    }\n    return true;\n  }\n\n  size_t Size() const override { return sizeof(PhdrType) * table_.size(); }\n\n  bool GetPreferredElfHeaderAddress(VMAddress* address,\n                                    bool verbose) const override {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n    for (const auto& header : table_) {\n      if (header.p_type == PT_LOAD && header.p_offset == 0) {\n        *address = header.p_vaddr;\n        return true;\n      }\n    }\n    LOG_IF(ERROR, verbose) << \"no preferred header address\";\n    return false;\n  }\n\n  bool GetPreferredLoadedMemoryRange(VMAddress* base,\n                                     VMSize* size,\n                                     bool verbose) const override {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n    VMAddress preferred_base = 0;\n    VMAddress preferred_end = 0;\n    bool load_found = false;\n    for (const auto& header : table_) {\n      if (header.p_type == PT_LOAD) {\n        if (!load_found) {\n          preferred_base = header.p_vaddr;\n          load_found = true;\n        }\n        preferred_end = header.p_vaddr + header.p_memsz;\n      }\n    }\n    if (load_found) {\n      *base = preferred_base;\n      *size = preferred_end - preferred_base;\n      return true;\n    }\n    LOG_IF(ERROR, verbose) << \"no load segments\";\n    return false;\n  }\n\n  bool GetDynamicSegment(VMAddress* address, VMSize* size) const override {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n    const PhdrType* phdr;\n    if (!GetProgramHeader(PT_DYNAMIC, &phdr)) {\n      return false;\n    }\n    *address = phdr->p_vaddr;\n    *size = phdr->p_memsz;\n    return true;\n  }\n\n  bool GetProgramHeader(uint32_t type, const PhdrType** header_out) const {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n    for (const auto& header : table_) {\n      if (header.p_type == type) {\n        *header_out = &header;\n        return true;\n      }\n    }\n    return false;\n  }\n\n  bool GetNoteSegment(size_t* start_index,\n                      VMAddress* address,\n                      VMSize* size) const override {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n    for (size_t index = *start_index; index < table_.size(); ++index) {\n      if (table_[index].p_type == PT_NOTE && table_[index].p_vaddr != 0) {\n        *start_index = index + 1;\n        *address = table_[index].p_vaddr;\n        *size = table_[index].p_memsz;\n        return true;\n      }\n    }\n    return false;\n  }\n\n private:\n  std::vector<PhdrType> table_;\n  InitializationStateDcheck initialized_;\n};\n\nElfImageReader::NoteReader::~NoteReader() = default;\n\nElfImageReader::NoteReader::Result ElfImageReader::NoteReader::NextNote(\n    std::string* name,\n    NoteType* type,\n    std::string* desc,\n    VMAddress* desc_address) {\n  if (!is_valid_) {\n    LOG(ERROR) << \"invalid note reader\";\n    return Result::kError;\n  }\n\n  Result result = Result::kError;\n  do {\n    while (current_address_ == segment_end_address_) {\n      VMSize segment_size;\n      if (!phdr_table_->GetNoteSegment(\n              &phdr_index_, &current_address_, &segment_size)) {\n        return Result::kNoMoreNotes;\n      }\n      current_address_ += elf_reader_->GetLoadBias();\n      segment_end_address_ = current_address_ + segment_size;\n      segment_range_ = std::make_unique<ProcessMemoryRange>();\n      if (!segment_range_->Initialize(*range_) ||\n          !segment_range_->RestrictRange(current_address_, segment_size)) {\n        return Result::kError;\n      }\n    }\n\n    retry_ = false;\n    result = range_->Is64Bit()\n                 ? ReadNote<Elf64_Nhdr>(name, type, desc, desc_address)\n                 : ReadNote<Elf32_Nhdr>(name, type, desc, desc_address);\n  } while (retry_);\n\n  if (result == Result::kSuccess) {\n    return Result::kSuccess;\n  }\n  is_valid_ = false;\n  return Result::kError;\n}\n\nnamespace {\n\n// The maximum size the user can specify for maximum note size. Clamping this\n// ensures that buffer allocations cannot be wildly large. It is not expected\n// that a note would be larger than ~1k in normal usage.\nconstexpr size_t kMaxMaxNoteSize = 16384;\n\n}  // namespace\n\nElfImageReader::NoteReader::NoteReader(const ElfImageReader* elf_reader,\n                                       const ProcessMemoryRange* range,\n                                       const ProgramHeaderTable* phdr_table,\n                                       size_t max_note_size,\n                                       const std::string& name_filter,\n                                       NoteType type_filter,\n                                       bool use_filter)\n    : current_address_(0),\n      segment_end_address_(0),\n      elf_reader_(elf_reader),\n      range_(range),\n      phdr_table_(phdr_table),\n      segment_range_(),\n      phdr_index_(0),\n      max_note_size_(std::min(kMaxMaxNoteSize, max_note_size)),\n      name_filter_(name_filter),\n      type_filter_(type_filter),\n      use_filter_(use_filter),\n      is_valid_(true),\n      retry_(false) {\n  DCHECK_LT(max_note_size, kMaxMaxNoteSize);\n}\n\ntemplate <typename NhdrType>\nElfImageReader::NoteReader::Result ElfImageReader::NoteReader::ReadNote(\n    std::string* name,\n    NoteType* type,\n    std::string* desc,\n    VMAddress* desc_address) {\n  static_assert(sizeof(*type) >= sizeof(NhdrType::n_namesz),\n                \"Note field size mismatch\");\n  DCHECK_LT(current_address_, segment_end_address_);\n\n  NhdrType note_info;\n  if (!segment_range_->Read(current_address_, sizeof(note_info), &note_info)) {\n    return Result::kError;\n  }\n  current_address_ += sizeof(note_info);\n\n  constexpr size_t align = sizeof(note_info.n_namesz);\n\n#define CHECKED_PAD(x, into)                                 \\\n  base::CheckAnd(base::CheckAdd(x, align - 1), ~(align - 1)) \\\n      .AssignIfValid(&into)\n\n  size_t padded_namesz;\n  if (!CHECKED_PAD(note_info.n_namesz, padded_namesz)) {\n    return Result::kError;\n  }\n  size_t padded_descsz;\n  if (!CHECKED_PAD(note_info.n_descsz, padded_descsz)) {\n    return Result::kError;\n  }\n\n  size_t note_size;\n  if (!base::CheckAdd(padded_namesz, padded_descsz).AssignIfValid(&note_size)) {\n    return Result::kError;\n  }\n\n  // Notes typically have 4-byte alignment. However, .note.android.ident may\n  // inadvertently use 2-byte alignment.\n  // https://android-review.googlesource.com/c/platform/bionic/+/554986/\n  // We can still find .note.android.ident if it appears first in a note segment\n  // but there may be 4-byte aligned notes following it. If this note was\n  // aligned at less than 4-bytes, expect that the next note will be aligned at\n  // 4-bytes and add extra padding, if necessary.\n\n  VMAddress end_of_note_candidate;\n  if (!base::CheckAdd(current_address_, note_size)\n           .AssignIfValid(&end_of_note_candidate)) {\n    return Result::kError;\n  }\n  VMAddress end_of_note;\n  if (!CHECKED_PAD(end_of_note_candidate, end_of_note)) {\n    return Result::kError;\n  }\n  end_of_note = std::min(end_of_note, segment_end_address_);\n\n#undef CHECKED_PAD\n\n  if (note_size > max_note_size_) {\n    current_address_ = end_of_note;\n    retry_ = true;\n    return Result::kError;\n  }\n\n  if (use_filter_ && note_info.n_type != type_filter_) {\n    current_address_ = end_of_note;\n    retry_ = true;\n    return Result::kError;\n  }\n\n  std::string local_name(note_info.n_namesz, '\\0');\n  if (!segment_range_->Read(\n          current_address_, note_info.n_namesz, &local_name[0])) {\n    return Result::kError;\n  }\n  if (!local_name.empty()) {\n    if (local_name.back() != '\\0') {\n      LOG(ERROR) << \"unterminated note name\";\n      return Result::kError;\n    }\n    local_name.pop_back();\n  }\n\n  if (use_filter_ && local_name != name_filter_) {\n    current_address_ = end_of_note;\n    retry_ = true;\n    return Result::kError;\n  }\n\n  current_address_ += padded_namesz;\n\n  std::string local_desc(note_info.n_descsz, '\\0');\n  if (!segment_range_->Read(\n          current_address_, note_info.n_descsz, &local_desc[0])) {\n    return Result::kError;\n  }\n  *desc_address = current_address_;\n\n  current_address_ = end_of_note;\n\n  if (name) {\n    name->swap(local_name);\n  }\n  if (type) {\n    *type = note_info.n_type;\n  }\n  desc->swap(local_desc);\n  return Result::kSuccess;\n}\n\nElfImageReader::ElfImageReader()\n    : header_64_(),\n      ehdr_address_(0),\n      load_bias_(0),\n      memory_(),\n      program_headers_(),\n      dynamic_array_(),\n      symbol_table_(),\n      initialized_(),\n      dynamic_array_initialized_(),\n      symbol_table_initialized_() {}\n\nElfImageReader::~ElfImageReader() {}\n\nbool ElfImageReader::Initialize(const ProcessMemoryRange& memory,\n                                VMAddress address,\n                                bool verbose) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  ehdr_address_ = address;\n  if (!memory_.Initialize(memory)) {\n    return false;\n  }\n\n  uint8_t e_ident[EI_NIDENT];\n  if (!memory_.Read(ehdr_address_, EI_NIDENT, e_ident)) {\n    return false;\n  }\n\n  if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||\n      e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {\n    LOG_IF(ERROR, verbose) << \"Incorrect ELF magic number\";\n    return false;\n  }\n\n  if (!(memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS64) &&\n      !(!memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS32)) {\n    LOG_IF(ERROR, verbose) << \"unexpected bitness\";\n    return false;\n  }\n\n#if defined(ARCH_CPU_LITTLE_ENDIAN)\n  constexpr uint8_t expected_encoding = ELFDATA2LSB;\n#elif defined(ARCH_CPU_BIG_ENDIAN)\n  constexpr uint8_t expected_encoding = ELFDATA2MSB;\n#endif\n  if (e_ident[EI_DATA] != expected_encoding) {\n    LOG_IF(ERROR, verbose) << \"unexpected encoding\";\n    return false;\n  }\n\n  if (e_ident[EI_VERSION] != EV_CURRENT) {\n    LOG_IF(ERROR, verbose) << \"unexpected version\";\n    return false;\n  }\n\n  if (!(memory_.Is64Bit()\n            ? memory_.Read(ehdr_address_, sizeof(header_64_), &header_64_)\n            : memory_.Read(ehdr_address_, sizeof(header_32_), &header_32_))) {\n    return false;\n  }\n\n#define VERIFY_HEADER(header)                                  \\\n  do {                                                         \\\n    if (header.e_type != ET_EXEC && header.e_type != ET_DYN) { \\\n      LOG_IF(ERROR, verbose) << \"unexpected image type\";       \\\n      return false;                                            \\\n    }                                                          \\\n    if (header.e_version != EV_CURRENT) {                      \\\n      LOG_IF(ERROR, verbose) << \"unexpected version\";          \\\n      return false;                                            \\\n    }                                                          \\\n    if (header.e_ehsize != sizeof(header)) {                   \\\n      LOG_IF(ERROR, verbose) << \"unexpected header size\";      \\\n      return false;                                            \\\n    }                                                          \\\n  } while (false);\n\n  if (memory_.Is64Bit()) {\n    VERIFY_HEADER(header_64_);\n  } else {\n    VERIFY_HEADER(header_32_);\n  }\n\n  if (!InitializeProgramHeaders(verbose)) {\n    return false;\n  }\n\n  VMAddress preferred_ehdr_address;\n  if (!program_headers_.get()->GetPreferredElfHeaderAddress(\n          &preferred_ehdr_address, verbose)) {\n    return false;\n  }\n  load_bias_ = ehdr_address_ - preferred_ehdr_address;\n\n  VMAddress base_address;\n  VMSize loaded_size;\n  if (!program_headers_.get()->GetPreferredLoadedMemoryRange(\n          &base_address, &loaded_size, verbose)) {\n    return false;\n  }\n  base_address += load_bias_;\n\n  if (!memory_.RestrictRange(base_address, loaded_size)) {\n    return false;\n  }\n\n  VMSize ehdr_size;\n  VMAddress phdr_address;\n  if (memory_.Is64Bit()) {\n    ehdr_size = sizeof(header_64_);\n    phdr_address = ehdr_address_ + header_64_.e_phoff;\n  } else {\n    ehdr_size = sizeof(header_32_);\n    phdr_address = ehdr_address_ + header_32_.e_phoff;\n  }\n\n  CheckedVMAddressRange range(memory_.Is64Bit(), base_address, loaded_size);\n  if (!range.ContainsRange(\n          CheckedVMAddressRange(memory_.Is64Bit(), ehdr_address_, ehdr_size))) {\n    LOG_IF(ERROR, verbose) << \"ehdr out of range\";\n    return false;\n  }\n  if (!range.ContainsRange(CheckedVMAddressRange(\n          memory.Is64Bit(), phdr_address, program_headers_->Size()))) {\n    LOG_IF(ERROR, verbose) << \"phdrs out of range\";\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nuint16_t ElfImageReader::FileType() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return memory_.Is64Bit() ? header_64_.e_type : header_32_.e_type;\n}\n\nbool ElfImageReader::SoName(std::string* name) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!InitializeDynamicArray()) {\n    return false;\n  }\n\n  VMSize offset;\n  if (!dynamic_array_->GetValue(DT_SONAME, true, &offset)) {\n    return false;\n  }\n\n  return ReadDynamicStringTableAtOffset(offset, name);\n}\n\nbool ElfImageReader::GetDynamicSymbol(const std::string& name,\n                                      VMAddress* address,\n                                      VMSize* size) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!InitializeDynamicSymbolTable()) {\n    return false;\n  }\n\n  ElfSymbolTableReader::SymbolInformation info;\n  if (!symbol_table_->GetSymbol(name, &info)) {\n    return false;\n  }\n  if (info.shndx == SHN_UNDEF || info.shndx == SHN_COMMON) {\n    return false;\n  }\n\n  switch (info.binding) {\n    case STB_GLOBAL:\n    case STB_WEAK:\n      break;\n\n    case STB_LOCAL:\n    default:\n      return false;\n  }\n\n  switch (info.type) {\n    case STT_OBJECT:\n    case STT_FUNC:\n      break;\n\n    case STT_COMMON:\n    case STT_NOTYPE:\n    case STT_SECTION:\n    case STT_FILE:\n    case STT_TLS:\n    default:\n      return false;\n  }\n\n  if (info.shndx != SHN_ABS) {\n    info.address += GetLoadBias();\n  }\n\n  *address = info.address;\n  *size = info.size;\n  return true;\n}\n\nbool ElfImageReader::ReadDynamicStringTableAtOffset(VMSize offset,\n                                                    std::string* string) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!InitializeDynamicArray()) {\n    return false;\n  }\n\n  VMAddress string_table_address;\n  VMSize string_table_size;\n  if (!GetAddressFromDynamicArray(DT_STRTAB, true, &string_table_address) ||\n      !dynamic_array_->GetValue(DT_STRSZ, true, &string_table_size)) {\n    LOG(ERROR) << \"missing string table info\";\n    return false;\n  }\n  if (offset >= string_table_size) {\n    LOG(ERROR) << \"bad offset\";\n    return false;\n  }\n\n  // GNU ld.so doesn't adjust the vdso's dynamic array entries by the load bias.\n  // If the address is too small to point into the loaded module range and is\n  // small enough to be an offset from the base of the module, adjust it now.\n  if (string_table_address < memory_.Base() &&\n      string_table_address < memory_.Size()) {\n    string_table_address += GetLoadBias();\n  }\n\n  if (!memory_.ReadCStringSizeLimited(\n          string_table_address + offset, string_table_size - offset, string)) {\n    LOG(ERROR) << \"missing nul-terminator\";\n    return false;\n  }\n  return true;\n}\n\nbool ElfImageReader::GetDebugAddress(VMAddress* debug) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!InitializeDynamicArray()) {\n    return false;\n  }\n  return GetAddressFromDynamicArray(DT_DEBUG, true, debug);\n}\n\nbool ElfImageReader::GetDynamicArrayAddress(VMAddress* address) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  VMAddress dyn_segment_address;\n  VMSize dyn_segment_size;\n  if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address,\n                                                 &dyn_segment_size)) {\n    LOG(ERROR) << \"no dynamic segment\";\n    return false;\n  }\n  *address = dyn_segment_address + GetLoadBias();\n  return true;\n}\n\nVMAddress ElfImageReader::GetProgramHeaderTableAddress() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ehdr_address_ +\n         (memory_.Is64Bit() ? header_64_.e_phoff : header_32_.e_phoff);\n}\n\nbool ElfImageReader::InitializeProgramHeaders(bool verbose) {\n#define INITIALIZE_PROGRAM_HEADERS(PhdrType, header)         \\\n  do {                                                       \\\n    if (header.e_phentsize != sizeof(PhdrType)) {            \\\n      LOG_IF(ERROR, verbose) << \"unexpected phdr size\";      \\\n      return false;                                          \\\n    }                                                        \\\n    auto phdrs = new ProgramHeaderTableSpecific<PhdrType>(); \\\n    program_headers_.reset(phdrs);                           \\\n    if (!phdrs->Initialize(memory_,                          \\\n                           ehdr_address_ + header.e_phoff,   \\\n                           header.e_phnum,                   \\\n                           verbose)) {                       \\\n      return false;                                          \\\n    }                                                        \\\n  } while (false);\n\n  if (memory_.Is64Bit()) {\n    INITIALIZE_PROGRAM_HEADERS(Elf64_Phdr, header_64_);\n  } else {\n    INITIALIZE_PROGRAM_HEADERS(Elf32_Phdr, header_32_);\n  }\n  return true;\n}\n\nbool ElfImageReader::InitializeDynamicArray() {\n  if (dynamic_array_initialized_.is_valid()) {\n    return true;\n  }\n  if (!dynamic_array_initialized_.is_uninitialized()) {\n    return false;\n  }\n  dynamic_array_initialized_.set_invalid();\n\n  VMAddress dyn_segment_address;\n  VMSize dyn_segment_size;\n  if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address,\n                                                 &dyn_segment_size)) {\n    LOG(ERROR) << \"no dynamic segment\";\n    return false;\n  }\n  dyn_segment_address += GetLoadBias();\n\n  dynamic_array_.reset(new ElfDynamicArrayReader());\n  if (!dynamic_array_->Initialize(\n          memory_, dyn_segment_address, dyn_segment_size)) {\n    return false;\n  }\n  dynamic_array_initialized_.set_valid();\n  return true;\n}\n\nbool ElfImageReader::InitializeDynamicSymbolTable() {\n  if (symbol_table_initialized_.is_valid()) {\n    return true;\n  }\n  if (!symbol_table_initialized_.is_uninitialized()) {\n    return false;\n  }\n  symbol_table_initialized_.set_invalid();\n\n  if (!InitializeDynamicArray()) {\n    return false;\n  }\n\n  VMAddress symbol_table_address;\n  if (!GetAddressFromDynamicArray(DT_SYMTAB, true, &symbol_table_address)) {\n    LOG(ERROR) << \"no symbol table\";\n    return false;\n  }\n\n  // Try both DT_HASH and DT_GNU_HASH. They're completely different, but both\n  // circuitously offer a way to find the number of entries in the symbol table.\n  // DT_HASH is specifically checked first, because depending on the linker, the\n  // count maybe be incorrect for zero-export cases. In practice, it is believed\n  // that the zero-export case is probably not particularly useful, so this\n  // incorrect count will only occur in constructed test cases (see\n  // ElfImageReader.DtHashAndDtGnuHashMatch).\n  VMSize number_of_symbol_table_entries;\n  if (!GetNumberOfSymbolEntriesFromDtHash(&number_of_symbol_table_entries) &&\n      !GetNumberOfSymbolEntriesFromDtGnuHash(&number_of_symbol_table_entries)) {\n    LOG(ERROR) << \"could not retrieve number of symbol table entries\";\n    return false;\n  }\n\n  symbol_table_.reset(new ElfSymbolTableReader(\n      &memory_, this, symbol_table_address, number_of_symbol_table_entries));\n  symbol_table_initialized_.set_valid();\n  return true;\n}\n\nbool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag,\n                                                bool log,\n                                                VMAddress* address) {\n  if (!dynamic_array_->GetValue(tag, log, address)) {\n    return false;\n  }\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || \\\n    (defined(__GLIBC__) && defined(ARCH_CPU_RISCV64))\n  // The GNU loader updates the dynamic array according to the load bias (except\n  // for RISC-V: https://sourceware.org/bugzilla/show_bug.cgi?id=24484).\n  // The Android and Fuchsia loaders only update the debug address.\n  if (tag != DT_DEBUG) {\n    *address += GetLoadBias();\n  }\n#endif  // BUILDFLAG(IS_ANDROID)\n  return true;\n}\n\nbool ElfImageReader::GetNumberOfSymbolEntriesFromDtHash(\n    VMSize* number_of_symbol_table_entries) {\n  if (!InitializeDynamicArray()) {\n    return false;\n  }\n\n  VMAddress dt_hash_address;\n  if (!GetAddressFromDynamicArray(DT_HASH, false, &dt_hash_address)) {\n    return false;\n  }\n\n  struct {\n    uint32_t nbucket;\n    uint32_t nchain;\n  } header;\n\n  if (!memory_.Read(dt_hash_address, sizeof(header), &header)) {\n    LOG(ERROR) << \"failed to read DT_HASH header\";\n    return false;\n  }\n\n  *number_of_symbol_table_entries = header.nchain;\n  return true;\n}\n\nbool ElfImageReader::GetNumberOfSymbolEntriesFromDtGnuHash(\n    VMSize* number_of_symbol_table_entries) {\n  if (!InitializeDynamicArray()) {\n    return false;\n  }\n\n  VMAddress dt_gnu_hash_address;\n  if (!GetAddressFromDynamicArray(DT_GNU_HASH, false, &dt_gnu_hash_address)) {\n    return false;\n  }\n\n  // See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ and\n  // https://sourceware.org/ml/binutils/2006-10/msg00377.html.\n  struct {\n    uint32_t nbuckets;\n    uint32_t symoffset;\n    uint32_t bloom_size;\n    uint32_t bloom_shift;\n  } header;\n  if (!memory_.Read(dt_gnu_hash_address, sizeof(header), &header)) {\n    LOG(ERROR) << \"failed to read DT_GNU_HASH header\";\n    return false;\n  }\n\n  std::vector<uint32_t> buckets(header.nbuckets);\n  const size_t kNumBytesForBuckets = sizeof(buckets[0]) * buckets.size();\n  const size_t kWordSize =\n      memory_.Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t);\n  const VMAddress buckets_address =\n      dt_gnu_hash_address + sizeof(header) + (kWordSize * header.bloom_size);\n  if (!memory_.Read(buckets_address, kNumBytesForBuckets, buckets.data())) {\n    LOG(ERROR) << \"read buckets\";\n    return false;\n  }\n\n  // Locate the chain that handles the largest index bucket.\n  uint32_t last_symbol = 0;\n  for (uint32_t i = 0; i < header.nbuckets; ++i) {\n    last_symbol = std::max(buckets[i], last_symbol);\n  }\n\n  if (last_symbol < header.symoffset) {\n    *number_of_symbol_table_entries = header.symoffset;\n    return true;\n  }\n\n  // Walk the bucket's chain to add the chain length to the total.\n  const VMAddress chains_base_address = buckets_address + kNumBytesForBuckets;\n  for (;;) {\n    uint32_t chain_entry;\n    if (!memory_.Read(chains_base_address + (last_symbol - header.symoffset) *\n                                                sizeof(chain_entry),\n                      sizeof(chain_entry),\n                      &chain_entry)) {\n      LOG(ERROR) << \"read chain entry\";\n      return false;\n    }\n\n    ++last_symbol;\n\n    // If the low bit is set, this entry is the end of the chain.\n    if (chain_entry & 1)\n      break;\n  }\n\n  *number_of_symbol_table_entries = last_symbol;\n  return true;\n}\n\nstd::unique_ptr<ElfImageReader::NoteReader> ElfImageReader::Notes(\n    size_t max_note_size) {\n  return std::make_unique<NoteReader>(\n      this, &memory_, program_headers_.get(), max_note_size);\n}\n\nstd::unique_ptr<ElfImageReader::NoteReader>\nElfImageReader::NotesWithNameAndType(const std::string& name,\n                                     NoteReader::NoteType type,\n                                     size_t max_note_size) {\n  return std::make_unique<NoteReader>(\n      this, &memory_, program_headers_.get(), max_note_size, name, type, true);\n}\n\nconst ProcessMemoryRange* ElfImageReader::Memory() const {\n  return &memory_;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/elf/elf_image_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_\n#define CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_\n\n#include <elf.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <string>\n\n#include \"snapshot/elf/elf_dynamic_array_reader.h\"\n#include \"snapshot/elf/elf_symbol_table_reader.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n//! \\brief A reader for ELF images mapped into another process.\n//!\n//! This class is capable of reading both 32-bit and 64-bit images.\nclass ElfImageReader {\n private:\n  class ProgramHeaderTable;\n\n public:\n  //! \\brief This class enables reading note segments from an ELF image.\n  //!\n  //! Objects of this class should be created by calling\n  //! ElfImageReader::Notes() or ElfImageReader::NotesWithNameAndType().\n  class NoteReader {\n   public:\n    NoteReader(const NoteReader&) = delete;\n    NoteReader& operator=(const NoteReader&) = delete;\n\n    ~NoteReader();\n\n    //! \\brief The return value for NextNote().\n    enum class Result {\n      //! \\brief An error occurred. The NoteReader is invalidated and message is\n      //!     logged.\n      kError,\n\n      //! \\brief A note was found.\n      kSuccess,\n\n      //! \\brief No more notes were found.\n      kNoMoreNotes,\n    };\n\n    //! \\brief A type large enough to hold a note type, potentially across\n    //!     bitness.\n    using NoteType = decltype(Elf64_Nhdr::n_type);\n\n    //! \\brief Searches for the next note in the image.\n    //!\n    //! \\param[out] name The name of the note owner, if not `nullptr`.\n    //! \\param[out] type A type for the note, if not `nullptr`.\n    //! \\param[out] desc The note descriptor.\n    //! \\param[out] desc_addr The address in the remote process' address space\n    //!     \\a desc was read from.\n    //! \\return a #Result value. \\a name, \\a type, \\a desc, and \\a desc_addr are\n    //!     only valid if this method returns Result::kSuccess.\n    Result NextNote(std::string* name,\n                    NoteType* type,\n                    std::string* desc,\n                    VMAddress* desc_addr);\n\n    // private\n    NoteReader(const ElfImageReader* elf_reader_,\n               const ProcessMemoryRange* range,\n               const ProgramHeaderTable* phdr_table,\n               size_t max_note_size,\n               const std::string& name_filter = std::string(),\n               NoteType type_filter = 0,\n               bool use_filter = false);\n\n   private:\n    // Reads the next note at the current segment address. Sets retry_ to true\n    // and returns kError if use_filter_ is true and the note's name and type do\n    // not match name_filter_ and type_filter_.\n    template <typename T>\n    Result ReadNote(std::string* name,\n                    NoteType* type,\n                    std::string* desc,\n                    VMAddress* desc_addr);\n\n    VMAddress current_address_;\n    VMAddress segment_end_address_;\n    const ElfImageReader* elf_reader_;  // weak\n    const ProcessMemoryRange* range_;  // weak\n    const ProgramHeaderTable* phdr_table_;  // weak\n    std::unique_ptr<ProcessMemoryRange> segment_range_;\n    size_t phdr_index_;\n    size_t max_note_size_;\n    std::string name_filter_;\n    NoteType type_filter_;\n    bool use_filter_;\n    bool is_valid_;\n    bool retry_;\n  };\n\n  ElfImageReader();\n\n  ElfImageReader(const ElfImageReader&) = delete;\n  ElfImageReader& operator=(const ElfImageReader&) = delete;\n\n  ~ElfImageReader();\n\n  //! \\brief Initializes the reader.\n  //!\n  //! This method must be called once on an object and must be successfully\n  //! called before any other method in this class may be called.\n  //!\n  //! \\param[in] memory A memory reader for the remote process.\n  //! \\param[in] address The address in the remote process' address space where\n  //!     the ELF image is loaded.\n  //! \\param[in] verbose `true` if this method should log error messages during\n  //!     initialization. Setting this value to `false` will reduce the error\n  //!     messages relating to verifying the ELF image, but may not suppress\n  //!     logging entirely.\n  bool Initialize(const ProcessMemoryRange& memory,\n                  VMAddress address,\n                  bool verbose = true);\n\n  //! \\brief Returns the base address of the image's memory range.\n  //!\n  //! This may differ from the address passed to Initialize() if the ELF header\n  //! is not loaded at the start of the first `PT_LOAD` segment.\n  VMAddress Address() const { return memory_.Base(); }\n\n  //! \\brief Returns the size of the range containing all loaded segments for\n  //!     this image.\n  //!\n  //! The size may include memory that is unmapped or mapped to other objects if\n  //! this image's `PT_LOAD` segments are not contiguous.\n  VMSize Size() const { return memory_.Size(); }\n\n  //! \\brief Returns the file type for the image.\n  //!\n  //! Possible values include `ET_EXEC` or `ET_DYN` from `<elf.h>`.\n  uint16_t FileType() const;\n\n  //! \\brief Returns the load bias for the image.\n  //!\n  //! The load bias is the actual load address minus the preferred load address.\n  VMOffset GetLoadBias() const { return load_bias_; }\n\n  //! \\brief Determines the name of this object using `DT_SONAME`, if present.\n  //!\n  //! \\param[out] name The name of this object, only valid if this method\n  //!     returns `true`.\n  //! \\return `true` if a name was found for this object.\n  bool SoName(std::string* name);\n\n  //! \\brief Reads information from the dynamic symbol table about the symbol\n  //!     identified by \\a name.\n  //!\n  //! \\param[in] name The name of the symbol to search for.\n  //! \\param[out] address The address of the symbol in the target process'\n  //!     address space, if found.\n  //! \\param[out] size The size of the symbol, if found.\n  //! \\return `true` if the symbol was found.\n  bool GetDynamicSymbol(const std::string& name,\n                        VMAddress* address,\n                        VMSize* size);\n\n  //! \\brief Reads a `NUL`-terminated C string from this image's dynamic string\n  //!     table.\n  //!\n  //! \\param[in] offset the byte offset in the string table to start reading.\n  //! \\param[out] string the string read.\n  //! \\return `true` on success. Otherwise `false` with a message logged.\n  bool ReadDynamicStringTableAtOffset(VMSize offset, std::string* string);\n\n  //! \\brief Determine the debug address.\n  //!\n  //! The debug address is a pointer to an `r_debug` struct defined in\n  //! `<link.h>`.\n  //!\n  //! \\param[out] debug the debug address, if found.\n  //! \\return `true` if the debug address was found.\n  bool GetDebugAddress(VMAddress* debug);\n\n  //! \\brief Determine the address of `PT_DYNAMIC` segment.\n  //!\n  //! \\param[out] address The address of the array, valid if this method returns\n  //!     `true`.\n  //! \\return `true` on success. Otherwise `false` with a message logged.\n  bool GetDynamicArrayAddress(VMAddress* address);\n\n  //! \\brief Return the address of the program header table.\n  VMAddress GetProgramHeaderTableAddress();\n\n  //! \\brief Return a NoteReader for this image, which scans all PT_NOTE\n  //!     segments in the image.\n  //!\n  //! The returned NoteReader is only valid for the lifetime of the\n  //! ElfImageReader that created it.\n  //!\n  //! \\param[in] max_note_size The maximum note size to read. Notes whose\n  //!     combined name, descriptor, and padding size are greater than\n  //!     \\a max_note_size will be silently skipped.\n  //! \\return A NoteReader object capable of reading notes in this image.\n  std::unique_ptr<NoteReader> Notes(size_t max_note_size);\n\n  //! \\brief Return a NoteReader for this image, which scans all PT_NOTE\n  //!     segments in the image, filtering by name and type.\n  //!\n  //! The returned NoteReader is only valid for the lifetime of the\n  //! ElfImageReader that created it.\n  //!\n  //! \\param[in] name The note name to match.\n  //! \\param[in] type The note type to match.\n  //! \\param[in] max_note_size The maximum note size to read. Notes whose\n  //!     combined name, descriptor, and padding size are greater than\n  //!     \\a max_note_size will be silently skipped.\n  //! \\return A NoteReader object capable of reading notes in this image.\n  std::unique_ptr<NoteReader> NotesWithNameAndType(const std::string& name,\n                                                   NoteReader::NoteType type,\n                                                   size_t max_note_size);\n\n  //! \\brief Return a ProcessMemoryRange restricted to the range of this image.\n  //!\n  //! The caller does not take ownership of the returned object.\n  const ProcessMemoryRange* Memory() const;\n\n  //! \\brief Retrieves the number of symbol table entries in `DT_SYMTAB`\n  //!     according to the data in the `DT_HASH` section.\n  //!\n  //! \\note Exposed for testing, not normally otherwise useful.\n  //!\n  //! \\param[out] number_of_symbol_table_entries The number of entries expected\n  //!     in `DT_SYMTAB`.\n  //! \\return `true` if a `DT_HASH` section was found, and was read\n  //!     successfully, otherwise `false` with an error logged.\n  bool GetNumberOfSymbolEntriesFromDtHash(\n      VMSize* number_of_symbol_table_entries);\n\n  //! \\brief Retrieves the number of symbol table entries in `DT_SYMTAB`\n  //!     according to the data in the `DT_GNU_HASH` section.\n  //!\n  //! \\note Exposed for testing, not normally otherwise useful.\n  //!\n  //! \\note Depending on the linker that generated the `DT_GNU_HASH` section,\n  //!     this value may not be as expected if there are zero exported symbols.\n  //!\n  //! \\param[out] number_of_symbol_table_entries The number of entries expected\n  //!     in `DT_SYMTAB`.\n  //! \\return `true` if a `DT_GNU_HASH` section was found, and was read\n  //!     successfully, otherwise `false` with an error logged.\n  bool GetNumberOfSymbolEntriesFromDtGnuHash(\n      VMSize* number_of_symbol_table_entries);\n\n private:\n  template <typename PhdrType>\n  class ProgramHeaderTableSpecific;\n\n  bool InitializeProgramHeaders(bool verbose);\n  bool InitializeDynamicArray();\n  bool InitializeDynamicSymbolTable();\n  bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address);\n\n  union {\n    Elf32_Ehdr header_32_;\n    Elf64_Ehdr header_64_;\n  };\n  VMAddress ehdr_address_;\n  VMOffset load_bias_;\n  ProcessMemoryRange memory_;\n  std::unique_ptr<ProgramHeaderTable> program_headers_;\n  std::unique_ptr<ElfDynamicArrayReader> dynamic_array_;\n  std::unique_ptr<ElfSymbolTableReader> symbol_table_;\n  InitializationStateDcheck initialized_;\n  InitializationState dynamic_array_initialized_;\n  InitializationState symbol_table_initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_\n"
  },
  {
    "path": "snapshot/elf/elf_image_reader_fuzzer.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <inttypes.h>\n\n#include \"base/logging.h\"\n#include \"snapshot/elf/elf_image_reader.h\"\n#include \"util/process/process_memory.h\"\n\nusing namespace crashpad;\n\nclass FakeProcessMemory : public ProcessMemory {\n public:\n  FakeProcessMemory(const uint8_t* data, size_t size, VMAddress fake_base)\n      : data_(data), size_(size), fake_base_(fake_base) {}\n\n  ssize_t ReadUpTo(VMAddress address,\n                   size_t size,\n                   void* buffer) const override {\n    VMAddress offset_in_data = address - fake_base_;\n    if (offset_in_data > size_)\n      return -1;\n    size_t read_size =\n        std::min(static_cast<size_t>(size_ - offset_in_data), size);\n    memcpy(buffer, &data_[offset_in_data], read_size);\n    return read_size;\n  }\n\n private:\n  const uint8_t* data_;\n  size_t size_;\n  VMAddress fake_base_;\n};\n\nextern \"C\" int LLVMFuzzerInitialize(int* argc, char*** argv) {\n  // Swallow all logs to avoid spam.\n  logging::SetLogMessageHandler(\n      [](logging::LogSeverity, const char*, int, size_t, const std::string&) {\n        return true;\n      });\n  return 0;\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n  constexpr size_t kBase = 0x10000;\n  FakeProcessMemory process_memory(data, size, kBase);\n  ProcessMemoryRange process_memory_range;\n  process_memory_range.Initialize(&process_memory, true, kBase, size);\n\n  ElfImageReader reader;\n  if (!reader.Initialize(process_memory_range, kBase))\n    return 0;\n\n  ElfImageReader::NoteReader::Result result;\n  std::string note_name;\n  std::string note_desc;\n  ElfImageReader::NoteReader::NoteType note_type;\n  VMAddress desc_addr;\n  auto notes = reader.Notes(9999);\n  while ((result = notes->NextNote(\n              &note_name, &note_type, &note_desc, &desc_addr)) ==\n         ElfImageReader::NoteReader::Result::kSuccess) {\n    LOG(ERROR) << note_name << note_type << note_desc;\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "snapshot/elf/elf_image_reader_fuzzer_corpus/.gitattributes",
    "content": "# Copyright 2019 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# ELF executables normally don’t have any extension, so there’s no pattern to\n# match in the root .gitattributes file.\n/ret42 binary\n"
  },
  {
    "path": "snapshot/elf/elf_image_reader_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/elf/elf_image_reader.h\"\n\n#include <dlfcn.h>\n#include <link.h>\n#include <unistd.h>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"test/process_type.h\"\n#include \"test/scoped_module_handle.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/elf_note_types.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/process/process_memory_native.h\"\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include <lib/zx/process.h>\n\n#include \"base/fuchsia/fuchsia_logging.h\"\n\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n\n#include \"test/linux/fake_ptrace_connection.h\"\n#include \"util/linux/auxiliary_vector.h\"\n#include \"util/linux/memory_map.h\"\n\n#else\n\n#error Port.\n\n#endif  // BUILDFLAG(IS_FUCHSIA)\n\nextern \"C\" {\n__attribute__((visibility(\"default\"))) void ElfImageReaderTestExportedSymbol() {\n}\n}  // extern \"C\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n#if BUILDFLAG(IS_FUCHSIA)\n\nvoid LocateExecutable(const ProcessType& process,\n                      ProcessMemory* memory,\n                      VMAddress* elf_address) {\n  uintptr_t debug_address;\n  zx_status_t status = process->get_property(\n      ZX_PROP_PROCESS_DEBUG_ADDR, &debug_address, sizeof(debug_address));\n  ASSERT_EQ(status, ZX_OK)\n      << \"zx_object_get_property: ZX_PROP_PROCESS_DEBUG_ADDR\";\n  // Can be 0 if requested before the loader has loaded anything.\n  EXPECT_NE(debug_address, 0u);\n\n  constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map);\n  uintptr_t map;\n  ASSERT_TRUE(\n      memory->Read(debug_address + k_r_debug_map_offset, sizeof(map), &map))\n      << \"read link_map\";\n\n  constexpr auto k_link_map_addr_offset = offsetof(link_map, l_addr);\n  uintptr_t base;\n  ASSERT_TRUE(memory->Read(map + k_link_map_addr_offset, sizeof(base), &base))\n      << \"read base\";\n\n  *elf_address = base;\n}\n\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n\nvoid LocateExecutable(PtraceConnection* connection,\n                      ProcessMemory* memory,\n                      VMAddress* elf_address) {\n  AuxiliaryVector aux;\n  ASSERT_TRUE(aux.Initialize(connection));\n\n  VMAddress phdrs;\n  ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));\n\n  MemoryMap memory_map;\n  ASSERT_TRUE(memory_map.Initialize(connection));\n  const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs);\n  ASSERT_TRUE(phdr_mapping);\n  auto possible_mappings = memory_map.FindFilePossibleMmapStarts(*phdr_mapping);\n  ASSERT_EQ(possible_mappings->Count(), 1u);\n  *elf_address = possible_mappings->Next()->range.Base();\n}\n\n#endif  // BUILDFLAG(IS_FUCHSIA)\n\nvoid ExpectSymbol(ElfImageReader* reader,\n                  const std::string& symbol_name,\n                  VMAddress expected_symbol_address) {\n  VMAddress symbol_address;\n  VMSize symbol_size;\n  ASSERT_TRUE(\n      reader->GetDynamicSymbol(symbol_name, &symbol_address, &symbol_size));\n  EXPECT_EQ(symbol_address, expected_symbol_address);\n\n  EXPECT_FALSE(\n      reader->GetDynamicSymbol(\"notasymbol\", &symbol_address, &symbol_size));\n}\n\nvoid ReadThisExecutableInTarget(ProcessType process,\n                                VMAddress exported_symbol_address) {\n#if defined(ARCH_CPU_64_BITS)\n  constexpr bool am_64_bit = true;\n#else\n  constexpr bool am_64_bit = false;\n#endif  // ARCH_CPU_64_BITS\n\n  VMAddress elf_address;\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(process));\n  ProcessMemoryLinux memory(&connection);\n  LocateExecutable(&connection, &memory, &elf_address);\n#elif BUILDFLAG(IS_FUCHSIA)\n  ProcessMemoryFuchsia memory;\n  ASSERT_TRUE(memory.Initialize(process));\n  LocateExecutable(process, &memory, &elf_address);\n#endif\n  ASSERT_NO_FATAL_FAILURE();\n\n  ProcessMemoryRange range;\n  ASSERT_TRUE(range.Initialize(&memory, am_64_bit));\n\n  ElfImageReader reader;\n  ASSERT_TRUE(reader.Initialize(range, elf_address));\n\n  ExpectSymbol(\n      &reader, \"ElfImageReaderTestExportedSymbol\", exported_symbol_address);\n\n  ElfImageReader::NoteReader::Result result;\n  std::string note_name;\n  std::string note_desc;\n  ElfImageReader::NoteReader::NoteType note_type;\n  VMAddress desc_addr;\n\n  std::unique_ptr<ElfImageReader::NoteReader> notes = reader.Notes(10000);\n  while ((result = notes->NextNote(\n              &note_name, &note_type, &note_desc, &desc_addr)) ==\n         ElfImageReader::NoteReader::Result::kSuccess) {\n  }\n  EXPECT_EQ(result, ElfImageReader::NoteReader::Result::kNoMoreNotes);\n\n  notes = reader.Notes(0);\n  EXPECT_EQ(notes->NextNote(&note_name, &note_type, &note_desc, &desc_addr),\n            ElfImageReader::NoteReader::Result::kNoMoreNotes);\n\n  // Find the note defined in elf_image_reader_test_note.S.\n  constexpr uint32_t kCrashpadNoteDesc = 42;\n  notes = reader.NotesWithNameAndType(\n      CRASHPAD_ELF_NOTE_NAME, CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST, 10000);\n  ASSERT_EQ(notes->NextNote(&note_name, &note_type, &note_desc, &desc_addr),\n            ElfImageReader::NoteReader::Result::kSuccess);\n  EXPECT_EQ(note_name, CRASHPAD_ELF_NOTE_NAME);\n  EXPECT_EQ(note_type,\n            implicit_cast<unsigned int>(CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST));\n  EXPECT_EQ(note_desc.size(), sizeof(kCrashpadNoteDesc));\n  EXPECT_EQ(*reinterpret_cast<decltype(kCrashpadNoteDesc)*>(&note_desc[0]),\n            kCrashpadNoteDesc);\n\n  EXPECT_EQ(notes->NextNote(&note_name, &note_type, &note_desc, &desc_addr),\n            ElfImageReader::NoteReader::Result::kNoMoreNotes);\n}\n\nvoid ReadLibcInTarget(ProcessType process,\n                      VMAddress elf_address,\n                      VMAddress getpid_address) {\n#if defined(ARCH_CPU_64_BITS)\n  constexpr bool am_64_bit = true;\n#else\n  constexpr bool am_64_bit = false;\n#endif  // ARCH_CPU_64_BITS\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(process));\n  ProcessMemoryLinux memory(&connection);\n#else\n  ProcessMemoryNative memory;\n  ASSERT_TRUE(memory.Initialize(process));\n#endif\n\n  ProcessMemoryRange range;\n  ASSERT_TRUE(range.Initialize(&memory, am_64_bit));\n\n  ElfImageReader reader;\n  ASSERT_TRUE(reader.Initialize(range, elf_address));\n\n  ExpectSymbol(&reader, \"getpid\", getpid_address);\n}\n\nTEST(ElfImageReader, MainExecutableSelf) {\n  ReadThisExecutableInTarget(\n      GetSelfProcess(),\n      FromPointerCast<VMAddress>(ElfImageReaderTestExportedSymbol));\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ReadExecutableChild) {\n  VMAddress exported_symbol_address =\n      FromPointerCast<VMAddress>(ElfImageReaderTestExportedSymbol);\n  CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),\n                   &exported_symbol_address,\n                   sizeof(exported_symbol_address));\n  CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));\n  return 0;\n}\n\nclass ReadExecutableChildTest : public MultiprocessExec {\n public:\n  ReadExecutableChildTest() : MultiprocessExec() {}\n\n private:\n  void MultiprocessParent() {\n    // This read serves two purposes -- on Fuchsia, the loader may have not\n    // filled in debug address as soon as the child process handle is valid, so\n    // this causes a wait at least until the main() of the child, at which point\n    // it will always be valid. Secondarily, the address of the symbol to be\n    // looked up needs to be communicated.\n    VMAddress exported_symbol_address;\n    CheckedReadFileExactly(ReadPipeHandle(),\n                           &exported_symbol_address,\n                           sizeof(exported_symbol_address));\n    ReadThisExecutableInTarget(ChildProcess(), exported_symbol_address);\n  }\n};\n\nTEST(ElfImageReader, MainExecutableChild) {\n  ReadExecutableChildTest test;\n  test.SetChildTestMainFunction(\"ReadExecutableChild\");\n  test.Run();\n}\n\nTEST(ElfImageReader, OneModuleSelf) {\n  Dl_info info;\n  ASSERT_TRUE(dladdr(reinterpret_cast<void*>(getpid), &info)) << \"dladdr:\"\n                                                              << dlerror();\n  VMAddress elf_address = FromPointerCast<VMAddress>(info.dli_fbase);\n  ReadLibcInTarget(\n      GetSelfProcess(), elf_address, FromPointerCast<VMAddress>(getpid));\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ReadLibcChild) {\n  // Get the address of libc (by using getpid() as a representative member),\n  // and also the address of getpid() itself, and write them to the parent, so\n  // it can validate reading this information back out.\n  Dl_info info;\n  EXPECT_TRUE(dladdr(reinterpret_cast<void*>(getpid), &info))\n      << \"dladdr:\" << dlerror();\n  VMAddress elf_address = FromPointerCast<VMAddress>(info.dli_fbase);\n  VMAddress getpid_address = FromPointerCast<VMAddress>(getpid);\n\n  CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),\n                   &elf_address,\n                   sizeof(elf_address));\n  CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),\n                   &getpid_address,\n                   sizeof(getpid_address));\n  CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));\n  return 0;\n}\n\nclass ReadLibcChildTest : public MultiprocessExec {\n public:\n  ReadLibcChildTest() : MultiprocessExec() {}\n  ~ReadLibcChildTest() {}\n\n private:\n  void MultiprocessParent() {\n    VMAddress elf_address, getpid_address;\n    CheckedReadFileExactly(ReadPipeHandle(), &elf_address, sizeof(elf_address));\n    CheckedReadFileExactly(\n        ReadPipeHandle(), &getpid_address, sizeof(getpid_address));\n    ReadLibcInTarget(ChildProcess(), elf_address, getpid_address);\n  }\n};\n\nTEST(ElfImageReader, OneModuleChild) {\n  ReadLibcChildTest test;\n  test.SetChildTestMainFunction(\"ReadLibcChild\");\n  test.Run();\n}\n\n#if BUILDFLAG(IS_FUCHSIA)\n\n// crashpad_snapshot_test_both_dt_hash_styles is specially built and forced to\n// include both .hash and .gnu.hash sections. Linux, Android, and Fuchsia have\n// different defaults for which of these sections should be included; this test\n// confirms that we get the same count from both sections.\n//\n// TODO(scottmg): Investigation in https://crrev.com/c/876879 resulted in\n// realizing that ld.bfd does not emit a .gnu.hash that is very useful for this\n// purpose when there's 0 exported entries in the module. This is not likely to\n// be too important, as there's little need to look up non-exported symbols.\n// However, it makes this test not work on Linux, where the default build uses\n// ld.bfd. On Fuchsia, the only linker in use is lld, and it generates the\n// expected .gnu.hash. So, for now, this test is only run on Fuchsia, not Linux.\n//\n// TODO(scottmg): Separately, the location of the ELF on Android needs some\n// work, and then the test could also be enabled there.\nTEST(ElfImageReader, DtHashAndDtGnuHashMatch) {\n  base::FilePath module_path =\n      TestPaths::BuildArtifact(FILE_PATH_LITERAL(\"snapshot\"),\n                               FILE_PATH_LITERAL(\"both_dt_hash_styles\"),\n                               TestPaths::FileType::kLoadableModule);\n  // TODO(scottmg): Remove this when upstream Fuchsia bug ZX-1619 is resolved.\n  // See also explanation in build/run_tests.py for Fuchsia .so files.\n  module_path = module_path.BaseName();\n  ScopedModuleHandle module(\n      dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));\n  ASSERT_TRUE(module.valid()) << \"dlopen \" << module_path.value() << \": \"\n                              << dlerror();\n\n#if defined(ARCH_CPU_64_BITS)\n  constexpr bool am_64_bit = true;\n#else\n  constexpr bool am_64_bit = false;\n#endif  // ARCH_CPU_64_BITS\n\n  ProcessMemoryNative memory;\n  ASSERT_TRUE(memory.Initialize(GetSelfProcess()));\n  ProcessMemoryRange range;\n  ASSERT_TRUE(range.Initialize(&memory, am_64_bit));\n\n  struct link_map* lm = reinterpret_cast<struct link_map*>(module.get());\n\n  ElfImageReader reader;\n  ASSERT_TRUE(reader.Initialize(range, lm->l_addr));\n\n  VMSize from_dt_hash;\n  ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtHash(&from_dt_hash));\n\n  VMSize from_dt_gnu_hash;\n  ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtGnuHash(&from_dt_gnu_hash));\n\n  EXPECT_EQ(from_dt_hash, from_dt_gnu_hash);\n}\n\n#endif  // BUILDFLAG(IS_FUCHSIA)\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/elf/elf_image_reader_test_note.S",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/elf_note_types.h\"\n#include \"util/misc/arm64_pac_bti.S\"\n\n#define NOTE_ALIGN 4\n  .section .note.crashpad.test,\"a\",%note\n  .balign NOTE_ALIGN\n  .type testnote, %object\ntestnote:\n  .long name_end - name  // namesz\n  .long desc_end - desc  // descsz\n  .long CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST  // type\nname:\n  .asciz CRASHPAD_ELF_NOTE_NAME\nname_end:\n  .balign NOTE_ALIGN\ndesc:\n  .long 42\ndesc_end:\n  .size testnote, .-testnote\n"
  },
  {
    "path": "snapshot/elf/elf_symbol_table_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/elf/elf_symbol_table_reader.h\"\n\n#include <elf.h>\n\n#include \"snapshot/elf/elf_image_reader.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nuint8_t GetBinding(const Elf32_Sym& sym) {\n  return ELF32_ST_BIND(sym.st_info);\n}\n\nuint8_t GetBinding(const Elf64_Sym& sym) {\n  return ELF64_ST_BIND(sym.st_info);\n}\n\nuint8_t GetType(const Elf32_Sym& sym) {\n  return ELF32_ST_TYPE(sym.st_info);\n}\n\nuint8_t GetType(const Elf64_Sym& sym) {\n  return ELF64_ST_TYPE(sym.st_info);\n}\n\nuint8_t GetVisibility(const Elf32_Sym& sym) {\n  return ELF32_ST_VISIBILITY(sym.st_other);\n}\n\nuint8_t GetVisibility(const Elf64_Sym& sym) {\n  return ELF64_ST_VISIBILITY(sym.st_other);\n}\n\n}  // namespace\n\nElfSymbolTableReader::ElfSymbolTableReader(const ProcessMemoryRange* memory,\n                                           ElfImageReader* elf_reader,\n                                           VMAddress address,\n                                           VMSize num_entries)\n    : memory_(memory),\n      elf_reader_(elf_reader),\n      base_address_(address),\n      num_entries_(num_entries) {}\n\nElfSymbolTableReader::~ElfSymbolTableReader() {}\n\nbool ElfSymbolTableReader::GetSymbol(const std::string& name,\n                                     SymbolInformation* info) {\n  return memory_->Is64Bit() ? ScanSymbolTable<Elf64_Sym>(name, info)\n                            : ScanSymbolTable<Elf32_Sym>(name, info);\n}\n\ntemplate <typename SymEnt>\nbool ElfSymbolTableReader::ScanSymbolTable(const std::string& name,\n                                           SymbolInformation* info_out) {\n  VMAddress address = base_address_;\n  SymEnt entry;\n  std::string string;\n  size_t i = 0;\n  while (i < num_entries_ && memory_->Read(address, sizeof(entry), &entry)) {\n    if (elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string) &&\n        string == name) {\n      info_out->address = entry.st_value;\n      info_out->size = entry.st_size;\n      info_out->shndx = entry.st_shndx;\n      info_out->binding = GetBinding(entry);\n      info_out->type = GetType(entry);\n      info_out->visibility = GetVisibility(entry);\n      return true;\n    }\n    // TODO(scottmg): This should respect DT_SYMENT if present.\n    address += sizeof(entry);\n    ++i;\n  }\n  return false;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/elf/elf_symbol_table_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_ELF_ELF_SYMBOL_TABLE_READER_H_\n#define CRASHPAD_SNAPSHOT_ELF_ELF_SYMBOL_TABLE_READER_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"util/misc/address_types.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\nclass ElfImageReader;\n\n//! \\brief A reader for symbol tables in ELF images mapped into another process.\nclass ElfSymbolTableReader {\n public:\n  //! \\brief Information about a symbol in a module's symbol table.\n  struct SymbolInformation {\n    //! \\brief The address of the symbol as it exists in the symbol table, not\n    //!     adjusted for any load bias.\n    VMAddress address;\n\n    //! \\brief The size of the symbol.\n    VMSize size;\n\n    //! \\brief The section index that the symbol definition is in relation to.\n    uint16_t shndx;\n\n    //! \\brief Specifies the type of symbol. Possible values include\n    //!     `STT_OBJECT`, `STT_FUNC`, etc.\n    uint8_t type;\n\n    //! \\brief Specifies the default scope at which a symbol takes precedence.\n    //!     Possible values include `STB_LOCAL`, `STB_GLOBAL`, `STB_WEAK`, or\n    //!     OS/processor specific values.\n    uint8_t binding;\n\n    //! \\brief Together with binding, can limit the visibility of a symbol to\n    //!     the module that defines it. Possible values include `STV_DEFAULT`,\n    //!     `STV_INTERNAL`, `STV_HIDDEN`, and `STV_PROTECTED`.\n    uint8_t visibility;\n  };\n\n  // TODO(jperaza): Support using .hash and .gnu.hash sections to improve symbol\n  // lookup.\n  ElfSymbolTableReader(const ProcessMemoryRange* memory,\n                       ElfImageReader* elf_reader,\n                       VMAddress address,\n                       VMSize num_entries);\n\n  ElfSymbolTableReader(const ElfSymbolTableReader&) = delete;\n  ElfSymbolTableReader& operator=(const ElfSymbolTableReader&) = delete;\n\n  ~ElfSymbolTableReader();\n\n  //! \\brief Lookup information about a symbol.\n  //!\n  //! \\param[in] name The name of the symbol to search for.\n  //! \\param[out] info The symbol information, if found.\n  //! \\return `true` if the symbol is found.\n  bool GetSymbol(const std::string& name, SymbolInformation* info);\n\n private:\n  template <typename SymEnt>\n  bool ScanSymbolTable(const std::string& name, SymbolInformation* info);\n\n  const ProcessMemoryRange* const memory_;  // weak\n  ElfImageReader* const elf_reader_;  // weak\n  const VMAddress base_address_;\n  const VMSize num_entries_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_ELF_ELF_SYMBOL_TABLE_READER_H_\n"
  },
  {
    "path": "snapshot/elf/module_snapshot_elf.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/elf/module_snapshot_elf.h\"\n\n#include <endian.h>\n\n#include <algorithm>\n\n#include \"base/files/file_path.h\"\n#include \"snapshot/crashpad_types/image_annotation_reader.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"util/misc/elf_note_types.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nModuleSnapshotElf::ModuleSnapshotElf(const std::string& name,\n                                     ElfImageReader* elf_reader,\n                                     ModuleSnapshot::ModuleType type,\n                                     ProcessMemoryRange* process_memory_range,\n                                     const ProcessMemory* process_memory)\n    : ModuleSnapshot(),\n      name_(name),\n      elf_reader_(elf_reader),\n      process_memory_range_(process_memory_range),\n      process_memory_(process_memory),\n      crashpad_info_(),\n      type_(type),\n      initialized_(),\n      streams_() {}\n\nModuleSnapshotElf::~ModuleSnapshotElf() = default;\n\nbool ModuleSnapshotElf::Initialize() {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!elf_reader_) {\n    LOG(ERROR) << \"no elf reader\";\n    return false;\n  }\n\n  // The data payload is only sizeof(VMAddress) in the note, but add a bit to\n  // account for the name, header, and padding.\n  constexpr ssize_t kMaxNoteSize = 256;\n  std::unique_ptr<ElfImageReader::NoteReader> notes =\n      elf_reader_->NotesWithNameAndType(CRASHPAD_ELF_NOTE_NAME,\n                                        CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO,\n                                        kMaxNoteSize);\n  std::string desc;\n  VMAddress info_address;\n  VMAddress desc_address;\n  if (notes->NextNote(nullptr, nullptr, &desc, &desc_address) ==\n      ElfImageReader::NoteReader::Result::kSuccess) {\n    VMOffset offset;\n    if (elf_reader_->Memory()->Is64Bit()) {\n      offset = *reinterpret_cast<VMOffset*>(&desc[0]);\n    } else {\n      int32_t offset32 = *reinterpret_cast<int32_t*>(&desc[0]);\n      offset = offset32;\n    }\n    info_address = desc_address + offset;\n\n    ProcessMemoryRange range;\n    if (range.Initialize(*elf_reader_->Memory())) {\n      auto info = std::make_unique<CrashpadInfoReader>();\n      if (info->Initialize(&range, info_address)) {\n        crashpad_info_ = std::move(info);\n      }\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ModuleSnapshotElf::GetCrashpadOptions(CrashpadInfoClientOptions* options) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (!crashpad_info_) {\n    return false;\n  }\n\n  options->crashpad_handler_behavior =\n      crashpad_info_->CrashpadHandlerBehavior();\n  options->system_crash_reporter_forwarding =\n      crashpad_info_->SystemCrashReporterForwarding();\n  options->gather_indirectly_referenced_memory =\n      crashpad_info_->GatherIndirectlyReferencedMemory();\n  options->indirectly_referenced_memory_cap =\n      crashpad_info_->IndirectlyReferencedMemoryCap();\n  return true;\n}\n\nstd::string ModuleSnapshotElf::Name() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return name_;\n}\n\nuint64_t ModuleSnapshotElf::Address() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return elf_reader_->Address();\n}\n\nuint64_t ModuleSnapshotElf::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return elf_reader_->Size();\n}\n\ntime_t ModuleSnapshotElf::Timestamp() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return 0;\n}\n\nvoid ModuleSnapshotElf::FileVersion(uint16_t* version_0,\n                                    uint16_t* version_1,\n                                    uint16_t* version_2,\n                                    uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *version_0 = 0;\n  *version_1 = 0;\n  *version_2 = 0;\n  *version_3 = 0;\n}\n\nvoid ModuleSnapshotElf::SourceVersion(uint16_t* version_0,\n                                      uint16_t* version_1,\n                                      uint16_t* version_2,\n                                      uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *version_0 = 0;\n  *version_1 = 0;\n  *version_2 = 0;\n  *version_3 = 0;\n}\n\nModuleSnapshot::ModuleType ModuleSnapshotElf::GetModuleType() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return type_;\n}\n\nvoid ModuleSnapshotElf::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *age = 0;\n\n  auto build_id = BuildID();\n  build_id.insert(\n      build_id.end(), 16 - std::min(build_id.size(), size_t{16}), '\\0');\n  uuid->InitializeFromBytes(build_id.data());\n\n  // TODO(scottmg): https://crashpad.chromium.org/bug/229. These are\n  // endian-swapped to match FileID::ConvertIdentifierToUUIDString() in\n  // Breakpad. This is necessary as this identifier is used for symbol lookup.\n  uuid->data_1 = htobe32(uuid->data_1);\n  uuid->data_2 = htobe16(uuid->data_2);\n  uuid->data_3 = htobe16(uuid->data_3);\n}\n\nstd::string ModuleSnapshotElf::DebugFileName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return base::FilePath(Name()).BaseName().value();\n}\n\nstd::vector<uint8_t> ModuleSnapshotElf::BuildID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<ElfImageReader::NoteReader> notes =\n      elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64);\n  std::string desc;\n  VMAddress desc_addr;\n  notes->NextNote(nullptr, nullptr, &desc, &desc_addr);\n\n  std::vector<uint8_t> build_id;\n  build_id.reserve(desc.size());\n  std::copy(desc.begin(), desc.end(), std::back_inserter(build_id));\n  return build_id;\n}\n\nstd::vector<std::string> ModuleSnapshotElf::AnnotationsVector() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<std::string>();\n}\n\nstd::map<std::string, std::string> ModuleSnapshotElf::AnnotationsSimpleMap()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::map<std::string, std::string> annotations;\n  if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) {\n    ImageAnnotationReader reader(process_memory_range_);\n    reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations);\n  }\n  return annotations;\n}\n\nstd::vector<AnnotationSnapshot> ModuleSnapshotElf::AnnotationObjects() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<AnnotationSnapshot> annotations;\n  if (crashpad_info_ && crashpad_info_->AnnotationsList()) {\n    ImageAnnotationReader reader(process_memory_range_);\n    reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations);\n  }\n  return annotations;\n}\n\nstd::set<CheckedRange<uint64_t>> ModuleSnapshotElf::ExtraMemoryRanges() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::set<CheckedRange<uint64_t>>();\n}\n\nstd::vector<const UserMinidumpStream*>\nModuleSnapshotElf::CustomMinidumpStreams() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  streams_.clear();\n\n  std::vector<const UserMinidumpStream*> result;\n  if (!crashpad_info_)\n    return result;\n\n  for (uint64_t cur = crashpad_info_->UserDataMinidumpStreamHead(); cur;) {\n    internal::UserDataMinidumpStreamListEntry list_entry;\n    if (!process_memory_->Read(cur, sizeof(list_entry), &list_entry)) {\n      LOG(WARNING) << \"could not read user data stream entry from \" << name_;\n      return result;\n    }\n\n    if (list_entry.size != 0) {\n      auto memory = std::make_unique<internal::MemorySnapshotGeneric>();\n      memory->Initialize(\n          process_memory_, list_entry.base_address, list_entry.size);\n      streams_.push_back(std::make_unique<UserMinidumpStream>(\n          list_entry.stream_type, memory.release()));\n      result.push_back(streams_.back().get());\n    }\n\n    cur = list_entry.next;\n  }\n\n  return result;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/elf/module_snapshot_elf.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_\n#define CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"client/crashpad_info.h\"\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/crashpad_types/crashpad_info_reader.h\"\n#include \"snapshot/elf/elf_image_reader.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\n//! \\brief A ModuleSnapshot of a code module (binary image) loaded into a\n//!     running (or crashed) process on a system that uses ELF modules.\nclass ModuleSnapshotElf final : public ModuleSnapshot {\n public:\n  //! \\param[in] name The pathname used to load the module from disk.\n  //! \\param[in] elf_reader An image reader for the module.\n  //! \\param[in] type The module's type.\n  //! \\param[in] process_memory_range A memory reader giving protected access\n  //!     to the target process.\n  //! \\param[in] process_memory A memory reader for the target process which can\n  //!     be used to initialize a MemorySnapshot.\n  ModuleSnapshotElf(const std::string& name,\n                    ElfImageReader* elf_reader,\n                    ModuleSnapshot::ModuleType type,\n                    ProcessMemoryRange* process_memory_range,\n                    const ProcessMemory* process_memory);\n\n  ModuleSnapshotElf(const ModuleSnapshotElf&) = delete;\n  ModuleSnapshotElf& operator=(const ModuleSnapshotElf&) = delete;\n\n  ~ModuleSnapshotElf() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize();\n\n  //! \\brief Returns options from the module’s CrashpadInfo structure.\n  //!\n  //! \\param[out] options Options set in the module’s CrashpadInfo structure.\n  //! \\return `true` if there were options returned. Otherwise `false`.\n  bool GetCrashpadOptions(CrashpadInfoClientOptions* options);\n\n  // ModuleSnapshot:\n\n  std::string Name() const override;\n  uint64_t Address() const override;\n  uint64_t Size() const override;\n  time_t Timestamp() const override;\n  void FileVersion(uint16_t* version_0,\n                   uint16_t* version_1,\n                   uint16_t* version_2,\n                   uint16_t* version_3) const override;\n  void SourceVersion(uint16_t* version_0,\n                     uint16_t* version_1,\n                     uint16_t* version_2,\n                     uint16_t* version_3) const override;\n  ModuleType GetModuleType() const override;\n  void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;\n  std::string DebugFileName() const override;\n  std::vector<uint8_t> BuildID() const override;\n  std::vector<std::string> AnnotationsVector() const override;\n  std::map<std::string, std::string> AnnotationsSimpleMap() const override;\n  std::vector<AnnotationSnapshot> AnnotationObjects() const override;\n  std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;\n  std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;\n\n private:\n  std::string name_;\n  ElfImageReader* elf_reader_;\n  ProcessMemoryRange* process_memory_range_;\n  const ProcessMemory* process_memory_;\n  std::unique_ptr<CrashpadInfoReader> crashpad_info_;\n  ModuleType type_;\n  InitializationStateDcheck initialized_;\n  // Too const-y: https://crashpad.chromium.org/bug/9.\n  mutable std::vector<std::unique_ptr<const UserMinidumpStream>> streams_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_\n"
  },
  {
    "path": "snapshot/elf/test_exported_symbols.sym",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This symbol is used by elf_image_reader_test.cc.\n{\n  ElfImageReaderTestExportedSymbol;\n};\n"
  },
  {
    "path": "snapshot/exception_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_\n\n#include <stdint.h>\n\n#include <vector>\n\n#include \"snapshot/memory_snapshot.h\"\n\nnamespace crashpad {\n\nstruct CPUContext;\n\n//! \\brief An abstract interface to a snapshot representing an exception that a\n//!     snapshot process sustained and triggered the snapshot being taken.\nclass ExceptionSnapshot {\n public:\n  virtual ~ExceptionSnapshot() {}\n\n  //! \\brief Returns a CPUContext object corresponding to the exception thread’s\n  //!     CPU context at the time of the exception.\n  //!\n  //! The caller does not take ownership of this object, it is scoped to the\n  //! lifetime of the ThreadSnapshot object that it was obtained from.\n  virtual const CPUContext* Context() const = 0;\n\n  //! \\brief Returns the thread identifier of the thread that triggered the\n  //!     exception.\n  //!\n  //! This value can be compared to ThreadSnapshot::ThreadID() to associate an\n  //! ExceptionSnapshot object with the ThreadSnapshot that contains a snapshot\n  //! of the thread that triggered the exception.\n  virtual uint64_t ThreadID() const = 0;\n\n  //! \\brief Returns the top-level exception code identifying the exception.\n  //!\n  //! This is an operating system-specific value.\n  //!\n  //! For macOS, this will be an \\ref EXC_x \"EXC_*\" exception type, such as\n  //! `EXC_BAD_ACCESS`. `EXC_CRASH` will not appear here for exceptions\n  //! processed as `EXC_CRASH` when generated from another preceding exception:\n  //! the original exception code will appear instead. The exception type as it\n  //! was received will appear at index 0 of Codes().\n  //!\n  //! For Windows, this will be an `EXCEPTION_*` exception type, such as\n  //! `EXCEPTION_ACCESS_VIOLATION`.\n  virtual uint32_t Exception() const = 0;\n\n  //! \\brief Returns the second-level exception code identifying the exception.\n  //!\n  //! This is an operating system-specific value.\n  //!\n  //! For macOS, this will be the value of the exception code at index 0 as\n  //! received by a Mach exception handler, except:\n  //!  * For `EXC_CRASH` exceptions generated from another preceding exception,\n  //!    the original exception code will appear here, not the code as received\n  //!    by the Mach exception handler.\n  //!  * For `EXC_RESOURCE` and `EXC_GUARD` exceptions, the high 32 bits of the\n  //!    exception code at index 0 will appear here.\n  //!\n  //! In all cases on macOS, the full exception code at index 0 as it was\n  //! received will appear at index 1 of Codes().\n  //!\n  //! On Windows, this will either be `0` if the exception is continuable, or\n  //! `EXCEPTION_NONCONTINUABLE` to indicate a noncontinuable exception.\n  virtual uint32_t ExceptionInfo() const = 0;\n\n  //! \\brief Returns the address that triggered the exception.\n  //!\n  //! This may be the address that caused a fault on data access, or it may be\n  //! the instruction pointer that contained an offending instruction. For\n  //! exceptions where this value cannot be determined, it will be `0`.\n  //!\n  //! For macOS, this will be the value of the exception code at index 1 as\n  //! received by a Mach exception handler.\n  virtual uint64_t ExceptionAddress() const = 0;\n\n  //! \\brief Returns a series of operating system-specific exception codes.\n  //!\n  //! The precise interpretation of these codes is specific to the snapshot\n  //! operating system. These codes may provide a duplicate of information\n  //! available elsewhere, they may extend information available elsewhere, or\n  //! they may not be present at all. In this case, an empty vector will be\n  //! returned.\n  //!\n  //! For macOS, this will be a vector containing the original exception type\n  //! and the values of `code[0]` and `code[1]` as received by a Mach exception\n  //! handler.\n  //!\n  //! For Windows, these are additional arguments (if any) as provided to\n  //! `RaiseException()`. See the documentation for `ExceptionInformation` in\n  //! `EXCEPTION_RECORD`.\n  virtual const std::vector<uint64_t>& Codes() const = 0;\n\n  //! \\brief Returns a vector of additional memory blocks that should be\n  //!     included in a minidump.\n  //!\n  //! \\return A vector of MemorySnapshot objects that will be included in the\n  //!     crash dump. The caller does not take ownership of these objects, they\n  //!     are scoped to the lifetime of the ThreadSnapshot object that they\n  //!     were obtained from.\n  virtual std::vector<const MemorySnapshot*> ExtraMemory() const = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/fuchsia/cpu_context_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/cpu_context_fuchsia.h\"\n\n#include <string.h>\n\nnamespace crashpad {\nnamespace internal {\n\n#if defined(ARCH_CPU_X86_64)\n\nvoid InitializeCPUContextX86_64(\n    const zx_thread_state_general_regs_t& thread_context,\n    const zx_thread_state_fp_regs_t& float_context,\n    CPUContextX86_64* context) {\n  memset(context, 0, sizeof(*context));\n  context->rax = thread_context.rax;\n  context->rbx = thread_context.rbx;\n  context->rcx = thread_context.rcx;\n  context->rdx = thread_context.rdx;\n  context->rdi = thread_context.rdi;\n  context->rsi = thread_context.rsi;\n  context->rbp = thread_context.rbp;\n  context->rsp = thread_context.rsp;\n  context->r8 = thread_context.r8;\n  context->r9 = thread_context.r9;\n  context->r10 = thread_context.r10;\n  context->r11 = thread_context.r11;\n  context->r12 = thread_context.r12;\n  context->r13 = thread_context.r13;\n  context->r14 = thread_context.r14;\n  context->r15 = thread_context.r15;\n  context->rip = thread_context.rip;\n  context->rflags = thread_context.rflags;\n\n  context->fxsave.fcw = float_context.fcw;\n  context->fxsave.fsw = float_context.fsw;\n  context->fxsave.ftw = float_context.ftw;\n  context->fxsave.fop = float_context.fop;\n  context->fxsave.fpu_ip_64 = float_context.fip;\n  context->fxsave.fpu_dp_64 = float_context.fdp;\n\n  for (size_t i = 0; i < std::size(float_context.st); ++i) {\n    memcpy(&context->fxsave.st_mm[i],\n           &float_context.st[i],\n           sizeof(float_context.st[i]));\n  }\n}\n\n#elif defined(ARCH_CPU_ARM64)\n\nvoid InitializeCPUContextARM64(\n    const zx_thread_state_general_regs_t& thread_context,\n    const zx_thread_state_vector_regs_t& vector_context,\n    CPUContextARM64* context) {\n  memset(context, 0, sizeof(*context));\n\n  // Fuchsia stores the link register (x30) on its own while Crashpad stores it\n  // with the other general purpose x0-x28 and x29 frame pointer registers. So\n  // we expect the size and number of elements to be off by one unit.\n  static_assert(sizeof(context->regs) - sizeof(context->regs[30]) ==\n                    sizeof(thread_context.r),\n                \"registers size mismatch\");\n  static_assert((sizeof(context->regs) - sizeof(context->regs[30])) /\n                        sizeof(context->regs[0]) ==\n                    sizeof(thread_context.r) / sizeof(thread_context.r[0]),\n                \"registers number of elements mismatch\");\n  memcpy(&context->regs, &thread_context.r, sizeof(thread_context.r));\n  context->regs[30] = thread_context.lr;\n  context->sp = thread_context.sp;\n  context->pc = thread_context.pc;\n\n  // Only the NZCV flags (bits 31 to 28 respectively) of the cpsr register are\n  // readable and writable by userland on ARM64.\n  constexpr uint32_t kNZCV = 0xf0000000;\n  // Fuchsia uses the old \"cspr\" terminology from armv7 while Crashpad uses the\n  // new \"spsr\" terminology for armv8.\n  context->spsr = thread_context.cpsr & kNZCV;\n  if (thread_context.cpsr >\n      std::numeric_limits<decltype(context->spsr)>::max()) {\n    LOG(WARNING) << \"cpsr truncation: we only expect the first 32 bits to be \"\n                    \"set in the cpsr\";\n  }\n  context->spsr =\n      static_cast<decltype(context->spsr)>(thread_context.cpsr) & kNZCV;\n\n  context->fpcr = vector_context.fpcr;\n  context->fpsr = vector_context.fpsr;\n  static_assert(sizeof(context->fpsimd) == sizeof(vector_context.v),\n                \"registers size mismatch\");\n  memcpy(&context->fpsimd, &vector_context.v, sizeof(vector_context.v));\n}\n\n#elif defined(ARCH_CPU_RISCV64)\n\nvoid InitializeCPUContextRISCV64(\n    const zx_thread_state_general_regs_t& thread_context,\n    const zx_thread_state_fp_regs_t& float_context,\n    CPUContextRISCV64* context) {\n  context->pc = thread_context.pc;\n  context->regs[0] = thread_context.ra;\n  context->regs[1] = thread_context.sp;\n  context->regs[2] = thread_context.gp;\n  context->regs[3] = thread_context.tp;\n  context->regs[4] = thread_context.t0;\n  context->regs[5] = thread_context.t1;\n  context->regs[6] = thread_context.t2;\n  context->regs[7] = thread_context.s0;\n  context->regs[8] = thread_context.s1;\n  context->regs[9] = thread_context.a0;\n  context->regs[10] = thread_context.a1;\n  context->regs[11] = thread_context.a2;\n  context->regs[12] = thread_context.a3;\n  context->regs[13] = thread_context.a4;\n  context->regs[14] = thread_context.a5;\n  context->regs[15] = thread_context.a6;\n  context->regs[16] = thread_context.a7;\n  context->regs[17] = thread_context.s2;\n  context->regs[18] = thread_context.s3;\n  context->regs[19] = thread_context.s4;\n  context->regs[20] = thread_context.s5;\n  context->regs[21] = thread_context.s6;\n  context->regs[22] = thread_context.s7;\n  context->regs[23] = thread_context.s8;\n  context->regs[24] = thread_context.s9;\n  context->regs[25] = thread_context.s10;\n  context->regs[26] = thread_context.s11;\n  context->regs[27] = thread_context.t3;\n  context->regs[28] = thread_context.t4;\n  context->regs[29] = thread_context.t5;\n  context->regs[30] = thread_context.t6;\n\n  for (size_t i = 0; i < std::size(context->fpregs); ++i) {\n    context->fpregs[i] = float_context.q[i].low;\n  }\n\n  context->fcsr = float_context.fcsr;\n}\n\n#endif  // ARCH_CPU_X86_64\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/cpu_context_fuchsia.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_\n#define CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_\n\n#include <zircon/syscalls/debug.h>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/fuchsia/process_reader_fuchsia.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n#if defined(ARCH_CPU_X86_64) || DOXYGEN\n\n//! \\brief Initializes a CPUContextX86_64 structure from native context\n//!     structures on Fuchsia.\n//!\n//! Segment registers are currently initialized to zero.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] float_context The native floating point context.\n//! \\param[out] context The CPUContextX86_64 structure to initialize.\nvoid InitializeCPUContextX86_64(\n    const zx_thread_state_general_regs_t& thread_context,\n    const zx_thread_state_fp_regs_t& float_context,\n    CPUContextX86_64* context);\n\n#endif  // ARCH_CPU_X86_64 || DOXYGEN\n\n#if defined(ARCH_CPU_ARM64) || DOXYGEN\n\n//! \\brief Initializes a CPUContextARM64 structure from native context\n//!     structures on Fuchsia.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] vector_context The native vector context that also contains the\n//!                           floating point registers.\n//! \\param[out] context The CPUContextARM64 structure to initialize.\nvoid InitializeCPUContextARM64(\n    const zx_thread_state_general_regs_t& thread_context,\n    const zx_thread_state_vector_regs_t& vector_context,\n    CPUContextARM64* context);\n\n#endif  // ARCH_CPU_ARM64 || DOXYGEN\n\n#if defined(ARCH_CPU_RISCV64) || DOXYGEN\n\n//! \\brief Initializes a CPUContextRISCV64 structure from native context\n//!     structures on Fuchsia.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] float_context The native floating point context.\n//! \\param[out] context The CPUContextRISCV64 structure to initialize.\nvoid InitializeCPUContextRISCV64(\n    const zx_thread_state_general_regs_t& thread_context,\n    const zx_thread_state_fp_regs_t& float_context,\n    CPUContextRISCV64* context);\n\n#endif  // ARCH_CPU_RISCV64 || DOXYGEN\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_\n"
  },
  {
    "path": "snapshot/fuchsia/exception_snapshot_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/exception_snapshot_fuchsia.h\"\n\n#include \"base/numerics/safe_conversions.h\"\n#include \"snapshot/fuchsia/cpu_context_fuchsia.h\"\n#include \"snapshot/fuchsia/process_reader_fuchsia.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nExceptionSnapshotFuchsia::ExceptionSnapshotFuchsia() = default;\nExceptionSnapshotFuchsia::~ExceptionSnapshotFuchsia() = default;\n\nbool ExceptionSnapshotFuchsia::Initialize(\n    ProcessReaderFuchsia* process_reader,\n    zx_koid_t thread_id,\n    const zx_exception_report_t& exception_report) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  exception_ = exception_report.header.type;\n  thread_id_ = thread_id;\n\n  // TODO(scottmg): Not sure whether these values for exception_info_ are\n  // helpful or correct. Other values in the structures are stored below into\n  // Codes() in case they are useful.\n#if defined(ARCH_CPU_X86_64)\n  DCHECK(base::IsValueInRangeForNumericType<uint32_t>(\n      exception_report.context.arch.u.x86_64.err_code));\n  exception_info_ = exception_report.context.arch.u.x86_64.err_code;\n#elif defined(ARCH_CPU_ARM64)\n  exception_info_ = exception_report.context.arch.u.arm_64.esr;\n#elif defined(ARCH_CPU_RISCV64)\n  exception_info_ = exception_report.context.arch.u.riscv_64.cause;\n#endif\n\n  codes_.push_back(exception_);\n  codes_.push_back(exception_info_);\n\n#if defined(ARCH_CPU_X86_64)\n  codes_.push_back(exception_report.context.arch.u.x86_64.vector);\n  codes_.push_back(exception_report.context.arch.u.x86_64.cr2);\n#elif defined(ARCH_CPU_ARM64)\n  codes_.push_back(exception_report.context.arch.u.arm_64.far);\n#elif defined(ARCH_CPU_RISCV64)\n  codes_.push_back(exception_report.context.arch.u.riscv_64.tval);\n#endif\n\n  const auto threads = process_reader->Threads();\n  const auto& t =\n      std::find_if(threads.begin(),\n                   threads.end(),\n                   [thread_id](const ProcessReaderFuchsia::Thread& thread) {\n                     return thread.id == thread_id;\n                   });\n  if (t == threads.end()) {\n    // If no threads have been read, then context_ can't be initalized, and the\n    // exception snapshot can't be considered initialized_.\n    return false;\n  }\n\n#if defined(ARCH_CPU_X86_64)\n  context_.architecture = kCPUArchitectureX86_64;\n  context_.x86_64 = &context_arch_;\n  // TODO(fxbug.dev/42132536): Add vector context.\n  InitializeCPUContextX86_64(\n      t->general_registers, t->fp_registers, context_.x86_64);\n#elif defined(ARCH_CPU_ARM64)\n  context_.architecture = kCPUArchitectureARM64;\n  context_.arm64 = &context_arch_;\n  InitializeCPUContextARM64(\n      t->general_registers, t->vector_registers, context_.arm64);\n#elif defined(ARCH_CPU_RISCV64)\n  context_.architecture = kCPUArchitectureRISCV64;\n  context_.riscv64 = &context_arch_;\n  InitializeCPUContextRISCV64(\n      t->general_registers, t->fp_registers, context_.riscv64);\n#else\n#error Port.\n#endif\n\n  if (context_.InstructionPointer() != 0 &&\n      (exception_ == ZX_EXCP_UNDEFINED_INSTRUCTION ||\n       exception_ == ZX_EXCP_SW_BREAKPOINT ||\n       exception_ == ZX_EXCP_HW_BREAKPOINT)) {\n    exception_address_ = context_.InstructionPointer();\n  } else {\n#if defined(ARCH_CPU_X86_64)\n    exception_address_ = exception_report.context.arch.u.x86_64.cr2;\n#elif defined(ARCH_CPU_ARM64)\n    exception_address_ = exception_report.context.arch.u.arm_64.far;\n#elif defined(ARCH_CPU_RISCV64)\n    exception_address_ = exception_report.context.arch.u.riscv_64.tval;\n#else\n#error Port.\n#endif\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst CPUContext* ExceptionSnapshotFuchsia::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nuint64_t ExceptionSnapshotFuchsia::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nuint32_t ExceptionSnapshotFuchsia::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_;\n}\n\nuint32_t ExceptionSnapshotFuchsia::ExceptionInfo() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_info_;\n}\n\nuint64_t ExceptionSnapshotFuchsia::ExceptionAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_address_;\n}\n\nconst std::vector<uint64_t>& ExceptionSnapshotFuchsia::Codes() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return codes_;\n}\n\nstd::vector<const MemorySnapshot*> ExceptionSnapshotFuchsia::ExtraMemory()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<const MemorySnapshot*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/exception_snapshot_fuchsia.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_\n#define CRASHPAD_SNAPSHOT_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_\n\n#include <stdint.h>\n#include <zircon/syscalls/exception.h>\n#include <zircon/types.h>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderFuchsia;\n\nnamespace internal {\n\n//! \\brief An ExceptionSnapshot of an exception sustained by a process on a\n//!     Fuchsia system.\nclass ExceptionSnapshotFuchsia final : public ExceptionSnapshot {\n public:\n  ExceptionSnapshotFuchsia();\n\n  ExceptionSnapshotFuchsia(const ExceptionSnapshotFuchsia&) = delete;\n  ExceptionSnapshotFuchsia& operator=(const ExceptionSnapshotFuchsia&) = delete;\n\n  ~ExceptionSnapshotFuchsia() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderFuchsia for the process that\n  //!     sustained the exception.\n  //! \\param[in] thread_id The koid of the thread that sustained the exception.\n  //! \\param[in] exception_report The `zx_exception_report_t` retrieved from the\n  //!     thread in the exception state, corresponding to \\a thread_id.\n  //!\n  //! \\return `true` if the exception data was initialized, `false` otherwise\n  //!     with an error logged.\n  bool Initialize(ProcessReaderFuchsia* process_reader,\n                  zx_koid_t thread_id,\n                  const zx_exception_report_t& exception_report);\n\n  // ExceptionSnapshot:\n  const CPUContext* Context() const override;\n  uint64_t ThreadID() const override;\n  uint32_t Exception() const override;\n  uint32_t ExceptionInfo() const override;\n  uint64_t ExceptionAddress() const override;\n  const std::vector<uint64_t>& Codes() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n#if defined(ARCH_CPU_X86_64)\n  CPUContextX86_64 context_arch_;\n#elif defined(ARCH_CPU_ARM64)\n  CPUContextARM64 context_arch_;\n#elif defined(ARCH_CPU_RISCV64)\n  CPUContextRISCV64 context_arch_;\n#endif\n  CPUContext context_;\n  std::vector<uint64_t> codes_;\n  zx_koid_t thread_id_;\n  zx_vaddr_t exception_address_;\n  uint32_t exception_;\n  uint32_t exception_info_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_\n"
  },
  {
    "path": "snapshot/fuchsia/memory_map_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/memory_map_fuchsia.h\"\n\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\n\nMemoryMapFuchsia::MemoryMapFuchsia() = default;\n\nMemoryMapFuchsia::~MemoryMapFuchsia() = default;\n\nbool MemoryMapFuchsia::Initialize(const zx::process& process) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  // There's no way to know what an appropriate buffer size is before starting.\n  // Start at a size that should be more than enough for any reasonable process.\n  map_entries_.resize(4096);\n\n  // Retrieving the maps is racy with new mappings being created, so retry this\n  // loop up to |tries| times until the number of actual mappings retrieved\n  // matches those available.\n  int tries = 5;\n  for (;;) {\n    size_t actual;\n    size_t available;\n    zx_status_t status =\n        process.get_info(ZX_INFO_PROCESS_MAPS,\n                         &map_entries_[0],\n                         map_entries_.size() * sizeof(map_entries_[0]),\n                         &actual,\n                         &available);\n    if (status != ZX_OK) {\n      ZX_LOG(ERROR, status) << \"zx_object_get_info ZX_INFO_PROCESS_MAPS\";\n      map_entries_.clear();\n      return false;\n    }\n    if (actual < available && tries-- > 0) {\n      // Make the buffer slightly larger than |available| to attempt to account\n      // for the race between here and the next retrieval.\n      map_entries_.resize(available + 20);\n      continue;\n    }\n\n    map_entries_.resize(actual);\n\n    INITIALIZATION_STATE_SET_VALID(initialized_);\n    return true;\n  }\n}\n\nbool MemoryMapFuchsia::FindMappingForAddress(zx_vaddr_t address,\n                                             zx_info_maps_t* map) const {\n  bool found = false;\n  zx_info_maps_t result = {};\n  for (const auto& m : map_entries_) {\n    CheckedRange<zx_vaddr_t, size_t> range(m.base, m.size);\n    if (range.ContainsValue(address)) {\n      if (!found || m.depth > result.depth) {\n        result = m;\n        found = true;\n      }\n    }\n  }\n\n  if (found) {\n    *map = result;\n  }\n  return found;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/memory_map_fuchsia.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_\n#define CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_\n\n#include <lib/zx/process.h>\n#include <zircon/syscalls/object.h>\n\n#include <vector>\n\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\n//! \\brief A list of mappings in the address space of a Fuchsia process.\nclass MemoryMapFuchsia {\n public:\n  MemoryMapFuchsia();\n\n  MemoryMapFuchsia(const MemoryMapFuchsia&) = delete;\n  MemoryMapFuchsia& operator=(const MemoryMapFuchsia&) = delete;\n\n  ~MemoryMapFuchsia();\n\n  //! \\brief Initializes this object with information about the mapped memory\n  //!     regions in the given process.\n  //!\n  //! \\return `true` on success, or `false`, with an error logged.\n  bool Initialize(const zx::process& process);\n\n  //! \\brief Searches through the previously retrieved memory map for the given\n  //!     address. If found, returns the deepest `zx_info_maps_t` mapping that\n  //!     contains \\a address.\n  //!\n  //! \\param[in] address The address to locate.\n  //! \\param[out] map The `zx_info_maps_t` data corresponding to the address.\n  //! \\return `true` if a mapping for \\a address was found, in which case \\a map\n  //!     will be filled out, otherwise `false` and \\a map will be unchanged.\n  bool FindMappingForAddress(zx_vaddr_t address, zx_info_maps_t* map) const;\n\n  //! \\brief Get a vector of `zx_info_maps_t` representing the memory map for\n  //!     this process.\n  const std::vector<zx_info_maps_t>& Entries() const { return map_entries_; }\n\n private:\n  std::vector<zx_info_maps_t> map_entries_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_\n"
  },
  {
    "path": "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h\"\n\n#include <iterator>\n\n#include \"base/check_op.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\n// Maps from bitwise OR of Zircon's flags to enumerated Windows version.\nuint32_t MmuFlagsToProtectFlags(zx_vm_option_t flags) {\n  // These bits are currently the lowest 3 of zx_vm_option_t. They're used to\n  // index into a mapping array, so make sure that we notice if they change\n  // values.\n  static_assert(\n      ZX_VM_PERM_READ == 1 && ZX_VM_PERM_WRITE == 2 && ZX_VM_PERM_EXECUTE == 4,\n      \"table below will need fixing\");\n\n  // The entries set to zero don't have good corresponding Windows minidump\n  // names. They also aren't currently supported by the mapping syscall on\n  // Zircon, so DCHECK that they don't happen in practice. EXECUTE-only also\n  // cannot currently happen, but as that has a good mapping to the Windows\n  // value, leave it in place in case it's supported by the syscall in the\n  // future.\n  static constexpr uint32_t mapping[] = {\n      /* --- */ PAGE_NOACCESS,\n      /* --r */ PAGE_READONLY,\n      /* -w- */ 0,\n      /* -wr */ PAGE_READWRITE,\n      /* x-- */ PAGE_EXECUTE,\n      /* x-r */ PAGE_EXECUTE_READ,\n      /* xw- */ 0,\n      /* xwr */ PAGE_EXECUTE_READWRITE,\n  };\n\n  const uint32_t index =\n      flags & (ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE);\n  DCHECK_LT(index, std::size(mapping));\n\n  const uint32_t protect_flags = mapping[index];\n  DCHECK_NE(protect_flags, 0u);\n  return protect_flags;\n}\n\n}  // namespace\n\nMemoryMapRegionSnapshotFuchsia::MemoryMapRegionSnapshotFuchsia(\n    const zx_info_maps_t& info_map)\n    : memory_info_() {\n  DCHECK_EQ(info_map.type, ZX_INFO_MAPS_TYPE_MAPPING);\n\n  memory_info_.BaseAddress = info_map.base;\n  memory_info_.AllocationBase = info_map.base;\n  memory_info_.RegionSize = info_map.size;\n  memory_info_.State = MEM_COMMIT;\n  memory_info_.Protect = memory_info_.AllocationProtect =\n      MmuFlagsToProtectFlags(info_map.u.mapping.mmu_flags);\n  memory_info_.Type = MEM_MAPPED;\n}\n\nMemoryMapRegionSnapshotFuchsia::~MemoryMapRegionSnapshotFuchsia() {}\n\nconst MINIDUMP_MEMORY_INFO&\nMemoryMapRegionSnapshotFuchsia::AsMinidumpMemoryInfo() const {\n  return memory_info_;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_\n#define CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_\n\n#include \"snapshot/memory_map_region_snapshot.h\"\n\n#include <zircon/syscalls/object.h>\n\nnamespace crashpad {\nnamespace internal {\n\nclass MemoryMapRegionSnapshotFuchsia : public MemoryMapRegionSnapshot {\n public:\n  explicit MemoryMapRegionSnapshotFuchsia(const zx_info_maps_t& info_map);\n  ~MemoryMapRegionSnapshotFuchsia() override;\n\n  virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override;\n\n private:\n  MINIDUMP_MEMORY_INFO memory_info_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_\n"
  },
  {
    "path": "snapshot/fuchsia/process_reader_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/process_reader_fuchsia.h\"\n\n#include <lib/zx/thread.h>\n#include <link.h>\n#include <zircon/status.h>\n#include <zircon/syscalls.h>\n\n#include \"base/check_op.h\"\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"base/logging.h\"\n#include \"util/fuchsia/koid_utilities.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Based on the thread's SP and the process's memory map, attempts to figure out\n// the stack regions for the thread. Fuchsia's C ABI specifies\n// https://fuchsia.googlesource.com/zircon/+/master/docs/safestack.md so the\n// callstack and locals-that-have-their-address-taken are in two different\n// stacks.\nvoid GetStackRegions(\n    const zx_thread_state_general_regs_t& regs,\n    const MemoryMapFuchsia& memory_map,\n    std::vector<CheckedRange<zx_vaddr_t, size_t>>* stack_regions) {\n  stack_regions->clear();\n\n  uint64_t sp;\n#if defined(ARCH_CPU_X86_64)\n  sp = regs.rsp;\n#elif defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_RISCV64)\n  sp = regs.sp;\n#else\n#error Port\n#endif\n\n  // TODO(fxbug.dev/42154629): make this work for stack overflows, e.g., by\n  // looking up using the initial stack pointer (sp) when the thread was\n  // created. Right now, it gets the stack by getting the mapping that contains\n  // the current sp. But in the case of stack overflows, the current sp is by\n  // definition outside of the stack so the mapping returned is not the stack\n  // and fails the type check, at least on arm64.\n  zx_info_maps_t range_with_sp;\n  if (!memory_map.FindMappingForAddress(sp, &range_with_sp)) {\n    LOG(ERROR) << \"stack pointer not found in mapping\";\n    return;\n  }\n\n  if (range_with_sp.type != ZX_INFO_MAPS_TYPE_MAPPING) {\n    LOG(ERROR) << \"stack range has unexpected type \" << range_with_sp.type\n               << \", stack overflow? Aborting\";\n    return;\n  }\n\n  if (range_with_sp.u.mapping.mmu_flags & ZX_VM_PERM_EXECUTE) {\n    LOG(ERROR)\n        << \"stack range is unexpectedly marked executable, continuing anyway\";\n  }\n\n  // The stack covers [range_with_sp.base, range_with_sp.base +\n  // range_with_sp.size). The stack pointer (sp) can be anywhere in that range.\n  // It starts at the end of the range (range_with_sp.base + range_with_sp.size)\n  // and goes downwards until range_with_sp.base. Capture the part of the stack\n  // that is currently used: [sp, range_with_sp.base + range_with_sp.size).\n\n  // Capture up to kExtraCaptureSize additional bytes of stack, but only if\n  // present in the region that was already found.\n  constexpr uint64_t kExtraCaptureSize = 128;\n  const uint64_t start_address =\n      std::max(sp >= kExtraCaptureSize ? sp - kExtraCaptureSize : sp,\n               range_with_sp.base);\n  const size_t region_size =\n      range_with_sp.size - (start_address - range_with_sp.base);\n\n  // Because most Fuchsia processes use safestack, it is very unlikely that a\n  // stack this large would be valid. Even if it were, avoid creating\n  // unreasonably large dumps by artificially limiting the captured amount.\n  constexpr uint64_t kMaxStackCapture = 1048576u;\n  LOG_IF(ERROR, region_size > kMaxStackCapture)\n      << \"clamping unexpectedly large stack capture of \" << region_size;\n  const size_t clamped_region_size = std::min(region_size, kMaxStackCapture);\n  stack_regions->push_back(\n      CheckedRange<zx_vaddr_t, size_t>(start_address, clamped_region_size));\n\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196, once the retrievable\n  // registers include FS and similar for ARM, retrieve the region for the\n  // unsafe part of the stack too.\n}\n\n}  // namespace\n\nProcessReaderFuchsia::Module::Module() = default;\n\nProcessReaderFuchsia::Module::~Module() = default;\n\nProcessReaderFuchsia::Thread::Thread() = default;\n\nProcessReaderFuchsia::Thread::~Thread() = default;\n\nProcessReaderFuchsia::ProcessReaderFuchsia() = default;\n\nProcessReaderFuchsia::~ProcessReaderFuchsia() = default;\n\nbool ProcessReaderFuchsia::Initialize(const zx::process& process) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  process_ = zx::unowned_process(process);\n\n  process_memory_.reset(new ProcessMemoryFuchsia());\n  process_memory_->Initialize(*process_);\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst std::vector<ProcessReaderFuchsia::Module>&\nProcessReaderFuchsia::Modules() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (!initialized_modules_) {\n    InitializeModules();\n  }\n\n  return modules_;\n}\n\nconst std::vector<ProcessReaderFuchsia::Thread>&\nProcessReaderFuchsia::Threads() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (!initialized_threads_) {\n    InitializeThreads();\n  }\n\n  return threads_;\n}\n\nconst MemoryMapFuchsia* ProcessReaderFuchsia::MemoryMap() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (!initialized_memory_map_) {\n    InitializeMemoryMap();\n  }\n\n  return memory_map_.get();\n}\n\nvoid ProcessReaderFuchsia::InitializeModules() {\n  DCHECK(!initialized_modules_);\n  DCHECK(modules_.empty());\n\n  initialized_modules_ = true;\n\n  // TODO(scottmg): <inspector/inspector.h> does some of this, but doesn't\n  // expose any of the data that's necessary to fill out a Module after it\n  // retrieves (some of) the data into internal structures. It may be worth\n  // trying to refactor/upstream some of this into Fuchsia.\n\n  // Starting from the ld.so's _dl_debug_addr, read the link_map structure and\n  // walk the list to fill out modules_.\n\n  uintptr_t debug_address;\n  zx_status_t status = process_->get_property(\n      ZX_PROP_PROCESS_DEBUG_ADDR, &debug_address, sizeof(debug_address));\n  if (status != ZX_OK || debug_address == 0) {\n    ZX_LOG(ERROR, status)\n        << \"zx_object_get_property ZX_PROP_PROCESS_DEBUG_ADDR\";\n    return;\n  }\n\n  constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map);\n  uintptr_t map;\n  if (!process_memory_->Read(\n          debug_address + k_r_debug_map_offset, sizeof(map), &map)) {\n    LOG(ERROR) << \"read link_map\";\n    return;\n  }\n\n  int i = 0;\n  constexpr int kMaxDso = 1000;  // Stop after an unreasonably large number.\n  while (map != 0) {\n    if (++i >= kMaxDso) {\n      LOG(ERROR) << \"possibly circular dso list, terminating\";\n      return;\n    }\n\n    constexpr auto k_link_map_addr_offset = offsetof(link_map, l_addr);\n    zx_vaddr_t base;\n    if (!process_memory_->Read(\n            map + k_link_map_addr_offset, sizeof(base), &base)) {\n      LOG(ERROR) << \"Read base\";\n      // Could theoretically continue here, but realistically if any part of\n      // link_map fails to read, things are looking bad, so just abort.\n      break;\n    }\n\n    constexpr auto k_link_map_next_offset = offsetof(link_map, l_next);\n    zx_vaddr_t next;\n    if (!process_memory_->Read(\n            map + k_link_map_next_offset, sizeof(next), &next)) {\n      LOG(ERROR) << \"Read next\";\n      break;\n    }\n\n    constexpr auto k_link_map_name_offset = offsetof(link_map, l_name);\n    zx_vaddr_t name_address;\n    if (!process_memory_->Read(map + k_link_map_name_offset,\n                               sizeof(name_address),\n                               &name_address)) {\n      LOG(ERROR) << \"Read name address\";\n      break;\n    }\n\n    std::string dsoname;\n    if (!process_memory_->ReadCString(name_address, &dsoname)) {\n      // In this case, it could be reasonable to continue on to the next module\n      // as this data isn't strictly in the link_map.\n      LOG(ERROR) << \"ReadCString name\";\n    }\n\n    // Debug symbols are indexed by module name x build-id on the crash server.\n    // The module name in the indexed Breakpad files is set at build time. So\n    // Crashpad needs to use the same module name at run time for symbol\n    // resolution to work properly.\n    //\n    // TODO: https://fxbug.dev/42138764 - once Crashpad switches to elf-search,\n    // the following overwrites won't be necessary as only shared libraries will\n    // have a soname at runtime, just like at build time.\n    //\n    // * For shared libraries, the soname is used as module name at build time,\n    //   which is the dsoname here except for libzircon.so (because it is\n    //   injected by the kernel, its load name is \"<vDSO>\" and Crashpad needs to\n    //   replace it for symbol resolution to work properly).\n    if (dsoname == \"<vDSO>\") {\n      dsoname = \"libzircon.so\";\n    }\n    // * For executables and loadable modules, the dummy value \"<_>\" is used as\n    //   module name at build time. This is because executable and loadable\n    //   modules don't have a name on Fuchsia. So we need to use the same dummy\n    //   value at build and run times.\n    //   Most executables have an empty dsoname. Loadable modules (and some rare\n    //   executables) have a non-empty dsoname starting with a specific prefix,\n    //   which Crashpas can use to identify loadable modules and clear the\n    //   dsoname for them.\n    static constexpr const char kLoadableModuleLoadNamePrefix[] = \"<VMO#\";\n    // Pre-C++ 20 std::basic_string::starts_with\n    if (dsoname.compare(0,\n                        strlen(kLoadableModuleLoadNamePrefix),\n                        kLoadableModuleLoadNamePrefix) == 0) {\n      dsoname = \"\";\n    }\n\n    Module module;\n    if (dsoname.empty()) {\n      // This value must be kept in sync with what is used at build time to\n      // index symbols for executables and loadable modules.\n      module.name = \"<_>\";\n      module.type = ModuleSnapshot::kModuleTypeExecutable;\n    } else {\n      module.name = dsoname;\n      // TODO(scottmg): Handle kModuleTypeDynamicLoader.\n      module.type = ModuleSnapshot::kModuleTypeSharedLibrary;\n    }\n\n    std::unique_ptr<ElfImageReader> reader(new ElfImageReader());\n\n    std::unique_ptr<ProcessMemoryRange> process_memory_range(\n        new ProcessMemoryRange());\n    // TODO(scottmg): Could this be limited range?\n    if (process_memory_range->Initialize(process_memory_.get(), true)) {\n      process_memory_ranges_.push_back(std::move(process_memory_range));\n\n      if (reader->Initialize(*process_memory_ranges_.back(), base)) {\n        module.reader = reader.get();\n        module_readers_.push_back(std::move(reader));\n        modules_.push_back(module);\n      }\n    }\n\n    map = next;\n  }\n}\n\nvoid ProcessReaderFuchsia::InitializeThreads() {\n  DCHECK(!initialized_threads_);\n  DCHECK(threads_.empty());\n\n  initialized_threads_ = true;\n\n  std::vector<zx_koid_t> thread_koids =\n      GetChildKoids(*process_, ZX_INFO_PROCESS_THREADS);\n  std::vector<zx::thread> thread_handles =\n      GetHandlesForThreadKoids(*process_, thread_koids);\n  DCHECK_EQ(thread_koids.size(), thread_handles.size());\n\n  for (size_t i = 0; i < thread_handles.size(); ++i) {\n    Thread thread;\n    thread.id = thread_koids[i];\n\n    if (thread_handles[i].is_valid()) {\n      char name[ZX_MAX_NAME_LEN] = {0};\n      zx_status_t status =\n          thread_handles[i].get_property(ZX_PROP_NAME, &name, sizeof(name));\n      if (status != ZX_OK) {\n        ZX_LOG(WARNING, status) << \"zx_object_get_property ZX_PROP_NAME\";\n      } else {\n        thread.name.assign(name);\n      }\n\n      zx_info_thread_t thread_info;\n      status = thread_handles[i].get_info(\n          ZX_INFO_THREAD, &thread_info, sizeof(thread_info), nullptr, nullptr);\n      if (status != ZX_OK) {\n        ZX_LOG(WARNING, status) << \"zx_object_get_info ZX_INFO_THREAD\";\n      } else {\n        thread.state = thread_info.state;\n      }\n\n      zx_thread_state_general_regs_t general_regs;\n      status = thread_handles[i].read_state(\n          ZX_THREAD_STATE_GENERAL_REGS, &general_regs, sizeof(general_regs));\n      if (status != ZX_OK) {\n        ZX_LOG(WARNING, status)\n            << \"zx_thread_read_state(ZX_THREAD_STATE_GENERAL_REGS)\";\n      } else {\n        thread.general_registers = general_regs;\n\n        const MemoryMapFuchsia* memory_map = MemoryMap();\n        if (memory_map) {\n          // Attempt to retrive stack regions if a memory map was retrieved. In\n          // particular, this may be null when operating on the current process\n          // where the memory map will not be able to be retrieved.\n          GetStackRegions(general_regs, *memory_map, &thread.stack_regions);\n        }\n      }\n\n// Floating point registers are in the vector context for ARM.\n#if !defined(ARCH_CPU_ARM64)\n      zx_thread_state_fp_regs_t fp_regs;\n      status = thread_handles[i].read_state(\n          ZX_THREAD_STATE_FP_REGS, &fp_regs, sizeof(fp_regs));\n      if (status != ZX_OK) {\n        ZX_LOG(WARNING, status)\n            << \"zx_thread_read_state(ZX_THREAD_STATE_FP_REGS)\";\n      } else {\n        thread.fp_registers = fp_regs;\n      }\n#endif\n\n      zx_thread_state_vector_regs_t vector_regs;\n      status = thread_handles[i].read_state(\n          ZX_THREAD_STATE_VECTOR_REGS, &vector_regs, sizeof(vector_regs));\n      if (status != ZX_OK) {\n        ZX_LOG(WARNING, status)\n            << \"zx_thread_read_state(ZX_THREAD_STATE_VECTOR_REGS)\";\n      } else {\n        thread.vector_registers = vector_regs;\n      }\n    }\n\n    threads_.push_back(thread);\n  }\n}\n\nvoid ProcessReaderFuchsia::InitializeMemoryMap() {\n  DCHECK(!initialized_memory_map_);\n\n  initialized_memory_map_ = true;\n\n  memory_map_.reset(new MemoryMapFuchsia);\n  if (!memory_map_->Initialize(*process_)) {\n    memory_map_.reset();\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/process_reader_fuchsia.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_\n#define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_\n\n#include <lib/zx/process.h>\n#include <zircon/syscalls/debug.h>\n\n#include <memory>\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"snapshot/elf/elf_image_reader.h\"\n#include \"snapshot/fuchsia/memory_map_fuchsia.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/numeric/checked_range.h\"\n#include \"util/process/process_memory_fuchsia.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n//! \\brief Accesses information about another process, identified by a Fuchsia\n//!     process.\nclass ProcessReaderFuchsia {\n public:\n  //! \\brief Contains information about a module loaded into a process.\n  struct Module {\n    Module();\n    ~Module();\n\n    //! \\brief The `ZX_PROP_NAME` of the module.\n    std::string name;\n\n    //! \\brief An image reader for the module.\n    //!\n    //! The lifetime of this ElfImageReader is scoped to the lifetime of the\n    //! ProcessReaderFuchsia that created it.\n    //!\n    //! This field may be `nullptr` if a reader could not be created for the\n    //! module.\n    ElfImageReader* reader;\n\n    //! \\brief The module's type.\n    ModuleSnapshot::ModuleType type = ModuleSnapshot::kModuleTypeUnknown;\n  };\n\n  //! \\brief Contains information about a thread that belongs to a process.\n  struct Thread {\n    Thread();\n    ~Thread();\n\n    //! \\brief The kernel identifier for the thread.\n    zx_koid_t id = ZX_KOID_INVALID;\n\n    //! \\brief The state of the thread, the `ZX_THREAD_STATE_*` value or `-1` if\n    //!     the value could not be retrieved.\n    uint32_t state = -1;\n\n    //! \\brief The `ZX_PROP_NAME` property of the thread. This may be empty.\n    std::string name;\n\n    //! \\brief The raw architecture-specific `zx_thread_state_general_regs_t` as\n    //!     returned by `zx_thread_read_state()`.\n    zx_thread_state_general_regs_t general_registers = {};\n\n    //! \\brief The raw architecture-specific `zx_thread_state_fp_regs_t` as\n    //!     returned by `zx_thread_read_state()`.\n    zx_thread_state_fp_regs_t fp_registers = {};\n\n    //! \\brief The raw architecture-specific `zx_thread_state_vector_regs_t` as\n    //!     returned by `zx_thread_read_state()`.\n    zx_thread_state_vector_regs_t vector_registers = {};\n\n    //! \\brief The regions representing the stack. The first entry in the vector\n    //!     represents the callstack, and further entries optionally identify\n    //!     other stack data when the thread uses a split stack representation.\n    std::vector<CheckedRange<zx_vaddr_t, size_t>> stack_regions;\n  };\n\n  ProcessReaderFuchsia();\n\n  ProcessReaderFuchsia(const ProcessReaderFuchsia&) = delete;\n  ProcessReaderFuchsia& operator=(const ProcessReaderFuchsia&) = delete;\n\n  ~ProcessReaderFuchsia();\n\n  //! \\brief Initializes this object. This method must be called before any\n  //!     other.\n  //!\n  //! \\param[in] process A process handle with permissions to read properties\n  //!     and memory from the target process.\n  //!\n  //! \\return `true` on success, indicating that this object will respond\n  //!     validly to further method calls. `false` on failure. On failure, no\n  //!     further method calls should be made.\n  bool Initialize(const zx::process& process);\n\n  //! \\return The modules loaded in the process. The first element (at index\n  //!     `0`) corresponds to the main executable.\n  const std::vector<Module>& Modules();\n\n  //! \\return The threads that are in the process.\n  const std::vector<Thread>& Threads();\n\n  //! \\brief Return a memory reader for the target process.\n  const ProcessMemory* Memory() const { return process_memory_.get(); }\n\n  //! \\brief Return a memory map for the target process.\n  const MemoryMapFuchsia* MemoryMap();\n\n private:\n  //! Performs lazy initialization of the \\a modules_ vector on behalf of\n  //! Modules().\n  void InitializeModules();\n\n  //! Performs lazy initialization of the \\a threads_ vector on behalf of\n  //! Threads().\n  void InitializeThreads();\n\n  //! Performs lazy initialization of the \\a memory_map_ on behalf of\n  //! MemoryMap().\n  void InitializeMemoryMap();\n\n  std::vector<Module> modules_;\n  std::vector<Thread> threads_;\n  std::vector<std::unique_ptr<ElfImageReader>> module_readers_;\n  std::vector<std::unique_ptr<ProcessMemoryRange>> process_memory_ranges_;\n  std::unique_ptr<ProcessMemoryFuchsia> process_memory_;\n  std::unique_ptr<MemoryMapFuchsia> memory_map_;\n  zx::unowned_process process_;\n  bool initialized_modules_ = false;\n  bool initialized_threads_ = false;\n  bool initialized_memory_map_ = false;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_\n"
  },
  {
    "path": "snapshot/fuchsia/process_reader_fuchsia_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/process_reader_fuchsia.h\"\n\n#include <pthread.h>\n#include <zircon/process.h>\n#include <zircon/syscalls.h>\n#include <zircon/syscalls/port.h>\n#include <zircon/types.h>\n\n#include <iterator>\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"test/scoped_set_thread_name.h\"\n#include \"test/test_paths.h\"\n#include \"util/fuchsia/scoped_task_suspend.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ProcessReaderFuchsia, SelfBasic) {\n  const ScopedSetThreadName scoped_set_thread_name(\"SelfBasic\");\n\n  ProcessReaderFuchsia process_reader;\n  ASSERT_TRUE(process_reader.Initialize(*zx::process::self()));\n\n  static constexpr char kTestMemory[] = \"Some test memory\";\n  char buffer[std::size(kTestMemory)];\n  ASSERT_TRUE(process_reader.Memory()->Read(\n      reinterpret_cast<zx_vaddr_t>(kTestMemory), sizeof(kTestMemory), &buffer));\n  EXPECT_STREQ(kTestMemory, buffer);\n\n  const auto& modules = process_reader.Modules();\n  // The process should have at least one module, the executable, and then some\n  // shared libraries, no loadable modules.\n  EXPECT_GT(modules.size(), 0u);\n  size_t num_executables = 0u;\n  size_t num_shared_libraries = 0u;\n  for (const auto& module : modules) {\n    EXPECT_FALSE(module.name.empty());\n    EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown);\n\n    if (module.type == ModuleSnapshot::kModuleTypeExecutable) {\n      EXPECT_EQ(module.name, \"<_>\");\n      num_executables++;\n    } else if (module.type == ModuleSnapshot::kModuleTypeSharedLibrary) {\n      EXPECT_NE(module.name, \"<_>\");\n      num_shared_libraries++;\n    }\n  }\n  EXPECT_EQ(num_executables, 1u);\n  EXPECT_EQ(num_shared_libraries, modules.size() - num_executables);\n\n  const auto& threads = process_reader.Threads();\n  EXPECT_GT(threads.size(), 0u);\n\n  zx_info_handle_basic_t info;\n  ASSERT_EQ(zx_object_get_info(zx_thread_self(),\n                               ZX_INFO_HANDLE_BASIC,\n                               &info,\n                               sizeof(info),\n                               nullptr,\n                               nullptr),\n            ZX_OK);\n  EXPECT_EQ(threads[0].id, info.koid);\n  EXPECT_EQ(threads[0].state, ZX_THREAD_STATE_RUNNING);\n  EXPECT_EQ(threads[0].name, \"SelfBasic\");\n}\n\nconstexpr char kTestMemory[] = \"Read me from another process\";\n\nCRASHPAD_CHILD_TEST_MAIN(ProcessReaderBasicChildTestMain) {\n  zx_vaddr_t addr = reinterpret_cast<zx_vaddr_t>(kTestMemory);\n  CheckedWriteFile(\n      StdioFileHandle(StdioStream::kStandardOutput), &addr, sizeof(addr));\n  CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));\n  return 0;\n}\n\nclass BasicChildTest : public MultiprocessExec {\n public:\n  BasicChildTest() : MultiprocessExec() {\n    SetChildTestMainFunction(\"ProcessReaderBasicChildTestMain\");\n  }\n\n  BasicChildTest(const BasicChildTest&) = delete;\n  BasicChildTest& operator=(const BasicChildTest&) = delete;\n\n  ~BasicChildTest() {}\n\n private:\n  void MultiprocessParent() override {\n    ProcessReaderFuchsia process_reader;\n    ASSERT_TRUE(process_reader.Initialize(*ChildProcess()));\n\n    zx_vaddr_t addr;\n    ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &addr, sizeof(addr)));\n\n    std::string read_string;\n    ASSERT_TRUE(process_reader.Memory()->ReadCString(addr, &read_string));\n    EXPECT_EQ(read_string, kTestMemory);\n  }\n};\n\nTEST(ProcessReaderFuchsia, ChildBasic) {\n  BasicChildTest test;\n  test.Run();\n}\n\nstruct ThreadData {\n  zx_handle_t port;\n  std::string name;\n};\n\nvoid* SignalAndSleep(void* arg) {\n  const ThreadData* thread_data = reinterpret_cast<const ThreadData*>(arg);\n  const ScopedSetThreadName scoped_set_thread_name(thread_data->name);\n  zx_port_packet_t packet = {};\n  packet.type = ZX_PKT_TYPE_USER;\n  zx_port_queue(thread_data->port, &packet);\n  zx_nanosleep(ZX_TIME_INFINITE);\n  return nullptr;\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ProcessReaderChildThreadsTestMain) {\n  const ScopedSetThreadName scoped_set_thread_name(\n      \"ProcessReaderChildThreadsTest-Main\");\n\n  // Create 5 threads with stack sizes of 4096, 8192, ...\n  zx_handle_t port;\n  zx_status_t status = zx_port_create(0, &port);\n  EXPECT_EQ(status, ZX_OK);\n\n  constexpr size_t kNumThreads = 5;\n  struct ThreadData thread_data[kNumThreads] = {{0, \"\"}};\n\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    thread_data[i] = {\n        .port = port,\n        .name = base::StringPrintf(\"ProcessReaderChildThreadsTest-%zu\", i + 1),\n    };\n    pthread_attr_t attr;\n    EXPECT_EQ(pthread_attr_init(&attr), 0);\n    EXPECT_EQ(pthread_attr_setstacksize(&attr, (i + 1) * 4096), 0);\n    pthread_t thread;\n    EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &thread_data[i]),\n              0);\n  }\n\n  // Wait until all threads are ready.\n  for (size_t i = 0; i < kNumThreads; ++i) {\n    zx_port_packet_t packet;\n    zx_port_wait(port, ZX_TIME_INFINITE, &packet);\n  }\n\n  char c = ' ';\n  CheckedWriteFile(\n      StdioFileHandle(StdioStream::kStandardOutput), &c, sizeof(c));\n  CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));\n  return 0;\n}\n\nclass ThreadsChildTest : public MultiprocessExec {\n public:\n  ThreadsChildTest() : MultiprocessExec() {\n    SetChildTestMainFunction(\"ProcessReaderChildThreadsTestMain\");\n  }\n\n  ThreadsChildTest(const ThreadsChildTest&) = delete;\n  ThreadsChildTest& operator=(const ThreadsChildTest&) = delete;\n\n  ~ThreadsChildTest() {}\n\n private:\n  void MultiprocessParent() override {\n    char c;\n    ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &c, 1));\n    ASSERT_EQ(c, ' ');\n\n    ScopedTaskSuspend suspend(*ChildProcess());\n\n    ProcessReaderFuchsia process_reader;\n    ASSERT_TRUE(process_reader.Initialize(*ChildProcess()));\n\n    const auto& threads = process_reader.Threads();\n    EXPECT_EQ(threads.size(), 6u);\n\n    EXPECT_EQ(threads[0].name, \"ProcessReaderChildThreadsTest-main\");\n\n    for (size_t i = 1; i < 6; ++i) {\n      ASSERT_GT(threads[i].stack_regions.size(), 0u);\n      EXPECT_GT(threads[i].stack_regions[0].size(), 0u);\n      EXPECT_LE(threads[i].stack_regions[0].size(), i * 4096u);\n      EXPECT_EQ(threads[i].name,\n                base::StringPrintf(\"ProcessReaderChildThreadsTest-%zu\", i));\n    }\n  }\n};\n\n// TODO(scottmg): US-553. ScopedTaskSuspend fails sometimes, with a 50ms\n// timeout. Currently unclear how to make that more reliable, so disable the\n// test for now as otherwise it flakes.\nTEST(ProcessReaderFuchsia, DISABLED_ChildThreads) {\n  ThreadsChildTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/process_snapshot_fuchsia.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/process_snapshot_fuchsia.h\"\n\n#include \"base/logging.h\"\n#include \"util/fuchsia/koid_utilities.h\"\n\nnamespace crashpad {\n\nProcessSnapshotFuchsia::ProcessSnapshotFuchsia() = default;\n\nProcessSnapshotFuchsia::~ProcessSnapshotFuchsia() = default;\n\nbool ProcessSnapshotFuchsia::Initialize(const zx::process& process) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (gettimeofday(&snapshot_time_, nullptr) != 0) {\n    PLOG(ERROR) << \"gettimeofday\";\n    return false;\n  }\n\n  if (!process_reader_.Initialize(process) ||\n      !memory_range_.Initialize(process_reader_.Memory(), true)) {\n    return false;\n  }\n\n  client_id_.InitializeToZero();\n  system_.Initialize(&snapshot_time_);\n\n  InitializeThreads();\n  InitializeModules();\n\n  const MemoryMapFuchsia* memory_map = process_reader_.MemoryMap();\n  if (memory_map) {\n    for (const auto& entry : memory_map->Entries()) {\n      if (entry.type == ZX_INFO_MAPS_TYPE_MAPPING) {\n        memory_map_.push_back(\n            std::make_unique<internal::MemoryMapRegionSnapshotFuchsia>(entry));\n      }\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessSnapshotFuchsia::InitializeException(\n    zx_koid_t thread_id,\n    const zx_exception_report_t& report) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::unique_ptr<internal::ExceptionSnapshotFuchsia> exception(\n      new internal::ExceptionSnapshotFuchsia());\n  if (exception->Initialize(&process_reader_, thread_id, report)) {\n    exception_.swap(exception);\n    return true;\n  }\n  return false;\n}\n\nvoid ProcessSnapshotFuchsia::GetCrashpadOptions(\n    CrashpadInfoClientOptions* options) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  CrashpadInfoClientOptions local_options;\n\n  for (const auto& module : modules_) {\n    CrashpadInfoClientOptions module_options;\n    module->GetCrashpadOptions(&module_options);\n\n    if (local_options.crashpad_handler_behavior == TriState::kUnset) {\n      local_options.crashpad_handler_behavior =\n          module_options.crashpad_handler_behavior;\n    }\n    if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {\n      local_options.system_crash_reporter_forwarding =\n          module_options.system_crash_reporter_forwarding;\n    }\n    if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {\n      local_options.gather_indirectly_referenced_memory =\n          module_options.gather_indirectly_referenced_memory;\n      local_options.indirectly_referenced_memory_cap =\n          module_options.indirectly_referenced_memory_cap;\n    }\n\n    // If non-default values have been found for all options, the loop can end\n    // early.\n    if (local_options.crashpad_handler_behavior != TriState::kUnset &&\n        local_options.system_crash_reporter_forwarding != TriState::kUnset &&\n        local_options.gather_indirectly_referenced_memory != TriState::kUnset) {\n      break;\n    }\n  }\n\n  *options = local_options;\n}\n\ncrashpad::ProcessID ProcessSnapshotFuchsia::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return GetKoidForHandle(*zx::process::self());\n}\n\ncrashpad::ProcessID ProcessSnapshotFuchsia::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196\n  NOTREACHED();\n}\n\nvoid ProcessSnapshotFuchsia::SnapshotTime(timeval* snapshot_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *snapshot_time = snapshot_time_;\n}\n\nvoid ProcessSnapshotFuchsia::ProcessStartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196. Nothing available.\n  *start_time = timeval{};\n}\n\nvoid ProcessSnapshotFuchsia::ProcessCPUTimes(timeval* user_time,\n                                             timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196. Nothing available.\n  *user_time = timeval{};\n  *system_time = timeval{};\n}\n\nvoid ProcessSnapshotFuchsia::ReportID(UUID* report_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *report_id = report_id_;\n}\n\nvoid ProcessSnapshotFuchsia::ClientID(UUID* client_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *client_id = client_id_;\n}\n\nconst std::map<std::string, std::string>&\nProcessSnapshotFuchsia::AnnotationsSimpleMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_simple_map_;\n}\n\nconst SystemSnapshot* ProcessSnapshotFuchsia::System() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &system_;\n}\n\nstd::vector<const ThreadSnapshot*> ProcessSnapshotFuchsia::Threads() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ThreadSnapshot*> threads;\n  for (const auto& thread : threads_) {\n    threads.push_back(thread.get());\n  }\n  return threads;\n}\n\nstd::vector<const ModuleSnapshot*> ProcessSnapshotFuchsia::Modules() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ModuleSnapshot*> modules;\n  for (const auto& module : modules_) {\n    modules.push_back(module.get());\n  }\n  return modules;\n}\n\nstd::vector<UnloadedModuleSnapshot> ProcessSnapshotFuchsia::UnloadedModules()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // dlclose() never unloads on Fuchsia. ZX-1728 upstream.\n  return std::vector<UnloadedModuleSnapshot>();\n}\n\nconst ExceptionSnapshot* ProcessSnapshotFuchsia::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_.get();\n}\n\nstd::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotFuchsia::MemoryMap()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemoryMapRegionSnapshot*> memory_map;\n  for (const auto& item : memory_map_) {\n    memory_map.push_back(item.get());\n  }\n  return memory_map;\n}\n\nstd::vector<HandleSnapshot> ProcessSnapshotFuchsia::Handles() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<HandleSnapshot>();\n}\n\nstd::vector<const MemorySnapshot*> ProcessSnapshotFuchsia::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<const MemorySnapshot*>();\n}\n\nconst ProcessMemory* ProcessSnapshotFuchsia::Memory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.Memory();\n}\n\nvoid ProcessSnapshotFuchsia::InitializeThreads() {\n  const std::vector<ProcessReaderFuchsia::Thread>& process_reader_threads =\n      process_reader_.Threads();\n  for (const ProcessReaderFuchsia::Thread& process_reader_thread :\n       process_reader_threads) {\n    auto thread = std::make_unique<internal::ThreadSnapshotFuchsia>();\n    if (thread->Initialize(&process_reader_, process_reader_thread)) {\n      threads_.push_back(std::move(thread));\n    }\n  }\n}\n\nvoid ProcessSnapshotFuchsia::InitializeModules() {\n  for (const ProcessReaderFuchsia::Module& reader_module :\n       process_reader_.Modules()) {\n    auto module =\n        std::make_unique<internal::ModuleSnapshotElf>(reader_module.name,\n                                                      reader_module.reader,\n                                                      reader_module.type,\n                                                      &memory_range_,\n                                                      process_reader_.Memory());\n    if (module->Initialize()) {\n      modules_.push_back(std::move(module));\n    }\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/process_snapshot_fuchsia.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_\n#define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_\n\n#include <lib/zx/process.h>\n#include <sys/time.h>\n#include <zircon/syscalls/exception.h>\n#include <zircon/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/elf/elf_image_reader.h\"\n#include \"snapshot/elf/module_snapshot_elf.h\"\n#include \"snapshot/fuchsia/exception_snapshot_fuchsia.h\"\n#include \"snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h\"\n#include \"snapshot/fuchsia/process_reader_fuchsia.h\"\n#include \"snapshot/fuchsia/system_snapshot_fuchsia.h\"\n#include \"snapshot/fuchsia/thread_snapshot_fuchsia.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_id.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n//! \\brief A ProcessSnapshot of a running (or crashed) process running on a\n//!     Fuchsia system. This class is not yet implemented.\nclass ProcessSnapshotFuchsia : public ProcessSnapshot {\n public:\n  ProcessSnapshotFuchsia();\n\n  ProcessSnapshotFuchsia(const ProcessSnapshotFuchsia&) = delete;\n  ProcessSnapshotFuchsia& operator=(const ProcessSnapshotFuchsia&) = delete;\n\n  ~ProcessSnapshotFuchsia() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process The process handle to create a snapshot from.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(const zx::process& process);\n\n  //! \\brief Initializes the object's exception.\n  //!\n  //! This populates the data to be returned by Exception(). The thread\n  //! identified by \\a thread_id must be in an exception.\n  //!\n  //! This method must not be called until after a successful call to\n  //! Initialize().\n  //!\n  //! \\param[in] thread_id Koid of the thread which sustained the exception.\n  //! \\param[in] report The `zx_exception_report_t` for the thread which\n  //!     sustained the exception.\n  //! \\return `true` if the exception information could be initialized, `false`\n  //!     otherwise with an appropriate message logged. When this method returns\n  //!     `false`, the ProcessSnapshotFuchsia object’s validity remains\n  //!     unchanged.\n  bool InitializeException(zx_koid_t thread_id,\n                           const zx_exception_report_t& report);\n\n  //! \\brief Returns options from CrashpadInfo structures found in modules in\n  //!     the process.\n  //!\n  //! \\param[out] options Options set in CrashpadInfo structures in modules in\n  //!     the process.\n  void GetCrashpadOptions(CrashpadInfoClientOptions* options);\n\n  //! \\brief Sets the value to be returned by ReportID().\n  //!\n  //! On Fuchsia, the crash report ID is under the control of the snapshot\n  //! producer, which may call this method to set the report ID. If this is not\n  //! done, ReportID() will return an identifier consisting entirely of zeroes.\n  void SetReportID(const UUID& report_id) { report_id_ = report_id; }\n\n  //! \\brief Sets the value to be returned by ClientID().\n  //!\n  //! On Fuchsia, the client ID is under the control of the snapshot producer,\n  //! which may call this method to set the client ID. If this is not done,\n  //! ClientID() will return an identifier consisting entirely of zeroes.\n  void SetClientID(const UUID& client_id) { client_id_ = client_id; }\n\n  //! \\brief Sets the value to be returned by AnnotationsSimpleMap().\n  //!\n  //! On Fuchsia, all process annotations are under the control of the snapshot\n  //! producer, which may call this method to establish these annotations.\n  //! Contrast this with module annotations, which are under the control of the\n  //! process being snapshotted.\n  void SetAnnotationsSimpleMap(\n      const std::map<std::string, std::string>& annotations_simple_map) {\n    annotations_simple_map_ = annotations_simple_map;\n  }\n\n  // ProcessSnapshot:\n  crashpad::ProcessID ProcessID() const override;\n  crashpad::ProcessID ParentProcessID() const override;\n  void SnapshotTime(timeval* snapshot_time) const override;\n  void ProcessStartTime(timeval* start_time) const override;\n  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;\n  void ReportID(UUID* report_id) const override;\n  void ClientID(UUID* client_id) const override;\n  const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const override;\n  const SystemSnapshot* System() const override;\n  std::vector<const ThreadSnapshot*> Threads() const override;\n  std::vector<const ModuleSnapshot*> Modules() const override;\n  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;\n  const ExceptionSnapshot* Exception() const override;\n  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;\n  std::vector<HandleSnapshot> Handles() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n  const ProcessMemory* Memory() const override;\n\n private:\n  // Initializes threads_ on behalf of Initialize().\n  void InitializeThreads();\n\n  // Initializes modules_ on behalf of Initialize().\n  void InitializeModules();\n\n  internal::SystemSnapshotFuchsia system_;\n  std::vector<std::unique_ptr<internal::ThreadSnapshotFuchsia>> threads_;\n  std::vector<std::unique_ptr<internal::ModuleSnapshotElf>> modules_;\n  std::unique_ptr<internal::ExceptionSnapshotFuchsia> exception_;\n  ProcessReaderFuchsia process_reader_;\n  ProcessMemoryRange memory_range_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotFuchsia>>\n      memory_map_;\n  UUID report_id_;\n  UUID client_id_;\n  timeval snapshot_time_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_\n"
  },
  {
    "path": "snapshot/fuchsia/process_snapshot_fuchsia_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/process_snapshot_fuchsia.h\"\n\n#include <dbghelp.h>\n#include <zircon/syscalls.h>\n\n#include <iterator>\n\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"util/fuchsia/koid_utilities.h\"\n#include \"util/fuchsia/scoped_task_suspend.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr struct {\n  uint32_t zircon_perm;\n  size_t pages;\n  uint32_t minidump_perm;\n} kTestMappingPermAndSizes[] = {\n    // Zircon doesn't currently allow write-only, execute-only, or\n    // write-execute-only, returning ZX_ERR_INVALID_ARGS on map.\n    {0, 5, PAGE_NOACCESS},\n    {ZX_VM_PERM_READ, 6, PAGE_READONLY},\n    // {ZX_VM_PERM_WRITE, 7, PAGE_WRITECOPY},\n    // {ZX_VM_PERM_EXECUTE, 8, PAGE_EXECUTE},\n    {ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 9, PAGE_READWRITE},\n    {ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, 10, PAGE_EXECUTE_READ},\n    // {ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE, 11, PAGE_EXECUTE_WRITECOPY},\n    {ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE,\n     12,\n     PAGE_EXECUTE_READWRITE},\n};\n\nCRASHPAD_CHILD_TEST_MAIN(AddressSpaceChildTestMain) {\n  // Create specifically sized mappings/permissions and write the address in\n  // our address space to the parent so that the reader can check they're read\n  // correctly.\n  for (const auto& t : kTestMappingPermAndSizes) {\n    zx_handle_t vmo = ZX_HANDLE_INVALID;\n    const size_t size = t.pages * zx_system_get_page_size();\n    zx_status_t status = zx_vmo_create(size, 0, &vmo);\n    ZX_CHECK(status == ZX_OK, status) << \"zx_vmo_create\";\n    status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo);\n    ZX_CHECK(status == ZX_OK, status) << \"zx_vmo_replace_as_executable\";\n    uintptr_t mapping_addr = 0;\n    status = zx_vmar_map(\n        zx_vmar_root_self(), t.zircon_perm, 0, vmo, 0, size, &mapping_addr);\n    ZX_CHECK(status == ZX_OK, status) << \"zx_vmar_map\";\n    CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),\n                     &mapping_addr,\n                     sizeof(mapping_addr));\n  }\n\n  CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));\n  return 0;\n}\n\nbool HasSingleMatchingMapping(\n    const std::vector<const MemoryMapRegionSnapshot*>& memory_map,\n    uintptr_t address,\n    size_t size,\n    uint32_t perm) {\n  const MemoryMapRegionSnapshot* matching = nullptr;\n  for (const auto* region : memory_map) {\n    const MINIDUMP_MEMORY_INFO& mmi = region->AsMinidumpMemoryInfo();\n    if (mmi.BaseAddress == address) {\n      if (matching) {\n        LOG(ERROR) << \"multiple mappings matching address\";\n        return false;\n      }\n      matching = region;\n    }\n  }\n\n  if (!matching)\n    return false;\n\n  const MINIDUMP_MEMORY_INFO& matching_mmi = matching->AsMinidumpMemoryInfo();\n  return matching_mmi.Protect == perm && matching_mmi.RegionSize == size;\n}\n\nclass AddressSpaceTest : public MultiprocessExec {\n public:\n  AddressSpaceTest() : MultiprocessExec() {\n    SetChildTestMainFunction(\"AddressSpaceChildTestMain\");\n  }\n\n  AddressSpaceTest(const AddressSpaceTest&) = delete;\n  AddressSpaceTest& operator=(const AddressSpaceTest&) = delete;\n\n  ~AddressSpaceTest() {}\n\n private:\n  void MultiprocessParent() override {\n    uintptr_t test_addresses[std::size(kTestMappingPermAndSizes)];\n    for (size_t i = 0; i < std::size(test_addresses); ++i) {\n      ASSERT_TRUE(ReadFileExactly(\n          ReadPipeHandle(), &test_addresses[i], sizeof(test_addresses[i])));\n    }\n\n    ScopedTaskSuspend suspend(*ChildProcess());\n\n    ProcessSnapshotFuchsia process_snapshot;\n    ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess()));\n\n    for (size_t i = 0; i < std::size(test_addresses); ++i) {\n      const auto& t = kTestMappingPermAndSizes[i];\n      EXPECT_TRUE(HasSingleMatchingMapping(process_snapshot.MemoryMap(),\n                                           test_addresses[i],\n                                           t.pages * zx_system_get_page_size(),\n                                           t.minidump_perm))\n          << base::StringPrintf(\n                 \"index %zu, zircon_perm 0x%x, minidump_perm 0x%x\",\n                 i,\n                 t.zircon_perm,\n                 t.minidump_perm);\n    }\n  }\n};\n\nTEST(ProcessSnapshotFuchsiaTest, AddressSpaceMapping) {\n  AddressSpaceTest test;\n  test.Run();\n}\n\nCRASHPAD_CHILD_TEST_MAIN(StackPointerIntoInvalidLocation) {\n  // Map a large block, output the base address of it, and block. The parent\n  // will artificially set the SP into this large block to confirm that a huge\n  // stack is not accidentally captured.\n  zx_handle_t large_vmo;\n  constexpr uint64_t kSize = 1 << 30u;\n  zx_status_t status = zx_vmo_create(kSize, 0, &large_vmo);\n  ZX_CHECK(status == ZX_OK, status) << \"zx_vmo_create\";\n  zx_vaddr_t mapped_addr;\n  status = zx_vmar_map(zx_vmar_root_self(),\n                       ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,\n                       0,\n                       large_vmo,\n                       0,\n                       kSize,\n                       &mapped_addr);\n  ZX_CHECK(status == ZX_OK, status) << \"zx_vmar_map\";\n  CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),\n                   &mapped_addr,\n                   sizeof(mapped_addr));\n  zx_nanosleep(ZX_TIME_INFINITE);\n  return 0;\n}\n\nclass InvalidStackPointerTest : public MultiprocessExec {\n public:\n  InvalidStackPointerTest() : MultiprocessExec() {\n    SetChildTestMainFunction(\"StackPointerIntoInvalidLocation\");\n    SetExpectedChildTermination(kTerminationNormal,\n                                ZX_TASK_RETCODE_SYSCALL_KILL);\n  }\n\n  InvalidStackPointerTest(const InvalidStackPointerTest&) = delete;\n  InvalidStackPointerTest& operator=(const InvalidStackPointerTest&) = delete;\n\n  ~InvalidStackPointerTest() {}\n\n private:\n  void MultiprocessParent() override {\n    uint64_t address_of_large_mapping;\n    ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(),\n                                &address_of_large_mapping,\n                                sizeof(address_of_large_mapping)));\n\n    ScopedTaskSuspend suspend(*ChildProcess());\n\n    std::vector<zx::thread> threads = GetThreadHandles(*ChildProcess());\n    ASSERT_EQ(threads.size(), 1u);\n\n    zx_thread_state_general_regs_t regs;\n    ASSERT_EQ(threads[0].read_state(\n                  ZX_THREAD_STATE_GENERAL_REGS, &regs, sizeof(regs)),\n              ZX_OK);\n\n    constexpr uint64_t kOffsetIntoMapping = 1024;\n#if defined(ARCH_CPU_X86_64)\n    regs.rsp = address_of_large_mapping + kOffsetIntoMapping;\n#elif defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_RISCV64)\n    regs.sp = address_of_large_mapping + kOffsetIntoMapping;\n#else\n#error\n#endif\n\n    ASSERT_EQ(threads[0].write_state(\n                  ZX_THREAD_STATE_GENERAL_REGS, &regs, sizeof(regs)),\n              ZX_OK);\n\n    ProcessSnapshotFuchsia process_snapshot;\n    ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess()));\n\n    ASSERT_EQ(process_snapshot.Threads().size(), 1u);\n    const MemorySnapshot* stack = process_snapshot.Threads()[0]->Stack();\n    ASSERT_TRUE(stack);\n    // Ensure the stack capture isn't unreasonably large.\n    EXPECT_LT(stack->Size(), 10 * 1048576u);\n\n    // As we've corrupted the child, don't let it run again.\n    ASSERT_EQ(ChildProcess()->kill(), ZX_OK);\n  }\n};\n\n// This is a test for a specific failure detailed in\n// https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=41212. A test of stack\n// behavior that was intentionally overflowing the stack, and so when Crashpad\n// received the exception the SP did not point into the actual stack. This\n// caused Crashpad to erronously capture the \"stack\" from the next mapping in\n// the address space (which could be very large, cause OOM, etc.).\nTEST(ProcessSnapshotFuchsiaTest, InvalidStackPointer) {\n  InvalidStackPointerTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/system_snapshot_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/system_snapshot_fuchsia.h\"\n\n#include <zircon/syscalls.h>\n\n#include \"base/check_op.h\"\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"base/notreached.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"snapshot/posix/timezone.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nSystemSnapshotFuchsia::SystemSnapshotFuchsia() = default;\n\nSystemSnapshotFuchsia::~SystemSnapshotFuchsia() = default;\n\nvoid SystemSnapshotFuchsia::Initialize(const timeval* snapshot_time) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  snapshot_time_ = snapshot_time;\n\n  // This version string mirrors `uname -a` as written by\n  // garnet/bin/uname/uname.c, however, this information isn't provided by\n  // uname(). Additionally, uname() seems to hang if the network is in a bad\n  // state when attempting to retrieve the nodename, so avoid it for now.\n  std::string kernel_version = zx_system_get_version_string();\n\n#if defined(ARCH_CPU_X86_64)\n  static constexpr const char kArch[] = \"x86_64\";\n#elif defined(ARCH_CPU_ARM64)\n  static constexpr const char kArch[] = \"aarch64\";\n#elif defined(ARCH_CPU_RISCV64)\n  static constexpr const char kArch[] = \"riscv64\";\n#else\n  static constexpr const char kArch[] = \"unknown\";\n#endif\n  os_version_full_ = base::StringPrintf(\n      \"Zircon prerelease %s %s\", kernel_version.c_str(), kArch);\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n}\n\nCPUArchitecture SystemSnapshotFuchsia::GetCPUArchitecture() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_64)\n  return kCPUArchitectureX86_64;\n#elif defined(ARCH_CPU_ARM64)\n  return kCPUArchitectureARM64;\n#elif defined(ARCH_CPU_RISCV64)\n  return kCPUArchitectureRISCV64;\n#else\n#error Port\n#endif\n}\n\nuint32_t SystemSnapshotFuchsia::CPURevision() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_64)\n  return cpuid_.Revision();\n#else\n  // TODO: https://fxbug.dev/42133257 - Read actual revision.\n  return 0;\n#endif\n}\n\nuint8_t SystemSnapshotFuchsia::CPUCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return base::saturated_cast<uint8_t>(zx_system_get_num_cpus());\n}\n\nstd::string SystemSnapshotFuchsia::CPUVendor() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_64)\n  return cpuid_.Vendor();\n#else\n  // TODO: https://fxbug.dev/42133257 - Read actual vendor.\n  return std::string();\n#endif\n}\n\nvoid SystemSnapshotFuchsia::CPUFrequency(uint64_t* current_hz,\n                                         uint64_t* max_hz) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196.\n  *current_hz = 0;\n  *max_hz = 0;\n}\n\nuint32_t SystemSnapshotFuchsia::CPUX86Signature() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_64)\n  return cpuid_.Signature();\n#else\n  NOTREACHED();\n#endif\n}\n\nuint64_t SystemSnapshotFuchsia::CPUX86Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_64)\n  return cpuid_.Features();\n#else\n  NOTREACHED();\n#endif\n}\n\nuint64_t SystemSnapshotFuchsia::CPUX86ExtendedFeatures() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_64)\n  return cpuid_.ExtendedFeatures();\n#else\n  NOTREACHED();\n#endif\n}\n\nuint32_t SystemSnapshotFuchsia::CPUX86Leaf7Features() const {\n#if defined(ARCH_CPU_X86_64)\n  return cpuid_.Leaf7Features();\n#else\n  NOTREACHED();\n#endif\n}\n\nbool SystemSnapshotFuchsia::CPUX86SupportsDAZ() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_64)\n  return cpuid_.SupportsDAZ();\n#else\n  NOTREACHED();\n#endif\n}\n\nSystemSnapshot::OperatingSystem SystemSnapshotFuchsia::GetOperatingSystem()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kOperatingSystemFuchsia;\n}\n\nbool SystemSnapshotFuchsia::OSServer() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return false;\n}\n\nvoid SystemSnapshotFuchsia::OSVersion(int* major,\n                                      int* minor,\n                                      int* bugfix,\n                                      std::string* build) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196. There's no version\n  // available to be reported yet.\n  *major = 0;\n  *minor = 0;\n  *bugfix = 0;\n  *build = std::string();\n}\n\nstd::string SystemSnapshotFuchsia::OSVersionFull() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return os_version_full_;\n}\n\nstd::string SystemSnapshotFuchsia::MachineDescription() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196. Not yet available,\n  // upstream ZX-1775.\n  return std::string();\n}\n\nbool SystemSnapshotFuchsia::NXEnabled() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_64)\n  return cpuid_.NXEnabled();\n#else\n  // TODO: https://fxbug.dev/42133257 - Read actual NX bit value.\n  return false;\n#endif\n}\n\nvoid SystemSnapshotFuchsia::TimeZone(DaylightSavingTimeStatus* dst_status,\n                                     int* standard_offset_seconds,\n                                     int* daylight_offset_seconds,\n                                     std::string* standard_name,\n                                     std::string* daylight_name) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  internal::TimeZone(*snapshot_time_,\n                     dst_status,\n                     standard_offset_seconds,\n                     daylight_offset_seconds,\n                     standard_name,\n                     daylight_name);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/system_snapshot_fuchsia.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_\n#define CRASHPAD_SNAPSHOT_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_\n\n#include <sys/time.h>\n\n#include \"build/build_config.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\n#if defined(ARCH_CPU_X86_FAMILY)\n#include \"snapshot/x86/cpuid_reader.h\"\n#endif\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A SystemSnapshot of the running system, when the system runs Fuchsia.\nclass SystemSnapshotFuchsia final : public SystemSnapshot {\n public:\n  SystemSnapshotFuchsia();\n\n  SystemSnapshotFuchsia(const SystemSnapshotFuchsia&) = delete;\n  SystemSnapshotFuchsia& operator=(const SystemSnapshotFuchsia&) = delete;\n\n  ~SystemSnapshotFuchsia() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] snapshot_time The time of the snapshot being taken.\n  //!\n  //! This parameter is necessary for TimeZone() to determine whether daylight\n  //! saving time was in effect at the time the snapshot was taken. Otherwise,\n  //! it would need to base its determination on the current time, which may be\n  //! different than the snapshot time for snapshots generated around the\n  //! daylight saving transition time.\n  void Initialize(const timeval* snapshot_time);\n\n  // SystemSnapshot:\n\n  CPUArchitecture GetCPUArchitecture() const override;\n  uint32_t CPURevision() const override;\n  uint8_t CPUCount() const override;\n  std::string CPUVendor() const override;\n  void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;\n  uint32_t CPUX86Signature() const override;\n  uint64_t CPUX86Features() const override;\n  uint64_t CPUX86ExtendedFeatures() const override;\n  uint32_t CPUX86Leaf7Features() const override;\n  bool CPUX86SupportsDAZ() const override;\n  OperatingSystem GetOperatingSystem() const override;\n  bool OSServer() const override;\n  void OSVersion(\n      int* major, int* minor, int* bugfix, std::string* build) const override;\n  std::string OSVersionFull() const override;\n  bool NXEnabled() const override;\n  std::string MachineDescription() const override;\n  void TimeZone(DaylightSavingTimeStatus* dst_status,\n                int* standard_offset_seconds,\n                int* daylight_offset_seconds,\n                std::string* standard_name,\n                std::string* daylight_name) const override;\n  uint64_t AddressMask() const override { return 0; }\n\n private:\n  std::string os_version_full_;\n  const timeval* snapshot_time_;  // weak\n#if defined(ARCH_CPU_X86_FAMILY)\n  CpuidReader cpuid_;\n#endif  // ARCH_CPU_X86_FAMILY\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_\n"
  },
  {
    "path": "snapshot/fuchsia/thread_snapshot_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/fuchsia/thread_snapshot_fuchsia.h\"\n\n#include \"base/check_op.h\"\n#include \"snapshot/fuchsia/cpu_context_fuchsia.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nThreadSnapshotFuchsia::ThreadSnapshotFuchsia()\n    : ThreadSnapshot(),\n      context_arch_(),\n      context_(),\n      stack_(),\n      thread_name_(),\n      thread_id_(ZX_KOID_INVALID),\n      thread_specific_data_address_(0),\n      initialized_() {}\n\nThreadSnapshotFuchsia::~ThreadSnapshotFuchsia() {}\n\nbool ThreadSnapshotFuchsia::Initialize(\n    ProcessReaderFuchsia* process_reader,\n    const ProcessReaderFuchsia::Thread& thread) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n#if defined(ARCH_CPU_X86_64)\n  context_.architecture = kCPUArchitectureX86_64;\n  context_.x86_64 = &context_arch_;\n  // TODO(fxbug.dev/42132536): Add vector context.\n  InitializeCPUContextX86_64(\n      thread.general_registers, thread.fp_registers, context_.x86_64);\n#elif defined(ARCH_CPU_ARM64)\n  context_.architecture = kCPUArchitectureARM64;\n  context_.arm64 = &context_arch_;\n  InitializeCPUContextARM64(\n      thread.general_registers, thread.vector_registers, context_.arm64);\n#elif defined(ARCH_CPU_RISCV64)\n  context_.architecture = kCPUArchitectureRISCV64;\n  context_.riscv64 = &context_arch_;\n  InitializeCPUContextRISCV64(\n      thread.general_registers, thread.fp_registers, context_.riscv64);\n#else\n#error Port.\n#endif\n\n  if (thread.stack_regions.empty()) {\n    stack_.Initialize(process_reader->Memory(), 0, 0);\n  } else {\n    stack_.Initialize(process_reader->Memory(),\n                      thread.stack_regions[0].base(),\n                      thread.stack_regions[0].size());\n    // TODO(scottmg): Handle split stack by adding other parts to ExtraMemory().\n  }\n\n  thread_name_ = thread.name;\n  thread_id_ = thread.id;\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst CPUContext* ThreadSnapshotFuchsia::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nconst MemorySnapshot* ThreadSnapshotFuchsia::Stack() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &stack_;\n}\n\nuint64_t ThreadSnapshotFuchsia::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nstd::string ThreadSnapshotFuchsia::ThreadName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_name_;\n}\n\nint ThreadSnapshotFuchsia::SuspendCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // There is not (currently) a suspend count for threads on Fuchsia.\n  return 0;\n}\n\nint ThreadSnapshotFuchsia::Priority() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // There is not (currently) thread priorities on Fuchsia.\n  return 0;\n}\n\nuint64_t ThreadSnapshotFuchsia::ThreadSpecificDataAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_specific_data_address_;\n}\n\nstd::vector<const MemorySnapshot*> ThreadSnapshotFuchsia::ExtraMemory() const {\n  return std::vector<const MemorySnapshot*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/fuchsia/thread_snapshot_fuchsia.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_\n#define CRASHPAD_SNAPSHOT_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_\n\n#include <stdint.h>\n#include <zircon/types.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/fuchsia/process_reader_fuchsia.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ThreadSnapshot of a thread on a Fuchsia system.\nclass ThreadSnapshotFuchsia final : public ThreadSnapshot {\n public:\n  ThreadSnapshotFuchsia();\n\n  ThreadSnapshotFuchsia(const ThreadSnapshotFuchsia&) = delete;\n  ThreadSnapshotFuchsia& operator=(const ThreadSnapshotFuchsia&) = delete;\n\n  ~ThreadSnapshotFuchsia() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderFuchsia for the process\n  //!     containing the thread.\n  //! \\param[in] thread The thread within the ProcessReaderFuchsia for\n  //!     which the snapshot should be created.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     a message logged.\n  bool Initialize(ProcessReaderFuchsia* process_reader,\n                  const ProcessReaderFuchsia::Thread& thread);\n\n  // ThreadSnapshot:\n\n  const CPUContext* Context() const override;\n  const MemorySnapshot* Stack() const override;\n  uint64_t ThreadID() const override;\n  std::string ThreadName() const override;\n  int SuspendCount() const override;\n  int Priority() const override;\n  uint64_t ThreadSpecificDataAddress() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n#if defined(ARCH_CPU_X86_64)\n  CPUContextX86_64 context_arch_;\n#elif defined(ARCH_CPU_ARM64)\n  CPUContextARM64 context_arch_;\n#elif defined(ARCH_CPU_RISCV64)\n  CPUContextRISCV64 context_arch_;\n#else\n#error Port.\n#endif\n  CPUContext context_;\n  MemorySnapshotGeneric stack_;\n  std::string thread_name_;\n  zx_koid_t thread_id_;\n  zx_vaddr_t thread_specific_data_address_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_\n"
  },
  {
    "path": "snapshot/handle_snapshot.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/handle_snapshot.h\"\n\nnamespace crashpad {\n\nHandleSnapshot::HandleSnapshot()\n    : type_name(),\n      handle(0),\n      attributes(0),\n      granted_access(0),\n      pointer_count(0),\n      handle_count(0) {\n}\n\nHandleSnapshot::~HandleSnapshot() {\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/handle_snapshot.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_\n\n#include <stdint.h>\n\n#include <string>\n\nnamespace crashpad {\n\nstruct HandleSnapshot {\n  HandleSnapshot();\n  ~HandleSnapshot();\n\n  //! \\brief A UTF-8 string representation of the handle's type.\n  std::string type_name;\n\n  //! \\brief The handle's value.\n  uint32_t handle;\n\n  //! \\brief The attributes for the handle, e.g. `OBJ_INHERIT`,\n  //!     `OBJ_CASE_INSENSITIVE`, etc.\n  uint32_t attributes;\n\n  //! \\brief The ACCESS_MASK for the handle in this process.\n  //!\n  //! See\n  //! https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/\n  //! for more information.\n  uint32_t granted_access;\n\n  //! \\brief The number of kernel references to the object that this handle\n  //!     refers to.\n  uint32_t pointer_count;\n\n  //! \\brief The number of open handles to the object that this handle refers\n  //!     to.\n  uint32_t handle_count;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/hash_types_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nint main() {\n  return 0;\n}\n"
  },
  {
    "path": "snapshot/ios/exception_snapshot_ios_intermediate_dump.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/exception_snapshot_ios_intermediate_dump.h\"\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/ios/intermediate_dump_reader_util.h\"\n#include \"snapshot/mac/cpu_context_mac.h\"\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_list.h\"\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n#include \"util/misc/from_pointer_cast.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\nsize_t ThreadStateLengthForFlavor(thread_state_flavor_t flavor) {\n#if defined(ARCH_CPU_X86_64)\n  switch (flavor) {\n    case x86_THREAD_STATE:\n      return sizeof(x86_thread_state_t);\n    case x86_FLOAT_STATE:\n      return sizeof(x86_float_state_t);\n    case x86_DEBUG_STATE:\n      return sizeof(x86_debug_state_t);\n    case x86_THREAD_STATE64:\n      return sizeof(x86_thread_state64_t);\n    case x86_FLOAT_STATE64:\n      return sizeof(x86_float_state64_t);\n    case x86_DEBUG_STATE64:\n      return sizeof(x86_debug_state64_t);\n    default:\n      return 0;\n  }\n#elif defined(ARCH_CPU_ARM64)\n  switch (flavor) {\n    case ARM_UNIFIED_THREAD_STATE:\n      return sizeof(arm_unified_thread_state_t);\n    case ARM_THREAD_STATE64:\n      return sizeof(arm_thread_state64_t);\n    case ARM_NEON_STATE64:\n      return sizeof(arm_neon_state64_t);\n    case ARM_DEBUG_STATE64:\n      return sizeof(arm_debug_state64_t);\n    default:\n      return 0;\n  }\n#endif\n}\n\nusing Key = IntermediateDumpKey;\n\nExceptionSnapshotIOSIntermediateDump::ExceptionSnapshotIOSIntermediateDump()\n    : ExceptionSnapshot(),\n#if defined(ARCH_CPU_X86_64)\n      context_x86_64_(),\n#elif defined(ARCH_CPU_ARM64)\n      context_arm64_(),\n#else\n#error Port to your CPU architecture\n#endif\n      context_(),\n      codes_(),\n      thread_id_(0),\n      exception_address_(0),\n      exception_(0),\n      exception_info_(0),\n      initialized_() {\n#if defined(ARCH_CPU_X86_64)\n  context_.architecture = kCPUArchitectureX86_64;\n  context_.x86_64 = &context_x86_64_;\n#elif defined(ARCH_CPU_ARM64)\n  context_.architecture = kCPUArchitectureARM64;\n  context_.arm64 = &context_arm64_;\n#else\n#error Port to your CPU architecture\n#endif\n}\n\nExceptionSnapshotIOSIntermediateDump::~ExceptionSnapshotIOSIntermediateDump() {}\n\nbool ExceptionSnapshotIOSIntermediateDump::InitializeFromSignal(\n    const IOSIntermediateDumpMap* exception_data) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  DCHECK(exception_data);\n\n  if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) {\n    LOG(ERROR) << \"Exceptions require a thread id.\";\n    return false;\n  }\n\n#if defined(ARCH_CPU_X86_64)\n  typedef x86_thread_state64_t thread_state_type;\n  typedef x86_float_state64_t float_state_type;\n#elif defined(ARCH_CPU_ARM64)\n  typedef arm_thread_state64_t thread_state_type;\n  typedef arm_neon_state64_t float_state_type;\n#endif\n\n  thread_state_type thread_state;\n  float_state_type float_state;\n  if (GetDataValueFromMap(exception_data, Key::kThreadState, &thread_state) &&\n      GetDataValueFromMap(exception_data, Key::kFloatState, &float_state)) {\n#if defined(ARCH_CPU_X86_64)\n    x86_debug_state64_t empty_debug_state = {};\n    InitializeCPUContextX86_64(&context_x86_64_,\n                               THREAD_STATE_NONE,\n                               nullptr,\n                               0,\n                               &thread_state,\n                               &float_state,\n                               &empty_debug_state);\n#elif defined(ARCH_CPU_ARM64)\n    arm_debug_state64_t empty_debug_state = {};\n    InitializeCPUContextARM64(&context_arm64_,\n                              THREAD_STATE_NONE,\n                              nullptr,\n                              0,\n                              &thread_state,\n                              &float_state,\n                              &empty_debug_state);\n#else\n#error Port to your CPU architecture\n#endif\n  }\n\n  exception_ = EXC_SOFT_SIGNAL;\n  GetDataValueFromMap(exception_data, Key::kSignalNumber, &exception_info_);\n  GetDataValueFromMap(exception_data, Key::kSignalAddress, &exception_address_);\n\n  codes_.push_back(exception_);\n  codes_.push_back(exception_info_);\n  uint32_t code;\n  GetDataValueFromMap(exception_data, Key::kSignalCode, &code);\n  codes_.push_back(code);\n\n  const IOSIntermediateDumpList* thread_context_memory_regions =\n      GetListFromMap(exception_data, Key::kThreadContextMemoryRegions);\n  if (thread_context_memory_regions) {\n    for (auto& region : *thread_context_memory_regions) {\n      vm_address_t address;\n      const IOSIntermediateDumpData* region_data =\n          region->GetAsData(Key::kThreadContextMemoryRegionData);\n      if (!region_data)\n        continue;\n      if (GetDataValueFromMap(\n              region.get(), Key::kThreadContextMemoryRegionAddress, &address)) {\n        const std::vector<uint8_t>& bytes = region_data->bytes();\n        vm_size_t data_size = bytes.size();\n        if (data_size == 0)\n          continue;\n\n        const vm_address_t data =\n            reinterpret_cast<const vm_address_t>(bytes.data());\n\n        auto memory =\n            std::make_unique<internal::MemorySnapshotIOSIntermediateDump>();\n        memory->Initialize(address, data, data_size);\n        extra_memory_.push_back(std::move(memory));\n      }\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ExceptionSnapshotIOSIntermediateDump::InitializeFromMachException(\n    const IOSIntermediateDumpMap* exception_data,\n    const IOSIntermediateDumpList* thread_list) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  DCHECK(exception_data);\n\n  if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) {\n    LOG(ERROR) << \"Exceptions require a thread id.\";\n    return false;\n  }\n\n  exception_type_t exception;\n  if (GetDataValueFromMap(exception_data, Key::kException, &exception)) {\n    codes_.push_back(exception);\n    exception_ = exception;\n  }\n\n  const IOSIntermediateDumpData* code_dump =\n      GetDataFromMap(exception_data, Key::kCodes);\n  if (code_dump) {\n    const std::vector<uint8_t>& bytes = code_dump->bytes();\n    const mach_exception_data_type_t* code =\n        reinterpret_cast<const mach_exception_data_type_t*>(bytes.data());\n    if (bytes.size() == 0 ||\n        bytes.size() % sizeof(mach_exception_data_type_t) != 0 || !code) {\n      LOG(ERROR) << \"Invalid mach exception code.\";\n    } else {\n      mach_msg_type_number_t code_count =\n          bytes.size() / sizeof(mach_exception_data_type_t);\n      for (mach_msg_type_number_t code_index = 0; code_index < code_count;\n           ++code_index) {\n        codes_.push_back(code[code_index]);\n      }\n      DCHECK_GE(code_count, 1u);\n      exception_info_ = code[0];\n      if (code_count >= 2) {\n        exception_address_ = code[1];\n      }\n    }\n  }\n\n  if (thread_list) {\n    for (const auto& other_thread : *thread_list) {\n      uint64_t other_thread_id;\n      if (GetDataValueFromMap(\n              other_thread.get(), Key::kThreadID, &other_thread_id)) {\n        if (thread_id_ == other_thread_id) {\n          LoadContextFromThread(exception_data, other_thread.get());\n          break;\n        }\n      }\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ExceptionSnapshotIOSIntermediateDump::InitializeFromNSException(\n    const IOSIntermediateDumpMap* exception_data,\n    const IOSIntermediateDumpList* thread_list) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  DCHECK(exception_data);\n\n  exception_ = kMachExceptionFromNSException;\n\n  if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) {\n    LOG(ERROR) << \"Exceptions require a thread id.\";\n    return false;\n  }\n\n  if (thread_list) {\n    for (const auto& other_thread : *thread_list) {\n      uint64_t other_thread_id;\n      if (GetDataValueFromMap(\n              other_thread.get(), Key::kThreadID, &other_thread_id)) {\n        if (thread_id_ == other_thread_id) {\n          const IOSIntermediateDumpData* uncaught_exceptions =\n              other_thread->GetAsData(Key::kThreadUncaughtNSExceptionFrames);\n          if (uncaught_exceptions) {\n            LoadContextFromUncaughtNSExceptionFrames(uncaught_exceptions,\n                                                     other_thread.get());\n          } else {\n            LoadContextFromThread(exception_data, other_thread.get());\n          }\n          break;\n        }\n      }\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst CPUContext* ExceptionSnapshotIOSIntermediateDump::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nuint64_t ExceptionSnapshotIOSIntermediateDump::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nuint32_t ExceptionSnapshotIOSIntermediateDump::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_;\n}\n\nuint32_t ExceptionSnapshotIOSIntermediateDump::ExceptionInfo() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_info_;\n}\n\nuint64_t ExceptionSnapshotIOSIntermediateDump::ExceptionAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_address_;\n}\n\nconst std::vector<uint64_t>& ExceptionSnapshotIOSIntermediateDump::Codes()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return codes_;\n}\n\nstd::vector<const MemorySnapshot*>\nExceptionSnapshotIOSIntermediateDump::ExtraMemory() const {\n  std::vector<const MemorySnapshot*> extra_memory;\n  for (const auto& memory : extra_memory_) {\n    extra_memory.push_back(memory.get());\n  }\n  return extra_memory;\n}\n\nvoid ExceptionSnapshotIOSIntermediateDump::LoadContextFromThread(\n    const IOSIntermediateDumpMap* exception_data,\n    const IOSIntermediateDumpMap* other_thread) {\n#if defined(ARCH_CPU_X86_64)\n  typedef x86_thread_state64_t thread_state_type;\n  typedef x86_float_state64_t float_state_type;\n  typedef x86_debug_state64_t debug_state_type;\n#elif defined(ARCH_CPU_ARM64)\n  typedef arm_thread_state64_t thread_state_type;\n  typedef arm_neon_state64_t float_state_type;\n  typedef arm_debug_state64_t debug_state_type;\n#endif\n\n  thread_state_type thread_state;\n  float_state_type float_state;\n  debug_state_type debug_state;\n\n  thread_state_flavor_t flavor = THREAD_STATE_NONE;\n  if (GetDataValueFromMap(exception_data, Key::kFlavor, &flavor) &&\n      GetDataValueFromMap(other_thread, Key::kThreadState, &thread_state) &&\n      GetDataValueFromMap(other_thread, Key::kFloatState, &float_state) &&\n      GetDataValueFromMap(other_thread, Key::kDebugState, &debug_state)) {\n    const IOSIntermediateDumpData* state_dump =\n        GetDataFromMap(exception_data, Key::kState);\n    if (state_dump) {\n      std::vector<uint8_t> bytes = state_dump->bytes();\n      size_t actual_length = bytes.size();\n      size_t expected_length = ThreadStateLengthForFlavor(flavor);\n      if (actual_length < expected_length) {\n        // Zero out bytes if actual_length is shorter than expected_length.\n        bytes.resize(expected_length, 0);\n        actual_length = bytes.size();\n        LOG(WARNING) << \"Exception context length \" << actual_length\n                     << \" shorter than expected length \" << expected_length;\n      }\n      const ConstThreadState state =\n          reinterpret_cast<const ConstThreadState>(bytes.data());\n      // Tolerating actual_length longer than expected_length by setting\n      // state_count based on expected_length, not bytes.size().\n      mach_msg_type_number_t state_count = expected_length / sizeof(uint32_t);\n#if defined(ARCH_CPU_X86_64)\n      InitializeCPUContextX86_64(&context_x86_64_,\n                                 flavor,\n                                 state,\n                                 state_count,\n                                 &thread_state,\n                                 &float_state,\n                                 &debug_state);\n#elif defined(ARCH_CPU_ARM64)\n      InitializeCPUContextARM64(&context_arm64_,\n                                flavor,\n                                state,\n                                state_count,\n                                &thread_state,\n                                &float_state,\n                                &debug_state);\n#else\n#error Port to your CPU architecture\n#endif\n    }\n  }\n\n  // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present\n  // in code[1]. It may or may not be the instruction pointer address (usually\n  // it’s not). code[1] may carry the exception address for other exception\n  // types too, but it’s not guaranteed. But for all other exception types, the\n  // instruction pointer will be the exception address, and in fact will be\n  // equal to codes[1] when it’s carrying the exception address. In those cases,\n  // just use the instruction pointer directly.\n  bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;\n\n#if defined(ARCH_CPU_X86_64)\n  // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values\n  // indicate that code[1] does not (or may not) carry the exception address:\n  // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for\n  // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE)\n  // which collides with EXC_I386_BOUNDFLT (10.9.5\n  // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS\n  // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c\n  // user_page_fault_continue() and do contain the exception address in\n  // code[1].\n  if (exception_ == EXC_BAD_ACCESS &&\n      (exception_info_ == EXC_I386_GPFLT ||\n       exception_info_ == (VM_PROT_READ | VM_PROT_EXECUTE))) {\n    code_1_is_exception_address = false;\n  }\n#endif\n\n  if (!code_1_is_exception_address) {\n    exception_address_ = context_.InstructionPointer();\n  }\n}\n\nvoid ExceptionSnapshotIOSIntermediateDump::\n    LoadContextFromUncaughtNSExceptionFrames(\n        const IOSIntermediateDumpData* frames_dump,\n        const IOSIntermediateDumpMap* other_thread) {\n  const std::vector<uint8_t>& bytes = frames_dump->bytes();\n  const uint64_t* frames = reinterpret_cast<const uint64_t*>(bytes.data());\n  size_t num_frames = bytes.size() / sizeof(uint64_t);\n  if (num_frames < 2) {\n    return;\n  }\n\n#if defined(ARCH_CPU_X86_64)\n  context_x86_64_.rip = frames[0];  // instruction pointer\n  context_x86_64_.rsp = frames[1];\n#elif defined(ARCH_CPU_ARM64)\n  context_arm64_.sp = 0;\n  context_arm64_.pc = frames[0];\n  context_arm64_.regs[30] = frames[1];  // link register\n  context_arm64_.regs[29] = sizeof(uintptr_t);  // function pointers\n#else\n#error Port to your CPU architecture\n#endif\n\n  exception_address_ = frames[0];\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/exception_snapshot_ios_intermediate_dump.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n\n#include <mach/mach.h>\n#include <stdint.h>\n\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/ios/memory_snapshot_ios_intermediate_dump.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\n//! \\brief An ExceptionSnapshot of an exception sustained by a running (or\n//!     crashed) process on an iOS system.\nclass ExceptionSnapshotIOSIntermediateDump final : public ExceptionSnapshot {\n public:\n  ExceptionSnapshotIOSIntermediateDump();\n\n  ExceptionSnapshotIOSIntermediateDump(\n      const ExceptionSnapshotIOSIntermediateDump&) = delete;\n  ExceptionSnapshotIOSIntermediateDump& operator=(\n      const ExceptionSnapshotIOSIntermediateDump&) = delete;\n\n  ~ExceptionSnapshotIOSIntermediateDump() override;\n\n  //! \\brief Initialize the snapshot as a signal exception.\n  //!\n  //! \\param[in] exception_data The intermediate dump map used to initialize\n  //!     this object.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool InitializeFromSignal(const IOSIntermediateDumpMap* exception_data);\n\n  //! \\brief Initialize the object as a Mach exception from an intermediate\n  //!     dump.\n  //!\n  //! \\param[in] exception_data The intermediate dump map used to initialize\n  //!     this object.\n  //! \\param[in] thread_list The intermediate dump map containing list of\n  //!     threads.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool InitializeFromMachException(const IOSIntermediateDumpMap* exception_data,\n                                   const IOSIntermediateDumpList* thread_list);\n\n  //! \\brief Initialize the object as an NSException from an intermediate dump.\n  //!\n  //! \\param[in] exception_data The intermediate dump map used to initialize\n  //!     this object.\n  //! \\param[in] thread_list The intermediate dump map containing list of\n  //!     threads.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool InitializeFromNSException(const IOSIntermediateDumpMap* exception_data,\n                                 const IOSIntermediateDumpList* thread_list);\n\n  // ExceptionSnapshot:\n  const CPUContext* Context() const override;\n  uint64_t ThreadID() const override;\n  uint32_t Exception() const override;\n  uint32_t ExceptionInfo() const override;\n  uint64_t ExceptionAddress() const override;\n  const std::vector<uint64_t>& Codes() const override;\n  virtual std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  void LoadContextFromUncaughtNSExceptionFrames(\n      const IOSIntermediateDumpData* data,\n      const IOSIntermediateDumpMap* other_thread);\n  void LoadContextFromThread(const IOSIntermediateDumpMap* exception_data,\n                             const IOSIntermediateDumpMap* other_thread);\n#if defined(ARCH_CPU_X86_64)\n  CPUContextX86_64 context_x86_64_;\n#elif defined(ARCH_CPU_ARM64)\n  CPUContextARM64 context_arm64_;\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_64\n  CPUContext context_;\n  std::vector<uint64_t> codes_;\n  uint64_t thread_id_;\n  uintptr_t exception_address_;\n  uint32_t exception_;\n  uint32_t exception_info_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>\n      extra_memory_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n"
  },
  {
    "path": "snapshot/ios/intermediate_dump_reader_util.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/intermediate_dump_reader_util.h\"\n\n#include \"base/logging.h\"\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n#include \"util/misc/metrics.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nstd::ostream& operator<<(std::ostream& os, const IntermediateDumpKey& t) {\n  switch (t) {\n#define X(Name, Value)            \\\n  case IntermediateDumpKey::Name: \\\n    os << #Name;                  \\\n    break;\n    INTERMEDIATE_DUMP_KEYS(X)\n#undef X\n  }\n  return os;\n}\n\nconst IOSIntermediateDumpData* GetDataFromMap(\n    const IOSIntermediateDumpMap* map,\n    const IntermediateDumpKey& key,\n    LogMissingDataValueFromMap logging) {\n  const IOSIntermediateDumpData* data = map->GetAsData(key);\n  if (!data) {\n    if (logging != LogMissingDataValueFromMap::kDontLogIfMissing) {\n      LOG(ERROR) << \"Missing expected data for key \" << key;\n      Metrics::MissingIntermediateDumpKey(key);\n    }\n    return nullptr;\n  }\n  return data;\n}\n\nconst IOSIntermediateDumpMap* GetMapFromMap(const IOSIntermediateDumpMap* map,\n                                            const IntermediateDumpKey& key) {\n  const IOSIntermediateDumpMap* map_dump = map->GetAsMap(key);\n  if (!map_dump) {\n    LOG(ERROR) << \"Missing expected map for key \" << key;\n    Metrics::MissingIntermediateDumpKey(key);\n    return nullptr;\n  }\n  return map_dump;\n}\n\nconst IOSIntermediateDumpList* GetListFromMap(const IOSIntermediateDumpMap* map,\n                                              const IntermediateDumpKey& key) {\n  const IOSIntermediateDumpList* list = map->GetAsList(key);\n  if (!list) {\n    LOG(ERROR) << \"Missing expected list for key \" << key;\n    Metrics::MissingIntermediateDumpKey(key);\n    return nullptr;\n  }\n  return list;\n}\n\nbool GetDataStringFromMap(const IOSIntermediateDumpMap* map,\n                          const IntermediateDumpKey& key,\n                          std::string* value) {\n  const IOSIntermediateDumpData* data = GetDataFromMap(map, key);\n  if (!data) {\n    LOG(ERROR) << \"Missing expected string for key \" << key;\n    Metrics::MissingIntermediateDumpKey(key);\n    return false;\n  }\n\n  *value = data->GetString();\n  return true;\n}\n\nvoid GetDataValueFromMapErrorInternal(const IntermediateDumpKey& key) {\n  LOG(ERROR) << \"Invalid key size: \" << key;\n  Metrics::InvalidIntermediateDumpKeySize(key);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/intermediate_dump_reader_util.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <ostream>\n\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n\n#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_\n#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_\n\nnamespace crashpad {\n\nnamespace internal {\n\n//! \\brief Overload the ostream output operator to make logged keys readable.\nstd::ostream& operator<<(std::ostream& os, const IntermediateDumpKey& t);\n\n//! \\brief Determine if GetDataFromMap will log and report missing keys.\nenum class LogMissingDataValueFromMap : bool {\n  //! \\brief Do not log an error and report to UMA if a key is missing.\n  kDontLogIfMissing,\n\n  //! \\brief Log an error and report to UMA if a key is missing.\n  kLogIfMissing,\n};\n\n//! \\brief Call GetAsData with error and UMA logging.\n//!\n//! \\param[in] map The map to load from.\n//! \\param[in] key The key to load from \\a map.\n//! \\param[in] logging This call will log missing keys unless \\a logging is\n//!     LogDataValueFromMap::kDontLogIfMissing\n//!\n//! \\return The IOSIntermediateDumpData pointer or a nullptr;\nconst IOSIntermediateDumpData* GetDataFromMap(\n    const IOSIntermediateDumpMap* map,\n    const IntermediateDumpKey& key,\n    LogMissingDataValueFromMap logging =\n        LogMissingDataValueFromMap::kLogIfMissing);\n\n//! \\brief Call GetAsMap with error and UMA logging.\n//!\n//! \\param[in] map The map to load from.\n//! \\param[in] key The key to load from \\a map.\n//!\n//! \\return The IOSIntermediateDumpMap pointer or a nullptr;\nconst IOSIntermediateDumpMap* GetMapFromMap(const IOSIntermediateDumpMap* map,\n                                            const IntermediateDumpKey& key);\n\n//! \\brief Call GetAsList with error and UMA logging.\n//!\n//! \\param[in] map The map to load from.\n//! \\param[in] key The key to load from \\a map.\n//!\n//! \\return The IOSIntermediateDumpList pointer or a nullptr;\nconst IOSIntermediateDumpList* GetListFromMap(const IOSIntermediateDumpMap* map,\n                                              const IntermediateDumpKey& key);\n\n//! \\brief Call GetAsList with error and UMA logging.\n//!\n//! \\param[in] map The map to load from.\n//! \\param[in] key The key to load from \\a map.\n//! \\param[out] value The loaded string.\n//!\n//! \\return Returns `true` with \\a value set accordingly if the string could be\n//!     loaded, otherwise returns `false` and logs an error.\nbool GetDataStringFromMap(const IOSIntermediateDumpMap* map,\n                          const IntermediateDumpKey& key,\n                          std::string* value);\n\n//! \\brief Log key size error and record error with UMA.\u0010\u0010\u0010\u0010\u0010\nvoid GetDataValueFromMapErrorInternal(const IntermediateDumpKey& key);\n\n//! \\brief Call GetAsData and GetValue with error and UMA logging.\n//!\n//! \\param[in] map The map to load from.\n//! \\param[in] key The key to load from \\a map.\n//! \\param[out] value The data to populate.\n//! \\param[in] logging This call will log missing keys unless \\a logging is\n//!     LogDataValueFromMap::kDontLogIfMissing. This call will always log\n//!     keys with an invalid size.\n//!\n//! \\return On success, returns `true`, otherwise returns `false`.\ntemplate <typename T>\nbool GetDataValueFromMap(const IOSIntermediateDumpMap* map,\n                         const IntermediateDumpKey& key,\n                         T* value,\n                         LogMissingDataValueFromMap logging =\n                             LogMissingDataValueFromMap::kLogIfMissing) {\n  const IOSIntermediateDumpData* data = GetDataFromMap(map, key, logging);\n  if (!data) {\n    return false;\n  }\n  if (!data->GetValue(value)) {\n    GetDataValueFromMapErrorInternal(key);\n    return false;\n  }\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_\n"
  },
  {
    "path": "snapshot/ios/memory_snapshot_ios_intermediate_dump.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/memory_snapshot_ios_intermediate_dump.h\"\n\n#include \"base/check_op.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nvoid MemorySnapshotIOSIntermediateDump::Initialize(vm_address_t address,\n                                                   vm_address_t data,\n                                                   vm_size_t size) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  address_ = address;\n  data_ = data;\n  size_ = base::checked_cast<size_t>(size);\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n}\n\nuint64_t MemorySnapshotIOSIntermediateDump::Address() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return address_;\n}\n\nsize_t MemorySnapshotIOSIntermediateDump::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return size_;\n}\n\nbool MemorySnapshotIOSIntermediateDump::Read(Delegate* delegate) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (size_ == 0) {\n    return delegate->MemorySnapshotDelegateRead(nullptr, size_);\n  }\n\n  return delegate->MemorySnapshotDelegateRead(reinterpret_cast<void*>(data_),\n                                              size_);\n}\n\nconst MemorySnapshot* MemorySnapshotIOSIntermediateDump::MergeWithOtherSnapshot(\n    const MemorySnapshot* other) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  auto other_snapshot =\n      reinterpret_cast<const MemorySnapshotIOSIntermediateDump*>(other);\n\n  INITIALIZATION_STATE_DCHECK_VALID(other_snapshot->initialized_);\n  if (other_snapshot->address_ < address_) {\n    return other_snapshot->MergeWithOtherSnapshot(this);\n  }\n\n  CheckedRange<uint64_t, size_t> merged(0, 0);\n  if (!LoggingDetermineMergedRange(this, other, &merged))\n    return nullptr;\n\n  auto result = std::make_unique<MemorySnapshotIOSIntermediateDump>();\n  result->Initialize(merged.base(), data_, merged.size());\n  if (size_ == merged.size()) {\n    return result.release();\n  }\n\n  const uint8_t* data = reinterpret_cast<const uint8_t*>(data_);\n  const uint8_t* other_data =\n      reinterpret_cast<const uint8_t*>(other_snapshot->data_);\n  vm_size_t overlap = merged.size() - other_snapshot->size_;\n  result->merged_data_.reserve(merged.size());\n  result->merged_data_.insert(result->merged_data_.end(), data, data + overlap);\n  result->merged_data_.insert(result->merged_data_.end(),\n                              other_data,\n                              other_data + other_snapshot->size_);\n  result->data_ =\n      reinterpret_cast<const vm_address_t>(result->merged_data_.data());\n  DCHECK_EQ(result->merged_data_.size(), merged.size());\n  return result.release();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/memory_snapshot_ios_intermediate_dump.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n\n#include <mach/mach.h>\n\n#include <vector>\n\n#include \"snapshot/memory_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A MemorySnapshot of a memory region.\nclass MemorySnapshotIOSIntermediateDump final : public MemorySnapshot {\n public:\n  MemorySnapshotIOSIntermediateDump() = default;\n\n  MemorySnapshotIOSIntermediateDump(const MemorySnapshotIOSIntermediateDump&) =\n      delete;\n  MemorySnapshotIOSIntermediateDump& operator=(\n      const MemorySnapshotIOSIntermediateDump&) = delete;\n\n  ~MemorySnapshotIOSIntermediateDump() = default;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] address The base address of the memory region to snapshot.\n  //! \\param[in] data The destination address where the snapshot will be stored.\n  //! \\param[in] size The size of the memory region to snapshot.\n  void Initialize(vm_address_t address, vm_address_t data, vm_size_t size);\n\n  // MemorySnapshot:\n  uint64_t Address() const override;\n  size_t Size() const override;\n  bool Read(Delegate* delegate) const override;\n  const MemorySnapshot* MergeWithOtherSnapshot(\n      const MemorySnapshot* other) const override;\n\n private:\n  template <class T>\n  friend const MemorySnapshot* MergeWithOtherSnapshotImpl(\n      const T* self,\n      const MemorySnapshot* other);\n\n  vm_address_t address_;\n  vm_address_t data_;\n\n  // Because the iOS snapshot memory region is owned by the intermediate dump,\n  // it's necessary to copy the merged data into a vector owned by the memory\n  // snapshot itself.\n  std::vector<uint8_t> merged_data_;\n\n  vm_size_t size_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n"
  },
  {
    "path": "snapshot/ios/memory_snapshot_ios_intermediate_dump_test.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/memory_snapshot_ios_intermediate_dump.h\"\n\n#include <string>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing internal::MemorySnapshotIOSIntermediateDump;\n\nconst vm_address_t kDefaultAddress = 0x1000;\n\nclass ReadToString : public crashpad::MemorySnapshot::Delegate {\n public:\n  const std::string& result() { return result_; }\n\n private:\n  // MemorySnapshot::Delegate:\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override {\n    result_ = std::string(reinterpret_cast<const char*>(data), size);\n    return true;\n  }\n\n  std::string result_;\n};\n\nstd::unique_ptr<MemorySnapshotIOSIntermediateDump> CreateMemorySnapshot(\n    vm_address_t address,\n    std::vector<uint8_t>& data) {\n  auto memory = std::make_unique<MemorySnapshotIOSIntermediateDump>();\n  memory->Initialize(\n      address, reinterpret_cast<const vm_address_t>(data.data()), data.size());\n  return memory;\n}\n\nTEST(MemorySnapshotIOSIntermediateDumpTest, MergeSame) {\n  std::vector<uint8_t> data(10, 'a');\n  auto memory = CreateMemorySnapshot(kDefaultAddress, data);\n  std::unique_ptr<const MemorySnapshot> merged(\n      memory->MergeWithOtherSnapshot(memory.get()));\n  EXPECT_EQ(merged->Address(), kDefaultAddress);\n  EXPECT_EQ(merged->Size(), data.size());\n  ReadToString delegate;\n  merged->Read(&delegate);\n  EXPECT_EQ(delegate.result(), \"aaaaaaaaaa\");\n}\n\nTEST(MemorySnapshotIOSIntermediateDumpTest, MergeNoOverlap) {\n  std::vector<uint8_t> data1(10, 'a');\n  auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1);\n\n  std::vector<uint8_t> data2(10, 'b');\n  auto memory2 = CreateMemorySnapshot(kDefaultAddress + 10, data2);\n\n  std::unique_ptr<const MemorySnapshot> merged(\n      memory1->MergeWithOtherSnapshot(memory2.get()));\n  EXPECT_EQ(merged->Address(), kDefaultAddress);\n  EXPECT_EQ(merged->Size(), 20u);\n  ReadToString delegate;\n  merged->Read(&delegate);\n  EXPECT_EQ(delegate.result(), \"aaaaaaaaaabbbbbbbbbb\");\n}\n\nTEST(MemorySnapshotIOSIntermediateDumpTest, MergePartial) {\n  std::vector<uint8_t> data1(10, 'a');\n  auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1);\n\n  std::vector<uint8_t> data2(10, 'b');\n  auto memory2 = CreateMemorySnapshot(kDefaultAddress + 5, data2);\n\n  std::unique_ptr<const MemorySnapshot> merged(\n      memory1->MergeWithOtherSnapshot(memory2.get()));\n  EXPECT_EQ(merged->Address(), kDefaultAddress);\n  EXPECT_EQ(merged->Size(), 15u);\n  ReadToString delegate;\n  merged->Read(&delegate);\n  EXPECT_EQ(delegate.result(), \"aaaaabbbbbbbbbb\");\n}\n\nTEST(MemorySnapshotIOSIntermediateDumpTest, NoMerge) {\n  std::vector<uint8_t> data1(10, 'a');\n  auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1);\n\n  std::vector<uint8_t> data2(10, 'b');\n  auto memory2 = CreateMemorySnapshot(kDefaultAddress + 20, data2);\n\n  std::unique_ptr<const MemorySnapshot> merged(\n      memory1->MergeWithOtherSnapshot(memory2.get()));\n  EXPECT_EQ(merged.get(), nullptr);\n}\n\nTEST(MemorySnapshotIOSIntermediateDumpTest, EnvelopeBiggerFirst) {\n  std::vector<uint8_t> data1(30, 'a');\n  auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1);\n\n  std::vector<uint8_t> data2(10, 'b');\n  auto memory2 = CreateMemorySnapshot(kDefaultAddress + 15, data2);\n\n  std::unique_ptr<const MemorySnapshot> merged(\n      memory1->MergeWithOtherSnapshot(memory2.get()));\n  EXPECT_EQ(merged->Address(), kDefaultAddress);\n  EXPECT_EQ(merged->Size(), data1.size());\n\n  ReadToString delegate;\n  merged->Read(&delegate);\n  EXPECT_EQ(delegate.result(), \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\");\n}\n\nTEST(MemorySnapshotIOSIntermediateDumpTest, EnvelopeBiggerSecond) {\n  std::vector<uint8_t> data1(10, 'a');\n  auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1);\n\n  std::vector<uint8_t> data2(20, 'b');\n  auto memory2 = CreateMemorySnapshot(kDefaultAddress, data2);\n\n  std::unique_ptr<const MemorySnapshot> merged(\n      memory1->MergeWithOtherSnapshot(memory2.get()));\n  EXPECT_EQ(merged->Address(), kDefaultAddress);\n  EXPECT_EQ(merged->Size(), data2.size());\n\n  ReadToString delegate;\n  merged->Read(&delegate);\n  EXPECT_EQ(delegate.result(), \"bbbbbbbbbbbbbbbbbbbb\");\n}\n\nTEST(MemorySnapshotIOSIntermediateDumpTest, SmallerAddressSecond) {\n  std::vector<uint8_t> data1(10, 'a');\n  auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1);\n\n  std::vector<uint8_t> data2(20, 'b');\n  auto memory2 = CreateMemorySnapshot(kDefaultAddress - 10, data2);\n\n  std::unique_ptr<const MemorySnapshot> merged(\n      memory1->MergeWithOtherSnapshot(memory2.get()));\n  EXPECT_EQ(merged->Address(), kDefaultAddress - 10);\n  EXPECT_EQ(merged->Size(), data2.size());\n  ReadToString delegate;\n  merged->Read(&delegate);\n  EXPECT_EQ(delegate.result(), \"bbbbbbbbbbbbbbbbbbbb\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/module_snapshot_ios_intermediate_dump.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/module_snapshot_ios_intermediate_dump.h\"\n\n#include <mach-o/loader.h>\n#include <mach/mach.h>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/files/file_path.h\"\n#include \"client/annotation.h\"\n#include \"snapshot/ios/intermediate_dump_reader_util.h\"\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_list.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nusing Key = IntermediateDumpKey;\n\nModuleSnapshotIOSIntermediateDump::ModuleSnapshotIOSIntermediateDump()\n    : ModuleSnapshot(),\n      name_(),\n      address_(0),\n      size_(0),\n      timestamp_(0),\n      dylib_version_(0),\n      source_version_(0),\n      filetype_(0),\n      initialized_() {}\n\nModuleSnapshotIOSIntermediateDump::~ModuleSnapshotIOSIntermediateDump() {}\n\nbool ModuleSnapshotIOSIntermediateDump::Initialize(\n    const IOSIntermediateDumpMap* image_data) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  GetDataStringFromMap(image_data, Key::kName, &name_);\n  GetDataValueFromMap(image_data, Key::kAddress, &address_);\n  GetDataValueFromMap(image_data, Key::kSize, &size_);\n  GetDataValueFromMap(image_data, Key::kFileType, &filetype_);\n\n  // These keys are often missing.\n  GetDataValueFromMap(image_data,\n                      Key::kSourceVersion,\n                      &source_version_,\n                      LogMissingDataValueFromMap::kDontLogIfMissing);\n  GetDataValueFromMap(image_data,\n                      Key::kTimestamp,\n                      &timestamp_,\n                      LogMissingDataValueFromMap::kDontLogIfMissing);\n  GetDataValueFromMap(image_data,\n                      Key::kDylibCurrentVersion,\n                      &dylib_version_,\n                      LogMissingDataValueFromMap::kDontLogIfMissing);\n\n  const IOSIntermediateDumpData* uuid_dump =\n      GetDataFromMap(image_data, IntermediateDumpKey::kUUID);\n  if (uuid_dump) {\n    const std::vector<uint8_t>& bytes = uuid_dump->bytes();\n    if (!bytes.data() || bytes.size() != 16) {\n      LOG(ERROR) << \"Invalid module uuid.\";\n    } else {\n      uuid_.InitializeFromBytes(bytes.data());\n    }\n  }\n\n  const IOSIntermediateDumpList* annotation_list =\n      image_data->GetAsList(IntermediateDumpKey::kAnnotationObjects);\n  if (annotation_list) {\n    for (auto& annotation : *annotation_list) {\n      std::string name;\n      if (!GetDataStringFromMap(\n              annotation.get(), Key::kAnnotationName, &name) ||\n          name.empty() || name.length() > Annotation::kNameMaxLength) {\n        LOG(ERROR) << \"Invalid annotation name (\" << name\n                   << \"), size=\" << name.size()\n                   << \", max size=\" << Annotation::kNameMaxLength\n                   << \", discarding annotation.\";\n        continue;\n      }\n\n      uint16_t type;\n      const IOSIntermediateDumpData* type_dump =\n          annotation->GetAsData(IntermediateDumpKey::kAnnotationType);\n      const IOSIntermediateDumpData* value_dump =\n          annotation->GetAsData(IntermediateDumpKey::kAnnotationValue);\n      if (type_dump && value_dump && type_dump->GetValue<uint16_t>(&type)) {\n        const std::vector<uint8_t>& bytes = value_dump->bytes();\n        uint64_t length = bytes.size();\n        if (!bytes.data() || length > Annotation::kValueMaxSize) {\n          LOG(ERROR) << \"Invalid annotation value, size=\" << length\n                     << \", max size=\" << Annotation::kValueMaxSize\n                     << \", discarding annotation.\";\n          continue;\n        }\n        annotation_objects_.push_back(AnnotationSnapshot(name, type, bytes));\n      }\n    }\n  }\n\n  const IOSIntermediateDumpList* simple_map_dump =\n      image_data->GetAsList(IntermediateDumpKey::kAnnotationsSimpleMap);\n  if (simple_map_dump) {\n    for (auto& annotation : *simple_map_dump) {\n      const IOSIntermediateDumpData* name_dump =\n          annotation->GetAsData(IntermediateDumpKey::kAnnotationName);\n      const IOSIntermediateDumpData* value_dump =\n          annotation->GetAsData(IntermediateDumpKey::kAnnotationValue);\n      if (name_dump && value_dump) {\n        annotations_simple_map_.insert(\n            make_pair(name_dump->GetString(), value_dump->GetString()));\n      }\n    }\n  }\n\n  const IOSIntermediateDumpList* extra_memory_regions_array =\n      image_data->GetAsList(IntermediateDumpKey::kModuleExtraMemoryRegions);\n  if (extra_memory_regions_array) {\n    for (auto& region : *extra_memory_regions_array) {\n      vm_address_t address;\n      const IOSIntermediateDumpData* region_data =\n          region->GetAsData(Key::kModuleExtraMemoryRegionData);\n      if (!region_data)\n        continue;\n      if (GetDataValueFromMap(\n              region.get(), Key::kModuleExtraMemoryRegionAddress, &address)) {\n        const std::vector<uint8_t>& bytes = region_data->bytes();\n        vm_size_t data_size = bytes.size();\n        if (data_size == 0)\n          continue;\n\n        const vm_address_t data =\n            reinterpret_cast<const vm_address_t>(bytes.data());\n\n        auto memory =\n            std::make_unique<internal::MemorySnapshotIOSIntermediateDump>();\n        memory->Initialize(address, data, data_size);\n        extra_memory_.push_back(std::move(memory));\n      }\n    }\n  }\n\n  const IOSIntermediateDumpList* intermediate_dump_extra_memory_regions_array =\n      image_data->GetAsList(\n          IntermediateDumpKey::kModuleIntermediateDumpExtraMemoryRegions);\n  if (intermediate_dump_extra_memory_regions_array) {\n    for (auto& region : *intermediate_dump_extra_memory_regions_array) {\n      vm_address_t address;\n      const IOSIntermediateDumpData* region_data =\n          region->GetAsData(Key::kModuleIntermediateDumpExtraMemoryRegionData);\n      if (!region_data)\n        continue;\n      if (GetDataValueFromMap(\n              region.get(),\n              Key::kModuleIntermediateDumpExtraMemoryRegionAddress,\n              &address)) {\n        const std::vector<uint8_t>& bytes = region_data->bytes();\n        vm_size_t data_size = bytes.size();\n        if (data_size == 0)\n          continue;\n\n        const vm_address_t data =\n            reinterpret_cast<const vm_address_t>(bytes.data());\n\n        auto memory =\n            std::make_unique<internal::MemorySnapshotIOSIntermediateDump>();\n        memory->Initialize(address, data, data_size);\n        intermediate_dump_extra_memory_.push_back(std::move(memory));\n      }\n    }\n  }\n\n  const IOSIntermediateDumpMap* crash_info_dump =\n      image_data->GetAsMap(IntermediateDumpKey::kAnnotationsCrashInfo);\n  if (crash_info_dump) {\n    const IOSIntermediateDumpData* message1_dump = crash_info_dump->GetAsData(\n        IntermediateDumpKey::kAnnotationsCrashInfoMessage1);\n    if (message1_dump) {\n      std::string message1 = message1_dump->GetString();\n      if (!message1.empty())\n        annotations_vector_.push_back(message1);\n    }\n    const IOSIntermediateDumpData* message2_dump = crash_info_dump->GetAsData(\n        IntermediateDumpKey::kAnnotationsCrashInfoMessage2);\n    if (message2_dump) {\n      std::string message2 = message2_dump->GetString();\n      if (!message2.empty())\n        annotations_vector_.push_back(message2);\n    }\n  }\n\n  const IOSIntermediateDumpData* dyld_error_dump =\n      image_data->GetAsData(IntermediateDumpKey::kAnnotationsDyldErrorString);\n  if (dyld_error_dump) {\n    std::string dyld_error_string = dyld_error_dump->GetString();\n    if (!dyld_error_string.empty())\n      annotations_vector_.push_back(dyld_error_string);\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nstd::string ModuleSnapshotIOSIntermediateDump::Name() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return name_;\n}\n\nuint64_t ModuleSnapshotIOSIntermediateDump::Address() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return address_;\n}\n\nuint64_t ModuleSnapshotIOSIntermediateDump::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return size_;\n}\n\ntime_t ModuleSnapshotIOSIntermediateDump::Timestamp() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return timestamp_;\n}\n\nvoid ModuleSnapshotIOSIntermediateDump::FileVersion(uint16_t* version_0,\n                                                    uint16_t* version_1,\n                                                    uint16_t* version_2,\n                                                    uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (filetype_ == MH_DYLIB) {\n    *version_0 = (dylib_version_ & 0xffff0000) >> 16;\n    *version_1 = (dylib_version_ & 0x0000ff00) >> 8;\n    *version_2 = (dylib_version_ & 0x000000ff);\n    *version_3 = 0;\n  } else {\n    *version_0 = 0;\n    *version_1 = 0;\n    *version_2 = 0;\n    *version_3 = 0;\n  }\n}\n\nvoid ModuleSnapshotIOSIntermediateDump::SourceVersion(\n    uint16_t* version_0,\n    uint16_t* version_1,\n    uint16_t* version_2,\n    uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *version_0 = (source_version_ & 0xffff000000000000u) >> 48;\n  *version_1 = (source_version_ & 0x0000ffff00000000u) >> 32;\n  *version_2 = (source_version_ & 0x00000000ffff0000u) >> 16;\n  *version_3 = source_version_ & 0x000000000000ffffu;\n}\n\nModuleSnapshot::ModuleType ModuleSnapshotIOSIntermediateDump::GetModuleType()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  switch (filetype_) {\n    case MH_EXECUTE:\n      return kModuleTypeExecutable;\n    case MH_DYLIB:\n      return kModuleTypeSharedLibrary;\n    case MH_DYLINKER:\n      return kModuleTypeDynamicLoader;\n    case MH_BUNDLE:\n      return kModuleTypeLoadableModule;\n    default:\n      return kModuleTypeUnknown;\n  }\n}\n\nvoid ModuleSnapshotIOSIntermediateDump::UUIDAndAge(crashpad::UUID* uuid,\n                                                   uint32_t* age) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *uuid = uuid_;\n  *age = 0;\n}\n\nstd::string ModuleSnapshotIOSIntermediateDump::DebugFileName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return base::FilePath(Name()).BaseName().value();\n}\n\nstd::vector<uint8_t> ModuleSnapshotIOSIntermediateDump::BuildID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<uint8_t>();\n}\n\nstd::vector<std::string> ModuleSnapshotIOSIntermediateDump::AnnotationsVector()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_vector_;\n}\n\nstd::map<std::string, std::string>\nModuleSnapshotIOSIntermediateDump::AnnotationsSimpleMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_simple_map_;\n}\n\nstd::vector<AnnotationSnapshot>\nModuleSnapshotIOSIntermediateDump::AnnotationObjects() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotation_objects_;\n}\n\nstd::set<CheckedRange<uint64_t>>\nModuleSnapshotIOSIntermediateDump::ExtraMemoryRanges() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::set<CheckedRange<uint64_t>>();\n}\n\nstd::vector<const MemorySnapshot*>\nModuleSnapshotIOSIntermediateDump::ExtraMemory() const {\n  std::vector<const MemorySnapshot*> extra_memory;\n  for (const auto& memory : extra_memory_) {\n    extra_memory.push_back(memory.get());\n  }\n  return extra_memory;\n}\n\nstd::vector<const MemorySnapshot*>\nModuleSnapshotIOSIntermediateDump::IntermediateDumpExtraMemory() const {\n  std::vector<const MemorySnapshot*> intermediate_dump_extra_memory;\n  for (const auto& memory : intermediate_dump_extra_memory_) {\n    intermediate_dump_extra_memory.push_back(memory.get());\n  }\n  return intermediate_dump_extra_memory;\n}\n\nstd::vector<const UserMinidumpStream*>\nModuleSnapshotIOSIntermediateDump::CustomMinidumpStreams() const {\n  return std::vector<const UserMinidumpStream*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/module_snapshot_ios_intermediate_dump.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n\n#include <mach-o/dyld_images.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"snapshot/ios/memory_snapshot_ios_intermediate_dump.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ModuleSnapshot of a code module (binary image) loaded into a\n//!     running (or crashed) process on an iOS system.\nclass ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot {\n public:\n  ModuleSnapshotIOSIntermediateDump();\n\n  ModuleSnapshotIOSIntermediateDump(const ModuleSnapshotIOSIntermediateDump&) =\n      delete;\n  ModuleSnapshotIOSIntermediateDump& operator=(\n      const ModuleSnapshotIOSIntermediateDump&) = delete;\n\n  ~ModuleSnapshotIOSIntermediateDump() override;\n\n  //! \\brief Initialize the snapshot\n  //!\n  //! \\param[in] image_data The intermediate dump map used to initialize this\n  //!     object.\n  //!\n  //! \\return `true` if the snapshot could be created.\n  bool Initialize(const IOSIntermediateDumpMap* image_data);\n\n  // ModuleSnapshot:\n  std::string Name() const override;\n  uint64_t Address() const override;\n  uint64_t Size() const override;\n  time_t Timestamp() const override;\n  void FileVersion(uint16_t* version_0,\n                   uint16_t* version_1,\n                   uint16_t* version_2,\n                   uint16_t* version_3) const override;\n  void SourceVersion(uint16_t* version_0,\n                     uint16_t* version_1,\n                     uint16_t* version_2,\n                     uint16_t* version_3) const override;\n  ModuleType GetModuleType() const override;\n  void UUIDAndAge(UUID* uuid, uint32_t* age) const override;\n  std::string DebugFileName() const override;\n  std::vector<uint8_t> BuildID() const override;\n  std::vector<std::string> AnnotationsVector() const override;\n  std::map<std::string, std::string> AnnotationsSimpleMap() const override;\n  std::vector<AnnotationSnapshot> AnnotationObjects() const override;\n  std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;\n  std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;\n\n  // Used by ProcessSnapshot\n  std::vector<const MemorySnapshot*> ExtraMemory() const;\n  std::vector<const MemorySnapshot*> IntermediateDumpExtraMemory() const;\n\n private:\n  std::string name_;\n  uint64_t address_;\n  uint64_t size_;\n  time_t timestamp_;\n  uint32_t dylib_version_;\n  uint64_t source_version_;\n  uint32_t filetype_;\n  UUID uuid_;\n  std::vector<std::string> annotations_vector_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  std::vector<AnnotationSnapshot> annotation_objects_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>\n      extra_memory_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>\n      intermediate_dump_extra_memory_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n"
  },
  {
    "path": "snapshot/ios/process_snapshot_ios_intermediate_dump.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/process_snapshot_ios_intermediate_dump.h\"\n\n#include <sys/stat.h>\n\n#include \"base/logging.h\"\n#include \"snapshot/ios/intermediate_dump_reader_util.h\"\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_list.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n\nnamespace {\n\nvoid MachTimeValueToTimeval(const time_value& mach, timeval* tv) {\n  tv->tv_sec = mach.seconds;\n  tv->tv_usec = mach.microseconds;\n}\n\n}  // namespace\n\nnamespace crashpad {\nnamespace internal {\n\nusing Key = internal::IntermediateDumpKey;\n\nbool ProcessSnapshotIOSIntermediateDump::InitializeWithFilePath(\n    const base::FilePath& dump_path,\n    const std::map<std::string, std::string>& annotations) {\n  IOSIntermediateDumpFilePath dump_interface;\n  if (!dump_interface.Initialize(dump_path))\n    return false;\n\n  return InitializeWithFileInterface(dump_interface, annotations);\n}\n\nbool ProcessSnapshotIOSIntermediateDump::InitializeWithFileInterface(\n    const IOSIntermediateDumpInterface& dump_interface,\n    const std::map<std::string, std::string>& annotations) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  annotations_simple_map_ = annotations;\n  client_id_.InitializeToZero();\n\n  IOSIntermediateDumpReaderInitializeResult result =\n      reader_.Initialize(dump_interface);\n  if (result == IOSIntermediateDumpReaderInitializeResult::kFailure) {\n    return false;\n  } else if (result == IOSIntermediateDumpReaderInitializeResult::kIncomplete) {\n    annotations_simple_map_[\"crashpad_intermediate_dump_incomplete\"] = \"yes\";\n  }\n\n  const IOSIntermediateDumpMap* root_map = reader_.RootMap();\n  if (root_map->empty())\n    return false;\n\n  uint8_t version;\n  if (!GetDataValueFromMap(root_map, Key::kVersion, &version) || version != 1) {\n    LOG(ERROR) << \"Root map version mismatch\";\n    return false;\n  }\n\n  const internal::IOSIntermediateDumpMap* process_info =\n      GetMapFromMap(root_map, Key::kProcessInfo);\n  if (!process_info) {\n    LOG(ERROR) << \"Process snapshot missing required process info map.\";\n    return false;\n  }\n\n  GetDataValueFromMap(process_info, Key::kPID, &p_pid_);\n  GetDataValueFromMap(process_info, Key::kParentPID, &e_ppid_);\n  GetDataValueFromMap(process_info, Key::kStartTime, &p_starttime_);\n  const IOSIntermediateDumpMap* basic_info =\n      process_info->GetAsMap(Key::kTaskBasicInfo);\n  if (basic_info) {\n    GetDataValueFromMap(basic_info, Key::kUserTime, &basic_info_user_time_);\n    GetDataValueFromMap(basic_info, Key::kSystemTime, &basic_info_system_time_);\n  }\n\n  const IOSIntermediateDumpMap* thread_times =\n      process_info->GetAsMap(Key::kTaskThreadTimes);\n  if (thread_times) {\n    GetDataValueFromMap(thread_times, Key::kUserTime, &thread_times_user_time_);\n    GetDataValueFromMap(\n        thread_times, Key::kSystemTime, &thread_times_system_time_);\n  }\n\n  GetDataValueFromMap(process_info, Key::kSnapshotTime, &snapshot_time_);\n\n  const IOSIntermediateDumpList* simple_map_dump =\n      process_info->GetAsList(IntermediateDumpKey::kAnnotationsSimpleMap);\n  if (simple_map_dump) {\n    for (auto& annotation : *simple_map_dump) {\n      const IOSIntermediateDumpData* name_dump =\n          annotation->GetAsData(IntermediateDumpKey::kAnnotationName);\n      const IOSIntermediateDumpData* value_dump =\n          annotation->GetAsData(IntermediateDumpKey::kAnnotationValue);\n      if (name_dump && value_dump) {\n        annotations_simple_map_.insert(\n            make_pair(name_dump->GetString(), value_dump->GetString()));\n      }\n    }\n  }\n\n  const IOSIntermediateDumpMap* system_info =\n      GetMapFromMap(root_map, Key::kSystemInfo);\n  if (!system_info) {\n    LOG(ERROR) << \"Process snapshot missing required system info map.\";\n    return false;\n  }\n  system_.Initialize(system_info);\n\n  annotations_simple_map_[\"crashpad_uptime_ns\"] =\n      std::to_string(system_.CrashpadUptime());\n\n  // Threads\n  const IOSIntermediateDumpList* thread_list =\n      GetListFromMap(root_map, Key::kThreads);\n  if (thread_list) {\n    for (const auto& value : *thread_list) {\n      auto thread =\n          std::make_unique<internal::ThreadSnapshotIOSIntermediateDump>();\n      if (thread->Initialize(value.get())) {\n        threads_.push_back(std::move(thread));\n      }\n    }\n  }\n\n  const IOSIntermediateDumpList* module_list =\n      GetListFromMap(root_map, Key::kModules);\n  if (module_list) {\n    for (const auto& value : *module_list) {\n      auto module =\n          std::make_unique<internal::ModuleSnapshotIOSIntermediateDump>();\n      if (module->Initialize(value.get())) {\n        modules_.push_back(std::move(module));\n      }\n    }\n  }\n\n  // Exceptions\n  const IOSIntermediateDumpMap* signal_exception =\n      root_map->GetAsMap(Key::kSignalException);\n  const IOSIntermediateDumpMap* mach_exception =\n      root_map->GetAsMap(Key::kMachException);\n  const IOSIntermediateDumpMap* ns_exception =\n      root_map->GetAsMap(Key::kNSException);\n  if (signal_exception) {\n    exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());\n    if (!exception_->InitializeFromSignal(signal_exception)) {\n      LOG(ERROR) << \"Process snapshot could not initialize signal exception.\";\n      return false;\n    }\n  } else if (mach_exception) {\n    exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());\n    if (!exception_->InitializeFromMachException(\n            mach_exception, GetListFromMap(root_map, Key::kThreads))) {\n      LOG(ERROR) << \"Process snapshot could not initialize Mach exception.\";\n      return false;\n    }\n  } else if (ns_exception) {\n    exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump());\n    if (!exception_->InitializeFromNSException(\n            ns_exception, GetListFromMap(root_map, Key::kThreads))) {\n      LOG(ERROR) << \"Process snapshot could not initialize NSException.\";\n      return false;\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nvoid ProcessSnapshotIOSIntermediateDump::SetClientID(const UUID& client_id) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  client_id_ = client_id;\n}\n\nvoid ProcessSnapshotIOSIntermediateDump::SetReportID(const UUID& report_id) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  report_id_ = report_id;\n}\n\npid_t ProcessSnapshotIOSIntermediateDump::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return p_pid_;\n}\n\npid_t ProcessSnapshotIOSIntermediateDump::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return e_ppid_;\n}\n\nvoid ProcessSnapshotIOSIntermediateDump::SnapshotTime(\n    timeval* snapshot_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *snapshot_time = snapshot_time_;\n}\n\nvoid ProcessSnapshotIOSIntermediateDump::ProcessStartTime(\n    timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *start_time = p_starttime_;\n}\n\nvoid ProcessSnapshotIOSIntermediateDump::ProcessCPUTimes(\n    timeval* user_time,\n    timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // Calculate user and system time the same way the kernel does for\n  // getrusage(). See 10.15.0 xnu-6153.11.26/bsd/kern/kern_resource.c calcru().\n  timerclear(user_time);\n  timerclear(system_time);\n\n  MachTimeValueToTimeval(basic_info_user_time_, user_time);\n  MachTimeValueToTimeval(basic_info_system_time_, system_time);\n\n  timeval thread_user_time;\n  MachTimeValueToTimeval(thread_times_user_time_, &thread_user_time);\n  timeval thread_system_time;\n  MachTimeValueToTimeval(thread_times_system_time_, &thread_system_time);\n\n  timeradd(user_time, &thread_user_time, user_time);\n  timeradd(system_time, &thread_system_time, system_time);\n}\n\nvoid ProcessSnapshotIOSIntermediateDump::ReportID(UUID* report_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *report_id = report_id_;\n}\n\nvoid ProcessSnapshotIOSIntermediateDump::ClientID(UUID* client_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *client_id = client_id_;\n}\n\nconst std::map<std::string, std::string>&\nProcessSnapshotIOSIntermediateDump::AnnotationsSimpleMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_simple_map_;\n}\n\nconst SystemSnapshot* ProcessSnapshotIOSIntermediateDump::System() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &system_;\n}\n\nstd::vector<const ThreadSnapshot*> ProcessSnapshotIOSIntermediateDump::Threads()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ThreadSnapshot*> threads;\n  for (const auto& thread : threads_) {\n    threads.push_back(thread.get());\n  }\n  return threads;\n}\n\nstd::vector<const ModuleSnapshot*> ProcessSnapshotIOSIntermediateDump::Modules()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ModuleSnapshot*> modules;\n  for (const auto& module : modules_) {\n    modules.push_back(module.get());\n  }\n  return modules;\n}\n\nstd::vector<UnloadedModuleSnapshot>\nProcessSnapshotIOSIntermediateDump::UnloadedModules() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<UnloadedModuleSnapshot>();\n}\n\nconst ExceptionSnapshot* ProcessSnapshotIOSIntermediateDump::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_.get();\n}\n\nstd::vector<const MemoryMapRegionSnapshot*>\nProcessSnapshotIOSIntermediateDump::MemoryMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<const MemoryMapRegionSnapshot*>();\n}\n\nstd::vector<HandleSnapshot> ProcessSnapshotIOSIntermediateDump::Handles()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<HandleSnapshot>();\n}\n\nstd::vector<const MemorySnapshot*>\nProcessSnapshotIOSIntermediateDump::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> extra_memory;\n  for (const auto& module : modules_) {\n    for (const auto& memory : module->ExtraMemory()) {\n      extra_memory.push_back(memory);\n    }\n  }\n  return extra_memory;\n}\n\nstd::vector<const MemorySnapshot*>\nProcessSnapshotIOSIntermediateDump::IntermediateDumpExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> intermediate_dump_extra_memory;\n  for (const auto& module : modules_) {\n    for (const auto& memory : module->IntermediateDumpExtraMemory()) {\n      intermediate_dump_extra_memory.push_back(memory);\n    }\n  }\n  return intermediate_dump_extra_memory;\n}\n\nconst ProcessMemory* ProcessSnapshotIOSIntermediateDump::Memory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return nullptr;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/process_snapshot_ios_intermediate_dump.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n\n#include <sys/sysctl.h>\n\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"snapshot/ios/exception_snapshot_ios_intermediate_dump.h\"\n#include \"snapshot/ios/module_snapshot_ios_intermediate_dump.h\"\n#include \"snapshot/ios/process_snapshot_ios_intermediate_dump.h\"\n#include \"snapshot/ios/system_snapshot_ios_intermediate_dump.h\"\n#include \"snapshot/ios/thread_snapshot_ios_intermediate_dump.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n#include \"util/ios/ios_intermediate_dump_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ProcessSnapshot of a running (or crashed) process running on a\n//!     iphoneOS system.\nclass ProcessSnapshotIOSIntermediateDump final : public ProcessSnapshot {\n public:\n  ProcessSnapshotIOSIntermediateDump() = default;\n\n  ProcessSnapshotIOSIntermediateDump(\n      const ProcessSnapshotIOSIntermediateDump&) = delete;\n  ProcessSnapshotIOSIntermediateDump& operator=(\n      const ProcessSnapshotIOSIntermediateDump&) = delete;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] dump_path The intermediate dump to read.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool InitializeWithFilePath(\n      const base::FilePath& dump_path,\n      const std::map<std::string, std::string>& annotations);\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] dump_interface An interface corresponding to an intermediate\n  //!     dump file.\n  //! \\param[in] annotations Process annotations to set in each crash report.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool InitializeWithFileInterface(\n      const IOSIntermediateDumpInterface& dump_interface,\n      const std::map<std::string, std::string>& annotations);\n\n  //! On iOS, the client ID is under the control of the snapshot producer,\n  //! which may call this method to set the client ID. If this is not done,\n  //! ClientID() will return an identifier consisting entirely of zeroes.\n  void SetClientID(const UUID& client_id);\n\n  //! \\brief Sets the value to be returned by ReportID().\n  //!\n  //! On iOS, the crash report ID is under the control of the snapshot\n  //! producer, which may call this method to set the report ID. If this is not\n  //! done, ReportID() will return an identifier consisting entirely of zeroes.\n  void SetReportID(const UUID& report_id);\n\n  // ProcessSnapshot:\n  pid_t ProcessID() const override;\n  pid_t ParentProcessID() const override;\n  void SnapshotTime(timeval* snapshot_time) const override;\n  void ProcessStartTime(timeval* start_time) const override;\n  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;\n  void ReportID(UUID* report_id) const override;\n  void ClientID(UUID* client_id) const override;\n  const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const override;\n  const SystemSnapshot* System() const override;\n  std::vector<const ThreadSnapshot*> Threads() const override;\n  std::vector<const ModuleSnapshot*> Modules() const override;\n  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;\n  const ExceptionSnapshot* Exception() const override;\n  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;\n  std::vector<HandleSnapshot> Handles() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n  const ProcessMemory* Memory() const override;\n\n  // Returns ModuleSnapshotIOSIntermediateDump::IntermediateDumpExtraMemory\n  // stored in the intermediate dump. This is used by UserExtensionStreams for\n  // processing the snapshot. This memory will not be written to a minidump\n  std::vector<const MemorySnapshot*> IntermediateDumpExtraMemory() const;\n\n private:\n  // Retain the reader for the lifetime of the ProcessSnapshot so large chunks\n  // of data do not need to be copied around (such as MemorySnapshot\n  // intermediate dumps).\n  IOSIntermediateDumpReader reader_;\n  pid_t p_pid_;\n  pid_t e_ppid_;\n  timeval p_starttime_;\n  time_value_t basic_info_user_time_;\n  time_value_t basic_info_system_time_;\n  time_value_t thread_times_user_time_;\n  time_value_t thread_times_system_time_;\n  internal::SystemSnapshotIOSIntermediateDump system_;\n  std::vector<std::unique_ptr<internal::ThreadSnapshotIOSIntermediateDump>>\n      threads_;\n  std::vector<std::unique_ptr<internal::ModuleSnapshotIOSIntermediateDump>>\n      modules_;\n  std::unique_ptr<internal::ExceptionSnapshotIOSIntermediateDump> exception_;\n  UUID report_id_;\n  UUID client_id_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  timeval snapshot_time_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>\n      extra_memory_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n"
  },
  {
    "path": "snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/process_snapshot_ios_intermediate_dump.h\"\n\n#include <mach-o/loader.h>\n\n#include <algorithm>\n\n#include \"base/files/scoped_file.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n#include \"client/annotation.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/file/string_file.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing Key = internal::IntermediateDumpKey;\nusing internal::IOSIntermediateDumpWriter;\nusing internal::ProcessSnapshotIOSIntermediateDump;\n\nclass ReadToString : public crashpad::MemorySnapshot::Delegate {\n public:\n  std::string result;\n\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override {\n    result = std::string(reinterpret_cast<const char*>(data), size);\n    return true;\n  }\n};\n\nclass ProcessSnapshotIOSIntermediateDumpTest : public testing::Test {\n protected:\n  ProcessSnapshotIOSIntermediateDumpTest()\n      : long_annotation_name_(Annotation::kNameMaxLength, 'a'),\n        long_annotation_value_(Annotation::kValueMaxSize, 'b') {}\n\n  void SetUp() override {\n    path_ = temp_dir_.path().Append(\"dump_file\");\n    writer_ = std::make_unique<internal::IOSIntermediateDumpWriter>();\n    EXPECT_TRUE(writer_->Open(path_));\n    ASSERT_TRUE(IsRegularFile(path_));\n  }\n\n  void TearDown() override {\n    CloseWriter();\n    writer_.reset();\n    EXPECT_FALSE(IsRegularFile(path_));\n  }\n\n  const auto& path() const { return path_; }\n  const auto& annotations() const { return annotations_; }\n  auto writer() const { return writer_.get(); }\n\n  bool DumpSnapshot(const ProcessSnapshotIOSIntermediateDump& snapshot) {\n    MinidumpFileWriter minidump;\n    minidump.InitializeFromSnapshot(&snapshot);\n    StringFile string_file;\n    return minidump.WriteEverything(&string_file);\n  }\n\n  void WriteProcessInfo(IOSIntermediateDumpWriter* writer) {\n    IOSIntermediateDumpWriter::ScopedMap map(writer, Key::kProcessInfo);\n    pid_t pid = 2;\n    pid_t parent = 1;\n    EXPECT_TRUE(writer->AddProperty(Key::kPID, &pid));\n    EXPECT_TRUE(writer->AddProperty(Key::kParentPID, &parent));\n    timeval start_time = {12, 0};\n    EXPECT_TRUE(writer->AddProperty(Key::kStartTime, &start_time));\n\n    time_value_t user_time = {20, 0};\n    time_value_t system_time = {30, 0};\n    {\n      IOSIntermediateDumpWriter::ScopedMap taskInfo(writer,\n                                                    Key::kTaskBasicInfo);\n      EXPECT_TRUE(writer->AddProperty(Key::kUserTime, &user_time));\n      EXPECT_TRUE(writer->AddProperty(Key::kSystemTime, &system_time));\n    }\n    {\n      IOSIntermediateDumpWriter::ScopedMap taskThreadTimesMap(\n          writer, Key::kTaskThreadTimes);\n      writer->AddProperty(Key::kUserTime, &user_time);\n      writer->AddProperty(Key::kSystemTime, &system_time);\n    }\n\n    timeval snapshot_time = {42, 0};\n    writer->AddProperty(Key::kSnapshotTime, &snapshot_time);\n  }\n\n  void WriteSystemInfo(IOSIntermediateDumpWriter* writer) {\n    IOSIntermediateDumpWriter::ScopedMap map(writer, Key::kSystemInfo);\n    std::string machine_description = \"Gibson\";\n    EXPECT_TRUE(writer->AddProperty(Key::kMachineDescription,\n                                    machine_description.c_str(),\n                                    machine_description.length()));\n    int os_version_major = 1995;\n    int os_version_minor = 9;\n    int os_version_bugfix = 15;\n    EXPECT_TRUE(writer->AddProperty(Key::kOSVersionMajor, &os_version_major));\n    EXPECT_TRUE(writer->AddProperty(Key::kOSVersionMinor, &os_version_minor));\n    EXPECT_TRUE(writer->AddProperty(Key::kOSVersionBugfix, &os_version_bugfix));\n    std::string os_version_build = \"Da Vinci\";\n    writer->AddProperty(Key::kOSVersionBuild,\n                        os_version_build.c_str(),\n                        os_version_build.length());\n\n    int cpu_count = 1;\n    EXPECT_TRUE(writer->AddProperty(Key::kCpuCount, &cpu_count));\n    std::string cpu_vendor = \"RISC\";\n    EXPECT_TRUE(writer->AddProperty(\n        Key::kCpuVendor, cpu_vendor.c_str(), cpu_vendor.length()));\n\n    bool has_daylight_saving_time = true;\n    EXPECT_TRUE(writer->AddProperty(Key::kHasDaylightSavingTime,\n                                    &has_daylight_saving_time));\n    bool is_daylight_saving_time = true;\n    EXPECT_TRUE(writer->AddProperty(Key::kIsDaylightSavingTime,\n                                    &is_daylight_saving_time));\n    int standard_offset_seconds = 7200;\n    EXPECT_TRUE(writer->AddProperty(Key::kStandardOffsetSeconds,\n                                    &standard_offset_seconds));\n    int daylight_offset_seconds = 3600;\n    EXPECT_TRUE(writer->AddProperty(Key::kDaylightOffsetSeconds,\n                                    &daylight_offset_seconds));\n    std::string standard_name = \"Standard\";\n    EXPECT_TRUE(writer->AddProperty(\n        Key::kStandardName, standard_name.c_str(), standard_name.length()));\n    std::string daylight_name = \"Daylight\";\n    EXPECT_TRUE(writer->AddProperty(\n        Key::kDaylightName, daylight_name.c_str(), daylight_name.length()));\n\n    vm_size_t page_size = getpagesize();\n    EXPECT_TRUE(writer->AddProperty(Key::kPageSize, &page_size));\n    {\n      natural_t count = 0;\n      IOSIntermediateDumpWriter::ScopedMap vmStatMap(writer, Key::kVMStat);\n      EXPECT_TRUE(writer->AddProperty(Key::kActive, &count));\n      EXPECT_TRUE(writer->AddProperty(Key::kInactive, &count));\n      EXPECT_TRUE(writer->AddProperty(Key::kWired, &count));\n      EXPECT_TRUE(writer->AddProperty(Key::kFree, &count));\n    }\n\n    uint64_t crashpad_report_time_nanos = 1234567890;\n    EXPECT_TRUE(\n        writer->AddProperty(Key::kCrashpadUptime, &crashpad_report_time_nanos));\n  }\n\n  void WriteAnnotations(IOSIntermediateDumpWriter* writer,\n                        bool use_long_annotations) {\n    constexpr char short_annotation_name[] = \"annotation_name\";\n    constexpr char short_annotation_value[] = \"annotation_value\";\n    const char* const annotation_name = use_long_annotations\n                                            ? long_annotation_name_.c_str()\n                                            : short_annotation_name;\n    const char* const annotation_value = use_long_annotations\n                                             ? long_annotation_value_.c_str()\n                                             : short_annotation_value;\n    {\n      IOSIntermediateDumpWriter::ScopedArray annotationObjectArray(\n          writer, Key::kAnnotationObjects);\n      {\n        IOSIntermediateDumpWriter::ScopedArrayMap annotationMap(writer);\n        EXPECT_TRUE(writer->AddPropertyBytes(\n            Key::kAnnotationName, annotation_name, strlen(annotation_name)));\n        EXPECT_TRUE(writer->AddPropertyBytes(\n            Key::kAnnotationValue, annotation_value, strlen(annotation_value)));\n        Annotation::Type type = Annotation::Type::kString;\n        EXPECT_TRUE(writer->AddProperty(Key::kAnnotationType, &type));\n      }\n    }\n    {\n      IOSIntermediateDumpWriter::ScopedArray annotationsSimpleArray(\n          writer, Key::kAnnotationsSimpleMap);\n      {\n        IOSIntermediateDumpWriter::ScopedArrayMap annotationMap(writer);\n        EXPECT_TRUE(writer->AddPropertyBytes(\n            Key::kAnnotationName, annotation_name, strlen(annotation_name)));\n        EXPECT_TRUE(writer->AddPropertyBytes(\n            Key::kAnnotationValue, annotation_value, strlen(annotation_value)));\n      }\n    }\n\n    IOSIntermediateDumpWriter::ScopedMap annotationMap(\n        writer, Key::kAnnotationsCrashInfo);\n    {\n      EXPECT_TRUE(writer->AddPropertyBytes(Key::kAnnotationsCrashInfoMessage1,\n                                           annotation_value,\n                                           strlen(annotation_value)));\n      EXPECT_TRUE(writer->AddPropertyBytes(Key::kAnnotationsCrashInfoMessage2,\n                                           annotation_value,\n                                           strlen(annotation_value)));\n    }\n  }\n\n  void WriteModules(IOSIntermediateDumpWriter* writer,\n                    bool has_module_path,\n                    bool use_long_annotations) {\n    IOSIntermediateDumpWriter::ScopedArray moduleArray(writer, Key::kModules);\n    for (uint32_t image_index = 0; image_index < 2; ++image_index) {\n      IOSIntermediateDumpWriter::ScopedArrayMap modules(writer);\n\n      if (has_module_path) {\n        constexpr char image_file[] = \"/path/to/module\";\n        EXPECT_TRUE(\n            writer->AddProperty(Key::kName, image_file, strlen(image_file)));\n      }\n\n      uint64_t address = 0;\n      uint64_t vmsize = 1;\n      uintptr_t imageFileModDate = 2;\n      uint32_t current_version = 3;\n      uint32_t filetype = MH_DYLIB;\n      uint64_t source_version = 5;\n      static constexpr uint8_t uuid[16] = {0x00,\n                                           0x01,\n                                           0x02,\n                                           0x03,\n                                           0x04,\n                                           0x05,\n                                           0x06,\n                                           0x07,\n                                           0x08,\n                                           0x09,\n                                           0x0a,\n                                           0x0b,\n                                           0x0c,\n                                           0x0d,\n                                           0x0e,\n                                           0x0f};\n      EXPECT_TRUE(writer->AddProperty(Key::kAddress, &address));\n      EXPECT_TRUE(writer->AddProperty(Key::kSize, &vmsize));\n      EXPECT_TRUE(writer->AddProperty(Key::kTimestamp, &imageFileModDate));\n      EXPECT_TRUE(\n          writer->AddProperty(Key::kDylibCurrentVersion, &current_version));\n      EXPECT_TRUE(writer->AddProperty(Key::kSourceVersion, &source_version));\n      EXPECT_TRUE(writer->AddProperty(Key::kUUID, &uuid));\n      EXPECT_TRUE(writer->AddProperty(Key::kFileType, &filetype));\n      WriteAnnotations(writer, use_long_annotations);\n    }\n  }\n\n  void ExpectModules(const std::vector<const ModuleSnapshot*>& modules,\n                     bool expect_module_path,\n                     bool expect_long_annotations) {\n    for (auto module : modules) {\n      EXPECT_EQ(module->GetModuleType(),\n                ModuleSnapshot::kModuleTypeSharedLibrary);\n\n      if (expect_module_path) {\n        EXPECT_STREQ(module->Name().c_str(), \"/path/to/module\");\n        EXPECT_STREQ(module->DebugFileName().c_str(), \"module\");\n      }\n      UUID uuid;\n      uint32_t age;\n      module->UUIDAndAge(&uuid, &age);\n      EXPECT_EQ(uuid.ToString(), \"00010203-0405-0607-0809-0a0b0c0d0e0f\");\n\n      for (auto annotation : module->AnnotationsVector()) {\n        if (expect_long_annotations) {\n          EXPECT_EQ(annotation, long_annotation_value_);\n        } else {\n          EXPECT_STREQ(annotation.c_str(), \"annotation_value\");\n        }\n      }\n\n      for (const auto& it : module->AnnotationsSimpleMap()) {\n        if (expect_long_annotations) {\n          EXPECT_EQ(it.first, long_annotation_name_);\n          EXPECT_EQ(it.second, long_annotation_value_);\n        } else {\n          EXPECT_STREQ(it.first.c_str(), \"annotation_name\");\n          EXPECT_STREQ(it.second.c_str(), \"annotation_value\");\n        }\n      }\n\n      for (auto annotation_object : module->AnnotationObjects()) {\n        EXPECT_EQ(annotation_object.type, (short)Annotation::Type::kString);\n        if (expect_long_annotations) {\n          EXPECT_EQ(annotation_object.name, long_annotation_name_);\n          EXPECT_EQ(std::string(reinterpret_cast<const char*>(\n                                    annotation_object.value.data()),\n                                annotation_object.value.size()),\n                    long_annotation_value_);\n        } else {\n          EXPECT_STREQ(annotation_object.name.c_str(), \"annotation_name\");\n          EXPECT_STREQ(std::string(reinterpret_cast<const char*>(\n                                       annotation_object.value.data()),\n                                   annotation_object.value.size())\n                           .c_str(),\n                       \"annotation_value\");\n        }\n      }\n    }\n  }\n\n  void WriteMachException(IOSIntermediateDumpWriter* writer,\n                          bool short_context = false) {\n    IOSIntermediateDumpWriter::ScopedMap machExceptionMap(writer,\n                                                          Key::kMachException);\n    exception_type_t exception = 5;\n    mach_exception_data_type_t code[] = {4, 3};\n    mach_msg_type_number_t code_count = 2;\n\n#if defined(ARCH_CPU_X86_64)\n    thread_state_flavor_t flavor = x86_THREAD_STATE;\n    x86_thread_state_t state = {};\n    state.tsh.flavor = x86_THREAD_STATE64;\n    state.tsh.count = x86_THREAD_STATE64_COUNT;\n    state.uts.ts64.__rip = 0xdeadbeef;\n    size_t state_length = sizeof(x86_thread_state_t);\n#elif defined(ARCH_CPU_ARM64)\n    thread_state_flavor_t flavor = ARM_UNIFIED_THREAD_STATE;\n    arm_unified_thread_state_t state = {};\n    state.ash.flavor = ARM_THREAD_STATE64;\n    state.ash.count = ARM_THREAD_STATE64_COUNT;\n    state.ts_64.__pc = 0xdeadbeef;\n    size_t state_length = sizeof(arm_unified_thread_state_t);\n#endif\n    EXPECT_TRUE(writer->AddProperty(Key::kException, &exception));\n    EXPECT_TRUE(writer->AddProperty(Key::kCodes, code, code_count));\n    EXPECT_TRUE(writer->AddProperty(Key::kFlavor, &flavor));\n\n    if (short_context) {\n      state_length -= 10;\n    }\n    EXPECT_TRUE(writer->AddPropertyBytes(\n        Key::kState, reinterpret_cast<const void*>(&state), state_length));\n    uint64_t thread_id = 1;\n    EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id));\n  }\n\n  void WriteThreads(IOSIntermediateDumpWriter* writer) {\n    vm_address_t stack_region_address = 0;\n    IOSIntermediateDumpWriter::ScopedArray threadArray(writer, Key::kThreads);\n    for (uint64_t thread_id = 1; thread_id < 3; thread_id++) {\n      IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer);\n      EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id));\n\n      integer_t suspend_count = 666;\n      integer_t importance = 5;\n      uint64_t thread_handle = thread_id;\n      EXPECT_TRUE(writer->AddProperty(Key::kSuspendCount, &suspend_count));\n      EXPECT_TRUE(writer->AddProperty(Key::kPriority, &importance));\n      EXPECT_TRUE(writer->AddProperty(Key::kThreadDataAddress, &thread_handle));\n\n#if defined(ARCH_CPU_X86_64)\n      x86_thread_state64_t thread_state = {};\n      thread_state.__rip = 0xdeadbeef;\n      x86_float_state64_t float_state = {};\n      x86_debug_state64_t debug_state = {};\n#elif defined(ARCH_CPU_ARM64)\n      arm_thread_state64_t thread_state = {};\n      thread_state.__pc = 0xdeadbeef;\n      arm_neon_state64_t float_state = {};\n      arm_debug_state64_t debug_state = {};\n#endif\n      EXPECT_TRUE(writer->AddProperty(Key::kThreadState, &thread_state));\n      EXPECT_TRUE(writer->AddProperty(Key::kFloatState, &float_state));\n      EXPECT_TRUE(writer->AddProperty(Key::kDebugState, &debug_state));\n\n      // Non-overlapping stack_region_address.\n      stack_region_address += 10;\n      EXPECT_TRUE(\n          writer->AddProperty(Key::kStackRegionAddress, &stack_region_address));\n      EXPECT_TRUE(\n          writer->AddPropertyBytes(Key::kStackRegionData, \"stack_data\", 10));\n      {\n        IOSIntermediateDumpWriter::ScopedArray memoryRegions(\n            writer, Key::kThreadContextMemoryRegions);\n        {\n          IOSIntermediateDumpWriter::ScopedArrayMap memoryRegion(writer);\n          const vm_address_t memory_region_address = 0;\n          EXPECT_TRUE(writer->AddProperty(\n              Key::kThreadContextMemoryRegionAddress, &memory_region_address));\n          EXPECT_TRUE(writer->AddPropertyBytes(\n              Key::kThreadContextMemoryRegionData, \"string\", 6));\n        }\n      }\n      EXPECT_TRUE(writer->AddPropertyBytes(Key::kThreadName, \"ariadne\", 7));\n    }\n  }\n\n  void ExpectMachException(const ExceptionSnapshot& exception) {\n    EXPECT_EQ(exception.ThreadID(), 1u);\n    EXPECT_EQ(exception.Exception(), 5u);\n    EXPECT_TRUE(exception.Context()->Is64Bit());\n    EXPECT_EQ(exception.Context()->InstructionPointer(), 0xdeadbeef);\n    EXPECT_EQ(exception.ExceptionInfo(), 4u);\n    EXPECT_EQ(exception.ExceptionAddress(), 0xdeadbeef);\n    EXPECT_EQ(exception.Codes()[0], 5u);\n    EXPECT_EQ(exception.Codes()[1], 4u);\n    EXPECT_EQ(exception.Codes()[2], 3u);\n  }\n\n  void ExpectThreads(const std::vector<const ThreadSnapshot*>& threads) {\n    uint64_t thread_id = 1;\n    for (auto thread : threads) {\n      EXPECT_EQ(thread->ThreadID(), thread_id);\n      EXPECT_EQ(thread->ThreadName(), \"ariadne\");\n      EXPECT_EQ(thread->SuspendCount(), 666);\n      EXPECT_EQ(thread->Priority(), 5);\n      EXPECT_EQ(thread->ThreadSpecificDataAddress(), thread_id++);\n      ReadToString delegate;\n      for (auto memory : thread->ExtraMemory()) {\n        memory->Read(&delegate);\n        EXPECT_STREQ(delegate.result.c_str(), \"string\");\n      }\n\n      thread->Stack()->Read(&delegate);\n      EXPECT_STREQ(delegate.result.c_str(), \"stack_data\");\n\n      EXPECT_TRUE(thread->Context()->Is64Bit());\n      EXPECT_EQ(thread->Context()->InstructionPointer(), 0xdeadbeef);\n    }\n  }\n\n  void ExpectSystem(const SystemSnapshot& system) {\n    EXPECT_EQ(system.CPUCount(), 1u);\n    EXPECT_STREQ(system.CPUVendor().c_str(), \"RISC\");\n    int major;\n    int minor;\n    int bugfix;\n    std::string build;\n    system.OSVersion(&major, &minor, &bugfix, &build);\n    EXPECT_EQ(major, 1995);\n    EXPECT_EQ(minor, 9);\n    EXPECT_EQ(bugfix, 15);\n    EXPECT_STREQ(build.c_str(), \"Da Vinci\");\n    EXPECT_STREQ(system.OSVersionFull().c_str(), \"1995.9.15 Da Vinci\");\n    EXPECT_STREQ(system.MachineDescription().c_str(), \"Gibson\");\n\n    SystemSnapshot::DaylightSavingTimeStatus dst_status;\n    int standard_offset_seconds;\n    int daylight_offset_seconds;\n    std::string standard_name;\n    std::string daylight_name;\n\n    system.TimeZone(&dst_status,\n                    &standard_offset_seconds,\n                    &daylight_offset_seconds,\n                    &standard_name,\n                    &daylight_name);\n    EXPECT_EQ(standard_offset_seconds, 7200);\n    EXPECT_EQ(daylight_offset_seconds, 3600);\n    EXPECT_STREQ(standard_name.c_str(), \"Standard\");\n    EXPECT_STREQ(daylight_name.c_str(), \"Daylight\");\n  }\n\n  void ExpectSnapshot(const ProcessSnapshot& snapshot,\n                      bool expect_module_path,\n                      bool expect_long_annotations) {\n    EXPECT_EQ(snapshot.ProcessID(), 2);\n    EXPECT_EQ(snapshot.ParentProcessID(), 1);\n\n    timeval snapshot_time;\n    snapshot.SnapshotTime(&snapshot_time);\n    EXPECT_EQ(snapshot_time.tv_sec, 42);\n    EXPECT_EQ(snapshot_time.tv_usec, 0);\n\n    timeval start_time;\n    snapshot.ProcessStartTime(&start_time);\n    EXPECT_EQ(start_time.tv_sec, 12);\n    EXPECT_EQ(start_time.tv_usec, 0);\n\n    timeval user_time, system_time;\n    snapshot.ProcessCPUTimes(&user_time, &system_time);\n    EXPECT_EQ(user_time.tv_sec, 40);\n    EXPECT_EQ(user_time.tv_usec, 0);\n    EXPECT_EQ(system_time.tv_sec, 60);\n    EXPECT_EQ(system_time.tv_usec, 0);\n\n    ExpectSystem(*snapshot.System());\n    ExpectThreads(snapshot.Threads());\n    ExpectModules(\n        snapshot.Modules(), expect_module_path, expect_long_annotations);\n    ExpectMachException(*snapshot.Exception());\n\n    auto map = snapshot.AnnotationsSimpleMap();\n    EXPECT_EQ(map[\"crashpad_uptime_ns\"], \"1234567890\");\n  }\n\n  void CloseWriter() { EXPECT_TRUE(writer_->Close()); }\n\n private:\n  std::unique_ptr<internal::IOSIntermediateDumpWriter> writer_;\n  ScopedTempDir temp_dir_;\n  base::FilePath path_;\n  std::map<std::string, std::string> annotations_;\n  const std::string long_annotation_name_;\n  const std::string long_annotation_value_;\n};\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeNoFile) {\n  const base::FilePath file;\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  EXPECT_FALSE(process_snapshot.InitializeWithFilePath(file, annotations()));\n  EXPECT_TRUE(LoggingRemoveFile(path()));\n  EXPECT_FALSE(IsRegularFile(path()));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeEmpty) {\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  EXPECT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeMinimumDump) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSystemInfo); }\n    { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kProcessInfo); }\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, MissingSystemDump) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kProcessInfo); }\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, MissingProcessDump) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSystemInfo); }\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptySignalDump) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    WriteSystemInfo(writer());\n    WriteProcessInfo(writer());\n    {\n      IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSignalException);\n      uint64_t thread_id = 1;\n      EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id));\n      {\n        IOSIntermediateDumpWriter::ScopedArray contextMemoryRegions(\n            writer(), Key::kThreadContextMemoryRegions);\n        IOSIntermediateDumpWriter::ScopedArrayMap memoryMap(writer());\n\n        std::string random_data(\"random_data\");\n        EXPECT_TRUE(writer()->AddProperty(\n            Key::kThreadContextMemoryRegionAddress, &thread_id));\n        EXPECT_TRUE(writer()->AddProperty(Key::kThreadContextMemoryRegionData,\n                                          random_data.c_str(),\n                                          random_data.length()));\n      }\n    }\n    {\n      IOSIntermediateDumpWriter::ScopedArray threadArray(writer(),\n                                                         Key::kThreads);\n      IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer());\n      uint64_t thread_id = 1;\n      writer()->AddProperty(Key::kThreadID, &thread_id);\n    }\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_EQ(process_snapshot.Exception()->ExtraMemory().size(), 1u);\n  ReadToString delegate;\n  for (auto memory : process_snapshot.Exception()->ExtraMemory()) {\n    memory->Read(&delegate);\n    EXPECT_STREQ(delegate.result.c_str(), \"random_data\");\n  }\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyMachDump) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    WriteSystemInfo(writer());\n    WriteProcessInfo(writer());\n    {\n      IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kMachException);\n      uint64_t thread_id = 1;\n      EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id));\n    }\n    {\n      IOSIntermediateDumpWriter::ScopedArray threadArray(writer(),\n                                                         Key::kThreads);\n      IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer());\n      uint64_t thread_id = 1;\n      writer()->AddProperty(Key::kThreadID, &thread_id);\n    }\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyExceptionDump) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    WriteSystemInfo(writer());\n    WriteProcessInfo(writer());\n    {\n      IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kNSException);\n      uint64_t thread_id = 1;\n      EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id));\n    }\n    {\n      IOSIntermediateDumpWriter::ScopedArray threadArray(writer(),\n                                                         Key::kThreads);\n      IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer());\n      uint64_t thread_id = 1;\n      writer()->AddProperty(Key::kThreadID, &thread_id);\n    }\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyUncaughtNSExceptionDump) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    WriteSystemInfo(writer());\n    WriteProcessInfo(writer());\n    {\n      IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kNSException);\n      uint64_t thread_id = 1;\n      EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id));\n    }\n    {\n      IOSIntermediateDumpWriter::ScopedArray threadArray(writer(),\n                                                         Key::kThreads);\n      IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer());\n      uint64_t thread_id = 1;\n      writer()->AddProperty(Key::kThreadID, &thread_id);\n      const uint64_t frames[] = {0, 0};\n      const size_t num_frames = 2;\n      writer()->AddProperty(\n          Key::kThreadUncaughtNSExceptionFrames, frames, num_frames);\n    }\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, ShortContext) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    WriteSystemInfo(writer());\n    WriteProcessInfo(writer());\n    WriteThreads(writer());\n    WriteModules(\n        writer(), /*has_module_path=*/false, /*use_long_annotations=*/false);\n    WriteMachException(writer(), true /* short_context=true*/);\n  }\n  CloseWriter();\n\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n  ExpectSnapshot(process_snapshot,\n                 /*expect_module_path=*/false,\n                 /*expect_long_annotations=*/false);\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, LongAnnotations) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    WriteSystemInfo(writer());\n    WriteProcessInfo(writer());\n    WriteThreads(writer());\n    WriteModules(\n        writer(), /*has_module_path=*/false, /*use_long_annotations=*/true);\n    WriteMachException(writer());\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n  ExpectSnapshot(process_snapshot,\n                 /*expect_module_path=*/false,\n                 /*expect_long_annotations=*/true);\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, FullReport) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    WriteSystemInfo(writer());\n    WriteProcessInfo(writer());\n    WriteThreads(writer());\n    WriteModules(\n        writer(), /*has_module_path=*/true, /*use_long_annotations=*/false);\n    WriteMachException(writer());\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n  ExpectSnapshot(process_snapshot,\n                 /*expect_module_path=*/true,\n                 /*expect_long_annotations=*/false);\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, FuzzTestCases) {\n  base::FilePath fuzz_path = TestPaths::TestDataRoot().Append(FILE_PATH_LITERAL(\n      \"snapshot/ios/testdata/crash-1fa088dda0adb41459d063078a0f384a0bb8eefa\"));\n  crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot;\n  EXPECT_TRUE(process_snapshot.InitializeWithFilePath(fuzz_path, {}));\n  EXPECT_TRUE(LoggingRemoveFile(path()));\n\n  auto map = process_snapshot.AnnotationsSimpleMap();\n  ASSERT_TRUE(map.find(\"crashpad_intermediate_dump_incomplete\") != map.end());\n  EXPECT_EQ(map[\"crashpad_intermediate_dump_incomplete\"], \"yes\");\n\n  fuzz_path = TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"snapshot/ios/testdata/crash-5726011582644224\"));\n  crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot2;\n  EXPECT_TRUE(process_snapshot2.InitializeWithFilePath(fuzz_path, {}));\n  map = process_snapshot2.AnnotationsSimpleMap();\n  ASSERT_TRUE(map.find(\"crashpad_intermediate_dump_incomplete\") != map.end());\n  EXPECT_EQ(map[\"crashpad_intermediate_dump_incomplete\"], \"yes\");\n\n  fuzz_path = TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"snapshot/ios/testdata/crash-6605504629637120\"));\n  crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot3;\n  EXPECT_FALSE(process_snapshot3.InitializeWithFilePath(fuzz_path, {}));\n\n  fuzz_path = TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"snapshot/ios/testdata/crash-c44acfcbccd8c7a8\"));\n  crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot4;\n  EXPECT_TRUE(process_snapshot4.InitializeWithFilePath(fuzz_path, {}));\n}\n\nTEST_F(ProcessSnapshotIOSIntermediateDumpTest, WriteNoThreads) {\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer());\n    uint8_t version = 1;\n    EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version));\n    WriteSystemInfo(writer());\n    WriteProcessInfo(writer());\n    WriteMachException(writer());\n  }\n  CloseWriter();\n  ProcessSnapshotIOSIntermediateDump process_snapshot;\n  ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations()));\n  EXPECT_FALSE(IsRegularFile(path()));\n  EXPECT_TRUE(DumpSnapshot(process_snapshot));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/system_snapshot_ios_intermediate_dump.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/system_snapshot_ios_intermediate_dump.h\"\n\n#include <mach/mach.h>\n#include <sys/sysctl.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"snapshot/ios/intermediate_dump_reader_util.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\nusing Key = IntermediateDumpKey;\n\nSystemSnapshotIOSIntermediateDump::SystemSnapshotIOSIntermediateDump()\n    : SystemSnapshot(),\n      os_version_build_(),\n      machine_description_(),\n      os_version_major_(0),\n      os_version_minor_(0),\n      os_version_bugfix_(0),\n      active_(0),\n      inactive_(0),\n      wired_(0),\n      free_(0),\n      cpu_count_(0),\n      cpu_vendor_(),\n      dst_status_(),\n      standard_offset_seconds_(0),\n      daylight_offset_seconds_(0),\n      standard_name_(),\n      daylight_name_(),\n      address_mask_(0),\n      initialized_() {}\n\nSystemSnapshotIOSIntermediateDump::~SystemSnapshotIOSIntermediateDump() {}\n\nvoid SystemSnapshotIOSIntermediateDump::Initialize(\n    const IOSIntermediateDumpMap* system_data) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  GetDataStringFromMap(system_data, Key::kOSVersionBuild, &os_version_build_);\n  GetDataStringFromMap(\n      system_data, Key::kMachineDescription, &machine_description_);\n  GetDataStringFromMap(system_data, Key::kCpuVendor, &cpu_vendor_);\n  GetDataStringFromMap(system_data, Key::kStandardName, &standard_name_);\n  GetDataStringFromMap(system_data, Key::kDaylightName, &daylight_name_);\n\n  GetDataValueFromMap(system_data, Key::kOSVersionMajor, &os_version_major_);\n  GetDataValueFromMap(system_data, Key::kOSVersionMinor, &os_version_minor_);\n  GetDataValueFromMap(system_data, Key::kOSVersionBugfix, &os_version_bugfix_);\n  GetDataValueFromMap(system_data, Key::kCpuCount, &cpu_count_);\n\n  GetDataValueFromMap(\n      system_data, Key::kStandardOffsetSeconds, &standard_offset_seconds_);\n  GetDataValueFromMap(\n      system_data, Key::kDaylightOffsetSeconds, &daylight_offset_seconds_);\n\n  bool has_daylight_saving_time;\n  GetDataValueFromMap(\n      system_data, Key::kHasDaylightSavingTime, &has_daylight_saving_time);\n  bool is_daylight_saving_time;\n  GetDataValueFromMap(\n      system_data, Key::kIsDaylightSavingTime, &is_daylight_saving_time);\n\n  if (has_daylight_saving_time) {\n    dst_status_ = is_daylight_saving_time\n                      ? SystemSnapshot::kObservingDaylightSavingTime\n                      : SystemSnapshot::kObservingStandardTime;\n  } else {\n    dst_status_ = SystemSnapshot::kDoesNotObserveDaylightSavingTime;\n  }\n\n  GetDataValueFromMap(system_data, Key::kAddressMask, &address_mask_);\n\n  vm_size_t page_size;\n  if (GetDataValueFromMap(system_data, Key::kPageSize, &page_size)) {\n    const IOSIntermediateDumpMap* vm_stat =\n        GetMapFromMap(system_data, Key::kVMStat);\n    if (vm_stat) {\n      GetDataValueFromMap(vm_stat, Key::kActive, &active_);\n      active_ *= page_size;\n\n      GetDataValueFromMap(vm_stat, Key::kInactive, &inactive_);\n      inactive_ *= page_size;\n\n      GetDataValueFromMap(vm_stat, Key::kWired, &wired_);\n      wired_ *= page_size;\n\n      GetDataValueFromMap(vm_stat, Key::kFree, &free_);\n      free_ *= page_size;\n    }\n  }\n\n  GetDataValueFromMap(system_data, Key::kCrashpadUptime, &crashpad_uptime_ns_);\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n}\n\nCPUArchitecture SystemSnapshotIOSIntermediateDump::GetCPUArchitecture() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_64)\n  return kCPUArchitectureX86_64;\n#elif defined(ARCH_CPU_ARM64)\n  return kCPUArchitectureARM64;\n#endif\n}\n\nuint32_t SystemSnapshotIOSIntermediateDump::CPURevision() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(justincohen): sysctlbyname machdep.cpu.* returns -1 on iOS/ARM64, but\n  // consider recording this for X86_64 only.\n  return 0;\n}\n\nuint8_t SystemSnapshotIOSIntermediateDump::CPUCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return cpu_count_;\n}\n\nstd::string SystemSnapshotIOSIntermediateDump::CPUVendor() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return cpu_vendor_;\n}\n\nvoid SystemSnapshotIOSIntermediateDump::CPUFrequency(uint64_t* current_hz,\n                                                     uint64_t* max_hz) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(justincohen): sysctlbyname hw.cpufrequency returns -1 on iOS/ARM64,\n  // but consider recording this for X86_64 only.\n  *current_hz = 0;\n  *max_hz = 0;\n}\n\nuint32_t SystemSnapshotIOSIntermediateDump::CPUX86Signature() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(justincohen): Consider recording this for X86_64 only.\n  return 0;\n}\n\nuint64_t SystemSnapshotIOSIntermediateDump::CPUX86Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(justincohen): Consider recording this for X86_64 only.\n  return 0;\n}\n\nuint64_t SystemSnapshotIOSIntermediateDump::CPUX86ExtendedFeatures() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(justincohen): Consider recording this for X86_64 only.\n  return 0;\n}\n\nuint32_t SystemSnapshotIOSIntermediateDump::CPUX86Leaf7Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(justincohen): Consider recording this for X86_64 only.\n  return 0;\n}\n\nbool SystemSnapshotIOSIntermediateDump::CPUX86SupportsDAZ() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(justincohen): Consider recording this for X86_64 only.\n  return false;\n}\n\nSystemSnapshot::OperatingSystem\nSystemSnapshotIOSIntermediateDump::GetOperatingSystem() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kOperatingSystemIOS;\n}\n\nbool SystemSnapshotIOSIntermediateDump::OSServer() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return false;\n}\n\nvoid SystemSnapshotIOSIntermediateDump::OSVersion(int* major,\n                                                  int* minor,\n                                                  int* bugfix,\n                                                  std::string* build) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *major = os_version_major_;\n  *minor = os_version_minor_;\n  *bugfix = os_version_bugfix_;\n  build->assign(os_version_build_);\n}\n\nstd::string SystemSnapshotIOSIntermediateDump::OSVersionFull() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return base::StringPrintf(\"%d.%d.%d %s\",\n                            os_version_major_,\n                            os_version_minor_,\n                            os_version_bugfix_,\n                            os_version_build_.c_str());\n}\n\nstd::string SystemSnapshotIOSIntermediateDump::MachineDescription() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return machine_description_;\n}\n\nbool SystemSnapshotIOSIntermediateDump::NXEnabled() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(justincohen): Consider using kern.nx when available (pre-iOS 13,\n  // pre-OS X 10.15). Otherwise the bit is always enabled.\n  return true;\n}\n\nvoid SystemSnapshotIOSIntermediateDump::TimeZone(\n    DaylightSavingTimeStatus* dst_status,\n    int* standard_offset_seconds,\n    int* daylight_offset_seconds,\n    std::string* standard_name,\n    std::string* daylight_name) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *dst_status = dst_status_;\n  *standard_offset_seconds = standard_offset_seconds_;\n  *daylight_offset_seconds = daylight_offset_seconds_;\n  standard_name->assign(standard_name_);\n  daylight_name->assign(daylight_name_);\n}\n\nuint64_t SystemSnapshotIOSIntermediateDump::AddressMask() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return address_mask_;\n}\n\nuint64_t SystemSnapshotIOSIntermediateDump::CrashpadUptime() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return crashpad_uptime_ns_;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/system_snapshot_ios_intermediate_dump.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"snapshot/system_snapshot.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\n//! \\brief A SystemSnapshot of the running system, when the system runs iOS.\nclass SystemSnapshotIOSIntermediateDump final : public SystemSnapshot {\n public:\n  SystemSnapshotIOSIntermediateDump();\n\n  SystemSnapshotIOSIntermediateDump(const SystemSnapshotIOSIntermediateDump&) =\n      delete;\n  SystemSnapshotIOSIntermediateDump& operator=(\n      const SystemSnapshotIOSIntermediateDump&) = delete;\n\n  ~SystemSnapshotIOSIntermediateDump() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] system_data An intermediate dump map containing various system\n  //!     data points.\n  void Initialize(const IOSIntermediateDumpMap* system_data);\n\n  // SystemSnapshot:\n\n  CPUArchitecture GetCPUArchitecture() const override;\n  uint32_t CPURevision() const override;\n  uint8_t CPUCount() const override;\n  std::string CPUVendor() const override;\n  void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;\n  uint32_t CPUX86Signature() const override;\n  uint64_t CPUX86Features() const override;\n  uint64_t CPUX86ExtendedFeatures() const override;\n  uint32_t CPUX86Leaf7Features() const override;\n  bool CPUX86SupportsDAZ() const override;\n  OperatingSystem GetOperatingSystem() const override;\n  bool OSServer() const override;\n  void OSVersion(int* major,\n                 int* minor,\n                 int* bugfix,\n                 std::string* build) const override;\n  std::string OSVersionFull() const override;\n  bool NXEnabled() const override;\n  std::string MachineDescription() const override;\n  void TimeZone(DaylightSavingTimeStatus* dst_status,\n                int* standard_offset_seconds,\n                int* daylight_offset_seconds,\n                std::string* standard_name,\n                std::string* daylight_name) const override;\n  uint64_t AddressMask() const override;\n\n  //! \\brief Returns the number of nanoseconds between Crashpad initialization\n  //!     and snapshot generation.\n  uint64_t CrashpadUptime() const;\n\n private:\n  std::string os_version_build_;\n  std::string machine_description_;\n  int os_version_major_;\n  int os_version_minor_;\n  int os_version_bugfix_;\n  uint32_t active_;\n  uint32_t inactive_;\n  uint32_t wired_;\n  uint32_t free_;\n  int cpu_count_;\n  std::string cpu_vendor_;\n  DaylightSavingTimeStatus dst_status_;\n  int standard_offset_seconds_;\n  int daylight_offset_seconds_;\n  std::string standard_name_;\n  std::string daylight_name_;\n  uint64_t address_mask_;\n  uint64_t crashpad_uptime_ns_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n"
  },
  {
    "path": "snapshot/ios/thread_snapshot_ios_intermediate_dump.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/ios/thread_snapshot_ios_intermediate_dump.h\"\n\n#include \"base/apple/mach_logging.h\"\n#include \"snapshot/ios/intermediate_dump_reader_util.h\"\n#include \"snapshot/mac/cpu_context_mac.h\"\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_list.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n\n#include <vector>\n\nnamespace {\n\nstd::vector<uint8_t> GenerateStackMemoryFromFrames(const uint64_t* frames,\n                                                   const size_t frame_count) {\n  std::vector<uint8_t> stack_memory;\n  if (frame_count < 2) {\n    return stack_memory;\n  }\n  size_t pointer_size = sizeof(uintptr_t);\n  size_t frame_record_size = 2 * pointer_size;\n  size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size;\n  stack_memory.resize(stack_size);\n  uintptr_t sp = stack_size - pointer_size;\n  uintptr_t fp = 0;\n  uintptr_t lr = 0;\n  for (size_t current_frame = frame_count - 1; current_frame > 0;\n       --current_frame) {\n    memcpy(&stack_memory[0] + sp, &lr, sizeof(lr));\n    sp -= pointer_size;\n    memcpy(&stack_memory[0] + sp, &fp, sizeof(fp));\n    fp = sp;\n    sp -= pointer_size;\n    lr = frames[current_frame];\n  }\n\n  if (sp != 0) {\n    LOG(ERROR) << \"kExpectedFinalSp should be 0, is \" << sp;\n  }\n  if (fp != sizeof(uintptr_t)) {\n    LOG(ERROR) << \"kExpectedFinalFp should be sizeof(uintptr_t), is \" << fp;\n  }\n  if (lr != frames[1]) {\n    LOG(ERROR) << \"lr should be \" << frames[1] << \", is \" << lr;\n  }\n  return stack_memory;\n}\n\n}  // namespace\nnamespace crashpad {\nnamespace internal {\n\nusing Key = IntermediateDumpKey;\n\nThreadSnapshotIOSIntermediateDump::ThreadSnapshotIOSIntermediateDump()\n    : ThreadSnapshot(),\n#if defined(ARCH_CPU_X86_64)\n      context_x86_64_(),\n#elif defined(ARCH_CPU_ARM64)\n      context_arm64_(),\n#endif\n      context_(),\n      stack_(),\n      thread_name_(),\n      thread_id_(0),\n      thread_specific_data_address_(0),\n      suspend_count_(0),\n      priority_(0),\n      initialized_() {\n#if defined(ARCH_CPU_X86_64)\n  context_.architecture = kCPUArchitectureX86_64;\n  context_.x86_64 = &context_x86_64_;\n#elif defined(ARCH_CPU_ARM64)\n  context_.architecture = kCPUArchitectureARM64;\n  context_.arm64 = &context_arm64_;\n#endif\n}\n\nThreadSnapshotIOSIntermediateDump::~ThreadSnapshotIOSIntermediateDump() {}\n\nbool ThreadSnapshotIOSIntermediateDump::Initialize(\n    const IOSIntermediateDumpMap* thread_data) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  GetDataValueFromMap(thread_data, Key::kSuspendCount, &suspend_count_);\n  GetDataValueFromMap(thread_data, Key::kPriority, &priority_);\n  GetDataValueFromMap(thread_data, Key::kThreadID, &thread_id_);\n  GetDataValueFromMap(\n      thread_data, Key::kThreadDataAddress, &thread_specific_data_address_);\n  GetDataStringFromMap(thread_data, Key::kThreadName, &thread_name_);\n\n#if defined(ARCH_CPU_X86_64)\n  typedef x86_thread_state64_t thread_state_type;\n  typedef x86_float_state64_t float_state_type;\n  typedef x86_debug_state64_t debug_state_type;\n#elif defined(ARCH_CPU_ARM64)\n  typedef arm_thread_state64_t thread_state_type;\n  typedef arm_neon_state64_t float_state_type;\n  typedef arm_debug_state64_t debug_state_type;\n#endif\n\n  thread_state_type thread_state;\n  float_state_type float_state;\n  debug_state_type debug_state;\n\n  const IOSIntermediateDumpData* nsexception_frames =\n      thread_data->GetAsData(Key::kThreadUncaughtNSExceptionFrames);\n  const IOSIntermediateDumpData* thread_stack_data_dump =\n      thread_data->GetAsData(Key::kStackRegionData);\n  if (nsexception_frames && thread_stack_data_dump) {\n    LOG(ERROR) << \"Unexpected thread with kStackRegionData and \"\n               << \"kThreadUncaughtNSExceptionFrames, using kStackRegionData\";\n  }\n  if (thread_stack_data_dump) {\n    vm_address_t stack_region_address;\n    GetDataValueFromMap(\n        thread_data, Key::kStackRegionAddress, &stack_region_address);\n\n    const std::vector<uint8_t>& bytes = thread_stack_data_dump->bytes();\n    const vm_address_t stack_region_data =\n        reinterpret_cast<const vm_address_t>(bytes.data());\n    vm_size_t stack_region_size = bytes.size();\n    stack_.Initialize(\n        stack_region_address, stack_region_data, stack_region_size);\n  } else if (nsexception_frames) {\n    const std::vector<uint8_t>& bytes = nsexception_frames->bytes();\n    const uint64_t* frames = reinterpret_cast<const uint64_t*>(bytes.data());\n    size_t frame_count = bytes.size() / sizeof(uint64_t);\n    exception_stack_memory_ =\n        GenerateStackMemoryFromFrames(frames, frame_count);\n    vm_address_t stack_memory_addr =\n        !exception_stack_memory_.empty()\n            ? reinterpret_cast<vm_address_t>(&exception_stack_memory_[0])\n            : 0;\n    stack_.Initialize(0, stack_memory_addr, exception_stack_memory_.size());\n  } else {\n    stack_.Initialize(0, 0, 0);\n  }\n\n  if (GetDataValueFromMap(thread_data, Key::kThreadState, &thread_state) &&\n      GetDataValueFromMap(thread_data, Key::kFloatState, &float_state) &&\n      GetDataValueFromMap(thread_data, Key::kDebugState, &debug_state)) {\n#if defined(ARCH_CPU_X86_64)\n    InitializeCPUContextX86_64(&context_x86_64_,\n                               THREAD_STATE_NONE,\n                               nullptr,\n                               0,\n                               &thread_state,\n                               &float_state,\n                               &debug_state);\n#elif defined(ARCH_CPU_ARM64)\n    InitializeCPUContextARM64(&context_arm64_,\n                              THREAD_STATE_NONE,\n                              nullptr,\n                              0,\n                              &thread_state,\n                              &float_state,\n                              &debug_state);\n\n#else\n#error Port to your CPU architecture\n#endif\n  }\n  const IOSIntermediateDumpList* thread_context_memory_regions =\n      GetListFromMap(thread_data, Key::kThreadContextMemoryRegions);\n  if (thread_context_memory_regions) {\n    for (auto& region : *thread_context_memory_regions) {\n      vm_address_t address;\n      const IOSIntermediateDumpData* region_data =\n          region->GetAsData(Key::kThreadContextMemoryRegionData);\n      if (!region_data)\n        continue;\n      if (GetDataValueFromMap(\n              region.get(), Key::kThreadContextMemoryRegionAddress, &address)) {\n        const std::vector<uint8_t>& bytes = region_data->bytes();\n        vm_size_t data_size = bytes.size();\n        if (data_size == 0)\n          continue;\n\n        const vm_address_t data =\n            reinterpret_cast<const vm_address_t>(bytes.data());\n\n        auto memory =\n            std::make_unique<internal::MemorySnapshotIOSIntermediateDump>();\n        memory->Initialize(address, data, data_size);\n        extra_memory_.push_back(std::move(memory));\n      }\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\nconst CPUContext* ThreadSnapshotIOSIntermediateDump::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nconst MemorySnapshot* ThreadSnapshotIOSIntermediateDump::Stack() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &stack_;\n}\n\nuint64_t ThreadSnapshotIOSIntermediateDump::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nstd::string ThreadSnapshotIOSIntermediateDump::ThreadName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_name_;\n}\n\nint ThreadSnapshotIOSIntermediateDump::SuspendCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return suspend_count_;\n}\n\nint ThreadSnapshotIOSIntermediateDump::Priority() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return priority_;\n}\n\nuint64_t ThreadSnapshotIOSIntermediateDump::ThreadSpecificDataAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_specific_data_address_;\n}\n\nstd::vector<const MemorySnapshot*>\nThreadSnapshotIOSIntermediateDump::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> extra_memory;\n  for (const auto& memory : extra_memory_) {\n    extra_memory.push_back(memory.get());\n  }\n  return extra_memory;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/ios/thread_snapshot_ios_intermediate_dump.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/ios/memory_snapshot_ios_intermediate_dump.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ThreadSnapshot of a thread on an iOS system.\nclass ThreadSnapshotIOSIntermediateDump final : public ThreadSnapshot {\n public:\n  ThreadSnapshotIOSIntermediateDump();\n\n  ThreadSnapshotIOSIntermediateDump(const ThreadSnapshotIOSIntermediateDump&) =\n      delete;\n  ThreadSnapshotIOSIntermediateDump& operator=(\n      const ThreadSnapshotIOSIntermediateDump&) = delete;\n\n  ~ThreadSnapshotIOSIntermediateDump() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\brief thread_data The intermediate dump map used to initialize this\n  //!     object.\n  //!\n  //! \\return `true` if the snapshot could be created.\n  bool Initialize(const IOSIntermediateDumpMap* thread_data);\n\n  // ThreadSnapshot:\n  const CPUContext* Context() const override;\n  const MemorySnapshot* Stack() const override;\n  uint64_t ThreadID() const override;\n  std::string ThreadName() const override;\n  int SuspendCount() const override;\n  int Priority() const override;\n  uint64_t ThreadSpecificDataAddress() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n#if defined(ARCH_CPU_X86_64)\n  CPUContextX86_64 context_x86_64_;\n#elif defined(ARCH_CPU_ARM64)\n  CPUContextARM64 context_arm64_;\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_64\n  CPUContext context_;\n  std::vector<uint8_t> exception_stack_memory_;\n  MemorySnapshotIOSIntermediateDump stack_;\n  std::string thread_name_;\n  uint64_t thread_id_;\n  uint64_t thread_specific_data_address_;\n  int suspend_count_;\n  int priority_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotIOSIntermediateDump>>\n      extra_memory_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_\n"
  },
  {
    "path": "snapshot/linux/capture_memory_delegate_linux.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/capture_memory_delegate_linux.h\"\n\n#include <utility>\n\n#include \"base/numerics/safe_conversions.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nCaptureMemoryDelegateLinux::CaptureMemoryDelegateLinux(\n    ProcessReaderLinux* process_reader,\n    const ProcessReaderLinux::Thread* thread_opt,\n    std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots,\n    uint32_t* budget_remaining)\n    : stack_(thread_opt ? thread_opt->stack_region_address : 0,\n             thread_opt ? thread_opt->stack_region_size : 0),\n      process_reader_(process_reader),\n      snapshots_(snapshots),\n      budget_remaining_(budget_remaining) {}\n\nbool CaptureMemoryDelegateLinux::Is64Bit() const {\n  return process_reader_->Is64Bit();\n}\n\nbool CaptureMemoryDelegateLinux::ReadMemory(uint64_t at,\n                                            uint64_t num_bytes,\n                                            void* into) const {\n  return process_reader_->Memory()->Read(\n      at, base::checked_cast<size_t>(num_bytes), into);\n}\n\nstd::vector<CheckedRange<uint64_t>>\nCaptureMemoryDelegateLinux::GetReadableRanges(\n    const CheckedRange<uint64_t, uint64_t>& range) const {\n  return process_reader_->GetMemoryMap()->GetReadableRanges(range);\n}\n\nvoid CaptureMemoryDelegateLinux::AddNewMemorySnapshot(\n    const CheckedRange<uint64_t, uint64_t>& range) {\n  // Don't bother storing this memory if it points back into the stack.\n  if (stack_.ContainsRange(range))\n    return;\n  if (range.size() == 0)\n    return;\n  if (!budget_remaining_ || *budget_remaining_ == 0)\n    return;\n  snapshots_->push_back(std::make_unique<internal::MemorySnapshotGeneric>());\n  internal::MemorySnapshotGeneric* snapshot = snapshots_->back().get();\n  snapshot->Initialize(process_reader_->Memory(), range.base(), range.size());\n  if (!base::IsValueInRangeForNumericType<int64_t>(range.size())) {\n    *budget_remaining_ = 0;\n  } else {\n    int64_t temp = *budget_remaining_;\n    temp -= range.size();\n    *budget_remaining_ = base::saturated_cast<uint32_t>(temp);\n  }\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/capture_memory_delegate_linux.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_CAPTURE_MEMORY_DELEGATE_LINUX_H_\n#define CRASHPAD_SNAPSHOT_LINUX_CAPTURE_MEMORY_DELEGATE_LINUX_H_\n\n#include \"snapshot/capture_memory.h\"\n\n#include <stdint.h>\n\n#include <memory>\n#include <vector>\n\n#include \"snapshot/linux/process_reader_linux.h\"\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nclass MemorySnapshotGeneric;\n\nclass CaptureMemoryDelegateLinux : public CaptureMemory::Delegate {\n public:\n  //! \\brief A MemoryCaptureDelegate for Linux.\n  //!\n  //! \\param[in] process_reader A ProcessReaderLinux for the target process.\n  //! \\param[in] thread_opt The thread being inspected. Memory ranges\n  //!     overlapping this thread's stack will be ignored on the assumption\n  //!     that they're already captured elsewhere. May be nullptr.\n  //! \\param[in] snapshots A vector of MemorySnapshotGeneric to which the\n  //!     captured memory will be added.\n  //! \\param[in] budget_remaining If non-null, a pointer to the remaining number\n  //!     of bytes to capture. If this is `0`, no further memory will be\n  //!     captured.\n  CaptureMemoryDelegateLinux(\n      ProcessReaderLinux* process_reader,\n      const ProcessReaderLinux::Thread* thread_opt,\n      std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots,\n      uint32_t* budget_remaining);\n\n  // MemoryCaptureDelegate:\n  bool Is64Bit() const override;\n  bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override;\n  std::vector<CheckedRange<uint64_t>> GetReadableRanges(\n      const CheckedRange<uint64_t, uint64_t>& range) const override;\n  void AddNewMemorySnapshot(\n      const CheckedRange<uint64_t, uint64_t>& range) override;\n\n private:\n  CheckedRange<uint64_t, uint64_t> stack_;\n  ProcessReaderLinux* process_reader_;  // weak\n  std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots_;  // weak\n  uint32_t* budget_remaining_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_CAPTURE_MEMORY_DELEGATE_LINUX_H_\n"
  },
  {
    "path": "snapshot/linux/cpu_context_linux.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/cpu_context_linux.h\"\n\n#include <stddef.h>\n#include <string.h>\n\n#include <limits>\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\n#define SET_GPRS32()                         \\\n  do {                                       \\\n    context->eax = thread_context.eax;       \\\n    context->ebx = thread_context.ebx;       \\\n    context->ecx = thread_context.ecx;       \\\n    context->edx = thread_context.edx;       \\\n    context->edi = thread_context.edi;       \\\n    context->esi = thread_context.esi;       \\\n    context->ebp = thread_context.ebp;       \\\n    context->esp = thread_context.esp;       \\\n    context->eip = thread_context.eip;       \\\n    context->eflags = thread_context.eflags; \\\n    context->cs = thread_context.xcs;        \\\n    context->ds = thread_context.xds;        \\\n    context->es = thread_context.xes;        \\\n    context->fs = thread_context.xfs;        \\\n    context->gs = thread_context.xgs;        \\\n    context->ss = thread_context.xss;        \\\n  } while (false)\n\nvoid InitializeCPUContextX86(const ThreadContext::t32_t& thread_context,\n                             const FloatContext::f32_t& float_context,\n                             CPUContextX86* context) {\n  SET_GPRS32();\n\n  static_assert(sizeof(context->fxsave) == sizeof(float_context.fxsave),\n                \"fxsave size mismatch\");\n  memcpy(&context->fxsave, &float_context.fxsave, sizeof(context->fxsave));\n\n  // TODO(jperaza): debug registers\n  context->dr0 = 0;\n  context->dr1 = 0;\n  context->dr2 = 0;\n  context->dr3 = 0;\n  context->dr4 = 0;\n  context->dr5 = 0;\n  context->dr6 = 0;\n  context->dr7 = 0;\n}\n\nvoid InitializeCPUContextX86(const SignalThreadContext32& thread_context,\n                             const SignalFloatContext32& float_context,\n                             CPUContextX86* context) {\n  InitializeCPUContextX86_NoFloatingPoint(thread_context, context);\n  CPUContextX86::FsaveToFxsave(float_context.fsave, &context->fxsave);\n}\n\nvoid InitializeCPUContextX86_NoFloatingPoint(\n    const SignalThreadContext32& thread_context,\n    CPUContextX86* context) {\n  SET_GPRS32();\n\n  memset(&context->fxsave, 0, sizeof(context->fxsave));\n\n  context->dr0 = 0;\n  context->dr1 = 0;\n  context->dr2 = 0;\n  context->dr3 = 0;\n  context->dr4 = 0;\n  context->dr5 = 0;\n  context->dr6 = 0;\n  context->dr7 = 0;\n}\n\n#define SET_GPRS64()                         \\\n  do {                                       \\\n    context->rax = thread_context.rax;       \\\n    context->rbx = thread_context.rbx;       \\\n    context->rcx = thread_context.rcx;       \\\n    context->rdx = thread_context.rdx;       \\\n    context->rdi = thread_context.rdi;       \\\n    context->rsi = thread_context.rsi;       \\\n    context->rbp = thread_context.rbp;       \\\n    context->rsp = thread_context.rsp;       \\\n    context->r8 = thread_context.r8;         \\\n    context->r9 = thread_context.r9;         \\\n    context->r10 = thread_context.r10;       \\\n    context->r11 = thread_context.r11;       \\\n    context->r12 = thread_context.r12;       \\\n    context->r13 = thread_context.r13;       \\\n    context->r14 = thread_context.r14;       \\\n    context->r15 = thread_context.r15;       \\\n    context->rip = thread_context.rip;       \\\n    context->rflags = thread_context.eflags; \\\n    context->cs = thread_context.cs;         \\\n    context->fs = thread_context.fs;         \\\n    context->gs = thread_context.gs;         \\\n  } while (false)\n\nvoid InitializeCPUContextX86_64(const ThreadContext::t64_t& thread_context,\n                                const FloatContext::f64_t& float_context,\n                                CPUContextX86_64* context) {\n  SET_GPRS64();\n\n  static_assert(sizeof(context->fxsave) == sizeof(float_context.fxsave),\n                \"fxsave size mismatch\");\n  memcpy(&context->fxsave, &float_context.fxsave, sizeof(context->fxsave));\n\n  // TODO(jperaza): debug registers.\n  context->dr0 = 0;\n  context->dr1 = 0;\n  context->dr2 = 0;\n  context->dr3 = 0;\n  context->dr4 = 0;\n  context->dr5 = 0;\n  context->dr6 = 0;\n  context->dr7 = 0;\n}\n\nvoid InitializeCPUContextX86_64(const SignalThreadContext64& thread_context,\n                                const SignalFloatContext64& float_context,\n                                CPUContextX86_64* context) {\n  SET_GPRS64();\n\n  static_assert(\n      std::is_same<SignalFloatContext64, CPUContextX86_64::Fxsave>::value,\n      \"signal float context has unexpected type\");\n  memcpy(&context->fxsave, &float_context, sizeof(context->fxsave));\n\n  context->dr0 = 0;\n  context->dr1 = 0;\n  context->dr2 = 0;\n  context->dr3 = 0;\n  context->dr4 = 0;\n  context->dr5 = 0;\n  context->dr6 = 0;\n  context->dr7 = 0;\n}\n\nvoid InitializeCPUContextX86_64_NoFloatingPoint(\n    const SignalThreadContext64& thread_context,\n    CPUContextX86_64* context) {\n  SET_GPRS64();\n\n  memset(&context->fxsave, 0, sizeof(context->fxsave));\n\n  context->dr0 = 0;\n  context->dr1 = 0;\n  context->dr2 = 0;\n  context->dr3 = 0;\n  context->dr4 = 0;\n  context->dr5 = 0;\n  context->dr6 = 0;\n  context->dr7 = 0;\n}\n\n#elif defined(ARCH_CPU_ARM_FAMILY)\n\nvoid InitializeCPUContextARM(const ThreadContext::t32_t& thread_context,\n                             const FloatContext::f32_t& float_context,\n                             CPUContextARM* context) {\n  static_assert(sizeof(context->regs) == sizeof(thread_context.regs),\n                \"registers size mismatch\");\n  memcpy(&context->regs, &thread_context.regs, sizeof(context->regs));\n  context->fp = thread_context.fp;\n  context->ip = thread_context.ip;\n  context->sp = thread_context.sp;\n  context->lr = thread_context.lr;\n  context->pc = thread_context.pc;\n  context->cpsr = thread_context.cpsr;\n\n  static_assert(sizeof(context->vfp_regs) == sizeof(float_context.vfp),\n                \"vfp size mismatch\");\n  context->have_vfp_regs = float_context.have_vfp;\n  if (float_context.have_vfp) {\n    memcpy(&context->vfp_regs, &float_context.vfp, sizeof(context->vfp_regs));\n  }\n\n  static_assert(sizeof(context->fpa_regs) == sizeof(float_context.fpregs),\n                \"fpregs size mismatch\");\n  context->have_fpa_regs = float_context.have_fpregs;\n  if (float_context.have_fpregs) {\n    memcpy(\n        &context->fpa_regs, &float_context.fpregs, sizeof(context->fpa_regs));\n  }\n}\n\nvoid InitializeCPUContextARM_NoFloatingPoint(\n    const SignalThreadContext32& thread_context,\n    CPUContextARM* context) {\n  static_assert(sizeof(context->regs) == sizeof(thread_context.regs),\n                \"registers size mismatch\");\n  memcpy(&context->regs, &thread_context.regs, sizeof(context->regs));\n  context->fp = thread_context.fp;\n  context->ip = thread_context.ip;\n  context->sp = thread_context.sp;\n  context->lr = thread_context.lr;\n  context->pc = thread_context.pc;\n  context->cpsr = thread_context.cpsr;\n\n  memset(&context->fpa_regs, 0, sizeof(context->fpa_regs));\n  memset(&context->vfp_regs, 0, sizeof(context->vfp_regs));\n  context->have_fpa_regs = false;\n  context->have_vfp_regs = false;\n}\n\nvoid InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context,\n                               const FloatContext::f64_t& float_context,\n                               CPUContextARM64* context) {\n  InitializeCPUContextARM64_NoFloatingPoint(thread_context, context);\n\n  static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs),\n                \"fpsimd context size mismatch\");\n  memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd));\n  context->fpsr = float_context.fpsr;\n  context->fpcr = float_context.fpcr;\n}\n\nvoid InitializeCPUContextARM64_NoFloatingPoint(\n    const ThreadContext::t64_t& thread_context,\n    CPUContextARM64* context) {\n  static_assert(sizeof(context->regs) == sizeof(thread_context.regs),\n                \"gpr context size mismtach\");\n  memcpy(context->regs, thread_context.regs, sizeof(context->regs));\n  context->sp = thread_context.sp;\n  context->pc = thread_context.pc;\n  // Linux seems to only be putting the SPSR register in its \"pstate\" field.\n  // https://elixir.bootlin.com/linux/latest/source/arch/arm64/include/uapi/asm/ptrace.h\n  if (thread_context.pstate >\n      std::numeric_limits<decltype(context->spsr)>::max()) {\n    LOG(WARNING) << \"pstate truncation: we only expect the SPSR bits to be set \"\n                    \"in the pstate\";\n  }\n  context->spsr = static_cast<decltype(context->spsr)>(thread_context.pstate);\n\n  memset(&context->fpsimd, 0, sizeof(context->fpsimd));\n  context->fpsr = 0;\n  context->fpcr = 0;\n}\n\nvoid InitializeCPUContextARM64_OnlyFPSIMD(\n    const SignalFPSIMDContext& float_context,\n    CPUContextARM64* context) {\n  static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs),\n                \"fpsimd context size mismatch\");\n  memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd));\n  context->fpsr = float_context.fpsr;\n  context->fpcr = float_context.fpcr;\n}\n\n#elif defined(ARCH_CPU_RISCV64)\n\nvoid InitializeCPUContextRISCV64(const ThreadContext::t64_t& thread_context,\n                                 const FloatContext::f64_t& float_context,\n                                 CPUContextRISCV64* context) {\n  context->pc = thread_context.pc;\n\n  static_assert(sizeof(context->regs) == sizeof(thread_context.regs));\n  memcpy(context->regs, thread_context.regs, sizeof(context->regs));\n\n  static_assert(sizeof(context->fpregs) == sizeof(float_context.fpregs));\n  memcpy(context->fpregs, float_context.fpregs, sizeof(context->fpregs));\n  context->fcsr = float_context.fcsr;\n}\n\n#endif  // ARCH_CPU_X86_FAMILY\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/cpu_context_linux.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_\n#define CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/linux/signal_context.h\"\n#include \"util/linux/thread_info.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n#if defined(ARCH_CPU_X86_FAMILY) || DOXYGEN\n\n//! \\{\n//! \\brief Initializes a CPUContextX86 structure from native context structures\n//!     on Linux.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] float_context The native float context.\n//! \\param[out] context The CPUContextX86 structure to initialize.\nvoid InitializeCPUContextX86(const ThreadContext::t32_t& thread_context,\n                             const FloatContext::f32_t& float_context,\n                             CPUContextX86* context);\n\nvoid InitializeCPUContextX86(const SignalThreadContext32& thread_context,\n                             const SignalFloatContext32& float_context,\n                             CPUContextX86* context);\n//! \\}\n\n//! \\brief Initializes GPR and debug state in a CPUContextX86 from a native\n//!     signal context structure on Linux.\n//!\n//! Floating point state and debug registers are initialized to zero.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[out] context The CPUContextX86 structure to initialize.\nvoid InitializeCPUContextX86_NoFloatingPoint(\n    const SignalThreadContext32& thread_context,\n    CPUContextX86* context);\n\n//! \\{\n//! \\brief Initializes a CPUContextX86_64 structure from native context\n//!     structures on Linux.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] float_context The native float context.\n//! \\param[out] context The CPUContextX86_64 structure to initialize.\nvoid InitializeCPUContextX86_64(const ThreadContext::t64_t& thread_context,\n                                const FloatContext::f64_t& float_context,\n                                CPUContextX86_64* context);\n\nvoid InitializeCPUContextX86_64(const SignalThreadContext64& thread_context,\n                                const SignalFloatContext64& float_context,\n                                CPUContextX86_64* context);\n//! \\}\n\n//! \\brief Initializes GPR and debug state in a CPUContextX86_64 from a native\n//!     signal context structure on Linux.\n//!\n//! Floating point state and debug registers are initialized to zero.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[out] context The CPUContextX86_64 structure to initialize.\nvoid InitializeCPUContextX86_64_NoFloatingPoint(\n    const SignalThreadContext64& thread_context,\n    CPUContextX86_64* context);\n\n#endif  // ARCH_CPU_X86_FAMILY || DOXYGEN\n\n#if defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN\n\n//! \\brief Initializes a CPUContextARM structure from native context structures\n//!     on Linux.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] float_context The native float context.\n//! \\param[out] context The CPUContextARM structure to initialize.\nvoid InitializeCPUContextARM(const ThreadContext::t32_t& thread_context,\n                             const FloatContext::f32_t& float_context,\n                             CPUContextARM* context);\n\n//! \\brief Initializes GPR state in a CPUContextARM from a native signal context\n//!     structure on Linux.\n//!\n//! Floating point state is initialized to zero.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[out] context The CPUContextARM structure to initialize.\nvoid InitializeCPUContextARM_NoFloatingPoint(\n    const SignalThreadContext32& thread_context,\n    CPUContextARM* context);\n\n//! \\brief Initializes a CPUContextARM64 structure from native context\n//!     structures on Linux.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] float_context The native float context.\n//! \\param[out] context The CPUContextARM64 structure to initialize.\nvoid InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context,\n                               const FloatContext::f64_t& float_context,\n                               CPUContextARM64* context);\n\n//! \\brief Initializes GPR state in a CPUContextARM64 from a native context\n//!     structure on Linux.\n//!\n//! Floating point state is initialized to zero.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[out] context The CPUContextARM64 structure to initialize.\nvoid InitializeCPUContextARM64_NoFloatingPoint(\n    const ThreadContext::t64_t& thread_context,\n    CPUContextARM64* context);\n\n//! \\brief Initializes FPSIMD state in a CPUContextARM64 from a native fpsimd\n//!     signal context structure on Linux.\n//!\n//! General purpose registers are not initialized.\n//!\n//! \\param[in] float_context The native fpsimd context.\n//! \\param[out] context The CPUContextARM64 structure to initialize.\nvoid InitializeCPUContextARM64_OnlyFPSIMD(\n    const SignalFPSIMDContext& float_context,\n    CPUContextARM64* context);\n\n#endif  // ARCH_CPU_ARM_FAMILY || DOXYGEN\n\n#if defined(ARCH_CPU_MIPS_FAMILY) || DOXYGEN\n\n//! \\brief Initializes a CPUContextMIPS structure from native context\n//!     structures on Linux.\n//!\n//! This function has template specializations for MIPSEL and MIPS64EL\n//! architecture contexts, using ContextTraits32 or ContextTraits64 as template\n//! parameter, respectively.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] float_context The native float context.\n//! \\param[out] context The CPUContextMIPS structure to initialize.\ntemplate <typename Traits>\nvoid InitializeCPUContextMIPS(\n    const typename Traits::SignalThreadContext& thread_context,\n    const typename Traits::SignalFloatContext& float_context,\n    typename Traits::CPUContext* context) {\n  static_assert(sizeof(context->regs) == sizeof(thread_context.regs),\n                \"registers size mismatch\");\n  static_assert(sizeof(context->fpregs) == sizeof(float_context.fpregs),\n                \"fp registers size mismatch\");\n  memcpy(&context->regs, &thread_context.regs, sizeof(context->regs));\n  context->mdlo = thread_context.lo;\n  context->mdhi = thread_context.hi;\n  context->cp0_epc = thread_context.cp0_epc;\n  context->cp0_badvaddr = thread_context.cp0_badvaddr;\n  context->cp0_status = thread_context.cp0_status;\n  context->cp0_cause = thread_context.cp0_cause;\n\n  memcpy(&context->fpregs, &float_context.fpregs, sizeof(context->fpregs));\n  context->fpcsr = float_context.fpcsr;\n  context->fir = float_context.fpu_id;\n}\n\n#endif  // ARCH_CPU_MIPS_FAMILY || DOXYGEN\n\n#if defined(ARCH_CPU_RISCV64) || DOXYGEN\n\n//! \\brief Initializes a CPUContextRISCV64 structure from native context\n//!     structures on Linux.\n//!\n//! \\param[in] thread_context The native thread context.\n//! \\param[in] float_context The native float context.\n//! \\param[out] context The CPUContextRISCV64 structure to initialize.\nvoid InitializeCPUContextRISCV64(const ThreadContext::t64_t& thread_context,\n                                 const FloatContext::f64_t& float_context,\n                                 CPUContextRISCV64* context);\n\n#endif  // ARCH_CPU_RISCV64 || DOXYGEN\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_\n"
  },
  {
    "path": "snapshot/linux/debug_rendezvous.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/debug_rendezvous.h\"\n\n#include <stdint.h>\n\n#include <set>\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\nstruct Traits32 {\n  using Integer = int32_t;\n  using Address = uint32_t;\n};\n\nstruct Traits64 {\n  using Integer = int64_t;\n  using Address = uint64_t;\n};\n\ntemplate <typename Traits>\nstruct DebugRendezvousSpecific {\n  typename Traits::Integer r_version;\n  typename Traits::Address r_map;\n  typename Traits::Address r_brk;\n  typename Traits::Integer r_state;\n  typename Traits::Address r_ldbase;\n};\n\ntemplate <typename Traits>\nstruct LinkEntrySpecific {\n  typename Traits::Address l_addr;\n  typename Traits::Address l_name;\n  typename Traits::Address l_ld;\n  typename Traits::Address l_next;\n  typename Traits::Address l_prev;\n};\n\ntemplate <typename Traits>\nbool ReadLinkEntry(const ProcessMemoryRange& memory,\n                   LinuxVMAddress* address,\n                   DebugRendezvous::LinkEntry* entry_out) {\n  LinkEntrySpecific<Traits> entry;\n  if (!memory.Read(*address, sizeof(entry), &entry)) {\n    return false;\n  }\n\n  std::string name;\n  if (!memory.ReadCStringSizeLimited(entry.l_name, 4096, &name)) {\n    name.clear();\n  }\n\n  entry_out->load_bias = entry.l_addr;\n  entry_out->dynamic_array = entry.l_ld;\n  entry_out->name.swap(name);\n\n  *address = entry.l_next;\n  return true;\n}\n\n}  // namespace\n\nDebugRendezvous::LinkEntry::LinkEntry()\n    : name(), load_bias(0), dynamic_array(0) {}\n\nDebugRendezvous::DebugRendezvous()\n    : modules_(), executable_(), initialized_() {}\n\nDebugRendezvous::~DebugRendezvous() {}\n\nbool DebugRendezvous::Initialize(const ProcessMemoryRange& memory,\n                                 LinuxVMAddress address) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  if (!(memory.Is64Bit() ? InitializeSpecific<Traits64>(memory, address)\n                         : InitializeSpecific<Traits32>(memory, address))) {\n    return false;\n  }\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst DebugRendezvous::LinkEntry* DebugRendezvous::Executable() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &executable_;\n}\n\nconst std::vector<DebugRendezvous::LinkEntry>& DebugRendezvous::Modules()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return modules_;\n}\n\ntemplate <typename Traits>\nbool DebugRendezvous::InitializeSpecific(const ProcessMemoryRange& memory,\n                                         LinuxVMAddress address) {\n  DebugRendezvousSpecific<Traits> debug;\n  if (!memory.Read(address, sizeof(debug), &debug)) {\n    return false;\n  }\n  if (debug.r_version != 1) {\n    LOG(ERROR) << \"unexpected version \" << debug.r_version;\n    return false;\n  }\n\n  LinuxVMAddress link_entry_address = debug.r_map;\n  if (!ReadLinkEntry<Traits>(memory, &link_entry_address, &executable_)) {\n    return false;\n  }\n\n  std::set<LinuxVMAddress> visited;\n  while (link_entry_address) {\n    if (!visited.insert(link_entry_address).second) {\n      LOG(ERROR) << \"cycle at address 0x\" << std::hex << link_entry_address;\n      return false;\n    }\n\n    LinkEntry entry;\n    if (!ReadLinkEntry<Traits>(memory, &link_entry_address, &entry)) {\n      return false;\n    }\n    modules_.push_back(entry);\n  }\n\n#if BUILDFLAG(IS_ANDROID)\n  // Android P (API 28) mistakenly places the vdso in the first entry in the\n  // link map.\n  const int android_runtime_api = android_get_device_api_level();\n  if (android_runtime_api == 28 && executable_.name == \"[vdso]\") {\n    LinkEntry executable = modules_[0];\n    modules_[0] = executable_;\n    executable_ = executable;\n  }\n#endif  // BUILDFLAG(IS_ANDROID)\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/debug_rendezvous.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_\n#define CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_\n\n#include <string>\n#include <vector>\n\n#include \"util/linux/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n//! \\brief Reads an `r_debug` struct defined in `<link.h>` via\n//!     ProcessMemoryRange.\nclass DebugRendezvous {\n public:\n  //! \\brief An entry in the dynamic linker's list of loaded objects.\n  //!\n  //! All of these values should be checked before use. Whether and how they are\n  //! populated may vary by dynamic linker.\n  struct LinkEntry {\n    LinkEntry();\n\n    //! \\brief A filename identifying the object.\n    std::string name;\n\n    //! \\brief The difference between the preferred load address in the ELF file\n    //!     and the actual loaded address in memory.\n    VMAddress load_bias;\n\n    //! \\brief The address of the dynamic array for this object.\n    LinuxVMAddress dynamic_array;\n  };\n\n  DebugRendezvous();\n\n  DebugRendezvous(const DebugRendezvous&) = delete;\n  DebugRendezvous& operator=(const DebugRendezvous&) = delete;\n\n  ~DebugRendezvous();\n\n  //! \\brief Initializes this object by reading an `r_debug` struct from a\n  //!     target process.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class.\n  //!\n  //! \\param[in] memory A memory reader for the remote process.\n  //! \\param[in] address The address of an `r_debug` struct in the remote\n  //!     process.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Initialize(const ProcessMemoryRange& memory, LinuxVMAddress address);\n\n  //! \\brief Returns the LinkEntry for the main executable.\n  const LinkEntry* Executable() const;\n\n  //! \\brief Returns a vector of modules found in the link map.\n  //!\n  //! This list excludes the entry for the executable and may include entries\n  //! for the VDSO and loader.\n  const std::vector<LinkEntry>& Modules() const;\n\n private:\n  template <typename Traits>\n  bool InitializeSpecific(const ProcessMemoryRange& memory,\n                          LinuxVMAddress address);\n\n  std::vector<LinkEntry> modules_;\n  LinkEntry executable_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_\n"
  },
  {
    "path": "snapshot/linux/debug_rendezvous_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/debug_rendezvous.h\"\n\n#include <linux/auxvec.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <limits>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/elf/elf_image_reader.h\"\n#include \"snapshot/linux/test_modules.h\"\n#include \"test/linux/fake_ptrace_connection.h\"\n#include \"test/main_arguments.h\"\n#include \"test/multiprocess.h\"\n#include \"util/linux/address_types.h\"\n#include \"util/linux/auxiliary_vector.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#include \"util/linux/memory_map.h\"\n#include \"util/misc/address_sanitizer.h\"\n#include \"util/misc/memory_sanitizer.h\"\n#include \"util/numeric/safe_assignment.h\"\n#include \"util/process/process_memory_linux.h\"\n#include \"util/process/process_memory_range.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid ExpectLoadBias(bool is_64_bit,\n                    VMAddress unsigned_bias,\n                    VMOffset signed_bias) {\n  if (is_64_bit) {\n    EXPECT_EQ(unsigned_bias, static_cast<VMAddress>(signed_bias));\n  } else {\n    uint32_t unsigned_bias32;\n    ASSERT_TRUE(AssignIfInRange(&unsigned_bias32, unsigned_bias));\n\n    uint32_t casted_bias32 = static_cast<uint32_t>(signed_bias);\n    EXPECT_EQ(unsigned_bias32, casted_bias32);\n  }\n}\n\nvoid TestAgainstTarget(PtraceConnection* connection) {\n  // Use ElfImageReader on the main executable which can tell us the debug\n  // address. glibc declares the symbol _r_debug in link.h which we can use to\n  // get the address, but Android does not.\n  AuxiliaryVector aux;\n  ASSERT_TRUE(aux.Initialize(connection));\n\n  LinuxVMAddress phdrs;\n  ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));\n\n  MemoryMap mappings;\n  ASSERT_TRUE(mappings.Initialize(connection));\n\n  const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs);\n  ASSERT_TRUE(phdr_mapping);\n\n  auto exe_mappings = mappings.FindFilePossibleMmapStarts(*phdr_mapping);\n  ASSERT_EQ(exe_mappings->Count(), 1u);\n  LinuxVMAddress elf_address = exe_mappings->Next()->range.Base();\n\n  ProcessMemoryLinux memory(connection);\n  ProcessMemoryRange range;\n  ASSERT_TRUE(range.Initialize(&memory, connection->Is64Bit()));\n\n  ElfImageReader exe_reader;\n  ASSERT_TRUE(exe_reader.Initialize(range, elf_address));\n  LinuxVMAddress debug_address;\n  ASSERT_TRUE(exe_reader.GetDebugAddress(&debug_address));\n\n  VMAddress exe_dynamic_address = 0;\n  if (exe_reader.GetDynamicArrayAddress(&exe_dynamic_address)) {\n    CheckedLinuxAddressRange exe_range(\n        connection->Is64Bit(), exe_reader.Address(), exe_reader.Size());\n    EXPECT_TRUE(exe_range.ContainsValue(exe_dynamic_address));\n  }\n\n  // start the actual tests\n  DebugRendezvous debug;\n  ASSERT_TRUE(debug.Initialize(range, debug_address));\n\n#if BUILDFLAG(IS_ANDROID)\n  const int android_runtime_api = android_get_device_api_level();\n  ASSERT_GE(android_runtime_api, 1);\n\n  base::FilePath exe_name(base::FilePath(GetMainArguments()[0]).BaseName());\n  EXPECT_NE(debug.Executable()->name.find(exe_name.value()), std::string::npos);\n\n  // Android's loader doesn't set the dynamic array for the executable in the\n  // link map until Android 10.0 (API 29).\n  if (android_runtime_api >= 29) {\n    EXPECT_EQ(debug.Executable()->dynamic_array, exe_dynamic_address);\n  } else {\n    EXPECT_EQ(debug.Executable()->dynamic_array, 0u);\n  }\n#else\n  // glibc's loader implements most of the tested features that Android's was\n  // missing but has since gained.\n  const int android_runtime_api = std::numeric_limits<int>::max();\n\n  // glibc's loader does not set the name for the executable.\n  EXPECT_TRUE(debug.Executable()->name.empty());\n  EXPECT_EQ(debug.Executable()->dynamic_array, exe_dynamic_address);\n#endif  // BUILDFLAG(IS_ANDROID)\n\n  // Android's loader doesn't set the load bias until Android 4.3 (API 18).\n  if (android_runtime_api >= 18) {\n    ExpectLoadBias(connection->Is64Bit(),\n                   debug.Executable()->load_bias,\n                   exe_reader.GetLoadBias());\n  } else {\n    EXPECT_EQ(debug.Executable()->load_bias, 0u);\n  }\n\n  for (const DebugRendezvous::LinkEntry& module : debug.Modules()) {\n    SCOPED_TRACE(base::StringPrintf(\"name %s, load_bias 0x%\" PRIx64\n                                    \", dynamic_array 0x%\" PRIx64,\n                                    module.name.c_str(),\n                                    module.load_bias,\n                                    module.dynamic_array));\n    const bool is_android_loader = (module.name == \"/system/bin/linker\" ||\n                                    module.name == \"/system/bin/linker64\");\n\n    // Android's loader doesn't set its own dynamic array until Android 4.2\n    // (API 17).\n    if (is_android_loader && android_runtime_api < 17) {\n      EXPECT_EQ(module.dynamic_array, 0u);\n      EXPECT_EQ(module.load_bias, 0u);\n      continue;\n    }\n\n    ASSERT_TRUE(module.dynamic_array);\n    const MemoryMap::Mapping* dyn_mapping =\n        mappings.FindMapping(module.dynamic_array);\n    ASSERT_TRUE(dyn_mapping);\n\n    auto possible_mappings = mappings.FindFilePossibleMmapStarts(*dyn_mapping);\n    ASSERT_GE(possible_mappings->Count(), 1u);\n\n    std::unique_ptr<ElfImageReader> module_reader;\n#if !BUILDFLAG(IS_ANDROID)\n    const MemoryMap::Mapping* module_mapping = nullptr;\n#endif\n    const MemoryMap::Mapping* mapping = nullptr;\n    while ((mapping = possible_mappings->Next())) {\n      auto parsed_module = std::make_unique<ElfImageReader>();\n      VMAddress dynamic_address;\n      if (parsed_module->Initialize(\n              range, mapping->range.Base(), possible_mappings->Count() == 0) &&\n          parsed_module->GetDynamicArrayAddress(&dynamic_address) &&\n          dynamic_address == module.dynamic_array) {\n        module_reader = std::move(parsed_module);\n#if !BUILDFLAG(IS_ANDROID)\n        module_mapping = mapping;\n#endif\n        break;\n      }\n    }\n    ASSERT_TRUE(module_reader.get());\n\n#if BUILDFLAG(IS_ANDROID)\n    EXPECT_FALSE(module.name.empty());\n#else\n    // glibc's loader doesn't always set the name in the link map for the vdso.\n    EXPECT_PRED4(\n        [](const std::string mapping_name,\n           int device,\n           int inode,\n           const std::string& module_name) {\n          const bool is_vdso_mapping =\n              device == 0 && inode == 0 && mapping_name == \"[vdso]\";\n#if defined(ARCH_CPU_X86)\n          static constexpr char kPrefix[] = \"linux-gate.so.\";\n#else\n          static constexpr char kPrefix[] = \"linux-vdso.so.\";\n#endif\n          return is_vdso_mapping ==\n                 (module_name.empty() ||\n                  module_name.compare(0, strlen(kPrefix), kPrefix) == 0);\n        },\n        module_mapping->name,\n        module_mapping->device,\n        module_mapping->inode,\n        module.name);\n#endif  // BUILDFLAG(IS_ANDROID)\n\n    // Android's loader stops setting its own load bias after Android 4.4.4\n    // (API 20) until Android 6.0 (API 23).\n    if (is_android_loader && android_runtime_api > 20 &&\n        android_runtime_api < 23) {\n      EXPECT_EQ(module.load_bias, 0u);\n    } else {\n      ExpectLoadBias(connection->Is64Bit(),\n                     module.load_bias,\n                     static_cast<VMAddress>(module_reader->GetLoadBias()));\n    }\n\n    CheckedLinuxAddressRange module_range(\n        connection->Is64Bit(), module_reader->Address(), module_reader->Size());\n    EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array));\n  }\n}\n\nTEST(DebugRendezvous, Self) {\n#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER)\n  const std::string module_name = \"test_module.so\";\n  const std::string module_soname = \"test_module_soname\";\n  ScopedModuleHandle empty_test_module(\n      LoadTestModule(module_name, module_soname));\n  ASSERT_TRUE(empty_test_module.valid());\n#endif  // !ADDRESS_SANITIZER && !MEMORY_SANITIZER\n\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  TestAgainstTarget(&connection);\n}\n\nclass ChildTest : public Multiprocess {\n public:\n  ChildTest() {}\n\n  ChildTest(const ChildTest&) = delete;\n  ChildTest& operator=(const ChildTest&) = delete;\n\n  ~ChildTest() {}\n\n private:\n  void MultiprocessParent() {\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(ChildPID()));\n\n    TestAgainstTarget(&connection);\n  }\n\n  void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); }\n};\n\nTEST(DebugRendezvous, Child) {\n  ChildTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/exception_snapshot_linux.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/exception_snapshot_linux.h\"\n\n#include <signal.h>\n\n#include \"base/logging.h\"\n#include \"snapshot/linux/capture_memory_delegate_linux.h\"\n#include \"snapshot/linux/cpu_context_linux.h\"\n#include \"snapshot/linux/process_reader_linux.h\"\n#include \"snapshot/linux/signal_context.h\"\n#include \"util/linux/traits.h\"\n#include \"util/misc/reinterpret_bytes.h\"\n#include \"util/numeric/safe_assignment.h\"\n#include \"util/posix/signals.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nExceptionSnapshotLinux::ExceptionSnapshotLinux()\n    : ExceptionSnapshot(),\n      context_union_(),\n      context_(),\n      codes_(),\n      thread_id_(0),\n      exception_address_(0),\n      signal_number_(0),\n      signal_code_(0),\n      initialized_() {}\n\nExceptionSnapshotLinux::~ExceptionSnapshotLinux() {}\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\ntemplate <>\nbool ExceptionSnapshotLinux::ReadContext<ContextTraits32>(\n    ProcessReaderLinux* reader,\n    LinuxVMAddress context_address) {\n  UContext<ContextTraits32> ucontext;\n  if (!reader->Memory()->Read(context_address, sizeof(ucontext), &ucontext)) {\n    LOG(ERROR) << \"Couldn't read ucontext\";\n    return false;\n  }\n\n  context_.architecture = kCPUArchitectureX86;\n  context_.x86 = &context_union_.x86;\n\n  if (!ucontext.mcontext.fpptr) {\n    InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs,\n                                            context_.x86);\n    return true;\n  }\n\n  SignalFloatContext32 fprs;\n  if (!reader->Memory()->Read(ucontext.mcontext.fpptr, sizeof(fprs), &fprs)) {\n    LOG(ERROR) << \"Couldn't read float context\";\n    return false;\n  }\n\n  if (fprs.magic == X86_FXSR_MAGIC) {\n    InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs,\n                                            context_.x86);\n    if (!reader->Memory()->Read(\n            ucontext.mcontext.fpptr + offsetof(SignalFloatContext32, fxsave),\n            sizeof(CPUContextX86::Fxsave),\n            &context_.x86->fxsave)) {\n      LOG(ERROR) << \"Couldn't read fxsave\";\n      return false;\n    }\n  } else if (fprs.magic == 0xffff) {\n    InitializeCPUContextX86(ucontext.mcontext.gprs, fprs, context_.x86);\n  } else {\n    LOG(ERROR) << \"unexpected magic 0x\" << std::hex << fprs.magic;\n    return false;\n  }\n\n  return true;\n}\n\ntemplate <>\nbool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(\n    ProcessReaderLinux* reader,\n    LinuxVMAddress context_address) {\n  UContext<ContextTraits64> ucontext;\n  if (!reader->Memory()->Read(context_address, sizeof(ucontext), &ucontext)) {\n    LOG(ERROR) << \"Couldn't read ucontext\";\n    return false;\n  }\n\n  context_.architecture = kCPUArchitectureX86_64;\n  context_.x86_64 = &context_union_.x86_64;\n\n  if (!ucontext.mcontext.fpptr) {\n    InitializeCPUContextX86_64_NoFloatingPoint(ucontext.mcontext.gprs,\n                                               context_.x86_64);\n    return true;\n  }\n\n  SignalFloatContext64 fprs;\n  if (!reader->Memory()->Read(ucontext.mcontext.fpptr, sizeof(fprs), &fprs)) {\n    LOG(ERROR) << \"Couldn't read float context\";\n    return false;\n  }\n\n  InitializeCPUContextX86_64(ucontext.mcontext.gprs, fprs, context_.x86_64);\n  return true;\n}\n\n#elif defined(ARCH_CPU_ARM_FAMILY)\n\ntemplate <>\nbool ExceptionSnapshotLinux::ReadContext<ContextTraits32>(\n    ProcessReaderLinux* reader,\n    LinuxVMAddress context_address) {\n  context_.architecture = kCPUArchitectureARM;\n  context_.arm = &context_union_.arm;\n\n  CPUContextARM* dest_context = context_.arm;\n  const ProcessMemory* memory = reader->Memory();\n\n  LinuxVMAddress gprs_address =\n      context_address + offsetof(UContext<ContextTraits32>, mcontext32) +\n      offsetof(ContextTraits32::MContext32, gprs);\n\n  SignalThreadContext32 thread_context;\n  if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) {\n    LOG(ERROR) << \"Couldn't read gprs\";\n    return false;\n  }\n  InitializeCPUContextARM_NoFloatingPoint(thread_context, dest_context);\n\n  LinuxVMAddress reserved_address =\n      context_address + offsetof(UContext<ContextTraits32>, reserved);\n  if ((reserved_address & 7) != 0) {\n    LOG(ERROR) << \"invalid alignment 0x\" << std::hex << reserved_address;\n    return false;\n  }\n\n  constexpr VMSize kMaxContextSpace = 1024;\n\n  ProcessMemoryRange range;\n  if (!range.Initialize(memory, false, reserved_address, kMaxContextSpace)) {\n    return false;\n  }\n\n  do {\n    CoprocessorContextHead head;\n    if (!range.Read(reserved_address, sizeof(head), &head)) {\n      LOG(ERROR) << \"missing context terminator\";\n      return false;\n    }\n    reserved_address += sizeof(head);\n\n    switch (head.magic) {\n      case VFP_MAGIC:\n        if (head.size != sizeof(SignalVFPContext) + sizeof(head)) {\n          LOG(ERROR) << \"unexpected vfp context size \" << head.size;\n          return false;\n        }\n        static_assert(\n            sizeof(SignalVFPContext::vfp) == sizeof(dest_context->vfp_regs),\n            \"vfp context size mismatch\");\n        if (!range.Read(reserved_address + offsetof(SignalVFPContext, vfp),\n                        sizeof(dest_context->vfp_regs),\n                        &dest_context->vfp_regs)) {\n          LOG(ERROR) << \"Couldn't read vfp\";\n          return false;\n        }\n        dest_context->have_vfp_regs = true;\n        return true;\n\n      case CRUNCH_MAGIC:\n      case IWMMXT_MAGIC:\n      case DUMMY_MAGIC:\n        reserved_address += head.size - sizeof(head);\n        continue;\n\n      case 0:\n        return true;\n\n      default:\n        LOG(ERROR) << \"invalid magic number 0x\" << std::hex << head.magic;\n        return false;\n    }\n  } while (true);\n}\n\ntemplate <>\nbool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(\n    ProcessReaderLinux* reader,\n    LinuxVMAddress context_address) {\n  context_.architecture = kCPUArchitectureARM64;\n  context_.arm64 = &context_union_.arm64;\n\n  CPUContextARM64* dest_context = context_.arm64;\n  const ProcessMemory* memory = reader->Memory();\n\n  LinuxVMAddress gprs_address =\n      context_address + offsetof(UContext<ContextTraits64>, mcontext64) +\n      offsetof(ContextTraits64::MContext64, gprs);\n\n  ThreadContext::t64_t thread_context;\n  if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) {\n    LOG(ERROR) << \"Couldn't read gprs\";\n    return false;\n  }\n  InitializeCPUContextARM64_NoFloatingPoint(thread_context, dest_context);\n\n  LinuxVMAddress reserved_address =\n      context_address + offsetof(UContext<ContextTraits64>, reserved);\n  if ((reserved_address & 15) != 0) {\n    LOG(ERROR) << \"invalid alignment 0x\" << std::hex << reserved_address;\n    return false;\n  }\n\n  constexpr VMSize kMaxContextSpace = 4096;\n\n  ProcessMemoryRange range;\n  if (!range.Initialize(memory, true, reserved_address, kMaxContextSpace)) {\n    return false;\n  }\n\n  do {\n    CoprocessorContextHead head;\n    if (!range.Read(reserved_address, sizeof(head), &head)) {\n      LOG(ERROR) << \"missing context terminator\";\n      return false;\n    }\n    reserved_address += sizeof(head);\n\n    switch (head.magic) {\n      case FPSIMD_MAGIC:\n        if (head.size != sizeof(SignalFPSIMDContext) + sizeof(head)) {\n          LOG(ERROR) << \"unexpected fpsimd context size \" << head.size;\n          return false;\n        }\n        SignalFPSIMDContext fpsimd;\n        if (!range.Read(reserved_address, sizeof(fpsimd), &fpsimd)) {\n          LOG(ERROR) << \"Couldn't read fpsimd \" << head.size;\n          return false;\n        }\n        InitializeCPUContextARM64_OnlyFPSIMD(fpsimd, dest_context);\n        return true;\n\n      case ESR_MAGIC:\n      case EXTRA_MAGIC:\n        reserved_address += head.size - sizeof(head);\n        continue;\n\n      case 0:\n        LOG(WARNING) << \"fpsimd not found\";\n        return true;\n\n      default:\n        LOG(ERROR) << \"invalid magic number 0x\" << std::hex << head.magic;\n        return false;\n    }\n  } while (true);\n}\n\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n\ntemplate <typename Traits>\nstatic bool ReadContext(ProcessReaderLinux* reader,\n                        LinuxVMAddress context_address,\n                        typename Traits::CPUContext* dest_context) {\n  const ProcessMemory* memory = reader->Memory();\n\n  LinuxVMAddress gregs_address = context_address +\n                                 offsetof(UContext<Traits>, mcontext) +\n                                 offsetof(typename Traits::MContext, gregs);\n\n  typename Traits::SignalThreadContext thread_context;\n  if (!memory->Read(gregs_address, sizeof(thread_context), &thread_context)) {\n    LOG(ERROR) << \"Couldn't read gregs\";\n    return false;\n  }\n\n  LinuxVMAddress fpregs_address = context_address +\n                                  offsetof(UContext<Traits>, mcontext) +\n                                  offsetof(typename Traits::MContext, fpregs);\n\n  typename Traits::SignalFloatContext fp_context;\n  if (!memory->Read(fpregs_address, sizeof(fp_context), &fp_context)) {\n    LOG(ERROR) << \"Couldn't read fpregs\";\n    return false;\n  }\n\n  InitializeCPUContextMIPS<Traits>(thread_context, fp_context, dest_context);\n\n  return true;\n}\n\ntemplate <>\nbool ExceptionSnapshotLinux::ReadContext<ContextTraits32>(\n    ProcessReaderLinux* reader,\n    LinuxVMAddress context_address) {\n  context_.architecture = kCPUArchitectureMIPSEL;\n  context_.mipsel = &context_union_.mipsel;\n\n  return internal::ReadContext<ContextTraits32>(\n      reader, context_address, context_.mipsel);\n}\n\ntemplate <>\nbool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(\n    ProcessReaderLinux* reader,\n    LinuxVMAddress context_address) {\n  context_.architecture = kCPUArchitectureMIPS64EL;\n  context_.mips64 = &context_union_.mips64;\n\n  return internal::ReadContext<ContextTraits64>(\n      reader, context_address, context_.mips64);\n}\n\n#elif defined(ARCH_CPU_RISCV64)\n\nstatic bool ReadContext(ProcessReaderLinux* reader,\n                        LinuxVMAddress context_address,\n                        typename ContextTraits64::CPUContext* dest_context) {\n  const ProcessMemory* memory = reader->Memory();\n\n  LinuxVMAddress gregs_address = context_address +\n                                 offsetof(UContext<ContextTraits64>, mcontext) +\n                                 offsetof(MContext64, regs);\n\n  typename ContextTraits64::SignalThreadContext thread_context;\n  if (!memory->Read(gregs_address, sizeof(thread_context), &thread_context)) {\n    LOG(ERROR) << \"Couldn't read gregs\";\n    return false;\n  }\n\n  LinuxVMAddress fpregs_address =\n      context_address + offsetof(UContext<ContextTraits64>, mcontext) +\n      offsetof(MContext64, fpregs);\n\n  typename ContextTraits64::SignalFloatContext fp_context;\n  if (!memory->Read(fpregs_address, sizeof(fp_context), &fp_context)) {\n    LOG(ERROR) << \"Couldn't read fpregs\";\n    return false;\n  }\n\n  InitializeCPUContextRISCV64(thread_context, fp_context, dest_context);\n\n  return true;\n}\n\ntemplate <>\nbool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(\n    ProcessReaderLinux* reader,\n    LinuxVMAddress context_address) {\n  context_.architecture = kCPUArchitectureRISCV64;\n  context_.riscv64 = &context_union_.riscv64;\n\n  return internal::ReadContext(reader, context_address, context_.riscv64);\n}\n\n#endif  // ARCH_CPU_X86_FAMILY\n\nbool ExceptionSnapshotLinux::Initialize(\n    ProcessReaderLinux* process_reader,\n    LinuxVMAddress siginfo_address,\n    LinuxVMAddress context_address,\n    pid_t thread_id,\n    uint32_t* gather_indirectly_referenced_memory_cap) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  thread_id_ = thread_id;\n  const ProcessReaderLinux::Thread* thread = nullptr;\n  for (const auto& loop_thread : process_reader->Threads()) {\n    if (thread_id == loop_thread.tid) {\n      thread = &loop_thread;\n      break;\n    }\n  }\n  if (!thread) {\n    // This is allowed until {ProcessReaderLinux::InitializeThreads()} is\n    // improved to support target threads in the same thread group.\n    LOG(WARNING) << \"thread ID \" << thread_id << \" not found in process\";\n  }\n\n  if (process_reader->Is64Bit()) {\n    if (!ReadContext<ContextTraits64>(process_reader, context_address) ||\n        !ReadSiginfo<Traits64>(process_reader, siginfo_address)) {\n      return false;\n    }\n  } else {\n#if !defined(ARCH_CPU_RISCV64)\n    if (!ReadContext<ContextTraits32>(process_reader, context_address) ||\n        !ReadSiginfo<Traits32>(process_reader, siginfo_address)) {\n      return false;\n    }\n#endif\n  }\n\n  CaptureMemoryDelegateLinux capture_memory_delegate(\n      process_reader,\n      thread,\n      &extra_memory_,\n      gather_indirectly_referenced_memory_cap);\n  CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\ntemplate <typename Traits>\nbool ExceptionSnapshotLinux::ReadSiginfo(ProcessReaderLinux* reader,\n                                         LinuxVMAddress siginfo_address) {\n  Siginfo<Traits> siginfo;\n  if (!reader->Memory()->Read(siginfo_address, sizeof(siginfo), &siginfo)) {\n    LOG(ERROR) << \"Couldn't read siginfo\";\n    return false;\n  }\n\n  signal_number_ = siginfo.signo;\n  signal_code_ = siginfo.code;\n\n  uint64_t extra_code;\n#define PUSH_CODE(value)                         \\\n  do {                                           \\\n    if (!ReinterpretBytes(value, &extra_code)) { \\\n      LOG(ERROR) << \"bad code\";                  \\\n      return false;                              \\\n    }                                            \\\n    codes_.push_back(extra_code);                \\\n  } while (false)\n\n  switch (siginfo.signo) {\n    case SIGILL:\n    case SIGFPE:\n    case SIGSEGV:\n    case SIGBUS:\n    case SIGTRAP:\n      exception_address_ = siginfo.address;\n      break;\n\n    case SIGPOLL:  // SIGIO\n      PUSH_CODE(siginfo.band);\n      PUSH_CODE(siginfo.fd);\n      break;\n\n    case SIGSYS:\n      exception_address_ = siginfo.call_address;\n      PUSH_CODE(siginfo.syscall);\n      PUSH_CODE(siginfo.arch);\n      break;\n\n    case SIGALRM:\n    case SIGVTALRM:\n    case SIGPROF:\n      PUSH_CODE(siginfo.timerid);\n      PUSH_CODE(siginfo.overrun);\n      PUSH_CODE(siginfo.sigval.sigval);\n      break;\n\n    case SIGABRT:\n    case SIGQUIT:\n    case SIGXCPU:\n    case SIGXFSZ:\n    case SIGHUP:\n    case SIGINT:\n    case SIGPIPE:\n    case SIGTERM:\n    case SIGUSR1:\n    case SIGUSR2:\n#if defined(SIGEMT)\n    case SIGEMT:\n#endif  // SIGEMT\n#if defined(SIGPWR)\n    case SIGPWR:\n#endif  // SIGPWR\n#if defined(SIGSTKFLT)\n    case SIGSTKFLT:\n#endif  // SIGSTKFLT\n      PUSH_CODE(siginfo.pid);\n      PUSH_CODE(siginfo.uid);\n      PUSH_CODE(siginfo.sigval.sigval);\n      break;\n\n    case Signals::kSimulatedSigno:\n      break;\n\n    default:\n      LOG(WARNING) << \"Unhandled signal \" << siginfo.signo;\n  }\n\n  return true;\n}\n\nconst CPUContext* ExceptionSnapshotLinux::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nuint64_t ExceptionSnapshotLinux::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nuint32_t ExceptionSnapshotLinux::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return signal_number_;\n}\n\nuint32_t ExceptionSnapshotLinux::ExceptionInfo() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return signal_code_;\n}\n\nuint64_t ExceptionSnapshotLinux::ExceptionAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_address_;\n}\n\nconst std::vector<uint64_t>& ExceptionSnapshotLinux::Codes() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return codes_;\n}\n\nstd::vector<const MemorySnapshot*> ExceptionSnapshotLinux::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> result;\n  result.reserve(extra_memory_.size());\n  for (const auto& em : extra_memory_) {\n    result.push_back(em.get());\n  }\n  return result;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/exception_snapshot_linux.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_EXCEPTION_SNAPSHOT_LINUX_H_\n#define CRASHPAD_SNAPSHOT_LINUX_EXCEPTION_SNAPSHOT_LINUX_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/linux/process_reader_linux.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"util/linux/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief An ExceptionSnapshot of an signal received by a running (or crashed)\n//!     process on a Linux system.\nclass ExceptionSnapshotLinux final : public ExceptionSnapshot {\n public:\n  ExceptionSnapshotLinux();\n\n  ExceptionSnapshotLinux(const ExceptionSnapshotLinux&) = delete;\n  ExceptionSnapshotLinux& operator=(const ExceptionSnapshotLinux&) = delete;\n\n  ~ExceptionSnapshotLinux() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderLinux for the process that\n  //! received\n  //!     the signal.\n  //! \\param[in] siginfo_address The address in the target process' address\n  //!     space of the siginfo_t passed to the signal handler.\n  //! \\param[in] context_address The address in the target process' address\n  //!     space of the ucontext_t passed to the signal handler.\n  //! \\param[in] thread_id The thread ID of the thread that received the signal.\n  //! \\param[inout] gather_indirectly_referenced_memory_cap The remaining budget\n  //!     for indirectly referenced memory, honored on entry and updated on\n  //!     return.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(ProcessReaderLinux* process_reader,\n                  LinuxVMAddress siginfo_address,\n                  LinuxVMAddress context_address,\n                  pid_t thread_id,\n                  uint32_t* gather_indirectly_referenced_memory_cap);\n\n  // ExceptionSnapshot:\n\n  const CPUContext* Context() const override;\n  uint64_t ThreadID() const override;\n  uint32_t Exception() const override;\n  uint32_t ExceptionInfo() const override;\n  uint64_t ExceptionAddress() const override;\n  const std::vector<uint64_t>& Codes() const override;\n  virtual std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  template <typename Traits>\n  bool ReadSiginfo(ProcessReaderLinux* reader, LinuxVMAddress siginfo_address);\n\n  template <typename Traits>\n  bool ReadContext(ProcessReaderLinux* reader, LinuxVMAddress context_address);\n\n  union {\n#if defined(ARCH_CPU_X86_FAMILY)\n    CPUContextX86 x86;\n    CPUContextX86_64 x86_64;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n    CPUContextARM arm;\n    CPUContextARM64 arm64;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n    CPUContextMIPS mipsel;\n    CPUContextMIPS64 mips64;\n#elif defined(ARCH_CPU_RISCV64)\n    CPUContextRISCV64 riscv64;\n#endif\n  } context_union_;\n  CPUContext context_;\n  std::vector<uint64_t> codes_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>> extra_memory_;\n  uint64_t thread_id_;\n  uint64_t exception_address_;\n  uint32_t signal_number_;\n  uint32_t signal_code_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_EXCEPTION_SNAPSHOT_LINUX_H_\n"
  },
  {
    "path": "snapshot/linux/exception_snapshot_linux_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/exception_snapshot_linux.h\"\n\n#include <linux/posix_types.h>\n#include <signal.h>\n#include <string.h>\n#include <time.h>\n#include <ucontext.h>\n#include <unistd.h>\n\n#include <iterator>\n\n#include \"base/bit_cast.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/cpu_architecture.h\"\n#include \"snapshot/linux/process_reader_linux.h\"\n#include \"snapshot/linux/signal_context.h\"\n#include \"sys/syscall.h\"\n#include \"test/errors.h\"\n#include \"test/linux/fake_ptrace_connection.h\"\n#include \"util/linux/address_types.h\"\n#include \"util/misc/clock.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/posix/signals.h\"\n#include \"util/synchronization/semaphore.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\npid_t gettid() {\n  return syscall(SYS_gettid);\n}\n\n#if defined(ARCH_CPU_X86)\nstruct FxsaveUContext {\n  ucontext_t ucontext;\n  CPUContextX86::Fxsave fxsave;\n};\nusing NativeCPUContext = FxsaveUContext;\n\nvoid InitializeContext(NativeCPUContext* context) {\n  context->ucontext.uc_mcontext.gregs[REG_EAX] = 0xabcd1234;\n  context->ucontext.uc_mcontext.fpregs = &context->ucontext.__fpregs_mem;\n  // glibc and bionic use an unsigned long for status, but the kernel treats\n  // status as two uint16_t, with the upper 16 bits called \"magic\" which, if set\n  // to X86_FXSR_MAGIC, indicate that an fxsave follows.\n  reinterpret_cast<uint16_t*>(&context->ucontext.__fpregs_mem.status)[1] =\n      X86_FXSR_MAGIC;\n  memset(&context->fxsave, 43, sizeof(context->fxsave));\n}\n\nvoid ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {\n  EXPECT_EQ(actual.architecture, kCPUArchitectureX86);\n  EXPECT_EQ(\n      actual.x86->eax,\n      base::bit_cast<uint32_t>(expected.ucontext.uc_mcontext.gregs[REG_EAX]));\n  for (unsigned int byte_offset = 0; byte_offset < sizeof(actual.x86->fxsave);\n       ++byte_offset) {\n    SCOPED_TRACE(base::StringPrintf(\"byte offset = %u\\n\", byte_offset));\n    EXPECT_EQ(reinterpret_cast<const char*>(&actual.x86->fxsave)[byte_offset],\n              reinterpret_cast<const char*>(&expected.fxsave)[byte_offset]);\n  }\n}\n#elif defined(ARCH_CPU_X86_64)\nusing NativeCPUContext = ucontext_t;\n\nvoid InitializeContext(NativeCPUContext* context) {\n  context->uc_mcontext.gregs[REG_RAX] = 0xabcd1234abcd1234;\n  context->uc_mcontext.fpregs = &context->__fpregs_mem;\n  memset(&context->__fpregs_mem, 44, sizeof(context->__fpregs_mem));\n}\n\nvoid ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {\n  EXPECT_EQ(actual.architecture, kCPUArchitectureX86_64);\n  EXPECT_EQ(actual.x86_64->rax,\n            base::bit_cast<uint64_t>(expected.uc_mcontext.gregs[REG_RAX]));\n  for (unsigned int byte_offset = 0;\n       byte_offset < sizeof(actual.x86_64->fxsave);\n       ++byte_offset) {\n    SCOPED_TRACE(base::StringPrintf(\"byte offset = %u\\n\", byte_offset));\n    EXPECT_EQ(\n        reinterpret_cast<const char*>(&actual.x86_64->fxsave)[byte_offset],\n        reinterpret_cast<const char*>(&expected.__fpregs_mem)[byte_offset]);\n  }\n}\n#elif defined(ARCH_CPU_ARMEL)\n// A native ucontext_t on ARM doesn't have enough regspace (yet) to hold all of\n// the different possible coprocessor contexts at once. However, the ABI allows\n// it and the native regspace may be expanded in the future. Append some extra\n// space so this is testable now.\nstruct NativeCPUContext {\n  ucontext_t ucontext;\n  char extra[1024];\n};\n\nstruct CrunchContext {\n  uint32_t mvdx[16][2];\n  uint32_t mvax[4][3];\n  uint32_t dspsc[2];\n};\n\nstruct IWMMXTContext {\n  uint32_t save[38];\n};\n\nstruct TestCoprocessorContext {\n  struct {\n    internal::CoprocessorContextHead head;\n    CrunchContext context;\n  } crunch;\n  struct {\n    internal::CoprocessorContextHead head;\n    IWMMXTContext context;\n  } iwmmxt;\n  struct {\n    internal::CoprocessorContextHead head;\n    IWMMXTContext context;\n  } dummy;\n  struct {\n    internal::CoprocessorContextHead head;\n    internal::SignalVFPContext context;\n  } vfp;\n  internal::CoprocessorContextHead terminator;\n};\n\nvoid InitializeContext(NativeCPUContext* context) {\n  memset(context, 'x', sizeof(*context));\n\n  for (int index = 0; index < (&context->ucontext.uc_mcontext.fault_address -\n                               &context->ucontext.uc_mcontext.arm_r0);\n       ++index) {\n    (&context->ucontext.uc_mcontext.arm_r0)[index] = index;\n  }\n\n  static_assert(\n      sizeof(TestCoprocessorContext) <=\n          sizeof(context->ucontext.uc_regspace) + sizeof(context->extra),\n      \"Insufficient context space\");\n  auto test_context =\n      reinterpret_cast<TestCoprocessorContext*>(context->ucontext.uc_regspace);\n\n  test_context->crunch.head.magic = CRUNCH_MAGIC;\n  test_context->crunch.head.size = sizeof(test_context->crunch);\n  memset(\n      &test_context->crunch.context, 'c', sizeof(test_context->crunch.context));\n\n  test_context->iwmmxt.head.magic = IWMMXT_MAGIC;\n  test_context->iwmmxt.head.size = sizeof(test_context->iwmmxt);\n  memset(\n      &test_context->iwmmxt.context, 'i', sizeof(test_context->iwmmxt.context));\n\n  test_context->dummy.head.magic = DUMMY_MAGIC;\n  test_context->dummy.head.size = sizeof(test_context->dummy);\n  memset(\n      &test_context->dummy.context, 'd', sizeof(test_context->dummy.context));\n\n  test_context->vfp.head.magic = VFP_MAGIC;\n  test_context->vfp.head.size = sizeof(test_context->vfp);\n  memset(&test_context->vfp.context, 'v', sizeof(test_context->vfp.context));\n  for (size_t reg = 0; reg < std::size(test_context->vfp.context.vfp.fpregs);\n       ++reg) {\n    test_context->vfp.context.vfp.fpregs[reg] = reg;\n  }\n  test_context->vfp.context.vfp.fpscr = 42;\n\n  test_context->terminator.magic = 0;\n  test_context->terminator.size = 0;\n}\n\nvoid ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {\n  EXPECT_EQ(actual.architecture, kCPUArchitectureARM);\n\n  EXPECT_EQ(memcmp(actual.arm->regs,\n                   &expected.ucontext.uc_mcontext.arm_r0,\n                   sizeof(actual.arm->regs)),\n            0);\n  EXPECT_EQ(actual.arm->fp, expected.ucontext.uc_mcontext.arm_fp);\n  EXPECT_EQ(actual.arm->ip, expected.ucontext.uc_mcontext.arm_ip);\n  EXPECT_EQ(actual.arm->sp, expected.ucontext.uc_mcontext.arm_sp);\n  EXPECT_EQ(actual.arm->lr, expected.ucontext.uc_mcontext.arm_lr);\n  EXPECT_EQ(actual.arm->pc, expected.ucontext.uc_mcontext.arm_pc);\n  EXPECT_EQ(actual.arm->cpsr, expected.ucontext.uc_mcontext.arm_cpsr);\n\n  EXPECT_FALSE(actual.arm->have_fpa_regs);\n\n  EXPECT_TRUE(actual.arm->have_vfp_regs);\n\n  auto test_context = reinterpret_cast<const TestCoprocessorContext*>(\n      expected.ucontext.uc_regspace);\n\n  EXPECT_EQ(memcmp(actual.arm->vfp_regs.vfp,\n                   &test_context->vfp.context.vfp,\n                   sizeof(actual.arm->vfp_regs.vfp)),\n            0);\n}\n#elif defined(ARCH_CPU_ARM64)\nusing NativeCPUContext = ucontext_t;\n\nstruct TestCoprocessorContext {\n  esr_context esr;\n  fpsimd_context fpsimd;\n  _aarch64_ctx terminator;\n};\n\nvoid InitializeContext(NativeCPUContext* context) {\n  memset(context, 'x', sizeof(*context));\n\n  for (size_t index = 0; index < std::size(context->uc_mcontext.regs);\n       ++index) {\n    context->uc_mcontext.regs[index] = index;\n  }\n  context->uc_mcontext.sp = 1;\n  context->uc_mcontext.pc = 2;\n  context->uc_mcontext.pstate = 3;\n\n  auto test_context = reinterpret_cast<TestCoprocessorContext*>(\n      context->uc_mcontext.__reserved);\n\n  test_context->esr.head.magic = ESR_MAGIC;\n  test_context->esr.head.size = sizeof(test_context->esr);\n  memset(&test_context->esr.esr, 'e', sizeof(test_context->esr.esr));\n\n  test_context->fpsimd.head.magic = FPSIMD_MAGIC;\n  test_context->fpsimd.head.size = sizeof(test_context->fpsimd);\n  test_context->fpsimd.fpsr = 1;\n  test_context->fpsimd.fpcr = 2;\n  for (size_t reg = 0; reg < std::size(test_context->fpsimd.vregs); ++reg) {\n    test_context->fpsimd.vregs[reg] = reg;\n  }\n\n  test_context->terminator.magic = 0;\n  test_context->terminator.size = 0;\n}\n\nvoid ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {\n  EXPECT_EQ(actual.architecture, kCPUArchitectureARM64);\n\n  EXPECT_EQ(memcmp(actual.arm64->regs,\n                   expected.uc_mcontext.regs,\n                   sizeof(actual.arm64->regs)),\n            0);\n  EXPECT_EQ(actual.arm64->sp, expected.uc_mcontext.sp);\n  EXPECT_EQ(actual.arm64->pc, expected.uc_mcontext.pc);\n  EXPECT_EQ(actual.arm64->spsr, expected.uc_mcontext.pstate);\n\n  auto test_context = reinterpret_cast<const TestCoprocessorContext*>(\n      expected.uc_mcontext.__reserved);\n\n  EXPECT_EQ(actual.arm64->fpsr, test_context->fpsimd.fpsr);\n  EXPECT_EQ(actual.arm64->fpcr, test_context->fpsimd.fpcr);\n  EXPECT_EQ(memcmp(actual.arm64->fpsimd,\n                   &test_context->fpsimd.vregs,\n                   sizeof(actual.arm64->fpsimd)),\n            0);\n}\n#elif defined(ARCH_CPU_MIPS_FAMILY)\nusing NativeCPUContext = ucontext_t;\n\nvoid InitializeContext(NativeCPUContext* context) {\n  for (size_t reg = 0; reg < std::size(context->uc_mcontext.gregs); ++reg) {\n    context->uc_mcontext.gregs[reg] = reg;\n  }\n  memset(&context->uc_mcontext.fpregs, 44, sizeof(context->uc_mcontext.fpregs));\n}\n\nvoid ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {\n#if defined(ARCH_CPU_MIPSEL)\n  EXPECT_EQ(actual.architecture, kCPUArchitectureMIPSEL);\n#define CPU_ARCH_NAME mipsel\n#elif defined(ARCH_CPU_MIPS64EL)\n  EXPECT_EQ(actual.architecture, kCPUArchitectureMIPS64EL);\n#define CPU_ARCH_NAME mips64\n#endif\n\n  for (size_t reg = 0; reg < std::size(expected.uc_mcontext.gregs); ++reg) {\n    EXPECT_EQ(actual.CPU_ARCH_NAME->regs[reg], expected.uc_mcontext.gregs[reg]);\n  }\n\n  EXPECT_EQ(memcmp(&actual.CPU_ARCH_NAME->fpregs,\n                   &expected.uc_mcontext.fpregs,\n                   sizeof(actual.CPU_ARCH_NAME->fpregs)),\n            0);\n#undef CPU_ARCH_NAME\n}\n\n#elif defined(ARCH_CPU_RISCV64)\nusing NativeCPUContext = ucontext_t;\n\nvoid InitializeContext(NativeCPUContext* context) {\n  for (size_t reg = 0; reg < std::size(context->uc_mcontext.__gregs); ++reg) {\n    context->uc_mcontext.__gregs[reg] = reg;\n  }\n\n  memset(&context->uc_mcontext.__fpregs,\n         44,\n         sizeof(context->uc_mcontext.__fpregs));\n}\n\nvoid ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {\n  EXPECT_EQ(actual.architecture, kCPUArchitectureRISCV64);\n\n  EXPECT_EQ(actual.riscv64->pc, expected.uc_mcontext.__gregs[0]);\n\n  for (size_t reg = 0; reg < std::size(actual.riscv64->regs); ++reg) {\n    EXPECT_EQ(actual.riscv64->regs[reg], expected.uc_mcontext.__gregs[reg + 1]);\n  }\n\n  EXPECT_EQ(memcmp(&actual.riscv64->fpregs,\n                   &expected.uc_mcontext.__fpregs,\n                   sizeof(actual.riscv64->fpregs)),\n            0);\n}\n\n#else\n#error Port.\n#endif\n\nTEST(ExceptionSnapshotLinux, SelfBasic) {\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  ProcessReaderLinux process_reader;\n  ASSERT_TRUE(process_reader.Initialize(&connection));\n\n  siginfo_t siginfo;\n  siginfo.si_signo = SIGSEGV;\n  siginfo.si_errno = 42;\n  siginfo.si_code = SEGV_MAPERR;\n  siginfo.si_addr = reinterpret_cast<void*>(0xdeadbeef);\n\n  NativeCPUContext context;\n  InitializeContext(&context);\n\n  internal::ExceptionSnapshotLinux exception;\n  ASSERT_TRUE(exception.Initialize(&process_reader,\n                                   FromPointerCast<LinuxVMAddress>(&siginfo),\n                                   FromPointerCast<LinuxVMAddress>(&context),\n                                   gettid(),\n                                   nullptr));\n  EXPECT_EQ(exception.Exception(), static_cast<uint32_t>(siginfo.si_signo));\n  EXPECT_EQ(exception.ExceptionInfo(), static_cast<uint32_t>(siginfo.si_code));\n  EXPECT_EQ(exception.ExceptionAddress(),\n            FromPointerCast<uint64_t>(siginfo.si_addr));\n  ExpectContext(*exception.Context(), context);\n}\n\nclass ScopedSigactionRestore {\n public:\n  ScopedSigactionRestore() : old_action_(), signo_(-1), valid_(false) {}\n\n  ScopedSigactionRestore(const ScopedSigactionRestore&) = delete;\n  ScopedSigactionRestore& operator=(const ScopedSigactionRestore&) = delete;\n\n  ~ScopedSigactionRestore() { Reset(); }\n\n  bool Reset() {\n    if (valid_) {\n      int res = sigaction(signo_, &old_action_, nullptr);\n      EXPECT_EQ(res, 0) << ErrnoMessage(\"sigaction\");\n      if (res != 0) {\n        return false;\n      }\n    }\n    valid_ = false;\n    signo_ = -1;\n    return true;\n  }\n\n  bool ResetInstallHandler(int signo, Signals::Handler handler) {\n    if (Reset() && Signals::InstallHandler(signo, handler, 0, &old_action_)) {\n      signo_ = signo;\n      valid_ = true;\n      return true;\n    }\n    return false;\n  }\n\n private:\n  struct sigaction old_action_;\n  int signo_;\n  bool valid_;\n};\n\nclass RaiseTest {\n public:\n  RaiseTest() = delete;\n  RaiseTest(const RaiseTest&) = delete;\n  RaiseTest& operator=(const RaiseTest&) = delete;\n\n  static void Run() {\n    test_complete_ = false;\n\n    ScopedSigactionRestore sigrestore;\n    ASSERT_TRUE(sigrestore.ResetInstallHandler(kSigno, HandleRaisedSignal));\n\n    EXPECT_EQ(raise(kSigno), 0) << ErrnoMessage(\"raise\");\n    EXPECT_TRUE(test_complete_);\n  }\n\n private:\n  static void HandleRaisedSignal(int signo, siginfo_t* siginfo, void* context) {\n    FakePtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(getpid()));\n\n    ProcessReaderLinux process_reader;\n    ASSERT_TRUE(process_reader.Initialize(&connection));\n\n    internal::ExceptionSnapshotLinux exception;\n    ASSERT_TRUE(exception.Initialize(&process_reader,\n                                     FromPointerCast<LinuxVMAddress>(siginfo),\n                                     FromPointerCast<LinuxVMAddress>(context),\n                                     gettid(),\n                                     nullptr));\n\n    EXPECT_EQ(exception.Exception(), static_cast<uint32_t>(kSigno));\n\n    EXPECT_EQ(exception.Codes().size(), 3u);\n    EXPECT_EQ(exception.Codes()[0], static_cast<uint64_t>(getpid()));\n    EXPECT_EQ(exception.Codes()[1], getuid());\n    // Codes()[2] is not set by kill, but we still expect to get it because some\n    // interfaces may set it and we don't necessarily know where this signal\n    // came\n    // from.\n\n    test_complete_ = true;\n  }\n\n  static constexpr uint32_t kSigno = SIGUSR1;\n  static bool test_complete_;\n};\nbool RaiseTest::test_complete_ = false;\n\nTEST(ExceptionSnapshotLinux, Raise) {\n  RaiseTest::Run();\n}\n\nclass TimerTest {\n public:\n  TimerTest() : event_(), timer_(-1), test_complete_(false) { test_ = this; }\n\n  TimerTest(const TimerTest&) = delete;\n  TimerTest& operator=(const TimerTest&) = delete;\n\n  ~TimerTest() { test_ = nullptr; }\n\n  void Run() {\n    ScopedSigactionRestore sigrestore;\n    ASSERT_TRUE(sigrestore.ResetInstallHandler(kSigno, HandleTimer));\n\n    event_.sigev_notify = SIGEV_SIGNAL;\n    event_.sigev_signo = kSigno;\n    event_.sigev_value.sival_int = 42;\n    ASSERT_EQ(syscall(SYS_timer_create, CLOCK_MONOTONIC, &event_, &timer_), 0);\n\n    itimerspec spec;\n    spec.it_interval.tv_sec = 0;\n    spec.it_interval.tv_nsec = 0;\n    spec.it_value.tv_sec = 0;\n    spec.it_value.tv_nsec = 1;\n    ASSERT_EQ(syscall(SYS_timer_settime, timer_, TIMER_ABSTIME, &spec, nullptr),\n              0);\n\n    for (size_t attempt = 0; attempt < 3; ++attempt) {\n      SleepNanoseconds(1);\n      if (test_complete_) {\n        return;\n      }\n    }\n    ADD_FAILURE() << \"signal not received\";\n  }\n\n private:\n  static void HandleTimer(int signo, siginfo_t* siginfo, void* context) {\n    FakePtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(getpid()));\n\n    ProcessReaderLinux process_reader;\n    ASSERT_TRUE(process_reader.Initialize(&connection));\n\n    internal::ExceptionSnapshotLinux exception;\n    ASSERT_TRUE(exception.Initialize(&process_reader,\n                                     FromPointerCast<LinuxVMAddress>(siginfo),\n                                     FromPointerCast<LinuxVMAddress>(context),\n                                     gettid(),\n                                     nullptr));\n\n    EXPECT_EQ(exception.Exception(), static_cast<uint32_t>(kSigno));\n\n    EXPECT_EQ(exception.Codes().size(), 3u);\n    EXPECT_EQ(exception.Codes()[0], static_cast<uint64_t>(test_->timer_));\n    int overruns = syscall(SYS_timer_getoverrun, test_->timer_);\n    ASSERT_GE(overruns, 0);\n    EXPECT_EQ(exception.Codes()[1], static_cast<uint64_t>(overruns));\n    EXPECT_EQ(exception.Codes()[2],\n              static_cast<uint64_t>(test_->event_.sigev_value.sival_int));\n\n    test_->test_complete_ = true;\n  }\n\n  sigevent event_;\n  __kernel_timer_t timer_;\n  volatile bool test_complete_;\n\n  static constexpr uint32_t kSigno = SIGALRM;\n  static TimerTest* test_;\n};\nTimerTest* TimerTest::test_;\n\nTEST(ExceptionSnapshotLinux, SelfTimer) {\n  TimerTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/process_reader_linux.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/process_reader_linux.h\"\n\n#include <elf.h>\n#include <errno.h>\n#include <sched.h>\n#include <string.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\n#include <algorithm>\n\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"snapshot/linux/debug_rendezvous.h\"\n#include \"util/linux/auxiliary_vector.h\"\n#include \"util/linux/proc_stat_reader.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\nbool ShouldMergeStackMappings(const MemoryMap::Mapping& stack_mapping,\n                              const MemoryMap::Mapping& adj_mapping) {\n  DCHECK(stack_mapping.readable);\n  return adj_mapping.readable && stack_mapping.device == adj_mapping.device &&\n         stack_mapping.inode == adj_mapping.inode &&\n         (stack_mapping.name == adj_mapping.name ||\n          stack_mapping.name.empty() || adj_mapping.name.empty());\n}\n\n}  // namespace\n\nProcessReaderLinux::Thread::Thread()\n    : thread_info(),\n      stack_region_address(0),\n      stack_region_size(0),\n      name(),\n      tid(-1),\n      static_priority(-1),\n      nice_value(-1) {}\n\nProcessReaderLinux::Thread::~Thread() {}\n\nbool ProcessReaderLinux::Thread::InitializePtrace(\n    PtraceConnection* connection) {\n  if (!connection->GetThreadInfo(tid, &thread_info)) {\n    return false;\n  }\n\n  // From man proc(5):\n  //\n  // /proc/[pid]/comm (since Linux 2.6.33)\n  //\n  // Different threads in the same process may have different comm values,\n  // accessible via /proc/[pid]/task/[tid]/comm.\n  const std::string path = base::StringPrintf(\n      \"/proc/%d/task/%d/comm\", connection->GetProcessID(), tid);\n  if (connection->ReadFileContents(base::FilePath(path), &name)) {\n    if (!name.empty() && name.back() == '\\n') {\n      // Remove the final newline character.\n      name.pop_back();\n    }\n  } else {\n    // Continue on without the thread name.\n  }\n\n  // TODO(jperaza): Collect scheduling priorities via the broker when they can't\n  // be collected directly.\n  have_priorities = false;\n\n  // TODO(jperaza): Starting with Linux 3.14, scheduling policy, static\n  // priority, and nice value can be collected all in one call with\n  // sched_getattr().\n  int res = sched_getscheduler(tid);\n  if (res < 0) {\n    PLOG(WARNING) << \"sched_getscheduler\";\n    return true;\n  }\n  sched_policy = res;\n\n  sched_param param;\n  if (sched_getparam(tid, &param) != 0) {\n    PLOG(WARNING) << \"sched_getparam\";\n    return true;\n  }\n  static_priority = param.sched_priority;\n\n  errno = 0;\n  res = getpriority(PRIO_PROCESS, tid);\n  if (res == -1 && errno) {\n    PLOG(WARNING) << \"getpriority\";\n    return true;\n  }\n  nice_value = res;\n\n  have_priorities = true;\n  return true;\n}\n\nvoid ProcessReaderLinux::Thread::InitializeStack(ProcessReaderLinux* reader) {\n  LinuxVMAddress stack_pointer;\n#if defined(ARCH_CPU_X86_FAMILY)\n  stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.rsp\n                                    : thread_info.thread_context.t32.esp;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n  stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.sp\n                                    : thread_info.thread_context.t32.sp;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.regs[29]\n                                    : thread_info.thread_context.t32.regs[29];\n#elif defined(ARCH_CPU_RISCV64)\n  stack_pointer = thread_info.thread_context.t64.regs[1];\n#else\n#error Port.\n#endif\n  InitializeStackFromSP(reader, stack_pointer);\n}\n\nvoid ProcessReaderLinux::Thread::InitializeStackFromSP(\n    ProcessReaderLinux* reader,\n    LinuxVMAddress stack_pointer) {\n  const MemoryMap* memory_map = reader->GetMemoryMap();\n\n  // If we can't find the mapping, it's probably a bad stack pointer\n  const MemoryMap::Mapping* mapping = memory_map->FindMapping(stack_pointer);\n  if (!mapping) {\n    LOG(WARNING) << \"no stack mapping\";\n    return;\n  }\n  LinuxVMAddress stack_region_start =\n      reader->Memory()->PointerToAddress(stack_pointer);\n\n  // We've hit what looks like a guard page; skip to the end and check for a\n  // mapped stack region.\n  if (!mapping->readable) {\n    stack_region_start = mapping->range.End();\n    mapping = memory_map->FindMapping(stack_region_start);\n    if (!mapping) {\n      LOG(WARNING) << \"no stack mapping\";\n      return;\n    }\n  } else {\n#if defined(ARCH_CPU_X86_FAMILY)\n    // Adjust start address to include the red zone\n    if (reader->Is64Bit()) {\n      constexpr LinuxVMSize kRedZoneSize = 128;\n      LinuxVMAddress red_zone_base =\n          stack_region_start - std::min(kRedZoneSize, stack_region_start);\n\n      // Only include the red zone if it is part of a valid mapping\n      if (red_zone_base >= mapping->range.Base()) {\n        stack_region_start = red_zone_base;\n      } else {\n        const MemoryMap::Mapping* rz_mapping =\n            memory_map->FindMapping(red_zone_base);\n        if (rz_mapping && ShouldMergeStackMappings(*mapping, *rz_mapping)) {\n          stack_region_start = red_zone_base;\n        } else {\n          stack_region_start = mapping->range.Base();\n        }\n      }\n    }\n#endif\n  }\n  stack_region_address = stack_region_start;\n\n  // If there are more mappings at the end of this one, they may be a\n  // continuation of the stack.\n  LinuxVMAddress stack_end = mapping->range.End();\n  const MemoryMap::Mapping* next_mapping;\n  while ((next_mapping = memory_map->FindMapping(stack_end)) &&\n         ShouldMergeStackMappings(*mapping, *next_mapping)) {\n    stack_end = next_mapping->range.End();\n  }\n\n  // The main thread should have an entry in the maps file just for its stack,\n  // so we'll assume the base of the stack is at the end of the region. Other\n  // threads' stacks may not have their own entries in the maps file if they\n  // were user-allocated within a larger mapping, but pthreads places the TLS\n  // at the high-address end of the stack so we can try using that to shrink\n  // the stack region.\n  stack_region_size = stack_end - stack_region_address;\n  VMAddress tls_address = reader->Memory()->PointerToAddress(\n      thread_info.thread_specific_data_address);\n  if (tid != reader->ProcessID() && tls_address > stack_region_address &&\n      tls_address < stack_end) {\n    stack_region_size = tls_address - stack_region_address;\n  }\n}\n\nProcessReaderLinux::Module::Module()\n    : name(), elf_reader(nullptr), type(ModuleSnapshot::kModuleTypeUnknown) {}\n\nProcessReaderLinux::Module::~Module() = default;\n\nProcessReaderLinux::ProcessReaderLinux()\n    : connection_(),\n      process_info_(),\n      memory_map_(),\n      threads_(),\n      modules_(),\n      elf_readers_(),\n      is_64_bit_(false),\n      initialized_threads_(false),\n      initialized_modules_(false),\n      initialized_() {}\n\nProcessReaderLinux::~ProcessReaderLinux() {}\n\nbool ProcessReaderLinux::Initialize(PtraceConnection* connection) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  DCHECK(connection);\n  connection_ = connection;\n\n  if (!process_info_.InitializeWithPtrace(connection_)) {\n    return false;\n  }\n\n  if (!memory_map_.Initialize(connection_)) {\n    return false;\n  }\n\n  is_64_bit_ = process_info_.Is64Bit();\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessReaderLinux::StartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_info_.StartTime(start_time);\n}\n\nbool ProcessReaderLinux::CPUTimes(timeval* user_time,\n                                  timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  timerclear(user_time);\n  timerclear(system_time);\n\n  timeval local_user_time;\n  timerclear(&local_user_time);\n  timeval local_system_time;\n  timerclear(&local_system_time);\n\n  for (const Thread& thread : threads_) {\n    ProcStatReader stat;\n    if (!stat.Initialize(connection_, thread.tid)) {\n      return false;\n    }\n\n    timeval thread_user_time;\n    if (!stat.UserCPUTime(&thread_user_time)) {\n      return false;\n    }\n\n    timeval thread_system_time;\n    if (!stat.SystemCPUTime(&thread_system_time)) {\n      return false;\n    }\n\n    timeradd(&local_user_time, &thread_user_time, &local_user_time);\n    timeradd(&local_system_time, &thread_system_time, &local_system_time);\n  }\n\n  *user_time = local_user_time;\n  *system_time = local_system_time;\n  return true;\n}\n\nconst std::vector<ProcessReaderLinux::Thread>& ProcessReaderLinux::Threads() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!initialized_threads_) {\n    InitializeThreads();\n  }\n  return threads_;\n}\n\nconst std::vector<ProcessReaderLinux::Module>& ProcessReaderLinux::Modules() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!initialized_modules_) {\n    InitializeModules();\n  }\n  return modules_;\n}\n\nvoid ProcessReaderLinux::InitializeAbortMessage() {\n#if BUILDFLAG(IS_ANDROID)\n  const MemoryMap::Mapping* mapping =\n      memory_map_.FindMappingWithName(\"[anon:abort message]\");\n  if (!mapping) {\n    return;\n  }\n\n  if (is_64_bit_) {\n    ReadAbortMessage<true>(mapping);\n  } else {\n    ReadAbortMessage<false>(mapping);\n  }\n#endif\n}\n\n#if BUILDFLAG(IS_ANDROID)\n\n// These structure definitions and the magic numbers below were copied from\n// bionic/libc/bionic/android_set_abort_message.cpp\n\ntemplate <bool is64Bit>\nstruct abort_msg_t {\n  uint32_t size;\n  char msg[0];\n};\n\ntemplate <>\nstruct abort_msg_t<true> {\n  uint64_t size;\n  char msg[0];\n};\n\ntemplate <bool is64Bit>\nstruct magic_abort_msg_t {\n  uint64_t magic1;\n  uint64_t magic2;\n  abort_msg_t<is64Bit> msg;\n};\n\ntemplate <bool is64Bit>\nvoid ProcessReaderLinux::ReadAbortMessage(const MemoryMap::Mapping* mapping) {\n  magic_abort_msg_t<is64Bit> header;\n  if (!Memory()->Read(\n          mapping->range.Base(), sizeof(magic_abort_msg_t<is64Bit>), &header)) {\n    return;\n  }\n\n  size_t size = header.msg.size - sizeof(magic_abort_msg_t<is64Bit>) - 1;\n  if (header.magic1 != 0xb18e40886ac388f0ULL ||\n      header.magic2 != 0xc6dfba755a1de0b5ULL ||\n      mapping->range.Size() <\n          offsetof(magic_abort_msg_t<is64Bit>, msg.msg) + size) {\n    return;\n  }\n\n  abort_message_.resize(size);\n  if (!Memory()->Read(\n          mapping->range.Base() + offsetof(magic_abort_msg_t<is64Bit>, msg.msg),\n          size,\n          &abort_message_[0])) {\n    abort_message_.clear();\n  }\n}\n\n#endif  // BUILDFLAG(IS_ANDROID)\n\nconst std::string& ProcessReaderLinux::AbortMessage() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (abort_message_.empty()) {\n    InitializeAbortMessage();\n  }\n  return abort_message_;\n}\n\nvoid ProcessReaderLinux::InitializeThreads() {\n  DCHECK(threads_.empty());\n  initialized_threads_ = true;\n\n  pid_t pid = ProcessID();\n  if (pid == getpid()) {\n    // TODO(jperaza): ptrace can't be used on threads in the same thread group.\n    // Using clone to create a new thread in it's own thread group doesn't work\n    // because glibc doesn't support threads it didn't create via pthreads.\n    // Fork a new process to snapshot us and copy the data back?\n    LOG(ERROR) << \"not implemented\";\n    return;\n  }\n\n  Thread main_thread;\n  main_thread.tid = pid;\n  if (main_thread.InitializePtrace(connection_)) {\n    main_thread.InitializeStack(this);\n    threads_.push_back(main_thread);\n  } else {\n    LOG(WARNING) << \"Couldn't initialize main thread.\";\n  }\n\n  bool main_thread_found = false;\n  std::vector<pid_t> thread_ids;\n  bool result = connection_->Threads(&thread_ids);\n  DCHECK(result);\n  for (pid_t tid : thread_ids) {\n    if (tid == pid) {\n      DCHECK(!main_thread_found);\n      main_thread_found = true;\n      continue;\n    }\n\n    Thread thread;\n    thread.tid = tid;\n    if (connection_->Attach(tid) && thread.InitializePtrace(connection_)) {\n      thread.InitializeStack(this);\n      threads_.push_back(thread);\n    }\n  }\n  DCHECK(main_thread_found);\n}\n\nvoid ProcessReaderLinux::InitializeModules() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  initialized_modules_ = true;\n\n  AuxiliaryVector aux;\n  if (!aux.Initialize(connection_)) {\n    return;\n  }\n\n  LinuxVMAddress phdrs;\n  if (!aux.GetValue(AT_PHDR, &phdrs)) {\n    return;\n  }\n\n  ProcessMemoryRange range;\n  if (!range.Initialize(Memory(), is_64_bit_)) {\n    return;\n  }\n\n  // The strategy used for identifying loaded modules depends on ELF files\n  // conventionally loading their header and program headers into memory.\n  // Locating the correct module could fail if the headers aren't mapped, are\n  // mapped at an unexpected location, or if there are other mappings\n  // constructed to look like the ELF module being searched for.\n  const MemoryMap::Mapping* exe_mapping = nullptr;\n  std::unique_ptr<ElfImageReader> exe_reader;\n  {\n    const MemoryMap::Mapping* phdr_mapping = memory_map_.FindMapping(phdrs);\n    if (!phdr_mapping) {\n      return;\n    }\n\n    auto possible_mappings =\n        memory_map_.FindFilePossibleMmapStarts(*phdr_mapping);\n    const MemoryMap::Mapping* mapping = nullptr;\n    while ((mapping = possible_mappings->Next())) {\n      auto parsed_exe = std::make_unique<ElfImageReader>();\n      if (parsed_exe->Initialize(\n              range,\n              mapping->range.Base(),\n              /* verbose= */ possible_mappings->Count() == 0) &&\n          parsed_exe->GetProgramHeaderTableAddress() == phdrs) {\n        exe_mapping = mapping;\n        exe_reader = std::move(parsed_exe);\n        break;\n      }\n    }\n    if (!exe_mapping) {\n      LOG(ERROR) << \"no exe mappings 0x\" << std::hex\n                 << phdr_mapping->range.Base();\n      return;\n    }\n  }\n\n  LinuxVMAddress debug_address;\n  if (!exe_reader->GetDebugAddress(&debug_address)) {\n    return;\n  }\n\n  DebugRendezvous debug;\n  if (!debug.Initialize(range, debug_address)) {\n    return;\n  }\n\n  Module exe = {};\n  exe.name = !debug.Executable()->name.empty() ? debug.Executable()->name\n                                               : exe_mapping->name;\n  exe.elf_reader = exe_reader.get();\n  exe.type = ModuleSnapshot::ModuleType::kModuleTypeExecutable;\n\n  modules_.push_back(exe);\n  elf_readers_.push_back(std::move(exe_reader));\n\n  LinuxVMAddress loader_base = 0;\n  aux.GetValue(AT_BASE, &loader_base);\n\n  for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) {\n    const MemoryMap::Mapping* module_mapping = nullptr;\n    std::unique_ptr<ElfImageReader> elf_reader;\n    {\n      const MemoryMap::Mapping* dyn_mapping =\n          memory_map_.FindMapping(entry.dynamic_array);\n      if (!dyn_mapping) {\n        continue;\n      }\n\n#if BUILDFLAG(IS_ANDROID)\n      // Beginning at API 21, Bionic provides android_dlopen_ext() which allows\n      // passing a file descriptor with an existing relro segment to the loader.\n      // This means that the mapping attributes of dyn_mapping may be unrelated\n      // to the attributes of the other mappings for the module. In this case,\n      // search all mappings in reverse order from dyn_mapping until a module is\n      // parsed whose dynamic address matches the value in the debug link.\n      static int api_level = android_get_device_api_level();\n      auto possible_mappings =\n          (api_level >= 21 || api_level < 0)\n              ? memory_map_.ReverseIteratorFrom(*dyn_mapping)\n              : memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);\n#else\n      auto possible_mappings =\n          memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);\n#endif\n      const MemoryMap::Mapping* mapping = nullptr;\n      while ((mapping = possible_mappings->Next())) {\n        auto parsed_module = std::make_unique<ElfImageReader>();\n        VMAddress dynamic_address;\n        if (parsed_module->Initialize(\n                range,\n                mapping->range.Base(),\n                /* verbose= */ possible_mappings->Count() == 0) &&\n            parsed_module->GetDynamicArrayAddress(&dynamic_address) &&\n            dynamic_address == entry.dynamic_array) {\n          module_mapping = mapping;\n          elf_reader = std::move(parsed_module);\n          break;\n        }\n      }\n      if (!module_mapping) {\n        LOG(ERROR) << \"no module mappings 0x\" << std::hex\n                   << dyn_mapping->range.Base();\n        continue;\n      }\n    }\n\n    Module module = {};\n    std::string soname;\n    if (elf_reader->SoName(&soname) && !soname.empty()) {\n      module.name = soname;\n    } else {\n      module.name = !entry.name.empty() ? entry.name : module_mapping->name;\n    }\n    module.elf_reader = elf_reader.get();\n    module.type = loader_base && elf_reader->Address() == loader_base\n                      ? ModuleSnapshot::kModuleTypeDynamicLoader\n                      : ModuleSnapshot::kModuleTypeSharedLibrary;\n    modules_.push_back(module);\n    elf_readers_.push_back(std::move(elf_reader));\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/process_reader_linux.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_\n#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_\n\n#include <sys/time.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"snapshot/elf/elf_image_reader.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/linux/address_types.h\"\n#include \"util/linux/memory_map.h\"\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/linux/thread_info.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/posix/process_info.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\n\n//! \\brief Accesses information about another process, identified by a process\n//!     ID.\nclass ProcessReaderLinux {\n public:\n  //! \\brief Contains information about a thread that belongs to a process.\n  struct Thread {\n    Thread();\n    ~Thread();\n\n    //! \\brief Initializes the thread's stack using \\a stack_pointer instead of\n    //!   the stack pointer in \\a thread_info.\n    //!\n    //! This method initializes \\a stack_region_address and \\a stack_region_size\n    //! overwriting any values they previously contained. This is useful, for\n    //! example, if the thread is currently in a signal handler context, which\n    //! may execute on a different stack than was used before the signal was\n    //! received.\n    //!\n    //! \\param[in] reader A process reader for the target process.\n    //! \\param[in] stack_pointer The stack pointer for the stack to initialize.\n    void InitializeStackFromSP(ProcessReaderLinux* reader,\n                               LinuxVMAddress stack_pointer);\n\n    ThreadInfo thread_info;\n    LinuxVMAddress stack_region_address;\n    LinuxVMSize stack_region_size;\n    std::string name;\n    pid_t tid;\n    int sched_policy;\n    int static_priority;\n    int nice_value;\n\n    //! \\brief `true` if `sched_policy`, `static_priority`, and `nice_value` are\n    //!     all valid.\n    bool have_priorities;\n\n   private:\n    friend class ProcessReaderLinux;\n\n    bool InitializePtrace(PtraceConnection* connection);\n    void InitializeStack(ProcessReaderLinux* reader);\n  };\n\n  //! \\brief Contains information about a module loaded into a process.\n  struct Module {\n    Module();\n    ~Module();\n\n    //! \\brief The pathname used to load the module from disk.\n    std::string name;\n\n    //! \\brief An image reader for the module.\n    //!\n    //! The lifetime of this ElfImageReader is scoped to the lifetime of the\n    //! ProcessReaderLinux that created it.\n    //!\n    //! This field may be `nullptr` if a reader could not be created for the\n    //! module.\n    ElfImageReader* elf_reader;\n\n    //! \\brief The module's type.\n    ModuleSnapshot::ModuleType type;\n  };\n\n  ProcessReaderLinux();\n\n  ProcessReaderLinux(const ProcessReaderLinux&) = delete;\n  ProcessReaderLinux& operator=(const ProcessReaderLinux&) = delete;\n\n  ~ProcessReaderLinux();\n\n  //! \\brief Initializes this object.\n  //!\n  //! This method must be successfully called before calling any other method in\n  //! this class and may only be called once.\n  //!\n  //! \\param[in] connection A PtraceConnection to the target process.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Initialize(PtraceConnection* connection);\n\n  //! \\brief Return `true` if the target task is a 64-bit process.\n  bool Is64Bit() const { return is_64_bit_; }\n\n  //! \\brief Return the target process' process ID.\n  pid_t ProcessID() const { return process_info_.ProcessID(); }\n\n  //! \\brief Return the target process' parent process ID.\n  pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }\n\n  //! \\brief Return a memory reader for the target process.\n  const ProcessMemoryLinux* Memory() const { return connection_->Memory(); }\n\n  //! \\brief Return a memory map of the target process.\n  MemoryMap* GetMemoryMap() { return &memory_map_; }\n\n  //! \\brief Determines the target process’ start time.\n  //!\n  //! \\param[out] start_time The time that the process started.\n  //! \\return `true` on success with \\a start_time set. Otherwise `false` with a\n  //!     message logged.\n  bool StartTime(timeval* start_time) const;\n\n  //! \\brief Determines the target process’ execution time.\n  //!\n  //! \\param[out] user_time The amount of time the process has executed code in\n  //!     user mode.\n  //! \\param[out] system_time The amount of time the process has executed code\n  //!     in system mode.\n  //!\n  //! \\return `true` on success, `false` on failure, with a warning logged. On\n  //!     failure, \\a user_time and \\a system_time will be set to represent no\n  //!     time spent executing code in user or system mode.\n  bool CPUTimes(timeval* user_time, timeval* system_time) const;\n\n  //! \\brief Return a vector of threads that are in the task process. If the\n  //!     main thread is able to be identified and traced, it will be placed at\n  //!     index `0`.\n  const std::vector<Thread>& Threads();\n\n  //! \\return The modules loaded in the process. The first element (at index\n  //!     `0`) corresponds to the main executable.\n  const std::vector<Module>& Modules();\n\n  //! \\return On Android, the abort message that was passed to\n  //!     android_set_abort_message(). This is only available on Q or later.\n  const std::string& AbortMessage();\n\n private:\n  void InitializeThreads();\n  void InitializeModules();\n  void InitializeAbortMessage();\n  template <bool Is64Bit>\n  void ReadAbortMessage(const MemoryMap::Mapping* mapping);\n\n  PtraceConnection* connection_;  // weak\n  ProcessInfo process_info_;\n  MemoryMap memory_map_;\n  std::vector<Thread> threads_;\n  std::vector<Module> modules_;\n  std::string abort_message_;\n  std::vector<std::unique_ptr<ElfImageReader>> elf_readers_;\n  bool is_64_bit_;\n  bool initialized_threads_;\n  bool initialized_modules_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_\n"
  },
  {
    "path": "snapshot/linux/process_reader_linux_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/process_reader_linux.h\"\n\n#include <dlfcn.h>\n#include <elf.h>\n#include <errno.h>\n#include <link.h>\n#include <pthread.h>\n#include <sched.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <sys/syscall.h>\n#include <unistd.h>\n\n#include <iterator>\n#include <map>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"base/format_macros.h\"\n#include \"base/memory/free_deleter.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/linux/test_modules.h\"\n#include \"test/errors.h\"\n#include \"test/linux/fake_ptrace_connection.h\"\n#include \"test/linux/get_tls.h\"\n#include \"test/multiprocess.h\"\n#include \"test/scoped_module_handle.h\"\n#include \"test/scoped_set_thread_name.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#include \"util/misc/address_sanitizer.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/memory_sanitizer.h\"\n#include \"util/posix/scoped_mmap.h\"\n#include \"util/synchronization/semaphore.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#include <android/set_abort_message.h>\n#include \"dlfcn_internal.h\"\n\n// Normally this comes from set_abort_message.h, but only at API level 21.\nextern \"C\" void android_set_abort_message(const char* msg)\n    __attribute__((weak));\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\npid_t gettid() {\n  return syscall(SYS_gettid);\n}\n\nTEST(ProcessReaderLinux, SelfBasic) {\n  FakePtraceConnection connection;\n  connection.Initialize(getpid());\n\n  ProcessReaderLinux process_reader;\n  ASSERT_TRUE(process_reader.Initialize(&connection));\n\n#if defined(ARCH_CPU_64_BITS)\n  EXPECT_TRUE(process_reader.Is64Bit());\n#else\n  EXPECT_FALSE(process_reader.Is64Bit());\n#endif\n\n  EXPECT_EQ(process_reader.ProcessID(), getpid());\n  EXPECT_EQ(process_reader.ParentProcessID(), getppid());\n\n  static constexpr char kTestMemory[] = \"Some test memory\";\n  char buffer[std::size(kTestMemory)];\n  ASSERT_TRUE(process_reader.Memory()->Read(\n      reinterpret_cast<LinuxVMAddress>(kTestMemory),\n      sizeof(kTestMemory),\n      &buffer));\n  EXPECT_STREQ(kTestMemory, buffer);\n\n  EXPECT_EQ(\"\", process_reader.AbortMessage());\n}\n\nconstexpr char kTestMemory[] = \"Read me from another process\";\n\nclass BasicChildTest : public Multiprocess {\n public:\n  BasicChildTest() : Multiprocess() {}\n\n  BasicChildTest(const BasicChildTest&) = delete;\n  BasicChildTest& operator=(const BasicChildTest&) = delete;\n\n  ~BasicChildTest() {}\n\n private:\n  void MultiprocessParent() override {\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(ChildPID()));\n\n    ProcessReaderLinux process_reader;\n    ASSERT_TRUE(process_reader.Initialize(&connection));\n\n#if !defined(ARCH_CPU_64_BITS)\n    EXPECT_FALSE(process_reader.Is64Bit());\n#else\n    EXPECT_TRUE(process_reader.Is64Bit());\n#endif\n\n    EXPECT_EQ(process_reader.ParentProcessID(), getpid());\n    EXPECT_EQ(process_reader.ProcessID(), ChildPID());\n\n    std::string read_string;\n    ASSERT_TRUE(process_reader.Memory()->ReadCString(\n        reinterpret_cast<LinuxVMAddress>(kTestMemory), &read_string));\n    EXPECT_EQ(read_string, kTestMemory);\n  }\n\n  void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); }\n};\n\nTEST(ProcessReaderLinux, ChildBasic) {\n  BasicChildTest test;\n  test.Run();\n}\n\nclass TestThreadPool {\n public:\n  struct ThreadExpectation {\n    LinuxVMAddress tls = 0;\n    LinuxVMAddress stack_address = 0;\n    LinuxVMSize max_stack_size = 0;\n    int sched_policy = 0;\n    int static_priority = 0;\n    int nice_value = 0;\n  };\n\n  TestThreadPool() : threads_() {}\n\n  TestThreadPool(const TestThreadPool&) = delete;\n  TestThreadPool& operator=(const TestThreadPool&) = delete;\n\n  ~TestThreadPool() {\n    for (const auto& thread : threads_) {\n      thread->exit_semaphore.Signal();\n    }\n\n    for (const auto& thread : threads_) {\n      EXPECT_EQ(pthread_join(thread->pthread, nullptr), 0)\n          << ErrnoMessage(\"pthread_join\");\n    }\n  }\n\n  void StartThreads(size_t thread_count, size_t stack_size = 0) {\n    for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) {\n      const std::string thread_name =\n          base::StringPrintf(\"ThreadPool-%zu\", thread_index);\n      threads_.push_back(std::make_unique<Thread>(thread_name));\n      Thread* thread = threads_.back().get();\n\n      pthread_attr_t attr;\n      ASSERT_EQ(pthread_attr_init(&attr), 0)\n          << ErrnoMessage(\"pthread_attr_init\");\n\n      if (stack_size > 0) {\n        const size_t page_size = getpagesize();\n        DCHECK_EQ(stack_size % page_size, 0u);\n        size_t stack_alloc_size = 2 * page_size + stack_size;\n\n        ASSERT_TRUE(thread->stack.ResetMmap(nullptr,\n                                            stack_alloc_size,\n                                            PROT_NONE,\n                                            MAP_PRIVATE | MAP_ANONYMOUS,\n                                            -1,\n                                            0));\n        char* stack_ptr = thread->stack.addr_as<char*>() + page_size;\n        ASSERT_EQ(mprotect(stack_ptr, stack_size, PROT_READ | PROT_WRITE), 0)\n            << \"mprotect\";\n\n        ASSERT_EQ(pthread_attr_setstack(&attr, stack_ptr, stack_size), 0)\n            << ErrnoMessage(\"pthread_attr_setstack\");\n        thread->expectation.max_stack_size = stack_size;\n      }\n\n      ASSERT_EQ(pthread_attr_setschedpolicy(&attr, SCHED_OTHER), 0)\n          << ErrnoMessage(\"pthread_attr_setschedpolicy\");\n      thread->expectation.sched_policy = SCHED_OTHER;\n\n      sched_param param;\n      param.sched_priority = 0;\n      ASSERT_EQ(pthread_attr_setschedparam(&attr, &param), 0)\n          << ErrnoMessage(\"pthread_attr_setschedparam\");\n      thread->expectation.static_priority = 0;\n\n      thread->expectation.nice_value = thread_index % 20;\n\n      ASSERT_EQ(pthread_create(&thread->pthread, &attr, ThreadMain, thread), 0)\n          << ErrnoMessage(\"pthread_create\");\n    }\n\n    for (const auto& thread : threads_) {\n      thread->ready_semaphore.Wait();\n    }\n  }\n\n  pid_t GetThreadExpectation(size_t thread_index,\n                             ThreadExpectation* expectation,\n                             std::string* thread_name_expectation) {\n    CHECK_LT(thread_index, threads_.size());\n\n    const Thread* thread = threads_[thread_index].get();\n    *expectation = thread->expectation;\n    *thread_name_expectation = thread->name;\n    return thread->tid;\n  }\n\n private:\n  struct Thread {\n    explicit Thread(const std::string& name)\n        : pthread(),\n          expectation(),\n          ready_semaphore(0),\n          exit_semaphore(0),\n          tid(-1),\n          name(name) {\n    }\n    ~Thread() {}\n\n    pthread_t pthread;\n    ThreadExpectation expectation;\n    ScopedMmap stack;\n    Semaphore ready_semaphore;\n    Semaphore exit_semaphore;\n    pid_t tid;\n    const std::string name;\n  };\n\n  static void* ThreadMain(void* argument) {\n    Thread* thread = static_cast<Thread*>(argument);\n    const ScopedSetThreadName scoped_set_thread_name(thread->name);\n\n    CHECK_EQ(setpriority(PRIO_PROCESS, 0, thread->expectation.nice_value), 0)\n        << ErrnoMessage(\"setpriority\");\n\n    thread->expectation.tls = GetTLS();\n    thread->expectation.stack_address =\n        reinterpret_cast<LinuxVMAddress>(&thread);\n    thread->tid = gettid();\n\n    thread->ready_semaphore.Signal();\n    thread->exit_semaphore.Wait();\n\n    CHECK_EQ(pthread_self(), thread->pthread);\n\n    return nullptr;\n  }\n\n  std::vector<std::unique_ptr<Thread>> threads_;\n};\n\nusing ThreadMap = std::map<pid_t, TestThreadPool::ThreadExpectation>;\nusing ThreadNameMap = std::map<pid_t, std::string>;\n\nvoid ExpectThreads(const ThreadMap& thread_map,\n                   const ThreadNameMap& thread_name_map,\n                   const std::vector<ProcessReaderLinux::Thread>& threads,\n                   PtraceConnection* connection) {\n  ASSERT_EQ(threads.size(), thread_map.size());\n  ASSERT_EQ(threads.size(), thread_name_map.size());\n\n  MemoryMap memory_map;\n  ASSERT_TRUE(memory_map.Initialize(connection));\n\n  for (const auto& thread : threads) {\n    SCOPED_TRACE(\n        base::StringPrintf(\"Thread id %d, name %s, tls 0x%\" PRIx64\n                           \", stack addr 0x%\" PRIx64 \", stack size 0x%\" PRIx64,\n                           thread.tid,\n                           thread.name.c_str(),\n                           thread.thread_info.thread_specific_data_address,\n                           thread.stack_region_address,\n                           thread.stack_region_size));\n\n    const auto& iterator = thread_map.find(thread.tid);\n    ASSERT_NE(iterator, thread_map.end());\n\n    EXPECT_EQ(thread.thread_info.thread_specific_data_address,\n              iterator->second.tls);\n\n    ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address));\n    ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address +\n                                       thread.stack_region_size - 1));\n\n#if !defined(ADDRESS_SANITIZER)\n    // AddressSanitizer causes stack variables to be stored separately from the\n    // call stack.\n    EXPECT_LE(\n        thread.stack_region_address,\n        connection->Memory()->PointerToAddress(iterator->second.stack_address));\n    EXPECT_GE(\n        thread.stack_region_address + thread.stack_region_size,\n        connection->Memory()->PointerToAddress(iterator->second.stack_address));\n#endif  // !defined(ADDRESS_SANITIZER)\n\n    if (iterator->second.max_stack_size) {\n      EXPECT_LT(thread.stack_region_size, iterator->second.max_stack_size);\n    }\n\n    EXPECT_EQ(thread.sched_policy, iterator->second.sched_policy);\n    EXPECT_EQ(thread.static_priority, iterator->second.static_priority);\n    EXPECT_EQ(thread.nice_value, iterator->second.nice_value);\n\n    const auto& thread_name_iterator = thread_name_map.find(thread.tid);\n    ASSERT_NE(thread_name_iterator, thread_name_map.end());\n    EXPECT_EQ(thread.name, thread_name_iterator->second);\n  }\n}\n\nclass ChildThreadTest : public Multiprocess {\n public:\n  ChildThreadTest(size_t stack_size = 0)\n      : Multiprocess(), stack_size_(stack_size) {}\n\n  ChildThreadTest(const ChildThreadTest&) = delete;\n  ChildThreadTest& operator=(const ChildThreadTest&) = delete;\n\n  ~ChildThreadTest() {}\n\n private:\n  void MultiprocessParent() override {\n    ThreadMap thread_map;\n    ThreadNameMap thread_name_map;\n    for (size_t thread_index = 0; thread_index < kThreadCount + 1;\n         ++thread_index) {\n      pid_t tid;\n      TestThreadPool::ThreadExpectation expectation;\n\n      CheckedReadFileExactly(ReadPipeHandle(), &tid, sizeof(tid));\n      CheckedReadFileExactly(\n          ReadPipeHandle(), &expectation, sizeof(expectation));\n      thread_map[tid] = expectation;\n\n      std::string::size_type thread_name_length;\n      CheckedReadFileExactly(\n          ReadPipeHandle(), &thread_name_length, sizeof(thread_name_length));\n      std::string thread_name(thread_name_length, '\\0');\n      CheckedReadFileExactly(\n          ReadPipeHandle(), thread_name.data(), thread_name_length);\n      thread_name_map[tid] = thread_name;\n    }\n\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(ChildPID()));\n\n    ProcessReaderLinux process_reader;\n    ASSERT_TRUE(process_reader.Initialize(&connection));\n    const std::vector<ProcessReaderLinux::Thread>& threads =\n        process_reader.Threads();\n    ExpectThreads(thread_map, thread_name_map, threads, &connection);\n  }\n\n  void MultiprocessChild() override {\n    TestThreadPool thread_pool;\n    thread_pool.StartThreads(kThreadCount, stack_size_);\n\n    const std::string current_thread_name = \"MultiprocChild\";\n    const ScopedSetThreadName scoped_set_thread_name(current_thread_name);\n\n    TestThreadPool::ThreadExpectation expectation;\n#if defined(MEMORY_SANITIZER)\n    // memset() + re-initialization is required to zero padding bytes for MSan.\n    memset(&expectation, 0, sizeof(expectation));\n#endif  // defined(MEMORY_SANITIZER)\n    expectation = {0};\n    expectation.tls = GetTLS();\n    expectation.stack_address = reinterpret_cast<LinuxVMAddress>(&thread_pool);\n\n    int res = sched_getscheduler(0);\n    ASSERT_GE(res, 0) << ErrnoMessage(\"sched_getscheduler\");\n    expectation.sched_policy = res;\n\n    sched_param param;\n    ASSERT_EQ(sched_getparam(0, &param), 0) << ErrnoMessage(\"sched_getparam\");\n    expectation.static_priority = param.sched_priority;\n\n    errno = 0;\n    res = getpriority(PRIO_PROCESS, 0);\n    ASSERT_FALSE(res == -1 && errno) << ErrnoMessage(\"getpriority\");\n    expectation.nice_value = res;\n\n    pid_t tid = gettid();\n\n    CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid));\n    CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation));\n    const std::string::size_type current_thread_name_length =\n        current_thread_name.length();\n    CheckedWriteFile(WritePipeHandle(),\n                     &current_thread_name_length,\n                     sizeof(current_thread_name_length));\n    CheckedWriteFile(WritePipeHandle(),\n                     current_thread_name.data(),\n                     current_thread_name_length);\n\n    for (size_t thread_index = 0; thread_index < kThreadCount; ++thread_index) {\n      std::string thread_name_expectation;\n      tid = thread_pool.GetThreadExpectation(\n          thread_index, &expectation, &thread_name_expectation);\n      CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid));\n      CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation));\n      const std::string::size_type thread_name_length =\n          thread_name_expectation.length();\n      CheckedWriteFile(\n          WritePipeHandle(), &thread_name_length, sizeof(thread_name_length));\n      CheckedWriteFile(WritePipeHandle(),\n                       thread_name_expectation.data(),\n                       thread_name_length);\n    }\n\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  static constexpr size_t kThreadCount = 3;\n  const size_t stack_size_;\n};\n\nTEST(ProcessReaderLinux, ChildWithThreads) {\n  ChildThreadTest test;\n  test.Run();\n}\n\nTEST(ProcessReaderLinux, ChildThreadsWithSmallUserStacks) {\n  ChildThreadTest test(PTHREAD_STACK_MIN);\n  test.Run();\n}\n\n// Tests a thread with a stack that spans multiple mappings.\nclass ChildWithSplitStackTest : public Multiprocess {\n public:\n  ChildWithSplitStackTest() : Multiprocess(), page_size_(getpagesize()) {}\n\n  ChildWithSplitStackTest(const ChildWithSplitStackTest&) = delete;\n  ChildWithSplitStackTest& operator=(const ChildWithSplitStackTest&) = delete;\n\n  ~ChildWithSplitStackTest() {}\n\n private:\n  void MultiprocessParent() override {\n    LinuxVMAddress stack_addr1;\n    LinuxVMAddress stack_addr2;\n    LinuxVMAddress stack_addr3;\n\n    CheckedReadFileExactly(ReadPipeHandle(), &stack_addr1, sizeof(stack_addr1));\n    CheckedReadFileExactly(ReadPipeHandle(), &stack_addr2, sizeof(stack_addr2));\n    CheckedReadFileExactly(ReadPipeHandle(), &stack_addr3, sizeof(stack_addr3));\n\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(ChildPID()));\n\n    ProcessReaderLinux process_reader;\n    ASSERT_TRUE(process_reader.Initialize(&connection));\n\n    const std::vector<ProcessReaderLinux::Thread>& threads =\n        process_reader.Threads();\n    ASSERT_EQ(threads.size(), 1u);\n\n    LinuxVMAddress thread_stack_start = threads[0].stack_region_address;\n    EXPECT_LE(thread_stack_start, stack_addr1);\n    EXPECT_LE(thread_stack_start, stack_addr2);\n    EXPECT_LE(thread_stack_start, stack_addr3);\n\n    LinuxVMAddress thread_stack_end =\n        thread_stack_start + threads[0].stack_region_size;\n    EXPECT_GE(thread_stack_end, stack_addr1);\n    EXPECT_GE(thread_stack_end, stack_addr2);\n    EXPECT_GE(thread_stack_end, stack_addr3);\n  }\n\n  void MultiprocessChild() override {\n    const LinuxVMSize stack_size = page_size_ * 4;\n    GrowStack(stack_size, reinterpret_cast<LinuxVMAddress>(&stack_size));\n  }\n\n  void GrowStack(LinuxVMSize stack_size, LinuxVMAddress bottom_of_stack) {\n    char stack_contents[4096];\n    auto stack_address = reinterpret_cast<LinuxVMAddress>(&stack_contents);\n\n    if (bottom_of_stack - stack_address < stack_size) {\n      GrowStack(stack_size, bottom_of_stack);\n    } else {\n      // Write-protect a page on our stack to split up the mapping\n      LinuxVMAddress page_addr =\n          stack_address - (stack_address % page_size_) + 2 * page_size_;\n      ASSERT_EQ(\n          mprotect(reinterpret_cast<void*>(page_addr), page_size_, PROT_READ),\n          0)\n          << ErrnoMessage(\"mprotect\");\n\n      CheckedWriteFile(\n          WritePipeHandle(), &bottom_of_stack, sizeof(bottom_of_stack));\n      CheckedWriteFile(WritePipeHandle(), &page_addr, sizeof(page_addr));\n      CheckedWriteFile(\n          WritePipeHandle(), &stack_address, sizeof(stack_address));\n\n      // Wait for parent to read us\n      CheckedReadFileAtEOF(ReadPipeHandle());\n\n      ASSERT_EQ(mprotect(reinterpret_cast<void*>(page_addr),\n                         page_size_,\n                         PROT_READ | PROT_WRITE),\n                0)\n          << ErrnoMessage(\"mprotect\");\n    }\n  }\n\n  const size_t page_size_;\n};\n\n// AddressSanitizer with use-after-return detection causes stack variables to\n// be allocated on the heap.\n#if defined(ADDRESS_SANITIZER)\n#define MAYBE_ChildWithSplitStack DISABLED_ChildWithSplitStack\n#else\n#define MAYBE_ChildWithSplitStack ChildWithSplitStack\n#endif\nTEST(ProcessReaderLinux, MAYBE_ChildWithSplitStack) {\n  ChildWithSplitStackTest test;\n  test.Run();\n}\n\n// Android doesn't provide dl_iterate_phdr on ARM until API 21.\n#if !BUILDFLAG(IS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21\nint ExpectFindModule(dl_phdr_info* info, size_t size, void* data) {\n  SCOPED_TRACE(\n      base::StringPrintf(\"module %s at 0x%\" PRIx64 \" phdrs 0x%\" PRIx64,\n                         info->dlpi_name,\n                         LinuxVMAddress{info->dlpi_addr},\n                         FromPointerCast<LinuxVMAddress>(info->dlpi_phdr)));\n  auto modules =\n      reinterpret_cast<const std::vector<ProcessReaderLinux::Module>*>(data);\n\n#if BUILDFLAG(IS_ANDROID)\n  // Prior to API 27, Bionic includes a null entry for /system/bin/linker.\n  if (!info->dlpi_name) {\n    EXPECT_EQ(info->dlpi_addr, 0u);\n    EXPECT_EQ(info->dlpi_phnum, 0u);\n    EXPECT_EQ(info->dlpi_phdr, nullptr);\n    return 0;\n  }\n#endif\n\n  // Bionic doesn't always set both of these addresses for the vdso and\n  // /system/bin/linker, but it does always set one of them.\n  VMAddress module_addr = info->dlpi_phdr\n                              ? FromPointerCast<LinuxVMAddress>(info->dlpi_phdr)\n                              : info->dlpi_addr;\n\n  // TODO(jperaza): This can use a range map when one is available.\n  bool found = false;\n  for (const auto& module : *modules) {\n    if (module.elf_reader && module_addr >= module.elf_reader->Address() &&\n        module_addr <\n            module.elf_reader->Address() + module.elf_reader->Size()) {\n      found = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(found);\n  return 0;\n}\n#endif  // !BUILDFLAG(IS_ANDROID) || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21\n\nvoid ExpectModulesFromSelf(\n    const std::vector<ProcessReaderLinux::Module>& modules) {\n  for (const auto& module : modules) {\n    EXPECT_FALSE(module.name.empty());\n    EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown);\n  }\n\n// Android doesn't provide dl_iterate_phdr on ARM until API 21.\n#if !BUILDFLAG(IS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21\n  EXPECT_EQ(\n      dl_iterate_phdr(\n          ExpectFindModule,\n          reinterpret_cast<void*>(\n              const_cast<std::vector<ProcessReaderLinux::Module>*>(&modules))),\n      0);\n#endif  // !BUILDFLAG(IS_ANDROID) || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21\n}\n\n#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER)\nvoid ExpectTestModule(ProcessReaderLinux* reader,\n                      const std::string& module_name) {\n  for (const auto& module : reader->Modules()) {\n    if (module.name.find(module_name) != std::string::npos) {\n      ASSERT_TRUE(module.elf_reader);\n\n      VMAddress dynamic_addr;\n      ASSERT_TRUE(module.elf_reader->GetDynamicArrayAddress(&dynamic_addr));\n\n      auto dynamic_mapping = reader->GetMemoryMap()->FindMapping(dynamic_addr);\n      auto mappings =\n          reader->GetMemoryMap()->FindFilePossibleMmapStarts(*dynamic_mapping);\n      EXPECT_EQ(mappings->Count(), 2u);\n      return;\n    }\n  }\n  ADD_FAILURE() << \"Test module not found\";\n}\n#endif  // !ADDRESS_SANITIZER && !MEMORY_SANITIZER\n\nTEST(ProcessReaderLinux, SelfModules) {\n#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER)\n  const std::string module_name = \"test_module.so\";\n  const std::string module_soname = \"test_module_soname\";\n  ScopedModuleHandle empty_test_module(\n      LoadTestModule(module_name, module_soname));\n  ASSERT_TRUE(empty_test_module.valid());\n#endif  // !ADDRESS_SANITIZER && !MEMORY_SANITIZER\n\n  FakePtraceConnection connection;\n  connection.Initialize(getpid());\n\n  ProcessReaderLinux process_reader;\n  ASSERT_TRUE(process_reader.Initialize(&connection));\n\n  ExpectModulesFromSelf(process_reader.Modules());\n#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER)\n  ExpectTestModule(&process_reader, module_soname);\n#endif  // !ADDRESS_SANITIZER && !MEMORY_SANITIZER\n}\n\nclass ChildModuleTest : public Multiprocess {\n public:\n  ChildModuleTest() : Multiprocess(), module_soname_(\"test_module_soname\") {}\n\n  ChildModuleTest(const ChildModuleTest&) = delete;\n  ChildModuleTest& operator=(const ChildModuleTest&) = delete;\n\n  ~ChildModuleTest() = default;\n\n private:\n  void MultiprocessParent() override {\n    char c;\n    ASSERT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));\n\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(ChildPID()));\n\n    ProcessReaderLinux process_reader;\n    ASSERT_TRUE(process_reader.Initialize(&connection));\n\n    ExpectModulesFromSelf(process_reader.Modules());\n#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER)\n    ExpectTestModule(&process_reader, module_soname_);\n#endif  // !ADDRESS_SANITIZER && !MEMORY_SANITIZER\n  }\n\n  void MultiprocessChild() override {\n#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER)\n    ScopedModuleHandle empty_test_module(\n        LoadTestModule(\"test_module.so\", module_soname_));\n    ASSERT_TRUE(empty_test_module.valid());\n#endif  // !ADDRESS_SANITIZER && !MEMORY_SANITIZER\n\n    char c = 0;\n    ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &c, sizeof(c)));\n\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  const std::string module_soname_;\n};\n\nTEST(ProcessReaderLinux, ChildModules) {\n  ChildModuleTest test;\n  test.Run();\n}\n\n#if BUILDFLAG(IS_ANDROID)\nconst char kTestAbortMessage[] = \"test abort message\";\n\nTEST(ProcessReaderLinux, AbortMessage) {\n  // This test requires Q. The API level on Q devices will be 28 until the API\n  // is finalized, so we can't check API level yet. For now, test for the\n  // presence of a libc symbol which was introduced in Q.\n  if (!crashpad::internal::Dlsym(RTLD_DEFAULT,\n                                 \"android_fdsan_close_with_tag\")) {\n    GTEST_SKIP();\n  }\n\n  android_set_abort_message(kTestAbortMessage);\n\n  FakePtraceConnection connection;\n  connection.Initialize(getpid());\n\n  ProcessReaderLinux process_reader;\n  ASSERT_TRUE(process_reader.Initialize(&connection));\n\n  EXPECT_EQ(kTestAbortMessage, process_reader.AbortMessage());\n}\n#endif\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/process_snapshot_linux.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/process_snapshot_linux.h\"\n\n#include <utility>\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"util/linux/exception_information.h\"\n\nnamespace crashpad {\n\nProcessSnapshotLinux::ProcessSnapshotLinux() = default;\n\nProcessSnapshotLinux::~ProcessSnapshotLinux() = default;\n\nbool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (gettimeofday(&snapshot_time_, nullptr) != 0) {\n    PLOG(ERROR) << \"gettimeofday\";\n    return false;\n  }\n\n  if (!process_reader_.Initialize(connection) ||\n      !memory_range_.Initialize(process_reader_.Memory(),\n                                process_reader_.Is64Bit())) {\n    return false;\n  }\n\n  client_id_.InitializeToZero();\n  system_.Initialize(&process_reader_, &snapshot_time_);\n\n  InitializeModules();\n  GetCrashpadOptionsInternal((&options_));\n  InitializeThreads();\n  InitializeAnnotations();\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\npid_t ProcessSnapshotLinux::FindThreadWithStackAddress(\n    VMAddress stack_address) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  for (const auto& thread : process_reader_.Threads()) {\n    if (stack_address >= thread.stack_region_address &&\n        stack_address <\n            thread.stack_region_address + thread.stack_region_size) {\n      return thread.tid;\n    }\n  }\n  return -1;\n}\n\nbool ProcessSnapshotLinux::InitializeException(\n    LinuxVMAddress exception_info_address,\n    pid_t exception_thread_id) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  DCHECK(!exception_);\n\n  ExceptionInformation info;\n  if (!process_reader_.Memory()->Read(\n          exception_info_address, sizeof(info), &info)) {\n    LOG(ERROR) << \"Couldn't read exception info\";\n    return false;\n  }\n\n  if (exception_thread_id >= 0) {\n    info.thread_id = exception_thread_id;\n  }\n\n  uint32_t* budget_remaining_pointer =\n      options_.gather_indirectly_referenced_memory == TriState::kEnabled\n          ? &options_.indirectly_referenced_memory_cap\n          : nullptr;\n\n  exception_.reset(new internal::ExceptionSnapshotLinux());\n  if (!exception_->Initialize(&process_reader_,\n                              info.siginfo_address,\n                              info.context_address,\n                              info.thread_id,\n                              budget_remaining_pointer)) {\n    exception_.reset();\n    return false;\n  }\n\n  // The thread's existing snapshot will have captured the stack for the signal\n  // handler. Replace it with a thread snapshot which captures the stack for the\n  // exception context.\n  for (const auto& reader_thread : process_reader_.Threads()) {\n    if (reader_thread.tid == info.thread_id) {\n      ProcessReaderLinux::Thread thread = reader_thread;\n      thread.InitializeStackFromSP(&process_reader_,\n                                   exception_->Context()->StackPointer());\n\n      auto exc_thread_snapshot =\n          std::make_unique<internal::ThreadSnapshotLinux>();\n      if (!exc_thread_snapshot->Initialize(&process_reader_, thread, nullptr)) {\n        return false;\n      }\n\n      for (auto& thread_snapshot : threads_) {\n        if (thread_snapshot->ThreadID() ==\n            static_cast<uint64_t>(info.thread_id)) {\n          thread_snapshot.reset(exc_thread_snapshot.release());\n          return true;\n        }\n      }\n\n      LOG(ERROR) << \"thread not found \" << info.thread_id;\n      return false;\n    }\n  }\n\n  LOG(ERROR) << \"thread not found \" << info.thread_id;\n  return false;\n}\n\nvoid ProcessSnapshotLinux::GetCrashpadOptions(\n    CrashpadInfoClientOptions* options) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *options = options_;\n}\n\nvoid ProcessSnapshotLinux::GetCrashpadOptionsInternal(\n    CrashpadInfoClientOptions* options) {\n  CrashpadInfoClientOptions local_options;\n\n  for (const auto& module : modules_) {\n    CrashpadInfoClientOptions module_options;\n    if (!module->GetCrashpadOptions(&module_options)) {\n      continue;\n    }\n\n    if (local_options.crashpad_handler_behavior == TriState::kUnset) {\n      local_options.crashpad_handler_behavior =\n          module_options.crashpad_handler_behavior;\n    }\n    if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {\n      local_options.system_crash_reporter_forwarding =\n          module_options.system_crash_reporter_forwarding;\n    }\n    if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {\n      local_options.gather_indirectly_referenced_memory =\n          module_options.gather_indirectly_referenced_memory;\n      local_options.indirectly_referenced_memory_cap =\n          module_options.indirectly_referenced_memory_cap;\n    }\n\n    // If non-default values have been found for all options, the loop can end\n    // early.\n    if (local_options.crashpad_handler_behavior != TriState::kUnset &&\n        local_options.system_crash_reporter_forwarding != TriState::kUnset &&\n        local_options.gather_indirectly_referenced_memory != TriState::kUnset) {\n      break;\n    }\n  }\n\n  *options = local_options;\n}\n\ncrashpad::ProcessID ProcessSnapshotLinux::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.ProcessID();\n}\n\ncrashpad::ProcessID ProcessSnapshotLinux::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.ParentProcessID();\n}\n\nvoid ProcessSnapshotLinux::SnapshotTime(timeval* snapshot_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *snapshot_time = snapshot_time_;\n}\n\nvoid ProcessSnapshotLinux::ProcessStartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  process_reader_.StartTime(start_time);\n}\n\nvoid ProcessSnapshotLinux::ProcessCPUTimes(timeval* user_time,\n                                           timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  process_reader_.CPUTimes(user_time, system_time);\n}\n\nvoid ProcessSnapshotLinux::ReportID(UUID* report_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *report_id = report_id_;\n}\n\nvoid ProcessSnapshotLinux::ClientID(UUID* client_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *client_id = client_id_;\n}\n\nconst std::map<std::string, std::string>&\nProcessSnapshotLinux::AnnotationsSimpleMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_simple_map_;\n}\n\nconst SystemSnapshot* ProcessSnapshotLinux::System() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &system_;\n}\n\nstd::vector<const ThreadSnapshot*> ProcessSnapshotLinux::Threads() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ThreadSnapshot*> threads;\n  for (const auto& thread : threads_) {\n    threads.push_back(thread.get());\n  }\n  return threads;\n}\n\nstd::vector<const ModuleSnapshot*> ProcessSnapshotLinux::Modules() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ModuleSnapshot*> modules;\n  for (const auto& module : modules_) {\n    modules.push_back(module.get());\n  }\n  return modules;\n}\n\nstd::vector<UnloadedModuleSnapshot> ProcessSnapshotLinux::UnloadedModules()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(jperaza): Can this be implemented on Linux?\n  return std::vector<UnloadedModuleSnapshot>();\n}\n\nconst ExceptionSnapshot* ProcessSnapshotLinux::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_.get();\n}\n\nstd::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotLinux::MemoryMap()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(jperaza): do this.\n  return std::vector<const MemoryMapRegionSnapshot*>();\n}\n\nstd::vector<HandleSnapshot> ProcessSnapshotLinux::Handles() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<HandleSnapshot>();\n}\n\nstd::vector<const MemorySnapshot*> ProcessSnapshotLinux::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<const MemorySnapshot*>();\n}\n\nconst ProcessMemory* ProcessSnapshotLinux::Memory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.Memory();\n}\n\nvoid ProcessSnapshotLinux::InitializeThreads() {\n  const std::vector<ProcessReaderLinux::Thread>& process_reader_threads =\n      process_reader_.Threads();\n  uint32_t* budget_remaining_pointer =\n      options_.gather_indirectly_referenced_memory == TriState::kEnabled\n          ? &options_.indirectly_referenced_memory_cap\n          : nullptr;\n\n  for (const ProcessReaderLinux::Thread& process_reader_thread :\n       process_reader_threads) {\n    auto thread = std::make_unique<internal::ThreadSnapshotLinux>();\n    if (thread->Initialize(&process_reader_,\n                           process_reader_thread,\n                           budget_remaining_pointer)) {\n      threads_.push_back(std::move(thread));\n    }\n  }\n}\n\nvoid ProcessSnapshotLinux::InitializeModules() {\n  for (const ProcessReaderLinux::Module& reader_module :\n       process_reader_.Modules()) {\n    auto module =\n        std::make_unique<internal::ModuleSnapshotElf>(reader_module.name,\n                                                      reader_module.elf_reader,\n                                                      reader_module.type,\n                                                      &memory_range_,\n                                                      process_reader_.Memory());\n    if (module->Initialize()) {\n      modules_.push_back(std::move(module));\n    }\n  }\n}\n\nvoid ProcessSnapshotLinux::InitializeAnnotations() {\n#if BUILDFLAG(IS_ANDROID)\n  const std::string& abort_message = process_reader_.AbortMessage();\n  if (!abort_message.empty()) {\n    annotations_simple_map_[\"abort_message\"] = abort_message;\n  }\n#endif\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/process_snapshot_linux.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_\n#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_\n\n#include <sys/time.h>\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/elf/module_snapshot_elf.h\"\n#include \"snapshot/linux/exception_snapshot_linux.h\"\n#include \"snapshot/linux/process_reader_linux.h\"\n#include \"snapshot/linux/system_snapshot_linux.h\"\n#include \"snapshot/linux/thread_snapshot_linux.h\"\n#include \"snapshot/memory_map_region_snapshot.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/process/process_id.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n//! \\brief A ProcessSnapshot of a running (or crashed) process running on a\n//!     Linux system.\nclass ProcessSnapshotLinux final : public ProcessSnapshot {\n public:\n  ProcessSnapshotLinux();\n\n  ProcessSnapshotLinux(const ProcessSnapshotLinux&) = delete;\n  ProcessSnapshotLinux& operator=(const ProcessSnapshotLinux&) = delete;\n\n  ~ProcessSnapshotLinux() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] connection A connection to the process to snapshot.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(PtraceConnection* connection);\n\n  //! \\brief Finds the thread whose stack contains \\a stack_address.\n  //!\n  //! \\param[in] stack_address A stack address to search for.\n  //! \\return The thread ID of the thread whose stack contains \\a stack_address\n  //!     or -1 if no matching thread is found.\n  pid_t FindThreadWithStackAddress(VMAddress stack_address);\n\n  //! \\brief Initializes the object's exception.\n  //!\n  //! \\param[in] exception_info The address of an ExceptionInformation in the\n  //!     target process' address space.\n  //! \\param[in] exception_thread_id The thread ID to assocaite the thread with.\n  //!     Optional. If -1, the exception thread will be identified by the\n  //!     ExceptionInformation struct which contains the thread ID in the target\n  //!     process' namespace.\n  bool InitializeException(LinuxVMAddress exception_info,\n                           pid_t exception_thread_id = -1);\n\n  //! \\brief Sets the value to be returned by ReportID().\n  //!\n  //! The crash report ID is under the control of the snapshot\n  //! producer, which may call this method to set the report ID. If this is not\n  //! done, ReportID() will return an identifier consisting entirely of zeroes.\n  void SetReportID(const UUID& report_id) { report_id_ = report_id; }\n\n  //! \\brief Sets the value to be returned by ClientID().\n  //!\n  //! The client ID is under the control of the snapshot producer,\n  //! which may call this method to set the client ID. If this is not done,\n  //! ClientID() will return an identifier consisting entirely of zeroes.\n  void SetClientID(const UUID& client_id) { client_id_ = client_id; }\n\n  //! \\brief Add an annotation to be returned by AnnotationsSimpleMap().\n  //!\n  //! Most process annotations are under the control of the snapshot\n  //! producer, which may call this method to establish these annotations.\n  //! On Android Q or later, the process snapshot may add an \"abort_message\"\n  //! annotation, which will contain the abort message passed to the\n  //! android_set_abort_message() function. Contrast this with module\n  //! annotations, which are under the control of the process being snapshotted.\n  void AddAnnotation(const std::string& key, const std::string& value) {\n    annotations_simple_map_[key] = value;\n  }\n\n  //! \\brief Returns options from CrashpadInfo structures found in modules in\n  //!     the process.\n  //!\n  //! \\param[out] options Options set in CrashpadInfo structures in modules in\n  //!     the process.\n  void GetCrashpadOptions(CrashpadInfoClientOptions* options);\n\n  // ProcessSnapshot:\n\n  crashpad::ProcessID ProcessID() const override;\n  crashpad::ProcessID ParentProcessID() const override;\n  void SnapshotTime(timeval* snapshot_time) const override;\n  void ProcessStartTime(timeval* start_time) const override;\n  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;\n  void ReportID(UUID* report_id) const override;\n  void ClientID(UUID* client_id) const override;\n  const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const override;\n  const SystemSnapshot* System() const override;\n  std::vector<const ThreadSnapshot*> Threads() const override;\n  std::vector<const ModuleSnapshot*> Modules() const override;\n  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;\n  const ExceptionSnapshot* Exception() const override;\n  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;\n  std::vector<HandleSnapshot> Handles() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n  const ProcessMemory* Memory() const override;\n\n private:\n  void InitializeThreads();\n  void InitializeModules();\n  void InitializeAnnotations();\n\n  // Initializes options_ on behalf of Initialize().\n  void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options);\n\n  std::map<std::string, std::string> annotations_simple_map_;\n  timeval snapshot_time_;\n  UUID report_id_;\n  UUID client_id_;\n  std::vector<std::unique_ptr<internal::ThreadSnapshotLinux>> threads_;\n  std::vector<std::unique_ptr<internal::ModuleSnapshotElf>> modules_;\n  std::unique_ptr<internal::ExceptionSnapshotLinux> exception_;\n  internal::SystemSnapshotLinux system_;\n  ProcessReaderLinux process_reader_;\n  ProcessMemoryRange memory_range_;\n  CrashpadInfoClientOptions options_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_\n"
  },
  {
    "path": "snapshot/linux/signal_context.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_\n#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_\n\n#include <signal.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <sys/ucontext.h>\n\n#include <type_traits>\n\n#include \"build/build_config.h\"\n#include \"util/linux/thread_info.h\"\n#include \"util/linux/traits.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n#pragma pack(push, 1)\n\ntemplate <class Traits>\nunion Sigval {\n  int32_t sigval;\n  typename Traits::Address pointer;\n};\n\ntemplate <class Traits>\nstruct Siginfo {\n  int32_t signo;\n#ifdef ARCH_CPU_MIPS_FAMILY\n  // Attribute order for signo_t defined in kernel is different for MIPS.\n  int32_t code;\n  int32_t err;\n#else\n  int32_t err;\n  int32_t code;\n#endif\n  typename Traits::UInteger32_64Only padding;\n\n  union {\n    // SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGTRAP\n    struct {\n      typename Traits::Address address;\n    };\n\n    // SIGPOLL\n    struct {\n      typename Traits::Long band;\n      int32_t fd;\n    };\n\n    // SIGSYS\n    struct {\n      typename Traits::Address call_address;\n      int32_t syscall;\n      uint32_t arch;\n    };\n\n    // Everything else\n    struct {\n      union {\n        struct {\n          pid_t pid;\n          uid_t uid;\n        };\n        struct {\n          int32_t timerid;\n          int32_t overrun;\n        };\n      };\n\n      union {\n        Sigval<Traits> sigval;\n\n        // SIGCHLD\n        struct {\n          int32_t status;\n          typename Traits::Clock utime;\n          typename Traits::Clock stime;\n        };\n      };\n    };\n  };\n};\n\ntemplate <typename Traits>\nstruct SignalStack {\n  typename Traits::Address stack_pointer;\n  uint32_t flags;\n  typename Traits::UInteger32_64Only padding;\n  typename Traits::Size size;\n};\n\ntemplate <typename Traits, typename Enable = void>\nstruct Sigset {};\n\ntemplate <typename Traits>\nstruct Sigset<\n    Traits,\n    typename std::enable_if<std::is_base_of<Traits32, Traits>::value>::type> {\n  uint64_t val;\n};\n\ntemplate <typename Traits>\nstruct Sigset<\n    Traits,\n    typename std::enable_if<std::is_base_of<Traits64, Traits>::value>::type> {\n#if BUILDFLAG(IS_ANDROID)\n  uint64_t val;\n#else\n  typename Traits::ULong val[16];\n#endif  // BUILDFLAG(IS_ANDROID)\n};\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\nstruct SignalThreadContext32 {\n  uint32_t xgs;\n  uint32_t xfs;\n  uint32_t xes;\n  uint32_t xds;\n  uint32_t edi;\n  uint32_t esi;\n  uint32_t ebp;\n  uint32_t esp;\n  uint32_t ebx;\n  uint32_t edx;\n  uint32_t ecx;\n  uint32_t eax;\n  uint32_t trapno;\n  uint32_t err;\n  uint32_t eip;\n  uint32_t xcs;\n  uint32_t eflags;\n  uint32_t uesp;\n  uint32_t xss;\n};\n\nstruct SignalThreadContext64 {\n  uint64_t r8;\n  uint64_t r9;\n  uint64_t r10;\n  uint64_t r11;\n  uint64_t r12;\n  uint64_t r13;\n  uint64_t r14;\n  uint64_t r15;\n  uint64_t rdi;\n  uint64_t rsi;\n  uint64_t rbp;\n  uint64_t rbx;\n  uint64_t rdx;\n  uint64_t rax;\n  uint64_t rcx;\n  uint64_t rsp;\n  uint64_t rip;\n  uint64_t eflags;\n  uint16_t cs;\n  uint16_t gs;\n  uint16_t fs;\n  uint16_t padding;\n  uint64_t err;\n  uint64_t trapno;\n  uint64_t oldmask;\n  uint64_t cr2;\n};\n\nstruct SignalFloatContext32 {\n  CPUContextX86::Fsave fsave;\n  uint16_t status;\n  uint16_t magic;\n  CPUContextX86::Fxsave fxsave[0];\n};\n\nusing SignalFloatContext64 = CPUContextX86_64::Fxsave;\n\nstruct ContextTraits32 : public Traits32 {\n  using ThreadContext = SignalThreadContext32;\n  using FloatContext = SignalFloatContext32;\n};\n\nstruct ContextTraits64 : public Traits64 {\n  using ThreadContext = SignalThreadContext64;\n  using FloatContext = SignalFloatContext64;\n};\n\ntemplate <typename Traits>\nstruct MContext {\n  typename Traits::ThreadContext gprs;\n  typename Traits::Address fpptr;\n  typename Traits::ULong_32Only oldmask;\n  typename Traits::ULong_32Only cr2;\n  typename Traits::ULong_64Only reserved[8];\n};\n\ntemplate <typename Traits>\nstruct UContext {\n  typename Traits::ULong flags;\n  typename Traits::Address link;\n  SignalStack<Traits> stack;\n  MContext<Traits> mcontext;\n  Sigset<Traits> sigmask;\n  char fpregs_mem[0];\n};\n\n#elif defined(ARCH_CPU_ARM_FAMILY)\n\nstruct CoprocessorContextHead {\n  uint32_t magic;\n  uint32_t size;\n};\n\nstruct SignalFPSIMDContext {\n  uint32_t fpsr;\n  uint32_t fpcr;\n  uint128_struct vregs[32];\n};\n\nstruct SignalVFPContext {\n  FloatContext::f32_t::vfp_t vfp;\n  struct vfp_exc {\n    uint32_t fpexc;\n    uint32_t fpinst;\n    uint32_t fpinst2;\n  } vfp_exc;\n  uint32_t padding;\n};\n\nstruct SignalThreadContext32 {\n  uint32_t regs[11];\n  uint32_t fp;\n  uint32_t ip;\n  uint32_t sp;\n  uint32_t lr;\n  uint32_t pc;\n  uint32_t cpsr;\n};\n\nusing SignalThreadContext64 = ThreadContext::t64_t;\n\nstruct MContext32Data {\n  uint32_t trap_no;\n  uint32_t error_code;\n  uint32_t oldmask;\n  SignalThreadContext32 gprs;\n  uint32_t fault_address;\n};\n\nstruct MContext64Data {\n  uint64_t fault_address;\n  SignalThreadContext64 gprs;\n};\n\nstruct ContextTraits32 : public Traits32 {\n  using MContext32 = MContext32Data;\n  using MContext64 = Nothing;\n};\n\nstruct ContextTraits64 : public Traits64 {\n  using MContext32 = Nothing;\n  using MContext64 = MContext64Data;\n};\n\ntemplate <typename Traits>\nstruct UContext {\n  typename Traits::ULong flags;\n  typename Traits::Address link;\n  SignalStack<Traits> stack;\n  typename Traits::MContext32 mcontext32;\n  Sigset<Traits> sigmask;\n  char padding[128 - sizeof(sigmask)];\n  typename Traits::Char_64Only padding2[8];\n  typename Traits::MContext64 mcontext64;\n  typename Traits::Char_64Only padding3[8];\n  char reserved[0];\n};\n\n#if defined(ARCH_CPU_ARMEL)\nstatic_assert(offsetof(UContext<ContextTraits32>, mcontext32) ==\n                  offsetof(ucontext_t, uc_mcontext),\n              \"context offset mismatch\");\nstatic_assert(offsetof(UContext<ContextTraits32>, reserved) ==\n                  offsetof(ucontext_t, uc_regspace),\n              \"regspace offset mismatch\");\n\n#elif defined(ARCH_CPU_ARM64)\nstatic_assert(offsetof(UContext<ContextTraits64>, mcontext64) ==\n                  offsetof(ucontext_t, uc_mcontext),\n              \"context offset mismtach\");\nstatic_assert(offsetof(UContext<ContextTraits64>, reserved) ==\n                  offsetof(ucontext_t, uc_mcontext) +\n                      offsetof(mcontext_t, __reserved),\n              \"reserved space offset mismtach\");\n#endif\n\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n\nstruct MContext32 {\n  uint32_t regmask;\n  uint32_t status;\n  uint64_t pc;\n  uint64_t gregs[32];\n  struct {\n    float _fp_fregs;\n    unsigned int _fp_pad;\n  } fpregs[32];\n  uint32_t fp_owned;\n  uint32_t fpc_csr;\n  uint32_t fpc_eir;\n  uint32_t used_math;\n  uint32_t dsp;\n  uint64_t mdhi;\n  uint64_t mdlo;\n  uint32_t hi1;\n  uint32_t lo1;\n  uint32_t hi2;\n  uint32_t lo2;\n  uint32_t hi3;\n  uint32_t lo3;\n};\n\nstruct MContext64 {\n  uint64_t gregs[32];\n  double fpregs[32];\n  uint64_t mdhi;\n  uint64_t hi1;\n  uint64_t hi2;\n  uint64_t hi3;\n  uint64_t mdlo;\n  uint64_t lo1;\n  uint64_t lo2;\n  uint64_t lo3;\n  uint64_t pc;\n  uint32_t fpc_csr;\n  uint32_t used_math;\n  uint32_t dsp;\n  uint32_t __glibc_reserved1;\n};\n\nstruct SignalThreadContext32 {\n  uint64_t regs[32];\n  uint32_t lo;\n  uint32_t hi;\n  uint32_t cp0_epc;\n  uint32_t cp0_badvaddr;\n  uint32_t cp0_status;\n  uint32_t cp0_cause;\n\n  SignalThreadContext32() {}\n  explicit SignalThreadContext32(\n      const struct ThreadContext::t32_t& thread_context) {\n    for (size_t reg = 0; reg < 32; ++reg) {\n      regs[reg] = thread_context.regs[reg];\n    }\n    lo = thread_context.lo;\n    hi = thread_context.hi;\n    cp0_epc = thread_context.cp0_epc;\n    cp0_badvaddr = thread_context.cp0_badvaddr;\n    cp0_status = thread_context.cp0_status;\n    cp0_cause = thread_context.cp0_cause;\n  }\n};\n\nstruct ContextTraits32 : public Traits32 {\n  using MContext = MContext32;\n  using SignalThreadContext = SignalThreadContext32;\n  using SignalFloatContext = FloatContext::f32_t;\n  using CPUContext = CPUContextMIPS;\n};\n\nstruct ContextTraits64 : public Traits64 {\n  using MContext = MContext64;\n  using SignalThreadContext = ThreadContext::t64_t;\n  using SignalFloatContext = FloatContext::f64_t;\n  using CPUContext = CPUContextMIPS64;\n};\n\ntemplate <typename Traits>\nstruct UContext {\n  typename Traits::ULong flags;\n  typename Traits::Address link;\n  SignalStack<Traits> stack;\n  typename Traits::ULong_32Only alignment_padding_;\n  typename Traits::MContext mcontext;\n  Sigset<Traits> sigmask;\n};\n\n#if defined(ARCH_CPU_MIPSEL)\nstatic_assert(offsetof(UContext<ContextTraits32>, mcontext) ==\n                  offsetof(ucontext_t, uc_mcontext),\n              \"context offset mismatch\");\nstatic_assert(offsetof(UContext<ContextTraits32>, mcontext.gregs) ==\n                  offsetof(ucontext_t, uc_mcontext.gregs),\n              \"context offset mismatch\");\nstatic_assert(offsetof(UContext<ContextTraits32>, mcontext.fpregs) ==\n                  offsetof(ucontext_t, uc_mcontext.fpregs),\n              \"context offset mismatch\");\n\n#elif defined(ARCH_CPU_MIPS64EL)\nstatic_assert(offsetof(UContext<ContextTraits64>, mcontext) ==\n                  offsetof(ucontext_t, uc_mcontext),\n              \"context offset mismtach\");\nstatic_assert(offsetof(UContext<ContextTraits64>, mcontext.gregs) ==\n                  offsetof(ucontext_t, uc_mcontext.gregs),\n              \"context offset mismatch\");\nstatic_assert(offsetof(UContext<ContextTraits64>, mcontext.fpregs) ==\n                  offsetof(ucontext_t, uc_mcontext.fpregs),\n              \"context offset mismatch\");\n#endif\n\n#elif defined(ARCH_CPU_RISCV64)\n\nstruct ContextTraits64 : public Traits64 {\n  using SignalThreadContext = ThreadContext::t64_t;\n  using SignalFloatContext = FloatContext::f64_t;\n  using CPUContext = CPUContextRISCV64;\n};\n\nstruct MContext64 {\n  ThreadContext::t64_t regs;\n  FloatContext::f64_t fpregs;\n};\n\ntemplate <typename Traits>\nstruct UContext {\n  typename Traits::ULong flags;\n  typename Traits::Address link;\n  SignalStack<Traits> stack;\n  Sigset<Traits> sigmask;\n  char alignment_padding_[8];\n  char padding[128 - sizeof(Sigset<Traits>)];\n  MContext64 mcontext;\n};\n\nstatic_assert(offsetof(UContext<ContextTraits64>, mcontext) ==\n                  offsetof(ucontext_t, uc_mcontext),\n              \"context offset mismatch\");\nstatic_assert(offsetof(UContext<ContextTraits64>, mcontext.regs) ==\n                  offsetof(ucontext_t, uc_mcontext.__gregs),\n              \"context offset mismatch\");\nstatic_assert(offsetof(UContext<ContextTraits64>, mcontext.fpregs) ==\n                  offsetof(ucontext_t, uc_mcontext.__fpregs),\n              \"context offset mismatch\");\n\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY\n\n#pragma pack(pop)\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_\n"
  },
  {
    "path": "snapshot/linux/system_snapshot_linux.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/system_snapshot_linux.h\"\n\n#include <stddef.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n\n#include <algorithm>\n#include <string_view>\n\n#include \"base/check_op.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/posix/timezone.h\"\n#include \"util/file/file_io.h\"\n#include \"util/numeric/in_range_cast.h\"\n#include \"util/string/split_string.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <sys/system_properties.h>\n#endif\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\nbool ReadCPUsOnline(uint32_t* first_cpu, uint8_t* cpu_count) {\n  std::string contents;\n  if (!LoggingReadEntireFile(base::FilePath(\"/sys/devices/system/cpu/online\"),\n                             &contents)) {\n    return false;\n  }\n  if (contents.back() != '\\n') {\n    LOG(ERROR) << \"format error\";\n    return false;\n  }\n  contents.pop_back();\n\n  unsigned int count = 0;\n  unsigned int first = 0;\n  bool have_first = false;\n  std::vector<std::string> ranges = SplitString(contents, ',');\n  for (const auto& range : ranges) {\n    std::string left, right;\n    if (SplitStringFirst(range, '-', &left, &right)) {\n      unsigned int start, end;\n      if (!base::StringToUint(left, &start) ||\n          !base::StringToUint(right, &end) || end <= start) {\n        LOG(ERROR) << \"format error: \" << range;\n        return false;\n      }\n      if (end <= start) {\n        LOG(ERROR) << \"format error\";\n        return false;\n      }\n      count += end - start + 1;\n      if (!have_first) {\n        first = start;\n        have_first = true;\n      }\n    } else {\n      unsigned int cpuno;\n      if (!base::StringToUint(range, &cpuno)) {\n        LOG(ERROR) << \"format error\";\n        return false;\n      }\n      if (!have_first) {\n        first = cpuno;\n        have_first = true;\n      }\n      ++count;\n    }\n  }\n  if (!have_first) {\n    LOG(ERROR) << \"no cpus online\";\n    return false;\n  }\n  *cpu_count = InRangeCast<uint8_t>(count, std::numeric_limits<uint8_t>::max());\n  *first_cpu = first;\n  return true;\n}\n\nbool ReadFreqFile(const std::string& filename, uint64_t* hz) {\n  std::string contents;\n  if (!LoggingReadEntireFile(base::FilePath(filename), &contents)) {\n    return false;\n  }\n  if (contents.back() != '\\n') {\n    LOG(ERROR) << \"format error\";\n    return false;\n  }\n  contents.pop_back();\n\n  uint64_t khz;\n  if (!base::StringToUint64(contents, &khz)) {\n    LOG(ERROR) << \"format error\";\n    return false;\n  }\n\n  *hz = khz * 1000;\n  return true;\n}\n\n#if BUILDFLAG(IS_ANDROID)\nstruct ReadPropertyData {\n  std::string* value;\n  bool read = false;\n};\n\nvoid ReadPropertyCallback(void* cookie,\n                          const char* name,\n                          const char* value,\n                          uint32_t serial) {\n  auto* data = static_cast<ReadPropertyData*>(cookie);\n  data->value->assign(value);\n  data->read = true;\n}\n\nbool ReadProperty(const char* property, std::string* value) {\n  const prop_info* prop = __system_property_find(property);\n  if (!prop) {\n    LOG(ERROR) << \"Couldn't read property \" << property;\n    return false;\n  }\n\n  ReadPropertyData data;\n  data.value = value;\n  __system_property_read_callback(prop, ReadPropertyCallback, &data);\n\n  if (!data.read) {\n    LOG(ERROR) << \"Couldn't read property \" << property;\n    return false;\n  }\n  return true;\n}\n#endif  // BUILDFLAG(IS_ANDROID)\n\n}  // namespace\n\nSystemSnapshotLinux::SystemSnapshotLinux()\n    : SystemSnapshot(),\n      os_version_full_(),\n      os_version_build_(),\n      process_reader_(nullptr),\n      snapshot_time_(nullptr),\n#if defined(ARCH_CPU_X86_FAMILY)\n      cpuid_(),\n#endif  // ARCH_CPU_X86_FAMILY\n      os_version_major_(-1),\n      os_version_minor_(-1),\n      os_version_bugfix_(-1),\n      target_cpu_(0),\n      cpu_count_(0),\n      initialized_() {\n}\n\nSystemSnapshotLinux::~SystemSnapshotLinux() {}\n\nvoid SystemSnapshotLinux::Initialize(ProcessReaderLinux* process_reader,\n                                     const timeval* snapshot_time) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  process_reader_ = process_reader;\n  snapshot_time_ = snapshot_time;\n\n#if BUILDFLAG(IS_ANDROID)\n  std::string build_string;\n  if (ReadProperty(\"ro.build.fingerprint\", &build_string)) {\n    os_version_build_ = build_string;\n    os_version_full_ = build_string;\n  }\n#endif  // BUILDFLAG(IS_ANDROID)\n\n  utsname uts;\n  if (uname(&uts) != 0) {\n    PLOG(WARNING) << \"uname\";\n  } else {\n    if (!os_version_full_.empty()) {\n      os_version_full_.push_back(' ');\n    }\n    os_version_full_ += base::StringPrintf(\n        \"%s %s %s %s\", uts.sysname, uts.release, uts.version, uts.machine);\n  }\n  ReadKernelVersion(uts.release);\n\n  if (!os_version_build_.empty()) {\n    os_version_build_.push_back(' ');\n  }\n  os_version_build_ += uts.version;\n  os_version_build_.push_back(' ');\n  os_version_build_ += uts.machine;\n\n  if (!ReadCPUsOnline(&target_cpu_, &cpu_count_)) {\n    target_cpu_ = 0;\n    cpu_count_ = 0;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n}\n\nCPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return process_reader_->Is64Bit() ? kCPUArchitectureX86_64\n                                    : kCPUArchitectureX86;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n  return process_reader_->Is64Bit() ? kCPUArchitectureARM64\n                                    : kCPUArchitectureARM;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  return process_reader_->Is64Bit() ? kCPUArchitectureMIPS64EL\n                                    : kCPUArchitectureMIPSEL;\n#elif defined(ARCH_CPU_RISCV64)\n  return kCPUArchitectureRISCV64;\n#else\n#error port to your architecture\n#endif\n}\n\nuint32_t SystemSnapshotLinux::CPURevision() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return cpuid_.Revision();\n#elif defined(ARCH_CPU_ARM_FAMILY)\n  // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30\n  return 0;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  // Not implementable on MIPS\n  return 0;\n#elif defined(ARCH_CPU_RISCV64)\n  // Not implemented\n  return 0;\n#else\n#error port to your architecture\n#endif\n}\n\nuint8_t SystemSnapshotLinux::CPUCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return cpu_count_;\n}\n\nstd::string SystemSnapshotLinux::CPUVendor() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return cpuid_.Vendor();\n#elif defined(ARCH_CPU_ARM_FAMILY)\n  // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30\n  return std::string();\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  // Not implementable on MIPS\n  return std::string();\n#elif defined(ARCH_CPU_RISCV64)\n  // Not implemented\n  return std::string();\n#else\n#error port to your architecture\n#endif\n}\n\nvoid SystemSnapshotLinux::CPUFrequency(uint64_t* current_hz,\n                                       uint64_t* max_hz) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *current_hz = 0;\n  *max_hz = 0;\n\n  ReadFreqFile(base::StringPrintf(\n                   \"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq\",\n                   target_cpu_),\n               current_hz);\n\n  ReadFreqFile(base::StringPrintf(\n                   \"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq\",\n                   target_cpu_),\n               max_hz);\n}\n\nuint32_t SystemSnapshotLinux::CPUX86Signature() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return cpuid_.Signature();\n#else\n  NOTREACHED();\n#endif\n}\n\nuint64_t SystemSnapshotLinux::CPUX86Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return cpuid_.Features();\n#else\n  NOTREACHED();\n#endif\n}\n\nuint64_t SystemSnapshotLinux::CPUX86ExtendedFeatures() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return cpuid_.ExtendedFeatures();\n#else\n  NOTREACHED();\n#endif\n}\n\nuint32_t SystemSnapshotLinux::CPUX86Leaf7Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return cpuid_.Leaf7Features();\n#else\n  NOTREACHED();\n#endif\n}\n\nbool SystemSnapshotLinux::CPUX86SupportsDAZ() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return cpuid_.SupportsDAZ();\n#else\n  NOTREACHED();\n#endif  // ARCH_CPU_X86_FMAILY\n}\n\nSystemSnapshot::OperatingSystem SystemSnapshotLinux::GetOperatingSystem()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if BUILDFLAG(IS_ANDROID)\n  return kOperatingSystemAndroid;\n#else\n  return kOperatingSystemLinux;\n#endif  // BUILDFLAG(IS_ANDROID)\n}\n\nbool SystemSnapshotLinux::OSServer() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return false;\n}\n\nvoid SystemSnapshotLinux::OSVersion(int* major,\n                                    int* minor,\n                                    int* bugfix,\n                                    std::string* build) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *major = os_version_major_;\n  *minor = os_version_minor_;\n  *bugfix = os_version_bugfix_;\n  build->assign(os_version_build_);\n}\n\nstd::string SystemSnapshotLinux::OSVersionFull() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return os_version_full_;\n}\n\nstd::string SystemSnapshotLinux::MachineDescription() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if BUILDFLAG(IS_ANDROID)\n  std::string description;\n  std::string prop;\n  if (ReadProperty(\"ro.product.model\", &prop)) {\n    description += prop;\n  }\n  if (ReadProperty(\"ro.product.board\", &prop)) {\n    if (!description.empty()) {\n      description.push_back(' ');\n    }\n    description += prop;\n  }\n  return description;\n#else\n  return std::string();\n#endif  // BUILDFLAG(IS_ANDROID)\n}\n\nbool SystemSnapshotLinux::NXEnabled() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  return cpuid_.NXEnabled();\n#elif defined(ARCH_CPU_ARM_FAMILY)\n  // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30\n  return false;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  // Not implementable on MIPS\n  return false;\n#elif defined(ARCH_CPU_RISCV64)\n  // Not implemented\n  return false;\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY\n}\n\nvoid SystemSnapshotLinux::TimeZone(DaylightSavingTimeStatus* dst_status,\n                                   int* standard_offset_seconds,\n                                   int* daylight_offset_seconds,\n                                   std::string* standard_name,\n                                   std::string* daylight_name) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  internal::TimeZone(*snapshot_time_,\n                     dst_status,\n                     standard_offset_seconds,\n                     daylight_offset_seconds,\n                     standard_name,\n                     daylight_name);\n}\n\nvoid SystemSnapshotLinux::ReadKernelVersion(const std::string& version_string) {\n  std::vector<std::string> versions = SplitString(version_string, '.');\n  if (versions.size() < 3) {\n    LOG(WARNING) << \"format error\";\n    return;\n  }\n\n  if (!base::StringToInt(versions[0], &os_version_major_)) {\n    LOG(WARNING) << \"no kernel version\";\n    return;\n  }\n  DCHECK_GE(os_version_major_, 3);\n\n  if (!base::StringToInt(versions[1], &os_version_minor_)) {\n    LOG(WARNING) << \"no major revision\";\n    return;\n  }\n  DCHECK_GE(os_version_minor_, 0);\n\n  size_t minor_rev_end = versions[2].find_first_not_of(\"0123456789\");\n  if (minor_rev_end == std::string::npos) {\n    minor_rev_end = versions[2].size();\n  }\n  if (!base::StringToInt(std::string_view(versions[2].c_str(), minor_rev_end),\n                         &os_version_bugfix_)) {\n    LOG(WARNING) << \"no minor revision\";\n    return;\n  }\n  DCHECK_GE(os_version_bugfix_, 0);\n\n  if (!os_version_build_.empty()) {\n    os_version_build_.push_back(' ');\n  }\n  os_version_build_ += versions[2].substr(minor_rev_end);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/system_snapshot_linux.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_SYSTEM_SNAPSHOT_LINUX_H_\n#define CRASHPAD_SNAPSHOT_LINUX_SYSTEM_SNAPSHOT_LINUX_H_\n\n#include <stdint.h>\n#include <time.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"snapshot/linux/process_reader_linux.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\n#if defined(ARCH_CPU_X86_FAMILY)\n#include \"snapshot/x86/cpuid_reader.h\"\n#endif  // ARCH_CPU_X86_FAMILY\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A SystemSnapshot of the running system, when the system runs Linux.\nclass SystemSnapshotLinux final : public SystemSnapshot {\n public:\n  SystemSnapshotLinux();\n\n  SystemSnapshotLinux(const SystemSnapshotLinux&) = delete;\n  SystemSnapshotLinux& operator=(const SystemSnapshotLinux&) = delete;\n\n  ~SystemSnapshotLinux() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A reader for the process being snapshotted.\n  //!     \\n\\n\n  //!     It seems odd that a system snapshot implementation would need a\n  //!     ProcessReaderLinux, but some of the information reported about the\n  //!     system depends on the process it’s being reported for. For example,\n  //!     the architecture returned by GetCPUArchitecture() should be the\n  //!     architecture of the process, which may be different than the native\n  //!     architecture of the system: an x86_64 system can run both x86_64 and\n  //!     32-bit x86 processes.\n  //! \\param[in] snapshot_time The time of the snapshot being taken.\n  //!     \\n\\n\n  //!     This parameter is necessary for TimeZone() to determine whether\n  //!     daylight saving time was in effect at the time the snapshot was taken.\n  //!     Otherwise, it would need to base its determination on the current\n  //!     time, which may be different than the snapshot time for snapshots\n  //!     generated around the daylight saving transition time.\n  void Initialize(ProcessReaderLinux* process_reader,\n                  const timeval* snapshot_time);\n\n  // SystemSnapshot:\n\n  CPUArchitecture GetCPUArchitecture() const override;\n  uint32_t CPURevision() const override;\n  uint8_t CPUCount() const override;\n  std::string CPUVendor() const override;\n  void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;\n  uint32_t CPUX86Signature() const override;\n  uint64_t CPUX86Features() const override;\n  uint64_t CPUX86ExtendedFeatures() const override;\n  uint32_t CPUX86Leaf7Features() const override;\n  bool CPUX86SupportsDAZ() const override;\n  OperatingSystem GetOperatingSystem() const override;\n  bool OSServer() const override;\n  void OSVersion(int* major,\n                 int* minor,\n                 int* bugfix,\n                 std::string* build) const override;\n  std::string OSVersionFull() const override;\n  bool NXEnabled() const override;\n  std::string MachineDescription() const override;\n  void TimeZone(DaylightSavingTimeStatus* dst_status,\n                int* standard_offset_seconds,\n                int* daylight_offset_seconds,\n                std::string* standard_name,\n                std::string* daylight_name) const override;\n  uint64_t AddressMask() const override { return 0; }\n\n private:\n  void ReadKernelVersion(const std::string& version_string);\n\n  std::string os_version_full_;\n  std::string os_version_build_;\n  ProcessReaderLinux* process_reader_;  // weak\n  const timeval* snapshot_time_;  // weak\n#if defined(ARCH_CPU_X86_FAMILY)\n  CpuidReader cpuid_;\n#endif  // ARCH_CPU_X86_FAMILY\n  int os_version_major_;\n  int os_version_minor_;\n  int os_version_bugfix_;\n  uint32_t target_cpu_;\n  uint8_t cpu_count_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_SYSTEM_SNAPSHOT_LINUX_H_\n"
  },
  {
    "path": "snapshot/linux/system_snapshot_linux_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/system_snapshot_linux.h\"\n\n#include <sys/time.h>\n#include <unistd.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/linux/process_reader_linux.h\"\n#include \"test/errors.h\"\n#include \"test/linux/fake_ptrace_connection.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(SystemSnapshotLinux, Basic) {\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  ProcessReaderLinux process_reader;\n  ASSERT_TRUE(process_reader.Initialize(&connection));\n\n  timeval snapshot_time;\n  ASSERT_EQ(gettimeofday(&snapshot_time, nullptr), 0)\n      << ErrnoMessage(\"gettimeofday\");\n\n  internal::SystemSnapshotLinux system;\n  system.Initialize(&process_reader, &snapshot_time);\n\n  EXPECT_GT(system.CPUCount(), 0u);\n\n  uint64_t current_hz, max_hz;\n  system.CPUFrequency(&current_hz, &max_hz);\n  // For short-term loads, modern CPUs can boost single-core frequency beyond\n  // the advertised base clock. Let's assume this is no more than a factor 2.\n  EXPECT_GE(max_hz * 2, current_hz);\n\n  int major, minor, bugfix;\n  std::string build;\n  system.OSVersion(&major, &minor, &bugfix, &build);\n  EXPECT_GE(major, 3);\n  EXPECT_GE(minor, 0);\n  EXPECT_GE(bugfix, 0);\n  EXPECT_FALSE(build.empty());\n\n  EXPECT_FALSE(system.OSVersionFull().empty());\n\n  // No expectations; just make sure these can be called successfully.\n  system.CPURevision();\n  system.NXEnabled();\n\n#if BUILDFLAG(IS_ANDROID)\n  EXPECT_FALSE(system.MachineDescription().empty());\n#else\n  system.MachineDescription();\n#endif  // BUILDFLAG(IS_ANDROID)\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  system.CPUX86Signature();\n  system.CPUX86Features();\n  system.CPUX86ExtendedFeatures();\n  system.CPUX86Leaf7Features();\n\n  EXPECT_PRED1(\n      [](std::string vendor) {\n        return vendor == \"GenuineIntel\" || vendor == \"AuthenticAMD\" ||\n               vendor == \"HygonGenuine\";\n      },\n      system.CPUVendor());\n\n  EXPECT_TRUE(system.CPUX86SupportsDAZ());\n#endif  // ARCH_CPU_X86_FAMILY\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/test_modules.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/test_modules.h\"\n\n#include <elf.h>\n\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/file/file_writer.h\"\n\nnamespace crashpad {\nnamespace test {\n\nbool WriteTestModule(const base::FilePath& module_path,\n                     const std::string& soname) {\n#if defined(ARCH_CPU_64_BITS)\n  using Ehdr = Elf64_Ehdr;\n  using Phdr = Elf64_Phdr;\n  using Shdr = Elf64_Shdr;\n  using Dyn = Elf64_Dyn;\n  using Sym = Elf64_Sym;\n  unsigned char elf_class = ELFCLASS64;\n#else\n  using Ehdr = Elf32_Ehdr;\n  using Phdr = Elf32_Phdr;\n  using Shdr = Elf32_Shdr;\n  using Dyn = Elf32_Dyn;\n  using Sym = Elf32_Sym;\n  unsigned char elf_class = ELFCLASS32;\n#endif\n\n  struct {\n    Ehdr ehdr;\n    struct {\n      Phdr load1;\n      Phdr load2;\n      Phdr dynamic;\n    } phdr_table;\n    struct {\n      Dyn hash;\n      Dyn strtab;\n      Dyn symtab;\n      Dyn strsz;\n      Dyn syment;\n      Dyn soname;\n      Dyn null;\n    } dynamic_array;\n    struct {\n      Elf32_Word nbucket;\n      Elf32_Word nchain;\n      Elf32_Word bucket;\n      Elf32_Word chain;\n    } hash_table;\n    char string_table[32];\n    struct {\n    } section_header_string_table;\n    struct {\n      Sym und_symbol;\n    } symbol_table;\n    struct {\n      Shdr null;\n      Shdr dynamic;\n      Shdr string_table;\n      Shdr section_header_string_table;\n    } shdr_table;\n  } module = {};\n\n  module.ehdr.e_ident[EI_MAG0] = ELFMAG0;\n  module.ehdr.e_ident[EI_MAG1] = ELFMAG1;\n  module.ehdr.e_ident[EI_MAG2] = ELFMAG2;\n  module.ehdr.e_ident[EI_MAG3] = ELFMAG3;\n\n  module.ehdr.e_ident[EI_CLASS] = elf_class;\n\n#if defined(ARCH_CPU_LITTLE_ENDIAN)\n  module.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;\n#else\n  module.ehdr.e_ident[EI_DATA] = ELFDATA2MSB;\n#endif  // ARCH_CPU_LITTLE_ENDIAN\n\n  module.ehdr.e_ident[EI_VERSION] = EV_CURRENT;\n\n  module.ehdr.e_type = ET_DYN;\n\n#if defined(ARCH_CPU_X86)\n  module.ehdr.e_machine = EM_386;\n#elif defined(ARCH_CPU_X86_64)\n  module.ehdr.e_machine = EM_X86_64;\n#elif defined(ARCH_CPU_ARMEL)\n  module.ehdr.e_machine = EM_ARM;\n#elif defined(ARCH_CPU_ARM64)\n  module.ehdr.e_machine = EM_AARCH64;\n#elif defined(ARCH_CPU_MIPSEL) || defined(ARCH_CPU_MIPS64EL)\n  module.ehdr.e_machine = EM_MIPS;\n#elif defined(ARCH_CPU_RISCV64)\n  module.ehdr.e_machine = EM_RISCV;\n#endif\n\n#if defined(ARCH_CPU_RISCV64)\n  // Crashpad supports RV64GC\n  module.ehdr.e_flags = EF_RISCV_RVC | EF_RISCV_FLOAT_ABI_DOUBLE;\n#endif\n\n  module.ehdr.e_version = EV_CURRENT;\n  module.ehdr.e_ehsize = sizeof(module.ehdr);\n\n  module.ehdr.e_phoff = offsetof(decltype(module), phdr_table);\n  module.ehdr.e_phnum = sizeof(module.phdr_table) / sizeof(Phdr);\n  module.ehdr.e_phentsize = sizeof(Phdr);\n\n  module.ehdr.e_shoff = offsetof(decltype(module), shdr_table);\n  module.ehdr.e_shentsize = sizeof(Shdr);\n  module.ehdr.e_shnum = sizeof(module.shdr_table) / sizeof(Shdr);\n  module.ehdr.e_shstrndx =\n      offsetof(decltype(module.shdr_table), section_header_string_table) /\n      sizeof(Shdr);\n\n  const size_t page_size = getpagesize();\n  auto align = [page_size](uintptr_t addr) {\n    return (addr + page_size - 1) & ~(page_size - 1);\n  };\n  constexpr size_t segment_size = offsetof(decltype(module), shdr_table);\n\n  // This test module covers cases where:\n  // 1. Multiple segments are mapped from file offset 0.\n  // 2. Load bias is negative.\n\n  const uintptr_t load2_vaddr = align(std::numeric_limits<uintptr_t>::max() -\n                                      align(segment_size) - page_size);\n  const uintptr_t load1_vaddr = load2_vaddr - align(segment_size);\n\n  module.phdr_table.load1.p_type = PT_LOAD;\n  module.phdr_table.load1.p_offset = 0;\n  module.phdr_table.load1.p_vaddr = load1_vaddr;\n  module.phdr_table.load1.p_filesz = segment_size;\n  module.phdr_table.load1.p_memsz = segment_size;\n  module.phdr_table.load1.p_flags = PF_R;\n  module.phdr_table.load1.p_align = page_size;\n\n  module.phdr_table.load2.p_type = PT_LOAD;\n  module.phdr_table.load2.p_offset = 0;\n  module.phdr_table.load2.p_vaddr = load2_vaddr;\n  module.phdr_table.load2.p_filesz = segment_size;\n  module.phdr_table.load2.p_memsz = segment_size;\n  module.phdr_table.load2.p_flags = PF_R | PF_W;\n  module.phdr_table.load2.p_align = page_size;\n\n  module.phdr_table.dynamic.p_type = PT_DYNAMIC;\n  module.phdr_table.dynamic.p_offset =\n      offsetof(decltype(module), dynamic_array);\n  module.phdr_table.dynamic.p_vaddr =\n      load2_vaddr + module.phdr_table.dynamic.p_offset;\n  module.phdr_table.dynamic.p_filesz = sizeof(module.dynamic_array);\n  module.phdr_table.dynamic.p_memsz = sizeof(module.dynamic_array);\n  module.phdr_table.dynamic.p_flags = PF_R | PF_W;\n  module.phdr_table.dynamic.p_align = 8;\n\n  module.dynamic_array.hash.d_tag = DT_HASH;\n  module.dynamic_array.hash.d_un.d_ptr =\n      load1_vaddr + offsetof(decltype(module), hash_table);\n  module.dynamic_array.strtab.d_tag = DT_STRTAB;\n  module.dynamic_array.strtab.d_un.d_ptr =\n      load1_vaddr + offsetof(decltype(module), string_table);\n  module.dynamic_array.symtab.d_tag = DT_SYMTAB;\n  module.dynamic_array.symtab.d_un.d_ptr =\n      load1_vaddr + offsetof(decltype(module), symbol_table);\n  module.dynamic_array.strsz.d_tag = DT_STRSZ;\n  module.dynamic_array.strsz.d_un.d_val = sizeof(module.string_table);\n  module.dynamic_array.syment.d_tag = DT_SYMENT;\n  module.dynamic_array.syment.d_un.d_val = sizeof(Sym);\n  constexpr size_t kSonameOffset = 1;\n  module.dynamic_array.soname.d_tag = DT_SONAME;\n  module.dynamic_array.soname.d_un.d_val = kSonameOffset;\n\n  module.dynamic_array.null.d_tag = DT_NULL;\n\n  module.hash_table.nbucket = 1;\n  module.hash_table.nchain = 1;\n  module.hash_table.bucket = 0;\n  module.hash_table.chain = 0;\n\n  if (sizeof(module.string_table) < soname.size() + 2) {\n    ADD_FAILURE() << \"string table too small\";\n    return false;\n  }\n  module.string_table[0] = '\\0';\n  memcpy(&module.string_table[kSonameOffset], soname.c_str(), soname.size());\n\n  module.shdr_table.null.sh_type = SHT_NULL;\n\n  module.shdr_table.dynamic.sh_name = 0;\n  module.shdr_table.dynamic.sh_type = SHT_DYNAMIC;\n  module.shdr_table.dynamic.sh_flags = SHF_WRITE | SHF_ALLOC;\n  module.shdr_table.dynamic.sh_addr = module.phdr_table.dynamic.p_vaddr;\n  module.shdr_table.dynamic.sh_offset = module.phdr_table.dynamic.p_offset;\n  module.shdr_table.dynamic.sh_size = module.phdr_table.dynamic.p_filesz;\n  module.shdr_table.dynamic.sh_link =\n      offsetof(decltype(module.shdr_table), string_table) / sizeof(Shdr);\n\n  module.shdr_table.string_table.sh_name = 0;\n  module.shdr_table.string_table.sh_type = SHT_STRTAB;\n  module.shdr_table.string_table.sh_offset =\n      offsetof(decltype(module), string_table);\n  module.shdr_table.string_table.sh_size = sizeof(module.string_table);\n\n  module.shdr_table.section_header_string_table.sh_name = 0;\n  module.shdr_table.section_header_string_table.sh_type = SHT_STRTAB;\n  module.shdr_table.section_header_string_table.sh_offset =\n      offsetof(decltype(module), section_header_string_table);\n  module.shdr_table.section_header_string_table.sh_size =\n      sizeof(module.section_header_string_table);\n\n  FileWriter writer;\n  if (!writer.Open(module_path,\n                   FileWriteMode::kCreateOrFail,\n                   FilePermissions::kWorldReadable)) {\n    ADD_FAILURE();\n    return false;\n  }\n\n  if (!writer.Write(&module, sizeof(module))) {\n    ADD_FAILURE();\n    LoggingRemoveFile(module_path);\n    return false;\n  }\n\n  return true;\n}\n\nScopedModuleHandle LoadTestModule(const std::string& module_name,\n                                  const std::string& module_soname) {\n  base::FilePath module_path(\n      TestPaths::Executable().DirName().Append(module_name));\n\n  if (!WriteTestModule(module_path, module_soname)) {\n    return ScopedModuleHandle(nullptr);\n  }\n  EXPECT_TRUE(IsRegularFile(module_path));\n\n  ScopedModuleHandle handle(\n      dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));\n  EXPECT_TRUE(handle.valid())\n      << \"dlopen: \" << module_path.value() << \" \" << dlerror();\n\n  EXPECT_TRUE(LoggingRemoveFile(module_path));\n\n  return handle;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/test_modules.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_TEST_MODULES_H_\n#define CRASHPAD_SNAPSHOT_LINUX_TEST_MODULES_H_\n\n#include <string>\n\n#include \"test/scoped_module_handle.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Constructs and loads a test module.\n//!\n//! \\param module_name The filename of the mdoule.\n//! \\param module_soname The SONAME for the module.\n//! \\return a handle to the loaded module on success. On failure, the handle\n//!     will be invalid and a message will be logged.\nScopedModuleHandle LoadTestModule(const std::string& module_name,\n                                  const std::string& module_soname);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_\n"
  },
  {
    "path": "snapshot/linux/thread_snapshot_linux.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/linux/thread_snapshot_linux.h\"\n\n#include <sched.h>\n\n#include \"base/logging.h\"\n#include \"snapshot/linux/capture_memory_delegate_linux.h\"\n#include \"snapshot/linux/cpu_context_linux.h\"\n#include \"util/misc/reinterpret_bytes.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\nint ComputeThreadPriority(int static_priority,\n                          int sched_policy,\n                          int nice_value) {\n  // Map Linux scheduling policy, static priority, and nice value into a\n  // single int value.\n  //\n  // The possible policies in order of approximate priority (low to high) are\n  //   SCHED_IDLE\n  //   SCHED_BATCH\n  //   SCHED_OTHER\n  //   SCHED_RR\n  //   SCHED_FIFO\n  //\n  // static_priority is not used for OTHER, BATCH, or IDLE and should be 0.\n  // For FIFO and RR, static_priority should range from 1 to 99 with 99 being\n  // the highest priority.\n  //\n  // nice value ranges from -20 to 19, with -20 being highest priority\n\n  enum class Policy : uint8_t {\n    kUnknown = 0,\n    kIdle,\n    kBatch,\n    kOther,\n    kRR,\n    kFIFO\n  };\n\n  struct LinuxPriority {\n#if defined(ARCH_CPU_LITTLE_ENDIAN)\n    // nice values affect how dynamic priorities are updated, which only\n    // matters for threads with the same static priority.\n    uint8_t nice_value = 0;\n\n    // The scheduling policy also affects how threads with the same static\n    // priority are ordered, but has greater impact than nice value.\n    Policy policy = Policy::kUnknown;\n\n    // The static priority is the most significant in determining overall\n    // priority.\n    uint8_t static_priority = 0;\n\n    // Put this in the most significant byte position to prevent negative\n    // priorities.\n    uint8_t unused = 0;\n#elif defined(ARCH_CPU_BIG_ENDIAN)\n    uint8_t unused = 0;\n    uint8_t static_priority = 0;\n    Policy policy = Policy::kUnknown;\n    uint8_t nice_value = 0;\n#endif  // ARCH_CPU_LITTLE_ENDIAN\n  };\n  static_assert(sizeof(LinuxPriority) <= sizeof(int), \"priority is too large\");\n\n  LinuxPriority prio;\n\n  // Lower nice values have higher priority, so negate them and add 20 to put\n  // them in the range 1-40 with 40 being highest priority.\n  if (nice_value < -20 || nice_value > 19) {\n    LOG(WARNING) << \"invalid nice value \" << nice_value;\n    prio.nice_value = 0;\n  } else {\n    prio.nice_value = -1 * nice_value + 20;\n  }\n\n  switch (sched_policy) {\n    case SCHED_IDLE:\n      prio.policy = Policy::kIdle;\n      break;\n    case SCHED_BATCH:\n      prio.policy = Policy::kBatch;\n      break;\n    case SCHED_OTHER:\n      prio.policy = Policy::kOther;\n      break;\n    case SCHED_RR:\n      prio.policy = Policy::kRR;\n      break;\n    case SCHED_FIFO:\n      prio.policy = Policy::kFIFO;\n      break;\n    default:\n      prio.policy = Policy::kUnknown;\n      LOG(WARNING) << \"Unknown scheduling policy \" << sched_policy;\n  }\n\n  if (static_priority < 0 || static_priority > 99) {\n    LOG(WARNING) << \"invalid static priority \" << static_priority;\n  }\n  prio.static_priority = static_priority;\n\n  int priority;\n  if (!ReinterpretBytes(prio, &priority)) {\n    LOG(ERROR) << \"Couldn't set priority\";\n    return -1;\n  }\n  return priority;\n}\n\n}  // namespace\n\nThreadSnapshotLinux::ThreadSnapshotLinux()\n    : ThreadSnapshot(),\n      context_union_(),\n      context_(),\n      stack_(),\n      thread_specific_data_address_(0),\n      thread_name_(),\n      thread_id_(-1),\n      priority_(-1),\n      initialized_() {}\n\nThreadSnapshotLinux::~ThreadSnapshotLinux() {}\n\nbool ThreadSnapshotLinux::Initialize(\n    ProcessReaderLinux* process_reader,\n    const ProcessReaderLinux::Thread& thread,\n    uint32_t* gather_indirectly_referenced_memory_bytes_remaining) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  if (process_reader->Is64Bit()) {\n    context_.architecture = kCPUArchitectureX86_64;\n    context_.x86_64 = &context_union_.x86_64;\n    InitializeCPUContextX86_64(thread.thread_info.thread_context.t64,\n                               thread.thread_info.float_context.f64,\n                               context_.x86_64);\n  } else {\n    context_.architecture = kCPUArchitectureX86;\n    context_.x86 = &context_union_.x86;\n    InitializeCPUContextX86(thread.thread_info.thread_context.t32,\n                            thread.thread_info.float_context.f32,\n                            context_.x86);\n  }\n#elif defined(ARCH_CPU_ARM_FAMILY)\n  if (process_reader->Is64Bit()) {\n    context_.architecture = kCPUArchitectureARM64;\n    context_.arm64 = &context_union_.arm64;\n    InitializeCPUContextARM64(thread.thread_info.thread_context.t64,\n                              thread.thread_info.float_context.f64,\n                              context_.arm64);\n  } else {\n    context_.architecture = kCPUArchitectureARM;\n    context_.arm = &context_union_.arm;\n    InitializeCPUContextARM(thread.thread_info.thread_context.t32,\n                            thread.thread_info.float_context.f32,\n                            context_.arm);\n  }\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  if (process_reader->Is64Bit()) {\n    context_.architecture = kCPUArchitectureMIPS64EL;\n    context_.mips64 = &context_union_.mips64;\n    InitializeCPUContextMIPS<ContextTraits64>(\n        thread.thread_info.thread_context.t64,\n        thread.thread_info.float_context.f64,\n        context_.mips64);\n  } else {\n    context_.architecture = kCPUArchitectureMIPSEL;\n    context_.mipsel = &context_union_.mipsel;\n    InitializeCPUContextMIPS<ContextTraits32>(\n        SignalThreadContext32(thread.thread_info.thread_context.t32),\n        thread.thread_info.float_context.f32,\n        context_.mipsel);\n  }\n#elif defined(ARCH_CPU_RISCV64)\n  context_.architecture = kCPUArchitectureRISCV64;\n  context_.riscv64 = &context_union_.riscv64;\n  InitializeCPUContextRISCV64(thread.thread_info.thread_context.t64,\n                              thread.thread_info.float_context.f64,\n                              context_.riscv64);\n#else\n#error Port.\n#endif\n\n  stack_.Initialize(process_reader->Memory(),\n                    thread.stack_region_address,\n                    thread.stack_region_size);\n\n  thread_specific_data_address_ =\n      thread.thread_info.thread_specific_data_address;\n\n  thread_name_ = thread.name;\n  thread_id_ = thread.tid;\n\n  priority_ =\n      thread.have_priorities\n          ? ComputeThreadPriority(\n                thread.static_priority, thread.sched_policy, thread.nice_value)\n          : -1;\n\n  CaptureMemoryDelegateLinux capture_memory_delegate(\n      process_reader,\n      &thread,\n      &pointed_to_memory_,\n      gather_indirectly_referenced_memory_bytes_remaining);\n  CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst CPUContext* ThreadSnapshotLinux::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nconst MemorySnapshot* ThreadSnapshotLinux::Stack() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &stack_;\n}\n\nuint64_t ThreadSnapshotLinux::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nstd::string ThreadSnapshotLinux::ThreadName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_name_;\n}\n\nint ThreadSnapshotLinux::SuspendCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return 0;\n}\n\nint ThreadSnapshotLinux::Priority() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return priority_;\n}\n\nuint64_t ThreadSnapshotLinux::ThreadSpecificDataAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_specific_data_address_;\n}\n\nstd::vector<const MemorySnapshot*> ThreadSnapshotLinux::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> result;\n  result.reserve(pointed_to_memory_.size());\n  for (const auto& pointed_to_memory : pointed_to_memory_) {\n    result.push_back(pointed_to_memory.get());\n  }\n  return result;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/linux/thread_snapshot_linux.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_\n#define CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_\n\n#include <stdint.h>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/linux/process_reader_linux.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ThreadSnapshot of a thread on a Linux system.\nclass ThreadSnapshotLinux final : public ThreadSnapshot {\n public:\n  ThreadSnapshotLinux();\n\n  ThreadSnapshotLinux(const ThreadSnapshotLinux&) = delete;\n  ThreadSnapshotLinux& operator=(const ThreadSnapshotLinux&) = delete;\n\n  ~ThreadSnapshotLinux() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderLinux for the process containing\n  //!     the thread.\n  //! \\param[in] thread The thread within the ProcessReaderLinux for\n  //!     which the snapshot should be created.\n  //! \\param[inout] gather_indirectly_referenced_memory_bytes_remaining The\n  //!     remaining budget for indirectly referenced memory, honored on entry\n  //!     and updated on return.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     a message logged.\n  bool Initialize(\n      ProcessReaderLinux* process_reader,\n      const ProcessReaderLinux::Thread& thread,\n      uint32_t* gather_indirectly_referenced_memory_bytes_remaining);\n\n  // ThreadSnapshot:\n\n  const CPUContext* Context() const override;\n  const MemorySnapshot* Stack() const override;\n  uint64_t ThreadID() const override;\n  std::string ThreadName() const override;\n  int SuspendCount() const override;\n  int Priority() const override;\n  uint64_t ThreadSpecificDataAddress() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  union {\n#if defined(ARCH_CPU_X86_FAMILY)\n    CPUContextX86 x86;\n    CPUContextX86_64 x86_64;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n    CPUContextARM arm;\n    CPUContextARM64 arm64;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n    CPUContextMIPS mipsel;\n    CPUContextMIPS64 mips64;\n#elif defined(ARCH_CPU_RISCV64)\n    CPUContextRISCV64 riscv64;\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY\n  } context_union_;\n  CPUContext context_;\n  MemorySnapshotGeneric stack_;\n  LinuxVMAddress thread_specific_data_address_;\n  std::string thread_name_;\n  pid_t thread_id_;\n  int priority_;\n  InitializationStateDcheck initialized_;\n  std::vector<std::unique_ptr<MemorySnapshotGeneric>> pointed_to_memory_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_\n"
  },
  {
    "path": "snapshot/mac/cpu_context_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/cpu_context_mac.h\"\n\n#include <stddef.h>\n#include <string.h>\n\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n\nnamespace crashpad {\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\nnamespace {\n\nvoid InitializeCPUContextX86Thread(\n    CPUContextX86* context,\n    const x86_thread_state32_t* x86_thread_state32) {\n  context->eax = x86_thread_state32->__eax;\n  context->ebx = x86_thread_state32->__ebx;\n  context->ecx = x86_thread_state32->__ecx;\n  context->edx = x86_thread_state32->__edx;\n  context->edi = x86_thread_state32->__edi;\n  context->esi = x86_thread_state32->__esi;\n  context->ebp = x86_thread_state32->__ebp;\n  context->esp = x86_thread_state32->__esp;\n  context->eip = x86_thread_state32->__eip;\n  context->eflags = x86_thread_state32->__eflags;\n  context->cs = x86_thread_state32->__cs;\n  context->ds = x86_thread_state32->__ds;\n  context->es = x86_thread_state32->__es;\n  context->fs = x86_thread_state32->__fs;\n  context->gs = x86_thread_state32->__gs;\n  context->ss = x86_thread_state32->__ss;\n}\n\nvoid InitializeCPUContextX86Float(\n    CPUContextX86* context, const x86_float_state32_t* x86_float_state32) {\n  // This relies on both x86_float_state32_t and context->fxsave having\n  // identical (fxsave) layout.\n  static_assert(offsetof(x86_float_state32_t, __fpu_reserved1) -\n                         offsetof(x86_float_state32_t, __fpu_fcw) ==\n                     sizeof(context->fxsave),\n                \"types must be equivalent\");\n\n  memcpy(\n      &context->fxsave, &x86_float_state32->__fpu_fcw, sizeof(context->fxsave));\n}\n\nvoid InitializeCPUContextX86Debug(\n    CPUContextX86* context, const x86_debug_state32_t* x86_debug_state32) {\n  context->dr0 = x86_debug_state32->__dr0;\n  context->dr1 = x86_debug_state32->__dr1;\n  context->dr2 = x86_debug_state32->__dr2;\n  context->dr3 = x86_debug_state32->__dr3;\n  context->dr4 = x86_debug_state32->__dr4;\n  context->dr5 = x86_debug_state32->__dr5;\n  context->dr6 = x86_debug_state32->__dr6;\n  context->dr7 = x86_debug_state32->__dr7;\n}\n\n// Initializes |context| from the native thread state structure |state|, which\n// is interpreted according to |flavor|. |state_count| must be at least the\n// expected size for |flavor|. This handles the architecture-specific\n// x86_THREAD_STATE32, x86_FLOAT_STATE32, and x86_DEBUG_STATE32 flavors. It also\n// handles the universal x86_THREAD_STATE, x86_FLOAT_STATE, and x86_DEBUG_STATE\n// flavors provided that the associated structure carries 32-bit data of the\n// corresponding state type. |flavor| may be THREAD_STATE_NONE to avoid setting\n// any thread state in |context|. This returns the architecture-specific flavor\n// value for the thread state that was actually set, or THREAD_STATE_NONE if no\n// thread state was set.\nthread_state_flavor_t InitializeCPUContextX86Flavor(\n    CPUContextX86* context,\n    thread_state_flavor_t flavor,\n    ConstThreadState state,\n    mach_msg_type_number_t state_count) {\n  mach_msg_type_number_t expected_state_count;\n  switch (flavor) {\n    case x86_THREAD_STATE:\n      expected_state_count = x86_THREAD_STATE_COUNT;\n      break;\n    case x86_FLOAT_STATE:\n      expected_state_count = x86_FLOAT_STATE_COUNT;\n      break;\n    case x86_DEBUG_STATE:\n      expected_state_count = x86_DEBUG_STATE_COUNT;\n      break;\n    case x86_THREAD_STATE32:\n      expected_state_count = x86_THREAD_STATE32_COUNT;\n      break;\n    case x86_FLOAT_STATE32:\n      expected_state_count = x86_FLOAT_STATE32_COUNT;\n      break;\n    case x86_DEBUG_STATE32:\n      expected_state_count = x86_DEBUG_STATE32_COUNT;\n      break;\n    case THREAD_STATE_NONE:\n      expected_state_count = 0;\n      break;\n    default:\n      LOG(WARNING) << \"unhandled flavor \" << flavor;\n      return THREAD_STATE_NONE;\n  }\n\n  if (state_count < expected_state_count) {\n    LOG(WARNING) << \"expected state_count \" << expected_state_count\n                 << \" for flavor \" << flavor << \", observed \" << state_count;\n    return THREAD_STATE_NONE;\n  }\n\n  switch (flavor) {\n    case x86_THREAD_STATE: {\n      const x86_thread_state_t* x86_thread_state =\n          reinterpret_cast<const x86_thread_state_t*>(state);\n      if (x86_thread_state->tsh.flavor != x86_THREAD_STATE32) {\n        LOG(WARNING) << \"expected flavor x86_THREAD_STATE32, observed \"\n                     << x86_thread_state->tsh.flavor;\n        return THREAD_STATE_NONE;\n      }\n      return InitializeCPUContextX86Flavor(\n          context,\n          x86_thread_state->tsh.flavor,\n          reinterpret_cast<ConstThreadState>(&x86_thread_state->uts.ts32),\n          x86_thread_state->tsh.count);\n    }\n\n    case x86_FLOAT_STATE: {\n      const x86_float_state_t* x86_float_state =\n          reinterpret_cast<const x86_float_state_t*>(state);\n      if (x86_float_state->fsh.flavor != x86_FLOAT_STATE32) {\n        LOG(WARNING) << \"expected flavor x86_FLOAT_STATE32, observed \"\n                     << x86_float_state->fsh.flavor;\n        return THREAD_STATE_NONE;\n      }\n      return InitializeCPUContextX86Flavor(\n          context,\n          x86_float_state->fsh.flavor,\n          reinterpret_cast<ConstThreadState>(&x86_float_state->ufs.fs32),\n          x86_float_state->fsh.count);\n    }\n\n    case x86_DEBUG_STATE: {\n      const x86_debug_state_t* x86_debug_state =\n          reinterpret_cast<const x86_debug_state_t*>(state);\n      if (x86_debug_state->dsh.flavor != x86_DEBUG_STATE32) {\n        LOG(WARNING) << \"expected flavor x86_DEBUG_STATE32, observed \"\n                     << x86_debug_state->dsh.flavor;\n        return THREAD_STATE_NONE;\n      }\n      return InitializeCPUContextX86Flavor(\n          context,\n          x86_debug_state->dsh.flavor,\n          reinterpret_cast<ConstThreadState>(&x86_debug_state->uds.ds32),\n          x86_debug_state->dsh.count);\n    }\n\n    case x86_THREAD_STATE32: {\n      const x86_thread_state32_t* x86_thread_state32 =\n          reinterpret_cast<const x86_thread_state32_t*>(state);\n      InitializeCPUContextX86Thread(context, x86_thread_state32);\n      return flavor;\n    }\n\n    case x86_FLOAT_STATE32: {\n      const x86_float_state32_t* x86_float_state32 =\n          reinterpret_cast<const x86_float_state32_t*>(state);\n      InitializeCPUContextX86Float(context, x86_float_state32);\n      return flavor;\n    }\n\n    case x86_DEBUG_STATE32: {\n      const x86_debug_state32_t* x86_debug_state32 =\n          reinterpret_cast<const x86_debug_state32_t*>(state);\n      InitializeCPUContextX86Debug(context, x86_debug_state32);\n      return flavor;\n    }\n\n    case THREAD_STATE_NONE: {\n      // This may happen without error when called without exception-style\n      // flavor data, or even from an exception handler when the exception\n      // behavior is EXCEPTION_DEFAULT.\n      return flavor;\n    }\n\n    default: {\n      NOTREACHED();\n    }\n  }\n}\n\nvoid InitializeCPUContextX86_64Thread(\n    CPUContextX86_64* context, const x86_thread_state64_t* x86_thread_state64) {\n  context->rax = x86_thread_state64->__rax;\n  context->rbx = x86_thread_state64->__rbx;\n  context->rcx = x86_thread_state64->__rcx;\n  context->rdx = x86_thread_state64->__rdx;\n  context->rdi = x86_thread_state64->__rdi;\n  context->rsi = x86_thread_state64->__rsi;\n  context->rbp = x86_thread_state64->__rbp;\n  context->rsp = x86_thread_state64->__rsp;\n  context->r8 = x86_thread_state64->__r8;\n  context->r9 = x86_thread_state64->__r9;\n  context->r10 = x86_thread_state64->__r10;\n  context->r11 = x86_thread_state64->__r11;\n  context->r12 = x86_thread_state64->__r12;\n  context->r13 = x86_thread_state64->__r13;\n  context->r14 = x86_thread_state64->__r14;\n  context->r15 = x86_thread_state64->__r15;\n  context->rip = x86_thread_state64->__rip;\n  context->rflags = x86_thread_state64->__rflags;\n  context->cs = x86_thread_state64->__cs;\n  context->fs = x86_thread_state64->__fs;\n  context->gs = x86_thread_state64->__gs;\n}\n\nvoid InitializeCPUContextX86_64Float(\n    CPUContextX86_64* context, const x86_float_state64_t* x86_float_state64) {\n  // This relies on both x86_float_state64_t and context->fxsave having\n  // identical (fxsave) layout.\n  static_assert(offsetof(x86_float_state64_t, __fpu_reserved1) -\n                         offsetof(x86_float_state64_t, __fpu_fcw) ==\n                     sizeof(context->fxsave),\n                \"types must be equivalent\");\n\n  memcpy(&context->fxsave,\n         &x86_float_state64->__fpu_fcw,\n         sizeof(context->fxsave));\n}\n\nvoid InitializeCPUContextX86_64Debug(\n    CPUContextX86_64* context, const x86_debug_state64_t* x86_debug_state64) {\n  context->dr0 = x86_debug_state64->__dr0;\n  context->dr1 = x86_debug_state64->__dr1;\n  context->dr2 = x86_debug_state64->__dr2;\n  context->dr3 = x86_debug_state64->__dr3;\n  context->dr4 = x86_debug_state64->__dr4;\n  context->dr5 = x86_debug_state64->__dr5;\n  context->dr6 = x86_debug_state64->__dr6;\n  context->dr7 = x86_debug_state64->__dr7;\n}\n\n// Initializes |context| from the native thread state structure |state|, which\n// is interpreted according to |flavor|. |state_count| must be at least the\n// expected size for |flavor|. This handles the architecture-specific\n// x86_THREAD_STATE64, x86_FLOAT_STATE64, and x86_DEBUG_STATE64 flavors. It also\n// handles the universal x86_THREAD_STATE, x86_FLOAT_STATE, and x86_DEBUG_STATE\n// flavors provided that the associated structure carries 64-bit data of the\n// corresponding state type. |flavor| may be THREAD_STATE_NONE to avoid setting\n// any thread state in |context|. This returns the architecture-specific flavor\n// value for the thread state that was actually set, or THREAD_STATE_NONE if no\n// thread state was set.\nthread_state_flavor_t InitializeCPUContextX86_64Flavor(\n    CPUContextX86_64* context,\n    thread_state_flavor_t flavor,\n    ConstThreadState state,\n    mach_msg_type_number_t state_count) {\n  mach_msg_type_number_t expected_state_count;\n  switch (flavor) {\n    case x86_THREAD_STATE:\n      expected_state_count = x86_THREAD_STATE_COUNT;\n      break;\n    case x86_FLOAT_STATE:\n      expected_state_count = x86_FLOAT_STATE_COUNT;\n      break;\n    case x86_DEBUG_STATE:\n      expected_state_count = x86_DEBUG_STATE_COUNT;\n      break;\n    case x86_THREAD_STATE64:\n      expected_state_count = x86_THREAD_STATE64_COUNT;\n      break;\n    case x86_FLOAT_STATE64:\n      expected_state_count = x86_FLOAT_STATE64_COUNT;\n      break;\n    case x86_DEBUG_STATE64:\n      expected_state_count = x86_DEBUG_STATE64_COUNT;\n      break;\n    case THREAD_STATE_NONE:\n      expected_state_count = 0;\n      break;\n    default:\n      LOG(WARNING) << \"unhandled flavor \" << flavor;\n      return THREAD_STATE_NONE;\n  }\n\n  if (state_count < expected_state_count) {\n    LOG(WARNING) << \"expected state_count \" << expected_state_count\n                 << \" for flavor \" << flavor << \", observed \" << state_count;\n    return THREAD_STATE_NONE;\n  }\n\n  switch (flavor) {\n    case x86_THREAD_STATE: {\n      const x86_thread_state_t* x86_thread_state =\n          reinterpret_cast<const x86_thread_state_t*>(state);\n      if (x86_thread_state->tsh.flavor != x86_THREAD_STATE64) {\n        LOG(WARNING) << \"expected flavor x86_THREAD_STATE64, observed \"\n                     << x86_thread_state->tsh.flavor;\n        return THREAD_STATE_NONE;\n      }\n      return InitializeCPUContextX86_64Flavor(\n          context,\n          x86_thread_state->tsh.flavor,\n          reinterpret_cast<ConstThreadState>(&x86_thread_state->uts.ts64),\n          x86_thread_state->tsh.count);\n    }\n\n    case x86_FLOAT_STATE: {\n      const x86_float_state_t* x86_float_state =\n          reinterpret_cast<const x86_float_state_t*>(state);\n      if (x86_float_state->fsh.flavor != x86_FLOAT_STATE64) {\n        LOG(WARNING) << \"expected flavor x86_FLOAT_STATE64, observed \"\n                     << x86_float_state->fsh.flavor;\n        return THREAD_STATE_NONE;\n      }\n      return InitializeCPUContextX86_64Flavor(\n          context,\n          x86_float_state->fsh.flavor,\n          reinterpret_cast<ConstThreadState>(&x86_float_state->ufs.fs64),\n          x86_float_state->fsh.count);\n    }\n\n    case x86_DEBUG_STATE: {\n      const x86_debug_state_t* x86_debug_state =\n          reinterpret_cast<const x86_debug_state_t*>(state);\n      if (x86_debug_state->dsh.flavor != x86_DEBUG_STATE64) {\n        LOG(WARNING) << \"expected flavor x86_DEBUG_STATE64, observed \"\n                     << x86_debug_state->dsh.flavor;\n        return THREAD_STATE_NONE;\n      }\n      return InitializeCPUContextX86_64Flavor(\n          context,\n          x86_debug_state->dsh.flavor,\n          reinterpret_cast<ConstThreadState>(&x86_debug_state->uds.ds64),\n          x86_debug_state->dsh.count);\n    }\n\n    case x86_THREAD_STATE64: {\n      const x86_thread_state64_t* x86_thread_state64 =\n          reinterpret_cast<const x86_thread_state64_t*>(state);\n      InitializeCPUContextX86_64Thread(context, x86_thread_state64);\n      return flavor;\n    }\n\n    case x86_FLOAT_STATE64: {\n      const x86_float_state64_t* x86_float_state64 =\n          reinterpret_cast<const x86_float_state64_t*>(state);\n      InitializeCPUContextX86_64Float(context, x86_float_state64);\n      return flavor;\n    }\n\n    case x86_DEBUG_STATE64: {\n      const x86_debug_state64_t* x86_debug_state64 =\n          reinterpret_cast<const x86_debug_state64_t*>(state);\n      InitializeCPUContextX86_64Debug(context, x86_debug_state64);\n      return flavor;\n    }\n\n    case THREAD_STATE_NONE: {\n      // This may happen without error when called without exception-style\n      // flavor data, or even from an exception handler when the exception\n      // behavior is EXCEPTION_DEFAULT.\n      return flavor;\n    }\n\n    default: {\n      NOTREACHED();\n    }\n  }\n}\n\n}  // namespace\n\nnamespace internal {\n\nvoid InitializeCPUContextX86(CPUContextX86* context,\n                             thread_state_flavor_t flavor,\n                             ConstThreadState state,\n                             mach_msg_type_number_t state_count,\n                             const x86_thread_state32_t* x86_thread_state32,\n                             const x86_float_state32_t* x86_float_state32,\n                             const x86_debug_state32_t* x86_debug_state32) {\n  thread_state_flavor_t set_flavor = THREAD_STATE_NONE;\n  if (flavor != THREAD_STATE_NONE) {\n    set_flavor =\n        InitializeCPUContextX86Flavor(context, flavor, state, state_count);\n  }\n\n  if (set_flavor != x86_THREAD_STATE32) {\n    InitializeCPUContextX86Thread(context, x86_thread_state32);\n  }\n  if (set_flavor != x86_FLOAT_STATE32) {\n    InitializeCPUContextX86Float(context, x86_float_state32);\n  }\n  if (set_flavor != x86_DEBUG_STATE32) {\n    InitializeCPUContextX86Debug(context, x86_debug_state32);\n  }\n}\n\nvoid InitializeCPUContextX86_64(CPUContextX86_64* context,\n                                thread_state_flavor_t flavor,\n                                ConstThreadState state,\n                                mach_msg_type_number_t state_count,\n                                const x86_thread_state64_t* x86_thread_state64,\n                                const x86_float_state64_t* x86_float_state64,\n                                const x86_debug_state64_t* x86_debug_state64) {\n  thread_state_flavor_t set_flavor = THREAD_STATE_NONE;\n  if (flavor != THREAD_STATE_NONE) {\n    set_flavor =\n        InitializeCPUContextX86_64Flavor(context, flavor, state, state_count);\n  }\n\n  if (set_flavor != x86_THREAD_STATE64) {\n    InitializeCPUContextX86_64Thread(context, x86_thread_state64);\n  }\n  if (set_flavor != x86_FLOAT_STATE64) {\n    InitializeCPUContextX86_64Float(context, x86_float_state64);\n  }\n  if (set_flavor != x86_DEBUG_STATE64) {\n    InitializeCPUContextX86_64Debug(context, x86_debug_state64);\n  }\n}\n\n}  // namespace internal\n\n#elif defined(ARCH_CPU_ARM64)\n\nnamespace {\n\nvoid InitializeCPUContextARM64Thread(\n    CPUContextARM64* context,\n    const arm_thread_state64_t* arm_thread_state64) {\n  // The first 29 fields of context->regs is laid out identically to\n  // arm_thread_state64->__x.\n  memcpy(\n      context->regs, arm_thread_state64->__x, sizeof(arm_thread_state64->__x));\n\n  context->regs[29] = arm_thread_state64_get_fp(*arm_thread_state64);\n  context->regs[30] = arm_thread_state64_get_lr(*arm_thread_state64);\n  context->sp = arm_thread_state64_get_sp(*arm_thread_state64);\n  context->pc = arm_thread_state64_get_pc(*arm_thread_state64);\n  context->spsr =\n      static_cast<decltype(context->spsr)>(arm_thread_state64->__cpsr);\n}\n\nvoid InitializeCPUContextARM64Neon(CPUContextARM64* context,\n                                   const arm_neon_state64_t* arm_neon_state64) {\n  static_assert(sizeof(context->fpsimd) == sizeof(arm_neon_state64->__v),\n                \"fpsimd context size mismatch\");\n  memcpy(context->fpsimd, arm_neon_state64->__v, sizeof(arm_neon_state64->__v));\n  context->fpsr = arm_neon_state64->__fpsr;\n  context->fpcr = arm_neon_state64->__fpcr;\n}\n\nvoid InitializeCPUContextARM64Debug(\n    CPUContextARM64* context,\n    const arm_debug_state64_t* arm_debug_state64) {\n  // TODO(macos_arm64): Create a spot in CPUContextARM64 to keep this.\n}\n\nthread_state_flavor_t InitializeCPUContextARM64Flavor(\n    CPUContextARM64* context,\n    thread_state_flavor_t flavor,\n    ConstThreadState state,\n    mach_msg_type_number_t state_count) {\n  mach_msg_type_number_t expected_state_count;\n  switch (flavor) {\n    case ARM_UNIFIED_THREAD_STATE:\n      expected_state_count = ARM_UNIFIED_THREAD_STATE_COUNT;\n      break;\n    case ARM_THREAD_STATE64:\n      expected_state_count = ARM_THREAD_STATE64_COUNT;\n      break;\n    case ARM_NEON_STATE64:\n      expected_state_count = ARM_NEON_STATE64_COUNT;\n      break;\n    case ARM_DEBUG_STATE64:\n      expected_state_count = ARM_DEBUG_STATE64_COUNT;\n      break;\n    case THREAD_STATE_NONE: {\n      // This may happen without error when called without exception-style\n      // flavor data, or even from an exception handler when the exception\n      // behavior is EXCEPTION_DEFAULT.\n      return flavor;\n    }\n    default:\n      LOG(WARNING) << \"unhandled flavor \" << flavor;\n      return THREAD_STATE_NONE;\n  }\n\n  if (state_count < expected_state_count) {\n    LOG(WARNING) << \"expected state_count \" << expected_state_count\n                 << \" for flavor \" << flavor << \", observed \" << state_count;\n    return THREAD_STATE_NONE;\n  }\n\n  switch (flavor) {\n    case ARM_UNIFIED_THREAD_STATE: {\n      const arm_unified_thread_state_t* arm_thread_state =\n          reinterpret_cast<const arm_unified_thread_state_t*>(state);\n      if (arm_thread_state->ash.flavor != ARM_THREAD_STATE64) {\n        LOG(WARNING) << \"expected flavor ARM_THREAD_STATE64, observed \"\n                     << arm_thread_state->ash.flavor;\n        return THREAD_STATE_NONE;\n      }\n      return InitializeCPUContextARM64Flavor(\n          context,\n          arm_thread_state->ash.flavor,\n          reinterpret_cast<ConstThreadState>(&arm_thread_state->ts_64),\n          arm_thread_state->ash.count);\n    }\n\n    case ARM_THREAD_STATE64: {\n      const arm_thread_state64_t* arm_thread_state =\n          reinterpret_cast<const arm_thread_state64_t*>(state);\n      InitializeCPUContextARM64Thread(context, arm_thread_state);\n      return ARM_THREAD_STATE64;\n    }\n\n    case ARM_NEON_STATE64: {\n      const arm_neon_state64_t* arm_neon_state =\n          reinterpret_cast<const arm_neon_state64_t*>(state);\n      InitializeCPUContextARM64Neon(context, arm_neon_state);\n      return ARM_NEON_STATE64;\n    }\n\n    case ARM_DEBUG_STATE64: {\n      const arm_debug_state64_t* arm_debug_state =\n          reinterpret_cast<const arm_debug_state64_t*>(state);\n      InitializeCPUContextARM64Debug(context, arm_debug_state);\n      return ARM_DEBUG_STATE64;\n    }\n\n    case THREAD_STATE_NONE: {\n      // This may happen without error when called without exception-style\n      // flavor data, or even from an exception handler when the exception\n      // behavior is EXCEPTION_DEFAULT.\n      return flavor;\n    }\n\n    default: {\n      NOTREACHED();\n    }\n  }\n}\n\n}  // namespace\n\nnamespace internal {\n\nvoid InitializeCPUContextARM64(CPUContextARM64* context,\n                               thread_state_flavor_t flavor,\n                               ConstThreadState state,\n                               mach_msg_type_number_t state_count,\n                               const arm_thread_state64_t* arm_thread_state64,\n                               const arm_neon_state64_t* arm_neon_state64,\n                               const arm_debug_state64_t* arm_debug_state64) {\n  thread_state_flavor_t set_flavor = THREAD_STATE_NONE;\n  if (flavor != THREAD_STATE_NONE) {\n    set_flavor =\n        InitializeCPUContextARM64Flavor(context, flavor, state, state_count);\n  }\n\n  if (set_flavor != ARM_THREAD_STATE64) {\n    InitializeCPUContextARM64Thread(context, arm_thread_state64);\n  }\n  if (set_flavor != ARM_NEON_STATE64) {\n    InitializeCPUContextARM64Neon(context, arm_neon_state64);\n  }\n  if (set_flavor != ARM_DEBUG_STATE64) {\n    InitializeCPUContextARM64Debug(context, arm_debug_state64);\n  }\n}\n\n}  // namespace internal\n\n#endif\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/cpu_context_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_\n#define CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_\n\n#include <mach/mach.h>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"util/mach/mach_extensions.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n#if defined(ARCH_CPU_X86_FAMILY) || DOXYGEN\n\n//! \\brief Initializes a CPUContextX86 structure from native context structures\n//!     on macOS.\n//!\n//! \\a flavor, \\a state, and \\a state_count may be supplied by exception\n//! handlers in order for the \\a context parameter to be initialized by the\n//! thread state received by the exception handler to the extent possible. In\n//! that case, whatever thread state specified by these three parameters will\n//! supersede \\a x86_thread_state32, \\a x86_float_state32, or \\a\n//! x86_debug_state32. If thread state in this format is not available, \\a\n//! flavor may be set to `THREAD_STATE_NONE`, and all of \\a x86_thread_state32,\n//! \\a x86_float_state32, and \\a x86_debug_state32 will be honored.\n//!\n//! If \\a flavor, \\a state, and \\a state_count are provided but do not contain\n//! valid values, a message will be logged and their values will be ignored as\n//! though \\a flavor were specified as `THREAD_STATE_NONE`.\n//!\n//! \\param[out] context The CPUContextX86 structure to initialize.\n//! \\param[in] flavor The native thread state flavor of \\a state. This may be\n//!     `x86_THREAD_STATE32`, `x86_FLOAT_STATE32`, `x86_DEBUG_STATE32`,\n//!     `x86_THREAD_STATE`, `x86_FLOAT_STATE`, or `x86_DEBUG_STATE`. It may also\n//!     be `THREAD_STATE_NONE` if \\a state is not supplied (and is `nullptr`).\n//! \\param[in] state The native thread state, which may be a casted pointer to\n//!     `x86_thread_state32_t`, `x86_float_state32_t`, `x86_debug_state32_t`,\n//!     `x86_thread_state`, `x86_float_state`, or `x86_debug_state`. This\n//!     parameter may be `nullptr` to not supply this data, in which case \\a\n//!     flavor must be `THREAD_STATE_NONE`. If a “universal” structure is used,\n//!     it must carry 32-bit state data of the correct type.\n//! \\param[in] state_count The number of `natural_t`-sized (`int`-sized) units\n//!     in \\a state. This may be 0 if \\a state is `nullptr`.\n//! \\param[in] x86_thread_state32 The state of the thread’s integer registers.\n//! \\param[in] x86_float_state32 The state of the thread’s floating-point\n//!     registers.\n//! \\param[in] x86_debug_state32 The state of the thread’s debug registers.\nvoid InitializeCPUContextX86(CPUContextX86* context,\n                             thread_state_flavor_t flavor,\n                             ConstThreadState state,\n                             mach_msg_type_number_t state_count,\n                             const x86_thread_state32_t* x86_thread_state32,\n                             const x86_float_state32_t* x86_float_state32,\n                             const x86_debug_state32_t* x86_debug_state32);\n\n//! \\brief Initializes a CPUContextX86_64 structure from native context\n//!     structures on macOS.\n//!\n//! \\a flavor, \\a state, and \\a state_count may be supplied by exception\n//! handlers in order for the \\a context parameter to be initialized by the\n//! thread state received by the exception handler to the extent possible. In\n//! that case, whatever thread state specified by these three parameters will\n//! supersede \\a x86_thread_state64, \\a x86_float_state64, or \\a\n//! x86_debug_state64. If thread state in this format is not available, \\a\n//! flavor may be set to `THREAD_STATE_NONE`, and all of \\a x86_thread_state64,\n//! \\a x86_float_state64, and \\a x86_debug_state64 will be honored.\n//!\n//! If \\a flavor, \\a state, and \\a state_count are provided but do not contain\n//! valid values, a message will be logged and their values will be ignored as\n//! though \\a flavor were specified as `THREAD_STATE_NONE`.\n//!\n//! \\param[out] context The CPUContextX86_64 structure to initialize.\n//! \\param[in] flavor The native thread state flavor of \\a state. This may be\n//!     `x86_THREAD_STATE64`, `x86_FLOAT_STATE64`, `x86_DEBUG_STATE64`,\n//!     `x86_THREAD_STATE`, `x86_FLOAT_STATE`, or `x86_DEBUG_STATE`. It may also\n//!     be `THREAD_STATE_NONE` if \\a state is not supplied (and is `nullptr`).\n//! \\param[in] state The native thread state, which may be a casted pointer to\n//!     `x86_thread_state64_t`, `x86_float_state64_t`, `x86_debug_state64_t`,\n//!     `x86_thread_state`, `x86_float_state`, or `x86_debug_state`. This\n//!     parameter may be `nullptr` to not supply this data, in which case \\a\n//!     flavor must be `THREAD_STATE_NONE`. If a “universal” structure is used,\n//!     it must carry 64-bit state data of the correct type.\n//! \\param[in] state_count The number of `int`-sized units in \\a state. This may\n//!     be 0 if \\a state is `nullptr`.\n//! \\param[in] x86_thread_state64 The state of the thread’s integer registers.\n//! \\param[in] x86_float_state64 The state of the thread’s floating-point\n//!     registers.\n//! \\param[in] x86_debug_state64 The state of the thread’s debug registers.\nvoid InitializeCPUContextX86_64(CPUContextX86_64* context,\n                                thread_state_flavor_t flavor,\n                                ConstThreadState state,\n                                mach_msg_type_number_t state_count,\n                                const x86_thread_state64_t* x86_thread_state64,\n                                const x86_float_state64_t* x86_float_state64,\n                                const x86_debug_state64_t* x86_debug_state64);\n\n#elif defined(ARCH_CPU_ARM64) || DOXYGEN\n//! \\brief Initializes a CPUContextARM64 structure from native context\n//!     structures on macOS or iOS.\n//!\n//! \\a flavor, \\a state, and \\a state_count may be supplied by exception\n//! handlers in order for the \\a context parameter to be initialized by the\n//! thread state received by the exception handler to the extent possible. In\n//! that case, whatever thread state specified by these three parameters will\n//! supersede \\a arm_thread_state64 or \\a arm_neon_state64. If thread state in\n//! this format is not available, \\a flavor may be set to `THREAD_STATE_NONE`,\n//! and all of \\a arm_thread_state64 abd \\a arm_neon_state64 will be honored.\n//!\n//! If \\a flavor, \\a state, and \\a state_count are provided but do not contain\n//! valid values, a message will be logged and their values will be ignored as\n//! though \\a flavor were specified as `THREAD_STATE_NONE`.\n//!\n//! \\param[out] context The CPUContextARM64 structure to initialize.\n//! \\param[in] flavor The native thread state flavor of \\a state. This may be\n//!     `ARM_THREAD_STATE64`, `ARM_THREAD_STATE` or `ARM_NEON_STATE64`. It may\n//!     also be `THREAD_STATE_NONE` if \\a state is not supplied (and is\n//!     `nullptr`).\n//! \\param[in] state The native thread state, which may be a casted pointer to\n//!     `arm_thread_state64_t`, `arm_unified_thread_state` or\n//!     `arm_neon_state64_t`. This parameter may be `nullptr` to not supply this\n//!     data, in which case \\a flavor must be `THREAD_STATE_NONE`. If a\n//!     “universal” structure is used, it must carry 64-bit state data of the\n//!     correct type.\n//! \\param[in] state_count The number of `int`-sized units in \\a state. This\n//!     may be 0 if \\a state is `nullptr`.\n//! \\param[in] arm_thread_state64 The state of the thread’s integer registers.\n//! \\param[in] arm_neon_state64 The state of the thread’s floating-point\n//!     registers.\n//! \\param[in] arm_debug_state64 The state of the thread’s debug registers.\nvoid InitializeCPUContextARM64(CPUContextARM64* context,\n                               thread_state_flavor_t flavor,\n                               ConstThreadState state,\n                               mach_msg_type_number_t state_count,\n                               const arm_thread_state64_t* arm_thread_state64,\n                               const arm_neon_state64_t* arm_neon_state64,\n                               const arm_debug_state64_t* arm_debug_state64);\n#endif\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_\n"
  },
  {
    "path": "snapshot/mac/cpu_context_mac_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/cpu_context_mac.h\"\n\n#include <mach/mach.h>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\nTEST(CPUContextMac, InitializeContextX86) {\n  x86_thread_state32_t x86_thread_state32 = {};\n  x86_float_state32_t x86_float_state32 = {};\n  x86_debug_state32_t x86_debug_state32 = {};\n  x86_thread_state32.__eax = 1;\n  x86_float_state32.__fpu_ftw = 2;\n  x86_debug_state32.__dr0 = 3;\n\n  // Test the simple case, where everything in the CPUContextX86 argument is set\n  // directly from the supplied thread, float, and debug state parameters.\n  {\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(&cpu_context_x86,\n                                      THREAD_STATE_NONE,\n                                      nullptr,\n                                      0,\n                                      &x86_thread_state32,\n                                      &x86_float_state32,\n                                      &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u);\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n\n  // Supply context in a CPU-specific “flavor” parameter expected to be used\n  // instead of the supplied thread, float, or debug state parameters. Do this\n  // once for each of the three valid flavors. This simulates how\n  // InitializeCPUContextX86() might be used to initialize the context in an\n  // exception handler, where the exception handler may have received the\n  // “flavor” parameter and this context should be used to initialize the\n  // CPUContextX86.\n\n  {\n    x86_thread_state32_t alt_x86_thread_state32 = {};\n    alt_x86_thread_state32.__eax = 4;\n\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(\n        &cpu_context_x86,\n        x86_THREAD_STATE32,\n        reinterpret_cast<natural_t*>(&alt_x86_thread_state32),\n        x86_THREAD_STATE32_COUNT,\n        &x86_thread_state32,\n        &x86_float_state32,\n        &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 4u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u);\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n\n  {\n    x86_float_state32_t alt_x86_float_state32 = {};\n    alt_x86_float_state32.__fpu_ftw = 5;\n\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(\n        &cpu_context_x86,\n        x86_FLOAT_STATE32,\n        reinterpret_cast<natural_t*>(&alt_x86_float_state32),\n        x86_FLOAT_STATE32_COUNT,\n        &x86_thread_state32,\n        &x86_float_state32,\n        &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 5u);\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n\n  {\n    x86_debug_state32_t alt_x86_debug_state32 = {};\n    alt_x86_debug_state32.__dr0 = 6;\n\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(\n        &cpu_context_x86,\n        x86_DEBUG_STATE32,\n        reinterpret_cast<natural_t*>(&alt_x86_debug_state32),\n        x86_DEBUG_STATE32_COUNT,\n        &x86_thread_state32,\n        &x86_float_state32,\n        &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u);\n    EXPECT_EQ(cpu_context_x86.dr0, 6u);\n  }\n\n  // Supply context in a universal “flavor” parameter expected to be used\n  // instead of the supplied thread, float, or debug state parameters. The\n  // universal format allows an exception handler to be registered to receive\n  // thread, float, or debug state without having to know in advance whether it\n  // will be receiving the state from a 32-bit or 64-bit process. For\n  // CPUContextX86, only the 32-bit form is supported.\n\n  {\n    x86_thread_state x86_thread_state_3264 = {};\n    x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE32;\n    x86_thread_state_3264.tsh.count = x86_THREAD_STATE32_COUNT;\n    x86_thread_state_3264.uts.ts32.__eax = 7;\n\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(\n        &cpu_context_x86,\n        x86_THREAD_STATE,\n        reinterpret_cast<natural_t*>(&x86_thread_state_3264),\n        x86_THREAD_STATE_COUNT,\n        &x86_thread_state32,\n        &x86_float_state32,\n        &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 7u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u);\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n\n  {\n    x86_float_state x86_float_state_3264 = {};\n    x86_float_state_3264.fsh.flavor = x86_FLOAT_STATE32;\n    x86_float_state_3264.fsh.count = x86_FLOAT_STATE32_COUNT;\n    x86_float_state_3264.ufs.fs32.__fpu_ftw = 8;\n\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(\n        &cpu_context_x86,\n        x86_FLOAT_STATE,\n        reinterpret_cast<natural_t*>(&x86_float_state_3264),\n        x86_FLOAT_STATE_COUNT,\n        &x86_thread_state32,\n        &x86_float_state32,\n        &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 8u);\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n\n  {\n    x86_debug_state x86_debug_state_3264 = {};\n    x86_debug_state_3264.dsh.flavor = x86_DEBUG_STATE32;\n    x86_debug_state_3264.dsh.count = x86_DEBUG_STATE32_COUNT;\n    x86_debug_state_3264.uds.ds32.__dr0 = 9;\n\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(\n        &cpu_context_x86,\n        x86_DEBUG_STATE,\n        reinterpret_cast<natural_t*>(&x86_debug_state_3264),\n        x86_DEBUG_STATE_COUNT,\n        &x86_thread_state32,\n        &x86_float_state32,\n        &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u);\n    EXPECT_EQ(cpu_context_x86.dr0, 9u);\n  }\n\n  // Supply inappropriate “flavor” contexts to test that\n  // InitializeCPUContextX86() detects the problem and refuses to use the\n  // supplied “flavor” context, falling back to the thread, float, and debug\n  // states.\n\n  {\n    x86_thread_state64_t x86_thread_state64 = {};\n\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(\n        &cpu_context_x86,\n        x86_THREAD_STATE64,\n        reinterpret_cast<natural_t*>(&x86_thread_state64),\n        x86_THREAD_STATE64_COUNT,\n        &x86_thread_state32,\n        &x86_float_state32,\n        &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u);\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n\n  {\n    x86_thread_state x86_thread_state_3264 = {};\n    x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE64;\n    x86_thread_state_3264.tsh.count = x86_THREAD_STATE64_COUNT;\n\n    CPUContextX86 cpu_context_x86 = {};\n    internal::InitializeCPUContextX86(\n        &cpu_context_x86,\n        x86_THREAD_STATE,\n        reinterpret_cast<natural_t*>(&x86_thread_state_3264),\n        x86_THREAD_STATE_COUNT,\n        &x86_thread_state32,\n        &x86_float_state32,\n        &x86_debug_state32);\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u);\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n}\n\nTEST(CPUContextMac, InitializeContextX86_64) {\n  x86_thread_state64_t x86_thread_state64 = {};\n  x86_float_state64_t x86_float_state64 = {};\n  x86_debug_state64_t x86_debug_state64 = {};\n  x86_thread_state64.__rax = 10;\n  x86_float_state64.__fpu_ftw = 11;\n  x86_debug_state64.__dr0 = 12;\n\n  // Test the simple case, where everything in the CPUContextX86_64 argument is\n  // set directly from the supplied thread, float, and debug state parameters.\n  {\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(&cpu_context_x86_64,\n                                         THREAD_STATE_NONE,\n                                         nullptr,\n                                         0,\n                                         &x86_thread_state64,\n                                         &x86_float_state64,\n                                         &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 10u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 12u);\n  }\n\n  // Supply context in a CPU-specific “flavor” parameter expected to be used\n  // instead of the supplied thread, float, or debug state parameters. Do this\n  // once for each of the three valid flavors. This simulates how\n  // InitializeCPUContextX86_64() might be used to initialize the context in an\n  // exception handler, where the exception handler may have received the\n  // “flavor” parameter and this context should be used to initialize the\n  // CPUContextX86_64.\n\n  {\n    x86_thread_state64_t alt_x86_thread_state64 = {};\n    alt_x86_thread_state64.__rax = 13;\n\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(\n        &cpu_context_x86_64,\n        x86_THREAD_STATE64,\n        reinterpret_cast<natural_t*>(&alt_x86_thread_state64),\n        x86_THREAD_STATE64_COUNT,\n        &x86_thread_state64,\n        &x86_float_state64,\n        &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 13u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 12u);\n  }\n\n  {\n    x86_float_state64_t alt_x86_float_state64 = {};\n    alt_x86_float_state64.__fpu_ftw = 14;\n\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(\n        &cpu_context_x86_64,\n        x86_FLOAT_STATE64,\n        reinterpret_cast<natural_t*>(&alt_x86_float_state64),\n        x86_FLOAT_STATE64_COUNT,\n        &x86_thread_state64,\n        &x86_float_state64,\n        &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 10u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 14u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 12u);\n  }\n\n  {\n    x86_debug_state64_t alt_x86_debug_state64 = {};\n    alt_x86_debug_state64.__dr0 = 15;\n\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(\n        &cpu_context_x86_64,\n        x86_DEBUG_STATE64,\n        reinterpret_cast<natural_t*>(&alt_x86_debug_state64),\n        x86_DEBUG_STATE64_COUNT,\n        &x86_thread_state64,\n        &x86_float_state64,\n        &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 10u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 15u);\n  }\n\n  // Supply context in a universal “flavor” parameter expected to be used\n  // instead of the supplied thread, float, or debug state parameters. The\n  // universal format allows an exception handler to be registered to receive\n  // thread, float, or debug state without having to know in advance whether it\n  // will be receiving the state from a 32-bit or 64-bit process. For\n  // CPUContextX86_64, only the 64-bit form is supported.\n\n  {\n    x86_thread_state x86_thread_state_3264 = {};\n    x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE64;\n    x86_thread_state_3264.tsh.count = x86_THREAD_STATE64_COUNT;\n    x86_thread_state_3264.uts.ts64.__rax = 16;\n\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(\n        &cpu_context_x86_64,\n        x86_THREAD_STATE,\n        reinterpret_cast<natural_t*>(&x86_thread_state_3264),\n        x86_THREAD_STATE_COUNT,\n        &x86_thread_state64,\n        &x86_float_state64,\n        &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 16u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 12u);\n  }\n\n  {\n    x86_float_state x86_float_state_3264 = {};\n    x86_float_state_3264.fsh.flavor = x86_FLOAT_STATE64;\n    x86_float_state_3264.fsh.count = x86_FLOAT_STATE64_COUNT;\n    x86_float_state_3264.ufs.fs64.__fpu_ftw = 17;\n\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(\n        &cpu_context_x86_64,\n        x86_FLOAT_STATE,\n        reinterpret_cast<natural_t*>(&x86_float_state_3264),\n        x86_FLOAT_STATE_COUNT,\n        &x86_thread_state64,\n        &x86_float_state64,\n        &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 10u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 17u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 12u);\n  }\n\n  {\n    x86_debug_state x86_debug_state_3264 = {};\n    x86_debug_state_3264.dsh.flavor = x86_DEBUG_STATE64;\n    x86_debug_state_3264.dsh.count = x86_DEBUG_STATE64_COUNT;\n    x86_debug_state_3264.uds.ds64.__dr0 = 18;\n\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(\n        &cpu_context_x86_64,\n        x86_DEBUG_STATE,\n        reinterpret_cast<natural_t*>(&x86_debug_state_3264),\n        x86_DEBUG_STATE_COUNT,\n        &x86_thread_state64,\n        &x86_float_state64,\n        &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 10u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 18u);\n  }\n\n  // Supply inappropriate “flavor” contexts to test that\n  // InitializeCPUContextX86() detects the problem and refuses to use the\n  // supplied “flavor” context, falling back to the thread, float, and debug\n  // states.\n\n  {\n    x86_thread_state32_t x86_thread_state32 = {};\n\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(\n        &cpu_context_x86_64,\n        x86_THREAD_STATE32,\n        reinterpret_cast<natural_t*>(&x86_thread_state32),\n        x86_THREAD_STATE32_COUNT,\n        &x86_thread_state64,\n        &x86_float_state64,\n        &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 10u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 12u);\n  }\n\n  {\n    x86_thread_state x86_thread_state_3264 = {};\n    x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE32;\n    x86_thread_state_3264.tsh.count = x86_THREAD_STATE32_COUNT;\n\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    internal::InitializeCPUContextX86_64(\n        &cpu_context_x86_64,\n        x86_THREAD_STATE,\n        reinterpret_cast<natural_t*>(&x86_thread_state_3264),\n        x86_THREAD_STATE_COUNT,\n        &x86_thread_state64,\n        &x86_float_state64,\n        &x86_debug_state64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 10u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 12u);\n  }\n}\n\n#endif\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/exception_snapshot_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/exception_snapshot_mac.h\"\n\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"snapshot/mac/cpu_context_mac.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/exception_types.h\"\n#include \"util/mach/symbolic_constants_mach.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nExceptionSnapshotMac::ExceptionSnapshotMac()\n    : ExceptionSnapshot(),\n      context_union_(),\n      context_(),\n      codes_(),\n      thread_id_(0),\n      exception_address_(0),\n      exception_(0),\n      exception_code_0_(0),\n      initialized_() {\n}\n\nExceptionSnapshotMac::~ExceptionSnapshotMac() {\n}\n\nbool ExceptionSnapshotMac::Initialize(ProcessReaderMac* process_reader,\n                                      exception_behavior_t behavior,\n                                      thread_t exception_thread,\n                                      exception_type_t exception,\n                                      const mach_exception_data_type_t* code,\n                                      mach_msg_type_number_t code_count,\n                                      thread_state_flavor_t flavor,\n                                      ConstThreadState state,\n                                      mach_msg_type_number_t state_count) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  codes_.push_back(exception);\n  for (mach_msg_type_number_t code_index = 0;\n       code_index < code_count;\n       ++code_index) {\n    codes_.push_back(code[code_index]);\n  }\n\n  exception_ = exception;\n  mach_exception_code_t exception_code_0 = code[0];\n\n  if (exception_ == EXC_CRASH) {\n    exception_ = ExcCrashRecoverOriginalException(\n        exception_code_0, &exception_code_0, nullptr);\n\n    if (!ExcCrashCouldContainException(exception_)) {\n      LOG(WARNING) << base::StringPrintf(\n          \"exception %s invalid in EXC_CRASH\",\n          ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)\n              .c_str());\n    }\n  }\n\n  // The operations that follow put exception_code_0 (a mach_exception_code_t,\n  // a typedef for int64_t) into exception_code_0_ (a uint32_t). The range\n  // checks and bit shifts involved need the same signedness on both sides to\n  // work properly.\n  const uint64_t unsigned_exception_code_0 = exception_code_0;\n\n  // ExceptionInfo() returns code[0] as a 32-bit value, but exception_code_0 is\n  // a 64-bit value. The best treatment for this inconsistency depends on the\n  // exception type.\n  if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) {\n    // All 64 bits of code[0] are significant for these exceptions. See\n    // <mach/exc_resource.h> for EXC_RESOURCE and 10.10\n    // xnu-2782.1.97/bsd/kern/kern_guarded.c fd_guard_ast() for EXC_GUARD.\n    // code[0] is structured similarly for these two exceptions.\n    //\n    // EXC_RESOURCE: see <kern/exc_resource.h>. The resource type and “flavor”\n    // together define the resource and are in the highest bits. The resource\n    // limit is in the lowest bits.\n    //\n    // EXC_GUARD: see 10.10 xnu-2782.1.97/osfmk/ipc/mach_port.c\n    // mach_port_guard_exception() and xnu-2782.1.97/bsd/kern/kern_guarded.c\n    // fd_guard_ast(). The guard type (GUARD_TYPE_MACH_PORT or GUARD_TYPE_FD)\n    // and “flavor” (from the mach_port_guard_exception_codes or\n    // guard_exception_codes enums) are in the highest bits. The violating Mach\n    // port name or file descriptor number is in the lowest bits.\n\n    // If MACH_EXCEPTION_CODES is not set in |behavior|, code[0] will only carry\n    // 32 significant bits, and the interesting high bits will have been\n    // truncated.\n    if (!ExceptionBehaviorHasMachExceptionCodes(behavior)) {\n      LOG(WARNING) << base::StringPrintf(\n          \"behavior %s invalid for exception %s\",\n          ExceptionBehaviorToString(\n              behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(),\n          ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)\n              .c_str());\n    }\n\n    // Include the more-significant information from the high bits of code[0] in\n    // the value to be returned by ExceptionInfo(). The full value of codes[0]\n    // including the less-significant lower bits is still available via Codes().\n    exception_code_0_ = unsigned_exception_code_0 >> 32;\n  } else {\n    // For other exceptions, code[0]’s values never exceed 32 bits.\n    if (!base::IsValueInRangeForNumericType<decltype(exception_code_0_)>(\n            unsigned_exception_code_0)) {\n      LOG(WARNING) << base::StringPrintf(\"exception_code_0 0x%llx out of range\",\n                                         unsigned_exception_code_0);\n    }\n    exception_code_0_ = unsigned_exception_code_0;\n  }\n\n  const ProcessReaderMac::Thread* thread = nullptr;\n  for (const ProcessReaderMac::Thread& loop_thread :\n       process_reader->Threads()) {\n    if (exception_thread == loop_thread.port) {\n      thread = &loop_thread;\n      break;\n    }\n  }\n\n  if (!thread) {\n    LOG(ERROR) << \"exception_thread not found in task\";\n    return false;\n  }\n\n  thread_id_ = thread->id;\n\n  // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present\n  // in code[1]. It may or may not be the instruction pointer address (usually\n  // it’s not). code[1] may carry the exception address for other exception\n  // types too, but it’s not guaranteed. But for all other exception types, the\n  // instruction pointer will be the exception address, and in fact will be\n  // equal to codes[1] when it’s carrying the exception address. In those cases,\n  // just use the instruction pointer directly.\n  bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS;\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  if (process_reader->Is64Bit()) {\n    context_.architecture = kCPUArchitectureX86_64;\n    context_.x86_64 = &context_union_.x86_64;\n    InitializeCPUContextX86_64(context_.x86_64,\n                               flavor,\n                               state,\n                               state_count,\n                               &thread->thread_context.t64,\n                               &thread->float_context.f64,\n                               &thread->debug_context.d64);\n  } else {\n    context_.architecture = kCPUArchitectureX86;\n    context_.x86 = &context_union_.x86;\n    InitializeCPUContextX86(context_.x86,\n                            flavor,\n                            state,\n                            state_count,\n                            &thread->thread_context.t32,\n                            &thread->float_context.f32,\n                            &thread->debug_context.d32);\n  }\n\n  // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values indicate\n  // that code[1] does not (or may not) carry the exception address:\n  // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for\n  // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE)\n  // which collides with EXC_I386_BOUNDFLT (10.9.5\n  // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS\n  // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c\n  // user_page_fault_continue() and do contain the exception address in code[1].\n  if (exception_ == EXC_BAD_ACCESS &&\n      (exception_code_0_ == EXC_I386_GPFLT ||\n       exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) {\n    code_1_is_exception_address = false;\n  }\n#elif defined(ARCH_CPU_ARM64)\n  context_.architecture = kCPUArchitectureARM64;\n  context_.arm64 = &context_union_.arm64;\n  InitializeCPUContextARM64(context_.arm64,\n                            flavor,\n                            state,\n                            state_count,\n                            &thread->thread_context,\n                            &thread->float_context,\n                            &thread->debug_context);\n#else\n#error Port to your architecture\n#endif\n\n  if (code_1_is_exception_address) {\n    if (process_reader->Is64Bit() &&\n        !ExceptionBehaviorHasMachExceptionCodes(behavior)) {\n      // If code[1] is an address from a 64-bit process, the exception must have\n      // been received with MACH_EXCEPTION_CODES or the address will have been\n      // truncated.\n      LOG(WARNING) << base::StringPrintf(\n          \"behavior %s invalid for exception %s code %d in 64-bit process\",\n          ExceptionBehaviorToString(\n              behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(),\n          ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric)\n              .c_str(),\n          exception_code_0_);\n    }\n    exception_address_ = code[1];\n  } else {\n    exception_address_ = context_.InstructionPointer();\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst CPUContext* ExceptionSnapshotMac::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nuint64_t ExceptionSnapshotMac::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nuint32_t ExceptionSnapshotMac::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_;\n}\n\nuint32_t ExceptionSnapshotMac::ExceptionInfo() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_code_0_;\n}\n\nuint64_t ExceptionSnapshotMac::ExceptionAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_address_;\n}\n\nconst std::vector<uint64_t>& ExceptionSnapshotMac::Codes() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return codes_;\n}\n\nstd::vector<const MemorySnapshot*> ExceptionSnapshotMac::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<const MemorySnapshot*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/exception_snapshot_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_\n#define CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_\n\n#include <mach/mach.h>\n#include <stdint.h>\n\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderMac;\n\nnamespace internal {\n\n//! \\brief An ExceptionSnapshot of an exception sustained by a running (or\n//!     crashed) process on a macOS system.\nclass ExceptionSnapshotMac final : public ExceptionSnapshot {\n public:\n  ExceptionSnapshotMac();\n\n  ExceptionSnapshotMac(const ExceptionSnapshotMac&) = delete;\n  ExceptionSnapshotMac& operator=(const ExceptionSnapshotMac&) = delete;\n\n  ~ExceptionSnapshotMac() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! Other than \\a process_reader, the parameters may be passed directly\n  //! through from a Mach exception handler.\n  //!\n  //! \\param[in] process_reader A ProcessReaderMac for the task that sustained\n  //!     the exception.\n  //! \\param[in] behavior\n  //! \\param[in] exception_thread\n  //! \\param[in] exception\n  //! \\param[in] code\n  //! \\param[in] code_count\n  //! \\param[in,out] flavor\n  //! \\param[in] state\n  //! \\param[in] state_count\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(ProcessReaderMac* process_reader,\n                  exception_behavior_t behavior,\n                  thread_t exception_thread,\n                  exception_type_t exception,\n                  const mach_exception_data_type_t* code,\n                  mach_msg_type_number_t code_count,\n                  thread_state_flavor_t flavor,\n                  ConstThreadState state,\n                  mach_msg_type_number_t state_count);\n\n  // ExceptionSnapshot:\n\n  const CPUContext* Context() const override;\n  uint64_t ThreadID() const override;\n  uint32_t Exception() const override;\n  uint32_t ExceptionInfo() const override;\n  uint64_t ExceptionAddress() const override;\n  const std::vector<uint64_t>& Codes() const override;\n  virtual std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  union {\n#if defined(ARCH_CPU_X86_FAMILY)\n    CPUContextX86 x86;\n    CPUContextX86_64 x86_64;\n#elif defined(ARCH_CPU_ARM64)\n    CPUContextARM64 arm64;\n#else\n#error Port to your CPU architecture\n#endif\n  } context_union_;\n  CPUContext context_;\n  std::vector<uint64_t> codes_;\n  uint64_t thread_id_;\n  uint64_t exception_address_;\n  exception_type_t exception_;\n  uint32_t exception_code_0_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_annotations_reader.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/mach_o_image_annotations_reader.h\"\n\n#include <mach-o/loader.h>\n#include <mach/mach.h>\n#include <sys/types.h>\n\n#include <utility>\n\n#include \"base/logging.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"snapshot/mac/mach_o_image_reader.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"snapshot/snapshot_constants.h\"\n#include \"util/stdlib/strnlen.h\"\n\nnamespace crashpad {\n\nMachOImageAnnotationsReader::MachOImageAnnotationsReader(\n    ProcessReaderMac* process_reader,\n    const MachOImageReader* image_reader,\n    const std::string& name)\n    : name_(name),\n      process_reader_(process_reader),\n      image_reader_(image_reader) {}\n\nstd::vector<std::string> MachOImageAnnotationsReader::Vector() const {\n  std::vector<std::string> vector_annotations;\n\n  ReadCrashReporterClientAnnotations(&vector_annotations);\n  ReadDyldErrorStringAnnotation(&vector_annotations);\n\n  return vector_annotations;\n}\n\nstd::map<std::string, std::string> MachOImageAnnotationsReader::SimpleMap()\n    const {\n  std::map<std::string, std::string> simple_map_annotations;\n\n  ReadCrashpadSimpleAnnotations(&simple_map_annotations);\n\n  return simple_map_annotations;\n}\n\nstd::vector<AnnotationSnapshot> MachOImageAnnotationsReader::AnnotationsList()\n    const {\n  std::vector<AnnotationSnapshot> annotations;\n\n  ReadCrashpadAnnotationsList(&annotations);\n\n  return annotations;\n}\n\nvoid MachOImageAnnotationsReader::ReadCrashReporterClientAnnotations(\n    std::vector<std::string>* vector_annotations) const {\n  mach_vm_address_t crash_info_address;\n  const process_types::section* crash_info_section =\n      image_reader_->GetSectionByName(\n          SEG_DATA, \"__crash_info\", &crash_info_address);\n\n  if (!crash_info_section) {\n    // On macOS 13, under some circumstances, `__crash_info` ends up in the\n    // `__DATA_DIRTY` segment. This is known to happen for `dyld`.\n    crash_info_section = image_reader_->GetSectionByName(\n        \"__DATA_DIRTY\", \"__crash_info\", &crash_info_address);\n  }\n  if (!crash_info_section) {\n    return;\n  }\n\n  process_types::crashreporter_annotations_t crash_info;\n  if (!crash_info.Read(process_reader_, crash_info_address)) {\n    LOG(WARNING) << \"could not read crash info from \" << name_;\n    return;\n  }\n\n  if (crash_info.version != 4 && crash_info.version != 5 &&\n      crash_info.version != 7) {\n    LOG(WARNING) << \"unexpected crash info version \" << crash_info.version\n                 << \" in \" << name_;\n    return;\n  }\n\n  size_t expected_size =\n      process_types::crashreporter_annotations_t::ExpectedSizeForVersion(\n          process_reader_, crash_info.version);\n  if (crash_info_section->size < expected_size) {\n    LOG(WARNING) << \"small crash info section size \" << crash_info_section->size\n                 << \" < \" << expected_size << \" for version \"\n                 << crash_info.version << \" in \" << name_;\n    return;\n  }\n\n  // It seems prudent to enforce some limit. Different users of\n  // CRSetCrashLogMessage and CRSetCrashLogMessage2, apparently the private\n  // <CrashReporterClient.h> functions used to set message and message2, use\n  // different buffer lengths. 15.0 dyld-1231.3 libdyld/dyld_process_info.cpp\n  // has `static char sCrashReporterInfo[4096]`, which seems like a reasonable\n  // limit.\n  constexpr size_t kMaxMessageSize = 4096;\n  if (crash_info.message) {\n    std::string message;\n    if (process_reader_->Memory()->ReadCStringSizeLimited(\n            crash_info.message, kMaxMessageSize, &message)) {\n      vector_annotations->push_back(message);\n    } else {\n      LOG(WARNING) << \"could not read crash message in \" << name_;\n    }\n  }\n\n  if (crash_info.message2) {\n    std::string message;\n    if (process_reader_->Memory()->ReadCStringSizeLimited(\n            crash_info.message2, kMaxMessageSize, &message)) {\n      vector_annotations->push_back(message);\n    } else {\n      LOG(WARNING) << \"could not read crash message 2 in \" << name_;\n    }\n  }\n}\n\nvoid MachOImageAnnotationsReader::ReadDyldErrorStringAnnotation(\n    std::vector<std::string>* vector_annotations) const {\n  // dyld stores its error string at the external symbol for |const char\n  // error_string[1024]|. See 10.9.5 dyld-239.4/src/dyld.cpp error_string.\n  if (image_reader_->FileType() != MH_DYLINKER) {\n    return;\n  }\n\n  mach_vm_address_t error_string_address;\n  if (!image_reader_->LookUpExternalDefinedSymbol(\"_error_string\",\n                                                  &error_string_address)) {\n    return;\n  }\n\n  std::string message;\n  // 1024 here is distinct from kMaxMessageSize above, because it refers to a\n  // precisely-sized buffer inside dyld.\n  if (process_reader_->Memory()->ReadCStringSizeLimited(\n          error_string_address, 1024, &message)) {\n    if (!message.empty()) {\n      vector_annotations->push_back(message);\n    }\n  } else {\n    LOG(WARNING) << \"could not read dylinker error string from \" << name_;\n  }\n}\n\nvoid MachOImageAnnotationsReader::ReadCrashpadSimpleAnnotations(\n    std::map<std::string, std::string>* simple_map_annotations) const {\n  process_types::CrashpadInfo crashpad_info;\n  if (!image_reader_->GetCrashpadInfo(&crashpad_info) ||\n      !crashpad_info.simple_annotations) {\n    return;\n  }\n\n  std::vector<SimpleStringDictionary::Entry>\n      simple_annotations(SimpleStringDictionary::num_entries);\n  if (!process_reader_->Memory()->Read(\n          crashpad_info.simple_annotations,\n          simple_annotations.size() * sizeof(simple_annotations[0]),\n          &simple_annotations[0])) {\n    LOG(WARNING) << \"could not read simple annotations from \" << name_;\n    return;\n  }\n\n  for (const auto& entry : simple_annotations) {\n    size_t key_length = strnlen(entry.key, sizeof(entry.key));\n    if (key_length) {\n      std::string key(entry.key, key_length);\n      std::string value(entry.value, strnlen(entry.value, sizeof(entry.value)));\n      if (!simple_map_annotations->insert(std::make_pair(key, value)).second) {\n        LOG(INFO) << \"duplicate simple annotation \" << key << \" in \" << name_;\n      }\n    }\n  }\n}\n\n// TODO(https://crbug.com/crashpad/270): Replace implementations of\n// ReadCrashpadAnnotationsList and ReadCrashpadSimpleAnnotations with the\n// platform-agnostic implementations in ImageAnnotationReader.\nvoid MachOImageAnnotationsReader::ReadCrashpadAnnotationsList(\n    std::vector<AnnotationSnapshot>* annotations) const {\n  process_types::CrashpadInfo crashpad_info;\n  if (!image_reader_->GetCrashpadInfo(&crashpad_info) ||\n      !crashpad_info.annotations_list) {\n    return;\n  }\n\n  process_types::AnnotationList annotation_list_object;\n  if (!annotation_list_object.Read(process_reader_,\n                                   crashpad_info.annotations_list)) {\n    LOG(WARNING) << \"could not read annotations list object in \" << name_;\n    return;\n  }\n\n  process_types::Annotation current = annotation_list_object.head;\n  for (size_t index = 0;\n       current.link_node != annotation_list_object.tail_pointer &&\n       index < kMaxNumberOfAnnotations;\n       ++index) {\n    if (!current.Read(process_reader_, current.link_node)) {\n      LOG(WARNING) << \"could not read annotation at index \" << index << \" in \"\n                   << name_;\n      return;\n    }\n\n    if (current.size == 0) {\n      continue;\n    }\n\n    AnnotationSnapshot snapshot;\n    snapshot.type = current.type;\n    snapshot.value.resize(current.size);\n\n    if (!process_reader_->Memory()->ReadCStringSizeLimited(\n            current.name, Annotation::kNameMaxLength, &snapshot.name)) {\n      LOG(WARNING) << \"could not read annotation name at index \" << index\n                   << \" in \" << name_;\n      continue;\n    }\n\n    size_t size =\n        std::min(static_cast<size_t>(current.size), Annotation::kValueMaxSize);\n    if (!process_reader_->Memory()->Read(\n            current.value, size, snapshot.value.data())) {\n      LOG(WARNING) << \"could not read annotation value at index \" << index\n                   << \" in \" << name_;\n      continue;\n    }\n\n    annotations->push_back(std::move(snapshot));\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_annotations_reader.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_\n#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"snapshot/annotation_snapshot.h\"\n#include \"snapshot/mac/process_types.h\"\n\nnamespace crashpad {\n\nclass MachOImageReader;\nclass ProcessReaderMac;\n\n//! \\brief A reader for annotations stored in a Mach-O image mapped into another\n//!     process.\n//!\n//! These annotations are stored for the benefit of crash reporters, and provide\n//! information thought to be potentially useful for crash analysis. This class\n//! can decode annotations stored in these formats:\n//!  - CrashpadInfo. This format is used by Crashpad clients. The “simple\n//!    annotations” are recovered from any module with a compatible data\n//!    section, and are included in the annotations returned by SimpleMap().\n//!  - `CrashReporterClient.h`’s `crashreporter_annotations_t`. This format is\n//!    used by Apple code. The `message` and `message2` fields can be recovered\n//!    from any module with a compatible data section, and are included in the\n//!    annotations returned by Vector().\n//!  - dyld’s `error_string`. This format was previously used exclusively by\n//!    dyld, typically for fatal errors. This string can be recovered from any\n//!    `MH_DYLINKER`-type module with this symbol, and is included in the\n//!    annotations returned by Vector(). Newer versions of dyld use\n//!    `crashreporter_annotations_t`.\nclass MachOImageAnnotationsReader {\n public:\n  //! \\brief Constructs an object.\n  //!\n  //! \\param[in] process_reader The reader for the remote process.\n  //! \\param[in] image_reader The MachOImageReader for the Mach-O image file\n  //!     contained within the remote process.\n  //! \\param[in] name The module’s name, a string to be used in logged messages.\n  //!     This string is for diagnostic purposes only, and may be empty.\n  MachOImageAnnotationsReader(ProcessReaderMac* process_reader,\n                              const MachOImageReader* image_reader,\n                              const std::string& name);\n\n  MachOImageAnnotationsReader(const MachOImageAnnotationsReader&) = delete;\n  MachOImageAnnotationsReader& operator=(const MachOImageAnnotationsReader&) =\n      delete;\n\n  ~MachOImageAnnotationsReader() {}\n\n  //! \\brief Returns the module’s annotations that are organized as a vector of\n  //!     strings.\n  std::vector<std::string> Vector() const;\n\n  //! \\brief Returns the module’s annotations that are organized as key-value\n  //!     pairs, where all keys and values are strings.\n  std::map<std::string, std::string> SimpleMap() const;\n\n  //! \\brief Returns the module’s annotations that are organized as a list of\n  //      typed annotation objects.\n  std::vector<AnnotationSnapshot> AnnotationsList() const;\n\n private:\n  // Reads crashreporter_annotations_t::message and\n  // crashreporter_annotations_t::message2 on behalf of Vector().\n  void ReadCrashReporterClientAnnotations(\n      std::vector<std::string>* vector_annotations) const;\n\n  // Reads dyld_error_string on behalf of Vector().\n  void ReadDyldErrorStringAnnotation(\n      std::vector<std::string>* vector_annotations) const;\n\n  // Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap().\n  void ReadCrashpadSimpleAnnotations(\n      std::map<std::string, std::string>* simple_map_annotations) const;\n\n  // Reads CrashpadInfo::annotations_list_ on behalf of AnnotationsList().\n  void ReadCrashpadAnnotationsList(\n      std::vector<AnnotationSnapshot>* vector_annotations) const;\n\n  std::string name_;\n  ProcessReaderMac* process_reader_;  // weak\n  const MachOImageReader* image_reader_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_annotations_reader_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/mach_o_image_annotations_reader.h\"\n\n#include <dlfcn.h>\n#include <mach/mach.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"client/annotation.h\"\n#include \"client/annotation_list.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"test/errors.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"test/mac/mach_multiprocess.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/file_io.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/mach/exc_server_variants.h\"\n#include \"util/mach/exception_ports.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// \\return The path to crashpad_snapshot_test_module_crashy_initializer.so\nbase::FilePath ModuleWithCrashyInitializer() {\n  return TestPaths::BuildArtifact(\"snapshot\",\n                                  \"module_crashy_initializer\",\n                                  TestPaths::FileType::kLoadableModule);\n}\n\n//! \\return The path to the crashpad_snapshot_test_no_op executable.\nbase::FilePath NoOpExecutable() {\n  return TestPaths::BuildArtifact(\n      \"snapshot\", \"no_op\", TestPaths::FileType::kExecutable);\n}\n\nclass TestMachOImageAnnotationsReader final\n    : public MachMultiprocess,\n      public UniversalMachExcServer::Interface {\n public:\n  enum TestType {\n    // Don’t crash, just test the CrashpadInfo interface.\n    kDontCrash = 0,\n\n    // The child process should crash by calling abort(). The parent verifies\n    // that the system libraries set the expected annotations.\n    //\n    // This test verifies that the message field in crashreporter_annotations_t\n    // can be recovered. Either 10.10.2 Libc-1044.1.2/stdlib/FreeBSD/abort.c\n    // abort() or 10.10.2 Libc-1044.10.1/sys/_libc_fork_child.c\n    // _libc_fork_child() calls CRSetCrashLogMessage() to set the message field.\n    kCrashAbort,\n\n    // The child process should crash at module initialization time, when dyld\n    // will have set an annotation matching the path of the module being\n    // initialized.\n    //\n    // This test exists to verify that the message2 field in\n    // crashreporter_annotations_t can be recovered. 10.10.2\n    // dyld-353.2.1/src/ImageLoaderMachO.cpp\n    // ImageLoaderMachO::doInitialization() calls CRSetCrashLogMessage2() to set\n    // the message2 field.\n    kCrashModuleInitialization,\n\n    // The child process should crash by setting DYLD_INSERT_LIBRARIES to\n    // contain a nonexistent library. The parent verifies that dyld sets the\n    // expected annotations.\n    kCrashDyld,\n  };\n\n  explicit TestMachOImageAnnotationsReader(TestType test_type)\n      : MachMultiprocess(),\n        UniversalMachExcServer::Interface(),\n        test_type_(test_type) {\n    switch (test_type_) {\n      case kDontCrash:\n        // SetExpectedChildTermination(kTerminationNormal, EXIT_SUCCESS) is the\n        // default.\n        break;\n\n      case kCrashAbort:\n        SetExpectedChildTermination(kTerminationSignal, SIGABRT);\n        break;\n\n      case kCrashModuleInitialization:\n        SetExpectedChildTerminationBuiltinTrap();\n        break;\n\n      case kCrashDyld:\n        // Prior to 10.12, dyld fatal errors result in the execution of an\n        // int3 instruction on x86 and a trap instruction on ARM, both of\n        // which raise SIGTRAP. 10.9.5 dyld-239.4/src/dyldStartup.s\n        // _dyld_fatal_error. This changed in 10.12 to use\n        // abort_with_payload(), which appears as SIGABRT to a waiting parent.\n        SetExpectedChildTermination(\n            kTerminationSignal,\n            MacOSVersionNumber() < 10'12'00 ? SIGTRAP : SIGABRT);\n        break;\n    }\n  }\n\n  TestMachOImageAnnotationsReader(const TestMachOImageAnnotationsReader&) =\n      delete;\n  TestMachOImageAnnotationsReader& operator=(\n      const TestMachOImageAnnotationsReader&) = delete;\n\n  ~TestMachOImageAnnotationsReader() {}\n\n  // UniversalMachExcServer::Interface:\n  kern_return_t CatchMachException(exception_behavior_t behavior,\n                                   exception_handler_t exception_port,\n                                   thread_t thread,\n                                   task_t task,\n                                   exception_type_t exception,\n                                   const mach_exception_data_type_t* code,\n                                   mach_msg_type_number_t code_count,\n                                   thread_state_flavor_t* flavor,\n                                   ConstThreadState old_state,\n                                   mach_msg_type_number_t old_state_count,\n                                   thread_state_t new_state,\n                                   mach_msg_type_number_t* new_state_count,\n                                   const mach_msg_trailer_t* trailer,\n                                   bool* destroy_complex_request) override {\n    *destroy_complex_request = true;\n\n    if (test_type_ != kCrashDyld) {\n      // In 10.12.1 and later, the task port will not match ChildTask() in the\n      // kCrashDyld case, because kCrashDyld uses execl(), which results in a\n      // new task port being assigned.\n      EXPECT_EQ(task, ChildTask());\n    }\n\n    // The process ID should always compare favorably.\n    pid_t task_pid;\n    kern_return_t kr = pid_for_task(task, &task_pid);\n    EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"pid_for_task\");\n    EXPECT_EQ(task_pid, ChildPID());\n\n    ProcessReaderMac process_reader;\n    bool rv = process_reader.Initialize(task);\n    if (!rv) {\n      ADD_FAILURE();\n    } else {\n      const std::vector<ProcessReaderMac::Module>& modules =\n          process_reader.Modules();\n      std::vector<std::string> all_annotations_vector;\n      for (const ProcessReaderMac::Module& module : modules) {\n        if (module.reader) {\n          MachOImageAnnotationsReader module_annotations_reader(\n              &process_reader, module.reader, module.name);\n          std::vector<std::string> module_annotations_vector =\n              module_annotations_reader.Vector();\n          all_annotations_vector.insert(all_annotations_vector.end(),\n                                        module_annotations_vector.begin(),\n                                        module_annotations_vector.end());\n        } else {\n          EXPECT_TRUE(module.reader);\n        }\n      }\n\n      // Mac OS X 10.6 doesn’t have support for CrashReporter annotations\n      // (CrashReporterClient.h), so don’t look for any special annotations in\n      // that version.\n      const int macos_version_number = MacOSVersionNumber();\n      if (macos_version_number > 10'07'00) {\n        EXPECT_GE(all_annotations_vector.size(), 1u);\n\n        std::string expected_annotation;\n        switch (test_type_) {\n          case kCrashAbort:\n            // The child process calls abort(), so the expected annotation\n            // reflects this, with a string set by 10.7.5\n            // Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still\n            // present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(),\n            // but because abort() tests to see if a message is already set and\n            // something else in Libc will have set a message, this string is\n            // not the expectation on 10.9 or higher. Instead, after fork(), the\n            // child process has a message indicating that a fork() without\n            // exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c\n            // _libc_fork_child().\n            expected_annotation =\n                macos_version_number <= 10'08'00\n                    ? \"abort() called\"\n                    : \"crashed on child side of fork pre-exec\";\n            break;\n\n          case kCrashModuleInitialization:\n            // This message is set by dyld-353.2.1/src/ImageLoaderMachO.cpp\n            // ImageLoaderMachO::doInitialization().\n            // dyld4 no longer sets this, so instead check for fork() without\n            // exec() like above.\n            expected_annotation =\n                macos_version_number < 12'00'00\n                    ? ModuleWithCrashyInitializer().value()\n                    : \"crashed on child side of fork pre-exec\";\n            break;\n\n          case kCrashDyld:\n            // This is independent of dyld’s error_string, which is tested\n            // below. dyld4 no longer sets this.\n            expected_annotation =\n                macos_version_number < 12'00'00\n                    ? \"dyld: launch, loading dependent libraries\"\n                    : \"\";\n            break;\n\n          default:\n            ADD_FAILURE();\n            break;\n        }\n\n        bool found = false;\n        for (const std::string& annotation : all_annotations_vector) {\n          // Look for the expectation as a leading susbtring, because the actual\n          // string that dyld uses will have the contents of the\n          // DYLD_INSERT_LIBRARIES environment variable appended to it on OS X\n          // 10.10.\n          if (annotation.substr(0, expected_annotation.length()) ==\n                  expected_annotation) {\n            found = true;\n            break;\n          }\n        }\n        EXPECT_TRUE(found) << expected_annotation;\n      }\n\n      // dyld exposes its error_string at least as far back as Mac OS X 10.4.\n      if (test_type_ == kCrashDyld) {\n        std::string couldnt_load_annotation =\n            macos_version_number < 12'00'00\n                ? \"could not load inserted library\"\n                // dyld4 no longer writes an annotation for the primary error\n                // See https://crbug.com/1334418/#c26\n                : \"tried: '/var/empty/NoDirectory/NoLibrary' (no such file)\";\n        bool found = false;\n        for (const std::string& annotation : all_annotations_vector) {\n          // Look for the expectation as a substring, because the actual\n          // string will contain the library’s pathname and a reason, or on\n          // macOS 12, only the reason.\n          if (annotation.find(couldnt_load_annotation) != std::string::npos) {\n            found = true;\n            break;\n          }\n        }\n\n        EXPECT_TRUE(found) << couldnt_load_annotation;\n      }\n    }\n\n    ExcServerCopyState(\n        behavior, old_state, old_state_count, new_state, new_state_count);\n    return ExcServerSuccessfulReturnValue(exception, behavior, false);\n  }\n\n private:\n  // MachMultiprocess:\n\n  void MachMultiprocessParent() override {\n    ProcessReaderMac process_reader;\n    ASSERT_TRUE(process_reader.Initialize(ChildTask()));\n\n    // Wait for the child process to indicate that it’s done setting up its\n    // annotations via the CrashpadInfo interface.\n    char c;\n    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));\n\n    // Verify the “simple map” and object-based annotations set via the\n    // CrashpadInfo interface.\n    const std::vector<ProcessReaderMac::Module>& modules =\n        process_reader.Modules();\n    std::map<std::string, std::string> all_annotations_simple_map;\n    std::vector<AnnotationSnapshot> all_annotations;\n    for (const ProcessReaderMac::Module& module : modules) {\n      MachOImageAnnotationsReader module_annotations_reader(\n          &process_reader, module.reader, module.name);\n      std::map<std::string, std::string> module_annotations_simple_map =\n          module_annotations_reader.SimpleMap();\n      all_annotations_simple_map.insert(module_annotations_simple_map.begin(),\n                                        module_annotations_simple_map.end());\n\n      std::vector<AnnotationSnapshot> annotations =\n          module_annotations_reader.AnnotationsList();\n      all_annotations.insert(\n          all_annotations.end(), annotations.begin(), annotations.end());\n    }\n\n    EXPECT_GE(all_annotations_simple_map.size(), 5u);\n    EXPECT_EQ(all_annotations_simple_map[\"#TEST# pad\"], \"crash\");\n    EXPECT_EQ(all_annotations_simple_map[\"#TEST# key\"], \"value\");\n    EXPECT_EQ(all_annotations_simple_map[\"#TEST# x\"], \"y\");\n    EXPECT_EQ(all_annotations_simple_map[\"#TEST# longer\"], \"shorter\");\n    EXPECT_EQ(all_annotations_simple_map[\"#TEST# empty_value\"], \"\");\n\n    EXPECT_EQ(all_annotations.size(), 3u);\n    bool saw_same_name_3 = false, saw_same_name_4 = false;\n    for (const auto& annotation : all_annotations) {\n      EXPECT_EQ(annotation.type,\n                static_cast<uint16_t>(Annotation::Type::kString));\n      std::string value(reinterpret_cast<const char*>(annotation.value.data()),\n                        annotation.value.size());\n\n      if (annotation.name == \"#TEST# one\") {\n        EXPECT_EQ(value, \"moocow\");\n      } else if (annotation.name == \"#TEST# same-name\") {\n        if (value == \"same-name 3\") {\n          EXPECT_FALSE(saw_same_name_3);\n          saw_same_name_3 = true;\n        } else if (value == \"same-name 4\") {\n          EXPECT_FALSE(saw_same_name_4);\n          saw_same_name_4 = true;\n        } else {\n          ADD_FAILURE() << \"unexpected annotation value \" << value;\n        }\n      } else {\n        ADD_FAILURE() << \"unexpected annotation \" << annotation.name;\n      }\n    }\n\n    // Tell the child process that it’s permitted to crash.\n    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));\n\n    if (test_type_ != kDontCrash) {\n      // Handle the child’s crash. Further validation will be done in\n      // CatchMachException().\n      UniversalMachExcServer universal_mach_exc_server(this);\n\n      mach_msg_return_t mr =\n          MachMessageServer::Run(&universal_mach_exc_server,\n                                 LocalPort(),\n                                 MACH_MSG_OPTION_NONE,\n                                 MachMessageServer::kOneShot,\n                                 MachMessageServer::kReceiveLargeError,\n                                 kMachMessageTimeoutWaitIndefinitely);\n      EXPECT_EQ(mr, MACH_MSG_SUCCESS)\n          << MachErrorMessage(mr, \"MachMessageServer::Run\");\n    }\n  }\n\n  void MachMultiprocessChild() override {\n    CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();\n\n    // This is “leaked” to crashpad_info.\n    SimpleStringDictionary* simple_annotations = new SimpleStringDictionary();\n    simple_annotations->SetKeyValue(\"#TEST# pad\", \"break\");\n    simple_annotations->SetKeyValue(\"#TEST# key\", \"value\");\n    simple_annotations->SetKeyValue(\"#TEST# pad\", \"crash\");\n    simple_annotations->SetKeyValue(\"#TEST# x\", \"y\");\n    simple_annotations->SetKeyValue(\"#TEST# longer\", \"shorter\");\n    simple_annotations->SetKeyValue(\"#TEST# empty_value\", \"\");\n\n    crashpad_info->set_simple_annotations(simple_annotations);\n\n    AnnotationList::Register();  // This is “leaked” to crashpad_info.\n\n    static StringAnnotation<32> test_annotation_one{\"#TEST# one\"};\n    static StringAnnotation<32> test_annotation_two{\"#TEST# two\"};\n    static StringAnnotation<32> test_annotation_three{\"#TEST# same-name\"};\n    static StringAnnotation<32> test_annotation_four{\"#TEST# same-name\"};\n\n    test_annotation_one.Set(\"moocow\");\n    test_annotation_two.Set(\"this will be cleared\");\n    test_annotation_three.Set(\"same-name 3\");\n    test_annotation_four.Set(\"same-name 4\");\n    test_annotation_two.Clear();\n\n    // Tell the parent that the environment has been set up.\n    char c = '\\0';\n    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));\n\n    // Wait for the parent to indicate that it’s safe to crash.\n    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));\n\n    // Direct an exception message to the exception server running in the\n    // parent.\n    ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask,\n                                   mach_task_self());\n    ASSERT_TRUE(exception_ports.SetExceptionPort(\n        EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE));\n\n    switch (test_type_) {\n      case kDontCrash: {\n        break;\n      }\n\n      case kCrashAbort: {\n        abort();\n      }\n\n      case kCrashModuleInitialization: {\n        // Load a module that crashes while executing a module initializer.\n        void* dl_handle = dlopen(ModuleWithCrashyInitializer().value().c_str(),\n                                 RTLD_LAZY | RTLD_LOCAL);\n\n        // This should have crashed in the dlopen(). If dlopen() failed, the\n        // ASSERT_NE() will show the message. If it succeeded without crashing,\n        // the FAIL() will fail the test.\n        ASSERT_NE(dl_handle, nullptr) << dlerror();\n        FAIL();\n      }\n\n      case kCrashDyld: {\n        // Set DYLD_INSERT_LIBRARIES to contain a library that does not exist.\n        // Unable to load it, dyld will abort with a fatal error.\n        ASSERT_EQ(\n            setenv(\n                \"DYLD_INSERT_LIBRARIES\", \"/var/empty/NoDirectory/NoLibrary\", 1),\n            0)\n            << ErrnoMessage(\"setenv\");\n\n        // The actual executable doesn’t matter very much, because dyld won’t\n        // ever launch it. It just needs to be an executable that uses dyld as\n        // its LC_LOAD_DYLINKER (all normal executables do). A custom no-op\n        // executable is provided because DYLD_INSERT_LIBRARIES does not work\n        // with system executables on OS X 10.11 due to System Integrity\n        // Protection.\n        base::FilePath no_op_executable = NoOpExecutable();\n        ASSERT_EQ(execl(no_op_executable.value().c_str(),\n                        no_op_executable.BaseName().value().c_str(),\n                        nullptr),\n                  0)\n            << ErrnoMessage(\"execl\");\n        break;\n      }\n\n      default:\n        break;\n    }\n  }\n\n  TestType test_type_;\n};\n\nTEST(MachOImageAnnotationsReader, DontCrash) {\n  TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(\n      TestMachOImageAnnotationsReader::kDontCrash);\n  test_mach_o_image_annotations_reader.Run();\n}\n\nTEST(MachOImageAnnotationsReader, CrashAbort) {\n  TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(\n      TestMachOImageAnnotationsReader::kCrashAbort);\n  test_mach_o_image_annotations_reader.Run();\n}\n\nTEST(MachOImageAnnotationsReader, CrashModuleInitialization) {\n  TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(\n      TestMachOImageAnnotationsReader::kCrashModuleInitialization);\n  test_mach_o_image_annotations_reader.Run();\n}\n\nTEST(MachOImageAnnotationsReader, CrashDyld) {\n  TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(\n      TestMachOImageAnnotationsReader::kCrashDyld);\n  test_mach_o_image_annotations_reader.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass CrashyClass {\n public:\n  CrashyClass() {\n    __builtin_trap();\n  }\n};\n\n// __attribute__((used)) keeps the dead code stripper away.\n__attribute__((used)) CrashyClass g_crashy_object;\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_annotations_reader_test_no_op.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdlib.h>\n\nint main(int argc, char* argv[]) {\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_reader.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/mach_o_image_reader.h\"\n\n#include <mach-o/loader.h>\n#include <mach-o/nlist.h>\n#include <string.h>\n\n#include <iterator>\n#include <limits>\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"client/crashpad_info.h\"\n#include \"snapshot/mac/mach_o_image_segment_reader.h\"\n#include \"snapshot/mac/mach_o_image_symbol_table_reader.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"util/mac/checked_mach_address_range.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace {\n\nconstexpr uint32_t kInvalidSegmentIndex = std::numeric_limits<uint32_t>::max();\n\n}  // namespace\n\nnamespace crashpad {\n\nMachOImageReader::MachOImageReader()\n    : segments_(),\n      segment_map_(),\n      module_name_(),\n      module_info_(),\n      dylinker_name_(),\n      uuid_(),\n      address_(0),\n      size_(0),\n      slide_(0),\n      source_version_(0),\n      symtab_command_(),\n      dysymtab_command_(),\n      symbol_table_(),\n      id_dylib_command_(),\n      process_reader_(nullptr),\n      file_type_(0),\n      initialized_(),\n      symbol_table_initialized_() {\n}\n\nMachOImageReader::~MachOImageReader() {\n}\n\nbool MachOImageReader::Initialize(ProcessReaderMac* process_reader,\n                                  mach_vm_address_t address,\n                                  const std::string& name) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  process_reader_ = process_reader;\n  address_ = address;\n  module_name_ = name;\n\n  module_info_ =\n      base::StringPrintf(\", module %s, address 0x%llx\", name.c_str(), address);\n\n  process_types::mach_header mach_header;\n  if (!mach_header.Read(process_reader, address)) {\n    LOG(WARNING) << \"could not read mach_header\" << module_info_;\n    return false;\n  }\n\n  const bool is_64_bit = process_reader->Is64Bit();\n  const uint32_t kExpectedMagic = is_64_bit ? MH_MAGIC_64 : MH_MAGIC;\n  if (mach_header.magic != kExpectedMagic) {\n    LOG(WARNING) << base::StringPrintf(\"unexpected mach_header::magic 0x%08x\",\n                                       mach_header.magic) << module_info_;\n    return false;\n  }\n\n  switch (mach_header.filetype) {\n    case MH_EXECUTE:\n    case MH_DYLIB:\n    case MH_DYLINKER:\n    case MH_BUNDLE:\n      file_type_ = mach_header.filetype;\n      break;\n    default:\n      LOG(WARNING) << base::StringPrintf(\n                          \"unexpected mach_header::filetype 0x%08x\",\n                          mach_header.filetype) << module_info_;\n      return false;\n  }\n\n  const uint32_t kExpectedSegmentCommand =\n      is_64_bit ? LC_SEGMENT_64 : LC_SEGMENT;\n  const uint32_t kUnexpectedSegmentCommand =\n      is_64_bit ? LC_SEGMENT : LC_SEGMENT_64;\n\n  const struct {\n    // Which method to call when encountering a load command matching |command|.\n    bool (MachOImageReader::*function)(mach_vm_address_t, const std::string&);\n\n    // The minimum size that may be allotted to store the load command.\n    size_t size;\n\n    // The load command to match.\n    uint32_t command;\n\n    // True if the load command must not appear more than one time.\n    bool singleton;\n  } kLoadCommandReaders[] = {\n    {\n      &MachOImageReader::ReadSegmentCommand,\n      process_types::segment_command::ExpectedSize(process_reader),\n      kExpectedSegmentCommand,\n      false,\n    },\n    {\n      &MachOImageReader::ReadSymTabCommand,\n      process_types::symtab_command::ExpectedSize(process_reader),\n      LC_SYMTAB,\n      true,\n    },\n    {\n      &MachOImageReader::ReadDySymTabCommand,\n      process_types::symtab_command::ExpectedSize(process_reader),\n      LC_DYSYMTAB,\n      true,\n    },\n    {\n      &MachOImageReader::ReadIdDylibCommand,\n      process_types::dylib_command::ExpectedSize(process_reader),\n      LC_ID_DYLIB,\n      true,\n    },\n    {\n      &MachOImageReader::ReadDylinkerCommand,\n      process_types::dylinker_command::ExpectedSize(process_reader),\n      LC_LOAD_DYLINKER,\n      true,\n    },\n    {\n      &MachOImageReader::ReadDylinkerCommand,\n      process_types::dylinker_command::ExpectedSize(process_reader),\n      LC_ID_DYLINKER,\n      true,\n    },\n    {\n      &MachOImageReader::ReadUUIDCommand,\n      process_types::uuid_command::ExpectedSize(process_reader),\n      LC_UUID,\n      true,\n    },\n    {\n      &MachOImageReader::ReadSourceVersionCommand,\n      process_types::source_version_command::ExpectedSize(process_reader),\n      LC_SOURCE_VERSION,\n      true,\n    },\n\n    // When reading a 64-bit process, no 32-bit segment commands should be\n    // present, and vice-versa.\n    {\n      &MachOImageReader::ReadUnexpectedCommand,\n      process_types::load_command::ExpectedSize(process_reader),\n      kUnexpectedSegmentCommand,\n      false,\n    },\n  };\n\n  // This vector is parallel to the kLoadCommandReaders array, and tracks\n  // whether a singleton load command matching the |command| field has been\n  // found yet.\n  std::vector<uint32_t> singleton_indices(std::size(kLoadCommandReaders),\n                                          kInvalidSegmentIndex);\n\n  size_t offset = mach_header.Size();\n  const mach_vm_address_t kLoadCommandAddressLimit =\n      address + offset + mach_header.sizeofcmds;\n\n  for (uint32_t load_command_index = 0;\n       load_command_index < mach_header.ncmds;\n       ++load_command_index) {\n    mach_vm_address_t load_command_address = address + offset;\n    std::string load_command_info = base::StringPrintf(\", load command %u/%u%s\",\n                                                       load_command_index,\n                                                       mach_header.ncmds,\n                                                       module_info_.c_str());\n\n    process_types::load_command load_command;\n\n    // Make sure that the basic load command structure doesn’t overflow the\n    // space allotted for load commands.\n    if (load_command_address + load_command.ExpectedSize(process_reader) >\n            kLoadCommandAddressLimit) {\n      LOG(WARNING) << base::StringPrintf(\n                          \"load_command at 0x%llx exceeds sizeofcmds 0x%x\",\n                          load_command_address,\n                          mach_header.sizeofcmds) << load_command_info;\n      return false;\n    }\n\n    if (!load_command.Read(process_reader, load_command_address)) {\n      LOG(WARNING) << \"could not read load_command\" << load_command_info;\n      return false;\n    }\n\n    load_command_info = base::StringPrintf(\", load command 0x%x %u/%u%s\",\n                                           load_command.cmd,\n                                           load_command_index,\n                                           mach_header.ncmds,\n                                           module_info_.c_str());\n\n    // Now that the load command’s stated size is known, make sure that it\n    // doesn’t overflow the space allotted for load commands.\n    if (load_command_address + load_command.cmdsize >\n            kLoadCommandAddressLimit) {\n      LOG(WARNING)\n          << base::StringPrintf(\n                 \"load_command at 0x%llx cmdsize 0x%x exceeds sizeofcmds 0x%x\",\n                 load_command_address,\n                 load_command.cmdsize,\n                 mach_header.sizeofcmds) << load_command_info;\n      return false;\n    }\n\n    for (size_t reader_index = 0; reader_index < std::size(kLoadCommandReaders);\n         ++reader_index) {\n      if (load_command.cmd != kLoadCommandReaders[reader_index].command) {\n        continue;\n      }\n\n      if (load_command.cmdsize < kLoadCommandReaders[reader_index].size) {\n        LOG(WARNING) << base::StringPrintf(\n                            \"load command cmdsize 0x%x insufficient for 0x%zx\",\n                            load_command.cmdsize,\n                            kLoadCommandReaders[reader_index].size)\n                     << load_command_info;\n        return false;\n      }\n\n      if (kLoadCommandReaders[reader_index].singleton) {\n        if (singleton_indices[reader_index] != kInvalidSegmentIndex) {\n          LOG(WARNING) << \"duplicate load command at \"\n                       << singleton_indices[reader_index] << load_command_info;\n          return false;\n        }\n\n        singleton_indices[reader_index] = load_command_index;\n      }\n\n      if (!((this)->*(kLoadCommandReaders[reader_index].function))(\n              load_command_address, load_command_info)) {\n        return false;\n      }\n\n      break;\n    }\n\n    offset += load_command.cmdsize;\n  }\n\n  // Now that the slide is known, push it into the segments.\n  for (const auto& segment : segments_) {\n    segment->SetSlide(slide_);\n\n    // This was already checked for the unslid values while the segments were\n    // read, but now it’s possible to check the slid values too. The individual\n    // sections don’t need to be checked because they were verified to be\n    // contained within their respective segments when the segments were read.\n    mach_vm_address_t slid_segment_address = segment->Address();\n    mach_vm_size_t slid_segment_size = segment->Size();\n    CheckedMachAddressRange slid_segment_range(\n        process_reader_->Is64Bit(), slid_segment_address, slid_segment_size);\n    if (!slid_segment_range.IsValid()) {\n      LOG(WARNING) << base::StringPrintf(\n                          \"invalid slid segment range 0x%llx + 0x%llx, \"\n                          \"segment \",\n                          slid_segment_address,\n                          slid_segment_size) << segment->Name() << module_info_;\n      return false;\n    }\n  }\n\n  if (!segment_map_.count(SEG_TEXT)) {\n    // The __TEXT segment is required. Even a module with no executable code\n    // will have a __TEXT segment encompassing the Mach-O header and load\n    // commands. Without a __TEXT segment, |size_| will not have been computed.\n    LOG(WARNING) << \"no \" SEG_TEXT \" segment\" << module_info_;\n    return false;\n  }\n\n  if (mach_header.filetype == MH_DYLIB && !id_dylib_command_) {\n    // This doesn’t render a module unusable, it’s just weird and worth noting.\n    LOG(INFO) << \"no LC_ID_DYLIB\" << module_info_;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst MachOImageSegmentReader* MachOImageReader::GetSegmentByName(\n    const std::string& segment_name) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  const auto& iterator = segment_map_.find(segment_name);\n  if (iterator == segment_map_.end()) {\n    return nullptr;\n  }\n\n  return segments_[iterator->second].get();\n}\n\nconst process_types::section* MachOImageReader::GetSectionByName(\n    const std::string& segment_name,\n    const std::string& section_name,\n    mach_vm_address_t* address) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  const MachOImageSegmentReader* segment = GetSegmentByName(segment_name);\n  if (!segment) {\n    return nullptr;\n  }\n\n  return segment->GetSectionByName(section_name, address);\n}\n\nconst process_types::section* MachOImageReader::GetSectionAtIndex(\n    size_t index,\n    const MachOImageSegmentReader** containing_segment,\n    mach_vm_address_t* address) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  static_assert(NO_SECT == 0, \"NO_SECT must be zero\");\n  if (index == NO_SECT) {\n    LOG(WARNING) << \"section index \" << index << \" out of range\";\n    return nullptr;\n  }\n\n  // Switch to a more comfortable 0-based index.\n  size_t local_index = index - 1;\n\n  for (const auto& segment : segments_) {\n    size_t nsects = segment->nsects();\n    if (local_index < nsects) {\n      const process_types::section* section =\n          segment->GetSectionAtIndex(local_index, address);\n\n      if (containing_segment) {\n        *containing_segment = segment.get();\n      }\n\n      return section;\n    }\n\n    local_index -= nsects;\n  }\n\n  LOG(WARNING) << \"section index \" << index << \" out of range\";\n  return nullptr;\n}\n\nbool MachOImageReader::LookUpExternalDefinedSymbol(\n    const std::string& name,\n    mach_vm_address_t* value) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (symbol_table_initialized_.is_uninitialized()) {\n    InitializeSymbolTable();\n  }\n\n  if (!symbol_table_initialized_.is_valid() || !symbol_table_) {\n    return false;\n  }\n\n  const MachOImageSymbolTableReader::SymbolInformation* symbol_info =\n      symbol_table_->LookUpExternalDefinedSymbol(name);\n  if (!symbol_info) {\n    return false;\n  }\n\n  if (symbol_info->section == NO_SECT) {\n    // This is an absolute (N_ABS) symbol, which requires no further validation\n    // or processing.\n    *value = symbol_info->value;\n    return true;\n  }\n\n  // This is a symbol defined in a particular section, so make sure that it’s\n  // valid for that section and fix it up for any “slide” as needed.\n\n  mach_vm_address_t section_address;\n  const MachOImageSegmentReader* segment;\n  const process_types::section* section =\n      GetSectionAtIndex(symbol_info->section, &segment, &section_address);\n  if (!section) {\n    return false;\n  }\n\n  mach_vm_address_t slid_value =\n      symbol_info->value + (segment->SegmentSlides() ? slide_ : 0);\n\n  // The __mh_execute_header (_MH_EXECUTE_SYM) symbol is weird. In\n  // position-independent executables, it shows up in the symbol table as a\n  // symbol in section 1, although it’s not really in that section. It points to\n  // the mach_header[_64], which is the beginning of the __TEXT segment, and the\n  // __text section normally begins after the load commands in the __TEXT\n  // segment. The range check below will fail for this symbol, because it’s not\n  // really in the section it claims to be in. See Xcode 5.1\n  // ld64-236.3/src/ld/OutputFile.cpp ld::tool::OutputFile::buildSymbolTable().\n  // There, ld takes symbols that refer to anything in the mach_header[_64] and\n  // marks them as being in section 1. Here, section 1 is treated in this same\n  // special way as long as it’s in the __TEXT segment that begins at the start\n  // of the image, which is normally the case, and as long as the symbol’s value\n  // is the base of the image.\n  //\n  // This only happens for PIE executables, because __mh_execute_header needs\n  // to slide. In non-PIE executables, __mh_execute_header is an absolute\n  // symbol.\n  CheckedMachAddressRange section_range(\n      process_reader_->Is64Bit(), section_address, section->size);\n  if (!section_range.ContainsValue(slid_value) &&\n      !(symbol_info->section == 1 && segment->Name() == SEG_TEXT &&\n        slid_value == Address())) {\n    std::string section_name_full =\n        MachOImageSegmentReader::SegmentAndSectionNameString(section->segname,\n                                                             section->sectname);\n    LOG(WARNING) << base::StringPrintf(\n                        \"symbol %s (0x%llx) outside of section %s (0x%llx + \"\n                        \"0x%llx)\",\n                        name.c_str(),\n                        slid_value,\n                        section_name_full.c_str(),\n                        section_address,\n                        section->size) << module_info_;\n    return false;\n  }\n\n  *value = slid_value;\n  return true;\n}\n\nuint32_t MachOImageReader::DylibVersion() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  DCHECK_EQ(FileType(), implicit_cast<uint32_t>(MH_DYLIB));\n\n  if (id_dylib_command_) {\n    return id_dylib_command_->dylib_current_version;\n  }\n\n  // In case this was a weird dylib without an LC_ID_DYLIB command.\n  return 0;\n}\n\nvoid MachOImageReader::UUID(crashpad::UUID* uuid) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  memcpy(uuid, &uuid_, sizeof(uuid_));\n}\n\nbool MachOImageReader::GetCrashpadInfo(\n    process_types::CrashpadInfo* crashpad_info) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  mach_vm_address_t crashpad_info_address;\n  const process_types::section* crashpad_info_section =\n      GetSectionByName(SEG_DATA, \"crashpad_info\", &crashpad_info_address);\n  if (!crashpad_info_section) {\n    return false;\n  }\n\n  if (crashpad_info_section->size <\n      crashpad_info->MinimumSize(process_reader_)) {\n    LOG(WARNING) << \"small crashpad info section size \"\n                 << crashpad_info_section->size << module_info_;\n    return false;\n  }\n\n  // This Read() will zero out anything beyond the structure’s declared size.\n  if (!crashpad_info->Read(process_reader_, crashpad_info_address)) {\n    LOG(WARNING) << \"could not read crashpad info\" << module_info_;\n    return false;\n  }\n\n  if (crashpad_info->signature != CrashpadInfo::kSignature ||\n      crashpad_info->version != 1) {\n    LOG(WARNING) << base::StringPrintf(\n        \"unexpected crashpad info signature 0x%x, version %u%s\",\n        crashpad_info->signature,\n        crashpad_info->version,\n        module_info_.c_str());\n    return false;\n  }\n\n  // Don’t require strict equality, to leave wiggle room for sloppy linkers.\n  if (crashpad_info->size > crashpad_info_section->size) {\n    LOG(WARNING) << \"crashpad info struct size \" << crashpad_info->size\n                 << \" large for section size \" << crashpad_info_section->size\n                 << module_info_;\n    return false;\n  }\n\n  if (crashpad_info->size > crashpad_info->ExpectedSize(process_reader_)) {\n    // This isn’t strictly a problem, because unknown fields will simply be\n    // ignored, but it may be of diagnostic interest.\n    LOG(INFO) << \"large crashpad info size \" << crashpad_info->size\n              << module_info_;\n  }\n\n  return true;\n}\n\ntemplate <typename T>\nbool MachOImageReader::ReadLoadCommand(mach_vm_address_t load_command_address,\n                                       const std::string& load_command_info,\n                                       uint32_t expected_load_command_id,\n                                       T* load_command) {\n  if (!load_command->Read(process_reader_, load_command_address)) {\n    LOG(WARNING) << \"could not read load command\" << load_command_info;\n    return false;\n  }\n\n  DCHECK_GE(load_command->cmdsize, load_command->Size());\n  DCHECK_EQ(load_command->cmd, expected_load_command_id);\n  return true;\n}\n\nbool MachOImageReader::ReadSegmentCommand(\n    mach_vm_address_t load_command_address,\n    const std::string& load_command_info) {\n  size_t segment_index = segments_.size();\n  segments_.push_back(std::make_unique<MachOImageSegmentReader>());\n  MachOImageSegmentReader* segment = segments_.back().get();\n\n  if (!segment->Initialize(process_reader_,\n                           load_command_address,\n                           load_command_info,\n                           module_name_,\n                           file_type_)) {\n    segments_.pop_back();\n    return false;\n  }\n\n  // At this point, the segment itself is considered valid, but if one of the\n  // next checks fails, it will render the module invalid. If any of the next\n  // checks fail, this method should return false, but it doesn’t need to bother\n  // removing the segment from segments_. The segment will be properly released\n  // when the image is destroyed, and the image won’t be usable because\n  // initialization won’t have completed. Most importantly, leaving the segment\n  // in segments_ means that no other structures (such as perhaps segment_map_)\n  // become inconsistent or require cleanup.\n\n  const std::string segment_name = segment->Name();\n  const auto insert_result =\n      segment_map_.insert(std::make_pair(segment_name, segment_index));\n  if (!insert_result.second) {\n    LOG(WARNING) << base::StringPrintf(\"duplicate %s segment at %zu and %zu\",\n                                       segment_name.c_str(),\n                                       insert_result.first->second,\n                                       segment_index) << load_command_info;\n    return false;\n  }\n\n  if (segment_name == SEG_TEXT) {\n    mach_vm_size_t vmsize = segment->vmsize();\n\n    if (vmsize == 0) {\n      LOG(WARNING) << \"zero-sized \" SEG_TEXT \" segment\" << load_command_info;\n      return false;\n    }\n\n    size_ = vmsize;\n\n    // The slide is computed as the difference between the __TEXT segment’s\n    // preferred and actual load addresses. This is the same way that dyld\n    // computes slide. See 10.9.2 dyld-239.4/src/dyldInitialization.cpp\n    // slideOfMainExecutable().\n    slide_ = address_ - segment->vmaddr();\n  }\n\n  return true;\n}\n\nbool MachOImageReader::ReadSymTabCommand(mach_vm_address_t load_command_address,\n                                         const std::string& load_command_info) {\n  symtab_command_.reset(new process_types::symtab_command());\n  return ReadLoadCommand(load_command_address,\n                         load_command_info,\n                         LC_SYMTAB,\n                         symtab_command_.get());\n}\n\nbool MachOImageReader::ReadDySymTabCommand(\n    mach_vm_address_t load_command_address,\n    const std::string& load_command_info) {\n  dysymtab_command_.reset(new process_types::dysymtab_command());\n  return ReadLoadCommand(load_command_address,\n                         load_command_info,\n                         LC_DYSYMTAB,\n                         dysymtab_command_.get());\n}\n\nbool MachOImageReader::ReadIdDylibCommand(\n    mach_vm_address_t load_command_address,\n    const std::string& load_command_info) {\n  if (file_type_ != MH_DYLIB) {\n    LOG(WARNING) << base::StringPrintf(\n                        \"LC_ID_DYLIB inappropriate in non-dylib file type 0x%x\",\n                        file_type_) << load_command_info;\n    return false;\n  }\n\n  DCHECK(!id_dylib_command_);\n  id_dylib_command_.reset(new process_types::dylib_command());\n  return ReadLoadCommand(load_command_address,\n                         load_command_info,\n                         LC_ID_DYLIB,\n                         id_dylib_command_.get());\n}\n\nbool MachOImageReader::ReadDylinkerCommand(\n    mach_vm_address_t load_command_address,\n    const std::string& load_command_info) {\n  if (file_type_ != MH_EXECUTE && file_type_ != MH_DYLINKER) {\n    LOG(WARNING) << base::StringPrintf(\n                        \"LC_LOAD_DYLINKER/LC_ID_DYLINKER inappropriate in file \"\n                        \"type 0x%x\",\n                        file_type_) << load_command_info;\n    return false;\n  }\n\n  const uint32_t kExpectedCommand =\n      file_type_ == MH_DYLINKER ? LC_ID_DYLINKER : LC_LOAD_DYLINKER;\n  process_types::dylinker_command dylinker_command;\n  if (!ReadLoadCommand(load_command_address,\n                       load_command_info,\n                       kExpectedCommand,\n                       &dylinker_command)) {\n    return false;\n  }\n\n  if (!process_reader_->Memory()->ReadCStringSizeLimited(\n          load_command_address + dylinker_command.name,\n          dylinker_command.cmdsize - dylinker_command.name,\n          &dylinker_name_)) {\n    LOG(WARNING) << \"could not read dylinker_command name\" << load_command_info;\n    return false;\n  }\n\n  return true;\n}\n\nbool MachOImageReader::ReadUUIDCommand(mach_vm_address_t load_command_address,\n                                       const std::string& load_command_info) {\n  process_types::uuid_command uuid_command;\n  if (!ReadLoadCommand(\n          load_command_address, load_command_info, LC_UUID, &uuid_command)) {\n    return false;\n  }\n\n  uuid_.InitializeFromBytes(uuid_command.uuid);\n  return true;\n}\n\nbool MachOImageReader::ReadSourceVersionCommand(\n    mach_vm_address_t load_command_address,\n    const std::string& load_command_info) {\n  process_types::source_version_command source_version_command;\n  if (!ReadLoadCommand(load_command_address,\n                       load_command_info,\n                       LC_SOURCE_VERSION,\n                       &source_version_command)) {\n    return false;\n  }\n\n  source_version_ = source_version_command.version;\n  return true;\n}\n\nbool MachOImageReader::ReadUnexpectedCommand(\n    mach_vm_address_t load_command_address,\n    const std::string& load_command_info) {\n  LOG(WARNING) << \"unexpected load command\" << load_command_info;\n  return false;\n}\n\nvoid MachOImageReader::InitializeSymbolTable() const {\n  DCHECK(symbol_table_initialized_.is_uninitialized());\n  symbol_table_initialized_.set_invalid();\n\n  if (!symtab_command_) {\n    // It’s technically valid for there to be no LC_SYMTAB, and in that case,\n    // any symbol lookups should fail. Mark the symbol table as valid, and\n    // LookUpExternalDefinedSymbol() will understand what it means when this is\n    // valid but symbol_table_ is not present.\n    symbol_table_initialized_.set_valid();\n    return;\n  }\n\n  // Find the __LINKEDIT segment. Technically, the symbol table can be in any\n  // mapped segment, but by convention, it’s in the one named __LINKEDIT.\n  const MachOImageSegmentReader* linkedit_segment =\n      GetSegmentByName(SEG_LINKEDIT);\n  if (!linkedit_segment) {\n    LOG(WARNING) << \"no \" SEG_LINKEDIT \" segment\";\n    return;\n  }\n\n  symbol_table_.reset(new MachOImageSymbolTableReader());\n  if (!symbol_table_->Initialize(process_reader_,\n                                 symtab_command_.get(),\n                                 dysymtab_command_.get(),\n                                 linkedit_segment,\n                                 module_info_)) {\n    symbol_table_.reset();\n    return;\n  }\n\n  symbol_table_initialized_.set_valid();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_reader.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_\n#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_\n\n#include <mach/mach.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"snapshot/mac/process_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\nclass MachOImageSegmentReader;\nclass MachOImageSymbolTableReader;\nclass ProcessReaderMac;\n\n//! \\brief A reader for Mach-O images mapped into another process.\n//!\n//! This class is capable of reading both 32-bit (`mach_header`/`MH_MAGIC`) and\n//! 64-bit (`mach_header_64`/`MH_MAGIC_64`) images based on the bitness of the\n//! remote process.\n//!\n//! \\sa MachOImageAnnotationsReader\nclass MachOImageReader {\n public:\n  MachOImageReader();\n\n  MachOImageReader(const MachOImageReader&) = delete;\n  MachOImageReader& operator=(const MachOImageReader&) = delete;\n\n  ~MachOImageReader();\n\n  //! \\brief Reads the Mach-O image file’s load commands from another process.\n  //!\n  //! This method must only be called once on an object. This method must be\n  //! called successfully before any other method in this class may be called.\n  //!\n  //! \\param[in] process_reader The reader for the remote process.\n  //! \\param[in] address The address, in the remote process’ address space,\n  //!     where the `mach_header` or `mach_header_64` at the beginning of the\n  //!     image to be read is located. This address can be determined by reading\n  //!     the remote process’ dyld information (see\n  //!     snapshot/mac/process_types/dyld_images.proctype).\n  //! \\param[in] name The module’s name, a string to be used in logged messages.\n  //!     This string is for diagnostic purposes and to relax otherwise strict\n  //!     parsing rules for common modules with known defects.\n  //!\n  //! \\return `true` if the image was read successfully, including all load\n  //!     commands. `false` otherwise, with an appropriate message logged.\n  bool Initialize(ProcessReaderMac* process_reader,\n                  mach_vm_address_t address,\n                  const std::string& name);\n\n  //! \\brief Returns the Mach-O file type.\n  //!\n  //! This value comes from the `filetype` field of the `mach_header` or\n  //! `mach_header_64`. Common values include `MH_EXECUTE`, `MH_DYLIB`,\n  //! `MH_DYLINKER`, and `MH_BUNDLE`.\n  uint32_t FileType() const { return file_type_; }\n\n  //! \\brief Returns the Mach-O image’s load address.\n  //!\n  //! This is the value passed as \\a address to Initialize().\n  mach_vm_address_t Address() const { return address_; }\n\n  //! \\brief Returns the mapped size of the Mach-O image’s `__TEXT` segment.\n  //!\n  //! Note that this is returns only the size of the `__TEXT` segment, not of\n  //! any other segment. This is because the interface only allows one load\n  //! address and size to be reported, but Mach-O image files may consist of\n  //! multiple discontiguous segments. By convention, the `__TEXT` segment is\n  //! always mapped at the beginning of a Mach-O image file, and it is the most\n  //! useful for the expected intended purpose of collecting data to obtain\n  //! stack backtraces. The implementation insists during initialization that\n  //! the `__TEXT` segment be mapped at the beginning of the file.\n  //!\n  //! In practice, discontiguous segments are only found for images that have\n  //! loaded out of the dyld shared cache, but the `__TEXT` segment’s size is\n  //! returned for modules that loaded with contiguous segments as well for\n  //! consistency.\n  mach_vm_size_t Size() const { return size_; }\n\n  //! \\brief Returns the Mach-O image’s “slide,” the difference between its\n  //!     actual load address and its preferred load address.\n  //!\n  //! “Slide” is computed by subtracting the `__TEXT` segment’s preferred load\n  //! address from its actual load address. It will be reported as a positive\n  //! offset when the actual load address is greater than the preferred load\n  //! address. The preferred load address is taken to be the segment’s reported\n  //! `vmaddr` value.\n  mach_vm_size_t Slide() const { return slide_; }\n\n  //! \\brief Obtain segment information by segment name.\n  //!\n  //! \\param[in] segment_name The name of the segment to search for, for\n  //!     example, `\"__TEXT\"`.\n  //!\n  //! \\return A pointer to the segment information if it was found, or `nullptr`\n  //!     if it was not found. The caller does not take ownership; the lifetime\n  //!     of the returned object is scoped to the lifetime of this\n  //!     MachOImageReader object.\n  const MachOImageSegmentReader* GetSegmentByName(\n      const std::string& segment_name) const;\n\n  //! \\brief Obtain section information by segment and section name.\n  //!\n  //! \\param[in] segment_name The name of the segment to search for, for\n  //!     example, `\"__TEXT\"`.\n  //! \\param[in] section_name The name of the section within the segment to\n  //!     search for, for example, `\"__text\"`.\n  //! \\param[out] address The actual address that the section was loaded at in\n  //!     memory, taking any “slide” into account if the section did not load at\n  //!     its preferred address as stored in the Mach-O image file. This\n  //!     parameter can be `nullptr`.\n  //!\n  //! \\return A pointer to the section information if it was found, or `nullptr`\n  //!     if it was not found. The caller does not take ownership; the lifetime\n  //!     of the returned object is scoped to the lifetime of this\n  //!     MachOImageReader object.\n  //!\n  //! No parameter is provided for the section’s size, because it can be\n  //! obtained from the returned process_types::section::size field.\n  //!\n  //! \\note The process_types::section::addr field gives the section’s preferred\n  //!     load address as stored in the Mach-O image file, and is not adjusted\n  //!     for any “slide” that may have occurred when the image was loaded. Use\n  //!     \\a address to obtain the section’s actual load address.\n  const process_types::section* GetSectionByName(\n      const std::string& segment_name,\n      const std::string& section_name,\n      mach_vm_address_t* address) const;\n\n  //! \\brief Obtain section information by section index.\n  //!\n  //! \\param[in] index The index of the section to return, in the order that it\n  //!     appears in the segment load commands. This is a 1-based index,\n  //!     matching the section number values used for `nlist::n_sect`.\n  //! \\param[out] containing_segment The segment that contains the section.\n  //!     This parameter can be `nullptr`. The caller does not take ownership;\n  //!     the lifetime of the returned object is scoped to the lifetime of this\n  //!     MachOImageReader object.\n  //! \\param[out] address The actual address that the section was loaded at in\n  //!     memory, taking any “slide” into account if the section did not load at\n  //!     its preferred address as stored in the Mach-O image file. This\n  //!     parameter can be `nullptr`.\n  //!\n  //! \\return A pointer to the section information. If \\a index is out of range,\n  //!     logs a warning and returns `nullptr`. The caller does not take\n  //!     ownership; the lifetime of the returned object is scoped to the\n  //!     lifetime of this MachOImageReader object.\n  //!\n  //! No parameter is provided for the section’s size, because it can be\n  //! obtained from the returned process_types::section::size field.\n  //!\n  //! \\note The process_types::section::addr field gives the section’s preferred\n  //!     load address as stored in the Mach-O image file, and is not adjusted\n  //!     for any “slide” that may have occurred when the image was loaded. Use\n  //!     \\a address to obtain the section’s actual load address.\n  //! \\note Unlike MachOImageSegmentReader::GetSectionAtIndex(), this method\n  //!     accepts out-of-range values for \\a index, and returns `nullptr`\n  //!     instead of aborting execution upon encountering an out-of-range value.\n  //!     This is because a Mach-O image file’s symbol table refers to this\n  //!     per-module section index, and an out-of-range index in that case\n  //!     should be treated as a data error (where the data is beyond this\n  //!     code’s control) and handled non-fatally by reporting the error to the\n  //!     caller.\n  const process_types::section* GetSectionAtIndex(\n      size_t index,\n      const MachOImageSegmentReader** containing_segment,\n      mach_vm_address_t* address) const;\n\n  //! \\brief Looks up a symbol in the image’s symbol table.\n  //!\n  //! This method is capable of locating external defined symbols. Specifically,\n  //! this method can look up symbols that have these charcteristics:\n  //!  - `N_STAB` (debugging) and `N_PEXT` (private external) must not be set.\n  //!  - `N_EXT` (external) must be set.\n  //!  - The type must be `N_ABS` (absolute) or `N_SECT` (defined in section).\n  //!\n  //! `N_INDR` (indirect), `N_UNDF` (undefined), and `N_PBUD` (prebound\n  //! undefined) symbols cannot be located through this mechanism.\n  //!\n  //! \\param[in] name The name of the symbol to look up, “mangled” or\n  //!     “decorated” appropriately. For example, use `\"_main\"` to look up the\n  //!     symbol for the C `main()` function, and use `\"__Z4Funcv\"` to look up\n  //!     the symbol for the C++ `Func()` function. Contrary to `dlsym()`, the\n  //!     leading underscore must not be stripped when using this interface.\n  //! \\param[out] value If the lookup was successful, this will be set to the\n  //!     value of the symbol, adjusted for any “slide” as needed. The value can\n  //!     be used as an address in the remote process’ address space where the\n  //!     pointee of the symbol exists in memory.\n  //!\n  //! \\return `true` if the symbol lookup was successful and the symbol was\n  //!     found. `false` otherwise, including error conditions (for which a\n  //!     warning message will be logged), modules without symbol tables, and\n  //!     symbol names not found in the symbol table.\n  //!\n  //! \\note Symbol values returned via this interface are adjusted for “slide”\n  //!     as appropriate, in contrast to the underlying implementation,\n  //!     MachOImageSymbolTableReader::LookUpExternalDefinedSymbol().\n  //!\n  //! \\warning Symbols that are resolved by running symbol resolvers\n  //!     (`.symbol_resolver`) are not properly handled by this interface. The\n  //!     address of the symbol resolver is returned because that’s what shows\n  //!     up in the symbol table, rather than the effective address of the\n  //!     resolved symbol as used by dyld after running the resolver. The only\n  //!     way to detect this situation would be to read the `LC_DYLD_INFO` or\n  //!     `LC_DYLD_INFO_ONLY` load command if present and looking for the\n  //!     `EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER` flag, but that would just be\n  //!     able to detect symbols with a resolver, it would not be able to\n  //!     resolve them from out-of-process, so it’s not currently done.\n  bool LookUpExternalDefinedSymbol(const std::string& name,\n                                   mach_vm_address_t* value) const;\n\n  //! \\brief Returns a Mach-O dylib image’s current version.\n  //!\n  //! This information comes from the `dylib_current_version` field of a dylib’s\n  //! `LC_ID_DYLIB` load command. For dylibs without this load command, `0` will\n  //! be returned.\n  //!\n  //! This method may only be called on Mach-O images for which FileType()\n  //! returns `MH_DYLIB`.\n  uint32_t DylibVersion() const;\n\n  //! \\brief Returns a Mach-O image’s source version.\n  //!\n  //! This information comes from a Mach-O image’s `LC_SOURCE_VERSION` load\n  //! command. For Mach-O images without this load command, `0` will be\n  //! returned.\n  uint64_t SourceVersion() const { return source_version_; }\n\n  //! \\brief Returns a Mach-O image’s UUID.\n  //!\n  //! This information comes from a Mach-O image’s `LC_UUID` load command. For\n  //! Mach-O images without this load command, a zeroed-out UUID value will be\n  //! returned.\n  //\n  // UUID is a name in this scope (referring to this method), so the parameter’s\n  // type needs to be qualified with |crashpad::|.\n  void UUID(crashpad::UUID* uuid) const;\n\n  //! \\brief Returns the dynamic linker’s pathname.\n  //!\n  //! The dynamic linker is normally /usr/lib/dyld.\n  //!\n  //! For executable images (those with file type `MH_EXECUTE`), this is the\n  //! name provided in the `LC_LOAD_DYLINKER` load command, if any. For dynamic\n  //! linker images (those with file type `MH_DYLINKER`), this is the name\n  //! provided in the `LC_ID_DYLINKER` load command. In other cases, this will\n  //! be empty.\n  std::string DylinkerName() const { return dylinker_name_; }\n\n  //! \\brief Obtains the module’s CrashpadInfo structure.\n  //!\n  //! \\return `true` on success, `false` on failure. If the module does not have\n  //!     a `__DATA,crashpad_info` section, this will return `false` without\n  //!     logging any messages. Other failures will result in messages being\n  //!     logged.\n  bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const;\n\n private:\n  // A generic helper routine for the other Read*Command() methods.\n  template <typename T>\n  bool ReadLoadCommand(mach_vm_address_t load_command_address,\n                       const std::string& load_command_info,\n                       uint32_t expected_load_command_id,\n                       T* load_command);\n\n  // The Read*Command() methods are subroutines called by Initialize(). They are\n  // responsible for reading a single load command. They may update the member\n  // fields of their MachOImageReader object. If they can’t make sense of a load\n  // command, they return false.\n  bool ReadSegmentCommand(mach_vm_address_t load_command_address,\n                          const std::string& load_command_info);\n  bool ReadSymTabCommand(mach_vm_address_t load_command_address,\n                         const std::string& load_command_info);\n  bool ReadDySymTabCommand(mach_vm_address_t load_command_address,\n                           const std::string& load_command_info);\n  bool ReadIdDylibCommand(mach_vm_address_t load_command_address,\n                          const std::string& load_command_info);\n  bool ReadDylinkerCommand(mach_vm_address_t load_command_address,\n                           const std::string& load_command_info);\n  bool ReadUUIDCommand(mach_vm_address_t load_command_address,\n                       const std::string& load_command_info);\n  bool ReadSourceVersionCommand(mach_vm_address_t load_command_address,\n                                const std::string& load_command_info);\n  bool ReadUnexpectedCommand(mach_vm_address_t load_command_address,\n                             const std::string& load_command_info);\n\n  // Performs deferred initialization of the symbol table. Because a module’s\n  // symbol table is often not needed, this is not handled in Initialize(), but\n  // is done lazily, on-demand as needed.\n  //\n  // symbol_table_initialized_ will be transitioned to the appropriate state. If\n  // initialization completes successfully, this will be the valid state.\n  // Otherwise, it will be left in the invalid state and a warning message will\n  // be logged.\n  //\n  // Note that if the object contains no symbol table, symbol_table_initialized_\n  // will be set to the valid state, but symbol_table_ will be nullptr.\n  void InitializeSymbolTable() const;\n\n  std::vector<std::unique_ptr<MachOImageSegmentReader>> segments_;\n  std::map<std::string, size_t> segment_map_;\n  std::string module_name_;\n  std::string module_info_;\n  std::string dylinker_name_;\n  crashpad::UUID uuid_;\n  mach_vm_address_t address_;\n  mach_vm_size_t size_;\n  mach_vm_size_t slide_;\n  uint64_t source_version_;\n  std::unique_ptr<process_types::symtab_command> symtab_command_;\n  std::unique_ptr<process_types::dysymtab_command> dysymtab_command_;\n\n  // symbol_table_ (and symbol_table_initialized_) are mutable in order to\n  // maintain LookUpExternalDefinedSymbol() as a const interface while allowing\n  // lazy initialization via InitializeSymbolTable(). This is logical\n  // const-ness, not physical const-ness.\n  mutable std::unique_ptr<MachOImageSymbolTableReader> symbol_table_;\n\n  std::unique_ptr<process_types::dylib_command> id_dylib_command_;\n  ProcessReaderMac* process_reader_;  // weak\n  uint32_t file_type_;\n  InitializationStateDcheck initialized_;\n\n  // symbol_table_initialized_ protects symbol_table_: symbol_table_ can only\n  // be used when symbol_table_initialized_ is valid, although\n  // symbol_table_initialized_ being valid doesn’t imply that symbol_table_ is\n  // set. symbol_table_initialized_ will be valid without symbol_table_ being\n  // set in modules that have no symbol table.\n  mutable InitializationState symbol_table_initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_reader_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/mach_o_image_reader.h\"\n\n#include <Availability.h>\n#include <dlfcn.h>\n#include <mach-o/dyld.h>\n#include <mach-o/dyld_images.h>\n#include <mach-o/getsect.h>\n#include <mach-o/ldsyms.h>\n#include <mach-o/loader.h>\n#include <mach-o/nlist.h>\n#include <stdint.h>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"client/crashpad_info.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/mac/mach_o_image_segment_reader.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"snapshot/mac/process_types.h\"\n#include \"test/mac/dyld.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/misc/uuid.h\"\n\n// This file is responsible for testing MachOImageReader,\n// MachOImageSegmentReader, and MachOImageSymbolTableReader.\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Native types and constants, in cases where the 32-bit and 64-bit versions\n// are different.\n#if defined(ARCH_CPU_64_BITS)\nusing MachHeader = mach_header_64;\nconstexpr uint32_t kMachMagic = MH_MAGIC_64;\nusing SegmentCommand = segment_command_64;\nconstexpr uint32_t kSegmentCommand = LC_SEGMENT_64;\nusing Section = section_64;\nusing Nlist = nlist_64;\n#else\nusing MachHeader = mach_header;\nconstexpr uint32_t kMachMagic = MH_MAGIC;\nusing SegmentCommand = segment_command;\nconstexpr uint32_t kSegmentCommand = LC_SEGMENT;\nusing Section = section;\n\n// This needs to be called “struct nlist” because “nlist” without the struct\n// refers to the nlist() function.\nusing Nlist = struct nlist;\n#endif\n\n#if defined(ARCH_CPU_X86_64)\nconstexpr int kCPUType = CPU_TYPE_X86_64;\n#elif defined(ARCH_CPU_X86)\nconstexpr int kCPUType = CPU_TYPE_X86;\n#elif defined(ARCH_CPU_ARM64)\nconstexpr int kCPUType = CPU_TYPE_ARM64;\n#endif\n\n// Verifies that |expect_section| and |actual_section| agree.\nvoid ExpectSection(const Section* expect_section,\n                   const process_types::section* actual_section) {\n  ASSERT_TRUE(expect_section);\n  ASSERT_TRUE(actual_section);\n\n  EXPECT_EQ(\n      MachOImageSegmentReader::SectionNameString(actual_section->sectname),\n      MachOImageSegmentReader::SectionNameString(expect_section->sectname));\n  EXPECT_EQ(\n      MachOImageSegmentReader::SegmentNameString(actual_section->segname),\n      MachOImageSegmentReader::SegmentNameString(expect_section->segname));\n  EXPECT_EQ(actual_section->addr, expect_section->addr);\n  EXPECT_EQ(actual_section->size, expect_section->size);\n  EXPECT_EQ(actual_section->offset, expect_section->offset);\n  EXPECT_EQ(actual_section->align, expect_section->align);\n  EXPECT_EQ(actual_section->reloff, expect_section->reloff);\n  EXPECT_EQ(actual_section->nreloc, expect_section->nreloc);\n  EXPECT_EQ(actual_section->flags, expect_section->flags);\n  EXPECT_EQ(actual_section->reserved1, expect_section->reserved1);\n  EXPECT_EQ(actual_section->reserved2, expect_section->reserved2);\n}\n\n// Verifies that |expect_segment| is a valid Mach-O segment load command for the\n// current system by checking its |cmd| field. Then, verifies that the\n// information in |actual_segment| matches that in |expect_segment|. The\n// |segname|, |vmaddr|, |vmsize|, and |fileoff| fields are examined. Each\n// section within the segment is also examined by calling ExpectSection().\n// Access to each section via both MachOImageSegmentReader::GetSectionByName()\n// and MachOImageReader::GetSectionByName() is verified, expecting that each\n// call produces the same section. Segment and section data addresses are\n// verified against data obtained by calling getsegmentdata() and\n// getsectiondata(). The segment is checked to make sure that it behaves\n// correctly when attempting to look up a nonexistent section by name.\n// |section_index| is used to track the last-used section index in an image on\n// entry, and is reset to the last-used section index on return after the\n// sections are processed. This is used to test that\n// MachOImageReader::GetSectionAtIndex() returns the correct result.\nvoid ExpectSegmentCommand(const SegmentCommand* expect_segment,\n                          const MachHeader* expect_image,\n                          const MachOImageSegmentReader* actual_segment,\n                          const MachOImageReader* actual_image,\n                          size_t* section_index) {\n  ASSERT_TRUE(expect_segment);\n  ASSERT_TRUE(actual_segment);\n\n  EXPECT_EQ(expect_segment->cmd, kSegmentCommand);\n\n  std::string segment_name = actual_segment->Name();\n  EXPECT_EQ(\n      segment_name,\n      MachOImageSegmentReader::SegmentNameString(expect_segment->segname));\n  EXPECT_EQ(actual_segment->vmaddr(), expect_segment->vmaddr);\n  EXPECT_EQ(actual_segment->vmsize(), expect_segment->vmsize);\n  EXPECT_EQ(actual_segment->fileoff(), expect_segment->fileoff);\n\n  if (actual_segment->SegmentSlides()) {\n    EXPECT_EQ(actual_segment->vmaddr() + actual_image->Slide(),\n              actual_segment->Address());\n\n    unsigned long expect_segment_size;\n    const uint8_t* expect_segment_data = getsegmentdata(\n        expect_image, segment_name.c_str(), &expect_segment_size);\n    mach_vm_address_t expect_segment_address =\n        FromPointerCast<mach_vm_address_t>(expect_segment_data);\n    EXPECT_EQ(actual_segment->Address(), expect_segment_address);\n    EXPECT_EQ(actual_segment->vmsize(), expect_segment_size);\n    EXPECT_EQ(actual_segment->Size(), actual_segment->vmsize());\n  } else {\n    // getsegmentdata() doesn’t return appropriate data for the __PAGEZERO\n    // segment because getsegmentdata() always adjusts for slide, but the\n    // __PAGEZERO segment never slides, it just grows. Skip the getsegmentdata()\n    // check for that segment according to the same rules that the kernel uses\n    // to identify __PAGEZERO. See 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c\n    // load_segment().\n    EXPECT_EQ(actual_segment->vmaddr(), actual_segment->Address());\n    EXPECT_EQ(actual_segment->Size(),\n              actual_segment->vmsize() + actual_image->Slide());\n  }\n\n  ASSERT_EQ(actual_segment->nsects(), expect_segment->nsects);\n\n  // Make sure that the expected load command is big enough for the number of\n  // sections that it claims to have, and set up a pointer to its first section\n  // structure.\n  ASSERT_EQ(expect_segment->cmdsize,\n            sizeof(*expect_segment) + expect_segment->nsects * sizeof(Section));\n  const Section* expect_sections =\n      reinterpret_cast<const Section*>(&expect_segment[1]);\n\n  for (size_t index = 0; index < actual_segment->nsects(); ++index) {\n    const Section* expect_section = &expect_sections[index];\n    const process_types::section* actual_section =\n        actual_segment->GetSectionAtIndex(index, nullptr);\n    ASSERT_NO_FATAL_FAILURE(\n        ExpectSection(&expect_sections[index], actual_section));\n\n    // Make sure that the section is accessible by GetSectionByName as well.\n    std::string section_name =\n        MachOImageSegmentReader::SectionNameString(expect_section->sectname);\n    const process_types::section* actual_section_by_name =\n        actual_segment->GetSectionByName(section_name, nullptr);\n    EXPECT_EQ(actual_section_by_name, actual_section);\n\n    // Make sure that the section is accessible by the parent MachOImageReader’s\n    // GetSectionByName.\n    mach_vm_address_t actual_section_address;\n    const process_types::section* actual_section_from_image_by_name =\n        actual_image->GetSectionByName(\n            segment_name, section_name, &actual_section_address);\n    EXPECT_EQ(actual_section_from_image_by_name, actual_section);\n\n    if (actual_segment->SegmentSlides()) {\n      EXPECT_EQ(actual_section->addr + actual_image->Slide(),\n                actual_section_address);\n\n      unsigned long expect_section_size;\n      const uint8_t* expect_section_data = getsectiondata(expect_image,\n                                                          segment_name.c_str(),\n                                                          section_name.c_str(),\n                                                          &expect_section_size);\n      mach_vm_address_t expect_section_address =\n          FromPointerCast<mach_vm_address_t>(expect_section_data);\n      EXPECT_EQ(actual_section_address, expect_section_address);\n      EXPECT_EQ(actual_section->size, expect_section_size);\n    } else {\n      EXPECT_EQ(actual_section->addr, actual_section_address);\n    }\n\n    // Test the parent MachOImageReader’s GetSectionAtIndex as well.\n    const MachOImageSegmentReader* containing_segment;\n    mach_vm_address_t actual_section_address_at_index;\n    const process_types::section* actual_section_from_image_at_index =\n        actual_image->GetSectionAtIndex(++(*section_index),\n                                        &containing_segment,\n                                        &actual_section_address_at_index);\n    EXPECT_EQ(actual_section_from_image_at_index, actual_section);\n    EXPECT_EQ(containing_segment, actual_segment);\n    EXPECT_EQ(actual_section_address_at_index, actual_section_address);\n  }\n\n  EXPECT_EQ(actual_segment->GetSectionByName(\"NoSuchSection\", nullptr),\n            nullptr);\n}\n\n// Walks through the load commands of |expect_image|, finding all of the\n// expected segment commands. For each expected segment command, calls\n// actual_image->GetSegmentByName() to obtain an actual segment command, and\n// calls ExpectSegmentCommand() to compare the expected and actual segments. A\n// series of by-name lookups is also performed on the segment to ensure that it\n// behaves correctly when attempting to look up segment and section names that\n// are not present. |test_section_indices| should be true to test\n// MachOImageReader::GetSectionAtIndex() using out-of-range section indices.\n// This should be tested for at least one module, but it’s very noisy in terms\n// of logging output, so this knob is provided to suppress this portion of the\n// test when looping over all modules.\nvoid ExpectSegmentCommands(const MachHeader* expect_image,\n                           const MachOImageReader* actual_image,\n                           bool test_section_index_bounds) {\n  ASSERT_TRUE(expect_image);\n  ASSERT_TRUE(actual_image);\n\n  // &expect_image[1] points right past the end of the mach_header[_64], to the\n  // start of the load commands.\n  const char* commands_base = reinterpret_cast<const char*>(&expect_image[1]);\n  uint32_t position = 0;\n  size_t section_index = 0;\n  for (uint32_t index = 0; index < expect_image->ncmds; ++index) {\n    ASSERT_LT(position, expect_image->sizeofcmds);\n    const load_command* command =\n        reinterpret_cast<const load_command*>(&commands_base[position]);\n    ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds);\n    if (command->cmd == kSegmentCommand) {\n      ASSERT_GE(command->cmdsize, sizeof(SegmentCommand));\n      const SegmentCommand* expect_segment =\n          reinterpret_cast<const SegmentCommand*>(command);\n      std::string segment_name =\n          MachOImageSegmentReader::SegmentNameString(expect_segment->segname);\n      const MachOImageSegmentReader* actual_segment =\n          actual_image->GetSegmentByName(segment_name);\n      ASSERT_NO_FATAL_FAILURE(ExpectSegmentCommand(expect_segment,\n                                                   expect_image,\n                                                   actual_segment,\n                                                   actual_image,\n                                                   &section_index));\n    }\n    position += command->cmdsize;\n  }\n  EXPECT_EQ(position, expect_image->sizeofcmds);\n\n  if (test_section_index_bounds) {\n    // GetSectionAtIndex uses a 1-based index. Make sure that the range is\n    // correct.\n    EXPECT_EQ(actual_image->GetSectionAtIndex(0, nullptr, nullptr), nullptr);\n    EXPECT_EQ(\n        actual_image->GetSectionAtIndex(section_index + 1, nullptr, nullptr),\n        nullptr);\n  }\n\n  // Make sure that by-name lookups for names that don’t exist work properly:\n  // they should return nullptr.\n  EXPECT_FALSE(actual_image->GetSegmentByName(\"NoSuchSegment\"));\n  EXPECT_FALSE(actual_image->GetSectionByName(\n      \"NoSuchSegment\", \"NoSuchSection\", nullptr));\n\n  // Make sure that there’s a __TEXT segment so that this can do a valid test of\n  // a section that doesn’t exist within a segment that does.\n  EXPECT_TRUE(actual_image->GetSegmentByName(SEG_TEXT));\n  EXPECT_FALSE(\n      actual_image->GetSectionByName(SEG_TEXT, \"NoSuchSection\", nullptr));\n\n  // Similarly, make sure that a section name that exists in one segment isn’t\n  // accidentally found during a lookup for that section in a different segment.\n  //\n  // If the image has no sections (unexpected), then any section lookup should\n  // fail, and these initial values of test_segment and test_section are fine\n  // for the EXPECT_FALSE checks on GetSectionByName() below.\n  std::string test_segment = SEG_DATA;\n  std::string test_section = SECT_TEXT;\n\n  const process_types::section* section =\n      actual_image->GetSectionAtIndex(1, nullptr, nullptr);\n  if (section) {\n    // Use the name of the first section in the image as the section that\n    // shouldn’t appear in a different segment. If the first section is in the\n    // __TEXT segment (as it is normally), then a section by the same name\n    // wouldn’t be expected in the __DATA segment. But if the first section is\n    // in any other segment, then it wouldn’t be expected in the __TEXT segment.\n    if (MachOImageSegmentReader::SegmentNameString(section->segname) ==\n            SEG_TEXT) {\n      test_segment = SEG_DATA;\n    } else {\n      test_segment = SEG_TEXT;\n    }\n    test_section =\n        MachOImageSegmentReader::SectionNameString(section->sectname);\n\n    // It should be possible to look up the first section by name.\n    EXPECT_EQ(actual_image->GetSectionByName(\n                  section->segname, section->sectname, nullptr),\n              section);\n  }\n  EXPECT_FALSE(\n      actual_image->GetSectionByName(\"NoSuchSegment\", test_section, nullptr));\n  EXPECT_FALSE(\n      actual_image->GetSectionByName(test_segment, test_section, nullptr));\n\n  // The __LINKEDIT segment normally does exist but doesn’t have any sections.\n  EXPECT_FALSE(\n      actual_image->GetSectionByName(SEG_LINKEDIT, \"NoSuchSection\", nullptr));\n  EXPECT_FALSE(\n      actual_image->GetSectionByName(SEG_LINKEDIT, SECT_TEXT, nullptr));\n}\n\n// In some cases, the expected slide value for an image is unknown, because no\n// reasonable API to return it is provided. When this happens, use kSlideUnknown\n// to avoid checking the actual slide value against anything.\nconstexpr mach_vm_size_t kSlideUnknown =\n    std::numeric_limits<mach_vm_size_t>::max();\n\n// Verifies that |expect_image| is a vaild Mach-O header for the current system\n// by checking its |magic| and |cputype| fields. Then, verifies that the\n// information in |actual_image| matches that in |expect_image|. The |filetype|\n// field is examined, actual_image->Address() is compared to\n// |expect_image_address|, and actual_image->Slide() is compared to\n// |expect_image_slide|, unless |expect_image_slide| is kSlideUnknown. Various\n// other attributes of |actual_image| are sanity-checked depending on the Mach-O\n// file type. Finally, ExpectSegmentCommands() is called to verify all that all\n// of the segments match; |test_section_index_bounds| is used as an argument to\n// that function.\nvoid ExpectMachImage(const MachHeader* expect_image,\n                     mach_vm_address_t expect_image_address,\n                     mach_vm_size_t expect_image_slide,\n                     const MachOImageReader* actual_image,\n                     bool test_section_index_bounds) {\n  ASSERT_TRUE(expect_image);\n  ASSERT_TRUE(actual_image);\n\n  EXPECT_EQ(expect_image->magic, kMachMagic);\n  EXPECT_EQ(expect_image->cputype, kCPUType);\n\n  EXPECT_EQ(actual_image->FileType(), expect_image->filetype);\n  EXPECT_EQ(actual_image->Address(), expect_image_address);\n  if (expect_image_slide != kSlideUnknown) {\n    EXPECT_EQ(actual_image->Slide(), expect_image_slide);\n  }\n\n  const MachOImageSegmentReader* actual_text_segment =\n      actual_image->GetSegmentByName(SEG_TEXT);\n  ASSERT_TRUE(actual_text_segment);\n  EXPECT_EQ(actual_text_segment->Address(), expect_image_address);\n  EXPECT_EQ(actual_text_segment->Size(), actual_image->Size());\n  EXPECT_EQ(actual_image->Slide(),\n            expect_image_address - actual_text_segment->vmaddr());\n\n  uint32_t file_type = actual_image->FileType();\n  EXPECT_TRUE(file_type == MH_EXECUTE || file_type == MH_DYLIB ||\n              file_type == MH_DYLINKER || file_type == MH_BUNDLE);\n\n  if (file_type == MH_EXECUTE || file_type == MH_DYLINKER) {\n    EXPECT_EQ(actual_image->DylinkerName(), \"/usr/lib/dyld\");\n  }\n\n  // For these, just don’t crash or anything.\n  if (file_type == MH_DYLIB) {\n    actual_image->DylibVersion();\n  }\n  actual_image->SourceVersion();\n  UUID uuid;\n  actual_image->UUID(&uuid);\n\n  ASSERT_NO_FATAL_FAILURE(ExpectSegmentCommands(\n      expect_image, actual_image, test_section_index_bounds));\n}\n\n// Verifies the symbol whose Nlist structure is |entry| and whose name is |name|\n// matches the value of a symbol by the same name looked up in |actual_image|.\n// MachOImageReader::LookUpExternalDefinedSymbol() is used for this purpose.\n// Only external defined symbols are considered, other types of symbols are\n// excluded because LookUpExternalDefinedSymbol() only deals with external\n// defined symbols.\nvoid ExpectSymbol(const Nlist* entry,\n                  const char* name,\n                  const MachOImageReader* actual_image) {\n  SCOPED_TRACE(name);\n\n  uint32_t entry_type = entry->n_type & N_TYPE;\n  if ((entry->n_type & N_STAB) == 0 && (entry->n_type & N_PEXT) == 0 &&\n      (entry_type == N_ABS || entry_type == N_SECT) &&\n      (entry->n_type & N_EXT) == 1) {\n    mach_vm_address_t actual_address;\n    ASSERT_TRUE(\n        actual_image->LookUpExternalDefinedSymbol(name, &actual_address));\n\n    // Since the nlist interface was used to read the symbol, use it to compute\n    // the symbol address too. This isn’t perfect, and it should be possible in\n    // theory to use dlsym() to get the expected address of a symbol. In\n    // practice, dlsym() is difficult to use when only a MachHeader* is\n    // available as in this function, as opposed to a void* opaque handle. It is\n    // possible to get a void* handle by using dladdr() to find the file name\n    // corresponding to the MachHeader*, and using dlopen() again on that name,\n    // assuming it hasn’t changed on disk since being loaded. However, even with\n    // that being done, dlsym() can only deal with symbols whose names begin\n    // with an underscore (and requires that the leading underscore be trimmed).\n    // dlsym() will also return different addresses for symbols that are\n    // resolved via symbol resolver.\n    mach_vm_address_t expect_address = entry->n_value;\n    if (entry_type == N_SECT) {\n      EXPECT_GE(entry->n_sect, 1u);\n      expect_address += actual_image->Slide();\n    } else {\n      EXPECT_EQ(entry->n_sect, NO_SECT);\n    }\n\n    EXPECT_EQ(actual_address, expect_address);\n  }\n\n  // You’d think that it might be a good idea to verify that if the conditions\n  // above weren’t met, that the symbol didn’t show up in actual_image’s symbol\n  // table at all. Unfortunately, it’s possible for the same name to show up as\n  // both an external defined symbol and as something else, so it’s not possible\n  // to verify this reliably.\n}\n\n// Locates the symbol table in |expect_image| and verifies that all of the\n// external defined symbols found there are also present and have the same\n// values in |actual_image|. ExpectSymbol() is used to verify the actual symbol.\nvoid ExpectSymbolTable(const MachHeader* expect_image,\n                       const MachOImageReader* actual_image) {\n  // This intentionally consults only LC_SYMTAB and not LC_DYSYMTAB so that it\n  // can look at the larger set of all symbols. The actual implementation being\n  // tested is free to consult LC_DYSYMTAB, but that’s considered an\n  // optimization. It’s not necessary for the test, and it’s better for the test\n  // to expose bugs in that optimization rather than duplicate them.\n  const char* commands_base = reinterpret_cast<const char*>(&expect_image[1]);\n  uint32_t position = 0;\n  const symtab_command* symtab = nullptr;\n  const SegmentCommand* linkedit = nullptr;\n  for (uint32_t index = 0; index < expect_image->ncmds; ++index) {\n    ASSERT_LT(position, expect_image->sizeofcmds);\n    const load_command* command =\n        reinterpret_cast<const load_command*>(&commands_base[position]);\n    ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds);\n    if (command->cmd == LC_SYMTAB) {\n      ASSERT_FALSE(symtab);\n      ASSERT_EQ(command->cmdsize, sizeof(symtab_command));\n      symtab = reinterpret_cast<const symtab_command*>(command);\n    } else if (command->cmd == kSegmentCommand) {\n      ASSERT_GE(command->cmdsize, sizeof(SegmentCommand));\n      const SegmentCommand* segment =\n          reinterpret_cast<const SegmentCommand*>(command);\n      std::string segment_name =\n          MachOImageSegmentReader::SegmentNameString(segment->segname);\n      if (segment_name == SEG_LINKEDIT) {\n        ASSERT_FALSE(linkedit);\n        linkedit = segment;\n      }\n    }\n    position += command->cmdsize;\n  }\n\n  if (symtab) {\n    ASSERT_TRUE(linkedit);\n\n    const char* linkedit_base =\n        reinterpret_cast<const char*>(linkedit->vmaddr + actual_image->Slide());\n    const Nlist* nlist = reinterpret_cast<const Nlist*>(\n        linkedit_base + symtab->symoff - linkedit->fileoff);\n    const char* strtab = linkedit_base + symtab->stroff - linkedit->fileoff;\n\n    for (uint32_t index = 0; index < symtab->nsyms; ++index) {\n      const Nlist* entry = nlist + index;\n      const char* name = strtab + entry->n_un.n_strx;\n      ASSERT_NO_FATAL_FAILURE(ExpectSymbol(entry, name, actual_image));\n    }\n  }\n\n  mach_vm_address_t ignore;\n  EXPECT_FALSE(actual_image->LookUpExternalDefinedSymbol(\"\", &ignore));\n  EXPECT_FALSE(\n      actual_image->LookUpExternalDefinedSymbol(\"NoSuchSymbolName\", &ignore));\n  EXPECT_FALSE(\n      actual_image->LookUpExternalDefinedSymbol(\"_NoSuchSymbolName\", &ignore));\n}\n\nTEST(MachOImageReader, Self_MainExecutable) {\n  ProcessReaderMac process_reader;\n  ASSERT_TRUE(process_reader.Initialize(mach_task_self()));\n\n  const MachHeader* mh_execute_header =\n      reinterpret_cast<MachHeader*>(dlsym(RTLD_MAIN_ONLY, MH_EXECUTE_SYM));\n  ASSERT_NE(mh_execute_header, nullptr);\n  mach_vm_address_t mh_execute_header_address =\n      FromPointerCast<mach_vm_address_t>(mh_execute_header);\n\n  MachOImageReader image_reader;\n  ASSERT_TRUE(image_reader.Initialize(\n      &process_reader, mh_execute_header_address, \"executable\"));\n\n  EXPECT_EQ(image_reader.FileType(), implicit_cast<uint32_t>(MH_EXECUTE));\n\n  // The main executable has image index 0.\n  intptr_t image_slide = _dyld_get_image_vmaddr_slide(0);\n\n  ASSERT_NO_FATAL_FAILURE(ExpectMachImage(mh_execute_header,\n                                          mh_execute_header_address,\n                                          image_slide,\n                                          &image_reader,\n                                          true));\n\n  // This symbol, __mh_execute_header, is known to exist in all MH_EXECUTE\n  // Mach-O files.\n  mach_vm_address_t symbol_address;\n  ASSERT_TRUE(image_reader.LookUpExternalDefinedSymbol(_MH_EXECUTE_SYM,\n                                                       &symbol_address));\n  EXPECT_EQ(symbol_address, mh_execute_header_address);\n\n  ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mh_execute_header, &image_reader));\n}\n\nTEST(MachOImageReader, Self_DyldImages) {\n  ProcessReaderMac process_reader;\n  ASSERT_TRUE(process_reader.Initialize(mach_task_self()));\n\n  uint32_t count = _dyld_image_count();\n  ASSERT_GE(count, 1u);\n\n  size_t modules_with_crashpad_info = 0;\n\n  for (uint32_t index = 0; index < count; ++index) {\n    const char* image_name = _dyld_get_image_name(index);\n    SCOPED_TRACE(base::StringPrintf(\"index %u, image %s\", index, image_name));\n\n    // _dyld_get_image_header() is poorly-declared: it’s declared as returning\n    // const mach_header* in both 32-bit and 64-bit environments, but in the\n    // 64-bit environment, it should be const mach_header_64*.\n    const MachHeader* mach_header =\n        reinterpret_cast<const MachHeader*>(_dyld_get_image_header(index));\n    mach_vm_address_t image_address =\n        FromPointerCast<mach_vm_address_t>(mach_header);\n\n    MachOImageReader image_reader;\n    ASSERT_TRUE(\n        image_reader.Initialize(&process_reader, image_address, image_name));\n\n    uint32_t file_type = image_reader.FileType();\n    if (index == 0) {\n      EXPECT_EQ(file_type, implicit_cast<uint32_t>(MH_EXECUTE));\n    } else {\n      EXPECT_TRUE(file_type == MH_DYLIB || file_type == MH_BUNDLE);\n    }\n\n    intptr_t image_slide = _dyld_get_image_vmaddr_slide(index);\n    ASSERT_NO_FATAL_FAILURE(ExpectMachImage(\n        mach_header, image_address, image_slide, &image_reader, false));\n\n    ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader));\n\n    process_types::CrashpadInfo crashpad_info;\n    if (image_reader.GetCrashpadInfo(&crashpad_info)) {\n      ++modules_with_crashpad_info;\n    }\n  }\n\n  EXPECT_GE(modules_with_crashpad_info, 1u);\n\n  // Now that all of the modules have been verified, make sure that dyld itself\n  // can be read properly too.\n  const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();\n  ASSERT_GE(dyld_image_infos->version, 1u);\n  EXPECT_EQ(dyld_image_infos->infoArrayCount, count);\n\n  if (dyld_image_infos->version >= 2) {\n    SCOPED_TRACE(\"dyld\");\n\n    // dyld_all_image_infos::dyldImageLoadAddress is poorly-declared too.\n    const MachHeader* mach_header = reinterpret_cast<const MachHeader*>(\n        dyld_image_infos->dyldImageLoadAddress);\n    mach_vm_address_t image_address =\n        FromPointerCast<mach_vm_address_t>(mach_header);\n\n    MachOImageReader image_reader;\n    ASSERT_TRUE(\n        image_reader.Initialize(&process_reader, image_address, \"dyld\"));\n\n    EXPECT_EQ(image_reader.FileType(), implicit_cast<uint32_t>(MH_DYLINKER));\n\n    // There’s no good API to get dyld’s slide, so don’t bother checking it.\n    ASSERT_NO_FATAL_FAILURE(ExpectMachImage(\n        mach_header, image_address, kSlideUnknown, &image_reader, false));\n\n    ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader));\n  }\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n  // If dyld is new enough to record UUIDs, check the UUID of any module that\n  // it says has one. Note that dyld doesn’t record UUIDs of anything that\n  // loaded out of the shared cache, but it should at least have a UUID for the\n  // main executable if it has one.\n  if (dyld_image_infos->version >= 8 && dyld_image_infos->uuidArray) {\n    for (uint32_t index = 0;\n         index < dyld_image_infos->uuidArrayCount;\n         ++index) {\n      const dyld_uuid_info* dyld_image = &dyld_image_infos->uuidArray[index];\n      SCOPED_TRACE(base::StringPrintf(\"uuid index %u\", index));\n\n      // dyld_uuid_info::imageLoadAddress is poorly-declared too.\n      const MachHeader* mach_header =\n          reinterpret_cast<const MachHeader*>(dyld_image->imageLoadAddress);\n      mach_vm_address_t image_address =\n          FromPointerCast<mach_vm_address_t>(mach_header);\n\n      MachOImageReader image_reader;\n      ASSERT_TRUE(\n          image_reader.Initialize(&process_reader, image_address, \"uuid\"));\n\n      // There’s no good way to get the image’s slide here, although the image\n      // should have already been checked along with its slide above, in the\n      // loop through all images.\n      ExpectMachImage(\n          mach_header, image_address, kSlideUnknown, &image_reader, false);\n\n      UUID expected_uuid;\n      expected_uuid.InitializeFromBytes(dyld_image->imageUUID);\n      UUID actual_uuid;\n      image_reader.UUID(&actual_uuid);\n      EXPECT_EQ(actual_uuid, expected_uuid);\n    }\n  }\n#endif\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_segment_reader.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/mach_o_image_segment_reader.h\"\n\n#include <Availability.h>\n#include <mach-o/loader.h>\n#include <string.h>\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"util/mac/checked_mach_address_range.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/stdlib/strnlen.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nstd::string SizeLimitedCString(const char* c_string, size_t max_length) {\n  return std::string(c_string, strnlen(c_string, max_length));\n}\n\n}  // namespace\n\nbool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,\n                                const std::string& module_name) {\n#if defined(ARCH_CPU_X86_FAMILY)\n  if (mach_o_file_type != MH_BUNDLE) {\n    return false;\n  }\n\n  if (module_name == \"cl_kernels\") {\n    return __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||\n        MacOSVersionNumber() >= 10'10'00;\n  }\n\n  static const char kCvmsObjectPathPrefix[] =\n      \"/private/var/db/CVMS/cvmsCodeSignObj\";\n  return module_name.compare(\n             0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 &&\n         (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14 ||\n          MacOSVersionNumber() >= 10'14'00);\n#else\n  return false;\n#endif  // ARCH_CPU_X86_FAMILY\n}\n\nMachOImageSegmentReader::MachOImageSegmentReader()\n    : segment_command_(),\n      sections_(),\n      section_map_(),\n      slide_(0),\n      initialized_(),\n      initialized_slide_() {\n}\n\nMachOImageSegmentReader::~MachOImageSegmentReader() {\n}\n\nbool MachOImageSegmentReader::Initialize(ProcessReaderMac* process_reader,\n                                         mach_vm_address_t load_command_address,\n                                         const std::string& load_command_info,\n                                         const std::string& module_name,\n                                         uint32_t file_type) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!segment_command_.Read(process_reader, load_command_address)) {\n    LOG(WARNING) << \"could not read segment_command\" << load_command_info;\n    return false;\n  }\n\n  const uint32_t kExpectedSegmentCommand =\n      process_reader->Is64Bit() ? LC_SEGMENT_64 : LC_SEGMENT;\n  DCHECK_EQ(segment_command_.cmd, kExpectedSegmentCommand);\n  DCHECK_GE(segment_command_.cmdsize, segment_command_.Size());\n  const size_t kSectionStructSize =\n      process_types::section::ExpectedSize(process_reader);\n  const size_t kRequiredSize =\n      segment_command_.Size() + segment_command_.nsects * kSectionStructSize;\n  if (segment_command_.cmdsize < kRequiredSize) {\n    LOG(WARNING) << base::StringPrintf(\n                        \"segment command cmdsize 0x%x insufficient for %u \"\n                        \"section%s (0x%zx)\",\n                        segment_command_.cmdsize,\n                        segment_command_.nsects,\n                        segment_command_.nsects == 1 ? \"\" : \"s\",\n                        kRequiredSize) << load_command_info;\n    return false;\n  }\n\n  std::string segment_name = NameInternal();\n  std::string segment_info = base::StringPrintf(\n      \", segment %s%s\", segment_name.c_str(), load_command_info.c_str());\n\n  // This checks the unslid segment range. The slid range (as loaded into\n  // memory) will be checked later by MachOImageReader.\n  CheckedMachAddressRange segment_range(process_reader->Is64Bit(),\n                                        segment_command_.vmaddr,\n                                        segment_command_.vmsize);\n  if (!segment_range.IsValid()) {\n    LOG(WARNING) << base::StringPrintf(\"invalid segment range 0x%llx + 0x%llx\",\n                                       segment_command_.vmaddr,\n                                       segment_command_.vmsize) << segment_info;\n    return false;\n  }\n\n  sections_.resize(segment_command_.nsects);\n  if (!sections_.empty() &&\n      !process_types::section::ReadArrayInto(\n          process_reader,\n          load_command_address + segment_command_.Size(),\n          segment_command_.nsects,\n          &sections_[0])) {\n    LOG(WARNING) << \"could not read sections\" << segment_info;\n    return false;\n  }\n\n  for (size_t section_index = 0;\n       section_index < sections_.size();\n       ++section_index) {\n    const process_types::section& section = sections_[section_index];\n    std::string section_segment_name = SegmentNameString(section.segname);\n    std::string section_name = SectionNameString(section.sectname);\n    std::string section_full_name =\n        SegmentAndSectionNameString(section.segname, section.sectname);\n\n    std::string section_info = base::StringPrintf(\", section %s %zu/%zu%s\",\n                                                  section_full_name.c_str(),\n                                                  section_index,\n                                                  sections_.size(),\n                                                  load_command_info.c_str());\n\n    // cl_kernels modules (for OpenCL) aren’t ld output, and they’re formatted\n    // incorrectly on OS X 10.10 and later. Because at least one cl_kernels\n    // module will commonly be found in a process, and sometimes more will be,\n    // tolerate this quirk.\n    //\n    // https://openradar.appspot.com/20239912\n    if (section_segment_name != segment_name &&\n        !(IsMalformedCLKernelsModule(file_type, module_name) &&\n          segment_name == SEG_TEXT && section_segment_name == \"__LD\" &&\n          section_name == \"__compact_unwind\" &&\n          (section.flags & S_ATTR_DEBUG))) {\n      LOG(WARNING) << \"section.segname incorrect in segment \" << segment_name\n                   << section_info;\n      return false;\n    }\n\n    CheckedMachAddressRange section_range(\n        process_reader->Is64Bit(), section.addr, section.size);\n    if (!section_range.IsValid()) {\n      LOG(WARNING) << base::StringPrintf(\n                          \"invalid section range 0x%llx + 0x%llx\",\n                          section.addr,\n                          section.size) << section_info;\n      return false;\n    }\n\n    if (!segment_range.ContainsRange(section_range)) {\n      LOG(WARNING) << base::StringPrintf(\n                          \"section at 0x%llx + 0x%llx outside of segment at \"\n                          \"0x%llx + 0x%llx\",\n                          section.addr,\n                          section.size,\n                          segment_command_.vmaddr,\n                          segment_command_.vmsize) << section_info;\n      return false;\n    }\n\n    const auto insert_result =\n        section_map_.insert(std::make_pair(section_name, section_index));\n    if (!insert_result.second) {\n      LOG(WARNING) << base::StringPrintf(\"duplicate section name at %zu\",\n                                         insert_result.first->second)\n                   << section_info;\n      return false;\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nstd::string MachOImageSegmentReader::Name() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return NameInternal();\n}\n\nmach_vm_address_t MachOImageSegmentReader::Address() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_);\n  return vmaddr() + (SegmentSlides() ? slide_ : 0);\n}\n\nmach_vm_size_t MachOImageSegmentReader::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_);\n  return vmsize() + (SegmentSlides() ? 0 : slide_);\n}\n\nconst process_types::section* MachOImageSegmentReader::GetSectionByName(\n    const std::string& section_name,\n    mach_vm_address_t* address) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  const auto& iterator = section_map_.find(section_name);\n  if (iterator == section_map_.end()) {\n    return nullptr;\n  }\n\n  return GetSectionAtIndex(iterator->second, address);\n}\n\nconst process_types::section* MachOImageSegmentReader::GetSectionAtIndex(\n    size_t index,\n    mach_vm_address_t* address) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  CHECK_LT(index, sections_.size());\n\n  const process_types::section* section = &sections_[index];\n\n  if (address) {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_);\n    *address = section->addr + (SegmentSlides() ? slide_ : 0);\n  }\n\n  return section;\n}\n\nbool MachOImageSegmentReader::SegmentSlides() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // These are the same rules that the kernel uses to identify __PAGEZERO. See\n  // 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c load_segment().\n  return !(segment_command_.vmaddr == 0 && segment_command_.filesize == 0 &&\n           segment_command_.vmsize != 0 &&\n           (segment_command_.initprot & VM_PROT_ALL) == VM_PROT_NONE &&\n           (segment_command_.maxprot & VM_PROT_ALL) == VM_PROT_NONE);\n}\n\n// static\nstd::string MachOImageSegmentReader::SegmentNameString(\n    const char* segment_name_c) {\n  // This is used to interpret the segname field of both the segment_command and\n  // section structures, so be sure that they’re identical.\n  static_assert(sizeof(process_types::segment_command::segname) ==\n                    sizeof(process_types::section::segname),\n                \"sizes must be equal\");\n\n  return SizeLimitedCString(segment_name_c,\n                            sizeof(process_types::segment_command::segname));\n}\n\n// static\nstd::string MachOImageSegmentReader::SectionNameString(\n    const char* section_name_c) {\n  return SizeLimitedCString(section_name_c,\n                            sizeof(process_types::section::sectname));\n}\n\n// static\nstd::string MachOImageSegmentReader::SegmentAndSectionNameString(\n    const char* segment_name_c,\n    const char* section_name_c) {\n  return base::StringPrintf(\"%s,%s\",\n                            SegmentNameString(segment_name_c).c_str(),\n                            SectionNameString(section_name_c).c_str());\n}\n\nstd::string MachOImageSegmentReader::NameInternal() const {\n  return SegmentNameString(segment_command_.segname);\n}\n\nvoid MachOImageSegmentReader::SetSlide(mach_vm_size_t slide) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_slide_);\n  slide_ = slide;\n  INITIALIZATION_STATE_SET_VALID(initialized_slide_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_segment_reader.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_\n#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_\n\n#include <mach/mach.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"snapshot/mac/process_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\n//! \\brief Determines whether a module appears to be a malformed OpenCL\n//!     `cl_kernels` module based on its name and Mach-O file type.\n//!\n//! `cl_kernels` modules require special handling because they’re malformed on\n//! OS X 10.10 and later. A `cl_kernels` module always has Mach-O type\n//! `MH_BUNDLE` and is named `\"cl_kernels\"` until macOS 10.14, and\n//! `\"/private/var/db/CVMS/cvmsCodeSignObj\"` plus 16 random characters on macOS\n//! 10.14.\n//!\n//! Malformed `cl_kernels` modules have a single `__TEXT` segment, but one of\n//! the sections within it claims to belong to the `__LD` segment. This mismatch\n//! shouldn’t happen. This errant section also has the `S_ATTR_DEBUG` flag set,\n//! which shouldn’t happen unless all of the other sections in the segment also\n//! have this bit set (they don’t). These odd sections are reminiscent of unwind\n//! information stored in `MH_OBJECT` images, although `cl_kernels` images claim\n//! to be `MH_BUNDLE`.\n//!\n//! These `cl_kernels` modules have only been observed on x86, not on arm64.\n//! This function always returns `false` on arm64.\n//!\n//! This function is exposed for testing purposes only.\n//!\n//! \\param[in] mach_o_file_type The Mach-O type of the module being examined.\n//! \\param[in] module_name The pathname that `dyld` reported having loaded the\n//!     module from.\n//!\n//! \\return `true` if the module appears to be a malformed `cl_kernels` module\n//!     based on the provided information, `false` otherwise.\nbool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,\n                                const std::string& module_name);\n\n//! \\brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O\n//!     images mapped into another process.\n//!\n//! This class is capable of reading both `LC_SEGMENT` and `LC_SEGMENT_64` based\n//! on the bitness of the remote process.\n//!\n//! A MachOImageSegmentReader will normally be instantiated by a\n//! MachOImageReader.\nclass MachOImageSegmentReader {\n public:\n  MachOImageSegmentReader();\n\n  MachOImageSegmentReader(const MachOImageSegmentReader&) = delete;\n  MachOImageSegmentReader& operator=(const MachOImageSegmentReader&) = delete;\n\n  ~MachOImageSegmentReader();\n\n  //! \\brief Reads the segment load command from another process.\n  //!\n  //! This method must only be called once on an object. This method must be\n  //! called successfully before any other method in this class may be called.\n  //!\n  //! \\param[in] process_reader The reader for the remote process.\n  //! \\param[in] load_command_address The address, in the remote process’\n  //!     address space, where the `LC_SEGMENT` or `LC_SEGMENT_64` load command\n  //!     to be read is located. This address is determined by a Mach-O image\n  //!     reader, such as MachOImageReader, as it walks Mach-O load commands.\n  //! \\param[in] load_command_info A string to be used in logged messages. This\n  //!     string is for diagnostic purposes only, and may be empty.\n  //! \\param[in] module_name The path used to load the module. This string is\n  //!     used to relax otherwise strict parsing rules for common modules with\n  //!     known defects.\n  //! \\param[in] file_type The module’s Mach-O file type. This is used to relax\n  //!     otherwise strict parsing rules for common modules with known defects.\n  //!\n  //! \\return `true` if the load command was read successfully. `false`\n  //!     otherwise, with an appropriate message logged.\n  bool Initialize(ProcessReaderMac* process_reader,\n                  mach_vm_address_t load_command_address,\n                  const std::string& load_command_info,\n                  const std::string& module_name,\n                  uint32_t file_type);\n\n  //! \\brief Sets the image’s slide value.\n  //!\n  //! This method must only be called once on an object, after Initialize() is\n  //! called successfully. It must be called before Address(), Size(),\n  //! GetSectionByName(), or GetSectionAtIndex() can be called.\n  //!\n  //! This method is provided because slide is a property of the image that\n  //! cannot be determined until at least some segments have been read. As such,\n  //! it is not necessarily known at the time that Initialize() is called.\n  void SetSlide(mach_vm_size_t slide);\n\n  //! \\brief Returns the segment’s name.\n  //!\n  //! The segment’s name is taken from the load command’s `segname` field.\n  //! Common segment names are `\"__TEXT\"`, `\"__DATA\"`, and `\"__LINKEDIT\"`.\n  //! Symbolic constants for these common names are defined in\n  //! `<mach-o/loader.h>`.\n  std::string Name() const;\n\n  //! \\return The segment’s actual load address in memory, adjusted for any\n  //!     “slide”.\n  //!\n  //! \\note For the segment’s preferred load address, not adjusted for slide,\n  //!     use vmaddr().\n  mach_vm_address_t Address() const;\n\n  //! \\return The segment’s actual size address in memory, adjusted for any\n  //!     growth in the case of a nonsliding segment.\n  //!\n  //! \\note For the segment’s preferred size, not adjusted for growth, use\n  //!     vmsize().\n  mach_vm_address_t Size() const;\n\n  //! \\brief The segment’s preferred load address.\n  //!\n  //! \\return The segment’s preferred load address as stored in the Mach-O file.\n  //!\n  //! \\note This value is not adjusted for any “slide” that may have occurred\n  //!     when the image was loaded. Use Address() for a value adjusted for\n  //!     slide.\n  //!\n  //! \\sa MachOImageReader::GetSegmentByName()\n  mach_vm_address_t vmaddr() const { return segment_command_.vmaddr; }\n\n  //! \\brief Returns the segment’s size as mapped into memory.\n  //!\n  //! \\note For non-sliding segments, this value is not adjusted for any growth\n  //!     that may have occurred when the image was loaded. Use Size() for a\n  //!     value adjusted for growth.\n  mach_vm_size_t vmsize() const { return segment_command_.vmsize; }\n\n  //! \\brief Returns the file offset of the mapped segment in the file from\n  //!     which it was mapped.\n  //!\n  //! The file offset is the difference between the beginning of the\n  //! `mach_header` or `mach_header_64` and the beginning of the segment’s\n  //! mapped region. For segments that are not mapped from a file (such as\n  //! `__PAGEZERO` segments), this will be `0`.\n  mach_vm_size_t fileoff() const { return segment_command_.fileoff; }\n\n  //! \\brief Returns the number of sections in the segment.\n  //!\n  //! This will return `0` for a segment without any sections, typical for\n  //! `__PAGEZERO` and `__LINKEDIT` segments.\n  //!\n  //! Although the Mach-O file format uses a `uint32_t` for this field, there is\n  //! an overall limit of 255 sections in an entire Mach-O image file (not just\n  //! in a single segment) imposed by the symbol table format. Symbols will not\n  //! be able to reference anything in a section beyond the first 255 in a\n  //! Mach-O image file.\n  uint32_t nsects() const { return segment_command_.nsects; }\n\n  //! \\brief Obtain section information by section name.\n  //!\n  //! \\param[in] section_name The name of the section to search for, without the\n  //!     leading segment name. For example, use `\"__text\"`, not\n  //!     `\"__TEXT,__text\"` or `\"__TEXT.__text\"`.\n  //! \\param[out] address The actual address that the section was loaded at in\n  //!     memory, taking any “slide” into account if the section did not load at\n  //!     its preferred address as stored in the Mach-O image file. This\n  //!     parameter can be `nullptr`.\n  //!\n  //! \\return A pointer to the section information if it was found, or `nullptr`\n  //!     if it was not found. The caller does not take ownership; the lifetime\n  //!     of the returned object is scoped to the lifetime of this\n  //!     MachOImageSegmentReader object.\n  //!\n  //! \\note The process_types::section::addr field gives the section’s preferred\n  //!     load address as stored in the Mach-O image file, and is not adjusted\n  //!     for any “slide” that may have occurred when the image was loaded.\n  //!\n  //! \\sa MachOImageReader::GetSectionByName()\n  const process_types::section* GetSectionByName(\n      const std::string& section_name,\n      mach_vm_address_t* address) const;\n\n  //! \\brief Obtain section information by section index.\n  //!\n  //! \\param[in] index The index of the section to return, in the order that it\n  //!     appears in the segment load command. Unlike\n  //!     MachOImageReader::GetSectionAtIndex(), this is a 0-based index. This\n  //!     parameter must be in the range of valid indices aas reported by\n  //!     nsects().\n  //! \\param[out] address The actual address that the section was loaded at in\n  //!     memory, taking any “slide” into account if the section did not load at\n  //!     its preferred address as stored in the Mach-O image file. This\n  //!     parameter can be `nullptr`.\n  //!\n  //! \\return A pointer to the section information. If \\a index is out of range,\n  //!     execution is aborted.  The caller does not take ownership; the\n  //!     lifetime of the returned object is scoped to the lifetime of this\n  //!     MachOImageSegmentReader object.\n  //!\n  //! \\note The process_types::section::addr field gives the section’s preferred\n  //!     load address as stored in the Mach-O image file, and is not adjusted\n  //!     for any “slide” that may have occurred when the image was loaded.\n  //! \\note Unlike MachOImageReader::GetSectionAtIndex(), this method does not\n  //!     accept out-of-range values for \\a index, and aborts execution instead\n  //!     of returning `nullptr` upon encountering an out-of-range value. This\n  //!     is because this method is expected to be used in a loop that can be\n  //!     limited to nsects() iterations, so an out-of-range error can be\n  //!     treated more harshly as a logic error, as opposed to a data error.\n  //!\n  //! \\sa MachOImageReader::GetSectionAtIndex()\n  const process_types::section* GetSectionAtIndex(\n      size_t index,\n      mach_vm_address_t* address) const;\n\n  //! Returns whether the segment slides.\n  //!\n  //! Most segments slide, but the `__PAGEZERO` segment does not, it grows\n  //! instead. This method identifies non-sliding segments in the same way that\n  //! the kernel does.\n  bool SegmentSlides() const;\n\n  //! \\brief Returns a segment name string.\n  //!\n  //! Segment names may be 16 characters long, and are not necessarily\n  //! `NUL`-terminated. This function will return a segment name based on up to\n  //! the first 16 characters found at \\a segment_name_c.\n  static std::string SegmentNameString(const char* segment_name_c);\n\n  //! \\brief Returns a section name string.\n  //!\n  //! Section names may be 16 characters long, and are not necessarily\n  //! `NUL`-terminated. This function will return a section name based on up to\n  //! the first 16 characters found at \\a section_name_c.\n  static std::string SectionNameString(const char* section_name_c);\n\n  //! \\brief Returns a segment and section name string.\n  //!\n  //! A segment and section name string is composed of a segment name string\n  //! (see SegmentNameString()) and a section name string (see\n  //! SectionNameString()) separated by a comma. An example is\n  //! `\"__TEXT,__text\"`.\n  static std::string SegmentAndSectionNameString(const char* segment_name_c,\n                                                 const char* section_name_c);\n\n private:\n  //! \\brief The internal implementation of Name().\n  //!\n  //! This is identical to Name() but does not perform the\n  //! InitializationStateDcheck check. It may be called during initialization\n  //! provided that the caller only does so after segment_command_ has been\n  //! read successfully.\n  std::string NameInternal() const;\n\n  // The segment command data read from the remote process.\n  process_types::segment_command segment_command_;\n\n  // Section structures read from the remote process in the order that they are\n  // given in the remote process.\n  std::vector<process_types::section> sections_;\n\n  // Maps section names to indices into the sections_ vector.\n  std::map<std::string, size_t> section_map_;\n\n  // The image’s slide. Note that the segment’s slide may be 0 and not the value\n  // of the image’s slide if SegmentSlides() is false. In that case, the\n  // segment is extended instead of slid, so its size as loaded will be\n  // increased by this value.\n  mach_vm_size_t slide_;\n\n  InitializationStateDcheck initialized_;\n  InitializationStateDcheck initialized_slide_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_segment_reader_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/mach_o_image_segment_reader.h\"\n\n#include <mach-o/loader.h>\n\n#include <iterator>\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Most of MachOImageSegmentReader is tested as part of MachOImageReader, which\n// depends on MachOImageSegmentReader to provide major portions of its\n// functionality. Because MachOImageSegmentReader is difficult to use except by\n// a Mach-O load command reader such as MachOImageReader, these portions\n// of MachOImageSegmentReader are not tested independently.\n//\n// The tests here exercise the portions of MachOImageSegmentReader that are\n// exposed and independently useful.\n\nTEST(MachOImageSegmentReader, SegmentNameString) {\n  // The output value should be a string of up to 16 characters, even if the\n  // input value is not NUL-terminated within 16 characters.\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"__TEXT\"), \"__TEXT\");\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"__OVER\"), \"__OVER\");\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"\"), \"\");\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"p\"), \"p\");\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"NoUnderChar\"),\n            \"NoUnderChar\");\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"0123456789abcde\"),\n            \"0123456789abcde\");\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"0123456789abcdef\"),\n            \"0123456789abcdef\");\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"gfedcba9876543210\"),\n            \"gfedcba987654321\");\n  EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(\"hgfedcba9876543210\"),\n            \"hgfedcba98765432\");\n\n  // Segment names defined in <mach-o/loader.h>. All of these should come\n  // through SegmentNameString() cleanly and without truncation.\n  static constexpr const char* kSegmentTestData[] = {\n      SEG_TEXT,\n      SEG_DATA,\n      SEG_OBJC,\n      SEG_ICON,\n      SEG_LINKEDIT,\n      SEG_UNIXSTACK,\n      SEG_IMPORT,\n  };\n\n  for (size_t index = 0; index < std::size(kSegmentTestData); ++index) {\n    EXPECT_EQ(\n        MachOImageSegmentReader::SegmentNameString(kSegmentTestData[index]),\n        kSegmentTestData[index])\n        << base::StringPrintf(\"index %zu\", index);\n  }\n}\n\nTEST(MachOImageSegmentReader, SectionNameString) {\n  // The output value should be a string of up to 16 characters, even if the\n  // input value is not NUL-terminated within 16 characters.\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"__text\"), \"__text\");\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"__over\"), \"__over\");\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"\"), \"\");\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"p\"), \"p\");\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"NoUnderChar\"),\n            \"NoUnderChar\");\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"0123456789abcde\"),\n            \"0123456789abcde\");\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"0123456789abcdef\"),\n            \"0123456789abcdef\");\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"gfedcba9876543210\"),\n            \"gfedcba987654321\");\n  EXPECT_EQ(MachOImageSegmentReader::SectionNameString(\"hgfedcba9876543210\"),\n            \"hgfedcba98765432\");\n\n  // Section names defined in <mach-o/loader.h>. All of these should come\n  // through SectionNameString() cleanly and without truncation.\n  static constexpr const char* kSectionTestData[] = {\n      SECT_TEXT,\n      SECT_FVMLIB_INIT0,\n      SECT_FVMLIB_INIT1,\n      SECT_DATA,\n      SECT_BSS,\n      SECT_COMMON,\n      SECT_OBJC_SYMBOLS,\n      SECT_OBJC_MODULES,\n      SECT_OBJC_STRINGS,\n      SECT_OBJC_REFS,\n      SECT_ICON_HEADER,\n      SECT_ICON_TIFF,\n  };\n\n  for (size_t index = 0; index < std::size(kSectionTestData); ++index) {\n    EXPECT_EQ(\n        MachOImageSegmentReader::SectionNameString(kSectionTestData[index]),\n        kSectionTestData[index])\n        << base::StringPrintf(\"index %zu\", index);\n  }\n}\n\nTEST(MachOImageSegmentReader, SegmentAndSectionNameString) {\n  static constexpr struct {\n    const char* segment;\n    const char* section;\n    const char* output;\n  } kSegmentAndSectionTestData[] = {\n      {\"segment\", \"section\", \"segment,section\"},\n      {\"Segment\", \"Section\", \"Segment,Section\"},\n      {\"SEGMENT\", \"SECTION\", \"SEGMENT,SECTION\"},\n      {\"__TEXT\", \"__plain\", \"__TEXT,__plain\"},\n      {\"__TEXT\", \"poetry\", \"__TEXT,poetry\"},\n      {\"__TEXT\", \"Prose\", \"__TEXT,Prose\"},\n      {\"__PLAIN\", \"__text\", \"__PLAIN,__text\"},\n      {\"rich\", \"__text\", \"rich,__text\"},\n      {\"segment\", \"\", \"segment,\"},\n      {\"\", \"section\", \",section\"},\n      {\"\", \"\", \",\"},\n      {\"0123456789abcdef\", \"section\", \"0123456789abcdef,section\"},\n      {\"gfedcba9876543210\", \"section\", \"gfedcba987654321,section\"},\n      {\"0123456789abcdef\", \"\", \"0123456789abcdef,\"},\n      {\"gfedcba9876543210\", \"\", \"gfedcba987654321,\"},\n      {\"segment\", \"0123456789abcdef\", \"segment,0123456789abcdef\"},\n      {\"segment\", \"gfedcba9876543210\", \"segment,gfedcba987654321\"},\n      {\"\", \"0123456789abcdef\", \",0123456789abcdef\"},\n      {\"\", \"gfedcba9876543210\", \",gfedcba987654321\"},\n      {\"0123456789abcdef\",\n       \"0123456789abcdef\",\n       \"0123456789abcdef,0123456789abcdef\"},\n      {\"gfedcba9876543210\",\n       \"gfedcba9876543210\",\n       \"gfedcba987654321,gfedcba987654321\"},\n\n      // Sections defined in <mach-o/loader.h>. All of these should come through\n      // SegmentAndSectionNameString() cleanly and without truncation.\n      {SEG_TEXT, SECT_TEXT, \"__TEXT,__text\"},\n      {SEG_TEXT, SECT_FVMLIB_INIT0, \"__TEXT,__fvmlib_init0\"},\n      {SEG_TEXT, SECT_FVMLIB_INIT1, \"__TEXT,__fvmlib_init1\"},\n      {SEG_DATA, SECT_DATA, \"__DATA,__data\"},\n      {SEG_DATA, SECT_BSS, \"__DATA,__bss\"},\n      {SEG_DATA, SECT_COMMON, \"__DATA,__common\"},\n      {SEG_OBJC, SECT_OBJC_SYMBOLS, \"__OBJC,__symbol_table\"},\n      {SEG_OBJC, SECT_OBJC_MODULES, \"__OBJC,__module_info\"},\n      {SEG_OBJC, SECT_OBJC_STRINGS, \"__OBJC,__selector_strs\"},\n      {SEG_OBJC, SECT_OBJC_REFS, \"__OBJC,__selector_refs\"},\n      {SEG_ICON, SECT_ICON_HEADER, \"__ICON,__header\"},\n      {SEG_ICON, SECT_ICON_TIFF, \"__ICON,__tiff\"},\n\n      // These segments don’t normally have sections, but the above group tested\n      // the known segment names for segments that do normally have sections.\n      // This group does the same for segments that normally don’t.\n      {SEG_LINKEDIT, \"\", \"__LINKEDIT,\"},\n      {SEG_UNIXSTACK, \"\", \"__UNIXSTACK,\"},\n      {SEG_IMPORT, \"\", \"__IMPORT,\"},\n  };\n\n  for (size_t index = 0; index < std::size(kSegmentAndSectionTestData);\n       ++index) {\n    const auto& test = kSegmentAndSectionTestData[index];\n    EXPECT_EQ(MachOImageSegmentReader::SegmentAndSectionNameString(\n                  test.segment, test.section),\n              test.output)\n        << base::StringPrintf(\"index %zu, segment %s, section %s\",\n                              index,\n                              test.segment,\n                              test.section);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_symbol_table_reader.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/mach_o_image_symbol_table_reader.h\"\n\n#include <mach-o/loader.h>\n#include <mach-o/nlist.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <utility>\n\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"util/mac/checked_mach_address_range.h\"\n#include \"util/process/process_memory_mac.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\n//! \\brief The internal implementation for MachOImageSymbolTableReader.\n//!\n//! Initialization is broken into more than one function that needs to share\n//! data, so member variables are used. However, much of this data is irrelevant\n//! after initialization is completed, so rather than doing it in\n//! MachOImageSymbolTableReader, it’s handled by this class, which is a “friend”\n//! of MachOImageSymbolTableReader.\nclass MachOImageSymbolTableReaderInitializer {\n public:\n  MachOImageSymbolTableReaderInitializer(\n      ProcessReaderMac* process_reader,\n      const MachOImageSegmentReader* linkedit_segment,\n      const std::string& module_info)\n      : module_info_(module_info),\n        linkedit_range_(),\n        process_reader_(process_reader),\n        linkedit_segment_(linkedit_segment) {\n    linkedit_range_.SetRange(process_reader_->Is64Bit(),\n                             linkedit_segment->Address(),\n                             linkedit_segment->Size());\n    DCHECK(linkedit_range_.IsValid());\n  }\n\n  MachOImageSymbolTableReaderInitializer(\n      const MachOImageSymbolTableReaderInitializer&) = delete;\n  MachOImageSymbolTableReaderInitializer& operator=(\n      const MachOImageSymbolTableReaderInitializer&) = delete;\n\n  ~MachOImageSymbolTableReaderInitializer() {}\n\n  //! \\brief Reads the symbol table from another process.\n  //!\n  //! \\sa MachOImageSymbolTableReader::Initialize()\n  bool Initialize(const process_types::symtab_command* symtab_command,\n                  const process_types::dysymtab_command* dysymtab_command,\n                  MachOImageSymbolTableReader::SymbolInformationMap*\n                      external_defined_symbols) {\n    mach_vm_address_t symtab_address =\n        AddressForLinkEditComponent(symtab_command->symoff);\n    uint32_t symbol_count = symtab_command->nsyms;\n    size_t nlist_size = process_types::nlist::ExpectedSize(process_reader_);\n    mach_vm_size_t symtab_size = symbol_count * nlist_size;\n    if (!IsInLinkEditSegment(symtab_address, symtab_size, \"symtab\")) {\n      return false;\n    }\n\n    // If a dysymtab is present, use it to filter the symtab for just the\n    // portion used for extdefsym. If no dysymtab is present, the entire symtab\n    // will need to be consulted.\n    uint32_t skip_count = 0;\n    if (dysymtab_command) {\n      if (dysymtab_command->iextdefsym >= symtab_command->nsyms ||\n          dysymtab_command->iextdefsym + dysymtab_command->nextdefsym >\n              symtab_command->nsyms) {\n        LOG(WARNING) << base::StringPrintf(\n                            \"dysymtab extdefsym %u + %u > symtab nsyms %u\",\n                            dysymtab_command->iextdefsym,\n                            dysymtab_command->nextdefsym,\n                            symtab_command->nsyms) << module_info_;\n        return false;\n      }\n\n      skip_count = dysymtab_command->iextdefsym;\n      mach_vm_size_t skip_size = skip_count * nlist_size;\n      symtab_address += skip_size;\n      symtab_size -= skip_size;\n      symbol_count = dysymtab_command->nextdefsym;\n    }\n\n    mach_vm_address_t strtab_address =\n        AddressForLinkEditComponent(symtab_command->stroff);\n    mach_vm_size_t strtab_size = symtab_command->strsize;\n    if (!IsInLinkEditSegment(strtab_address, strtab_size, \"strtab\")) {\n      return false;\n    }\n\n    std::unique_ptr<process_types::nlist[]> symbols(\n        new process_types::nlist[symtab_command->nsyms]);\n    if (!process_types::nlist::ReadArrayInto(\n            process_reader_, symtab_address, symbol_count, &symbols[0])) {\n      LOG(WARNING) << \"could not read symbol table\" << module_info_;\n      return false;\n    }\n\n    std::unique_ptr<ProcessMemoryMac::MappedMemory> string_table;\n    for (size_t symbol_index = 0; symbol_index < symbol_count; ++symbol_index) {\n      const process_types::nlist& symbol = symbols[symbol_index];\n      std::string symbol_info = base::StringPrintf(\", symbol index %zu%s\",\n                                                   skip_count + symbol_index,\n                                                   module_info_.c_str());\n      bool valid_symbol = true;\n      if ((symbol.n_type & N_STAB) == 0 && (symbol.n_type & N_PEXT) == 0 &&\n          (symbol.n_type & N_EXT)) {\n        uint8_t symbol_type = symbol.n_type & N_TYPE;\n        if (symbol_type == N_ABS || symbol_type == N_SECT) {\n          if (symbol.n_strx >= strtab_size) {\n            LOG(WARNING) << base::StringPrintf(\n                                \"string at 0x%x out of bounds (0x%llx)\",\n                                symbol.n_strx,\n                                strtab_size) << symbol_info;\n            return false;\n          }\n\n          if (!string_table) {\n            string_table = process_reader_->Memory()->ReadMapped(\n                strtab_address, strtab_size);\n            if (!string_table) {\n              LOG(WARNING) << \"could not read string table\" << module_info_;\n              return false;\n            }\n          }\n\n          std::string name;\n          if (!string_table->ReadCString(symbol.n_strx, &name)) {\n            LOG(WARNING) << \"could not read string\" << symbol_info;\n            return false;\n          }\n\n          if (symbol_type == N_ABS && symbol.n_sect != NO_SECT) {\n            LOG(WARNING) << base::StringPrintf(\"N_ABS symbol %s in section %u\",\n                                               name.c_str(),\n                                               symbol.n_sect) << symbol_info;\n            return false;\n          }\n\n          if (symbol_type == N_SECT && symbol.n_sect == NO_SECT) {\n            LOG(WARNING) << base::StringPrintf(\n                                \"N_SECT symbol %s in section NO_SECT\",\n                                name.c_str()) << symbol_info;\n            return false;\n          }\n\n          MachOImageSymbolTableReader::SymbolInformation this_symbol_info;\n          this_symbol_info.value = symbol.n_value;\n          this_symbol_info.section = symbol.n_sect;\n          if (!external_defined_symbols->insert(\n                  std::make_pair(name, this_symbol_info)).second) {\n            LOG(WARNING) << \"duplicate symbol \" << name << symbol_info;\n            return false;\n          }\n        } else {\n          // External indirect symbols may be found in the portion of the symbol\n          // table used for external symbols as opposed to indirect symbols when\n          // the indirect symbols are also external. These can be produced by\n          // Xcode 5.1 ld64-236.3/src/ld/LinkEditClassic.hpp\n          // ld::tool::SymbolTableAtom<>::addGlobal(). Indirect symbols are not\n          // currently supported by this symbol table reader, so ignore them\n          // without failing or logging a message when encountering them. See\n          // https://groups.google.com/a/chromium.org/d/topic/crashpad-dev/k7QkLwO71Zo\n          valid_symbol = symbol_type == N_INDR;\n        }\n      } else {\n        valid_symbol = false;\n      }\n      if (!valid_symbol && dysymtab_command) {\n        LOG(WARNING) << \"non-external symbol with type \" << symbol.n_type\n                     << \" in extdefsym\" << symbol_info;\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n private:\n  //! \\brief Computes the address for data in the `__LINKEDIT` segment\n  //!     identified by its file offset in a Mach-O image.\n  //!\n  //! \\param[in] fileoff The file offset relative to the beginning of an image’s\n  //!     `mach_header` or `mach_header_64` of the data in the `__LINKEDIT`\n  //!     segment.\n  //!\n  //! \\return The address, in the remote process’ address space, of the\n  //!     requested data.\n  mach_vm_address_t AddressForLinkEditComponent(uint32_t fileoff) const {\n    return linkedit_range_.Base() + fileoff - linkedit_segment_->fileoff();\n  }\n\n  //! \\brief Determines whether an address range is located within the\n  //!     `__LINKEDIT` segment.\n  //!\n  //! \\param[in] address The base address of the range to check.\n  //! \\param[in] size The size of the range to check.\n  //! \\param[in] tag A string that identifies the range being checked. This is\n  //!     used only for logging.\n  //!\n  //! \\return `true` if the range identified by \\a address + \\a size lies\n  //!     entirely within the `__LINKEDIT` segment. `false` if that range is\n  //!     invalid, or if that range is not contained by the `__LINKEDIT`\n  //!     segment, with an appropriate message logged.\n  bool IsInLinkEditSegment(mach_vm_address_t address,\n                           mach_vm_size_t size,\n                           const char* tag) const {\n    CheckedMachAddressRange subrange(process_reader_->Is64Bit(), address, size);\n    if (!subrange.IsValid()) {\n      LOG(WARNING) << base::StringPrintf(\"invalid %s range (0x%llx + 0x%llx)\",\n                                         tag,\n                                         address,\n                                         size) << module_info_;\n      return false;\n    }\n\n    if (!linkedit_range_.ContainsRange(subrange)) {\n      LOG(WARNING) << base::StringPrintf(\n                          \"%s at 0x%llx + 0x%llx outside of \" SEG_LINKEDIT\n                          \" segment at 0x%llx + 0x%llx\",\n                          tag,\n                          address,\n                          size,\n                          linkedit_range_.Base(),\n                          linkedit_range_.Size()) << module_info_;\n      return false;\n    }\n\n    return true;\n  }\n\n  std::string module_info_;\n  CheckedMachAddressRange linkedit_range_;\n  ProcessReaderMac* process_reader_;  // weak\n  const MachOImageSegmentReader* linkedit_segment_;  // weak\n};\n\n}  // namespace internal\n\nMachOImageSymbolTableReader::MachOImageSymbolTableReader()\n    : external_defined_symbols_(), initialized_() {\n}\n\nMachOImageSymbolTableReader::~MachOImageSymbolTableReader() {\n}\n\nbool MachOImageSymbolTableReader::Initialize(\n    ProcessReaderMac* process_reader,\n    const process_types::symtab_command* symtab_command,\n    const process_types::dysymtab_command* dysymtab_command,\n    const MachOImageSegmentReader* linkedit_segment,\n    const std::string& module_info) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  internal::MachOImageSymbolTableReaderInitializer initializer(process_reader,\n                                                               linkedit_segment,\n                                                               module_info);\n  if (!initializer.Initialize(\n          symtab_command, dysymtab_command, &external_defined_symbols_)) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst MachOImageSymbolTableReader::SymbolInformation*\nMachOImageSymbolTableReader::LookUpExternalDefinedSymbol(\n    const std::string& name) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  const auto& iterator = external_defined_symbols_.find(name);\n  if (iterator == external_defined_symbols_.end()) {\n    return nullptr;\n  }\n  return &iterator->second;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/mach_o_image_symbol_table_reader.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_\n#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_\n\n#include <map>\n#include <string>\n\n#include <mach/mach.h>\n#include <stdint.h>\n\n#include \"snapshot/mac/mach_o_image_segment_reader.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"snapshot/mac/process_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\n//! \\brief A reader for symbol tables in Mach-O images mapped into another\n//!     process.\nclass MachOImageSymbolTableReader {\n public:\n  //! \\brief Information about a symbol in a module’s symbol table.\n  //!\n  //! This is a more minimal form of the `nlist` (or `nlist_64`) structure,\n  //! only containing the equivalent of the `n_value` and `n_sect` fields.\n  struct SymbolInformation {\n    //! \\brief The address of the symbol as it exists in the symbol table, not\n    //!     adjusted for any “slide.”\n    mach_vm_address_t value;\n\n    //! \\brief The 1-based section index in the module in which the symbol is\n    //!     found.\n    //!\n    //! For symbols defined in a section (`N_SECT`), this is the section index\n    //! that can be passed to MachOImageReader::GetSectionAtIndex(), and \\a\n    //! value will need to be adjusted for segment slide if the containing\n    //! segment slid when loaded. For absolute symbols (`N_ABS`), this will be\n    //! `NO_SECT` (`0`), and \\a value must not be adjusted for segment slide.\n    uint8_t section;\n  };\n\n  // TODO(mark): Use std::unordered_map or a similar hash-based map? For now,\n  // std::map is fine because this map only stores external defined symbols, and\n  // there aren’t expected to be very many of those that performance would\n  // become a problem. In reality, std::unordered_map does not appear to provide\n  // a performance advantage. It appears that the memory copies currently done\n  // by ProcessMemoryMac::Read() have substantially more impact on symbol table\n  // operations.\n  //\n  // This is public so that the type is available to\n  // MachOImageSymbolTableReaderInitializer.\n  using SymbolInformationMap = std::map<std::string, SymbolInformation>;\n\n  MachOImageSymbolTableReader();\n\n  MachOImageSymbolTableReader(const MachOImageSymbolTableReader&) = delete;\n  MachOImageSymbolTableReader& operator=(const MachOImageSymbolTableReader&) =\n      delete;\n\n  ~MachOImageSymbolTableReader();\n\n  //! \\brief Reads the symbol table from another process.\n  //!\n  //! This method must only be called once on an object. This method must be\n  //! called successfully before any other method in this class may be called.\n  //!\n  //! \\param[in] process_reader The reader for the remote process.\n  //! \\param[in] symtab_command The `LC_SYMTAB` load command that identifies\n  //!     the symbol table.\n  //! \\param[in] dysymtab_command The `LC_DYSYMTAB` load command that identifies\n  //!     dynamic symbol information within the symbol table. This load command\n  //!     is not present in all modules, and this parameter may be `nullptr` for\n  //!     modules that do not have this information. When present, \\a\n  //!     dysymtab_command is an optimization that allows the symbol table\n  //!     reader to only examine symbol table entries known to be relevant for\n  //!     its purposes.\n  //! \\param[in] linkedit_segment The `__LINKEDIT` segment. This segment should\n  //!     contain the data referenced by \\a symtab_command and \\a\n  //!     dysymtab_command. This may be any segment in the module, but by\n  //!     convention, the name `__LINKEDIT` is used for this purpose.\n  //! \\param[in] module_info A string to be used in logged messages. This string\n  //!     is for diagnostic purposes only, and may be empty.\n  //!\n  //! \\return `true` if the symbol table was read successfully. `false`\n  //!     otherwise, with an appropriate message logged.\n  bool Initialize(ProcessReaderMac* process_reader,\n                  const process_types::symtab_command* symtab_command,\n                  const process_types::dysymtab_command* dysymtab_command,\n                  const MachOImageSegmentReader* linkedit_segment,\n                  const std::string& module_info);\n\n  //! \\brief Looks up a symbol in the image’s symbol table.\n  //!\n  //! The returned information captures the symbol as it exists in the image’s\n  //! symbol table, not adjusted for any “slide.”\n  //!\n  //! \\param[in] name The name of the symbol to look up, “mangled” or\n  //!     “decorated” appropriately. For example, use `\"_main\"` to look up the\n  //!     symbol for the C `main()` function, and use `\"__Z4Funcv\"` to look up\n  //!     the symbol for the C++ `Func()` function.\n  //!\n  //! \\return A SymbolInformation* object with information about the symbol if\n  //!     it was found, or `nullptr` if the symbol was not found or if an error\n  //!     occurred. On error, a warning message will also be logged. The caller\n  //!     does not take ownership; the lifetime of the returned object is scoped\n  //!     to the lifetime of this MachOImageSymbolTableReader object.\n  //!\n  //! \\note Symbol values returned via this interface are not adjusted for\n  //!     “slide.” For slide-adjusted values, use the higher-level\n  //!     MachOImageReader::LookUpExternalDefinedSymbol() interface.\n  const SymbolInformation* LookUpExternalDefinedSymbol(\n      const std::string& name) const;\n\n private:\n  SymbolInformationMap external_defined_symbols_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_\n"
  },
  {
    "path": "snapshot/mac/module_snapshot_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/module_snapshot_mac.h\"\n\n#include <mach-o/loader.h>\n#include <mach/mach.h>\n\n#include \"base/files/file_path.h\"\n#include \"snapshot/mac/mach_o_image_annotations_reader.h\"\n#include \"snapshot/mac/mach_o_image_reader.h\"\n#include \"util/misc/tri_state.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/stdlib/strnlen.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nModuleSnapshotMac::ModuleSnapshotMac()\n    : ModuleSnapshot(),\n      name_(),\n      timestamp_(0),\n      mach_o_image_reader_(nullptr),\n      process_reader_(nullptr),\n      initialized_() {}\n\nModuleSnapshotMac::~ModuleSnapshotMac() {}\n\nbool ModuleSnapshotMac::Initialize(\n    ProcessReaderMac* process_reader,\n    const ProcessReaderMac::Module& process_reader_module) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  process_reader_ = process_reader;\n  name_ = process_reader_module.name;\n  timestamp_ = process_reader_module.timestamp;\n  mach_o_image_reader_ = process_reader_module.reader;\n  if (!mach_o_image_reader_) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nvoid ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  process_types::CrashpadInfo crashpad_info;\n  if (!mach_o_image_reader_->GetCrashpadInfo(&crashpad_info)) {\n    options->crashpad_handler_behavior = TriState::kUnset;\n    options->system_crash_reporter_forwarding = TriState::kUnset;\n    options->gather_indirectly_referenced_memory = TriState::kUnset;\n    return;\n  }\n\n  options->crashpad_handler_behavior =\n      CrashpadInfoClientOptions::TriStateFromCrashpadInfo(\n          crashpad_info.crashpad_handler_behavior);\n\n  options->system_crash_reporter_forwarding =\n      CrashpadInfoClientOptions::TriStateFromCrashpadInfo(\n          crashpad_info.system_crash_reporter_forwarding);\n\n  options->gather_indirectly_referenced_memory =\n      CrashpadInfoClientOptions::TriStateFromCrashpadInfo(\n          crashpad_info.gather_indirectly_referenced_memory);\n\n  options->indirectly_referenced_memory_cap =\n      crashpad_info.indirectly_referenced_memory_cap;\n}\n\nstd::string ModuleSnapshotMac::Name() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return name_;\n}\n\nuint64_t ModuleSnapshotMac::Address() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return mach_o_image_reader_->Address();\n}\n\nuint64_t ModuleSnapshotMac::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return mach_o_image_reader_->Size();\n}\n\ntime_t ModuleSnapshotMac::Timestamp() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return timestamp_;\n}\n\nvoid ModuleSnapshotMac::FileVersion(uint16_t* version_0,\n                                    uint16_t* version_1,\n                                    uint16_t* version_2,\n                                    uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (mach_o_image_reader_->FileType() == MH_DYLIB) {\n    uint32_t dylib_version = mach_o_image_reader_->DylibVersion();\n    *version_0 = (dylib_version & 0xffff0000) >> 16;\n    *version_1 = (dylib_version & 0x0000ff00) >> 8;\n    *version_2 = (dylib_version & 0x000000ff);\n    *version_3 = 0;\n  } else {\n    *version_0 = 0;\n    *version_1 = 0;\n    *version_2 = 0;\n    *version_3 = 0;\n  }\n}\n\nvoid ModuleSnapshotMac::SourceVersion(uint16_t* version_0,\n                                      uint16_t* version_1,\n                                      uint16_t* version_2,\n                                      uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // LC_SOURCE_VERSION is supposed to be interpreted as a 5-component version\n  // number, 24 bits for the first component and 10 for the others, per\n  // <mach-o/loader.h>. To preserve the full range of possible version numbers\n  // without data loss, map it to the 4 16-bit fields mandated by the interface\n  // here, which was informed by the minidump file format.\n  uint64_t source_version = mach_o_image_reader_->SourceVersion();\n  *version_0 = (source_version & 0xffff000000000000u) >> 48;\n  *version_1 = (source_version & 0x0000ffff00000000u) >> 32;\n  *version_2 = (source_version & 0x00000000ffff0000u) >> 16;\n  *version_3 = source_version & 0x000000000000ffffu;\n}\n\nModuleSnapshot::ModuleType ModuleSnapshotMac::GetModuleType() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  uint32_t file_type = mach_o_image_reader_->FileType();\n  switch (file_type) {\n    case MH_EXECUTE:\n      return kModuleTypeExecutable;\n    case MH_DYLIB:\n      return kModuleTypeSharedLibrary;\n    case MH_DYLINKER:\n      return kModuleTypeDynamicLoader;\n    case MH_BUNDLE:\n      return kModuleTypeLoadableModule;\n    default:\n      return kModuleTypeUnknown;\n  }\n}\n\nvoid ModuleSnapshotMac::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  mach_o_image_reader_->UUID(uuid);\n  *age = 0;\n}\n\nstd::string ModuleSnapshotMac::DebugFileName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return base::FilePath(Name()).BaseName().value();\n}\n\nstd::vector<uint8_t> ModuleSnapshotMac::BuildID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<uint8_t>();\n}\n\nstd::vector<std::string> ModuleSnapshotMac::AnnotationsVector() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  MachOImageAnnotationsReader annotations_reader(\n      process_reader_, mach_o_image_reader_, name_);\n  return annotations_reader.Vector();\n}\n\nstd::map<std::string, std::string> ModuleSnapshotMac::AnnotationsSimpleMap()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  MachOImageAnnotationsReader annotations_reader(\n      process_reader_, mach_o_image_reader_, name_);\n  return annotations_reader.SimpleMap();\n}\n\nstd::vector<AnnotationSnapshot> ModuleSnapshotMac::AnnotationObjects() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  MachOImageAnnotationsReader annotations_reader(\n      process_reader_, mach_o_image_reader_, name_);\n  return annotations_reader.AnnotationsList();\n}\n\nstd::set<CheckedRange<uint64_t>> ModuleSnapshotMac::ExtraMemoryRanges() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::set<CheckedRange<uint64_t>>();\n}\n\nstd::vector<const UserMinidumpStream*>\nModuleSnapshotMac::CustomMinidumpStreams() const {\n  return std::vector<const UserMinidumpStream*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/module_snapshot_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_\n#define CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"client/crashpad_info.h\"\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nclass MachOImageReader;\nstruct UUID;\n\nnamespace internal {\n\n//! \\brief A ModuleSnapshot of a code module (binary image) loaded into a\n//!     running (or crashed) process on a macOS system.\nclass ModuleSnapshotMac final : public ModuleSnapshot {\n public:\n  ModuleSnapshotMac();\n\n  ModuleSnapshotMac(const ModuleSnapshotMac&) = delete;\n  ModuleSnapshotMac& operator=(const ModuleSnapshotMac&) = delete;\n\n  ~ModuleSnapshotMac() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderMac for the task containing the\n  //!     module.\n  //! \\param[in] process_reader_module The module within the ProcessReaderMac\n  //!     for which the snapshot should be created.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(ProcessReaderMac* process_reader,\n                  const ProcessReaderMac::Module& process_reader_module);\n\n  //! \\brief Returns options from the module’s CrashpadInfo structure.\n  //!\n  //! \\param[out] options Options set in the module’s CrashpadInfo structure.\n  void GetCrashpadOptions(CrashpadInfoClientOptions* options);\n\n  // ModuleSnapshot:\n\n  std::string Name() const override;\n  uint64_t Address() const override;\n  uint64_t Size() const override;\n  time_t Timestamp() const override;\n  void FileVersion(uint16_t* version_0,\n                   uint16_t* version_1,\n                   uint16_t* version_2,\n                   uint16_t* version_3) const override;\n  void SourceVersion(uint16_t* version_0,\n                     uint16_t* version_1,\n                     uint16_t* version_2,\n                     uint16_t* version_3) const override;\n  ModuleType GetModuleType() const override;\n  void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;\n  std::string DebugFileName() const override;\n  std::vector<uint8_t> BuildID() const override;\n  std::vector<std::string> AnnotationsVector() const override;\n  std::map<std::string, std::string> AnnotationsSimpleMap() const override;\n  std::vector<AnnotationSnapshot> AnnotationObjects() const override;\n  std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;\n  std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;\n\n private:\n  std::string name_;\n  time_t timestamp_;\n  const MachOImageReader* mach_o_image_reader_;  // weak\n  ProcessReaderMac* process_reader_;  // weak\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_\n"
  },
  {
    "path": "snapshot/mac/process_reader_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/process_reader_mac.h\"\n\n#include <Availability.h>\n#include <mach-o/loader.h>\n#include <mach/mach_vm.h>\n\n#include <algorithm>\n#include <utility>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/apple/scoped_mach_vm.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"snapshot/mac/mach_o_image_reader.h\"\n#include \"snapshot/mac/process_types.h\"\n#include \"util/misc/scoped_forbid_return.h\"\n\nnamespace {\n\nvoid MachTimeValueToTimeval(const time_value& mach, timeval* tv) {\n  tv->tv_sec = mach.seconds;\n  tv->tv_usec = mach.microseconds;\n}\n\nkern_return_t MachVMRegionRecurseDeepest(task_t task,\n                                         mach_vm_address_t* address,\n                                         mach_vm_size_t* size,\n                                         natural_t* depth,\n                                         vm_prot_t* protection,\n                                         unsigned int* user_tag) {\n  vm_region_submap_short_info_64 submap_info;\n  mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;\n  while (true) {\n    kern_return_t kr = mach_vm_region_recurse(\n        task,\n        address,\n        size,\n        depth,\n        reinterpret_cast<vm_region_recurse_info_t>(&submap_info),\n        &count);\n    if (kr != KERN_SUCCESS) {\n      return kr;\n    }\n\n    if (!submap_info.is_submap) {\n      *protection = submap_info.protection;\n      *user_tag = submap_info.user_tag;\n      return KERN_SUCCESS;\n    }\n\n    ++*depth;\n  }\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nProcessReaderMac::Thread::Thread()\n    : thread_context(),\n      float_context(),\n      debug_context(),\n      name(),\n      id(0),\n      stack_region_address(0),\n      stack_region_size(0),\n      thread_specific_data_address(0),\n      port(THREAD_NULL),\n      suspend_count(0),\n      priority(0) {}\n\nProcessReaderMac::Module::Module() : name(), reader(nullptr), timestamp(0) {}\n\nProcessReaderMac::Module::~Module() {}\n\nProcessReaderMac::ProcessReaderMac()\n    : process_info_(),\n      threads_(),\n      modules_(),\n      module_readers_(),\n      process_memory_(),\n      task_(TASK_NULL),\n      initialized_(),\n#if defined(CRASHPAD_MAC_32_BIT_SUPPORT)\n      is_64_bit_(false),\n#endif  // CRASHPAD_MAC_32_BIT_SUPPORT\n      initialized_threads_(false),\n      initialized_modules_(false) {\n}\n\nProcessReaderMac::~ProcessReaderMac() {\n  for (const Thread& thread : threads_) {\n    kern_return_t kr = mach_port_deallocate(mach_task_self(), thread.port);\n    MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << \"mach_port_deallocate\";\n  }\n}\n\nbool ProcessReaderMac::Initialize(task_t task) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!process_info_.InitializeWithTask(task)) {\n    return false;\n  }\n\n  if (!process_memory_.Initialize(task)) {\n    return false;\n  }\n\n#if defined(CRASHPAD_MAC_32_BIT_SUPPORT)\n  is_64_bit_ = process_info_.Is64Bit();\n#else  // CRASHPAD_MAC_32_BIT_SUPPORT\n  DCHECK(process_info_.Is64Bit());\n#endif  // CRASHPAD_MAC_32_BIT_SUPPORT\n\n  task_ = task;\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nvoid ProcessReaderMac::StartTime(timeval* start_time) const {\n  bool rv = process_info_.StartTime(start_time);\n  DCHECK(rv);\n}\n\nbool ProcessReaderMac::CPUTimes(timeval* user_time,\n                                timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // Calculate user and system time the same way the kernel does for\n  // getrusage(). See 10.9.2 xnu-2422.90.20/bsd/kern/kern_resource.c calcru().\n  timerclear(user_time);\n  timerclear(system_time);\n\n  // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO.\n  // TASK_BASIC_INFO_64 is equivalent and works on earlier systems.\n  task_basic_info_64 task_basic_info;\n  mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT;\n  kern_return_t kr = task_info(task_,\n                               TASK_BASIC_INFO_64,\n                               reinterpret_cast<task_info_t>(&task_basic_info),\n                               &task_basic_info_count);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(WARNING, kr) << \"task_info TASK_BASIC_INFO_64\";\n    return false;\n  }\n\n  task_thread_times_info_data_t task_thread_times;\n  mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT;\n  kr = task_info(task_,\n                 TASK_THREAD_TIMES_INFO,\n                 reinterpret_cast<task_info_t>(&task_thread_times),\n                 &task_thread_times_count);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(WARNING, kr) << \"task_info TASK_THREAD_TIMES\";\n    return false;\n  }\n\n  MachTimeValueToTimeval(task_basic_info.user_time, user_time);\n  MachTimeValueToTimeval(task_basic_info.system_time, system_time);\n\n  timeval thread_user_time;\n  MachTimeValueToTimeval(task_thread_times.user_time, &thread_user_time);\n  timeval thread_system_time;\n  MachTimeValueToTimeval(task_thread_times.system_time, &thread_system_time);\n\n  timeradd(user_time, &thread_user_time, user_time);\n  timeradd(system_time, &thread_system_time, system_time);\n\n  return true;\n}\n\nconst std::vector<ProcessReaderMac::Thread>& ProcessReaderMac::Threads() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (!initialized_threads_) {\n    InitializeThreads();\n  }\n\n  return threads_;\n}\n\nconst std::vector<ProcessReaderMac::Module>& ProcessReaderMac::Modules() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (!initialized_modules_) {\n    InitializeModules();\n  }\n\n  return modules_;\n}\n\nmach_vm_address_t ProcessReaderMac::DyldAllImageInfo(\n    mach_vm_size_t* all_image_info_size) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  task_dyld_info_data_t dyld_info;\n  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;\n  kern_return_t kr = task_info(\n      task_, TASK_DYLD_INFO, reinterpret_cast<task_info_t>(&dyld_info), &count);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(WARNING, kr) << \"task_info\";\n    return 0;\n  }\n\n// TODO(mark): Deal with statically linked executables which don’t use dyld.\n// This may look for the module that matches the executable path in the same\n// data set that vmmap uses.\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n  // The task_dyld_info_data_t struct grew in 10.7, adding the format field.\n  // Don’t check this field if it’s not present, which can happen when either\n  // the SDK used at compile time or the kernel at run time are too old and\n  // don’t know about it.\n  if (count >= TASK_DYLD_INFO_COUNT) {\n    const integer_t kExpectedFormat =\n        !Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64;\n    if (dyld_info.all_image_info_format != kExpectedFormat) {\n      LOG(WARNING) << \"unexpected task_dyld_info_data_t::all_image_info_format \"\n                   << dyld_info.all_image_info_format;\n      DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat);\n      return 0;\n    }\n  }\n#endif\n\n  if (all_image_info_size) {\n    *all_image_info_size = dyld_info.all_image_info_size;\n  }\n  return dyld_info.all_image_info_addr;\n}\n\nvoid ProcessReaderMac::InitializeThreads() {\n  DCHECK(!initialized_threads_);\n  DCHECK(threads_.empty());\n\n  initialized_threads_ = true;\n\n  thread_act_array_t threads;\n  mach_msg_type_number_t thread_count = 0;\n  kern_return_t kr = task_threads(task_, &threads, &thread_count);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(WARNING, kr) << \"task_threads\";\n    return;\n  }\n\n  // The send rights in the |threads| array won’t have their send rights managed\n  // by anything until they’re added to |threads_| by the loop below. Any early\n  // return (or exception) that happens between here and the completion of the\n  // loop below will leak thread port send rights.\n  ScopedForbidReturn threads_need_owners;\n\n  base::apple::ScopedMachVM threads_vm(\n      reinterpret_cast<vm_address_t>(threads),\n      mach_vm_round_page(thread_count * sizeof(*threads)));\n\n  for (size_t index = 0; index < thread_count; ++index) {\n    Thread thread;\n    thread.port = threads[index];\n\n#if defined(ARCH_CPU_X86_FAMILY)\n    const thread_state_flavor_t kThreadStateFlavor =\n        Is64Bit() ? x86_THREAD_STATE64 : x86_THREAD_STATE32;\n    mach_msg_type_number_t thread_state_count =\n        Is64Bit() ? x86_THREAD_STATE64_COUNT : x86_THREAD_STATE32_COUNT;\n\n    // TODO(mark): Use the AVX variants instead of the FLOAT variants?\n    const thread_state_flavor_t kFloatStateFlavor =\n        Is64Bit() ? x86_FLOAT_STATE64 : x86_FLOAT_STATE32;\n    mach_msg_type_number_t float_state_count =\n        Is64Bit() ? x86_FLOAT_STATE64_COUNT : x86_FLOAT_STATE32_COUNT;\n\n    const thread_state_flavor_t kDebugStateFlavor =\n        Is64Bit() ? x86_DEBUG_STATE64 : x86_DEBUG_STATE32;\n    mach_msg_type_number_t debug_state_count =\n        Is64Bit() ? x86_DEBUG_STATE64_COUNT : x86_DEBUG_STATE32_COUNT;\n#elif defined(ARCH_CPU_ARM64)\n    const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64;\n    mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT;\n\n    const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64;\n    mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT;\n\n    const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64;\n    mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT;\n#endif\n\n    kr = thread_get_state(\n        thread.port,\n        kThreadStateFlavor,\n        reinterpret_cast<thread_state_t>(&thread.thread_context),\n        &thread_state_count);\n    if (kr != KERN_SUCCESS) {\n      MACH_LOG(ERROR, kr) << \"thread_get_state(\" << kThreadStateFlavor << \")\";\n      continue;\n    }\n\n    kr = thread_get_state(\n        thread.port,\n        kFloatStateFlavor,\n        reinterpret_cast<thread_state_t>(&thread.float_context),\n        &float_state_count);\n    if (kr != KERN_SUCCESS) {\n      MACH_LOG(ERROR, kr) << \"thread_get_state(\" << kFloatStateFlavor << \")\";\n      continue;\n    }\n\n    kr = thread_get_state(\n        thread.port,\n        kDebugStateFlavor,\n        reinterpret_cast<thread_state_t>(&thread.debug_context),\n        &debug_state_count);\n    if (kr != KERN_SUCCESS) {\n      MACH_LOG(ERROR, kr) << \"thread_get_state(\" << kDebugStateFlavor << \")\";\n      continue;\n    }\n\n    thread_basic_info basic_info;\n    mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;\n    kr = thread_info(thread.port,\n                     THREAD_BASIC_INFO,\n                     reinterpret_cast<thread_info_t>(&basic_info),\n                     &count);\n    if (kr != KERN_SUCCESS) {\n      MACH_LOG(WARNING, kr) << \"thread_info(THREAD_BASIC_INFO)\";\n    } else {\n      thread.suspend_count = basic_info.suspend_count;\n    }\n\n    thread_identifier_info identifier_info;\n    count = THREAD_IDENTIFIER_INFO_COUNT;\n    kr = thread_info(thread.port,\n                     THREAD_IDENTIFIER_INFO,\n                     reinterpret_cast<thread_info_t>(&identifier_info),\n                     &count);\n    if (kr != KERN_SUCCESS) {\n      MACH_LOG(WARNING, kr) << \"thread_info(THREAD_IDENTIFIER_INFO)\";\n    } else {\n      thread.id = identifier_info.thread_id;\n\n      // thread_identifier_info::thread_handle contains the base of the\n      // thread-specific data area, which on x86 and x86_64 is the thread’s base\n      // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c\n      // thread_info_internal() gets the value from\n      // machine_thread::cthread_self, which is the same value used to set the\n      // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c\n      // act_machine_switch_pcb().\n      //\n      // This address is the internal pthread’s _pthread::tsd[], an array of\n      // void* values that can be indexed by pthread_key_t values.\n      thread.thread_specific_data_address = identifier_info.thread_handle;\n    }\n\n    thread_extended_info extended_info;\n    count = THREAD_EXTENDED_INFO_COUNT;\n    kr = thread_info(thread.port,\n                     THREAD_EXTENDED_INFO,\n                     reinterpret_cast<thread_info_t>(&extended_info),\n                     &count);\n    if (kr != KERN_SUCCESS) {\n      MACH_LOG(WARNING, kr) << \"thread_info(THREAD_EXTENDED_INFO)\";\n    } else {\n      thread.name.assign(\n          extended_info.pth_name,\n          strnlen(extended_info.pth_name, sizeof(extended_info.pth_name)));\n    }\n\n    thread_precedence_policy precedence;\n    count = THREAD_PRECEDENCE_POLICY_COUNT;\n    boolean_t get_default = FALSE;\n    kr = thread_policy_get(thread.port,\n                           THREAD_PRECEDENCE_POLICY,\n                           reinterpret_cast<thread_policy_t>(&precedence),\n                           &count,\n                           &get_default);\n    if (kr != KERN_SUCCESS) {\n      MACH_LOG(INFO, kr) << \"thread_policy_get\";\n    } else {\n      thread.priority = precedence.importance;\n    }\n\n#if defined(ARCH_CPU_X86_FAMILY)\n    mach_vm_address_t stack_pointer = Is64Bit()\n                                          ? thread.thread_context.t64.__rsp\n                                          : thread.thread_context.t32.__esp;\n#elif defined(ARCH_CPU_ARM64)\n    mach_vm_address_t stack_pointer =\n        arm_thread_state64_get_sp(thread.thread_context);\n#endif\n\n    thread.stack_region_address =\n        CalculateStackRegion(stack_pointer, &thread.stack_region_size);\n\n    threads_.push_back(thread);\n  }\n\n  threads_need_owners.Disarm();\n}\n\nvoid ProcessReaderMac::InitializeModules() {\n  DCHECK(!initialized_modules_);\n  DCHECK(modules_.empty());\n\n  initialized_modules_ = true;\n\n  mach_vm_size_t all_image_info_size;\n  mach_vm_address_t all_image_info_addr =\n      DyldAllImageInfo(&all_image_info_size);\n\n  process_types::dyld_all_image_infos all_image_infos;\n  if (!all_image_infos.Read(this, all_image_info_addr)) {\n    LOG(WARNING) << \"could not read dyld_all_image_infos\";\n    return;\n  }\n\n  if (all_image_infos.version < 1) {\n    LOG(WARNING) << \"unexpected dyld_all_image_infos version \"\n                 << all_image_infos.version;\n    return;\n  }\n\n  size_t expected_size =\n      process_types::dyld_all_image_infos::ExpectedSizeForVersion(\n          this, all_image_infos.version);\n  if (all_image_info_size < expected_size) {\n    LOG(WARNING) << \"small dyld_all_image_infos size \" << all_image_info_size\n                 << \" < \" << expected_size << \" for version \"\n                 << all_image_infos.version;\n    return;\n  }\n\n  // Note that all_image_infos.infoArrayCount may be 0 if a crash occurred while\n  // dyld was loading the executable. This can happen if a required dynamic\n  // library was not found. Similarly, all_image_infos.infoArray may be nullptr\n  // if a crash occurred while dyld was updating it.\n  //\n  // TODO(mark): It may be possible to recover from these situations by looking\n  // through memory mappings for Mach-O images.\n  //\n  // Continue along when this situation is detected, because even without any\n  // images in infoArray, dyldImageLoadAddress may be set, and it may be\n  // possible to recover some information from dyld.\n  if (all_image_infos.infoArrayCount == 0) {\n    LOG(WARNING) << \"all_image_infos.infoArrayCount is zero\";\n  } else if (!all_image_infos.infoArray) {\n    LOG(WARNING) << \"all_image_infos.infoArray is nullptr\";\n  }\n\n  std::vector<process_types::dyld_image_info> image_info_vector(\n      all_image_infos.infoArrayCount);\n  if (!process_types::dyld_image_info::ReadArrayInto(this,\n                                                     all_image_infos.infoArray,\n                                                     image_info_vector.size(),\n                                                     &image_info_vector[0])) {\n    LOG(WARNING) << \"could not read dyld_image_info array\";\n    return;\n  }\n\n  size_t main_executable_count = 0;\n  bool found_dyld = false;\n  modules_.reserve(image_info_vector.size());\n  for (const process_types::dyld_image_info& image_info : image_info_vector) {\n    Module module;\n    module.timestamp = image_info.imageFileModDate;\n\n    if (!process_memory_.ReadCString(image_info.imageFilePath, &module.name)) {\n      LOG(WARNING) << \"could not read dyld_image_info::imageFilePath\";\n      // Proceed anyway with an empty module name.\n    }\n\n    std::unique_ptr<MachOImageReader> reader(new MachOImageReader());\n    if (!reader->Initialize(this, image_info.imageLoadAddress, module.name)) {\n      reader.reset();\n    }\n\n    module.reader = reader.get();\n\n    uint32_t file_type = reader ? reader->FileType() : 0;\n\n    module_readers_.push_back(std::move(reader));\n    modules_.push_back(module);\n\n    if (all_image_infos.version >= 2 && all_image_infos.dyldImageLoadAddress &&\n        image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) {\n      found_dyld = true;\n      LOG(WARNING) << base::StringPrintf(\n          \"found dylinker (%s) in dyld_all_image_infos::infoArray\",\n          module.name.c_str());\n\n      LOG_IF(WARNING, file_type != MH_DYLINKER)\n          << base::StringPrintf(\"dylinker (%s) has unexpected Mach-O type %d\",\n                                module.name.c_str(),\n                                file_type);\n    }\n\n    if (file_type == MH_EXECUTE) {\n      // On macOS 14, the main executable does not normally show up at\n      // index 0. In previous versions of dyld, each loaded image was\n      // appended to the all image info vector as it was loaded.\n      // (For example, see RuntimeState::notifyDebuggerLoad in dyld-1066.8).\n      // Starting from dyld-1122.1, notifyDebuggerLoad calls\n      // ExternallyViewableState::addImages for all but the main executable\n      // (which has already been added). ExternallyViewableState::addImages\n      // inserts all new image infos at the front of the vector, leaving the\n      // main executable as the last item.\n      //\n      // The interface requires that the main executable be first in the list,\n      // so swap it into the right position.\n      size_t index = modules_.size() - 1;\n      if (index > 0) {\n        CHECK_EQ(index, image_info_vector.size() - 1);\n        if (main_executable_count == 0) {\n          std::rotate(\n              modules_.rbegin(), modules_.rbegin() + 1, modules_.rend());\n        }\n      }\n      if (main_executable_count > 0) {\n        LOG(WARNING) << base::StringPrintf(\n            \"multiple MH_EXECUTE modules (%s, %s)\",\n            modules_[0].name.c_str(),\n            modules_[index].name.c_str());\n      }\n      ++main_executable_count;\n    }\n  }\n\n  LOG_IF(WARNING, main_executable_count == 0) << \"no MH_EXECUTE modules\";\n\n  // all_image_infos.infoArray doesn’t include an entry for dyld, but dyld is\n  // loaded into the process’ address space as a module. Its load address is\n  // easily known given a sufficiently recent all_image_infos.version, but the\n  // timestamp and pathname are not given as they are for other modules.\n  //\n  // The timestamp is a lost cause, because the kernel doesn’t record the\n  // timestamp of the dynamic linker at the time it’s loaded in the same way\n  // that dyld records the timestamps of other modules when they’re loaded. (The\n  // timestamp for the main executable is also not reported and appears as 0\n  // even when accessed via dyld APIs, because it’s loaded by the kernel, not by\n  // dyld.)\n  //\n  // The name can be determined, but it’s not as simple as hardcoding the\n  // default \"/usr/lib/dyld\" because an executable could have specified anything\n  // in its LC_LOAD_DYLINKER command.\n  if (!found_dyld && all_image_infos.version >= 2 &&\n      all_image_infos.dyldImageLoadAddress) {\n    Module module;\n    module.timestamp = 0;\n\n    // Examine the executable’s LC_LOAD_DYLINKER load command to find the path\n    // used to load dyld.\n    if (all_image_infos.infoArrayCount >= 1 && main_executable_count >= 1) {\n      module.name = modules_[0].reader->DylinkerName();\n    }\n    std::string module_name = !module.name.empty() ? module.name : \"(dyld)\";\n\n    std::unique_ptr<MachOImageReader> reader(new MachOImageReader());\n    if (!reader->Initialize(\n            this, all_image_infos.dyldImageLoadAddress, module_name)) {\n      reader.reset();\n    }\n\n    module.reader = reader.get();\n\n    uint32_t file_type = reader ? reader->FileType() : 0;\n\n    LOG_IF(WARNING, file_type != MH_DYLINKER)\n        << base::StringPrintf(\"dylinker (%s) has unexpected Mach-O type %d\",\n                              module.name.c_str(),\n                              file_type);\n\n    if (module.name.empty() && file_type == MH_DYLINKER) {\n      // Look inside dyld directly to find its preferred path.\n      module.name = reader->DylinkerName();\n    }\n\n    if (module.name.empty()) {\n      module.name = \"(dyld)\";\n    }\n\n    // dyld is loaded in the process even if its path can’t be determined.\n    module_readers_.push_back(std::move(reader));\n    modules_.push_back(module);\n  }\n}\n\nmach_vm_address_t ProcessReaderMac::CalculateStackRegion(\n    mach_vm_address_t stack_pointer,\n    mach_vm_size_t* stack_region_size) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // For pthreads, it may be possible to compute the stack region based on the\n  // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct\n  // for a thread can be located at TSD slot 0, or the known offsets of\n  // stackaddr and stacksize from the TSD area could be used.\n  mach_vm_address_t region_base = stack_pointer;\n  mach_vm_size_t region_size;\n  natural_t depth = 0;\n  vm_prot_t protection;\n  unsigned int user_tag;\n  kern_return_t kr = MachVMRegionRecurseDeepest(\n      task_, &region_base, &region_size, &depth, &protection, &user_tag);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(INFO, kr) << \"mach_vm_region_recurse\";\n    *stack_region_size = 0;\n    return 0;\n  }\n\n  if (region_base > stack_pointer) {\n    // There’s nothing mapped at the stack pointer’s address. Something may have\n    // trashed the stack pointer. Note that this shouldn’t happen for a normal\n    // stack guard region violation because the guard region is mapped but has\n    // VM_PROT_NONE protection.\n    *stack_region_size = 0;\n    return 0;\n  }\n\n  mach_vm_address_t start_address = stack_pointer;\n\n  if ((protection & VM_PROT_READ) == 0) {\n    // If the region isn’t readable, the stack pointer probably points to the\n    // guard region. Don’t include it as part of the stack, and don’t include\n    // anything at any lower memory address. The code below may still possibly\n    // find the real stack region at a memory address higher than this region.\n    start_address = region_base + region_size;\n  } else {\n    // If the ABI requires a red zone, adjust the region to include it if\n    // possible.\n    LocateRedZone(&start_address, &region_base, &region_size, user_tag);\n\n    // Regardless of whether the ABI requires a red zone, capture up to\n    // kExtraCaptureSize additional bytes of stack, but only if present in the\n    // region that was already found.\n    constexpr mach_vm_size_t kExtraCaptureSize = 128;\n    start_address = std::max(start_address >= kExtraCaptureSize\n                                 ? start_address - kExtraCaptureSize\n                                 : start_address,\n                             region_base);\n\n    // Align start_address to a 16-byte boundary, which can help readers by\n    // ensuring that data is aligned properly. This could page-align instead,\n    // but that might be wasteful.\n    constexpr mach_vm_size_t kDesiredAlignment = 16;\n    start_address &= ~(kDesiredAlignment - 1);\n    DCHECK_GE(start_address, region_base);\n  }\n\n  region_size -= (start_address - region_base);\n  region_base = start_address;\n\n  mach_vm_size_t total_region_size = region_size;\n\n  // The stack region may have gotten split up into multiple abutting regions.\n  // Try to coalesce them. This frequently happens for the main thread’s stack\n  // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region\n  // is split up due to an mprotect() or vm_protect() call.\n  //\n  // Stack regions created by the kernel and the pthreads library will be marked\n  // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions\n  // with the same tag should find an entire stack region. Checking that the\n  // protection on individual regions is not VM_PROT_NONE should guarantee that\n  // this algorithm doesn’t collect map entries belonging to another thread’s\n  // stack: well-behaved stacks (such as those created by the kernel and the\n  // pthreads library) have VM_PROT_NONE guard regions at their low-address\n  // ends.\n  //\n  // Other stack regions may not be so well-behaved and thus if user_tag is not\n  // VM_MEMORY_STACK, the single region that was found is used as-is without\n  // trying to merge it with other adjacent regions.\n  if (user_tag == VM_MEMORY_STACK) {\n    mach_vm_address_t try_address = region_base;\n    mach_vm_address_t original_try_address;\n\n    while (try_address += region_size,\n           original_try_address = try_address,\n           (kr = MachVMRegionRecurseDeepest(task_,\n                                            &try_address,\n                                            &region_size,\n                                            &depth,\n                                            &protection,\n                                            &user_tag) == KERN_SUCCESS) &&\n               try_address == original_try_address &&\n               (protection & VM_PROT_READ) != 0 &&\n               user_tag == VM_MEMORY_STACK) {\n      total_region_size += region_size;\n    }\n\n    if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) {\n      // Tolerate KERN_INVALID_ADDRESS because it will be returned when there\n      // are no more regions in the map at or above the specified |try_address|.\n      MACH_LOG(INFO, kr) << \"mach_vm_region_recurse\";\n    }\n  }\n\n  *stack_region_size = total_region_size;\n  return region_base;\n}\n\nvoid ProcessReaderMac::LocateRedZone(mach_vm_address_t* const start_address,\n                                     mach_vm_address_t* const region_base,\n                                     mach_vm_address_t* const region_size,\n                                     const unsigned int user_tag) {\n#if defined(ARCH_CPU_X86_FAMILY)\n  if (Is64Bit()) {\n    // x86_64 has a red zone. See AMD64 ABI 0.99.8,\n    // https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/uploads/01de35b2c8adc7545de52604cc45d942/x86-64-psABI-2021-05-20.pdf#page=23.\n    // section 3.2.2, “The Stack Frame”.\n    constexpr mach_vm_size_t kRedZoneSize = 128;\n    mach_vm_address_t red_zone_base =\n        *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0;\n    bool red_zone_ok = false;\n    if (red_zone_base >= *region_base) {\n      // The red zone is within the region already discovered.\n      red_zone_ok = true;\n    } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) {\n      // Probe to see if there’s a region immediately below the one already\n      // discovered.\n      mach_vm_address_t red_zone_region_base = red_zone_base;\n      mach_vm_size_t red_zone_region_size;\n      natural_t red_zone_depth = 0;\n      vm_prot_t red_zone_protection;\n      unsigned int red_zone_user_tag;\n      kern_return_t kr = MachVMRegionRecurseDeepest(task_,\n                                                    &red_zone_region_base,\n                                                    &red_zone_region_size,\n                                                    &red_zone_depth,\n                                                    &red_zone_protection,\n                                                    &red_zone_user_tag);\n      if (kr != KERN_SUCCESS) {\n        MACH_LOG(INFO, kr) << \"mach_vm_region_recurse\";\n        *start_address = *region_base;\n      } else if (red_zone_region_base + red_zone_region_size == *region_base &&\n                 (red_zone_protection & VM_PROT_READ) != 0 &&\n                 red_zone_user_tag == user_tag) {\n        // The region containing the red zone is immediately below the region\n        // already found, it’s readable (not the guard region), and it has the\n        // same user tag as the region already found, so merge them.\n        red_zone_ok = true;\n        *region_base -= red_zone_region_size;\n        *region_size += red_zone_region_size;\n      }\n    }\n\n    if (red_zone_ok) {\n      // Begin capturing from the base of the red zone (but not the entire\n      // region that encompasses the red zone).\n      *start_address = red_zone_base;\n    } else {\n      // The red zone would go lower into another region in memory, but no\n      // region was found. Memory can only be captured to an address as low as\n      // the base address of the region already found.\n      *start_address = *region_base;\n    }\n  }\n#endif\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/process_reader_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_\n#define CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_\n\n#include <Availability.h>\n#include <mach/mach.h>\n#include <stdint.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <time.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/posix/process_info.h\"\n#include \"util/process/process_memory_mac.h\"\n\n#if defined(ARCH_CPU_32_BIT) ||  \\\n    (!defined(ARCH_CPU_ARM64) && \\\n     __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_15)\n// There’s no 32-bit x86 environment on macOS 10.15 or later, and there’s no\n// 32-bit ARM environment for macOS at all.\n#define CRASHPAD_MAC_32_BIT_SUPPORT 1\n#endif  // ARCH_CPU_32_BIT || (!ARCH_CPU_ARM64 && DT < 10.15)\n\nnamespace crashpad {\n\nclass MachOImageReader;\n\n//! \\brief Accesses information about another process, identified by a Mach\n//!     task.\nclass ProcessReaderMac {\n public:\n  //! \\brief Contains information about a thread that belongs to a task\n  //!     (process).\n  struct Thread {\n#if defined(ARCH_CPU_X86_FAMILY)\n    union ThreadContext {\n      x86_thread_state64_t t64;\n      x86_thread_state32_t t32;\n    };\n    union FloatContext {\n      x86_float_state64_t f64;\n      x86_float_state32_t f32;\n    };\n    union DebugContext {\n      x86_debug_state64_t d64;\n      x86_debug_state32_t d32;\n    };\n#elif defined(ARCH_CPU_ARM64)\n    using ThreadContext = arm_thread_state64_t;\n    using FloatContext = arm_neon_state64_t;\n    using DebugContext = arm_debug_state64_t;\n#endif\n\n    Thread();\n    ~Thread() {}\n\n    ThreadContext thread_context;\n    FloatContext float_context;\n    DebugContext debug_context;\n    std::string name;\n    uint64_t id;\n    mach_vm_address_t stack_region_address;\n    mach_vm_size_t stack_region_size;\n    mach_vm_address_t thread_specific_data_address;\n    thread_t port;\n    int suspend_count;\n    int priority;\n  };\n\n  //! \\brief Contains information about a module loaded into a process.\n  struct Module {\n    Module();\n    ~Module();\n\n    //! \\brief The pathname used to load the module from disk.\n    std::string name;\n\n    //! \\brief An image reader for the module.\n    //!\n    //! The lifetime of this MachOImageReader is scoped to the lifetime of the\n    //! ProcessReaderMac that created it.\n    //!\n    //! This field may be `nullptr` if a reader could not be created for the\n    //! module.\n    const MachOImageReader* reader;\n\n    //! \\brief The module’s timestamp.\n    //!\n    //! This field will be `0` if its value cannot be determined. It can only be\n    //! determined for images that are loaded by dyld, so it will be `0` for the\n    //! main executable and for dyld itself.\n    time_t timestamp;\n  };\n\n  ProcessReaderMac();\n\n  ProcessReaderMac(const ProcessReaderMac&) = delete;\n  ProcessReaderMac& operator=(const ProcessReaderMac&) = delete;\n\n  ~ProcessReaderMac();\n\n  //! \\brief Initializes this object. This method must be called before any\n  //!     other.\n  //!\n  //! \\param[in] task A send right to the target task’s task port. This object\n  //!     does not take ownership of the send right.\n  //!\n  //! \\return `true` on success, indicating that this object will respond\n  //!     validly to further method calls. `false` on failure. On failure, no\n  //!     further method calls should be made.\n  bool Initialize(task_t task);\n\n  //! \\return `true` if the target task is a 64-bit process.\n#if defined(CRASHPAD_MAC_32_BIT_SUPPORT) || DOXYGEN\n  bool Is64Bit() const { return is_64_bit_; }\n#else  // CRASHPAD_MAC_32_BIT_SUPPORT\n  bool Is64Bit() const { return true; }\n#endif  // CRASHPAD_MAC_32_BIT_SUPPORT\n\n  //! \\return The target task’s process ID.\n  pid_t ProcessID() const { return process_info_.ProcessID(); }\n\n  //! \\return The target task’s parent process ID.\n  pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }\n\n  //! \\brief Determines the target process’ start time.\n  //!\n  //! \\param[out] start_time The time that the process started.\n  void StartTime(timeval* start_time) const;\n\n  //! \\brief Determines the target process’ execution time.\n  //!\n  //! \\param[out] user_time The amount of time the process has executed code in\n  //!     user mode.\n  //! \\param[out] system_time The amount of time the process has executed code\n  //!     in system mode.\n  //!\n  //! \\return `true` on success, `false` on failure, with a warning logged. On\n  //!     failure, \\a user_time and \\a system_time will be set to represent no\n  //!     time spent executing code in user or system mode.\n  bool CPUTimes(timeval* user_time, timeval* system_time) const;\n\n  //! \\return Accesses the memory of the target task.\n  const ProcessMemoryMac* Memory() const { return &process_memory_; }\n\n  //! \\return The threads that are in the task (process). The first element (at\n  //!     index `0`) corresponds to the main thread.\n  const std::vector<Thread>& Threads();\n\n  //! \\return The modules loaded in the process. The first element (at index\n  //!     `0`) corresponds to the main executable, and the final element\n  //!     corresponds to the dynamic loader, dyld.\n  const std::vector<Module>& Modules();\n\n  //! \\brief Determines the location of the `dyld_all_image_infos` structure in\n  //!     the process’ address space.\n  //!\n  //! This function is an internal implementation detail of Modules(), and\n  //! should not normally be used directly. It is exposed solely for use by test\n  //! code.\n  //!\n  //! \\param[out] all_image_info_size The size of the `dyld_all_image_infos`\n  //!     structure. Optional, may be `nullptr` if not required.\n  //!\n  //! \\return The address of the `dyld_all_image_infos` structure in the\n  //!     process’ address space, with \\a all_image_info_size set appropriately.\n  //!     On failure, returns `0` with a message logged.\n  mach_vm_address_t DyldAllImageInfo(mach_vm_size_t* all_image_info_size);\n\n private:\n  //! Performs lazy initialization of the \\a threads_ vector on behalf of\n  //! Threads().\n  void InitializeThreads();\n\n  //! Performs lazy initialization of the \\a modules_ vector on behalf of\n  //! Modules().\n  void InitializeModules();\n\n  //! \\brief Calculates the base address and size of the region used as a\n  //!     thread’s stack.\n  //!\n  //! The region returned by this method may be formed by merging multiple\n  //! adjacent regions in a process’ memory map if appropriate. The base address\n  //! of the returned region may be lower than the \\a stack_pointer passed in\n  //! when the ABI mandates a red zone below the stack pointer.\n  //!\n  //! \\param[in] stack_pointer The stack pointer, referring to the top (lowest\n  //!     address) of a thread’s stack.\n  //! \\param[out] stack_region_size The size of the memory region used as the\n  //!     thread’s stack.\n  //!\n  //! \\return The base address (lowest address) of the memory region used as the\n  //!     thread’s stack.\n  mach_vm_address_t CalculateStackRegion(mach_vm_address_t stack_pointer,\n                                         mach_vm_size_t* stack_region_size);\n\n  //! \\brief Adjusts the region for the red zone, if the ABI requires one.\n  //!\n  //! This method performs red zone calculation for CalculateStackRegion(). Its\n  //! parameters are local variables used within that method, and may be\n  //! modified as needed.\n  //!\n  //! Where a red zone is required, the region of memory captured for a thread’s\n  //! stack will be extended to include the red zone below the stack pointer,\n  //! provided that such memory is mapped, readable, and has the correct user\n  //! tag value. If these conditions cannot be met fully, as much of the red\n  //! zone will be captured as is possible while meeting these conditions.\n  //!\n  //! \\param[in,out] start_address The base address of the region to begin\n  //!     capturing stack memory from. On entry, \\a start_address is the stack\n  //!     pointer. On return, \\a start_address may be decreased to encompass a\n  //!     red zone.\n  //! \\param[in,out] region_base The base address of the region that contains\n  //!     stack memory. This is distinct from \\a start_address in that \\a\n  //!     region_base will be page-aligned. On entry, \\a region_base is the\n  //!     base address of a region that contains \\a start_address. On return,\n  //!     if \\a start_address is decremented and is outside of the region\n  //!     originally described by \\a region_base, \\a region_base will also be\n  //!     decremented appropriately.\n  //! \\param[in,out] region_size The size of the region that contains stack\n  //!     memory. This region begins at \\a region_base. On return, if \\a\n  //!     region_base is decremented, \\a region_size will be incremented\n  //!     appropriately.\n  //! \\param[in] user_tag The Mach VM system’s user tag for the region described\n  //!     by the initial values of \\a region_base and \\a region_size. The red\n  //!     zone will only be allowed to extend out of the region described by\n  //!     these initial values if the user tag is appropriate for stack memory\n  //!     and the expanded region has the same user tag value.\n  void LocateRedZone(mach_vm_address_t* start_address,\n                     mach_vm_address_t* region_base,\n                     mach_vm_address_t* region_size,\n                     unsigned int user_tag);\n\n  ProcessInfo process_info_;\n  std::vector<Thread> threads_;  // owns send rights\n  std::vector<Module> modules_;\n  std::vector<std::unique_ptr<MachOImageReader>> module_readers_;\n  ProcessMemoryMac process_memory_;\n  task_t task_;  // weak\n  InitializationStateDcheck initialized_;\n\n#if defined(CRASHPAD_MAC_32_BIT_SUPPORT)\n  // This shadows a method of process_info_, but it’s accessed so frequently\n  // that it’s given a first-class field to save a call and a few bit operations\n  // on each access.\n  bool is_64_bit_;\n#endif  // CRASHPAD_MAC_32_BIT_SUPPORT\n\n  bool initialized_threads_;\n  bool initialized_modules_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_\n"
  },
  {
    "path": "snapshot/mac/process_reader_mac_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/process_reader_mac.h\"\n\n#include <Availability.h>\n#include <OpenCL/opencl.h>\n#include <dlfcn.h>\n#include <errno.h>\n#include <mach-o/dyld.h>\n#include <mach-o/dyld_images.h>\n#include <mach-o/loader.h>\n#include <mach/mach.h>\n#include <pthread.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <map>\n#include <unordered_set>\n#include <utility>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check_op.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/mac/mach_o_image_reader.h\"\n#include \"snapshot/mac/mach_o_image_segment_reader.h\"\n#include \"test/errors.h\"\n#include \"test/mac/dyld.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"test/mac/mach_multiprocess.h\"\n#include \"test/scoped_set_thread_name.h\"\n#include \"util/file/file_io.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/synchronization/semaphore.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n#if !defined(ARCH_CPU_64_BITS)\nusing MachHeader = mach_header;\n#else\nusing MachHeader = mach_header_64;\n#endif\n\nusing ModulePathAndAddress = std::pair<std::string, mach_vm_address_t>;\nstruct PathAndAddressHash {\n  std::size_t operator()(const ModulePathAndAddress& pair) const {\n    return std::hash<std::string>()(pair.first) ^\n           std::hash<mach_vm_address_t>()(pair.second);\n  }\n};\nusing ModuleSet = std::unordered_set<ModulePathAndAddress, PathAndAddressHash>;\n\nconstexpr char kDyldPath[] = \"/usr/lib/dyld\";\n\nTEST(ProcessReaderMac, SelfBasic) {\n  ProcessReaderMac process_reader;\n  ASSERT_TRUE(process_reader.Initialize(mach_task_self()));\n\n#if !defined(ARCH_CPU_64_BITS)\n  EXPECT_FALSE(process_reader.Is64Bit());\n#else\n  EXPECT_TRUE(process_reader.Is64Bit());\n#endif\n\n  EXPECT_EQ(process_reader.ProcessID(), getpid());\n  EXPECT_EQ(process_reader.ParentProcessID(), getppid());\n\n  static constexpr char kTestMemory[] = \"Some test memory\";\n  char buffer[std::size(kTestMemory)];\n  ASSERT_TRUE(process_reader.Memory()->Read(\n      FromPointerCast<mach_vm_address_t>(kTestMemory),\n      sizeof(kTestMemory),\n      &buffer));\n  EXPECT_STREQ(kTestMemory, buffer);\n}\n\nconstexpr char kTestMemory[] = \"Read me from another process\";\n\nclass ProcessReaderChild final : public MachMultiprocess {\n public:\n  ProcessReaderChild() : MachMultiprocess() {}\n\n  ProcessReaderChild(const ProcessReaderChild&) = delete;\n  ProcessReaderChild& operator=(const ProcessReaderChild&) = delete;\n\n  ~ProcessReaderChild() {}\n\n private:\n  void MachMultiprocessParent() override {\n    ProcessReaderMac process_reader;\n    ASSERT_TRUE(process_reader.Initialize(ChildTask()));\n\n#if !defined(ARCH_CPU_64_BITS)\n    EXPECT_FALSE(process_reader.Is64Bit());\n#else\n    EXPECT_TRUE(process_reader.Is64Bit());\n#endif\n\n    EXPECT_EQ(process_reader.ParentProcessID(), getpid());\n    EXPECT_EQ(process_reader.ProcessID(), ChildPID());\n\n    FileHandle read_handle = ReadPipeHandle();\n\n    mach_vm_address_t address;\n    CheckedReadFileExactly(read_handle, &address, sizeof(address));\n\n    std::string read_string;\n    ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string));\n    EXPECT_EQ(read_string, kTestMemory);\n  }\n\n  void MachMultiprocessChild() override {\n    FileHandle write_handle = WritePipeHandle();\n\n    mach_vm_address_t address = FromPointerCast<mach_vm_address_t>(kTestMemory);\n    CheckedWriteFile(write_handle, &address, sizeof(address));\n\n    // Wait for the parent to signal that it’s OK to exit by closing its end of\n    // the pipe.\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n};\n\nTEST(ProcessReaderMac, ChildBasic) {\n  ProcessReaderChild process_reader_child;\n  process_reader_child.Run();\n}\n\n// Returns a thread ID given a pthread_t. This wraps pthread_threadid_np() but\n// that function has a cumbersome interface because it returns a success value.\n// This function CHECKs success and returns the thread ID directly.\nuint64_t PthreadToThreadID(pthread_t pthread) {\n  uint64_t thread_id;\n  errno = pthread_threadid_np(pthread, &thread_id);\n  PCHECK(errno == 0) << \"pthread_threadid_np\";\n  return thread_id;\n}\n\nTEST(ProcessReaderMac, SelfOneThread) {\n  const ScopedSetThreadName scoped_set_thread_name(\n      \"ProcessReaderMac/SelfOneThread\");\n\n  ProcessReaderMac process_reader;\n  ASSERT_TRUE(process_reader.Initialize(mach_task_self()));\n\n  const std::vector<ProcessReaderMac::Thread>& threads =\n      process_reader.Threads();\n\n  // If other tests ran in this process previously, threads may have been\n  // created and may still be running. This check must look for at least one\n  // thread, not exactly one thread.\n  ASSERT_GE(threads.size(), 1u);\n\n  EXPECT_EQ(threads[0].id, PthreadToThreadID(pthread_self()));\n  EXPECT_EQ(threads[0].name, \"ProcessReaderMac/SelfOneThread\");\n\n  thread_t thread_self = MachThreadSelf();\n  EXPECT_EQ(threads[0].port, thread_self);\n\n  EXPECT_EQ(threads[0].suspend_count, 0);\n}\n\nclass TestThreadPool {\n public:\n  struct ThreadExpectation {\n    // The stack's base (highest) address.\n    mach_vm_address_t stack_base;\n\n    // The stack's maximum size.\n    mach_vm_size_t stack_size;\n\n    int suspend_count;\n    std::string thread_name;\n  };\n\n  TestThreadPool(const std::string& thread_name_prefix)\n      : thread_infos_(), thread_name_prefix_(thread_name_prefix) {}\n\n  TestThreadPool(const TestThreadPool&) = delete;\n  TestThreadPool& operator=(const TestThreadPool&) = delete;\n\n  // Resumes suspended threads, signals each thread’s exit semaphore asking it\n  // to exit, and joins each thread, blocking until they have all exited.\n  ~TestThreadPool() {\n    for (const auto& thread_info : thread_infos_) {\n      thread_t thread_port = pthread_mach_thread_np(thread_info->pthread);\n      while (thread_info->suspend_count > 0) {\n        kern_return_t kr = thread_resume(thread_port);\n        EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"thread_resume\");\n        --thread_info->suspend_count;\n      }\n    }\n\n    for (const auto& thread_info : thread_infos_) {\n      thread_info->exit_semaphore.Signal();\n    }\n\n    for (const auto& thread_info : thread_infos_) {\n      int rv = pthread_join(thread_info->pthread, nullptr);\n      CHECK_EQ(0, rv);\n    }\n  }\n\n  // Starts |thread_count| threads and waits on each thread’s ready semaphore,\n  // so that when this function returns, all threads have been started and have\n  // all run to the point that they’ve signalled that they are ready.\n  void StartThreads(size_t thread_count) {\n    ASSERT_TRUE(thread_infos_.empty());\n\n    for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) {\n      std::string thread_name = base::StringPrintf(\n          \"%s-%zu\", thread_name_prefix_.c_str(), thread_index);\n      thread_infos_.push_back(\n          std::make_unique<ThreadInfo>(std::move(thread_name)));\n      ThreadInfo* thread_info = thread_infos_.back().get();\n\n      int rv = pthread_create(\n          &thread_info->pthread, nullptr, ThreadMain, thread_info);\n      ASSERT_EQ(rv, 0);\n    }\n\n    for (const auto& thread_info : thread_infos_) {\n      thread_info->ready_semaphore.Wait();\n    }\n\n    // If present, suspend the thread at indices 1 through 3 the same number of\n    // times as their index. This tests reporting of suspend counts.\n    for (size_t thread_index = 1;\n         thread_index < thread_infos_.size() && thread_index < 4;\n         ++thread_index) {\n      thread_t thread_port =\n          pthread_mach_thread_np(thread_infos_[thread_index]->pthread);\n      for (size_t suspend_count = 0; suspend_count < thread_index;\n           ++suspend_count) {\n        kern_return_t kr = thread_suspend(thread_port);\n        EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"thread_suspend\");\n        if (kr == KERN_SUCCESS) {\n          ++thread_infos_[thread_index]->suspend_count;\n        }\n      }\n    }\n  }\n\n  uint64_t GetThreadInfo(size_t thread_index, ThreadExpectation* expectation) {\n    CHECK_LT(thread_index, thread_infos_.size());\n\n    const auto& thread_info = thread_infos_[thread_index];\n    expectation->stack_base = thread_info->stack_base;\n    expectation->stack_size = thread_info->stack_size;\n    expectation->suspend_count = thread_info->suspend_count;\n    expectation->thread_name = thread_info->thread_name;\n\n    return PthreadToThreadID(thread_info->pthread);\n  }\n\n private:\n  struct ThreadInfo {\n    ThreadInfo(const std::string& thread_name)\n        : pthread(nullptr),\n          stack_base(0),\n          stack_size(0),\n          ready_semaphore(0),\n          exit_semaphore(0),\n          suspend_count(0),\n          thread_name(thread_name) {}\n\n    ~ThreadInfo() {}\n\n    // The thread’s ID, set at the time the thread is created.\n    pthread_t pthread;\n\n    // The base address of thread’s stack. The thread sets this in\n    // its ThreadMain().\n    mach_vm_address_t stack_base;\n\n    // The stack's maximum size. The thread sets this in its ThreadMain().\n    mach_vm_size_t stack_size;\n\n    // The worker thread signals ready_semaphore to indicate that it’s done\n    // setting up its ThreadInfo structure. The main thread waits on this\n    // semaphore before using any data that the worker thread is responsible for\n    // setting.\n    Semaphore ready_semaphore;\n\n    // The worker thread waits on exit_semaphore to determine when it’s safe to\n    // exit. The main thread signals exit_semaphore when it no longer needs the\n    // worker thread.\n    Semaphore exit_semaphore;\n\n    // The thread’s suspend count.\n    int suspend_count;\n\n    // The thread's name.\n    const std::string thread_name;\n  };\n\n  static void* ThreadMain(void* argument) {\n    ThreadInfo* thread_info = static_cast<ThreadInfo*>(argument);\n    const ScopedSetThreadName scoped_set_thread_name(thread_info->thread_name);\n\n    pthread_t thread = pthread_self();\n    thread_info->stack_base =\n        FromPointerCast<mach_vm_address_t>(pthread_get_stackaddr_np(thread));\n    thread_info->stack_size = pthread_get_stacksize_np(thread);\n\n    thread_info->ready_semaphore.Signal();\n    thread_info->exit_semaphore.Wait();\n\n    // Check this here after everything’s known to be synchronized, otherwise\n    // there’s a race between the parent thread storing this thread’s pthread_t\n    // in thread_info_pthread and this thread starting and attempting to access\n    // it.\n    CHECK_EQ(pthread_self(), thread_info->pthread);\n\n    return nullptr;\n  }\n\n  // This is a vector of pointers because the address of a ThreadInfo object is\n  // passed to each thread’s ThreadMain(), so they cannot move around in memory.\n  std::vector<std::unique_ptr<ThreadInfo>> thread_infos_;\n\n  // Prefix to use for each thread's name, suffixed with \"-$threadindex\".\n  const std::string thread_name_prefix_;\n};\n\nusing ThreadMap = std::map<uint64_t, TestThreadPool::ThreadExpectation>;\n\n// Verifies that all of the threads in |threads|, obtained from\n// ProcessReaderMac, agree with the expectation in |thread_map|. If\n// |tolerate_extra_threads| is true, |threads| is allowed to contain threads\n// that are not listed in |thread_map|. This is useful when testing situations\n// where code outside of the test’s control (such as system libraries) may start\n// threads, or may have started threads prior to a test’s execution.\nvoid ExpectSeveralThreads(ThreadMap* thread_map,\n                          const std::vector<ProcessReaderMac::Thread>& threads,\n                          const bool tolerate_extra_threads) {\n  if (tolerate_extra_threads) {\n    ASSERT_GE(threads.size(), thread_map->size());\n  } else {\n    ASSERT_EQ(threads.size(), thread_map->size());\n  }\n\n  for (size_t thread_index = 0; thread_index < threads.size(); ++thread_index) {\n    const ProcessReaderMac::Thread& thread = threads[thread_index];\n    mach_vm_address_t thread_stack_region_end =\n        thread.stack_region_address + thread.stack_region_size;\n\n    const auto& iterator = thread_map->find(thread.id);\n    if (!tolerate_extra_threads) {\n      // Make sure that the thread is in the expectation map.\n      ASSERT_NE(iterator, thread_map->end());\n    }\n\n    if (iterator != thread_map->end()) {\n      mach_vm_address_t expected_stack_region_end = iterator->second.stack_base;\n      if (thread_index > 0) {\n        // Non-main threads use the stack region to store thread data. See\n        // macOS 12 libpthread-486.100.11 src/pthread.c _pthread_allocate().\n#if defined(ARCH_CPU_ARM64)\n        // arm64 has an additional offset for alignment. See macOS 12\n        // libpthread-486.100.11 src/pthread.c _pthread_allocate() and\n        // PTHREAD_T_OFFSET (defined in src/types_internal.h).\n        expected_stack_region_end += sizeof(_opaque_pthread_t) + 0x3000;\n#else\n        expected_stack_region_end += sizeof(_opaque_pthread_t);\n#endif\n      }\n      EXPECT_LT(iterator->second.stack_base - iterator->second.stack_size,\n                thread.stack_region_address);\n      EXPECT_EQ(expected_stack_region_end, thread_stack_region_end);\n\n      EXPECT_EQ(thread.suspend_count, iterator->second.suspend_count);\n      EXPECT_EQ(thread.name, iterator->second.thread_name);\n\n      // Remove the thread from the expectation map since it’s already been\n      // found. This makes it easy to check for duplicate thread IDs, and makes\n      // it easy to check that all expected threads were found.\n      thread_map->erase(iterator);\n    }\n\n    // Make sure that this thread’s ID, stack region, and port don’t conflict\n    // with any other thread’s. Each thread should have a unique value for its\n    // ID and port, and each should have its own stack that doesn’t touch any\n    // other thread’s stack.\n    for (size_t other_thread_index = 0; other_thread_index < threads.size();\n         ++other_thread_index) {\n      if (other_thread_index == thread_index) {\n        continue;\n      }\n\n      const ProcessReaderMac::Thread& other_thread =\n          threads[other_thread_index];\n\n      EXPECT_NE(other_thread.id, thread.id);\n      EXPECT_NE(other_thread.port, thread.port);\n\n      mach_vm_address_t other_thread_stack_region_end =\n          other_thread.stack_region_address + other_thread.stack_region_size;\n      EXPECT_FALSE(thread.stack_region_address >=\n                       other_thread.stack_region_address &&\n                   thread.stack_region_address < other_thread_stack_region_end);\n      EXPECT_FALSE(thread_stack_region_end >\n                       other_thread.stack_region_address &&\n                   thread_stack_region_end <= other_thread_stack_region_end);\n    }\n  }\n\n  // Make sure that each expected thread was found.\n  EXPECT_TRUE(thread_map->empty());\n}\n\nTEST(ProcessReaderMac, SelfSeveralThreads) {\n  // Set up the ProcessReaderMac here, before any other threads are running.\n  // This tests that the threads it returns are lazily initialized as a snapshot\n  // of the threads at the time of the first call to Threads(), and not at the\n  // time the ProcessReader was created or initialized.\n  ProcessReaderMac process_reader;\n  ASSERT_TRUE(process_reader.Initialize(mach_task_self()));\n\n  TestThreadPool thread_pool(\"SelfSeveralThreads\");\n  constexpr size_t kChildThreads = 16;\n  ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(kChildThreads));\n\n  // Build a map of all expected threads, keyed by each thread’s ID. The values\n  // are addresses that should lie somewhere within each thread’s stack.\n  ThreadMap thread_map;\n  const uint64_t self_thread_id = PthreadToThreadID(pthread_self());\n  TestThreadPool::ThreadExpectation expectation;\n  expectation.stack_base = FromPointerCast<mach_vm_address_t>(\n      pthread_get_stackaddr_np(pthread_self()));\n  expectation.stack_size = pthread_get_stacksize_np(pthread_self());\n  expectation.suspend_count = 0;\n  thread_map[self_thread_id] = expectation;\n  for (size_t thread_index = 0; thread_index < kChildThreads; ++thread_index) {\n    uint64_t thread_id = thread_pool.GetThreadInfo(thread_index, &expectation);\n\n    // There can’t be any duplicate thread IDs.\n    EXPECT_EQ(thread_map.count(thread_id), 0u);\n\n    expectation.thread_name =\n        base::StringPrintf(\"SelfSeveralThreads-%zu\", thread_index);\n    thread_map[thread_id] = expectation;\n  }\n\n  const std::vector<ProcessReaderMac::Thread>& threads =\n      process_reader.Threads();\n\n  // Other tests that have run previously may have resulted in the creation of\n  // threads that still exist, so pass true for |tolerate_extra_threads|.\n  ExpectSeveralThreads(&thread_map, threads, true);\n\n  // When testing in-process, verify that when this thread shows up in the\n  // vector, it has the expected thread port, and that this thread port only\n  // shows up once.\n  thread_t thread_self = MachThreadSelf();\n  bool found_thread_self = false;\n  for (const ProcessReaderMac::Thread& thread : threads) {\n    if (thread.port == thread_self) {\n      EXPECT_FALSE(found_thread_self);\n      found_thread_self = true;\n      EXPECT_EQ(thread.id, self_thread_id);\n    }\n  }\n  EXPECT_TRUE(found_thread_self);\n}\n\nuint64_t GetThreadID() {\n  thread_identifier_info info;\n  mach_msg_type_number_t info_count = THREAD_IDENTIFIER_INFO_COUNT;\n  kern_return_t kr = thread_info(MachThreadSelf(),\n                                 THREAD_IDENTIFIER_INFO,\n                                 reinterpret_cast<thread_info_t>(&info),\n                                 &info_count);\n  MACH_CHECK(kr == KERN_SUCCESS, kr) << \"thread_info\";\n\n  return info.thread_id;\n}\n\nclass ProcessReaderThreadedChild final : public MachMultiprocess {\n public:\n  explicit ProcessReaderThreadedChild(const std::string thread_name_prefix,\n                                      size_t thread_count)\n      : MachMultiprocess(),\n        thread_name_prefix_(thread_name_prefix),\n        thread_count_(thread_count) {}\n\n  ProcessReaderThreadedChild(const ProcessReaderThreadedChild&) = delete;\n  ProcessReaderThreadedChild& operator=(const ProcessReaderThreadedChild&) =\n      delete;\n\n  ~ProcessReaderThreadedChild() {}\n\n private:\n  void MachMultiprocessParent() override {\n    ProcessReaderMac process_reader;\n    ASSERT_TRUE(process_reader.Initialize(ChildTask()));\n\n    FileHandle read_handle = ReadPipeHandle();\n\n    // Build a map of all expected threads, keyed by each thread’s ID, and with\n    // addresses that should lie somewhere within each thread’s stack as values.\n    // These IDs and addresses all come from the child process via the pipe.\n    ThreadMap thread_map;\n    for (size_t thread_index = 0; thread_index < thread_count_ + 1;\n         ++thread_index) {\n      uint64_t thread_id;\n      CheckedReadFileExactly(read_handle, &thread_id, sizeof(thread_id));\n\n      TestThreadPool::ThreadExpectation expectation;\n      CheckedReadFileExactly(\n          read_handle, &expectation.stack_base, sizeof(expectation.stack_base));\n      CheckedReadFileExactly(\n          read_handle, &expectation.stack_size, sizeof(expectation.stack_size));\n      CheckedReadFileExactly(read_handle,\n                             &expectation.suspend_count,\n                             sizeof(expectation.suspend_count));\n      std::string::size_type expected_thread_name_length;\n      CheckedReadFileExactly(read_handle,\n                             &expected_thread_name_length,\n                             sizeof(expected_thread_name_length));\n      std::string expected_thread_name(expected_thread_name_length, '\\0');\n      CheckedReadFileExactly(read_handle,\n                             expected_thread_name.data(),\n                             expected_thread_name_length);\n      expectation.thread_name = expected_thread_name;\n\n      // There can’t be any duplicate thread IDs.\n      EXPECT_EQ(thread_map.count(thread_id), 0u);\n\n      thread_map[thread_id] = expectation;\n    }\n\n    const std::vector<ProcessReaderMac::Thread>& threads =\n        process_reader.Threads();\n\n    // The child shouldn’t have any threads other than its main thread and the\n    // ones it created in its pool, so pass false for |tolerate_extra_threads|.\n    ExpectSeveralThreads(&thread_map, threads, false);\n  }\n\n  void MachMultiprocessChild() override {\n    TestThreadPool thread_pool(thread_name_prefix_);\n    ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(thread_count_));\n\n    const std::string current_thread_name(base::StringPrintf(\n        \"%s-MachMultiprocessChild\", thread_name_prefix_.c_str()));\n    const ScopedSetThreadName scoped_set_thread_name(current_thread_name);\n\n    FileHandle write_handle = WritePipeHandle();\n\n    // This thread isn’t part of the thread pool, but the parent will be able\n    // to inspect it. Write an entry for it.\n    uint64_t thread_id = GetThreadID();\n\n    CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id));\n\n    TestThreadPool::ThreadExpectation expectation;\n    pthread_t thread = pthread_self();\n    expectation.stack_base =\n        FromPointerCast<mach_vm_address_t>(pthread_get_stackaddr_np(thread));\n    expectation.stack_size = pthread_get_stacksize_np(thread);\n    expectation.suspend_count = 0;\n\n    CheckedWriteFile(\n        write_handle, &expectation.stack_base, sizeof(expectation.stack_base));\n    CheckedWriteFile(\n        write_handle, &expectation.stack_size, sizeof(expectation.stack_size));\n    CheckedWriteFile(write_handle,\n                     &expectation.suspend_count,\n                     sizeof(expectation.suspend_count));\n    const std::string::size_type current_thread_name_length =\n        current_thread_name.length();\n    CheckedWriteFile(write_handle,\n                     &current_thread_name_length,\n                     sizeof(current_thread_name_length));\n    CheckedWriteFile(\n        write_handle, current_thread_name.data(), current_thread_name_length);\n\n    // Write an entry for everything in the thread pool.\n    for (size_t thread_index = 0; thread_index < thread_count_;\n         ++thread_index) {\n      thread_id = thread_pool.GetThreadInfo(thread_index, &expectation);\n\n      CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id));\n      CheckedWriteFile(write_handle,\n                       &expectation.stack_base,\n                       sizeof(expectation.stack_base));\n      CheckedWriteFile(write_handle,\n                       &expectation.stack_size,\n                       sizeof(expectation.stack_size));\n      CheckedWriteFile(write_handle,\n                       &expectation.suspend_count,\n                       sizeof(expectation.suspend_count));\n      const std::string thread_pool_thread_name = base::StringPrintf(\n          \"%s-%zu\", thread_name_prefix_.c_str(), thread_index);\n      const std::string::size_type thread_pool_thread_name_length =\n          thread_pool_thread_name.length();\n      CheckedWriteFile(write_handle,\n                       &thread_pool_thread_name_length,\n                       sizeof(thread_pool_thread_name_length));\n      CheckedWriteFile(write_handle,\n                       thread_pool_thread_name.data(),\n                       thread_pool_thread_name_length);\n    }\n\n    // Wait for the parent to signal that it’s OK to exit by closing its end of\n    // the pipe.\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  const std::string thread_name_prefix_;\n  size_t thread_count_;\n};\n\nTEST(ProcessReaderMac, ChildOneThread) {\n  // The main thread plus zero child threads equals one thread.\n  constexpr size_t kChildThreads = 0;\n  ProcessReaderThreadedChild process_reader_threaded_child(\"ChildOneThread\",\n                                                           kChildThreads);\n  process_reader_threaded_child.Run();\n}\n\nTEST(ProcessReaderMac, ChildSeveralThreads) {\n  constexpr size_t kChildThreads = 64;\n  ProcessReaderThreadedChild process_reader_threaded_child(\n      \"ChildSeveralThreads\", kChildThreads);\n  process_reader_threaded_child.Run();\n}\n\ntemplate <typename T>\nT GetDyldFunction(const char* symbol) {\n  static void* dl_handle = []() -> void* {\n    Dl_info dl_info;\n    if (!dladdr(reinterpret_cast<void*>(dlopen), &dl_info)) {\n      LOG(ERROR) << \"dladdr: failed\";\n      return nullptr;\n    }\n\n    void* dl_handle =\n        dlopen(dl_info.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);\n    DCHECK(dl_handle) << \"dlopen: \" << dlerror();\n\n    return dl_handle;\n  }();\n\n  if (!dl_handle) {\n    return nullptr;\n  }\n\n  return reinterpret_cast<T>(dlsym(dl_handle, symbol));\n}\n\nvoid VerifyImageExistence(const char* path) {\n  const char* stat_path;\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_16\n  static auto _dyld_shared_cache_contains_path =\n      GetDyldFunction<bool (*)(const char*)>(\n          \"_dyld_shared_cache_contains_path\");\n#endif\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunguarded-availability\"\n  if (&_dyld_shared_cache_contains_path &&\n      _dyld_shared_cache_contains_path(path)) {\n#pragma clang diagnostic pop\n    // The timestamp will either match the timestamp of the dyld_shared_cache\n    // file in use, or be 0.\n    static const char* dyld_shared_cache_file_path = []() -> const char* {\n      auto dyld_shared_cache_file_path_f =\n          GetDyldFunction<const char* (*)()>(\"dyld_shared_cache_file_path\");\n\n      // dyld_shared_cache_file_path should always be present if\n      // _dyld_shared_cache_contains_path is.\n      DCHECK(dyld_shared_cache_file_path_f);\n\n      const char* dyld_shared_cache_file_path = dyld_shared_cache_file_path_f();\n      DCHECK(dyld_shared_cache_file_path);\n\n      return dyld_shared_cache_file_path;\n    }();\n\n    stat_path = dyld_shared_cache_file_path;\n  } else {\n    stat_path = path;\n  }\n\n  struct stat stat_buf;\n  int rv = stat(stat_path, &stat_buf);\n  EXPECT_EQ(rv, 0) << ErrnoMessage(\"stat\");\n}\n\n// cl_kernels images (OpenCL kernels) are weird. They’re not ld output and don’t\n// exist as files on disk. On OS X 10.10 and 10.11, their Mach-O structure isn’t\n// perfect. They show up loaded into many executables, so these quirks should be\n// tolerated.\n//\n// Create an object of this class to ensure that at least one cl_kernels image\n// is present in a process, to be able to test that all of the process-reading\n// machinery tolerates them. On systems where cl_kernels modules have known\n// quirks, the image that an object of this class produces will also have those\n// quirks.\n//\n// https://openradar.appspot.com/20239912\nclass ScopedOpenCLNoOpKernel {\n public:\n  ScopedOpenCLNoOpKernel()\n      : context_(nullptr),\n        program_(nullptr),\n        kernel_(nullptr),\n        success_(false) {}\n\n  ScopedOpenCLNoOpKernel(const ScopedOpenCLNoOpKernel&) = delete;\n  ScopedOpenCLNoOpKernel& operator=(const ScopedOpenCLNoOpKernel&) = delete;\n\n  ~ScopedOpenCLNoOpKernel() {\n    if (kernel_) {\n      cl_int rv = clReleaseKernel(kernel_);\n      EXPECT_EQ(rv, CL_SUCCESS) << \"clReleaseKernel\";\n    }\n\n    if (program_) {\n      cl_int rv = clReleaseProgram(program_);\n      EXPECT_EQ(rv, CL_SUCCESS) << \"clReleaseProgram\";\n    }\n\n    if (context_) {\n      cl_int rv = clReleaseContext(context_);\n      EXPECT_EQ(rv, CL_SUCCESS) << \"clReleaseContext\";\n    }\n  }\n\n  void SetUp() {\n    cl_platform_id platform_id;\n    cl_int rv = clGetPlatformIDs(1, &platform_id, nullptr);\n    ASSERT_EQ(rv, CL_SUCCESS) << \"clGetPlatformIDs\";\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_10 && \\\n    __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_10\n    // cl_device_id is really available in OpenCL.framework back to 10.5, but in\n    // the 10.10 SDK and later, OpenCL.framework includes <OpenGL/CGLDevice.h>,\n    // which has its own cl_device_id that was introduced in 10.10. That\n    // triggers erroneous availability warnings.\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunguarded-availability\"\n#define DISABLED_WUNGUARDED_AVAILABILITY\n#endif  // SDK >= 10.10 && DT < 10.10\n    // Use CL_DEVICE_TYPE_CPU to ensure that the kernel would execute on the\n    // CPU. This is the only device type that a cl_kernels image will be created\n    // for.\n    cl_device_id device_id;\n#if defined(DISABLED_WUNGUARDED_AVAILABILITY)\n#pragma clang diagnostic pop\n#undef DISABLED_WUNGUARDED_AVAILABILITY\n#endif  // DISABLED_WUNGUARDED_AVAILABILITY\n    rv =\n        clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_CPU, 1, &device_id, nullptr);\n#if defined(ARCH_CPU_ARM64)\n    // CL_DEVICE_TYPE_CPU doesn’t seem to work at all on arm64, meaning that\n    // these weird OpenCL modules probably don’t show up there at all. Keep this\n    // test even on arm64 in case this ever does start working.\n    if (rv == CL_INVALID_VALUE) {\n      return;\n    }\n#endif  // ARCH_CPU_ARM64\n    ASSERT_EQ(rv, CL_SUCCESS) << \"clGetDeviceIDs\";\n\n    context_ = clCreateContext(nullptr, 1, &device_id, nullptr, nullptr, &rv);\n    ASSERT_EQ(rv, CL_SUCCESS) << \"clCreateContext\";\n\n    // The goal of the program in |sources| is to produce a cl_kernels image\n    // that doesn’t strictly conform to Mach-O expectations. On OS X 10.10,\n    // cl_kernels modules show up with an __LD,__compact_unwind section, showing\n    // up in the __TEXT segment. MachOImageSegmentReader would normally reject\n    // modules for this problem, but a special exception is made when this\n    // occurs in cl_kernels images. This portion of the test is aimed at making\n    // sure that this exception works correctly.\n    //\n    // A true no-op program doesn’t actually produce unwind data, so there would\n    // be no errant __LD,__compact_unwind section on 10.10, and the test\n    // wouldn’t be complete. This simple no-op, which calls a built-in function,\n    // does produce unwind data provided optimization is disabled.\n    // \"-cl-opt-disable\" is given to clBuildProgram() below.\n    const char* sources[] = {\n        \"__kernel void NoOp(void) {barrier(CLK_LOCAL_MEM_FENCE);}\",\n    };\n    const size_t source_lengths[] = {\n        strlen(sources[0]),\n    };\n    static_assert(std::size(sources) == std::size(source_lengths),\n                  \"arrays must be parallel\");\n\n    program_ = clCreateProgramWithSource(\n        context_, std::size(sources), sources, source_lengths, &rv);\n    ASSERT_EQ(rv, CL_SUCCESS) << \"clCreateProgramWithSource\";\n\n    rv = clBuildProgram(\n        program_, 1, &device_id, \"-cl-opt-disable\", nullptr, nullptr);\n    ASSERT_EQ(rv, CL_SUCCESS) << \"clBuildProgram\";\n\n    kernel_ = clCreateKernel(program_, \"NoOp\", &rv);\n    ASSERT_EQ(rv, CL_SUCCESS) << \"clCreateKernel\";\n\n    success_ = true;\n  }\n\n  bool success() const { return success_; }\n\n private:\n  cl_context context_;\n  cl_program program_;\n  cl_kernel kernel_;\n  bool success_;\n};\n\n// Although Mac OS X 10.6 has OpenCL and can compile and execute OpenCL code,\n// OpenCL kernels that run on the CPU do not result in cl_kernels images\n// appearing on that OS version.\nbool ExpectCLKernels() {\n  return __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_7 ||\n         MacOSVersionNumber() >= 10'07'00;\n}\n\n// Starting in dyld-1284.13 (macOS 15.4), _dyld_get_image_name (in\n// dyld4::PrebuiltLoader::path) returns an absolute path for a main executable,\n// even when the image was not loaded from an absolute path. This can break test\n// expectations when the test executable is not invoked from an absolute path.\n// As a workaround, just use the main executable’s basename for comparison\n// purposes.\nstd::string ComparableModuleName(const std::string& module_name,\n                                 uint32_t file_type) {\n  return file_type == MH_EXECUTE\n             ? base::FilePath(module_name).BaseName().value()\n             : module_name;\n}\n\nTEST(ProcessReaderMac, SelfModules) {\n  ScopedOpenCLNoOpKernel ensure_cl_kernels;\n  ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp());\n\n  ProcessReaderMac process_reader;\n  ASSERT_TRUE(process_reader.Initialize(mach_task_self()));\n\n  uint32_t dyld_image_count = _dyld_image_count();\n\n  std::set<std::string> cl_kernel_names;\n  auto modules = process_reader.Modules();\n  ModuleSet actual_modules;\n  for (size_t i = 0; i < modules.size(); ++i) {\n    auto& module = modules[i];\n    ASSERT_TRUE(module.reader);\n    if (i == modules.size() - 1) {\n      EXPECT_EQ(module.name, kDyldPath);\n      const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();\n      if (dyld_image_infos->version >= 2) {\n        EXPECT_EQ(module.reader->Address(),\n                  FromPointerCast<mach_vm_address_t>(\n                      dyld_image_infos->dyldImageLoadAddress));\n      }\n      // Don't include dyld, since dyld image APIs will not have an entry for\n      // dyld itself.\n      continue;\n    }\n    // Ensure executable is first, and that there's only one.\n    uint32_t file_type = module.reader->FileType();\n    if (i == 0) {\n      EXPECT_EQ(file_type, static_cast<uint32_t>(MH_EXECUTE));\n    } else {\n      EXPECT_NE(file_type, static_cast<uint32_t>(MH_EXECUTE));\n      EXPECT_NE(file_type, static_cast<uint32_t>(MH_DYLINKER));\n    }\n    if (IsMalformedCLKernelsModule(module.reader->FileType(), module.name)) {\n      cl_kernel_names.insert(module.name);\n    }\n    actual_modules.insert(\n        std::make_pair(ComparableModuleName(module.name, file_type),\n                       module.reader->Address()));\n  }\n  EXPECT_EQ(cl_kernel_names.size() > 0,\n            ExpectCLKernels() && ensure_cl_kernels.success());\n\n  // There needs to be at least an entry for the main executable and a dylib.\n  ASSERT_GE(actual_modules.size(), 2u);\n  ASSERT_EQ(actual_modules.size(), dyld_image_count);\n\n  ModuleSet expect_modules;\n  for (uint32_t index = 0; index < dyld_image_count; ++index) {\n    const char* dyld_image_name = _dyld_get_image_name(index);\n    mach_vm_address_t dyld_image_address =\n        FromPointerCast<mach_vm_address_t>(_dyld_get_image_header(index));\n    const MachHeader* image_header =\n        reinterpret_cast<const MachHeader*>(dyld_image_address);\n    expect_modules.insert(std::make_pair(\n        ComparableModuleName(dyld_image_name, image_header->filetype),\n        dyld_image_address));\n    if (cl_kernel_names.find(dyld_image_name) == cl_kernel_names.end()) {\n      VerifyImageExistence(dyld_image_name);\n    }\n  }\n  EXPECT_EQ(actual_modules, expect_modules);\n}\n\nclass ProcessReaderModulesChild final : public MachMultiprocess {\n public:\n  explicit ProcessReaderModulesChild(bool ensure_cl_kernels_success)\n      : MachMultiprocess(),\n        ensure_cl_kernels_success_(ensure_cl_kernels_success) {}\n\n  ProcessReaderModulesChild(const ProcessReaderModulesChild&) = delete;\n  ProcessReaderModulesChild& operator=(const ProcessReaderModulesChild&) =\n      delete;\n\n  ~ProcessReaderModulesChild() {}\n\n private:\n  void MachMultiprocessParent() override {\n    ProcessReaderMac process_reader;\n    ASSERT_TRUE(process_reader.Initialize(ChildTask()));\n    const std::vector<ProcessReaderMac::Module>& modules =\n        process_reader.Modules();\n\n    ModuleSet actual_modules;\n    std::set<std::string> cl_kernel_names;\n    for (size_t i = 0; i < modules.size(); ++i) {\n      auto& module = modules[i];\n      ASSERT_TRUE(module.reader);\n      uint32_t file_type = module.reader->FileType();\n      if (i == 0) {\n        EXPECT_EQ(file_type, static_cast<uint32_t>(MH_EXECUTE));\n      } else if (i == modules.size() - 1) {\n        EXPECT_EQ(file_type, static_cast<uint32_t>(MH_DYLINKER));\n      } else {\n        EXPECT_NE(file_type, static_cast<uint32_t>(MH_EXECUTE));\n        EXPECT_NE(file_type, static_cast<uint32_t>(MH_DYLINKER));\n      }\n      if (IsMalformedCLKernelsModule(module.reader->FileType(), module.name)) {\n        cl_kernel_names.insert(module.name);\n      }\n      actual_modules.insert(\n          std::make_pair(ComparableModuleName(module.name, file_type),\n                         module.reader->Address()));\n    }\n\n    // There needs to be at least an entry for the main executable, for a dylib,\n    // and for dyld.\n    ASSERT_GE(actual_modules.size(), 3u);\n\n    FileHandle read_handle = ReadPipeHandle();\n\n    uint32_t expect_modules_size;\n    CheckedReadFileExactly(\n        read_handle, &expect_modules_size, sizeof(expect_modules_size));\n\n    ASSERT_EQ(actual_modules.size(), expect_modules_size);\n    ModuleSet expect_modules;\n\n    for (size_t index = 0; index < expect_modules_size; ++index) {\n      uint32_t expect_name_length;\n      CheckedReadFileExactly(\n          read_handle, &expect_name_length, sizeof(expect_name_length));\n\n      // The NUL terminator is not read.\n      std::string expect_name(expect_name_length, '\\0');\n      CheckedReadFileExactly(read_handle, &expect_name[0], expect_name_length);\n\n      mach_vm_address_t expect_address;\n      CheckedReadFileExactly(\n          read_handle, &expect_address, sizeof(expect_address));\n\n      uint32_t file_type;\n      CheckedReadFileExactly(read_handle, &file_type, sizeof(file_type));\n\n      if (cl_kernel_names.find(expect_name) == cl_kernel_names.end()) {\n        VerifyImageExistence(expect_name.c_str());\n      }\n\n      expect_modules.insert(std::make_pair(\n          ComparableModuleName(expect_name, file_type), expect_address));\n    }\n    EXPECT_EQ(cl_kernel_names.size() > 0,\n              ExpectCLKernels() && ensure_cl_kernels_success_);\n    EXPECT_EQ(expect_modules, actual_modules);\n  }\n\n  void MachMultiprocessChild() override {\n    FileHandle write_handle = WritePipeHandle();\n\n    uint32_t dyld_image_count = _dyld_image_count();\n    const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();\n\n    uint32_t write_image_count = dyld_image_count;\n    if (dyld_image_infos->version >= 2) {\n      // dyld_image_count doesn’t include an entry for dyld itself, but one will\n      // be written.\n      ++write_image_count;\n    }\n\n    CheckedWriteFile(\n        write_handle, &write_image_count, sizeof(write_image_count));\n\n    for (size_t index = 0; index < write_image_count; ++index) {\n      const char* dyld_image_name;\n      mach_vm_address_t dyld_image_address;\n\n      if (index < dyld_image_count) {\n        dyld_image_name = _dyld_get_image_name(index);\n        dyld_image_address =\n            FromPointerCast<mach_vm_address_t>(_dyld_get_image_header(index));\n      } else {\n        dyld_image_name = kDyldPath;\n        dyld_image_address = FromPointerCast<mach_vm_address_t>(\n            dyld_image_infos->dyldImageLoadAddress);\n      }\n\n      const MachHeader* image_header =\n          reinterpret_cast<const MachHeader*>(dyld_image_address);\n      uint32_t file_type = image_header->filetype;\n\n      uint32_t dyld_image_name_length = strlen(dyld_image_name);\n      CheckedWriteFile(write_handle,\n                       &dyld_image_name_length,\n                       sizeof(dyld_image_name_length));\n\n      // The NUL terminator is not written.\n      CheckedWriteFile(write_handle, dyld_image_name, dyld_image_name_length);\n\n      CheckedWriteFile(\n          write_handle, &dyld_image_address, sizeof(dyld_image_address));\n      CheckedWriteFile(write_handle, &file_type, sizeof(file_type));\n    }\n\n    // Wait for the parent to signal that it’s OK to exit by closing its end of\n    // the pipe.\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  bool ensure_cl_kernels_success_;\n};\n\nTEST(ProcessReaderMac, ChildModules) {\n  ScopedOpenCLNoOpKernel ensure_cl_kernels;\n  ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp());\n\n  ProcessReaderModulesChild process_reader_modules_child(\n      ensure_cl_kernels.success());\n  process_reader_modules_child.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/process_snapshot_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/process_snapshot_mac.h\"\n\n#include <utility>\n\n#include \"base/logging.h\"\n#include \"util/misc/tri_state.h\"\n\nnamespace crashpad {\n\nProcessSnapshotMac::ProcessSnapshotMac()\n    : ProcessSnapshot(),\n      system_(),\n      threads_(),\n      modules_(),\n      exception_(),\n      process_reader_(),\n      report_id_(),\n      client_id_(),\n      annotations_simple_map_(),\n      snapshot_time_(),\n      initialized_() {\n}\n\nProcessSnapshotMac::~ProcessSnapshotMac() {\n}\n\nbool ProcessSnapshotMac::Initialize(task_t task) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (gettimeofday(&snapshot_time_, nullptr) != 0) {\n    PLOG(ERROR) << \"gettimeofday\";\n    return false;\n  }\n\n  if (!process_reader_.Initialize(task)) {\n    return false;\n  }\n\n  client_id_.InitializeToZero();\n  system_.Initialize(&process_reader_, &snapshot_time_);\n\n  InitializeThreads();\n  InitializeModules();\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessSnapshotMac::InitializeException(\n    exception_behavior_t behavior,\n    thread_t exception_thread,\n    exception_type_t exception,\n    const mach_exception_data_type_t* code,\n    mach_msg_type_number_t code_count,\n    thread_state_flavor_t flavor,\n    ConstThreadState state,\n    mach_msg_type_number_t state_count) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  DCHECK(!exception_);\n\n  exception_.reset(new internal::ExceptionSnapshotMac());\n  if (!exception_->Initialize(&process_reader_,\n                              behavior,\n                              exception_thread,\n                              exception,\n                              code,\n                              code_count,\n                              flavor,\n                              state,\n                              state_count)) {\n    exception_.reset();\n    return false;\n  }\n\n  return true;\n}\n\nvoid ProcessSnapshotMac::GetCrashpadOptions(\n    CrashpadInfoClientOptions* options) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  CrashpadInfoClientOptions local_options;\n\n  for (const auto& module : modules_) {\n    CrashpadInfoClientOptions module_options;\n    module->GetCrashpadOptions(&module_options);\n\n    if (local_options.crashpad_handler_behavior == TriState::kUnset) {\n      local_options.crashpad_handler_behavior =\n          module_options.crashpad_handler_behavior;\n    }\n    if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {\n      local_options.system_crash_reporter_forwarding =\n          module_options.system_crash_reporter_forwarding;\n    }\n    if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {\n      local_options.gather_indirectly_referenced_memory =\n          module_options.gather_indirectly_referenced_memory;\n      local_options.indirectly_referenced_memory_cap =\n          module_options.indirectly_referenced_memory_cap;\n    }\n\n    // If non-default values have been found for all options, the loop can end\n    // early.\n    if (local_options.crashpad_handler_behavior != TriState::kUnset &&\n        local_options.system_crash_reporter_forwarding != TriState::kUnset &&\n        local_options.gather_indirectly_referenced_memory != TriState::kUnset) {\n      break;\n    }\n  }\n\n  *options = local_options;\n}\n\npid_t ProcessSnapshotMac::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.ProcessID();\n}\n\npid_t ProcessSnapshotMac::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.ParentProcessID();\n}\n\nvoid ProcessSnapshotMac::SnapshotTime(timeval* snapshot_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *snapshot_time = snapshot_time_;\n}\n\nvoid ProcessSnapshotMac::ProcessStartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  process_reader_.StartTime(start_time);\n}\n\nvoid ProcessSnapshotMac::ProcessCPUTimes(timeval* user_time,\n                                         timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  process_reader_.CPUTimes(user_time, system_time);\n}\n\nvoid ProcessSnapshotMac::ReportID(UUID* report_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *report_id = report_id_;\n}\n\nvoid ProcessSnapshotMac::ClientID(UUID* client_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *client_id = client_id_;\n}\n\nconst std::map<std::string, std::string>&\nProcessSnapshotMac::AnnotationsSimpleMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_simple_map_;\n}\n\nconst SystemSnapshot* ProcessSnapshotMac::System() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &system_;\n}\n\nstd::vector<const ThreadSnapshot*> ProcessSnapshotMac::Threads() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ThreadSnapshot*> threads;\n  for (const auto& thread : threads_) {\n    threads.push_back(thread.get());\n  }\n  return threads;\n}\n\nstd::vector<const ModuleSnapshot*> ProcessSnapshotMac::Modules() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ModuleSnapshot*> modules;\n  for (const auto& module : modules_) {\n    modules.push_back(module.get());\n  }\n  return modules;\n}\n\nstd::vector<UnloadedModuleSnapshot> ProcessSnapshotMac::UnloadedModules()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<UnloadedModuleSnapshot>();\n}\n\nconst ExceptionSnapshot* ProcessSnapshotMac::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_.get();\n}\n\nstd::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotMac::MemoryMap()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<const MemoryMapRegionSnapshot*>();\n}\n\nstd::vector<HandleSnapshot> ProcessSnapshotMac::Handles() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<HandleSnapshot>();\n}\n\nstd::vector<const MemorySnapshot*> ProcessSnapshotMac::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<const MemorySnapshot*>();\n}\n\nconst ProcessMemory* ProcessSnapshotMac::Memory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.Memory();\n}\n\nvoid ProcessSnapshotMac::InitializeThreads() {\n  const std::vector<ProcessReaderMac::Thread>& process_reader_threads =\n      process_reader_.Threads();\n  for (const ProcessReaderMac::Thread& process_reader_thread :\n       process_reader_threads) {\n    auto thread = std::make_unique<internal::ThreadSnapshotMac>();\n    if (thread->Initialize(&process_reader_, process_reader_thread)) {\n      threads_.push_back(std::move(thread));\n    }\n  }\n}\n\nvoid ProcessSnapshotMac::InitializeModules() {\n  const std::vector<ProcessReaderMac::Module>& process_reader_modules =\n      process_reader_.Modules();\n  for (const ProcessReaderMac::Module& process_reader_module :\n       process_reader_modules) {\n    auto module = std::make_unique<internal::ModuleSnapshotMac>();\n    if (module->Initialize(&process_reader_, process_reader_module)) {\n      modules_.push_back(std::move(module));\n    }\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/process_snapshot_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_\n#define CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_\n\n#include <mach/mach.h>\n#include <sys/time.h>\n#include <unistd.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"client/crashpad_info.h\"\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/mac/exception_snapshot_mac.h\"\n#include \"snapshot/mac/module_snapshot_mac.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"snapshot/mac/system_snapshot_mac.h\"\n#include \"snapshot/mac/thread_snapshot_mac.h\"\n#include \"snapshot/memory_map_region_snapshot.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\n//! \\brief A ProcessSnapshot of a running (or crashed) process running on a\n//!     macOS system.\nclass ProcessSnapshotMac final : public ProcessSnapshot {\n public:\n  ProcessSnapshotMac();\n\n  ProcessSnapshotMac(const ProcessSnapshotMac&) = delete;\n  ProcessSnapshotMac& operator=(const ProcessSnapshotMac&) = delete;\n\n  ~ProcessSnapshotMac() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] task The task to create a snapshot from.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(task_t task);\n\n  //! \\brief Initializes the object’s exception.\n  //!\n  //! This populates the data to be returned by Exception(). The parameters may\n  //! be passed directly through from a Mach exception handler.\n  //!\n  //! This method must not be called until after a successful call to\n  //! Initialize().\n  //!\n  //! \\return `true` if the exception information could be initialized, `false`\n  //!     otherwise with an appropriate message logged. When this method returns\n  //!     `false`, the ProcessSnapshotMac object’s validity remains unchanged.\n  bool InitializeException(exception_behavior_t behavior,\n                           thread_t exception_thread,\n                           exception_type_t exception,\n                           const mach_exception_data_type_t* code,\n                           mach_msg_type_number_t code_count,\n                           thread_state_flavor_t flavor,\n                           ConstThreadState state,\n                           mach_msg_type_number_t state_count);\n\n  //! \\brief Sets the value to be returned by ReportID().\n  //!\n  //! On macOS, the crash report ID is under the control of the snapshot\n  //! producer, which may call this method to set the report ID. If this is not\n  //! done, ReportID() will return an identifier consisting entirely of zeroes.\n  void SetReportID(const UUID& report_id) { report_id_ = report_id; }\n\n  //! \\brief Sets the value to be returned by ClientID().\n  //!\n  //! On macOS, the client ID is under the control of the snapshot producer,\n  //! which may call this method to set the client ID. If this is not done,\n  //! ClientID() will return an identifier consisting entirely of zeroes.\n  void SetClientID(const UUID& client_id) { client_id_ = client_id; }\n\n  //! \\brief Sets the value to be returned by AnnotationsSimpleMap().\n  //!\n  //! On macOS, all process annotations are under the control of the snapshot\n  //! producer, which may call this method to establish these annotations.\n  //! Contrast this with module annotations, which are under the control of the\n  //! process being snapshotted.\n  void SetAnnotationsSimpleMap(\n      const std::map<std::string, std::string>& annotations_simple_map) {\n    annotations_simple_map_ = annotations_simple_map;\n  }\n\n  //! \\brief Returns options from CrashpadInfo structures found in modules in\n  //!     the process.\n  //!\n  //! \\param[out] options Options set in CrashpadInfo structures in modules in\n  //!     the process.\n  void GetCrashpadOptions(CrashpadInfoClientOptions* options);\n\n  // ProcessSnapshot:\n\n  pid_t ProcessID() const override;\n  pid_t ParentProcessID() const override;\n  void SnapshotTime(timeval* snapshot_time) const override;\n  void ProcessStartTime(timeval* start_time) const override;\n  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;\n  void ReportID(UUID* report_id) const override;\n  void ClientID(UUID* client_id) const override;\n  const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const override;\n  const SystemSnapshot* System() const override;\n  std::vector<const ThreadSnapshot*> Threads() const override;\n  std::vector<const ModuleSnapshot*> Modules() const override;\n  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;\n  const ExceptionSnapshot* Exception() const override;\n  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;\n  std::vector<HandleSnapshot> Handles() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n  const ProcessMemory* Memory() const override;\n\n private:\n  // Initializes threads_ on behalf of Initialize().\n  void InitializeThreads();\n\n  // Initializes modules_ on behalf of Initialize().\n  void InitializeModules();\n\n  internal::SystemSnapshotMac system_;\n  std::vector<std::unique_ptr<internal::ThreadSnapshotMac>> threads_;\n  std::vector<std::unique_ptr<internal::ModuleSnapshotMac>> modules_;\n  std::unique_ptr<internal::ExceptionSnapshotMac> exception_;\n  ProcessReaderMac process_reader_;\n  UUID report_id_;\n  UUID client_id_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  timeval snapshot_time_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_\n"
  },
  {
    "path": "snapshot/mac/process_types/all.proctype",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This file is intended to be included multiple times in the same translation\n// unit, so #include guards are intentionally absent.\n//\n// This file is included by snapshot/mac/process_types.h and\n// snapshot/mac/process_types.cc to produce process type struct definitions and\n// accessors.\n\n#include \"snapshot/mac/process_types/annotation.proctype\"\n#include \"snapshot/mac/process_types/crashpad_info.proctype\"\n#include \"snapshot/mac/process_types/crashreporterclient.proctype\"\n#include \"snapshot/mac/process_types/dyld_images.proctype\"\n#include \"snapshot/mac/process_types/loader.proctype\"\n#include \"snapshot/mac/process_types/nlist.proctype\"\n"
  },
  {
    "path": "snapshot/mac/process_types/annotation.proctype",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nPROCESS_TYPE_STRUCT_BEGIN(Annotation)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, link_node)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, name)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, value)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size)\n  PROCESS_TYPE_STRUCT_MEMBER(uint16_t, type)\nPROCESS_TYPE_STRUCT_END(Annotation)\n\n#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY)\n\nPROCESS_TYPE_STRUCT_BEGIN(AnnotationList)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, tail_pointer)\n  PROCESS_TYPE_STRUCT_MEMBER(crashpad::process_types::Annotation, head)\n  PROCESS_TYPE_STRUCT_MEMBER(crashpad::process_types::Annotation, tail)\nPROCESS_TYPE_STRUCT_END(AnnotationList)\n\n#endif  // !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY)\n"
  },
  {
    "path": "snapshot/mac/process_types/crashpad_info.proctype",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// The file corresponds to Crashpad’s client/crashpad_info.h.\n//\n// This file is intended to be included multiple times in the same translation\n// unit, so #include guards are intentionally absent.\n//\n// This file is included by snapshot/mac/process_types.h and\n// snapshot/mac/process_types.cc to produce process type struct definitions and\n// accessors.\n\n// Client Mach-O images will contain a __DATA,crashpad_info section formatted\n// according to this structure.\n//\n// CrashpadInfo is variable-length. Its length dictated by its |size| field\n// which is always present. A custom implementation of the flavored\n// ReadSpecificInto function that understands how to use this field is provided\n// in snapshot/mac/process_types/custom.cc. No implementation of ReadArrayInto\n// is provided because CrashpadInfo structs are singletons in a module and are\n// never present in arrays, so the functionality is unnecessary.\n\n#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \\\n    !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY)\n\nPROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, signature)\n\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size)\n  PROCESS_TYPE_STRUCT_SIZED(CrashpadInfo, size)\n\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version)\n\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, indirectly_referenced_memory_cap)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, padding_0)\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, crashpad_handler_behavior)  // TriState\n\n  // TriState\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, system_crash_reporter_forwarding)\n\n  // TriState\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, gather_indirectly_referenced_memory)\n\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, padding_1)\n\n  // SimpleAddressRangeBag*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, extra_memory_ranges)\n\n  // SimpleStringDictionary*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations)\n\n  // UserDataStreamListEntry*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, user_data_minidump_stream_head)\n\n  // AnnotationList*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, annotations_list)\nPROCESS_TYPE_STRUCT_END(CrashpadInfo)\n\n#endif  // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO &&\n        // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY\n"
  },
  {
    "path": "snapshot/mac/process_types/crashreporterclient.proctype",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// The name of this file was chosen based on an #include in\n// https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/PrettyStackTrace.cpp.\n// The name of the structure it describes was chosen based on that file as well\n// as 10.9\n// https://github.com/apple-oss-distributions/cups/blame/cups-372/cups/backend/usb-darwin.c.\n//\n// This file is intended to be included multiple times in the same translation\n// unit, so #include guards are intentionally absent.\n//\n// This file is included by snapshot/mac/process_types.h and\n// snapshot/mac/process_types.cc to produce process type struct definitions and\n// accessors. This file is also used by the iOS in process handler to read both\n// messages in client/ios_handler/in_process_intermediate_dump_handler.cc.\n\n// Client Mach-O images will contain a __DATA,__crash_info or\n// __DATA_DIRTY,__crash_info section formatted according to this structure.\n//\n// crashreporter_annotations_t is variable-length. Its length dictated by its\n// |version| field which is always present. A custom implementation of the\n// flavored ReadSpecificInto function that understands how to map this field to\n// the structure’s actual size is provided in\n// snapshot/mac/process_types/custom.cc. No implementation of ReadArrayInto is\n// provided because crashreporter_annotations_t structs are singletons in a\n// module and are never present in arrays, so the functionality is unnecessary.\n\n#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \\\n    !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY)\n\nPROCESS_TYPE_STRUCT_BEGIN(crashreporter_annotations_t)\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, version)  // unsigned long\n  PROCESS_TYPE_STRUCT_VERSIONED(crashreporter_annotations_t, version)\n\n  // Version 4 (OS X 10.7)\n  //\n  // From 10.9\n  // https://github.com/apple-oss-distributions/cups/blame/cups-372/cups/backend/usb-darwin.c.\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, message)  // char*\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, signature_string)  // char*\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, backtrace)  // char*\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, message2)  // char*\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, thread)\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, dialog_mode)  // unsigned int\n\n  // Version 5 (OS X 10.11)\n  //\n  // Empirically, this is 8 bytes longer than a version 4 structure. It being a\n  // single field named abort_cause is gleaned from\n  // https://github.com/llvm/llvm-project/commit/8c345dcb9b1d3a5b0f8b6a81c7c8531b435ff3e2.\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, abort_cause)\n\n  // Version 7 (macOS 26)\n  //\n  // Empirically, the version 7 structure is 328 bytes long. The exact meaning\n  // of the extended structure is unknown.\n  //\n  // TODO: Look to a future version of llvm’s\n  // llvm/lib/Support/PrettyStackTrace.cpp or\n  // https://github.com/WebKit/WebKit/blob/main/Source/WTF/wtf/spi/cocoa/CrashReporterClientSPI.h\n  // to aid in understanding. However, this may not be fruitful:\n  // https://github.com/llvm/llvm-project/commit/0b7cbd23a043ea4c14bd13ccd737049d38f64b5d\n  // (https://github.com/llvm/llvm-project/pull/123978) indicates that\n  // `CRASHREPORTER_ANNOTATIONS_INITIALIZER` may be used as the initializer,\n  // which would provide no additional information about the arrangement of\n  // structure members without <CrashReporterClient.h>.\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, unknown_0, [33])\nPROCESS_TYPE_STRUCT_END(crashreporter_annotations_t)\n\n#endif  // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO &&\n        // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY\n"
  },
  {
    "path": "snapshot/mac/process_types/custom.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stddef.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <iterator>\n#include <limits>\n#include <type_traits>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_math.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"snapshot/mac/process_types.h\"\n#include \"snapshot/mac/process_types/internal.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/process/process_memory_mac.h\"\n\n#if !DOXYGEN\n\nnamespace crashpad {\nnamespace process_types {\nnamespace internal {\n\nnamespace {\n\ntemplate <typename T>\nbool ReadIntoAndZero(const ProcessMemoryMac* process_memory,\n                     mach_vm_address_t address,\n                     mach_vm_size_t size,\n                     T* specific) {\n  DCHECK_LE(size, sizeof(*specific));\n\n  if (!process_memory->Read(address, size, specific)) {\n    return false;\n  }\n\n  // Zero out the rest of the structure in case anything accesses fields without\n  // checking the version or size.\n  const size_t remaining = sizeof(*specific) - size;\n  if (remaining > 0) {\n    char* const start = reinterpret_cast<char*>(specific) + size;\n    memset(start, 0, remaining);\n  }\n\n  return true;\n}\n\ntemplate <typename T>\nbool FieldAddressIfInRange(mach_vm_address_t address,\n                           size_t offset,\n                           mach_vm_address_t* field_address) {\n  base::CheckedNumeric<typename T::Pointer> checked_field_address(address);\n  checked_field_address += offset;\n  typename T::Pointer local_field_address;\n  if (!checked_field_address.AssignIfValid(&local_field_address)) {\n    LOG(ERROR) << base::StringPrintf(\n        \"address 0x%llx + offset 0x%zx out of range\", address, offset);\n    return false;\n  }\n\n  *field_address = local_field_address;\n  return true;\n}\n\ntemplate <typename T>\nbool ReadIntoVersioned(ProcessReaderMac* process_reader,\n                       mach_vm_address_t address,\n                       T* specific) {\n  mach_vm_address_t field_address;\n  if (!FieldAddressIfInRange<T>(\n          address, offsetof(T, version), &field_address)) {\n    return false;\n  }\n\n  const ProcessMemoryMac* process_memory = process_reader->Memory();\n  decltype(specific->version) version;\n  if (!process_memory->Read(field_address, sizeof(version), &version)) {\n    return false;\n  }\n\n  const size_t size = T::ExpectedSizeForVersion(version);\n  return ReadIntoAndZero(process_memory, address, size, specific);\n}\n\ntemplate <typename T>\nbool ReadIntoSized(ProcessReaderMac* process_reader,\n                   mach_vm_address_t address,\n                   T* specific) {\n  mach_vm_address_t field_address;\n  if (!FieldAddressIfInRange<T>(address, offsetof(T, size), &field_address)) {\n    return false;\n  }\n\n  const ProcessMemoryMac* process_memory = process_reader->Memory();\n  decltype(specific->size) size;\n  if (!process_memory->Read(address + offsetof(T, size), sizeof(size), &size)) {\n    return false;\n  }\n\n  if (size < T::MinimumSize()) {\n    LOG(ERROR) << \"small size \" << size;\n    return false;\n  }\n\n  size = std::min(static_cast<size_t>(size), sizeof(*specific));\n  return ReadIntoAndZero(process_memory, address, size, specific);\n}\n\n}  // namespace\n\n// static\ntemplate <typename Traits>\nsize_t dyld_all_image_infos<Traits>::ExpectedSizeForVersion(\n    decltype(dyld_all_image_infos<Traits>::version) version) {\n  static constexpr size_t kSizeForVersion[] = {\n      offsetof(dyld_all_image_infos<Traits>, infoArrayCount),  // 0\n      offsetof(dyld_all_image_infos<Traits>, libSystemInitialized),  // 1\n      offsetof(dyld_all_image_infos<Traits>, jitInfo),  // 2\n      offsetof(dyld_all_image_infos<Traits>, dyldVersion),  // 3\n      offsetof(dyld_all_image_infos<Traits>, dyldVersion),  // 4\n      offsetof(dyld_all_image_infos<Traits>, coreSymbolicationShmPage),  // 5\n      offsetof(dyld_all_image_infos<Traits>, systemOrderFlag),  // 6\n      offsetof(dyld_all_image_infos<Traits>, uuidArrayCount),  // 7\n      offsetof(dyld_all_image_infos<Traits>, dyldAllImageInfosAddress),  // 8\n      offsetof(dyld_all_image_infos<Traits>, initialImageCount),  // 9\n      offsetof(dyld_all_image_infos<Traits>, errorKind),  // 10\n      offsetof(dyld_all_image_infos<Traits>, sharedCacheSlide),  // 11\n      offsetof(dyld_all_image_infos<Traits>, sharedCacheUUID),  // 12\n      offsetof(dyld_all_image_infos<Traits>, infoArrayChangeTimestamp),  // 13\n      offsetof(dyld_all_image_infos<Traits>, end_v14),  // 14\n      std::numeric_limits<size_t>::max(),  // 15, see below\n      offsetof(dyld_all_image_infos<Traits>, end_v16),  // 16\n      sizeof(dyld_all_image_infos<Traits>),  // 17\n      sizeof(dyld_all_image_infos<Traits>),  // 18\n  };\n\n  if (version >= std::size(kSizeForVersion)) {\n    return kSizeForVersion[std::size(kSizeForVersion) - 1];\n  }\n\n  static_assert(std::is_unsigned<decltype(version)>::value,\n                \"version must be unsigned\");\n\n  if (version == 15) {\n    // Disambiguate between the two different layouts for version 15. The\n    // original one introduced in macOS 10.12 had the same size as version 14.\n    // The revised one in macOS 10.13 grew. It’s safe to assume that the\n    // dyld_all_image_infos structure came from the same system that’s now\n    // interpreting it, so use an OS version check.\n    const int macos_version_number = MacOSVersionNumber();\n    if (macos_version_number / 1'00 == 10'12) {\n      return offsetof(dyld_all_image_infos<Traits>, end_v14);\n    }\n\n    DCHECK_GE(macos_version_number, 10'13'00);\n    DCHECK_LT(macos_version_number, 10'15'00);\n    return offsetof(dyld_all_image_infos<Traits>, platform);\n  }\n\n  size_t size = kSizeForVersion[version];\n  DCHECK_NE(size, std::numeric_limits<size_t>::max());\n\n  return size;\n}\n\n// static\ntemplate <typename Traits>\nbool dyld_all_image_infos<Traits>::ReadInto(\n    ProcessReaderMac* process_reader,\n    mach_vm_address_t address,\n    dyld_all_image_infos<Traits>* specific) {\n  return ReadIntoVersioned(process_reader, address, specific);\n}\n\n// static\ntemplate <typename Traits>\nsize_t crashreporter_annotations_t<Traits>::ExpectedSizeForVersion(\n    decltype(crashreporter_annotations_t<Traits>::version) version) {\n  if (version >= 7) {\n    return sizeof(crashreporter_annotations_t<Traits>);\n  }\n  if (version >= 5) {\n    return offsetof(crashreporter_annotations_t<Traits>, unknown_0);\n  }\n  if (version >= 4) {\n    return offsetof(crashreporter_annotations_t<Traits>, abort_cause);\n  }\n  return offsetof(crashreporter_annotations_t<Traits>, message);\n}\n\n// static\ntemplate <typename Traits>\nbool crashreporter_annotations_t<Traits>::ReadInto(\n    ProcessReaderMac* process_reader,\n    mach_vm_address_t address,\n    crashreporter_annotations_t<Traits>* specific) {\n  return ReadIntoVersioned(process_reader, address, specific);\n}\n\n// static\ntemplate <typename Traits>\nbool CrashpadInfo<Traits>::ReadInto(ProcessReaderMac* process_reader,\n                                    mach_vm_address_t address,\n                                    CrashpadInfo<Traits>* specific) {\n  return ReadIntoSized(process_reader, address, specific);\n}\n\n// Explicit template instantiation of the above.\n#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits)                             \\\n  template size_t                                                       \\\n  dyld_all_image_infos<Traits##lp_bits>::ExpectedSizeForVersion(        \\\n      decltype(dyld_all_image_infos<Traits##lp_bits>::version));        \\\n  template bool dyld_all_image_infos<Traits##lp_bits>::ReadInto(        \\\n      ProcessReaderMac*,                                                \\\n      mach_vm_address_t,                                                \\\n      dyld_all_image_infos<Traits##lp_bits>*);                          \\\n  template size_t                                                       \\\n  crashreporter_annotations_t<Traits##lp_bits>::ExpectedSizeForVersion( \\\n      decltype(crashreporter_annotations_t<Traits##lp_bits>::version)); \\\n  template bool crashreporter_annotations_t<Traits##lp_bits>::ReadInto( \\\n      ProcessReaderMac*,                                                \\\n      mach_vm_address_t,                                                \\\n      crashreporter_annotations_t<Traits##lp_bits>*);                   \\\n  template bool CrashpadInfo<Traits##lp_bits>::ReadInto(                \\\n      ProcessReaderMac*, mach_vm_address_t, CrashpadInfo<Traits##lp_bits>*);\n\n#include \"snapshot/mac/process_types/flavors.h\"\n\n#undef PROCESS_TYPE_FLAVOR_TRAITS\n\n}  // namespace internal\n}  // namespace process_types\n}  // namespace crashpad\n\n#endif  // !DOXYGEN\n"
  },
  {
    "path": "snapshot/mac/process_types/dyld_images.proctype",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This file corresponds to the system’s <mach-o/dyld_images.h>.\n//\n// This file is intended to be included multiple times in the same translation\n// unit, so #include guards are intentionally absent.\n//\n// This file is included by snapshot/mac/process_types.h and\n// snapshot/mac/process_types.cc to produce process type struct definitions and\n// accessors.\n\nPROCESS_TYPE_STRUCT_BEGIN(dyld_image_info)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageLoadAddress)  // const mach_header*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageFilePath)  // const char*\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, imageFileModDate)\nPROCESS_TYPE_STRUCT_END(dyld_image_info)\n\nPROCESS_TYPE_STRUCT_BEGIN(dyld_uuid_info)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageLoadAddress)  // const mach_header*\n  PROCESS_TYPE_STRUCT_MEMBER(uuid_t, imageUUID)\nPROCESS_TYPE_STRUCT_END(dyld_uuid_info)\n\n// dyld_all_image_infos is variable-length. Its length dictated by its |version|\n// field which is always present. A custom implementation of the flavored\n// ReadSpecificInto function that understands how to map this field to the\n// structure’s actual size is provided in snapshot/mac/process_types/custom.cc.\n// No implementation of ReadArrayInto is provided because dyld_all_image_infos\n// structs are singletons in a process and are never present in arrays, so the\n// functionality is unnecessary.\n\n#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \\\n    !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY)\n\nPROCESS_TYPE_STRUCT_BEGIN(dyld_all_image_infos)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version)\n  PROCESS_TYPE_STRUCT_VERSIONED(dyld_all_image_infos, version)\n\n  // Version 1 (Mac OS X 10.4)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, infoArrayCount)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, infoArray)  // const dyld_image_info*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, notification)  // function pointer\n  PROCESS_TYPE_STRUCT_MEMBER(bool, processDetachedFromSharedRegion)\n\n  // Version 2 (Mac OS X 10.6)\n  PROCESS_TYPE_STRUCT_MEMBER(bool, libSystemInitialized)\n\n  // This field does not appear in the system’s structure definition but is\n  // necessary to ensure that when building in 32-bit mode, the 64-bit version\n  // of the process_types structure matches the genuine 64-bit structure. This\n  // is required because the alignment constraints on 64-bit types are more\n  // stringent in 64-bit mode.\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved32_64Only, alignment)\n\n  // const mach_header*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldImageLoadAddress)\n\n  // Version 3 (Mac OS X 10.6)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, jitInfo)  // void*\n\n  // Version 5 (Mac OS X 10.6)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldVersion)  // const char*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorMessage)  // const char*\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, terminationFlags)\n\n  // Version 6 (Mac OS X 10.6)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, coreSymbolicationShmPage)  // void*\n\n  // Version 7 (Mac OS X 10.6)\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, systemOrderFlag)\n\n  // Version 8 (OS X 10.7)\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, uuidArrayCount)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, uuidArray)  // const dyld_uuid_info*\n\n  // Version 9 (OS X 10.7)\n  // dyld_all_image_infos*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldAllImageInfosAddress)\n\n  // Version 10 (OS X 10.7)\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, initialImageCount)\n\n  // Version 11 (OS X 10.7)\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, errorKind)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorClientOfDylibPath)  // const char*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorTargetDylibPath)  // const char*\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorSymbol)  // const char*\n\n  // Version 12 (OS X 10.7)\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, sharedCacheSlide)\n\n  // Version 13 (OS X 10.9)\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, sharedCacheUUID, [16])\n\n  // Version 15 (macOS 10.12)\n  // This space is also allocated in version 14 (OS X 10.9) as part of the\n  // “reserved” member.\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, sharedCacheBaseAddress)\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, infoArrayChangeTimestamp)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldPath)  // const char*\n\n  // These should be considered mach_port_name_t when interacting with them from\n  // another Mach IPC namespace (process).\n  PROCESS_TYPE_STRUCT_MEMBER(mach_port_t, notifyPorts, [8])\n\n  // Version 14 (OS X 10.9)\n  // As of the 10.12 SDK, this is declared as reserved[9] for 64-bit platforms\n  // and reserved[4] for 32-bit platforms. It was expanded to reserved[5] for\n  // 32-bit platforms in the 10.13 SDK to provide proper padding, but because\n  // the runtimes that use versions 14 and 15 were built with SDKs that did not\n  // have this extra padding, it’s necessary to treat the element at index 4 on\n  // 32-bit systems as outside of the version 14 and 15 structure. This is why\n  // |reserved| is only declared a 4-element array, with a special end_v14\n  // member (not present in the native definition) available to indicate the end\n  // of the native version 14 structure and the 10.12 version 15 structure,\n  // preceding the padding in the 32-bit structure that would natively be\n  // addressed at index 4 of |reserved|. Treat reserved_4_32 as only available\n  // in version 16 of the structure.\n  // In the 12.0 SDK, 2 of the trailing UIntPtrs on 64-bit and\n  // 4 of them on 32-bit were replaced by two uint64_ts. On 32-bit, that\n  // awkwardly straddles end_v14. Since macOS 12.0 is 64-bit only, the proctype\n  // version of this struct only has these uint64_ts in the 64-bit version.\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, reserved, [4])\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_4_64)\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_5)\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_6)\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, shared_cache_fs_id)\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, shared_cache_fs_obj_id)\n  PROCESS_TYPE_STRUCT_MEMBER(Nothing, end_v14)\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved32_32Only, reserved_4_32)\n\n  // Version 15 (macOS 10.13)\n  // <mach-o/dyld_images.h> incorrectly claims that these were introduced at\n  // version 16. These fields are not present in macOS 10.12, which also\n  // identifies its structure as version 15.\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, compact_dyld_image_info_addr)\n  PROCESS_TYPE_STRUCT_MEMBER(ULong, compact_dyld_image_info_size)  // size_t\n\n  // Version 16 (macOS 10.15)\n  // The native structure is followed by 4 bytes of padding, marked by the\n  // end_v16 member later, not present in the native version of the structure.\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, platform)  // dyld_platform_t\n\n  // Version 17 (macOS 10.16/11.0)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, aotInfoCount)\n  PROCESS_TYPE_STRUCT_MEMBER(Nothing, end_v16)\n  PROCESS_TYPE_STRUCT_MEMBER(Pointer, aotInfoArray)  // dyld_aot_image_info*\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, aotInfoArrayChangeTimestamp)\n  PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, aotSharedCacheBaseAddress)\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, aotSharedCacheUUID, [16])\nPROCESS_TYPE_STRUCT_END(dyld_all_image_infos)\n\n#endif  // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO &&\n        // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY\n"
  },
  {
    "path": "snapshot/mac/process_types/flavors.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This file is intended to be included multiple times in the same translation\n// unit, so #include guards are intentionally absent.\n//\n// This file is included by snapshot/mac/process_types/internal.h to produce\n// process type flavor traits class declarations and by\n// snapshot/mac/process_types/custom.cc to provide explicit instantiation of\n// flavored implementations.\n\nPROCESS_TYPE_FLAVOR_TRAITS(32)\nPROCESS_TYPE_FLAVOR_TRAITS(64)\n"
  },
  {
    "path": "snapshot/mac/process_types/internal.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_\n#define CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_\n\n#include \"snapshot/mac/process_types.h\"\n\n// Declare Traits32 and Traits64, flavor-specific traits classes. These are\n// private traits classes not for use outside of process type internals.\n// TraitsGeneric is declared in snapshot/mac/process_types.h.\n\n#include \"snapshot/mac/process_types/traits.h\"\n\n#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \\\n  DECLARE_PROCESS_TYPE_TRAITS_CLASS(        \\\n      lp_bits, lp_bits, __attribute__((aligned(lp_bits / 8))))\n\n#include \"snapshot/mac/process_types/flavors.h\"\n\n#undef PROCESS_TYPE_FLAVOR_TRAITS\n\n#undef DECLARE_PROCESS_TYPE_TRAITS_CLASS\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_\n"
  },
  {
    "path": "snapshot/mac/process_types/loader.proctype",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This file corresponds to the system’s <mach-o/loader.h>.\n//\n// This file is intended to be included multiple times in the same translation\n// unit, so #include guards are intentionally absent.\n//\n// This file is included by snapshot/mac/process_types.h and\n// snapshot/mac/process_types.cc to produce process type struct definitions and\n// accessors.\n\nPROCESS_TYPE_STRUCT_BEGIN(mach_header)  // 64-bit: mach_header_64\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, magic)\n\n  // cputype is really cpu_type_t, a typedef for integer_t, itself a typedef for\n  // int. It is currently reasonable to assume that int is int32_t.\n  PROCESS_TYPE_STRUCT_MEMBER(int32_t, cputype)\n\n  // cpusubtype is really cpu_subtype_t, a typedef for integer_t, itself a\n  // typedef for int. It is currently reasonable to assume that int is int32_t.\n  PROCESS_TYPE_STRUCT_MEMBER(int32_t, cpusubtype)\n\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, filetype)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ncmds)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, sizeofcmds)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags)\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved32_64Only, reserved)\nPROCESS_TYPE_STRUCT_END(mach_header)\n\nPROCESS_TYPE_STRUCT_BEGIN(load_command)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)\nPROCESS_TYPE_STRUCT_END(load_command)\n\nPROCESS_TYPE_STRUCT_BEGIN(segment_command)  // 64-bit: segment_command_64\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)\n\n  // This string is not necessarily NUL-terminated.\n  PROCESS_TYPE_STRUCT_MEMBER(char, segname, [16])\n\n  PROCESS_TYPE_STRUCT_MEMBER(ULong, vmaddr)\n  PROCESS_TYPE_STRUCT_MEMBER(ULong, vmsize)\n  PROCESS_TYPE_STRUCT_MEMBER(ULong, fileoff)\n  PROCESS_TYPE_STRUCT_MEMBER(ULong, filesize)\n  PROCESS_TYPE_STRUCT_MEMBER(vm_prot_t, maxprot)\n  PROCESS_TYPE_STRUCT_MEMBER(vm_prot_t, initprot)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nsects)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags)\nPROCESS_TYPE_STRUCT_END(segment_command)\n\nPROCESS_TYPE_STRUCT_BEGIN(dylib_command)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)\n\n  // The following come from the dylib struct member of dylib_command.\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_name)  // lc_str\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_timestamp)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_current_version)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_compatibility_version)\nPROCESS_TYPE_STRUCT_END(dylib_command)\n\nPROCESS_TYPE_STRUCT_BEGIN(dylinker_command)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, name)  // lc_str\nPROCESS_TYPE_STRUCT_END(dylinker_command)\n\nPROCESS_TYPE_STRUCT_BEGIN(symtab_command)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, symoff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nsyms)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, stroff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, strsize)\nPROCESS_TYPE_STRUCT_END(symtab_command)\n\nPROCESS_TYPE_STRUCT_BEGIN(dysymtab_command)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ilocalsym)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nlocalsym)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, iextdefsym)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextdefsym)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, iundefsym)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nundefsym)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, tocoff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ntoc)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, modtaboff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nmodtab)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, extrefsymoff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextrefsyms)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, indirectsymoff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nindirectsyms)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, extreloff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextrel)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, locreloff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nlocrel)\nPROCESS_TYPE_STRUCT_END(dysymtab_command)\n\nPROCESS_TYPE_STRUCT_BEGIN(uuid_command)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, uuid, [16])\nPROCESS_TYPE_STRUCT_END(uuid_command)\n\nPROCESS_TYPE_STRUCT_BEGIN(source_version_command)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize)\n  PROCESS_TYPE_STRUCT_MEMBER(uint64_t, version)\nPROCESS_TYPE_STRUCT_END(source_version_command)\n\nPROCESS_TYPE_STRUCT_BEGIN(section)  // 64-bit: section_64\n  // These strings are not necessarily NUL-terminated.\n  PROCESS_TYPE_STRUCT_MEMBER(char, sectname, [16])\n  PROCESS_TYPE_STRUCT_MEMBER(char, segname, [16])\n\n  PROCESS_TYPE_STRUCT_MEMBER(ULong, addr)\n  PROCESS_TYPE_STRUCT_MEMBER(ULong, size)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, offset)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, align)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reloff)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nreloc)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reserved1)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reserved2)\n  PROCESS_TYPE_STRUCT_MEMBER(Reserved32_64Only, reserved3)\nPROCESS_TYPE_STRUCT_END(section)\n"
  },
  {
    "path": "snapshot/mac/process_types/nlist.proctype",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This file corresponds to the system’s <mach-o/nlist.h>.\n//\n// This file is intended to be included multiple times in the same translation\n// unit, so #include guards are intentionally absent.\n//\n// This file is included by snapshot/mac/process_types.h and\n// snapshot/mac/process_types.cc to produce process type struct definitions and\n// accessors.\n\nPROCESS_TYPE_STRUCT_BEGIN(nlist)\n  PROCESS_TYPE_STRUCT_MEMBER(uint32_t, n_strx)  // n_un.n_strx\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, n_type)\n  PROCESS_TYPE_STRUCT_MEMBER(uint8_t, n_sect)\n  PROCESS_TYPE_STRUCT_MEMBER(uint16_t, n_desc)  // 32-bit: int16_t\n  PROCESS_TYPE_STRUCT_MEMBER(ULong, n_value)\nPROCESS_TYPE_STRUCT_END(nlist)\n"
  },
  {
    "path": "snapshot/mac/process_types/traits.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This file is intended to be included multiple times in the same translation\n// unit, so #include guards are intentionally absent.\n//\n// This file is included by snapshot/mac/process_types.h and\n// snapshot/mac/process_types/internal.h to produce traits class definitions.\n\n// Things that #include this file should #undef\n// DECLARE_PROCESS_TYPE_TRAITS_CLASS before #including this file again and after\n// the last #include of this file.\n//\n// |Reserved*| is used for padding fields that may be zero-length, and |Nothing|\n// is always zero-length and is used solely as an anchor to compute offsets.\n// __VA_ARGS__, which is intended to set the alignment of the 64-bit types, is\n// not used for those type aliases.\n#define DECLARE_PROCESS_TYPE_TRAITS_CLASS(traits_name, lp_bits, ...) \\\n  namespace crashpad {                                               \\\n  namespace process_types {                                          \\\n  namespace internal {                                               \\\n  struct Traits##traits_name {                                       \\\n    using Long = int##lp_bits##_t __VA_ARGS__;                       \\\n    using ULong = uint##lp_bits##_t __VA_ARGS__;                     \\\n    using Pointer = uint##lp_bits##_t __VA_ARGS__;                   \\\n    using IntPtr = int##lp_bits##_t __VA_ARGS__;                     \\\n    using UIntPtr = uint##lp_bits##_t __VA_ARGS__;                   \\\n    using Reserved32_32Only = Reserved32_32Only##lp_bits;            \\\n    using Reserved32_64Only = Reserved32_64Only##lp_bits;            \\\n    using Reserved64_64Only = Reserved64_64Only##lp_bits;            \\\n    using Nothing = Nothing;                                         \\\n  };                                                                 \\\n  }                                                                  \\\n  }                                                                  \\\n  }\n"
  },
  {
    "path": "snapshot/mac/process_types.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/process_types.h\"\n\n#include <stddef.h>\n#include <string.h>\n#include <uuid/uuid.h>\n\n#include <iterator>\n\n#include \"base/containers/heap_array.h\"\n#include \"snapshot/mac/process_types/internal.h\"\n#include \"util/process/process_memory_mac.h\"\n\nnamespace crashpad {\nnamespace {\n\n// Assign() is used by each flavor's ReadInto implementation to copy data from a\n// specific struct to a generic struct. For fundamental types, the assignment\n// operator suffices. For other types such as arrays, an explicit Assign\n// specialization is needed, typically performing a copy.\n\ntemplate <typename DestinationType, typename SourceType>\ninline void Assign(DestinationType* destination, const SourceType& source) {\n  *destination = source;\n}\n\ntemplate <typename Type>\ninline void Assign(Type* destination, const Type& source) {\n  memcpy(destination, &source, sizeof(source));\n}\n\ntemplate <>\ninline void Assign<process_types::internal::Reserved32_32Only64,\n                   process_types::internal::Reserved32_32Only32>(\n    process_types::internal::Reserved32_32Only64* destination,\n    const process_types::internal::Reserved32_32Only32& source) {\n  // Reserved32_32Only32 carries no data and has no storage in the 64-bit\n  // structure.\n}\n\ntemplate <>\ninline void Assign<process_types::internal::Reserved32_64Only64,\n                   process_types::internal::Reserved32_64Only32>(\n    process_types::internal::Reserved32_64Only64* destination,\n    const process_types::internal::Reserved32_64Only32& source) {\n  // Reserved32_64Only32 carries no data.\n  *destination = 0;\n}\n\ntemplate <>\ninline void Assign<process_types::internal::Reserved64_64Only64,\n                   process_types::internal::Reserved64_64Only32>(\n    process_types::internal::Reserved64_64Only64* destination,\n    const process_types::internal::Reserved64_64Only32& source) {\n  // Reserved64_64Only32 carries no data.\n  *destination = 0;\n}\n\nusing UInt32Array4 = uint32_t[4];\nusing UInt64Array4 = uint64_t[4];\ntemplate <>\ninline void Assign<UInt64Array4, UInt32Array4>(UInt64Array4* destination,\n                                               const UInt32Array4& source) {\n  for (size_t index = 0; index < std::size(source); ++index) {\n    (*destination)[index] = source[index];\n  }\n}\n\n}  // namespace\n}  // namespace crashpad\n\n// Implement the generic crashpad::process_types::struct_name ReadInto(), which\n// delegates to the templatized ReadIntoInternal(), which reads the specific\n// struct type from the remote process and genericizes it. Also implement\n// crashpad::process_types::internal::struct_name<> GenericizeInto(), which\n// operates on each member in the struct.\n#define PROCESS_TYPE_STRUCT_IMPLEMENT 1\n\n#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)                            \\\n  namespace crashpad {                                                    \\\n  namespace process_types {                                               \\\n                                                                          \\\n  /* static */                                                            \\\n  size_t struct_name::ExpectedSize(ProcessReaderMac* process_reader) {    \\\n    if (!process_reader->Is64Bit()) {                                     \\\n      return internal::struct_name<internal::Traits32>::Size();           \\\n    } else {                                                              \\\n      return internal::struct_name<internal::Traits64>::Size();           \\\n    }                                                                     \\\n  }                                                                       \\\n                                                                          \\\n  /* static */                                                            \\\n  bool struct_name::ReadInto(ProcessReaderMac* process_reader,            \\\n                             mach_vm_address_t address,                   \\\n                             struct_name* generic) {                      \\\n    if (!process_reader->Is64Bit()) {                                     \\\n      return ReadIntoInternal<internal::struct_name<internal::Traits32>>( \\\n          process_reader, address, generic);                              \\\n    } else {                                                              \\\n      return ReadIntoInternal<internal::struct_name<internal::Traits64>>( \\\n          process_reader, address, generic);                              \\\n    }                                                                     \\\n  }                                                                       \\\n                                                                          \\\n  /* static */                                                            \\\n  template <typename T>                                                   \\\n  bool struct_name::ReadIntoInternal(ProcessReaderMac* process_reader,    \\\n                                     mach_vm_address_t address,           \\\n                                     struct_name* generic) {              \\\n    T specific;                                                           \\\n    if (!specific.Read(process_reader, address)) {                        \\\n      return false;                                                       \\\n    }                                                                     \\\n    specific.GenericizeInto(generic, &generic->size_);                    \\\n    return true;                                                          \\\n  }                                                                       \\\n                                                                          \\\n  namespace internal {                                                    \\\n                                                                          \\\n  template <typename Traits>                                              \\\n  void struct_name<Traits>::GenericizeInto(                               \\\n      process_types::struct_name* generic,                                \\\n      size_t* specific_size) {                                            \\\n    *specific_size = Size();\n\n#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \\\n    Assign(&generic->member_name, member_name);\n\n#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)\n\n#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)\n\n#define PROCESS_TYPE_STRUCT_END(struct_name) \\\n  }                                          \\\n  }  /* namespace internal */                \\\n  }  /* namespace process_types */           \\\n  }  /* namespace crashpad */\n\n#include \"snapshot/mac/process_types/all.proctype\"\n\n#undef PROCESS_TYPE_STRUCT_BEGIN\n#undef PROCESS_TYPE_STRUCT_MEMBER\n#undef PROCESS_TYPE_STRUCT_VERSIONED\n#undef PROCESS_TYPE_STRUCT_SIZED\n#undef PROCESS_TYPE_STRUCT_END\n#undef PROCESS_TYPE_STRUCT_IMPLEMENT\n\n// Implement the specific crashpad::process_types::internal::struct_name<>\n// ReadInto(). The default implementation simply reads the struct from the\n// remote process. This is separated from other method implementations because\n// some types may wish to provide custom readers. This can be done by guarding\n// such types’ proctype definitions against this macro and providing custom\n// implementations in snapshot/mac/process_types/custom.cc.\n#define PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO 1\n\n#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)                         \\\n  namespace crashpad {                                                 \\\n  namespace process_types {                                            \\\n  namespace internal {                                                 \\\n                                                                       \\\n  /* static */                                                         \\\n  template <typename Traits>                                           \\\n  bool struct_name<Traits>::ReadInto(ProcessReaderMac* process_reader, \\\n                                     mach_vm_address_t address,        \\\n                                     struct_name<Traits>* specific) {  \\\n    return process_reader->Memory()->Read(                             \\\n        address, sizeof(*specific), specific);                         \\\n  }                                                                    \\\n  } /* namespace internal */                                           \\\n  } /* namespace process_types */                                      \\\n  } /* namespace crashpad */\n\n#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)\n\n#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)\n\n#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)\n\n#define PROCESS_TYPE_STRUCT_END(struct_name)\n\n#include \"snapshot/mac/process_types/all.proctype\"\n\n#undef PROCESS_TYPE_STRUCT_BEGIN\n#undef PROCESS_TYPE_STRUCT_MEMBER\n#undef PROCESS_TYPE_STRUCT_VERSIONED\n#undef PROCESS_TYPE_STRUCT_SIZED\n#undef PROCESS_TYPE_STRUCT_END\n#undef PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO\n\n// Implement the array operations. These are separated from other method\n// implementations because some types are variable-length and are never stored\n// as direct-access arrays. It would be incorrect to provide reader\n// implementations for such types. Types that wish to suppress array operations\n// can do so by guarding their proctype definitions against this macro.\n#define PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY 1\n\n#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)                                 \\\n  namespace crashpad {                                                         \\\n  namespace process_types {                                                    \\\n  namespace internal {                                                         \\\n                                                                               \\\n  /* static */                                                                 \\\n  template <typename Traits>                                                   \\\n  bool struct_name<Traits>::ReadArrayInto(ProcessReaderMac* process_reader,    \\\n                                          mach_vm_address_t address,           \\\n                                          size_t count,                        \\\n                                          struct_name<Traits>* specific) {     \\\n    return process_reader->Memory()->Read(                                     \\\n        address, count * sizeof(struct_name<Traits>), specific);               \\\n  }                                                                            \\\n                                                                               \\\n  } /* namespace internal */                                                   \\\n                                                                               \\\n  /* static */                                                                 \\\n  bool struct_name::ReadArrayInto(ProcessReaderMac* process_reader,            \\\n                                  mach_vm_address_t address,                   \\\n                                  size_t count,                                \\\n                                  struct_name* generic) {                      \\\n    if (!process_reader->Is64Bit()) {                                          \\\n      return ReadArrayIntoInternal<internal::struct_name<internal::Traits32>>( \\\n          process_reader, address, count, generic);                            \\\n    } else {                                                                   \\\n      return ReadArrayIntoInternal<internal::struct_name<internal::Traits64>>( \\\n          process_reader, address, count, generic);                            \\\n    }                                                                          \\\n    return true;                                                               \\\n  }                                                                            \\\n                                                                               \\\n  /* static */                                                                 \\\n  template <typename T>                                                        \\\n  bool struct_name::ReadArrayIntoInternal(ProcessReaderMac* process_reader,    \\\n                                          mach_vm_address_t address,           \\\n                                          size_t count,                        \\\n                                          struct_name* generic) {              \\\n    auto specific = base::HeapArray<T>::Uninit(count);                         \\\n    if (!T::ReadArrayInto(                                                     \\\n            process_reader, address, specific.size(), specific.data())) {      \\\n      return false;                                                            \\\n    }                                                                          \\\n    for (size_t index = 0; index < count; ++index) {                           \\\n      specific[index].GenericizeInto(&generic[index], &generic[index].size_);  \\\n    }                                                                          \\\n    return true;                                                               \\\n  }                                                                            \\\n  } /* namespace process_types */                                              \\\n  } /* namespace crashpad */\n\n#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)\n\n#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)\n\n#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)\n\n#define PROCESS_TYPE_STRUCT_END(struct_name)\n\n#include \"snapshot/mac/process_types/all.proctype\"\n\n#undef PROCESS_TYPE_STRUCT_BEGIN\n#undef PROCESS_TYPE_STRUCT_MEMBER\n#undef PROCESS_TYPE_STRUCT_VERSIONED\n#undef PROCESS_TYPE_STRUCT_SIZED\n#undef PROCESS_TYPE_STRUCT_END\n#undef PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY\n\n// Implement the generic crashpad::process_types::struct_name\n// ExpectedSizeForVersion(), which delegates to the templatized\n// ExpectedSizeForVersion(), which returns the expected size of a versioned\n// structure given a version parameter. This is only implemented for structures\n// that use PROCESS_TYPE_STRUCT_VERSIONED(), and implementations of the internal\n// templatized functions must be provided in\n// snapshot/mac/process_types/custom.cc.\n#define PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED 1\n\n#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)\n\n#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)\n\n#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \\\n  namespace crashpad {                                            \\\n  namespace process_types {                                       \\\n                                                                  \\\n  /* static */                                                    \\\n  size_t struct_name::ExpectedSizeForVersion(                     \\\n      ProcessReaderMac* process_reader,                           \\\n      decltype(struct_name::version_field) version) {             \\\n    if (!process_reader->Is64Bit()) {                             \\\n      return internal::struct_name<                               \\\n          internal::Traits32>::ExpectedSizeForVersion(version);   \\\n    } else {                                                      \\\n      return internal::struct_name<                               \\\n          internal::Traits64>::ExpectedSizeForVersion(version);   \\\n    }                                                             \\\n  }                                                               \\\n                                                                  \\\n  } /* namespace process_types */                                 \\\n  } /* namespace crashpad */\n\n#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)\n\n#define PROCESS_TYPE_STRUCT_END(struct_name)\n\n#include \"snapshot/mac/process_types/all.proctype\"\n\n#undef PROCESS_TYPE_STRUCT_BEGIN\n#undef PROCESS_TYPE_STRUCT_MEMBER\n#undef PROCESS_TYPE_STRUCT_VERSIONED\n#undef PROCESS_TYPE_STRUCT_SIZED\n#undef PROCESS_TYPE_STRUCT_END\n#undef PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED\n\n// Implement the generic crashpad::process_types::struct_name MinimumSize() and\n// its templatized equivalent. The generic version delegates to the templatized\n// one, which returns the minimum size of a sized structure. This can be used to\n// ensure that enough of a sized structure is available to interpret its size\n// field. This is only implemented for structures that use\n// PROCESS_TYPE_STRUCT_SIZED().\n#define PROCESS_TYPE_STRUCT_IMPLEMENT_SIZED 1\n\n#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)\n\n#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)\n\n#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)\n\n#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)             \\\n  namespace crashpad {                                                 \\\n  namespace process_types {                                            \\\n                                                                       \\\n  namespace internal {                                                 \\\n                                                                       \\\n  /* static */                                                         \\\n  template <typename Traits>                                           \\\n  size_t struct_name<Traits>::MinimumSize() {                          \\\n    return offsetof(struct_name<Traits>, size_field) +                 \\\n           sizeof(struct_name<Traits>::size_field);                    \\\n  }                                                                    \\\n                                                                       \\\n  /* Explicit instantiations of the above. */                          \\\n  template size_t struct_name<Traits32>::MinimumSize();                \\\n  template size_t struct_name<Traits64>::MinimumSize();                \\\n                                                                       \\\n  } /* namespace internal */                                           \\\n                                                                       \\\n  /* static */                                                         \\\n  size_t struct_name::MinimumSize(ProcessReaderMac* process_reader) {  \\\n    if (!process_reader->Is64Bit()) {                                  \\\n      return internal::struct_name<internal::Traits32>::MinimumSize(); \\\n    } else {                                                           \\\n      return internal::struct_name<internal::Traits64>::MinimumSize(); \\\n    }                                                                  \\\n  }                                                                    \\\n                                                                       \\\n  } /* namespace process_types */                                      \\\n  } /* namespace crashpad */\n\n#define PROCESS_TYPE_STRUCT_END(struct_name)\n\n#include \"snapshot/mac/process_types/all.proctype\"\n\n#undef PROCESS_TYPE_STRUCT_BEGIN\n#undef PROCESS_TYPE_STRUCT_MEMBER\n#undef PROCESS_TYPE_STRUCT_VERSIONED\n#undef PROCESS_TYPE_STRUCT_SIZED\n#undef PROCESS_TYPE_STRUCT_END\n#undef PROCESS_TYPE_STRUCT_IMPLEMENT_SIZED\n"
  },
  {
    "path": "snapshot/mac/process_types.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_\n#define CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_\n\n#include <mach/mach.h>\n#include <mach-o/dyld_images.h>\n#include <mach-o/loader.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include \"snapshot/mac/process_reader_mac.h\"\n\nnamespace crashpad {\nnamespace process_types {\nnamespace internal {\n\n// Some structure definitions have tail padding when built in a 64-bit\n// environment, but will have less (or no) tail padding in a 32-bit environment.\n// These structure’s apparent sizes will differ between these two environments,\n// which is incorrect. Use this “Nothing” type to place an “end” marker after\n// all of the real members of such structures, and use offsetof(type, end) to\n// compute their actual sizes.\nusing Nothing = char[0];\n\n// Some structure definitions differ in 32-bit and 64-bit environments by having\n// additional “reserved” padding fields present only in the 64-bit environment.\n// These Reserved*_*Only* types allow the process_types system to replicate\n// these structures more precisely.\nusing Reserved32_32Only32 = uint32_t;\nusing Reserved32_32Only64 = Nothing;\nusing Reserved32_64Only32 = Nothing;\nusing Reserved32_64Only64 = uint32_t;\nusing Reserved64_64Only32 = Nothing;\nusing Reserved64_64Only64 = uint64_t;\n\n}  // namespace internal\n}  // namespace process_types\n}  // namespace crashpad\n\n#include \"snapshot/mac/process_types/traits.h\"\n\n// Creates the traits type crashpad::process_types::internal::TraitsGeneric.\nDECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64)\n\n#undef DECLARE_PROCESS_TYPE_TRAITS_CLASS\n\n// Declare the crashpad::process_types::struct_name structs. These are the\n// user-visible generic structs that callers will interact with. They read data\n// from 32-bit or 64-bit processes by using the specific internal templatized\n// structs below.\n#define PROCESS_TYPE_STRUCT_DECLARE 1\n\n#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)                                 \\\n  namespace crashpad {                                                         \\\n  namespace process_types {                                                    \\\n  struct struct_name {                                                         \\\n   public:                                                                     \\\n    using Long = internal::TraitsGeneric::Long;                                \\\n    using ULong = internal::TraitsGeneric::ULong;                              \\\n    using Pointer = internal::TraitsGeneric::Pointer;                          \\\n    using IntPtr = internal::TraitsGeneric::IntPtr;                            \\\n    using UIntPtr = internal::TraitsGeneric::UIntPtr;                          \\\n    using Reserved32_32Only = internal::TraitsGeneric::Reserved32_32Only;      \\\n    using Reserved32_64Only = internal::TraitsGeneric::Reserved32_64Only;      \\\n    using Reserved64_64Only = internal::TraitsGeneric::Reserved64_64Only;      \\\n    using Nothing = internal::TraitsGeneric::Nothing;                          \\\n                                                                               \\\n    /* Initializes an object with data read from |process_reader| at           \\\n     * |address|, properly genericized. */                                     \\\n    bool Read(ProcessReaderMac* process_reader, mach_vm_address_t address) {   \\\n      return ReadInto(process_reader, address, this);                          \\\n    }                                                                          \\\n                                                                               \\\n    /* Reads |count| objects from |process_reader| beginning at |address|, and \\\n     * genericizes the objects. The caller must provide storage for |count|    \\\n     * objects in |generic|. */                                                \\\n    static bool ReadArrayInto(ProcessReaderMac* process_reader,                \\\n                              mach_vm_address_t address,                       \\\n                              size_t count,                                    \\\n                              struct_name* generic);                           \\\n                                                                               \\\n    /* Returns the size of the object that was read. This is the size of the   \\\n     * storage in the process that the data is read from, and is not the same  \\\n     * as the size of the generic struct. For versioned and sized structures,  \\\n     * the size of the full structure is returned. */                          \\\n    size_t Size() const { return size_; }                                      \\\n                                                                               \\\n    /* Similar to Size(), but computes the expected size of a structure based  \\\n     * on the process’ bitness. This can be used prior to reading any data     \\\n     * from a process. For versioned and sized structures,                     \\\n     * ExpectedSizeForVersion() and MinimumSize() may also be useful. */       \\\n    static size_t ExpectedSize(ProcessReaderMac* process_reader);\n\n#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)              \\\n    member_type member_name __VA_ARGS__;\n\n#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)            \\\n  /* Similar to ExpectedSize(), but computes the expected size of a          \\\n   * structure based on the process’ bitness and a custom value, such as a   \\\n   * structure version number. This can be used prior to reading any data    \\\n   * from a process. */                                                      \\\n  static size_t ExpectedSizeForVersion(                                      \\\n      ProcessReaderMac* process_reader,                                      \\\n      decltype(struct_name::version_field) version);\n\n#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)                    \\\n  /* Similar to ExpectedSize(), but computes the minimum size of a            \\\n   * structure based on the process’ bitness, typically including enough of   \\\n   * a structure to contain its size field. This can be used prior to         \\\n   * reading any data from a process. */                                      \\\n  static size_t MinimumSize(ProcessReaderMac* process_reader);\n\n#define PROCESS_TYPE_STRUCT_END(struct_name)                          \\\n private:                                                             \\\n  /* The static form of Read(). Populates the struct at |generic|. */ \\\n  static bool ReadInto(ProcessReaderMac* process_reader,              \\\n                       mach_vm_address_t address,                     \\\n                       struct_name* generic);                         \\\n                                                                      \\\n  template <typename T>                                               \\\n  static bool ReadIntoInternal(ProcessReaderMac* process_reader,      \\\n                               mach_vm_address_t address,             \\\n                               struct_name* generic);                 \\\n  template <typename T>                                               \\\n  static bool ReadArrayIntoInternal(ProcessReaderMac* process_reader, \\\n                                    mach_vm_address_t address,        \\\n                                    size_t count,                     \\\n                                    struct_name* generic);            \\\n  size_t size_;                                                       \\\n  }                                                                   \\\n  ;                                                                   \\\n  } /* namespace process_types */                                     \\\n  } /* namespace crashpad */\n\n#include \"snapshot/mac/process_types/all.proctype\"\n\n#undef PROCESS_TYPE_STRUCT_BEGIN\n#undef PROCESS_TYPE_STRUCT_MEMBER\n#undef PROCESS_TYPE_STRUCT_VERSIONED\n#undef PROCESS_TYPE_STRUCT_SIZED\n#undef PROCESS_TYPE_STRUCT_END\n#undef PROCESS_TYPE_STRUCT_DECLARE\n\n// Declare the templatized crashpad::process_types::internal::struct_name<>\n// structs. These are the 32-bit and 64-bit specific structs that describe the\n// layout of objects in another process. This is repeated instead of being\n// shared with the generic declaration above because both the generic and\n// templatized specific structs need all of the struct members declared.\n//\n// GenericizeInto() translates a struct from the representation used in the\n// remote process into the generic form.\n#define PROCESS_TYPE_STRUCT_DECLARE_INTERNAL 1\n\n#define PROCESS_TYPE_STRUCT_BEGIN(struct_name)                                \\\n  namespace crashpad {                                                        \\\n  namespace process_types {                                                   \\\n  namespace internal {                                                        \\\n  template <typename Traits>                                                  \\\n  struct struct_name {                                                        \\\n   public:                                                                    \\\n    using Long = typename Traits::Long;                                       \\\n    using ULong = typename Traits::ULong;                                     \\\n    using Pointer = typename Traits::Pointer;                                 \\\n    using IntPtr = typename Traits::IntPtr;                                   \\\n    using UIntPtr = typename Traits::UIntPtr;                                 \\\n    using Reserved32_32Only = typename Traits::Reserved32_32Only;             \\\n    using Reserved32_64Only = typename Traits::Reserved32_64Only;             \\\n    using Reserved64_64Only = typename Traits::Reserved64_64Only;             \\\n    using Nothing = typename Traits::Nothing;                                 \\\n                                                                              \\\n    /* Read(), ReadArrayInto(), and Size() are as in the generic user-visible \\\n     * struct above. */                                                       \\\n    bool Read(ProcessReaderMac* process_reader, mach_vm_address_t address) {  \\\n      return ReadInto(process_reader, address, this);                         \\\n    }                                                                         \\\n    static bool ReadArrayInto(ProcessReaderMac* process_reader,               \\\n                              mach_vm_address_t address,                      \\\n                              size_t count,                                   \\\n                              struct_name<Traits>* specific);                 \\\n    static size_t Size() { return sizeof(struct_name<Traits>); }              \\\n                                                                              \\\n    /* Translates a struct from the representation used in the remote process \\\n     * into the generic form. */                                              \\\n    void GenericizeInto(process_types::struct_name* generic,                  \\\n                        size_t* specific_size);\n\n#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...)              \\\n    member_type member_name __VA_ARGS__;\n\n#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field)              \\\n    /* ExpectedSizeForVersion() is as in the generic user-visible struct       \\\n     * above. */                                                               \\\n    static size_t ExpectedSizeForVersion(                                      \\\n        decltype(struct_name::version_field) version);\n\n#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field)                     \\\n  /* MinimumSize() is as in the generic user-visible struct above. */          \\\n  static size_t MinimumSize();\n\n#define PROCESS_TYPE_STRUCT_END(struct_name)                       \\\n private:                                                          \\\n  /* ReadInto() is as in the generic user-visible struct above. */ \\\n  static bool ReadInto(ProcessReaderMac* process_reader,           \\\n                       mach_vm_address_t address,                  \\\n                       struct_name<Traits>* specific);             \\\n  }                                                                \\\n  ;                                                                \\\n  } /* namespace internal */                                       \\\n  } /* namespace process_types */                                  \\\n  } /* namespace crashpad */\n\n#include \"snapshot/mac/process_types/all.proctype\"\n\n#undef PROCESS_TYPE_STRUCT_BEGIN\n#undef PROCESS_TYPE_STRUCT_MEMBER\n#undef PROCESS_TYPE_STRUCT_VERSIONED\n#undef PROCESS_TYPE_STRUCT_SIZED\n#undef PROCESS_TYPE_STRUCT_END\n#undef PROCESS_TYPE_STRUCT_DECLARE_INTERNAL\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_\n"
  },
  {
    "path": "snapshot/mac/process_types_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/process_types.h\"\n\n#include <Availability.h>\n#include <mach/mach.h>\n#include <string.h>\n\n#include <iterator>\n#include <limits>\n#include <vector>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/mac/process_types/internal.h\"\n#include \"test/mac/dyld.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n#define TEST_STRING(process_reader, self_view, proctype_view, field)        \\\n  do {                                                                      \\\n    if (self_view->field) {                                                 \\\n      std::string proctype_string;                                          \\\n      ASSERT_TRUE(process_reader.Memory()->ReadCString(proctype_view.field, \\\n                                                       &proctype_string));  \\\n      EXPECT_EQ(proctype_string, self_view->field);                         \\\n    }                                                                       \\\n  } while (false)\n\nTEST(ProcessTypes, DyldImagesSelf) {\n  // Get the in-process view of dyld_all_image_infos, and check it for sanity.\n  const dyld_all_image_infos* self_image_infos = DyldGetAllImageInfos();\n  const int macos_version_number = MacOSVersionNumber();\n\n  if (macos_version_number >= 10'15'00) {\n    EXPECT_GE(self_image_infos->version, 16u);\n  } else if (macos_version_number >= 10'12'00) {\n    EXPECT_GE(self_image_infos->version, 15u);\n  } else if (macos_version_number >= 10'09'00) {\n    EXPECT_GE(self_image_infos->version, 13u);\n  } else if (macos_version_number >= 10'07'00) {\n    EXPECT_GE(self_image_infos->version, 8u);\n  } else if (macos_version_number >= 10'06'00) {\n    EXPECT_GE(self_image_infos->version, 2u);\n  } else {\n    EXPECT_GE(self_image_infos->version, 1u);\n  }\n\n  EXPECT_GT(self_image_infos->infoArrayCount, 1u);\n  if (self_image_infos->version >= 2) {\n    EXPECT_TRUE(self_image_infos->libSystemInitialized);\n  }\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n  if (self_image_infos->version >= 9) {\n    EXPECT_EQ(self_image_infos->dyldAllImageInfosAddress, self_image_infos);\n  }\n#endif\n\n  // Get the out-of-process view of dyld_all_image_infos, and work with it\n  // through the process_types interface.\n  task_dyld_info_data_t dyld_info;\n  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;\n  kern_return_t kr = task_info(mach_task_self(),\n                               TASK_DYLD_INFO,\n                               reinterpret_cast<task_info_t>(&dyld_info),\n                               &count);\n  ASSERT_EQ(kr, KERN_SUCCESS);\n\n  EXPECT_EQ(dyld_info.all_image_info_addr,\n            FromPointerCast<mach_vm_address_t>(self_image_infos));\n  EXPECT_GT(dyld_info.all_image_info_size, 1u);\n\n  // This field is only present in the OS X 10.7 SDK (at build time) and kernel\n  // (at run time).\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n  if (macos_version_number >= 10'07'00) {\n#if !defined(ARCH_CPU_64_BITS)\n    EXPECT_EQ(dyld_info.all_image_info_format, TASK_DYLD_ALL_IMAGE_INFO_32);\n#else\n    EXPECT_EQ(dyld_info.all_image_info_format, TASK_DYLD_ALL_IMAGE_INFO_64);\n#endif\n  }\n#endif\n\n  ProcessReaderMac process_reader;\n  ASSERT_TRUE(process_reader.Initialize(mach_task_self()));\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_16\n  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 17;\n#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15\n  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 16;\n#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_12\n  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 15;\n#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_9\n  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 14;\n#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 12;\n#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_6\n  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 7;\n#else\n  constexpr uint32_t kDyldAllImageInfosVersionInSDK = 1;\n#endif\n\n  // Make sure that the size of the structure as declared in the SDK matches the\n  // size expected for the version of the structure that the SDK describes.\n  //\n  // There are two possible layouts for version 15, and the\n  // ExpectedSizeForVersion() implementation infers the correct one based on the\n  // run-time OS version, so if the SDK defines the version 15 structure, this\n  // test can only be performed if the run-time OS natively uses the same format\n  // structure as the SDK.\n  bool test_expected_size_for_version_matches_sdk_sizeof;\n#if __MAC_OS_X_VERSION_MAX_ALLOWED == __MAC_10_12\n  test_expected_size_for_version_matches_sdk_sizeof =\n      macos_version_number / 1'00 == 10'12;\n#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_13 && \\\n    __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_15\n  test_expected_size_for_version_matches_sdk_sizeof =\n      macos_version_number >= 10'13'00 && macos_version_number < 10'15'00;\n#else\n  test_expected_size_for_version_matches_sdk_sizeof = true;\n#endif\n\n  if (test_expected_size_for_version_matches_sdk_sizeof) {\n    EXPECT_EQ(process_types::dyld_all_image_infos::ExpectedSizeForVersion(\n                  &process_reader, kDyldAllImageInfosVersionInSDK),\n              sizeof(dyld_all_image_infos));\n  }\n\n  // Make sure that the computed sizes of various versions of this structure are\n  // correct at different bitnessses. Version 16 and later are unsupported on\n  // 32-bit systems due to the OS deprecating 32-bit support in macOS 10.15.\n  constexpr size_t kSpecialCase = std::numeric_limits<size_t>::max();\n  constexpr size_t kUnsupported = std::numeric_limits<size_t>::max() - 1;\n  constexpr struct {\n    uint32_t version;\n    size_t size_32;\n    size_t size_64;\n  } kVersionsAndSizes[] = {\n      {1, 17, 25},\n      {2, 24, 40},\n      {3, 28, 48},\n      {5, 40, 72},\n      {6, 44, 80},\n      {7, 48, 88},\n      {8, 56, 104},\n      {9, 60, 112},\n      {10, 64, 120},\n      {11, 80, 152},\n      {12, 84, 160},\n      {13, 104, 184},\n      {14, 164, 304},\n      {15, kSpecialCase, kSpecialCase},\n      {16, kUnsupported, 328},\n      {17, kUnsupported, 368},\n  };\n  for (size_t index = 0; index < std::size(kVersionsAndSizes); ++index) {\n    uint32_t version = kVersionsAndSizes[index].version;\n    SCOPED_TRACE(base::StringPrintf(\"index %zu, version %u\", index, version));\n\n    if (version == 15) {\n      if (macos_version_number / 1'00 == 10'12) {\n        EXPECT_EQ(process_types::internal::dyld_all_image_infos<\n                      process_types::internal::Traits32>::\n                      ExpectedSizeForVersion(version),\n                  164u);\n        EXPECT_EQ(process_types::internal::dyld_all_image_infos<\n                      process_types::internal::Traits64>::\n                      ExpectedSizeForVersion(version),\n                  304u);\n      } else if (macos_version_number >= 10'13'00 &&\n                 macos_version_number < 10'15'00) {\n        EXPECT_EQ(process_types::internal::dyld_all_image_infos<\n                      process_types::internal::Traits32>::\n                      ExpectedSizeForVersion(version),\n                  176u);\n        EXPECT_EQ(process_types::internal::dyld_all_image_infos<\n                      process_types::internal::Traits64>::\n                      ExpectedSizeForVersion(version),\n                  320u);\n      }\n\n      continue;\n    }\n\n    ASSERT_NE(kVersionsAndSizes[index].size_32, kSpecialCase);\n    ASSERT_NE(kVersionsAndSizes[index].size_64, kSpecialCase);\n\n    if (kVersionsAndSizes[index].size_32 != kUnsupported) {\n      EXPECT_EQ(process_types::internal::dyld_all_image_infos<\n                    process_types::internal::Traits32>::\n                    ExpectedSizeForVersion(version),\n                kVersionsAndSizes[index].size_32);\n    }\n    if (kVersionsAndSizes[index].size_64 != kUnsupported) {\n      EXPECT_EQ(process_types::internal::dyld_all_image_infos<\n                    process_types::internal::Traits64>::\n                    ExpectedSizeForVersion(version),\n                kVersionsAndSizes[index].size_64);\n    }\n  }\n\n  process_types::dyld_all_image_infos proctype_image_infos;\n  ASSERT_TRUE(proctype_image_infos.Read(&process_reader,\n                                        dyld_info.all_image_info_addr));\n\n  ASSERT_EQ(proctype_image_infos.version, self_image_infos->version);\n\n  if (proctype_image_infos.version >= 1) {\n    EXPECT_EQ(proctype_image_infos.infoArrayCount,\n              self_image_infos->infoArrayCount);\n    EXPECT_EQ(proctype_image_infos.infoArray,\n              reinterpret_cast<uint64_t>(self_image_infos->infoArray));\n    EXPECT_EQ(proctype_image_infos.notification,\n              reinterpret_cast<uint64_t>(self_image_infos->notification));\n    EXPECT_EQ(proctype_image_infos.processDetachedFromSharedRegion,\n              self_image_infos->processDetachedFromSharedRegion);\n  }\n  if (proctype_image_infos.version >= 2) {\n    EXPECT_EQ(proctype_image_infos.libSystemInitialized,\n              self_image_infos->libSystemInitialized);\n    EXPECT_EQ(\n        proctype_image_infos.dyldImageLoadAddress,\n        reinterpret_cast<uint64_t>(self_image_infos->dyldImageLoadAddress));\n  }\n  if (proctype_image_infos.version >= 3) {\n    EXPECT_EQ(proctype_image_infos.jitInfo,\n              reinterpret_cast<uint64_t>(self_image_infos->jitInfo));\n  }\n  if (proctype_image_infos.version >= 5) {\n    EXPECT_EQ(proctype_image_infos.dyldVersion,\n              reinterpret_cast<uint64_t>(self_image_infos->dyldVersion));\n    EXPECT_EQ(proctype_image_infos.errorMessage,\n              reinterpret_cast<uint64_t>(self_image_infos->errorMessage));\n    EXPECT_EQ(proctype_image_infos.terminationFlags,\n              implicit_cast<uint64_t>(self_image_infos->terminationFlags));\n\n    TEST_STRING(\n        process_reader, self_image_infos, proctype_image_infos, dyldVersion);\n    TEST_STRING(\n        process_reader, self_image_infos, proctype_image_infos, errorMessage);\n  }\n  if (proctype_image_infos.version >= 6) {\n    EXPECT_EQ(\n        proctype_image_infos.coreSymbolicationShmPage,\n        reinterpret_cast<uint64_t>(self_image_infos->coreSymbolicationShmPage));\n  }\n  if (proctype_image_infos.version >= 7) {\n    EXPECT_EQ(proctype_image_infos.systemOrderFlag,\n              implicit_cast<uint64_t>(self_image_infos->systemOrderFlag));\n  }\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n  if (proctype_image_infos.version >= 8) {\n    EXPECT_EQ(proctype_image_infos.uuidArrayCount,\n              implicit_cast<uint64_t>(self_image_infos->uuidArrayCount));\n  }\n  if (proctype_image_infos.version >= 9) {\n    EXPECT_EQ(\n        proctype_image_infos.dyldAllImageInfosAddress,\n        reinterpret_cast<uint64_t>(self_image_infos->dyldAllImageInfosAddress));\n  }\n  if (proctype_image_infos.version >= 10) {\n    EXPECT_EQ(proctype_image_infos.initialImageCount,\n              implicit_cast<uint64_t>(self_image_infos->initialImageCount));\n  }\n  if (proctype_image_infos.version >= 11) {\n    EXPECT_EQ(proctype_image_infos.errorKind,\n              implicit_cast<uint64_t>(self_image_infos->errorKind));\n    EXPECT_EQ(\n        proctype_image_infos.errorClientOfDylibPath,\n        reinterpret_cast<uint64_t>(self_image_infos->errorClientOfDylibPath));\n    EXPECT_EQ(\n        proctype_image_infos.errorTargetDylibPath,\n        reinterpret_cast<uint64_t>(self_image_infos->errorTargetDylibPath));\n    EXPECT_EQ(proctype_image_infos.errorSymbol,\n              reinterpret_cast<uint64_t>(self_image_infos->errorSymbol));\n\n    TEST_STRING(process_reader,\n                self_image_infos,\n                proctype_image_infos,\n                errorClientOfDylibPath);\n    TEST_STRING(process_reader,\n                self_image_infos,\n                proctype_image_infos,\n                errorTargetDylibPath);\n    TEST_STRING(\n        process_reader, self_image_infos, proctype_image_infos, errorSymbol);\n  }\n  if (proctype_image_infos.version >= 12) {\n    EXPECT_EQ(proctype_image_infos.sharedCacheSlide,\n              implicit_cast<uint64_t>(self_image_infos->sharedCacheSlide));\n  }\n#endif\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_9\n  if (proctype_image_infos.version >= 13) {\n    EXPECT_EQ(memcmp(self_image_infos->sharedCacheUUID,\n                     proctype_image_infos.sharedCacheUUID,\n                     sizeof(self_image_infos->sharedCacheUUID)),\n              0);\n  }\n#endif\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_12\n  if (proctype_image_infos.version >= 15) {\n    EXPECT_EQ(proctype_image_infos.infoArrayChangeTimestamp,\n              self_image_infos->infoArrayChangeTimestamp);\n    EXPECT_EQ(proctype_image_infos.sharedCacheBaseAddress,\n              self_image_infos->sharedCacheBaseAddress);\n    EXPECT_EQ(proctype_image_infos.dyldPath,\n              reinterpret_cast<uint64_t>(self_image_infos->dyldPath));\n    for (size_t index = 0; index < std::size(self_image_infos->notifyPorts);\n         ++index) {\n      EXPECT_EQ(proctype_image_infos.notifyPorts[index],\n                self_image_infos->notifyPorts[index])\n          << \"index \" << index;\n    }\n\n    TEST_STRING(\n        process_reader, self_image_infos, proctype_image_infos, dyldPath);\n  }\n#endif\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_12\n  // As dyld_all_image_infos has evolved over time, new fields were added to the\n  // reserved region. process_types::dyld_all_image_infos declares a recent\n  // version of the structure, but an older SDK may declare an older version\n  // whose |reserved| member appears at a different (smaller) offset than the\n  // process_types version. It’s difficult to compare the reserved fields in\n  // these older SDKs, so only do it where the declarations match.\n  if (proctype_image_infos.version >= 14) {\n    for (size_t index = 0; index < std::size(proctype_image_infos.reserved);\n         ++index) {\n      EXPECT_EQ(proctype_image_infos.reserved[index],\n                implicit_cast<uint64_t>(self_image_infos->reserved[index]))\n          << \"index \" << index;\n    }\n#if defined(ARCH_CPU_64_BITS)\n    EXPECT_EQ(proctype_image_infos.reserved_4_64,\n              self_image_infos->reserved[4]);\n    EXPECT_EQ(proctype_image_infos.reserved_5, self_image_infos->reserved[5]);\n    EXPECT_EQ(proctype_image_infos.reserved_6, self_image_infos->reserved[6]);\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_12_0\n    uint64_t shared_cache_fs_id = self_image_infos->sharedCacheFSID;\n    uint64_t shared_cache_fs_obj_id = self_image_infos->sharedCacheFSObjID;\n#else\n    uint64_t shared_cache_fs_id = self_image_infos->reserved[7];\n    uint64_t shared_cache_fs_obj_id = self_image_infos->reserved[8];\n#endif\n    EXPECT_EQ(proctype_image_infos.shared_cache_fs_id, shared_cache_fs_id);\n    EXPECT_EQ(proctype_image_infos.shared_cache_fs_obj_id,\n              shared_cache_fs_obj_id);\n#endif\n  }\n#endif\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_13\n  if (proctype_image_infos.version >= 15 && macos_version_number >= 10'13'00) {\n    EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_addr,\n              self_image_infos->compact_dyld_image_info_addr);\n    EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_size,\n              self_image_infos->compact_dyld_image_info_size);\n  }\n#endif\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15\n  if (proctype_image_infos.version >= 16) {\n    EXPECT_EQ(proctype_image_infos.platform, self_image_infos->platform);\n  }\n#endif\n\n  if (proctype_image_infos.version >= 1) {\n    std::vector<process_types::dyld_image_info> proctype_image_info_vector(\n        proctype_image_infos.infoArrayCount);\n    ASSERT_TRUE(process_types::dyld_image_info::ReadArrayInto(\n        &process_reader,\n        proctype_image_infos.infoArray,\n        proctype_image_info_vector.size(),\n        &proctype_image_info_vector[0]));\n\n    for (size_t index = 0;\n         index < proctype_image_infos.infoArrayCount;\n         ++index) {\n      const dyld_image_info* self_image_info =\n          &self_image_infos->infoArray[index];\n      const process_types::dyld_image_info& proctype_image_info =\n          proctype_image_info_vector[index];\n\n      EXPECT_EQ(proctype_image_info.imageLoadAddress,\n                reinterpret_cast<uint64_t>(self_image_info->imageLoadAddress))\n          << \"index \" << index;\n      EXPECT_EQ(proctype_image_info.imageFilePath,\n                reinterpret_cast<uint64_t>(self_image_info->imageFilePath))\n          << \"index \" << index;\n      EXPECT_EQ(proctype_image_info.imageFileModDate,\n                implicit_cast<uint64_t>(self_image_info->imageFileModDate))\n          << \"index \" << index;\n\n      TEST_STRING(\n          process_reader, self_image_info, proctype_image_info, imageFilePath);\n    }\n  }\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n  if (proctype_image_infos.version >= 8) {\n    std::vector<process_types::dyld_uuid_info> proctype_uuid_info_vector(\n        proctype_image_infos.uuidArrayCount);\n    ASSERT_TRUE(process_types::dyld_uuid_info::ReadArrayInto(\n        &process_reader,\n        proctype_image_infos.uuidArray,\n        proctype_uuid_info_vector.size(),\n        &proctype_uuid_info_vector[0]));\n\n    for (size_t index = 0;\n         index < proctype_image_infos.uuidArrayCount;\n         ++index) {\n      const dyld_uuid_info* self_uuid_info =\n          &self_image_infos->uuidArray[index];\n      const process_types::dyld_uuid_info& proctype_uuid_info =\n          proctype_uuid_info_vector[index];\n\n      EXPECT_EQ(proctype_uuid_info.imageLoadAddress,\n                reinterpret_cast<uint64_t>(self_uuid_info->imageLoadAddress))\n          << \"index \" << index;\n      EXPECT_EQ(memcmp(self_uuid_info->imageUUID,\n                       proctype_uuid_info.imageUUID,\n                       sizeof(self_uuid_info->imageUUID)),\n                0)\n          << \"index \" << index;\n    }\n  }\n#endif\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/system_snapshot_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/system_snapshot_mac.h\"\n\n#include <Availability.h>\n#include <stddef.h>\n#include <sys/sysctl.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n\n#include <algorithm>\n\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/scoped_clear_last_error.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"snapshot/posix/timezone.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/mac/sysctl.h\"\n#include \"util/numeric/in_range_cast.h\"\n\nnamespace crashpad {\n\nnamespace {\n\ntemplate <typename T>\nint ReadIntSysctlByName_NoLog(const char* name, T* value) {\n  size_t value_len = sizeof(*value);\n  return sysctlbyname(name, value, &value_len, nullptr, 0);\n}\n\ntemplate <typename T>\nT ReadIntSysctlByName(const char* name, T default_value) {\n  T value;\n  if (ReadIntSysctlByName_NoLog(name, &value) != 0) {\n    PLOG(WARNING) << \"sysctlbyname \" << name;\n    return default_value;\n  }\n\n  return value;\n}\n\ntemplate <typename T>\nT CastIntSysctlByName(const char* name, T default_value) {\n  int int_value = ReadIntSysctlByName<int>(name, default_value);\n  return InRangeCast<T>(int_value, default_value);\n}\n\n#if defined(ARCH_CPU_X86_FAMILY)\nvoid CallCPUID(uint32_t leaf,\n               uint32_t* eax,\n               uint32_t* ebx,\n               uint32_t* ecx,\n               uint32_t* edx) {\n  asm(\"cpuid\"\n      : \"=a\"(*eax), \"=b\"(*ebx), \"=c\"(*ecx), \"=d\"(*edx)\n      : \"a\"(leaf), \"b\"(0), \"c\"(0), \"d\"(0));\n}\n#endif\n\n}  // namespace\n\nnamespace internal {\n\nSystemSnapshotMac::SystemSnapshotMac()\n    : SystemSnapshot(),\n      os_version_full_(),\n      os_version_build_(),\n      process_reader_(nullptr),\n      snapshot_time_(nullptr),\n      os_version_major_(0),\n      os_version_minor_(0),\n      os_version_bugfix_(0),\n      initialized_() {\n}\n\nSystemSnapshotMac::~SystemSnapshotMac() {\n}\n\nvoid SystemSnapshotMac::Initialize(ProcessReaderMac* process_reader,\n                                   const timeval* snapshot_time) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  process_reader_ = process_reader;\n  snapshot_time_ = snapshot_time;\n\n  // MacOSVersionComponents() logs its own warnings if it can’t figure anything\n  // out. It’s not fatal if this happens. The default values are reasonable.\n  std::string os_version_string;\n  MacOSVersionComponents(&os_version_major_,\n                         &os_version_minor_,\n                         &os_version_bugfix_,\n                         &os_version_build_,\n                         &os_version_string);\n\n  std::string uname_string;\n  utsname uts;\n  if (uname(&uts) != 0) {\n    PLOG(WARNING) << \"uname\";\n  } else {\n    uname_string = base::StringPrintf(\n        \"%s %s %s %s\", uts.sysname, uts.release, uts.version, uts.machine);\n  }\n\n  if (!os_version_string.empty()) {\n    if (!uname_string.empty()) {\n      os_version_full_ = base::StringPrintf(\n          \"%s; %s\", os_version_string.c_str(), uname_string.c_str());\n    } else {\n      os_version_full_ = os_version_string;\n    }\n  } else {\n    os_version_full_ = uname_string;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n}\n\nCPUArchitecture SystemSnapshotMac::GetCPUArchitecture() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  return process_reader_->Is64Bit() ? kCPUArchitectureX86_64\n                                    : kCPUArchitectureX86;\n#elif defined(ARCH_CPU_ARM64)\n  return kCPUArchitectureARM64;\n#else\n#error port to your architecture\n#endif\n}\n\nuint32_t SystemSnapshotMac::CPURevision() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  // machdep.cpu.family and machdep.cpu.model already take the extended family\n  // and model IDs into account. See 10.9.2 xnu-2422.90.20/osfmk/i386/cpuid.c\n  // cpuid_set_generic_info().\n  uint16_t family = CastIntSysctlByName<uint16_t>(\"machdep.cpu.family\", 0);\n  uint8_t model = CastIntSysctlByName<uint8_t>(\"machdep.cpu.model\", 0);\n  uint8_t stepping = CastIntSysctlByName<uint8_t>(\"machdep.cpu.stepping\", 0);\n\n  return (family << 16) | (model << 8) | stepping;\n#elif defined(ARCH_CPU_ARM64)\n  // TODO(macos_arm64): Verify and test.\n  return CastIntSysctlByName<uint32_t>(\"hw.cpufamily\", 0);\n#else\n#error port to your architecture\n#endif\n}\n\nuint8_t SystemSnapshotMac::CPUCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return CastIntSysctlByName<uint8_t>(\"hw.ncpu\", 1);\n}\n\nstd::string SystemSnapshotMac::CPUVendor() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  return ReadStringSysctlByName(\"machdep.cpu.vendor\", true);\n#elif defined(ARCH_CPU_ARM64)\n  return ReadStringSysctlByName(\"machdep.cpu.brand_string\", true);\n#else\n#error port to your architecture\n#endif\n}\n\nvoid SystemSnapshotMac::CPUFrequency(\n    uint64_t* current_hz, uint64_t* max_hz) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n#if defined(ARCH_CPU_X86_FAMILY)\n  *current_hz = ReadIntSysctlByName<uint64_t>(\"hw.cpufrequency\", 0);\n  *max_hz = ReadIntSysctlByName<uint64_t>(\"hw.cpufrequency_max\", 0);\n#elif defined(ARCH_CPU_ARM64)\n  // TODO(https://crashpad.chromium.org/bug/352): When production arm64\n  // hardware is available, determine whether CPU frequency is visible anywhere\n  // (likely via a sysctl or via IOKit) and use it if feasible.\n  *current_hz = 0;\n  *max_hz = 0;\n#else\n#error port to your architecture\n#endif\n}\n\nuint32_t SystemSnapshotMac::CPUX86Signature() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  return ReadIntSysctlByName<uint32_t>(\"machdep.cpu.signature\", 0);\n#else\n  NOTREACHED();\n#endif\n}\n\nuint64_t SystemSnapshotMac::CPUX86Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  return ReadIntSysctlByName<uint64_t>(\"machdep.cpu.feature_bits\", 0);\n#else\n  NOTREACHED();\n#endif\n}\n\nuint64_t SystemSnapshotMac::CPUX86ExtendedFeatures() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  return ReadIntSysctlByName<uint64_t>(\"machdep.cpu.extfeature_bits\", 0);\n#else\n  NOTREACHED();\n#endif\n}\n\nuint32_t SystemSnapshotMac::CPUX86Leaf7Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  // The machdep.cpu.leaf7_feature_bits sysctl isn’t supported prior to OS X\n  // 10.7, so read this by calling cpuid directly.\n  //\n  // machdep.cpu.max_basic could be used to check whether to read the leaf, but\n  // that sysctl isn’t supported prior to Mac OS X 10.6, so read the maximum\n  // basic leaf by calling cpuid directly as well. All CPUs that Apple is known\n  // to have shipped should support a maximum basic leaf value of at least 0xa.\n  uint32_t eax, ebx, ecx, edx;\n  CallCPUID(0, &eax, &ebx, &ecx, &edx);\n  if (eax < 7) {\n    return 0;\n  }\n\n  CallCPUID(7, &eax, &ebx, &ecx, &edx);\n  return ebx;\n#else\n  NOTREACHED();\n#endif\n}\n\nbool SystemSnapshotMac::CPUX86SupportsDAZ() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  // The correct way to check for denormals-as-zeros (DAZ) support is to examine\n  // mxcsr mask, which can be done with fxsave. See Intel Software Developer’s\n  // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the\n  // DAZ Flag in the MXCSR Register”. Note that since this function tests for\n  // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would\n  // indicate whether DAZ is actually enabled, which is a per-thread context\n  // concern.\n  //\n  // All CPUs that Apple is known to have shipped should support DAZ.\n\n  // Test for fxsave support.\n  uint64_t features = CPUX86Features();\n  if (!(features & (UINT64_C(1) << 24))) {\n    return false;\n  }\n\n  // Call fxsave.\n#if defined(ARCH_CPU_X86)\n  CPUContextX86::Fxsave fxsave __attribute__((aligned(16))) = {};\n#elif defined(ARCH_CPU_X86_64)\n  CPUContextX86_64::Fxsave fxsave __attribute__((aligned(16))) = {};\n#endif\n  static_assert(sizeof(fxsave) == 512, \"fxsave size\");\n  static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28,\n                \"mxcsr_mask offset\");\n  asm(\"fxsave %0\" : \"=m\"(fxsave));\n\n  // Test the DAZ bit.\n  return fxsave.mxcsr_mask & (1 << 6);\n#else\n  NOTREACHED();\n#endif\n}\n\nSystemSnapshot::OperatingSystem SystemSnapshotMac::GetOperatingSystem() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kOperatingSystemMacOSX;\n}\n\nbool SystemSnapshotMac::OSServer() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return false;\n}\n\nvoid SystemSnapshotMac::OSVersion(int* major,\n                                  int* minor,\n                                  int* bugfix,\n                                  std::string* build) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *major = os_version_major_;\n  *minor = os_version_minor_;\n  *bugfix = os_version_bugfix_;\n  build->assign(os_version_build_);\n}\n\nstd::string SystemSnapshotMac::OSVersionFull() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return os_version_full_;\n}\n\nstd::string SystemSnapshotMac::MachineDescription() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::string model;\n  std::string board_id;\n  MacModelAndBoard(&model, &board_id);\n\n  if (!model.empty()) {\n    if (!board_id.empty()) {\n      return base::StringPrintf(\"%s (%s)\", model.c_str(), board_id.c_str());\n    }\n    return model;\n  }\n  if (!board_id.empty()) {\n    return base::StringPrintf(\"(%s)\", board_id.c_str());\n  }\n  return std::string();\n}\n\nbool SystemSnapshotMac::NXEnabled() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  int value;\n  if (ReadIntSysctlByName_NoLog(\"kern.nx\", &value) != 0) {\n    {\n      // Support for the kern.nx sysctlbyname is compiled out of production\n      // kernels on macOS 10.14.5 and later, although it’s available in\n      // development and debug kernels. Compare 10.14.3\n      // xnu-4903.241.1/bsd/kern/kern_sysctl.c to 10.15.0\n      // xnu-6153.11.26/bsd/kern/kern_sysctl.c (10.14.4 and 10.14.5 xnu source\n      // are not yet available). In newer production kernels, NX is always\n      // enabled. See 10.15.0 xnu-6153.11.26/osfmk/x86_64/pmap.c nx_enabled.\n#if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14\n      const bool nx_always_enabled = true;\n#else  // DT >= 10.14\n      base::ScopedClearLastError reset_errno;\n      const bool nx_always_enabled = MacOSVersionNumber() >= 10'14'00;\n#endif  // DT >= 10.14\n      if (nx_always_enabled) {\n        return true;\n      }\n    }\n\n    // Even if sysctlbyname should have worked, NX is enabled by default in all\n    // supported configurations, so return true even while warning.\n    PLOG(WARNING) << \"sysctlbyname kern.nx\";\n    return true;\n  }\n\n  return value;\n}\n\nvoid SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status,\n                                 int* standard_offset_seconds,\n                                 int* daylight_offset_seconds,\n                                 std::string* standard_name,\n                                 std::string* daylight_name) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  internal::TimeZone(*snapshot_time_,\n                     dst_status,\n                     standard_offset_seconds,\n                     daylight_offset_seconds,\n                     standard_name,\n                     daylight_name);\n}\n\nuint64_t SystemSnapshotMac::AddressMask() const {\n  uint64_t mask = 0;\n#if defined(ARCH_CPU_ARM64)\n  // `machdep.virtual_address_size` is the number of addressable bits in\n  // userspace virtual addresses\n  uint8_t addressable_bits =\n      CastIntSysctlByName<uint8_t>(\"machdep.virtual_address_size\", 0);\n  if (addressable_bits) {\n    mask = ~((1UL << addressable_bits) - 1);\n  }\n#endif\n  return mask;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/system_snapshot_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_\n#define CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"snapshot/system_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderMac;\n\nnamespace internal {\n\n//! \\brief A SystemSnapshot of the running system, when the system runs macOS.\nclass SystemSnapshotMac final : public SystemSnapshot {\n public:\n  SystemSnapshotMac();\n\n  SystemSnapshotMac(const SystemSnapshotMac&) = delete;\n  SystemSnapshotMac& operator=(const SystemSnapshotMac&) = delete;\n\n  ~SystemSnapshotMac() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A reader for the process being snapshotted.\n  //!     \\n\\n\n  //!     It seems odd that a system snapshot implementation would need a\n  //!     ProcessReaderMac, but some of the information reported about the\n  //!     system depends on the process it’s being reported for. For example,\n  //!     the architecture returned by GetCPUArchitecture() should be the\n  //!     architecture of the process, which may be different than the native\n  //!     architecture of the system: an x86_64 system can run both x86_64 and\n  //!     32-bit x86 processes.\n  //! \\param[in] snapshot_time The time of the snapshot being taken.\n  //!     \\n\\n\n  //!     This parameter is necessary for TimeZone() to determine whether\n  //!     daylight saving time was in effect at the time the snapshot was taken.\n  //!     Otherwise, it would need to base its determination on the current\n  //!     time, which may be different than the snapshot time for snapshots\n  //!     generated around the daylight saving transition time.\n  void Initialize(ProcessReaderMac* process_reader,\n                  const timeval* snapshot_time);\n\n  // SystemSnapshot:\n\n  CPUArchitecture GetCPUArchitecture() const override;\n  uint32_t CPURevision() const override;\n  uint8_t CPUCount() const override;\n  std::string CPUVendor() const override;\n  void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;\n  uint32_t CPUX86Signature() const override;\n  uint64_t CPUX86Features() const override;\n  uint64_t CPUX86ExtendedFeatures() const override;\n  uint32_t CPUX86Leaf7Features() const override;\n  bool CPUX86SupportsDAZ() const override;\n  OperatingSystem GetOperatingSystem() const override;\n  bool OSServer() const override;\n  void OSVersion(\n      int* major, int* minor, int* bugfix, std::string* build) const override;\n  std::string OSVersionFull() const override;\n  bool NXEnabled() const override;\n  std::string MachineDescription() const override;\n  void TimeZone(DaylightSavingTimeStatus* dst_status,\n                int* standard_offset_seconds,\n                int* daylight_offset_seconds,\n                std::string* standard_name,\n                std::string* daylight_name) const override;\n  uint64_t AddressMask() const override;\n\n private:\n  std::string os_version_full_;\n  std::string os_version_build_;\n  ProcessReaderMac* process_reader_;  // weak\n  const timeval* snapshot_time_;  // weak\n  int os_version_major_;\n  int os_version_minor_;\n  int os_version_bugfix_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_\n"
  },
  {
    "path": "snapshot/mac/system_snapshot_mac_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/system_snapshot_mac.h\"\n\n#include <sys/time.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"test/errors.h\"\n#include \"util/mac/mac_util.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// SystemSnapshotMac objects would be cumbersome to construct in each test that\n// requires one, because of the repetitive and mechanical work necessary to set\n// up a ProcessReaderMac and timeval, along with the checks to verify that these\n// operations succeed. This test fixture class handles the initialization work\n// so that individual tests don’t have to.\nclass SystemSnapshotMacTest : public testing::Test {\n public:\n  SystemSnapshotMacTest()\n      : Test(),\n        process_reader_(),\n        snapshot_time_(),\n        system_snapshot_() {\n  }\n\n  SystemSnapshotMacTest(const SystemSnapshotMacTest&) = delete;\n  SystemSnapshotMacTest& operator=(const SystemSnapshotMacTest&) = delete;\n\n  const internal::SystemSnapshotMac& system_snapshot() const {\n    return system_snapshot_;\n  }\n\n  // testing::Test:\n  void SetUp() override {\n    ASSERT_TRUE(process_reader_.Initialize(mach_task_self()));\n    ASSERT_EQ(gettimeofday(&snapshot_time_, nullptr), 0)\n        << ErrnoMessage(\"gettimeofday\");\n    system_snapshot_.Initialize(&process_reader_, &snapshot_time_);\n  }\n\n private:\n  ProcessReaderMac process_reader_;\n  timeval snapshot_time_;\n  internal::SystemSnapshotMac system_snapshot_;\n};\n\nTEST_F(SystemSnapshotMacTest, GetCPUArchitecture) {\n  CPUArchitecture cpu_architecture = system_snapshot().GetCPUArchitecture();\n\n#if defined(ARCH_CPU_X86)\n  EXPECT_EQ(cpu_architecture, kCPUArchitectureX86);\n#elif defined(ARCH_CPU_X86_64)\n  EXPECT_EQ(cpu_architecture, kCPUArchitectureX86_64);\n#elif defined(ARCH_CPU_ARM64)\n  EXPECT_EQ(cpu_architecture, kCPUArchitectureARM64);\n#else\n#error port to your architecture\n#endif\n}\n\nTEST_F(SystemSnapshotMacTest, CPUCount) {\n  EXPECT_GE(system_snapshot().CPUCount(), 1);\n}\n\nTEST_F(SystemSnapshotMacTest, CPUVendor) {\n  std::string cpu_vendor = system_snapshot().CPUVendor();\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  // Apple has only shipped Intel x86-family CPUs, but here’s a small nod to the\n  // “Hackintosh” crowd.\n  if (cpu_vendor != \"GenuineIntel\" && cpu_vendor != \"AuthenticAMD\") {\n    FAIL() << \"cpu_vendor \" << cpu_vendor;\n  }\n#elif defined(ARCH_CPU_ARM64)\n  EXPECT_THAT(cpu_vendor, testing::StartsWith(\"Apple \"));\n#else\n#error port to your architecture\n#endif\n}\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\nTEST_F(SystemSnapshotMacTest, CPUX86SupportsDAZ) {\n  // All x86-family CPUs that Apple is known to have shipped should support DAZ.\n  EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ());\n}\n\n#endif\n\nTEST_F(SystemSnapshotMacTest, GetOperatingSystem) {\n  EXPECT_EQ(system_snapshot().GetOperatingSystem(),\n            SystemSnapshot::kOperatingSystemMacOSX);\n}\n\nTEST_F(SystemSnapshotMacTest, OSVersion) {\n  int major;\n  int minor;\n  int bugfix;\n  std::string build;\n  system_snapshot().OSVersion(&major, &minor, &bugfix, &build);\n\n  const int macos_version_number = MacOSVersionNumber();\n  EXPECT_EQ(major * 1'00'00 + minor * 1'00 +\n                (macos_version_number >= 10'13'04 ? bugfix : 0),\n            macos_version_number);\n  EXPECT_FALSE(build.empty());\n}\n\nTEST_F(SystemSnapshotMacTest, OSVersionFull) {\n  EXPECT_FALSE(system_snapshot().OSVersionFull().empty());\n}\n\nTEST_F(SystemSnapshotMacTest, MachineDescription) {\n  EXPECT_FALSE(system_snapshot().MachineDescription().empty());\n}\n\nTEST_F(SystemSnapshotMacTest, NXEnabled) {\n  // Assume NX will always be enabled, as it was always enabled by default on\n  // all supported versions of macOS.\n  EXPECT_TRUE(system_snapshot().NXEnabled());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/thread_snapshot_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/mac/thread_snapshot_mac.h\"\n\n#include \"base/check_op.h\"\n#include \"snapshot/mac/cpu_context_mac.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nThreadSnapshotMac::ThreadSnapshotMac()\n    : ThreadSnapshot(),\n      context_union_(),\n      context_(),\n      stack_(),\n      thread_name_(),\n      thread_id_(0),\n      thread_specific_data_address_(0),\n      thread_(MACH_PORT_NULL),\n      suspend_count_(0),\n      priority_(0),\n      initialized_() {}\n\nThreadSnapshotMac::~ThreadSnapshotMac() {\n}\n\nbool ThreadSnapshotMac::Initialize(\n    ProcessReaderMac* process_reader,\n    const ProcessReaderMac::Thread& process_reader_thread) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  thread_ = process_reader_thread.port;\n  thread_id_ = process_reader_thread.id;\n  thread_name_ = process_reader_thread.name;\n  suspend_count_ = process_reader_thread.suspend_count;\n  priority_ = process_reader_thread.priority;\n  thread_specific_data_address_ =\n      process_reader_thread.thread_specific_data_address;\n\n  stack_.Initialize(process_reader->Memory(),\n                    process_reader_thread.stack_region_address,\n                    process_reader_thread.stack_region_size);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  if (process_reader->Is64Bit()) {\n    context_.architecture = kCPUArchitectureX86_64;\n    context_.x86_64 = &context_union_.x86_64;\n    InitializeCPUContextX86_64(context_.x86_64,\n                               THREAD_STATE_NONE,\n                               nullptr,\n                               0,\n                               &process_reader_thread.thread_context.t64,\n                               &process_reader_thread.float_context.f64,\n                               &process_reader_thread.debug_context.d64);\n  } else {\n    context_.architecture = kCPUArchitectureX86;\n    context_.x86 = &context_union_.x86;\n    InitializeCPUContextX86(context_.x86,\n                            THREAD_STATE_NONE,\n                            nullptr,\n                            0,\n                            &process_reader_thread.thread_context.t32,\n                            &process_reader_thread.float_context.f32,\n                            &process_reader_thread.debug_context.d32);\n  }\n#elif defined(ARCH_CPU_ARM64)\n  context_.architecture = kCPUArchitectureARM64;\n  context_.arm64 = &context_union_.arm64;\n  InitializeCPUContextARM64(context_.arm64,\n                            THREAD_STATE_NONE,\n                            nullptr,\n                            0,\n                            &process_reader_thread.thread_context,\n                            &process_reader_thread.float_context,\n                            &process_reader_thread.debug_context);\n#else\n#error Port to your CPU architecture\n#endif\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst CPUContext* ThreadSnapshotMac::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nconst MemorySnapshot* ThreadSnapshotMac::Stack() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &stack_;\n}\n\nuint64_t ThreadSnapshotMac::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nstd::string ThreadSnapshotMac::ThreadName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_name_;\n}\n\nint ThreadSnapshotMac::SuspendCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return suspend_count_;\n}\n\nint ThreadSnapshotMac::Priority() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return priority_;\n}\n\nuint64_t ThreadSnapshotMac::ThreadSpecificDataAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_specific_data_address_;\n}\n\nstd::vector<const MemorySnapshot*> ThreadSnapshotMac::ExtraMemory() const {\n  return std::vector<const MemorySnapshot*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/mac/thread_snapshot_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_\n#define CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_\n\n#include <mach/mach.h>\n#include <stdint.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderMac;\n\nnamespace internal {\n\n//! \\brief A ThreadSnapshot of a thread in a running (or crashed) process on a\n//!     macOS system.\nclass ThreadSnapshotMac final : public ThreadSnapshot {\n public:\n  ThreadSnapshotMac();\n\n  ThreadSnapshotMac(const ThreadSnapshotMac&) = delete;\n  ThreadSnapshotMac& operator=(const ThreadSnapshotMac&) = delete;\n\n  ~ThreadSnapshotMac() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderMac for the task containing the\n  //!     thread.\n  //! \\param[in] process_reader_thread The thread within the ProcessReaderMac\n  //!     for which the snapshot should be created.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(ProcessReaderMac* process_reader,\n                  const ProcessReaderMac::Thread& process_reader_thread);\n\n  // ThreadSnapshot:\n\n  const CPUContext* Context() const override;\n  const MemorySnapshot* Stack() const override;\n  uint64_t ThreadID() const override;\n  std::string ThreadName() const override;\n  int SuspendCount() const override;\n  int Priority() const override;\n  uint64_t ThreadSpecificDataAddress() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  union {\n#if defined(ARCH_CPU_X86_FAMILY)\n    CPUContextX86 x86;\n    CPUContextX86_64 x86_64;\n#elif defined(ARCH_CPU_ARM64)\n    CPUContextARM64 arm64;\n#else\n#error Port to your CPU architecture\n#endif\n  } context_union_;\n  CPUContext context_;\n  MemorySnapshotGeneric stack_;\n  std::string thread_name_;\n  uint64_t thread_id_;\n  uint64_t thread_specific_data_address_;\n  thread_t thread_;\n  int suspend_count_;\n  int priority_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_\n"
  },
  {
    "path": "snapshot/memory_map_region_snapshot.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\nnamespace crashpad {\n\n//! \\brief An abstract interface to a snapshot representing a region of the\n//!     memory map present in the snapshot process.\nclass MemoryMapRegionSnapshot {\n public:\n  virtual ~MemoryMapRegionSnapshot() {}\n\n  //! \\brief Gets a MINIDUMP_MEMORY_INFO representing the region.\n  virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/memory_snapshot.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/memory_snapshot.h\"\n\n#include <algorithm>\n\n#include \"base/format_macros.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\nnamespace {\n\nbool DetermineMergedRangeImpl(bool log,\n                              const MemorySnapshot* a,\n                              const MemorySnapshot* b,\n                              CheckedRange<uint64_t, size_t>* merged) {\n  if (a->Size() == 0) {\n    LOG_IF(ERROR, log) << base::StringPrintf(\n        \"invalid empty range at 0x%\" PRIx64, a->Address());\n    return false;\n  }\n\n  if (b->Size() == 0) {\n    LOG_IF(ERROR, log) << base::StringPrintf(\n        \"invalid empty range at 0x%\" PRIx64, b->Address());\n    return false;\n  }\n\n  CheckedRange<uint64_t, size_t> range_a(a->Address(), a->Size());\n  if (!range_a.IsValid()) {\n    LOG_IF(ERROR, log) << base::StringPrintf(\"invalid range at 0x%\" PRIx64\n                                             \", size %\" PRIuS,\n                                             range_a.base(),\n                                             range_a.size());\n    return false;\n  }\n\n  CheckedRange<uint64_t, size_t> range_b(b->Address(), b->Size());\n  if (!range_b.IsValid()) {\n    LOG_IF(ERROR, log) << base::StringPrintf(\"invalid range at 0x%\" PRIx64\n                                             \", size %\" PRIuS,\n                                             range_b.base(),\n                                             range_b.size());\n    return false;\n  }\n\n  if (!range_a.OverlapsRange(range_b) && range_a.end() != range_b.base() &&\n      range_b.end() != range_a.base()) {\n    LOG_IF(ERROR, log) << base::StringPrintf(\n        \"ranges not overlapping or abutting: (0x%\" PRIx64 \", size %\" PRIuS\n        \") and (0x%\" PRIx64 \", size %\" PRIuS \")\",\n        range_a.base(),\n        range_a.size(),\n        range_b.base(),\n        range_b.size());\n    return false;\n  }\n\n  if (merged) {\n    uint64_t base = std::min(range_a.base(), range_b.base());\n    uint64_t end = std::max(range_a.end(), range_b.end());\n    size_t size = static_cast<size_t>(end - base);\n    merged->SetRange(base, size);\n  }\n  return true;\n}\n\n}  // namespace\n\nbool LoggingDetermineMergedRange(const MemorySnapshot* a,\n                                 const MemorySnapshot* b,\n                                 CheckedRange<uint64_t, size_t>* merged) {\n  return DetermineMergedRangeImpl(true, a, b, merged);\n}\n\nbool DetermineMergedRange(const MemorySnapshot* a,\n                          const MemorySnapshot* b,\n                          CheckedRange<uint64_t, size_t>* merged) {\n  return DetermineMergedRangeImpl(false, a, b, merged);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/memory_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\n\n//! \\brief An abstract interface to a snapshot representing a region of memory\n//!     present in a snapshot process.\nclass MemorySnapshot {\n public:\n  //! \\brief An interface that MemorySnapshot clients must implement in order to\n  //!     receive memory snapshot data.\n  //!\n  //! This callback-based model frees MemorySnapshot implementations from having\n  //! to deal with memory region ownership problems. When a memory snapshot’s\n  //! data is read, it will be passed to a delegate method.\n  class Delegate {\n   public:\n    virtual ~Delegate() {}\n\n    //! \\brief Called by MemorySnapshot::Read() to provide data requested by a\n    //!     call to that method.\n    //!\n    //! \\param[in] data A pointer to the data that was read. The callee does not\n    //!     take ownership of this data. This data is only valid for the\n    //!     duration of the call to this method. This parameter may be `nullptr`\n    //!     if \\a size is `0`.\n    //! \\param[in] size The size of the data that was read.\n    //!\n    //! \\return `true` on success, `false` on failure. MemoryDelegate::Read()\n    //!     will use this as its own return value.\n    virtual bool MemorySnapshotDelegateRead(void* data, size_t size) = 0;\n  };\n\n  virtual ~MemorySnapshot() {}\n\n  //! \\brief The base address of the memory snapshot in the snapshot process’\n  //!     address space.\n  virtual uint64_t Address() const = 0;\n\n  //! \\brief The size of the memory snapshot.\n  virtual size_t Size() const = 0;\n\n  //! \\brief Calls Delegate::MemorySnapshotDelegateRead(), providing it with\n  //!     the memory snapshot’s data.\n  //!\n  //! Implementations do not necessarily read the memory snapshot data prior to\n  //! this method being called. Memory snapshot data may be loaded lazily and\n  //! may be discarded after being passed to the delegate. This provides clean\n  //! memory management without burdening a snapshot implementation with the\n  //! requirement that it track all memory region data simultaneously.\n  //!\n  //! \\return `false` on failure, otherwise, the return value of\n  //!     Delegate::MemorySnapshotDelegateRead(), which should be `true` on\n  //!     success and `false` on failure.\n  virtual bool Read(Delegate* delegate) const = 0;\n\n  //! \\brief Creates a new MemorySnapshot based on merging this one with \\a\n  //!     other.\n  //!\n  //! The ranges described by the two snapshots must either overlap or abut, and\n  //! must be of the same concrete type.\n  //!\n  //! \\return A newly allocated MemorySnapshot representing the merged range, or\n  //!     `nullptr` with an error logged.\n  virtual const MemorySnapshot* MergeWithOtherSnapshot(\n      const MemorySnapshot* other) const = 0;\n};\n\n//! \\brief Given two memory snapshots, checks if they're overlapping or\n//!     abutting, and if so, returns the result of merging the two ranges.\n//!\n//! This function is useful to implement\n//! MemorySnapshot::MergeWithOtherSnapshot().\n//!\n//! \\param[in] a The first range. Must have Size() > 0.\n//! \\param[in] b The second range. Must have Size() > 0.\n//! \\param[out] merged The resulting merged range. May be `nullptr` if only a\n//!     characterization of the ranges is desired.\n//!\n//! \\return `true` if the input ranges overlap or abut, with \\a merged filled\n//!     out, otherwise, `false` with an error logged if \\a log is `true`.\nbool LoggingDetermineMergedRange(const MemorySnapshot* a,\n                                 const MemorySnapshot* b,\n                                 CheckedRange<uint64_t, size_t>* merged);\n\n//! \\brief The same as LoggingDetermineMergedRange but with no errors logged.\n//!\n//! \\sa LoggingDetermineMergedRange\nbool DetermineMergedRange(const MemorySnapshot* a,\n                          const MemorySnapshot* b,\n                          CheckedRange<uint64_t, size_t>* merged);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/memory_snapshot_generic.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_GENERIC_H_\n#define CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_GENERIC_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_math.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A MemorySnapshot of a memory region in a process on the running\n//!     system. Works on multiple platforms by using a platform-specific\n//!     ProcessMemory object.\nclass MemorySnapshotGeneric final : public MemorySnapshot {\n public:\n  MemorySnapshotGeneric() = default;\n\n  MemorySnapshotGeneric(const MemorySnapshotGeneric&) = delete;\n  MemorySnapshotGeneric& operator=(const MemorySnapshotGeneric&) = delete;\n\n  ~MemorySnapshotGeneric() = default;\n\n  //! \\brief Initializes the object.\n  //!\n  //! Memory is read lazily. No attempt is made to read the memory snapshot data\n  //! until Read() is called, and the memory snapshot data is discared when\n  //! Read() returns.\n  //!\n  //! \\param[in] process_memory A reader for the process being snapshotted.\n  //! \\param[in] address The base address of the memory region to snapshot, in\n  //!     the snapshot process’ address space.\n  //! \\param[in] size The size of the memory region to snapshot.\n  void Initialize(const ProcessMemory* process_memory,\n                  VMAddress address,\n                  VMSize size) {\n    INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n    process_memory_ = process_memory;\n    address_ = address;\n    size_ = base::checked_cast<size_t>(size);\n    INITIALIZATION_STATE_SET_VALID(initialized_);\n  }\n\n  // MemorySnapshot:\n\n  uint64_t Address() const override {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n    return address_;\n  }\n\n  size_t Size() const override {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n    return size_;\n  }\n\n  bool Read(Delegate* delegate) const override {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n    if (size_ == 0) {\n      return delegate->MemorySnapshotDelegateRead(nullptr, size_);\n    }\n\n    auto buffer = base::HeapArray<uint8_t>::Uninit(size_);\n    if (!process_memory_->Read(address_, buffer.size(), buffer.data())) {\n      return false;\n    }\n    return delegate->MemorySnapshotDelegateRead(buffer.data(), buffer.size());\n  }\n\n  const MemorySnapshot* MergeWithOtherSnapshot(\n      const MemorySnapshot* other) const override {\n    const MemorySnapshotGeneric* other_as_memory_snapshot_concrete =\n        reinterpret_cast<const MemorySnapshotGeneric*>(other);\n    if (process_memory_ != other_as_memory_snapshot_concrete->process_memory_) {\n      LOG(ERROR) << \"different process_memory_ for snapshots\";\n      return nullptr;\n    }\n    CheckedRange<uint64_t, size_t> merged(0, 0);\n    if (!LoggingDetermineMergedRange(this, other, &merged))\n      return nullptr;\n\n    auto result = std::make_unique<MemorySnapshotGeneric>();\n    result->Initialize(process_memory_, merged.base(), merged.size());\n    return result.release();\n  }\n\n private:\n  template <class T>\n  friend const MemorySnapshot* MergeWithOtherSnapshotImpl(\n      const T* self,\n      const MemorySnapshot* other);\n\n  const ProcessMemory* process_memory_;  // weak\n  VMAddress address_;\n  size_t size_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_GENERIC_MEMORY_SNAPSHOT_GENERIC_H_\n"
  },
  {
    "path": "snapshot/memory_snapshot_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/memory_snapshot.h\"\n\n#include \"gtest/gtest.h\"\n#include \"snapshot/test/test_memory_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(DetermineMergedRange, NonOverlapping) {\n  TestMemorySnapshot a;\n  TestMemorySnapshot b;\n  a.SetAddress(0);\n  a.SetSize(100);\n  b.SetAddress(200);\n  b.SetSize(50);\n  CheckedRange<uint64_t, size_t> range(0, 0);\n  EXPECT_FALSE(DetermineMergedRange(&a, &b, &range));\n  EXPECT_FALSE(DetermineMergedRange(&b, &a, &range));\n\n  a.SetSize(199);\n  EXPECT_FALSE(DetermineMergedRange(&a, &b, &range));\n}\n\nTEST(DetermineMergedRange, Empty) {\n  TestMemorySnapshot a;\n  TestMemorySnapshot b;\n  a.SetAddress(100);\n  a.SetSize(0);\n  b.SetAddress(200);\n  b.SetSize(20);\n\n  CheckedRange<uint64_t, size_t> range(0, 0);\n  // Empty are invalid.\n  EXPECT_FALSE(DetermineMergedRange(&a, &b, &range));\n  EXPECT_FALSE(DetermineMergedRange(&b, &a, &range));\n  EXPECT_FALSE(DetermineMergedRange(&a, &a, &range));\n}\n\nTEST(DetermineMergedRange, Abutting) {\n  TestMemorySnapshot a;\n  TestMemorySnapshot b;\n\n  a.SetAddress(0);\n  a.SetSize(10);\n  b.SetAddress(10);\n  b.SetSize(20);\n\n  CheckedRange<uint64_t, size_t> range(0, 0);\n  EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));\n  EXPECT_EQ(0u, range.base());\n  EXPECT_EQ(30u, range.size());\n\n  EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));\n  EXPECT_EQ(0u, range.base());\n  EXPECT_EQ(30u, range.size());\n}\n\nTEST(DetermineMergedRange, TypicalOverlapping) {\n  TestMemorySnapshot a;\n  TestMemorySnapshot b;\n\n  a.SetAddress(10);\n  a.SetSize(100);\n  b.SetAddress(50);\n  b.SetSize(100);\n\n  CheckedRange<uint64_t, size_t> range(0, 0);\n  EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));\n  EXPECT_EQ(10u, range.base());\n  EXPECT_EQ(140u, range.size());\n\n  EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));\n  EXPECT_EQ(10u, range.base());\n  EXPECT_EQ(140u, range.size());\n}\n\nTEST(DetermineMergedRange, OneFullyInsideAnother) {\n  TestMemorySnapshot a;\n  TestMemorySnapshot b;\n\n  a.SetAddress(20);\n  a.SetSize(100);\n  b.SetAddress(5);\n  b.SetSize(200);\n\n  CheckedRange<uint64_t, size_t> range(0, 0);\n  EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));\n  EXPECT_EQ(5u, range.base());\n  EXPECT_EQ(200u, range.size());\n\n  EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));\n  EXPECT_EQ(5u, range.base());\n  EXPECT_EQ(200u, range.size());\n}\n\nTEST(DetermineMergedRange, SameStart) {\n  TestMemorySnapshot a;\n  TestMemorySnapshot b;\n\n  a.SetAddress(5);\n  a.SetSize(100);\n  b.SetAddress(5);\n  b.SetSize(50);\n\n  CheckedRange<uint64_t, size_t> range(0, 0);\n  EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));\n  EXPECT_EQ(5u, range.base());\n  EXPECT_EQ(100u, range.size());\n\n  EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));\n  EXPECT_EQ(5u, range.base());\n  EXPECT_EQ(100u, range.size());\n}\n\nTEST(DetermineMergedRange, SameEnd) {\n  TestMemorySnapshot a;\n  TestMemorySnapshot b;\n\n  a.SetAddress(5);\n  a.SetSize(100);\n  b.SetAddress(70);\n  b.SetSize(35);\n\n  CheckedRange<uint64_t, size_t> range(0, 0);\n  EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));\n  EXPECT_EQ(5u, range.base());\n  EXPECT_EQ(100u, range.size());\n\n  EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));\n  EXPECT_EQ(5u, range.base());\n  EXPECT_EQ(100u, range.size());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/exception_snapshot_minidump.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/exception_snapshot_minidump.h\"\n\n#include \"snapshot/minidump/minidump_string_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nExceptionSnapshotMinidump::ExceptionSnapshotMinidump()\n    : ExceptionSnapshot(),\n      minidump_exception_stream_(),\n      context_(),\n      exception_information_(),\n      initialized_() {}\n\nExceptionSnapshotMinidump::~ExceptionSnapshotMinidump() {}\n\nbool ExceptionSnapshotMinidump::Initialize(FileReaderInterface* file_reader,\n                                           CPUArchitecture arch,\n                                           RVA minidump_exception_stream_rva) {\n  DCHECK(initialized_.is_uninitialized());\n  initialized_.set_invalid();\n\n  std::vector<unsigned char> minidump_context;\n\n  if (!file_reader->SeekSet(minidump_exception_stream_rva)) {\n    return false;\n  }\n\n  if (!file_reader->ReadExactly(&minidump_exception_stream_,\n                                sizeof(minidump_exception_stream_))) {\n    return false;\n  }\n\n  const size_t num_parameters =\n      minidump_exception_stream_.ExceptionRecord.NumberParameters;\n  for (size_t i = 0; i < num_parameters; ++i) {\n    exception_information_.push_back(\n        minidump_exception_stream_.ExceptionRecord.ExceptionInformation[i]);\n  }\n\n  if (!file_reader->SeekSet(minidump_exception_stream_.ThreadContext.Rva)) {\n    return false;\n  }\n\n  minidump_context.resize(minidump_exception_stream_.ThreadContext.DataSize);\n\n  if (!file_reader->ReadExactly(minidump_context.data(),\n                                minidump_context.size())) {\n    return false;\n  }\n\n  if (!context_.Initialize(arch, minidump_context)) {\n    return false;\n  }\n\n  initialized_.set_valid();\n  return true;\n}\n\nconst CPUContext* ExceptionSnapshotMinidump::Context() const {\n  DCHECK(initialized_.is_valid());\n  return context_.Get();\n}\n\nuint64_t ExceptionSnapshotMinidump::ThreadID() const {\n  DCHECK(initialized_.is_valid());\n  return minidump_exception_stream_.ThreadId;\n}\n\nuint32_t ExceptionSnapshotMinidump::Exception() const {\n  DCHECK(initialized_.is_valid());\n  return minidump_exception_stream_.ExceptionRecord.ExceptionCode;\n}\n\nuint32_t ExceptionSnapshotMinidump::ExceptionInfo() const {\n  DCHECK(initialized_.is_valid());\n  return minidump_exception_stream_.ExceptionRecord.ExceptionFlags;\n}\n\nuint64_t ExceptionSnapshotMinidump::ExceptionAddress() const {\n  DCHECK(initialized_.is_valid());\n  return minidump_exception_stream_.ExceptionRecord.ExceptionAddress;\n}\n\nconst std::vector<uint64_t>& ExceptionSnapshotMinidump::Codes() const {\n  DCHECK(initialized_.is_valid());\n  return exception_information_;\n}\n\nstd::vector<const MemorySnapshot*> ExceptionSnapshotMinidump::ExtraMemory()\n    const {\n  DCHECK(initialized_.is_valid());\n  return std::vector<const MemorySnapshot*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/exception_snapshot_minidump.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/minidump/minidump_context_converter.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/initialization_state.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief An ExceptionSnapshot based on a minidump file.\nclass ExceptionSnapshotMinidump final : public ExceptionSnapshot {\n public:\n  ExceptionSnapshotMinidump();\n\n  ExceptionSnapshotMinidump(const ExceptionSnapshotMinidump&) = delete;\n  ExceptionSnapshotMinidump& operator=(const ExceptionSnapshotMinidump&) =\n      delete;\n\n  ~ExceptionSnapshotMinidump() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] file_reader A file reader corresponding to a minidump file.\n  //!     The file reader must support seeking.\n  //! \\param[in] arch The CPU architecture of this snapshot.\n  //! \\param[in] minidump_exception_stream_rva The file offset in \\a file_reader\n  //!     at which the MINIDUMP_EXCEPTION_STREAM structure is located.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(FileReaderInterface* file_reader,\n                  CPUArchitecture arch,\n                  RVA minidump_exception_stream_rva);\n\n  // ExceptionSnapshot:\n  const CPUContext* Context() const override;\n  uint64_t ThreadID() const override;\n  uint32_t Exception() const override;\n  uint32_t ExceptionInfo() const override;\n  uint64_t ExceptionAddress() const override;\n  const std::vector<uint64_t>& Codes() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n  // Allow callers to explicitly check whether this exception snapshot has been\n  // initialized.\n  bool IsValid() const { return initialized_.is_valid(); }\n\n private:\n  MINIDUMP_EXCEPTION_STREAM minidump_exception_stream_;\n  MinidumpContextConverter context_;\n  std::vector<uint64_t> exception_information_;\n  InitializationState initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_\n"
  },
  {
    "path": "snapshot/minidump/memory_snapshot_minidump.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/memory_snapshot_minidump.h\"\n\n#include <memory>\n\n#include \"base/numerics/safe_math.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nMemorySnapshotMinidump::MemorySnapshotMinidump()\n    : MemorySnapshot(),\n      address_(0),\n      data_(),\n      initialized_() {}\n\nMemorySnapshotMinidump::~MemorySnapshotMinidump() {}\n\nbool MemorySnapshotMinidump::Initialize(FileReaderInterface* file_reader,\n                                        RVA location) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  MINIDUMP_MEMORY_DESCRIPTOR descriptor;\n\n  if (!file_reader->SeekSet(location)) {\n    return false;\n  }\n\n  if (!file_reader->ReadExactly(&descriptor, sizeof(descriptor))) {\n    return false;\n  }\n\n  address_ = descriptor.StartOfMemoryRange;\n  data_.resize(descriptor.Memory.DataSize);\n\n  if (!file_reader->SeekSet(descriptor.Memory.Rva)) {\n    return false;\n  }\n\n  if (!file_reader->ReadExactly(data_.data(), data_.size())) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nuint64_t MemorySnapshotMinidump::Address() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return address_;\n}\n\nsize_t MemorySnapshotMinidump::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return data_.size();\n}\n\nbool MemorySnapshotMinidump::Read(Delegate* delegate) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return delegate->MemorySnapshotDelegateRead(\n      const_cast<uint8_t*>(data_.data()), data_.size());\n}\n\nconst MemorySnapshot* MemorySnapshotMinidump::MergeWithOtherSnapshot(\n    const MemorySnapshot* other) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // TODO: Verify type of other\n  auto other_cast = reinterpret_cast<const MemorySnapshotMinidump*>(other);\n\n  INITIALIZATION_STATE_DCHECK_VALID(other_cast->initialized_);\n\n  if (other_cast->address_ < address_) {\n    return other_cast->MergeWithOtherSnapshot(this);\n  }\n\n  CheckedRange<uint64_t, size_t> merged(0, 0);\n  if (!LoggingDetermineMergedRange(this, other, &merged)) {\n    return nullptr;\n  }\n\n  auto result = std::make_unique<MemorySnapshotMinidump>();\n  result->address_ = merged.base();\n  result->data_ = data_;\n\n  if (result->data_.size() == merged.size()) {\n    return result.release();\n  }\n\n  result->data_.resize(\n      base::checked_cast<size_t>(other_cast->address_ - address_));\n  result->data_.insert(result->data_.end(), other_cast->data_.begin(),\n                       other_cast->data_.end());\n  return result.release();\n}\n\n} // namespace internal\n} // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/memory_snapshot_minidump.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include <vector>\n\n#include \"snapshot/memory_snapshot.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\nclass MemorySnapshotMinidump : public MemorySnapshot {\n public:\n  MemorySnapshotMinidump();\n\n  MemorySnapshotMinidump(const MemorySnapshotMinidump&) = delete;\n  MemorySnapshotMinidump& operator=(const MemorySnapshotMinidump&) = delete;\n\n  ~MemorySnapshotMinidump() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] file_reader A file reader corresponding to a minidump file.\n  //!     The file reader must support seeking.\n  //! \\param[in] location The location within the file where we will find a\n  //!     MINIDUMP_MEMORY_DESCRIPTOR from which to initialize this object.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(FileReaderInterface* file_reader, RVA location);\n\n  uint64_t Address() const override;\n  size_t Size() const override;\n  bool Read(Delegate* delegate) const override;\n  const MemorySnapshot* MergeWithOtherSnapshot(\n      const MemorySnapshot* other) const override;\n\n private:\n  uint64_t address_;\n  std::vector<uint8_t> data_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_\n"
  },
  {
    "path": "snapshot/minidump/minidump_annotation_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/minidump_annotation_reader.h\"\n\n#include <stdint.h>\n\n#include \"base/logging.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"snapshot/minidump/minidump_string_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\nbool ReadMinidumpByteArray(FileReaderInterface* file_reader,\n                           RVA rva,\n                           std::vector<uint8_t>* data) {\n  if (rva == 0) {\n    data->clear();\n    return true;\n  }\n\n  if (!file_reader->SeekSet(rva)) {\n    return false;\n  }\n\n  uint32_t length;\n  if (!file_reader->ReadExactly(&length, sizeof(length))) {\n    return false;\n  }\n\n  std::vector<uint8_t> local_data(length);\n  if (!file_reader->ReadExactly(local_data.data(), length)) {\n    return false;\n  }\n\n  data->swap(local_data);\n  return true;\n}\n\n}  // namespace\n\nbool ReadMinidumpAnnotationList(FileReaderInterface* file_reader,\n                                const MINIDUMP_LOCATION_DESCRIPTOR& location,\n                                std::vector<AnnotationSnapshot>* list) {\n  if (location.Rva == 0) {\n    list->clear();\n    return true;\n  }\n\n  if (location.DataSize < sizeof(MinidumpAnnotationList)) {\n    LOG(ERROR) << \"annotation list size mismatch\";\n    return false;\n  }\n\n  if (!file_reader->SeekSet(location.Rva)) {\n    return false;\n  }\n\n  uint32_t count;\n  if (!file_reader->ReadExactly(&count, sizeof(count))) {\n    return false;\n  }\n\n  if (location.DataSize !=\n      sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation)) {\n    LOG(ERROR) << \"annotation object size mismatch\";\n    return false;\n  }\n\n  std::vector<MinidumpAnnotation> minidump_annotations(count);\n  if (!file_reader->ReadExactly(minidump_annotations.data(),\n                                count * sizeof(MinidumpAnnotation))) {\n    return false;\n  }\n\n  std::vector<AnnotationSnapshot> annotations;\n  annotations.reserve(count);\n\n  for (size_t i = 0; i < count; ++i) {\n    const MinidumpAnnotation* minidump_annotation = &minidump_annotations[i];\n\n    AnnotationSnapshot annotation;\n    // The client-exposed size of this field is 16-bit, but the minidump field\n    // is 32-bit for padding. Take just the lower part.\n    annotation.type = static_cast<uint16_t>(minidump_annotation->type);\n\n    if (!ReadMinidumpUTF8String(\n            file_reader, minidump_annotation->name, &annotation.name)) {\n      return false;\n    }\n\n    if (!ReadMinidumpByteArray(\n            file_reader, minidump_annotation->value, &annotation.value)) {\n      return false;\n    }\n\n    annotations.push_back(std::move(annotation));\n  }\n\n  list->swap(annotations);\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/minidump_annotation_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_\n#define SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include <vector>\n\n#include \"snapshot/annotation_snapshot.h\"\n#include \"util/file/file_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Reads a MinidumpAnnotationList from a minidump file at \\a location\n//!     in \\a file_reader, and returns it in \\a list.\n//!\n//! \\return `true` on success, with \\a list set by replacing its contents.\n//!     `false` on failure, with a message logged.\nbool ReadMinidumpAnnotationList(FileReaderInterface* file_reader,\n                                const MINIDUMP_LOCATION_DESCRIPTOR& location,\n                                std::vector<AnnotationSnapshot>* list);\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_\n"
  },
  {
    "path": "snapshot/minidump/minidump_context_converter.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/minidump_context_converter.h\"\n\n#include <string.h>\n\n#include <iterator>\n\n#include \"base/logging.h\"\n#include \"minidump/minidump_context.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nMinidumpContextConverter::MinidumpContextConverter() : initialized_() {\n  context_.architecture = CPUArchitecture::kCPUArchitectureUnknown;\n}\n\nbool MinidumpContextConverter::Initialize(\n    CPUArchitecture arch,\n    const std::vector<unsigned char>& minidump_context) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (minidump_context.size() == 0) {\n    // Thread has no context.\n    context_.architecture = CPUArchitecture::kCPUArchitectureUnknown;\n    INITIALIZATION_STATE_SET_VALID(initialized_);\n    return true;\n  }\n\n  context_.architecture = arch;\n\n  if (context_.architecture == CPUArchitecture::kCPUArchitectureX86) {\n    context_memory_.resize(sizeof(CPUContextX86));\n    context_.x86 = reinterpret_cast<CPUContextX86*>(context_memory_.data());\n    const MinidumpContextX86* src =\n        reinterpret_cast<const MinidumpContextX86*>(minidump_context.data());\n    if (minidump_context.size() < sizeof(MinidumpContextX86)) {\n      return false;\n    }\n\n    if (!(src->context_flags & kMinidumpContextX86)) {\n      return false;\n    }\n\n    if (src->context_flags & kMinidumpContextX86Extended) {\n      context_.x86->fxsave = src->fxsave;\n    } else if (src->context_flags & kMinidumpContextX86FloatingPoint) {\n      CPUContextX86::FsaveToFxsave(src->fsave, &context_.x86->fxsave);\n    }\n\n    context_.x86->eax = src->eax;\n    context_.x86->ebx = src->ebx;\n    context_.x86->ecx = src->ecx;\n    context_.x86->edx = src->edx;\n    context_.x86->edi = src->edi;\n    context_.x86->esi = src->esi;\n    context_.x86->ebp = src->ebp;\n    context_.x86->esp = src->esp;\n    context_.x86->eip = src->eip;\n    context_.x86->eflags = src->eflags;\n    context_.x86->cs = static_cast<uint16_t>(src->cs);\n    context_.x86->ds = static_cast<uint16_t>(src->ds);\n    context_.x86->es = static_cast<uint16_t>(src->es);\n    context_.x86->fs = static_cast<uint16_t>(src->fs);\n    context_.x86->gs = static_cast<uint16_t>(src->gs);\n    context_.x86->ss = static_cast<uint16_t>(src->ss);\n    context_.x86->dr0 = src->dr0;\n    context_.x86->dr1 = src->dr1;\n    context_.x86->dr2 = src->dr2;\n    context_.x86->dr3 = src->dr3;\n    context_.x86->dr6 = src->dr6;\n    context_.x86->dr7 = src->dr7;\n\n    // Minidump passes no value for dr4/5. Our output context has space for\n    // them. According to spec they're obsolete, but when present read as\n    // aliases for dr6/7, so we'll do this.\n    context_.x86->dr4 = src->dr6;\n    context_.x86->dr5 = src->dr7;\n  } else if (context_.architecture == CPUArchitecture::kCPUArchitectureX86_64) {\n    context_memory_.resize(sizeof(CPUContextX86_64));\n    context_.x86_64 =\n        reinterpret_cast<CPUContextX86_64*>(context_memory_.data());\n    const MinidumpContextAMD64* src =\n        reinterpret_cast<const MinidumpContextAMD64*>(minidump_context.data());\n    if (minidump_context.size() < sizeof(MinidumpContextAMD64)) {\n      return false;\n    }\n\n    if (!(src->context_flags & kMinidumpContextAMD64)) {\n      return false;\n    }\n\n    context_.x86_64->fxsave = src->fxsave;\n    context_.x86_64->cs = src->cs;\n    context_.x86_64->fs = src->fs;\n    context_.x86_64->gs = src->gs;\n    context_.x86_64->rflags = src->eflags;\n    context_.x86_64->dr0 = src->dr0;\n    context_.x86_64->dr1 = src->dr1;\n    context_.x86_64->dr2 = src->dr2;\n    context_.x86_64->dr3 = src->dr3;\n    context_.x86_64->dr6 = src->dr6;\n    context_.x86_64->dr7 = src->dr7;\n    context_.x86_64->rax = src->rax;\n    context_.x86_64->rcx = src->rcx;\n    context_.x86_64->rdx = src->rdx;\n    context_.x86_64->rbx = src->rbx;\n    context_.x86_64->rsp = src->rsp;\n    context_.x86_64->rbp = src->rbp;\n    context_.x86_64->rsi = src->rsi;\n    context_.x86_64->rdi = src->rdi;\n    context_.x86_64->r8 = src->r8;\n    context_.x86_64->r9 = src->r9;\n    context_.x86_64->r10 = src->r10;\n    context_.x86_64->r11 = src->r11;\n    context_.x86_64->r12 = src->r12;\n    context_.x86_64->r13 = src->r13;\n    context_.x86_64->r14 = src->r14;\n    context_.x86_64->r15 = src->r15;\n    context_.x86_64->rip = src->rip;\n\n    // See comments on x86 above.\n    context_.x86_64->dr4 = src->dr6;\n    context_.x86_64->dr5 = src->dr7;\n  } else if (context_.architecture == CPUArchitecture::kCPUArchitectureARM) {\n    context_memory_.resize(sizeof(CPUContextARM));\n    context_.arm = reinterpret_cast<CPUContextARM*>(context_memory_.data());\n    const MinidumpContextARM* src =\n        reinterpret_cast<const MinidumpContextARM*>(minidump_context.data());\n    if (minidump_context.size() < sizeof(MinidumpContextARM)) {\n      return false;\n    }\n\n    if (!(src->context_flags & kMinidumpContextARM)) {\n      return false;\n    }\n\n    for (size_t i = 0; i < std::size(src->regs); i++) {\n      context_.arm->regs[i] = src->regs[i];\n    }\n\n    context_.arm->fp = src->fp;\n    context_.arm->ip = src->ip;\n    context_.arm->sp = src->sp;\n    context_.arm->lr = src->lr;\n    context_.arm->pc = src->pc;\n    context_.arm->cpsr = src->cpsr;\n    context_.arm->vfp_regs.fpscr = src->fpscr;\n\n    for (size_t i = 0; i < std::size(src->vfp); i++) {\n      context_.arm->vfp_regs.vfp[i] = src->vfp[i];\n    }\n\n    context_.arm->have_fpa_regs = false;\n    context_.arm->have_vfp_regs =\n        !!(src->context_flags & kMinidumpContextARMVFP);\n  } else if (context_.architecture == CPUArchitecture::kCPUArchitectureARM64) {\n    context_memory_.resize(sizeof(CPUContextARM64));\n    context_.arm64 = reinterpret_cast<CPUContextARM64*>(context_memory_.data());\n    const MinidumpContextARM64* src =\n        reinterpret_cast<const MinidumpContextARM64*>(minidump_context.data());\n    if (minidump_context.size() < sizeof(MinidumpContextARM64)) {\n      return false;\n    }\n\n    if (!(src->context_flags & kMinidumpContextARM64)) {\n      return false;\n    }\n\n    for (size_t i = 0; i < std::size(src->regs); i++) {\n      context_.arm64->regs[i] = src->regs[i];\n    }\n\n    context_.arm64->regs[29] = src->fp;\n    context_.arm64->regs[30] = src->lr;\n\n    for (size_t i = 0; i < std::size(src->fpsimd); i++) {\n      context_.arm64->fpsimd[i] = src->fpsimd[i];\n    }\n\n    context_.arm64->sp = src->sp;\n    context_.arm64->pc = src->pc;\n    context_.arm64->fpcr = src->fpcr;\n    context_.arm64->fpsr = src->fpsr;\n    context_.arm64->spsr = src->cpsr;\n  } else if (context_.architecture == CPUArchitecture::kCPUArchitectureMIPSEL) {\n    context_memory_.resize(sizeof(CPUContextMIPS));\n    context_.mipsel = reinterpret_cast<CPUContextMIPS*>(context_memory_.data());\n    const MinidumpContextMIPS* src =\n        reinterpret_cast<const MinidumpContextMIPS*>(minidump_context.data());\n    if (minidump_context.size() < sizeof(MinidumpContextMIPS)) {\n      return false;\n    }\n\n    if (!(src->context_flags & kMinidumpContextMIPS)) {\n      return false;\n    }\n\n    for (size_t i = 0; i < std::size(src->regs); i++) {\n      context_.mipsel->regs[i] = src->regs[i];\n    }\n\n    context_.mipsel->mdhi = static_cast<uint32_t>(src->mdhi);\n    context_.mipsel->mdlo = static_cast<uint32_t>(src->mdlo);\n    context_.mipsel->dsp_control = src->dsp_control;\n\n    for (size_t i = 0; i < std::size(src->hi); i++) {\n      context_.mipsel->hi[i] = src->hi[i];\n      context_.mipsel->lo[i] = src->lo[i];\n    }\n\n    context_.mipsel->cp0_epc = static_cast<uint32_t>(src->epc);\n    context_.mipsel->cp0_badvaddr = static_cast<uint32_t>(src->badvaddr);\n    context_.mipsel->cp0_status = src->status;\n    context_.mipsel->cp0_cause = src->cause;\n    context_.mipsel->fpcsr = src->fpcsr;\n    context_.mipsel->fir = src->fir;\n\n    memcpy(&context_.mipsel->fpregs, &src->fpregs, sizeof(src->fpregs));\n  } else if (context_.architecture ==\n             CPUArchitecture::kCPUArchitectureMIPS64EL) {\n    context_memory_.resize(sizeof(CPUContextMIPS64));\n    context_.mips64 =\n        reinterpret_cast<CPUContextMIPS64*>(context_memory_.data());\n    const MinidumpContextMIPS64* src =\n        reinterpret_cast<const MinidumpContextMIPS64*>(minidump_context.data());\n    if (minidump_context.size() < sizeof(MinidumpContextMIPS64)) {\n      return false;\n    }\n\n    if (!(src->context_flags & kMinidumpContextMIPS64)) {\n      return false;\n    }\n\n    for (size_t i = 0; i < std::size(src->regs); i++) {\n      context_.mips64->regs[i] = src->regs[i];\n    }\n\n    context_.mips64->mdhi = src->mdhi;\n    context_.mips64->mdlo = src->mdlo;\n    context_.mips64->dsp_control = src->dsp_control;\n\n    for (size_t i = 0; i < std::size(src->hi); i++) {\n      context_.mips64->hi[i] = src->hi[i];\n      context_.mips64->lo[i] = src->lo[i];\n    }\n\n    context_.mips64->cp0_epc = src->epc;\n    context_.mips64->cp0_badvaddr = src->badvaddr;\n    context_.mips64->cp0_status = src->status;\n    context_.mips64->cp0_cause = src->cause;\n    context_.mips64->fpcsr = src->fpcsr;\n    context_.mips64->fir = src->fir;\n\n    memcpy(&context_.mips64->fpregs, &src->fpregs, sizeof(src->fpregs));\n  } else if (context_.architecture ==\n             CPUArchitecture::kCPUArchitectureRISCV64) {\n    context_memory_.resize(sizeof(CPUContextRISCV64));\n    context_.riscv64 =\n        reinterpret_cast<CPUContextRISCV64*>(context_memory_.data());\n    const MinidumpContextRISCV64* src =\n        reinterpret_cast<const MinidumpContextRISCV64*>(\n            minidump_context.data());\n    if (minidump_context.size() < sizeof(MinidumpContextRISCV64)) {\n      return false;\n    }\n\n    if (!(src->context_flags & kMinidumpContextRISCV64)) {\n      return false;\n    }\n\n    context_.riscv64->pc = src->pc;\n\n    static_assert(sizeof(context_.riscv64->regs) == sizeof(src->regs),\n                  \"GPR size mismatch\");\n    memcpy(&context_.riscv64->regs, &src->regs, sizeof(src->regs));\n\n    static_assert(sizeof(context_.riscv64->fpregs) == sizeof(src->fpregs),\n                  \"FPR size mismatch\");\n    memcpy(&context_.riscv64->fpregs, &src->fpregs, sizeof(src->fpregs));\n\n    context_.riscv64->fcsr = src->fcsr;\n  } else {\n    // Architecture is listed as \"unknown\".\n    DLOG(ERROR) << \"Unknown architecture\";\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/minidump_context_converter.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_\n\n#include <vector>\n\n#include \"snapshot/cpu_context.h\"\n#include \"util/misc/initialization_state.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nclass MinidumpContextConverter {\n public:\n  MinidumpContextConverter();\n\n  bool Initialize(CPUArchitecture arch,\n                  const std::vector<unsigned char>& minidump_context);\n  const CPUContext* Get() const {\n    INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n    return &context_;\n  }\n\n private:\n  CPUContext context_;\n  std::vector<unsigned char> context_memory_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_\n"
  },
  {
    "path": "snapshot/minidump/minidump_simple_string_dictionary_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/minidump_simple_string_dictionary_reader.h\"\n\n#include <stdint.h>\n\n#include <utility>\n#include <vector>\n\n#include \"base/logging.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"snapshot/minidump/minidump_string_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nbool ReadMinidumpSimpleStringDictionary(\n    FileReaderInterface* file_reader,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location,\n    std::map<std::string, std::string>* dictionary) {\n  if (location.Rva == 0) {\n    dictionary->clear();\n    return true;\n  }\n\n  if (location.DataSize < sizeof(MinidumpSimpleStringDictionary)) {\n    LOG(ERROR) << \"simple_string_dictionary size mismatch\";\n    return false;\n  }\n\n  if (!file_reader->SeekSet(location.Rva)) {\n    return false;\n  }\n\n  uint32_t entry_count;\n  if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) {\n    return false;\n  }\n\n  if (location.DataSize !=\n      sizeof(MinidumpSimpleStringDictionary) +\n          entry_count * sizeof(MinidumpSimpleStringDictionaryEntry)) {\n    LOG(ERROR) << \"simple_string_dictionary size mismatch\";\n    return false;\n  }\n\n  std::vector<MinidumpSimpleStringDictionaryEntry> entries(entry_count);\n  if (!file_reader->ReadExactly(&entries[0],\n                                entry_count * sizeof(entries[0]))) {\n    return false;\n  }\n\n  std::map<std::string, std::string> local_dictionary;\n  for (const MinidumpSimpleStringDictionaryEntry& entry : entries) {\n    std::string key;\n    if (!ReadMinidumpUTF8String(file_reader, entry.key, &key)) {\n      return false;\n    }\n\n    std::string value;\n    if (!ReadMinidumpUTF8String(file_reader, entry.value, &value)) {\n      return false;\n    }\n\n    if (!local_dictionary.insert(std::make_pair(key, value)).second) {\n      LOG(ERROR) << \"duplicate key \" << key;\n      return false;\n    }\n  }\n\n  dictionary->swap(local_dictionary);\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/minidump_simple_string_dictionary_reader.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include <map>\n#include <string>\n\n#include \"util/file/file_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Reads a MinidumpSimpleStringDictionary from a minidump file \\a\n//!     location in \\a file_reader, and returns it in \\a dictionary.\n//!\n//! \\return `true` on success, with \\a dictionary set by replacing its contents.\n//!     `false` on failure, with a message logged.\nbool ReadMinidumpSimpleStringDictionary(\n    FileReaderInterface* file_reader,\n    const MINIDUMP_LOCATION_DESCRIPTOR& location,\n    std::map<std::string, std::string>* dictionary);\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_\n"
  },
  {
    "path": "snapshot/minidump/minidump_stream.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_\n\n#include <stdint.h>\n\n#include <vector>\n\n\nnamespace crashpad {\n\n//! \\brief Stores a minidump stream along with its stream ID.\nclass MinidumpStream {\n public:\n  MinidumpStream(uint32_t stream_type, std::vector<uint8_t> data)\n      : stream_type_(stream_type), data_(data) {}\n\n  MinidumpStream(const MinidumpStream&) = delete;\n  MinidumpStream& operator=(const MinidumpStream&) = delete;\n\n  uint32_t stream_type() const { return stream_type_; }\n  const std::vector<uint8_t>& data() const { return data_; }\n\n private:\n  uint32_t stream_type_;\n  std::vector<uint8_t> data_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_\n"
  },
  {
    "path": "snapshot/minidump/minidump_string_list_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/minidump_string_list_reader.h\"\n\n#include <stdint.h>\n\n#include \"base/logging.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"snapshot/minidump/minidump_string_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nbool ReadMinidumpStringList(FileReaderInterface* file_reader,\n                            const MINIDUMP_LOCATION_DESCRIPTOR& location,\n                            std::vector<std::string>* list) {\n  if (location.Rva == 0) {\n    list->clear();\n    return true;\n  }\n\n  if (location.DataSize < sizeof(MinidumpRVAList)) {\n    LOG(ERROR) << \"string_list size mismatch\";\n    return false;\n  }\n\n  if (!file_reader->SeekSet(location.Rva)) {\n    return false;\n  }\n\n  uint32_t entry_count;\n  if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) {\n    return false;\n  }\n\n  if (location.DataSize !=\n      sizeof(MinidumpRVAList) + entry_count * sizeof(RVA)) {\n    LOG(ERROR) << \"string_list size mismatch\";\n    return false;\n  }\n\n  std::vector<RVA> rvas(entry_count);\n  if (!file_reader->ReadExactly(&rvas[0], entry_count * sizeof(rvas[0]))) {\n    return false;\n  }\n\n  std::vector<std::string> local_list;\n  for (RVA rva : rvas) {\n    std::string element;\n    if (!ReadMinidumpUTF8String(file_reader, rva, &element)) {\n      return false;\n    }\n\n    local_list.push_back(element);\n  }\n\n  list->swap(local_list);\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/minidump_string_list_reader.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include <string>\n#include <vector>\n\n#include \"util/file/file_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Reads a list of MinidumpUTF8String objects in a MinidumpRVAList from\n//!     a minidump file \\a location in \\a file_reader, and returns it in \\a\n//!     list.\n//!\n//! \\return `true` on success, with \\a list set by replacing its contents.\n//!     `false` on failure, with a message logged.\nbool ReadMinidumpStringList(FileReaderInterface* file_reader,\n                            const MINIDUMP_LOCATION_DESCRIPTOR& location,\n                            std::vector<std::string>* list);\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_\n"
  },
  {
    "path": "snapshot/minidump/minidump_string_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/minidump_string_reader.h\"\n\n#include <stdint.h>\n\n#include \"base/strings/utf_string_conversions.h\"\n#include \"minidump/minidump_extensions.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\ntemplate<typename StringType, typename RVAType>\nbool ReadMinidumpString(FileReaderInterface* file_reader,\n                        RVAType rva,\n                        StringType* string) {\n  if (rva == 0) {\n    string->clear();\n    return true;\n  }\n\n  if (!file_reader->SeekSet(rva)) {\n    return false;\n  }\n\n  uint32_t string_size;\n  if (!file_reader->ReadExactly(&string_size, sizeof(string_size))) {\n    return false;\n  }\n\n  StringType local_string(string_size / sizeof((*string)[0]), '\\0');\n  if (!file_reader->ReadExactly(&local_string[0], string_size)) {\n    return false;\n  }\n\n  string->swap(local_string);\n  return true;\n}\n\n}  // namespace\n\nbool ReadMinidumpUTF8String(FileReaderInterface* file_reader,\n                            RVA rva,\n                            std::string* string) {\n  return ReadMinidumpString(file_reader, rva, string);\n}\n\nbool ReadMinidumpUTF8String(FileReaderInterface* file_reader,\n                            RVA64 rva,\n                            std::string* string) {\n  return ReadMinidumpString(file_reader, rva, string);\n}\n\nbool ReadMinidumpUTF16String(FileReaderInterface* file_reader,\n                             RVA rva,\n                             std::u16string* string) {\n  return ReadMinidumpString(file_reader, rva, string);\n}\n\nbool ReadMinidumpUTF16String(FileReaderInterface* file_reader,\n                             RVA64 rva,\n                             std::u16string* string) {\n  return ReadMinidumpString(file_reader, rva, string);\n}\n\nbool ReadMinidumpUTF16String(FileReaderInterface* file_reader,\n                            RVA rva,\n                            std::string* string) {\n  std::u16string string_raw;\n\n  if (!ReadMinidumpString(file_reader, rva, &string_raw)) {\n    return false;\n  }\n\n  base::UTF16ToUTF8(string_raw.data(), string_raw.size(), string);\n\n  return true;\n}\n\nbool ReadMinidumpUTF16String(FileReaderInterface* file_reader,\n                             RVA64 rva,\n                             std::string* string) {\n  std::u16string string_raw;\n\n  if (!ReadMinidumpString(file_reader, rva, &string_raw)) {\n    return false;\n  }\n\n  base::UTF16ToUTF8(string_raw.data(), string_raw.size(), string);\n\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/minidump_string_reader.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n\n#include <string>\n\n#include \"base/strings/utf_string_conversions.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"util/file/file_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Reads a MinidumpUTF8String from a minidump file at offset \\a rva in\n//!     \\a file_reader, and returns it in \\a string.\n//!\n//! \\return `true` on success, with \\a string set. `false` on failure, with a\n//!     message logged.\nbool ReadMinidumpUTF8String(FileReaderInterface* file_reader,\n                            RVA rva,\n                            std::string* string);\n\n//! \\brief 64-bit specialization of ReadMinidumpUTF8String.\nbool ReadMinidumpUTF8String(FileReaderInterface* file_reader,\n                            RVA64 rva,\n                            std::string* string);\n\n//! \\brief Reads a MinidumpUTF16String from a minidump file at offset \\a rva in\n//!     \\a file_reader, and returns it in \\a string.\n//!\n//! \\return `true` on success, with \\a string set. `false` on failure, with a\n//!     message logged.\nbool ReadMinidumpUTF16String(FileReaderInterface* file_reader,\n                             RVA rva,\n                             std::u16string* string);\n\n//! \\brief 64-bit specialization of ReadMinidumpUTF16String.\nbool ReadMinidumpUTF16String(FileReaderInterface* file_reader,\n                             RVA64 rva,\n                             std::u16string* string);\n\n//! \\brief Reads a MinidumpUTF16String from a minidump file at offset \\a rva in\n//!     \\a file_reader, and returns it in \\a string.\n//!\n//! \\return `true` on success, with \\a string set. `false` on failure, with a\n//!     message logged.\nbool ReadMinidumpUTF16String(FileReaderInterface* file_reader,\n                             RVA rva,\n                             std::string* string);\n\n//! \\brief 64-bit specialization of ReadMinidumpUTF16String.\nbool ReadMinidumpUTF16String(FileReaderInterface* file_reader,\n                             RVA64 rva,\n                             std::string* string);\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_\n"
  },
  {
    "path": "snapshot/minidump/module_snapshot_minidump.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/module_snapshot_minidump.h\"\n\n#include <stddef.h>\n#include <string.h>\n\n#include <iterator>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"snapshot/minidump/minidump_annotation_reader.h\"\n#include \"snapshot/minidump/minidump_simple_string_dictionary_reader.h\"\n#include \"snapshot/minidump/minidump_string_list_reader.h\"\n#include \"snapshot/minidump/minidump_string_reader.h\"\n#include \"util/misc/pdb_structures.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nModuleSnapshotMinidump::ModuleSnapshotMinidump()\n    : ModuleSnapshot(),\n      minidump_module_(),\n      annotations_vector_(),\n      annotations_simple_map_(),\n      annotation_objects_(),\n      uuid_(),\n      build_id_(),\n      name_(),\n      debug_file_name_(),\n      age_(0),\n      initialized_() {}\n\nModuleSnapshotMinidump::~ModuleSnapshotMinidump() {}\n\nbool ModuleSnapshotMinidump::Initialize(\n    FileReaderInterface* file_reader,\n    RVA minidump_module_rva,\n    const MINIDUMP_LOCATION_DESCRIPTOR*\n        minidump_module_crashpad_info_location) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!file_reader->SeekSet(minidump_module_rva)) {\n    return false;\n  }\n\n  if (!file_reader->ReadExactly(&minidump_module_, sizeof(minidump_module_))) {\n    return false;\n  }\n\n  if (!InitializeModuleCrashpadInfo(file_reader,\n                                    minidump_module_crashpad_info_location)) {\n    return false;\n  }\n\n  ReadMinidumpUTF16String(file_reader, minidump_module_.ModuleNameRva, &name_);\n\n  if (minidump_module_.CvRecord.Rva != 0 &&\n      !InitializeModuleCodeView(file_reader)) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ModuleSnapshotMinidump::InitializeModuleCodeView(\n    FileReaderInterface* file_reader) {\n  uint32_t signature;\n\n  DCHECK_NE(minidump_module_.CvRecord.Rva, 0u);\n\n  if (minidump_module_.CvRecord.DataSize < sizeof(signature)) {\n    LOG(ERROR) << \"CodeView record in module too small to contain signature\";\n    return false;\n  }\n\n  if (!file_reader->SeekSet(minidump_module_.CvRecord.Rva)) {\n    return false;\n  }\n\n  std::vector<uint8_t> cv_record;\n  cv_record.resize(minidump_module_.CvRecord.DataSize);\n\n  if (!file_reader->ReadExactly(cv_record.data(), cv_record.size())) {\n    return false;\n  }\n\n  signature = *reinterpret_cast<uint32_t*>(cv_record.data());\n\n  if (signature == CodeViewRecordPDB70::kSignature) {\n    if (cv_record.size() < offsetof(CodeViewRecordPDB70, pdb_name) + 1) {\n      LOG(ERROR) << \"CodeView record in module marked as PDB70 but too small\";\n      return false;\n    }\n\n    auto cv_record_pdb70 =\n        reinterpret_cast<CodeViewRecordPDB70*>(cv_record.data());\n\n    age_ = cv_record_pdb70->age;\n    uuid_ = cv_record_pdb70->uuid;\n\n    if (cv_record.back() != '\\0') {\n      LOG(ERROR) << \"CodeView record marked as PDB70 missing NUL-terminator in \"\n                    \"pdb_name\";\n      return false;\n    }\n\n    std::copy(cv_record.begin() + offsetof(CodeViewRecordPDB70, pdb_name),\n              cv_record.end() - 1,\n              std::back_inserter(debug_file_name_));\n    return true;\n  }\n\n  if (signature == CodeViewRecordBuildID::kSignature) {\n    std::copy(cv_record.begin() + offsetof(CodeViewRecordBuildID, build_id),\n              cv_record.end(),\n              std::back_inserter(build_id_));\n    return true;\n  }\n\n  LOG(ERROR) << \"Bad CodeView signature in module\";\n  return false;\n}\n\nstd::string ModuleSnapshotMinidump::Name() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return name_;\n}\n\nuint64_t ModuleSnapshotMinidump::Address() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_module_.BaseOfImage;\n}\n\nuint64_t ModuleSnapshotMinidump::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_module_.SizeOfImage;\n}\n\ntime_t ModuleSnapshotMinidump::Timestamp() const {\n  return minidump_module_.TimeDateStamp;\n}\n\nvoid ModuleSnapshotMinidump::FileVersion(uint16_t* version_0,\n                                         uint16_t* version_1,\n                                         uint16_t* version_2,\n                                         uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  uint32_t version_01 = minidump_module_.VersionInfo.dwFileVersionMS;\n  uint32_t version_23 = minidump_module_.VersionInfo.dwFileVersionLS;\n  *version_0 = static_cast<uint16_t>(version_01 >> 16);\n  *version_1 = static_cast<uint16_t>(version_01 & 0xFFFF);\n  *version_2 = static_cast<uint16_t>(version_23 >> 16);\n  *version_3 = static_cast<uint16_t>(version_23 & 0xFFFF);\n}\n\nvoid ModuleSnapshotMinidump::SourceVersion(uint16_t* version_0,\n                                           uint16_t* version_1,\n                                           uint16_t* version_2,\n                                           uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  uint32_t version_01 = minidump_module_.VersionInfo.dwProductVersionMS;\n  uint32_t version_23 = minidump_module_.VersionInfo.dwProductVersionLS;\n  *version_0 = static_cast<uint16_t>(version_01 >> 16);\n  *version_1 = static_cast<uint16_t>(version_01 & 0xFFFF);\n  *version_2 = static_cast<uint16_t>(version_23 >> 16);\n  *version_3 = static_cast<uint16_t>(version_23 & 0xFFFF);\n}\n\nModuleSnapshot::ModuleType ModuleSnapshotMinidump::GetModuleType() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  switch (minidump_module_.VersionInfo.dwFileType) {\n    case VFT_APP:\n      return kModuleTypeExecutable;\n    case VFT_DLL:\n      return kModuleTypeSharedLibrary;\n  }\n  return kModuleTypeUnknown;\n}\n\nvoid ModuleSnapshotMinidump::UUIDAndAge(crashpad::UUID* uuid,\n                                        uint32_t* age) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *uuid = uuid_;\n  *age = age_;\n}\n\nstd::string ModuleSnapshotMinidump::DebugFileName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return debug_file_name_;\n}\n\nstd::vector<uint8_t> ModuleSnapshotMinidump::BuildID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return build_id_;\n}\n\nstd::vector<std::string> ModuleSnapshotMinidump::AnnotationsVector() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_vector_;\n}\n\nstd::map<std::string, std::string>\nModuleSnapshotMinidump::AnnotationsSimpleMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_simple_map_;\n}\n\nstd::vector<AnnotationSnapshot> ModuleSnapshotMinidump::AnnotationObjects()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotation_objects_;\n}\n\nstd::set<CheckedRange<uint64_t>> ModuleSnapshotMinidump::ExtraMemoryRanges()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nstd::vector<const UserMinidumpStream*>\nModuleSnapshotMinidump::CustomMinidumpStreams() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nbool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo(\n    FileReaderInterface* file_reader,\n    const MINIDUMP_LOCATION_DESCRIPTOR*\n        minidump_module_crashpad_info_location) {\n  if (!minidump_module_crashpad_info_location ||\n      minidump_module_crashpad_info_location->Rva == 0) {\n    return true;\n  }\n\n  MinidumpModuleCrashpadInfo minidump_module_crashpad_info;\n  if (minidump_module_crashpad_info_location->DataSize <\n      sizeof(minidump_module_crashpad_info)) {\n    LOG(ERROR) << \"minidump_module_crashpad_info size mismatch\";\n    return false;\n  }\n\n  if (!file_reader->SeekSet(minidump_module_crashpad_info_location->Rva)) {\n    return false;\n  }\n\n  if (!file_reader->ReadExactly(&minidump_module_crashpad_info,\n                                sizeof(minidump_module_crashpad_info))) {\n    return false;\n  }\n\n  if (minidump_module_crashpad_info.version !=\n      MinidumpModuleCrashpadInfo::kVersion) {\n    LOG(ERROR) << \"minidump_module_crashpad_info version mismatch\";\n    return false;\n  }\n\n  if (!ReadMinidumpStringList(file_reader,\n                              minidump_module_crashpad_info.list_annotations,\n                              &annotations_vector_)) {\n    return false;\n  }\n\n  if (!ReadMinidumpSimpleStringDictionary(\n          file_reader,\n          minidump_module_crashpad_info.simple_annotations,\n          &annotations_simple_map_)) {\n    return false;\n  }\n\n  if (!ReadMinidumpAnnotationList(\n          file_reader,\n          minidump_module_crashpad_info.annotation_objects,\n          &annotation_objects_)) {\n    return false;\n  }\n\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/module_snapshot_minidump.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"snapshot/annotation_snapshot.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ModuleSnapshot based on a module in a minidump file.\nclass ModuleSnapshotMinidump final : public ModuleSnapshot {\n public:\n  ModuleSnapshotMinidump();\n\n  ModuleSnapshotMinidump(const ModuleSnapshotMinidump&) = delete;\n  ModuleSnapshotMinidump& operator=(const ModuleSnapshotMinidump&) = delete;\n\n  ~ModuleSnapshotMinidump() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] file_reader A file reader corresponding to a minidump file.\n  //!     The file reader must support seeking.\n  //! \\param[in] minidump_module_rva The file offset in \\a file_reader at which\n  //!     the module’s MINIDUMP_MODULE structure is located.\n  //! \\param[in] minidump_crashpad_module_info_location The location in \\a\n  //!     file_reader at which the module’s corresponding\n  //!     MinidumpModuleCrashpadInfo structure is located. If no such\n  //!     corresponding structure is available for a module, this may be\n  //!     `nullptr`.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(FileReaderInterface* file_reader,\n                  RVA minidump_module_rva,\n                  const MINIDUMP_LOCATION_DESCRIPTOR*\n                      minidump_crashpad_module_info_location);\n\n  // ModuleSnapshot:\n\n  std::string Name() const override;\n  uint64_t Address() const override;\n  uint64_t Size() const override;\n  time_t Timestamp() const override;\n  void FileVersion(uint16_t* version_0,\n                   uint16_t* version_1,\n                   uint16_t* version_2,\n                   uint16_t* version_3) const override;\n  void SourceVersion(uint16_t* version_0,\n                     uint16_t* version_1,\n                     uint16_t* version_2,\n                     uint16_t* version_3) const override;\n  ModuleType GetModuleType() const override;\n  void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;\n  std::string DebugFileName() const override;\n  std::vector<uint8_t> BuildID() const override;\n  std::vector<std::string> AnnotationsVector() const override;\n  std::map<std::string, std::string> AnnotationsSimpleMap() const override;\n  std::vector<AnnotationSnapshot> AnnotationObjects() const override;\n  std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;\n  std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;\n\n private:\n  // Initializes data carried in a MinidumpModuleCrashpadInfo structure on\n  // behalf of Initialize().\n  bool InitializeModuleCrashpadInfo(FileReaderInterface* file_reader,\n                                    const MINIDUMP_LOCATION_DESCRIPTOR*\n                                        minidump_module_crashpad_info_location);\n\n  // Initializes data from the CodeView record, which usually points toward\n  // debug symbols.\n  bool InitializeModuleCodeView(FileReaderInterface* file_reader);\n\n  MINIDUMP_MODULE minidump_module_;\n  std::vector<std::string> annotations_vector_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  std::vector<AnnotationSnapshot> annotation_objects_;\n  UUID uuid_;\n  std::vector<uint8_t> build_id_;\n  std::string name_;\n  std::string debug_file_name_;\n  uint32_t age_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_\n"
  },
  {
    "path": "snapshot/minidump/process_snapshot_minidump.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/process_snapshot_minidump.h\"\n\n#include <utility>\n\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"minidump/minidump_extensions.h\"\n#include \"snapshot/memory_map_region_snapshot.h\"\n#include \"snapshot/minidump/minidump_simple_string_dictionary_reader.h\"\n#include \"snapshot/minidump/minidump_string_reader.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\nclass MemoryMapRegionSnapshotMinidump : public MemoryMapRegionSnapshot {\n public:\n  MemoryMapRegionSnapshotMinidump(MINIDUMP_MEMORY_INFO info) : info_(info) {}\n  ~MemoryMapRegionSnapshotMinidump() override = default;\n\n  const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override {\n    return info_;\n  }\n\n private:\n  MINIDUMP_MEMORY_INFO info_;\n};\n\n}  // namespace internal\n\nProcessSnapshotMinidump::ProcessSnapshotMinidump()\n    : ProcessSnapshot(),\n      header_(),\n      stream_directory_(),\n      stream_map_(),\n      modules_(),\n      threads_(),\n      unloaded_modules_(),\n      mem_regions_(),\n      mem_regions_exposed_(),\n      custom_streams_(),\n      crashpad_info_(),\n      system_snapshot_(),\n      exception_snapshot_(),\n      arch_(CPUArchitecture::kCPUArchitectureUnknown),\n      annotations_simple_map_(),\n      file_reader_(nullptr),\n      process_id_(kInvalidProcessID),\n      create_time_(0),\n      user_time_(0),\n      kernel_time_(0),\n      initialized_() {}\n\nProcessSnapshotMinidump::~ProcessSnapshotMinidump() {}\n\nbool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  file_reader_ = file_reader;\n\n  if (!file_reader_->SeekSet(0)) {\n    return false;\n  }\n\n  if (!file_reader_->ReadExactly(&header_, sizeof(header_))) {\n    return false;\n  }\n\n  if (header_.Signature != MINIDUMP_SIGNATURE) {\n    LOG(ERROR) << \"minidump signature mismatch\";\n    return false;\n  }\n\n  if (header_.Version != MINIDUMP_VERSION) {\n    LOG(ERROR) << \"minidump version mismatch\";\n    return false;\n  }\n\n  if (!file_reader->SeekSet(header_.StreamDirectoryRva)) {\n    return false;\n  }\n\n  stream_directory_.resize(header_.NumberOfStreams);\n  if (!stream_directory_.empty() &&\n      !file_reader_->ReadExactly(\n          &stream_directory_[0],\n          header_.NumberOfStreams * sizeof(stream_directory_[0]))) {\n    return false;\n  }\n\n  for (const MINIDUMP_DIRECTORY& directory : stream_directory_) {\n    const MinidumpStreamType stream_type =\n        static_cast<MinidumpStreamType>(directory.StreamType);\n    if (stream_map_.find(stream_type) != stream_map_.end()) {\n      LOG(ERROR) << \"duplicate streams for type \" << directory.StreamType;\n      return false;\n    }\n\n    stream_map_[stream_type] = &directory.Location;\n  }\n\n  if (!InitializeCrashpadInfo() || !InitializeMiscInfo() ||\n      !InitializeModules() || !InitializeSystemSnapshot() ||\n      !InitializeMemoryInfo() || !InitializeExtraMemory() ||\n      !InitializeThreads() || !InitializeCustomMinidumpStreams() ||\n      !InitializeExceptionSnapshot()) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\ncrashpad::ProcessID ProcessSnapshotMinidump::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_id_;\n}\n\ncrashpad::ProcessID ProcessSnapshotMinidump::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nvoid ProcessSnapshotMinidump::SnapshotTime(timeval* snapshot_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  snapshot_time->tv_sec = header_.TimeDateStamp;\n  snapshot_time->tv_usec = 0;\n}\n\nvoid ProcessSnapshotMinidump::ProcessStartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  start_time->tv_sec = create_time_;\n  start_time->tv_usec = 0;\n}\n\nvoid ProcessSnapshotMinidump::ProcessCPUTimes(timeval* user_time,\n                                              timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  user_time->tv_sec = user_time_;\n  user_time->tv_usec = 0;\n  system_time->tv_sec = kernel_time_;\n  system_time->tv_usec = 0;\n}\n\nvoid ProcessSnapshotMinidump::ReportID(UUID* report_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *report_id = crashpad_info_.report_id;\n}\n\nvoid ProcessSnapshotMinidump::ClientID(UUID* client_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *client_id = crashpad_info_.client_id;\n}\n\nconst std::map<std::string, std::string>&\nProcessSnapshotMinidump::AnnotationsSimpleMap() const {\n  // TODO(mark): This method should not be const, although the interface\n  // currently imposes this requirement. Making it non-const would allow\n  // annotations_simple_map_ to be lazily constructed: InitializeCrashpadInfo()\n  // could be called here, and from other locations that require it, rather than\n  // calling it from Initialize().\n  // https://crashpad.chromium.org/bug/9\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_simple_map_;\n}\n\nconst SystemSnapshot* ProcessSnapshotMinidump::System() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &system_snapshot_;\n}\n\nstd::vector<const ThreadSnapshot*> ProcessSnapshotMinidump::Threads() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ThreadSnapshot*> threads;\n  for (const auto& thread : threads_) {\n    threads.push_back(thread.get());\n  }\n  return threads;\n}\n\nstd::vector<const ModuleSnapshot*> ProcessSnapshotMinidump::Modules() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ModuleSnapshot*> modules;\n  for (const auto& module : modules_) {\n    modules.push_back(module.get());\n  }\n  return modules;\n}\n\nstd::vector<UnloadedModuleSnapshot> ProcessSnapshotMinidump::UnloadedModules()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nconst ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (exception_snapshot_.IsValid()) {\n    return &exception_snapshot_;\n  }\n  // Allow caller to know whether the minidump contained an exception stream.\n  return nullptr;\n}\n\nstd::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotMinidump::MemoryMap()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return mem_regions_exposed_;\n}\n\nstd::vector<HandleSnapshot> ProcessSnapshotMinidump::Handles() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nstd::vector<const MemorySnapshot*> ProcessSnapshotMinidump::ExtraMemory()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> chunks;\n  for (const auto& chunk : extra_memory_) {\n    chunks.push_back(chunk.get());\n  }\n  return chunks;\n}\n\nconst ProcessMemory* ProcessSnapshotMinidump::Memory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return nullptr;\n}\n\nstd::vector<const MinidumpStream*>\nProcessSnapshotMinidump::CustomMinidumpStreams() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::vector<const MinidumpStream*> result;\n  result.reserve(custom_streams_.size());\n  for (const auto& custom_stream : custom_streams_) {\n    result.push_back(custom_stream.get());\n  }\n  return result;\n}\n\nbool ProcessSnapshotMinidump::InitializeCrashpadInfo() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  constexpr size_t crashpad_info_min_size =\n      offsetof(decltype(crashpad_info_), reserved);\n  size_t remaining_data_size = stream_it->second->DataSize;\n  if (remaining_data_size < crashpad_info_min_size) {\n    LOG(ERROR) << \"crashpad_info size mismatch\";\n    return false;\n  }\n\n  if (!file_reader_->SeekSet(stream_it->second->Rva)) {\n    return false;\n  }\n\n  if (!file_reader_->ReadExactly(&crashpad_info_, crashpad_info_min_size)) {\n    return false;\n  }\n  remaining_data_size -= crashpad_info_min_size;\n\n  // Read `reserved` if available.\n  size_t crashpad_reserved_size = sizeof(crashpad_info_.reserved);\n  if (remaining_data_size >= crashpad_reserved_size) {\n    if (!file_reader_->ReadExactly(&crashpad_info_.reserved,\n                                   crashpad_reserved_size)) {\n      return false;\n    }\n    remaining_data_size -= crashpad_reserved_size;\n  } else {\n    crashpad_info_.reserved = 0;\n  }\n\n  // Read `address_mask` if available.\n  size_t crashpad_address_mask_size = sizeof(crashpad_info_.address_mask);\n  if (remaining_data_size >= crashpad_address_mask_size) {\n    if (!file_reader_->ReadExactly(&crashpad_info_.address_mask,\n                                   crashpad_address_mask_size)) {\n      return false;\n    }\n    remaining_data_size -= crashpad_address_mask_size;\n  } else {\n    crashpad_info_.address_mask = 0;\n  }\n\n  if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) {\n    LOG(ERROR) << \"crashpad_info version mismatch\";\n    return false;\n  }\n\n  return internal::ReadMinidumpSimpleStringDictionary(\n      file_reader_,\n      crashpad_info_.simple_annotations,\n      &annotations_simple_map_);\n}\n\nbool ProcessSnapshotMinidump::InitializeMiscInfo() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeMiscInfo);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  if (!file_reader_->SeekSet(stream_it->second->Rva)) {\n    return false;\n  }\n\n  const size_t size = stream_it->second->DataSize;\n  if (size != sizeof(MINIDUMP_MISC_INFO_5) &&\n      size != sizeof(MINIDUMP_MISC_INFO_4) &&\n      size != sizeof(MINIDUMP_MISC_INFO_3) &&\n      size != sizeof(MINIDUMP_MISC_INFO_2) &&\n      size != sizeof(MINIDUMP_MISC_INFO)) {\n    LOG(ERROR) << \"misc_info size mismatch\";\n    return false;\n  }\n\n  MINIDUMP_MISC_INFO_5 info;\n  if (!file_reader_->ReadExactly(&info, size)) {\n    return false;\n  }\n\n  switch (stream_it->second->DataSize) {\n    case sizeof(MINIDUMP_MISC_INFO_5):\n    case sizeof(MINIDUMP_MISC_INFO_4):\n#if defined(WCHAR_T_IS_16_BIT)\n      full_version_ = base::WideToUTF8(info.BuildString);\n#else\n      full_version_ = base::UTF16ToUTF8(info.BuildString);\n#endif\n      full_version_ = full_version_.substr(0, full_version_.find(';'));\n      [[fallthrough]];\n    case sizeof(MINIDUMP_MISC_INFO_3):\n    case sizeof(MINIDUMP_MISC_INFO_2):\n    case sizeof(MINIDUMP_MISC_INFO):\n      // TODO(jperaza): Save the remaining misc info.\n      // https://crashpad.chromium.org/bug/10\n      process_id_ = info.ProcessId;\n      create_time_ = info.ProcessCreateTime;\n      user_time_ = info.ProcessUserTime;\n      kernel_time_ = info.ProcessKernelTime;\n  }\n\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeModules() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeModuleList);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR> module_crashpad_info_links;\n  if (!InitializeModulesCrashpadInfo(&module_crashpad_info_links)) {\n    return false;\n  }\n\n  if (stream_it->second->DataSize < sizeof(MINIDUMP_MODULE_LIST)) {\n    LOG(ERROR) << \"module_list size mismatch\";\n    return false;\n  }\n\n  if (!file_reader_->SeekSet(stream_it->second->Rva)) {\n    return false;\n  }\n\n  uint32_t module_count;\n  if (!file_reader_->ReadExactly(&module_count, sizeof(module_count))) {\n    return false;\n  }\n\n  if (sizeof(MINIDUMP_MODULE_LIST) + module_count * sizeof(MINIDUMP_MODULE) !=\n      stream_it->second->DataSize) {\n    LOG(ERROR) << \"module_list size mismatch\";\n    return false;\n  }\n\n  for (uint32_t module_index = 0; module_index < module_count; ++module_index) {\n    const RVA module_rva = stream_it->second->Rva + sizeof(module_count) +\n                           module_index * sizeof(MINIDUMP_MODULE);\n\n    const auto& module_crashpad_info_it =\n        module_crashpad_info_links.find(module_index);\n    const MINIDUMP_LOCATION_DESCRIPTOR* module_crashpad_info_location =\n        module_crashpad_info_it != module_crashpad_info_links.end()\n            ? &module_crashpad_info_it->second\n            : nullptr;\n\n    auto module = std::make_unique<internal::ModuleSnapshotMinidump>();\n    if (!module->Initialize(\n            file_reader_, module_rva, module_crashpad_info_location)) {\n      return false;\n    }\n\n    modules_.push_back(std::move(module));\n  }\n\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeModulesCrashpadInfo(\n    std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>*\n        module_crashpad_info_links) {\n  module_crashpad_info_links->clear();\n\n  if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) {\n    return false;\n  }\n\n  if (crashpad_info_.module_list.Rva == 0) {\n    return true;\n  }\n\n  if (crashpad_info_.module_list.DataSize <\n      sizeof(MinidumpModuleCrashpadInfoList)) {\n    LOG(ERROR) << \"module_crashpad_info_list size mismatch\";\n    return false;\n  }\n\n  if (!file_reader_->SeekSet(crashpad_info_.module_list.Rva)) {\n    return false;\n  }\n\n  uint32_t crashpad_module_count;\n  if (!file_reader_->ReadExactly(&crashpad_module_count,\n                                 sizeof(crashpad_module_count))) {\n    return false;\n  }\n\n  if (crashpad_info_.module_list.DataSize !=\n      sizeof(MinidumpModuleCrashpadInfoList) +\n          crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink)) {\n    LOG(ERROR) << \"module_crashpad_info_list size mismatch\";\n    return false;\n  }\n\n  std::unique_ptr<MinidumpModuleCrashpadInfoLink[]> minidump_links(\n      new MinidumpModuleCrashpadInfoLink[crashpad_module_count]);\n  if (!file_reader_->ReadExactly(\n          &minidump_links[0],\n          crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink))) {\n    return false;\n  }\n\n  for (uint32_t crashpad_module_index = 0;\n       crashpad_module_index < crashpad_module_count;\n       ++crashpad_module_index) {\n    const MinidumpModuleCrashpadInfoLink& minidump_link =\n        minidump_links[crashpad_module_index];\n    if (!module_crashpad_info_links\n             ->insert(std::make_pair(minidump_link.minidump_module_list_index,\n                                     minidump_link.location))\n             .second) {\n      LOG(WARNING)\n          << \"duplicate module_crashpad_info_list minidump_module_list_index \"\n          << minidump_link.minidump_module_list_index;\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeMemoryInfo() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeMemoryInfoList);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  if (stream_it->second->DataSize < sizeof(MINIDUMP_MEMORY_INFO_LIST)) {\n    LOG(ERROR) << \"memory_info_list size mismatch\";\n    return false;\n  }\n\n  if (!file_reader_->SeekSet(stream_it->second->Rva)) {\n    return false;\n  }\n\n  MINIDUMP_MEMORY_INFO_LIST list;\n\n  if (!file_reader_->ReadExactly(&list, sizeof(list))) {\n    return false;\n  }\n\n  if (list.SizeOfHeader != sizeof(list)) {\n    return false;\n  }\n\n  if (list.SizeOfEntry != sizeof(MINIDUMP_MEMORY_INFO)) {\n    return false;\n  }\n\n  if (sizeof(MINIDUMP_MEMORY_INFO_LIST) +\n          list.NumberOfEntries * list.SizeOfEntry !=\n      stream_it->second->DataSize) {\n    LOG(ERROR) << \"memory_info_list size mismatch\";\n    return false;\n  }\n\n  for (uint32_t i = 0; i < list.NumberOfEntries; i++) {\n    MINIDUMP_MEMORY_INFO info;\n\n    if (!file_reader_->ReadExactly(&info, sizeof(info))) {\n      return false;\n    }\n\n    mem_regions_.emplace_back(\n        std::make_unique<internal::MemoryMapRegionSnapshotMinidump>(info));\n    mem_regions_exposed_.emplace_back(mem_regions_.back().get());\n  }\n\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeExtraMemory() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeMemoryList);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  if (stream_it->second->DataSize < sizeof(MINIDUMP_MEMORY_LIST)) {\n    LOG(ERROR) << \"memory_list size mismatch\";\n    return false;\n  }\n\n  if (!file_reader_->SeekSet(stream_it->second->Rva)) {\n    return false;\n  }\n\n  // MSVC won't let us stack-allocate a MINIDUMP_MEMORY_LIST because of its\n  // trailing zero-element array. Luckily we're only interested in its other\n  // field anyway: a uint32_t indicating the number of memory descriptors that\n  // follow.\n  static_assert(\n      sizeof(MINIDUMP_MEMORY_LIST) == 4,\n      \"MINIDUMP_MEMORY_LIST's only actual field should be an uint32_t\");\n  uint32_t num_ranges;\n  if (!file_reader_->ReadExactly(&num_ranges, sizeof(num_ranges))) {\n    return false;\n  }\n\n  // We have to manually keep track of the locations of the entries in the\n  // contiguous list of MINIDUMP_MEMORY_DESCRIPTORs, because the Initialize()\n  // function jumps around the file to find the contents of each snapshot.\n  FileOffset location = file_reader_->SeekGet();\n  for (uint32_t i = 0; i < num_ranges; i++) {\n    extra_memory_.emplace_back(\n        std::make_unique<internal::MemorySnapshotMinidump>());\n    if (!extra_memory_.back()->Initialize(file_reader_,\n                                          static_cast<RVA>(location))) {\n      return false;\n    }\n    location += sizeof(MINIDUMP_MEMORY_DESCRIPTOR);\n  }\n\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeThreads() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeThreadList);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  if (stream_it->second->DataSize < sizeof(MINIDUMP_THREAD_LIST)) {\n    LOG(ERROR) << \"thread_list size mismatch\";\n    return false;\n  }\n\n  if (!file_reader_->SeekSet(stream_it->second->Rva)) {\n    return false;\n  }\n\n  uint32_t thread_count;\n  if (!file_reader_->ReadExactly(&thread_count, sizeof(thread_count))) {\n    return false;\n  }\n\n  if (sizeof(MINIDUMP_THREAD_LIST) + thread_count * sizeof(MINIDUMP_THREAD) !=\n      stream_it->second->DataSize) {\n    LOG(ERROR) << \"thread_list size mismatch\";\n    return false;\n  }\n\n  if (!InitializeThreadNames()) {\n    return false;\n  }\n\n  for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) {\n    const RVA thread_rva = stream_it->second->Rva + sizeof(thread_count) +\n                           thread_index * sizeof(MINIDUMP_THREAD);\n\n    auto thread = std::make_unique<internal::ThreadSnapshotMinidump>();\n    if (!thread->Initialize(file_reader_, thread_rva, arch_, thread_names_)) {\n      return false;\n    }\n\n    threads_.push_back(std::move(thread));\n  }\n\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeThreadNames() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeThreadNameList);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  if (stream_it->second->DataSize < sizeof(MINIDUMP_THREAD_NAME_LIST)) {\n    LOG(ERROR) << \"thread_name_list size mismatch\";\n    return false;\n  }\n\n  if (!file_reader_->SeekSet(stream_it->second->Rva)) {\n    return false;\n  }\n\n  uint32_t thread_name_count;\n  if (!file_reader_->ReadExactly(&thread_name_count,\n                                 sizeof(thread_name_count))) {\n    return false;\n  }\n\n  if (sizeof(MINIDUMP_THREAD_NAME_LIST) +\n          thread_name_count * sizeof(MINIDUMP_THREAD_NAME) !=\n      stream_it->second->DataSize) {\n    LOG(ERROR) << \"thread_name_list size mismatch\";\n    return false;\n  }\n\n  for (uint32_t thread_name_index = 0; thread_name_index < thread_name_count;\n       ++thread_name_index) {\n    const RVA thread_name_rva =\n        stream_it->second->Rva + sizeof(thread_name_count) +\n        thread_name_index * sizeof(MINIDUMP_THREAD_NAME);\n    if (!file_reader_->SeekSet(thread_name_rva)) {\n      return false;\n    }\n    MINIDUMP_THREAD_NAME minidump_thread_name;\n    if (!file_reader_->ReadExactly(&minidump_thread_name,\n                                   sizeof(minidump_thread_name))) {\n      return false;\n    }\n    std::string name;\n    if (!internal::ReadMinidumpUTF16String(\n            file_reader_, minidump_thread_name.RvaOfThreadName, &name)) {\n      return false;\n    }\n\n    // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36566\n    const uint32_t thread_id = minidump_thread_name.ThreadId;\n    thread_names_.emplace(thread_id, std::move(name));\n  }\n\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeSystemSnapshot() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeSystemInfo);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  if (stream_it->second->DataSize < sizeof(MINIDUMP_SYSTEM_INFO)) {\n    LOG(ERROR) << \"system info size mismatch\";\n    return false;\n  }\n\n  if (!system_snapshot_.Initialize(\n          file_reader_, stream_it->second->Rva, full_version_)) {\n    return false;\n  }\n\n  arch_ = system_snapshot_.GetCPUArchitecture();\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeCustomMinidumpStreams() {\n  for (size_t i = 0; i < stream_directory_.size(); i++) {\n    const auto& stream = stream_directory_[i];\n\n    // Filter out reserved minidump and crashpad streams.\n    const uint32_t stream_type = stream.StreamType;\n    if (stream_type <=\n            MinidumpStreamType::kMinidumpStreamTypeLastReservedStream ||\n        (stream_type >= MinidumpStreamType::kMinidumpStreamTypeCrashpadInfo &&\n         stream_type <= MinidumpStreamType::\n                            kMinidumpStreamTypeCrashpadLastReservedStream)) {\n      continue;\n    }\n\n    std::vector<uint8_t> data(stream.Location.DataSize);\n    if (!file_reader_->SeekSet(stream.Location.Rva) ||\n        !file_reader_->ReadExactly(data.data(), data.size())) {\n      LOG(ERROR) << \"Failed to read stream with ID 0x\" << std::hex\n                 << stream_type << std::dec << \" at index \" << i;\n      return false;\n    }\n\n    custom_streams_.push_back(\n        std::make_unique<MinidumpStream>(stream_type, std::move(data)));\n  }\n\n  return true;\n}\n\nbool ProcessSnapshotMinidump::InitializeExceptionSnapshot() {\n  const auto& stream_it = stream_map_.find(kMinidumpStreamTypeException);\n  if (stream_it == stream_map_.end()) {\n    return true;\n  }\n\n  if (stream_it->second->DataSize < sizeof(MINIDUMP_EXCEPTION_STREAM)) {\n    LOG(ERROR) << \"system info size mismatch\";\n    return false;\n  }\n\n  if (!exception_snapshot_.Initialize(\n          file_reader_, arch_, stream_it->second->Rva)) {\n    return false;\n  }\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/process_snapshot_minidump.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <stdint.h>\n#include <sys/time.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/minidump/exception_snapshot_minidump.h\"\n#include \"snapshot/minidump/minidump_stream.h\"\n#include \"snapshot/minidump/module_snapshot_minidump.h\"\n#include \"snapshot/minidump/system_snapshot_minidump.h\"\n#include \"snapshot/minidump/thread_snapshot_minidump.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/process/process_id.h\"\n\nnamespace crashpad {\n\nnamespace internal {\nclass MemoryMapRegionSnapshotMinidump;\n}  // namespace internal\n\n//! \\brief A ProcessSnapshot based on a minidump file.\nclass ProcessSnapshotMinidump final : public ProcessSnapshot {\n public:\n  ProcessSnapshotMinidump();\n\n  ProcessSnapshotMinidump(const ProcessSnapshotMinidump&) = delete;\n  ProcessSnapshotMinidump& operator=(const ProcessSnapshotMinidump&) = delete;\n\n  ~ProcessSnapshotMinidump() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] file_reader A file reader corresponding to a minidump file.\n  //!     The file reader must support seeking.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(FileReaderInterface* file_reader);\n\n  // ProcessSnapshot:\n\n  crashpad::ProcessID ProcessID() const override;\n  crashpad::ProcessID ParentProcessID() const override;\n  void SnapshotTime(timeval* snapshot_time) const override;\n  void ProcessStartTime(timeval* start_time) const override;\n  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;\n  void ReportID(UUID* report_id) const override;\n  void ClientID(UUID* client_id) const override;\n  const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const override;\n  const SystemSnapshot* System() const override;\n  std::vector<const ThreadSnapshot*> Threads() const override;\n  std::vector<const ModuleSnapshot*> Modules() const override;\n  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;\n  const ExceptionSnapshot* Exception() const override;\n  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;\n  std::vector<HandleSnapshot> Handles() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n  const ProcessMemory* Memory() const override;\n\n  //! \\brief Returns a list of custom minidump streams. This routine is the\n  //!     equivalent of ModuleSnapshot::CustomMinidumpStreams(), except that in\n  //!     a minidump it is impossible to associate a custom stream to a specific\n  //!     module.\n  //!\n  //! \\return The caller does not take ownership of the returned objects, they\n  //!     are scoped to the lifetime of the ProcessSnapshotMinidump object that\n  //!     they were obtained from.\n  std::vector<const MinidumpStream*> CustomMinidumpStreams() const;\n\n private:\n  // Initializes data carried in a MinidumpCrashpadInfo stream on behalf of\n  // Initialize().\n  bool InitializeCrashpadInfo();\n\n  // Initializes data carried in a MINIDUMP_MODULE_LIST stream on behalf of\n  // Initialize().\n  bool InitializeModules();\n\n  // Initializes data carried in a MINIDUMP_THREAD_LIST stream on behalf of\n  // Initialize().\n  bool InitializeThreads();\n\n  // Initializes data carried in a MINIDUMP_THREAD_NAME_LIST stream on behalf of\n  // Initialize().\n  bool InitializeThreadNames();\n\n  // Initializes data carried in a MINIDUMP_MEMORY_INFO_LIST stream on behalf of\n  // Initialize().\n  bool InitializeMemoryInfo();\n\n  // Initializes data carried in a MINIDUMP_MEMORY_LIST stream on behalf of\n  // Initialize().\n  bool InitializeExtraMemory();\n\n  // Initializes data carried in a MINIDUMP_SYSTEM_INFO stream on behalf of\n  // Initialize().\n  bool InitializeSystemSnapshot();\n\n  // Initializes data carried in a MinidumpModuleCrashpadInfoList structure on\n  // behalf of InitializeModules(). This makes use of MinidumpCrashpadInfo as\n  // well, so it must be called after InitializeCrashpadInfo().\n  bool InitializeModulesCrashpadInfo(\n      std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>*\n          module_crashpad_info_links);\n\n  // Initializes data carried in a MINIDUMP_MISC_INFO structure on behalf of\n  // Initialize().\n  bool InitializeMiscInfo();\n\n  // Initializes custom minidump streams.\n  bool InitializeCustomMinidumpStreams();\n\n  // Initializes data carried in a MINIDUMP_EXCEPTION_STREAM stream on behalf of\n  // Initialize().\n  bool InitializeExceptionSnapshot();\n\n  MINIDUMP_HEADER header_;\n  std::vector<MINIDUMP_DIRECTORY> stream_directory_;\n  std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;\n  std::vector<std::unique_ptr<internal::ModuleSnapshotMinidump>> modules_;\n  std::vector<std::unique_ptr<internal::ThreadSnapshotMinidump>> threads_;\n  std::map<uint32_t, std::string> thread_names_;\n  std::vector<UnloadedModuleSnapshot> unloaded_modules_;\n  std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotMinidump>>\n      mem_regions_;\n  std::vector<const MemoryMapRegionSnapshot*> mem_regions_exposed_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotMinidump>> extra_memory_;\n  std::vector<std::unique_ptr<MinidumpStream>> custom_streams_;\n  MinidumpCrashpadInfo crashpad_info_;\n  internal::SystemSnapshotMinidump system_snapshot_;\n  internal::ExceptionSnapshotMinidump exception_snapshot_;\n  CPUArchitecture arch_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  std::string full_version_;\n  FileReaderInterface* file_reader_;  // weak\n  crashpad::ProcessID process_id_;\n  uint32_t create_time_;\n  uint32_t user_time_;\n  uint32_t kernel_time_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_\n"
  },
  {
    "path": "snapshot/minidump/process_snapshot_minidump_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/process_snapshot_minidump.h\"\n\n#include <windows.h>\n#include <dbghelp.h>\n#include <string.h>\n\n#include <algorithm>\n#include <iterator>\n#include <memory>\n\n#include \"base/numerics/safe_math.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"minidump/minidump_context.h\"\n#include \"snapshot/memory_map_region_snapshot.h\"\n#include \"snapshot/minidump/minidump_annotation_reader.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"util/file/string_file.h\"\n#include \"util/misc/pdb_structures.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass ReadToVector : public crashpad::MemorySnapshot::Delegate {\n public:\n  std::vector<uint8_t> result;\n\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override {\n    result.resize(size);\n    memcpy(result.data(), data, size);\n    return true;\n  }\n};\n\nMinidumpContextARM64 GetArm64MinidumpContext() {\n  MinidumpContextARM64 minidump_context;\n\n  minidump_context.context_flags = kMinidumpContextARM64Full;\n\n  minidump_context.cpsr = 0;\n\n  for (int i = 0; i < 29; i++) {\n    minidump_context.regs[i] = i + 1;\n  }\n\n  minidump_context.fp = 30;\n  minidump_context.lr = 31;\n  minidump_context.sp = 32;\n  minidump_context.pc = 33;\n\n  for (int i = 0; i < 32; i++) {\n    minidump_context.fpsimd[i].lo = i * 2 + 34;\n    minidump_context.fpsimd[i].hi = i * 2 + 35;\n  }\n\n  minidump_context.fpcr = 98;\n  minidump_context.fpsr = 99;\n\n  for (int i = 0; i < 8; i++) {\n    minidump_context.bcr[i] = i * 2 + 100;\n    minidump_context.bvr[i] = i * 2 + 101;\n  }\n\n  for (int i = 0; i < 2; i++) {\n    minidump_context.wcr[i] = i * 2 + 115;\n    minidump_context.wvr[i] = i * 2 + 116;\n  }\n\n  return minidump_context;\n}\n\nTEST(ProcessSnapshotMinidump, EmptyFile) {\n  StringFile string_file;\n  ProcessSnapshotMinidump process_snapshot;\n\n  EXPECT_FALSE(process_snapshot.Initialize(&string_file));\n}\n\nTEST(ProcessSnapshotMinidump, InvalidSignatureAndVersion) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_FALSE(process_snapshot.Initialize(&string_file));\n}\n\nTEST(ProcessSnapshotMinidump, Empty) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  UUID client_id;\n  process_snapshot.ClientID(&client_id);\n  EXPECT_EQ(client_id, UUID());\n\n  EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty());\n}\n\n// Writes |string| to |writer| as a MinidumpUTF8String, and returns the file\n// offset of the beginning of the string.\nRVA WriteString(FileWriterInterface* writer, const std::string& string) {\n  RVA rva = static_cast<RVA>(writer->SeekGet());\n\n  uint32_t string_size = static_cast<uint32_t>(string.size());\n  EXPECT_TRUE(writer->Write(&string_size, sizeof(string_size)));\n\n  // Include the trailing NUL character.\n  EXPECT_TRUE(writer->Write(string.c_str(), string.size() + 1));\n\n  return rva;\n}\n\n// Writes |dictionary| to |writer| as a MinidumpSimpleStringDictionary, and\n// populates |location| with a location descriptor identifying what was written.\nvoid WriteMinidumpSimpleStringDictionary(\n    MINIDUMP_LOCATION_DESCRIPTOR* location,\n    FileWriterInterface* writer,\n    const std::map<std::string, std::string>& dictionary) {\n  std::vector<MinidumpSimpleStringDictionaryEntry> entries;\n  for (const auto& it : dictionary) {\n    MinidumpSimpleStringDictionaryEntry entry;\n    entry.key = WriteString(writer, it.first);\n    entry.value = WriteString(writer, it.second);\n    entries.push_back(entry);\n  }\n\n  location->Rva = static_cast<RVA>(writer->SeekGet());\n\n  const uint32_t simple_string_dictionary_entries =\n      static_cast<uint32_t>(entries.size());\n  EXPECT_TRUE(writer->Write(&simple_string_dictionary_entries,\n                            sizeof(simple_string_dictionary_entries)));\n  for (const MinidumpSimpleStringDictionaryEntry& entry : entries) {\n    EXPECT_TRUE(writer->Write(&entry, sizeof(entry)));\n  }\n\n  location->DataSize = static_cast<uint32_t>(\n      sizeof(simple_string_dictionary_entries) +\n      entries.size() * sizeof(MinidumpSimpleStringDictionaryEntry));\n}\n\n// Writes |strings| to |writer| as a MinidumpRVAList referencing\n// MinidumpUTF8String objects, and populates |location| with a location\n// descriptor identifying what was written.\nvoid WriteMinidumpStringList(MINIDUMP_LOCATION_DESCRIPTOR* location,\n                             FileWriterInterface* writer,\n                             const std::vector<std::string>& strings) {\n  std::vector<RVA> rvas;\n  for (const std::string& string : strings) {\n    rvas.push_back(WriteString(writer, string));\n  }\n\n  location->Rva = static_cast<RVA>(writer->SeekGet());\n\n  const uint32_t string_list_entries = static_cast<uint32_t>(rvas.size());\n  EXPECT_TRUE(writer->Write(&string_list_entries, sizeof(string_list_entries)));\n  for (RVA rva : rvas) {\n    EXPECT_TRUE(writer->Write(&rva, sizeof(rva)));\n  }\n\n  location->DataSize = static_cast<uint32_t>(sizeof(string_list_entries) +\n                                             rvas.size() * sizeof(RVA));\n}\n\n// Writes |data| to |writer| as a MinidumpByteArray, and returns the file offset\n// from the beginning of the string.\nRVA WriteByteArray(FileWriterInterface* writer,\n                   const std::vector<uint8_t> data) {\n  auto rva = static_cast<RVA>(writer->SeekGet());\n\n  auto length = static_cast<uint32_t>(data.size());\n  EXPECT_TRUE(writer->Write(&length, sizeof(length)));\n  EXPECT_TRUE(writer->Write(data.data(), length));\n\n  return rva;\n}\n\n// Writes |annotations| to |writer| as a MinidumpAnnotationList, and populates\n// |location| with a location descriptor identifying what was written.\nvoid WriteMinidumpAnnotationList(\n    MINIDUMP_LOCATION_DESCRIPTOR* location,\n    FileWriterInterface* writer,\n    const std::vector<AnnotationSnapshot>& annotations) {\n  std::vector<MinidumpAnnotation> minidump_annotations;\n  for (const auto& it : annotations) {\n    MinidumpAnnotation annotation;\n    annotation.name = WriteString(writer, it.name);\n    annotation.type = it.type;\n    annotation.reserved = 0;\n    annotation.value = WriteByteArray(writer, it.value);\n    minidump_annotations.push_back(annotation);\n  }\n\n  location->Rva = static_cast<RVA>(writer->SeekGet());\n\n  auto count = static_cast<uint32_t>(minidump_annotations.size());\n  EXPECT_TRUE(writer->Write(&count, sizeof(count)));\n\n  for (const auto& it : minidump_annotations) {\n    EXPECT_TRUE(writer->Write(&it, sizeof(MinidumpAnnotation)));\n  }\n\n  location->DataSize =\n      sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation);\n}\n\nTEST(ProcessSnapshotMinidump, ClientID) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  UUID client_id;\n  ASSERT_TRUE(\n      client_id.InitializeFromString(\"0001f4a9-d00d-5155-0a55-c0ffeec0ffee\"));\n\n  MinidumpCrashpadInfo crashpad_info = {};\n  crashpad_info.version = MinidumpCrashpadInfo::kVersion;\n  crashpad_info.client_id = client_id;\n\n  MINIDUMP_DIRECTORY crashpad_info_directory = {};\n  crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;\n  crashpad_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));\n  crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_info_directory,\n                                sizeof(crashpad_info_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 1;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  UUID actual_client_id;\n  process_snapshot.ClientID(&actual_client_id);\n  EXPECT_EQ(actual_client_id, client_id);\n\n  EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty());\n}\n\nTEST(ProcessSnapshotMinidump, ReadOldCrashpadInfo) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  UUID client_id;\n  ASSERT_TRUE(\n      client_id.InitializeFromString(\"0001f4a9-d00d-5155-0a55-c0ffeec0ffee\"));\n\n  MinidumpCrashpadInfo crashpad_info = {};\n  crashpad_info.version = MinidumpCrashpadInfo::kVersion;\n  crashpad_info.client_id = client_id;\n\n  MINIDUMP_DIRECTORY crashpad_info_directory = {};\n  crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;\n  crashpad_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info) - 8));\n  crashpad_info_directory.Location.DataSize = sizeof(crashpad_info) - 8;\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_info_directory,\n                                sizeof(crashpad_info_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 1;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  UUID actual_client_id;\n  process_snapshot.ClientID(&actual_client_id);\n  EXPECT_EQ(actual_client_id, client_id);\n\n  EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty());\n}\n\nTEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MinidumpCrashpadInfo crashpad_info = {};\n  crashpad_info.version = MinidumpCrashpadInfo::kVersion;\n\n  std::map<std::string, std::string> dictionary;\n  dictionary[\"the first key\"] = \"THE FIRST VALUE EVER!\";\n  dictionary[\"2key\"] = \"a lowly second value\";\n  WriteMinidumpSimpleStringDictionary(\n      &crashpad_info.simple_annotations, &string_file, dictionary);\n\n  MINIDUMP_DIRECTORY crashpad_info_directory = {};\n  crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;\n  crashpad_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));\n  crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_info_directory,\n                                sizeof(crashpad_info_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 1;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  UUID client_id;\n  process_snapshot.ClientID(&client_id);\n  EXPECT_EQ(client_id, UUID());\n\n  const auto annotations_simple_map = process_snapshot.AnnotationsSimpleMap();\n  EXPECT_EQ(annotations_simple_map, dictionary);\n}\n\nTEST(ProcessSnapshotMinidump, AnnotationObjects) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header{};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  std::vector<AnnotationSnapshot> annotations;\n  annotations.emplace_back(\n      AnnotationSnapshot(\"name 1\", 0xBBBB, {'t', 'e', '\\0', 's', 't', '\\0'}));\n  annotations.emplace_back(\n      AnnotationSnapshot(\"name 2\", 0xABBA, {0xF0, 0x9F, 0x92, 0x83}));\n\n  MINIDUMP_LOCATION_DESCRIPTOR location;\n  WriteMinidumpAnnotationList(&location, &string_file, annotations);\n\n  std::vector<AnnotationSnapshot> read_annotations;\n  EXPECT_TRUE(internal::ReadMinidumpAnnotationList(\n      &string_file, location, &read_annotations));\n\n  EXPECT_EQ(read_annotations, annotations);\n}\n\nTEST(ProcessSnapshotMinidump, Modules) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MINIDUMP_MODULE minidump_module = {};\n  constexpr uint32_t minidump_module_count = 4;\n  RVA name_rvas[minidump_module_count];\n  std::string names[minidump_module_count] = {\n      \"libtacotruck\",\n      \"libevidencebased\",\n      \"libgeorgism\",\n      \"librealistutopia\",\n  };\n  constexpr char debug_name[] = \"debugme.pdb\";\n\n  minidump_module.BaseOfImage = 0xbadf00d;\n  minidump_module.SizeOfImage = 9001;\n  minidump_module.TimeDateStamp = 1970;\n  minidump_module.VersionInfo.dwFileVersionMS = 0xAABBCCDD;\n  minidump_module.VersionInfo.dwFileVersionLS = 0xEEFF4242;\n  minidump_module.VersionInfo.dwProductVersionMS = 0xAAAABBBB;\n  minidump_module.VersionInfo.dwProductVersionLS = 0xCCCCDDDD;\n  minidump_module.VersionInfo.dwFileType = VFT_APP;\n\n  for (uint32_t i = 0; i < minidump_module_count; i++) {\n    name_rvas[i] = static_cast<RVA>(string_file.SeekGet());\n    auto name16 = base::UTF8ToUTF16(names[i]);\n    uint32_t size =\n        base::checked_cast<uint32_t>(sizeof(name16[0]) * name16.size());\n    EXPECT_TRUE(string_file.Write(&size, sizeof(size)));\n    EXPECT_TRUE(string_file.Write(&name16[0], size));\n  }\n\n  CodeViewRecordPDB70 pdb70_cv;\n  pdb70_cv.signature = CodeViewRecordPDB70::kSignature;\n  pdb70_cv.age = 7;\n  pdb70_cv.uuid.InitializeFromString(\"00112233-4455-6677-8899-aabbccddeeff\");\n\n  auto pdb70_loc = static_cast<RVA>(string_file.SeekGet());\n  auto pdb70_size = offsetof(CodeViewRecordPDB70, pdb_name);\n\n  EXPECT_TRUE(string_file.Write(&pdb70_cv, pdb70_size));\n\n  size_t nul_terminated_length = strlen(debug_name) + 1;\n  EXPECT_TRUE(string_file.Write(debug_name, nul_terminated_length));\n  pdb70_size += nul_terminated_length;\n\n  CodeViewRecordBuildID build_id_cv;\n  build_id_cv.signature = CodeViewRecordBuildID::kSignature;\n\n  auto build_id_cv_loc = static_cast<RVA>(string_file.SeekGet());\n\n  EXPECT_TRUE(string_file.Write(&build_id_cv,\n                                offsetof(CodeViewRecordBuildID, build_id)));\n  EXPECT_TRUE(string_file.Write(\"atestbuildidbecausewhynot\", 25));\n\n  auto build_id_cv_size =\n      static_cast<size_t>(string_file.SeekGet() - build_id_cv_loc);\n\n  MINIDUMP_DIRECTORY minidump_module_list_directory = {};\n  minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList;\n  minidump_module_list_directory.Location.DataSize =\n      sizeof(MINIDUMP_MODULE_LIST) +\n      minidump_module_count * sizeof(MINIDUMP_MODULE);\n  minidump_module_list_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  EXPECT_TRUE(\n      string_file.Write(&minidump_module_count, sizeof(minidump_module_count)));\n  for (uint32_t minidump_module_index = 0;\n       minidump_module_index < minidump_module_count;\n       ++minidump_module_index) {\n    if (minidump_module_index % 2) {\n      minidump_module.CvRecord.Rva = pdb70_loc;\n      minidump_module.CvRecord.DataSize = static_cast<uint32_t>(pdb70_size);\n    } else {\n      minidump_module.CvRecord.Rva = build_id_cv_loc;\n      minidump_module.CvRecord.DataSize =\n          static_cast<uint32_t>(build_id_cv_size);\n    }\n\n    minidump_module.ModuleNameRva = name_rvas[minidump_module_index];\n    EXPECT_TRUE(string_file.Write(&minidump_module, sizeof(minidump_module)));\n    minidump_module.TimeDateStamp++;\n  }\n\n  MinidumpModuleCrashpadInfo crashpad_module_0 = {};\n  crashpad_module_0.version = MinidumpModuleCrashpadInfo::kVersion;\n  std::map<std::string, std::string> dictionary_0;\n  dictionary_0[\"ptype\"] = \"browser\";\n  dictionary_0[\"pid\"] = \"12345\";\n  WriteMinidumpSimpleStringDictionary(\n      &crashpad_module_0.simple_annotations, &string_file, dictionary_0);\n\n  MinidumpModuleCrashpadInfoLink crashpad_module_0_link = {};\n  crashpad_module_0_link.minidump_module_list_index = 0;\n  crashpad_module_0_link.location.DataSize = sizeof(crashpad_module_0);\n  crashpad_module_0_link.location.Rva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_module_0, sizeof(crashpad_module_0)));\n\n  MinidumpModuleCrashpadInfo crashpad_module_2 = {};\n  crashpad_module_2.version = MinidumpModuleCrashpadInfo::kVersion;\n  std::map<std::string, std::string> dictionary_2;\n  dictionary_2[\"fakemodule\"] = \"yes\";\n  WriteMinidumpSimpleStringDictionary(\n      &crashpad_module_2.simple_annotations, &string_file, dictionary_2);\n\n  std::vector<std::string> list_annotations_2;\n  list_annotations_2.push_back(\"first string\");\n  list_annotations_2.push_back(\"last string\");\n  WriteMinidumpStringList(\n      &crashpad_module_2.list_annotations, &string_file, list_annotations_2);\n\n  MinidumpModuleCrashpadInfoLink crashpad_module_2_link = {};\n  crashpad_module_2_link.minidump_module_list_index = 2;\n  crashpad_module_2_link.location.DataSize = sizeof(crashpad_module_2);\n  crashpad_module_2_link.location.Rva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_module_2, sizeof(crashpad_module_2)));\n\n  MinidumpModuleCrashpadInfo crashpad_module_4 = {};\n  crashpad_module_4.version = MinidumpModuleCrashpadInfo::kVersion;\n  std::vector<AnnotationSnapshot> annotations_4{\n      {\"first one\", 0xBADE, {'a', 'b', 'c'}},\n      {\"2\", 0xEDD1, {0x11, 0x22, 0x33}},\n      {\"threeeeee\", 0xDADA, {'f'}},\n  };\n  WriteMinidumpAnnotationList(\n      &crashpad_module_4.annotation_objects, &string_file, annotations_4);\n\n  MinidumpModuleCrashpadInfoLink crashpad_module_4_link = {};\n  crashpad_module_4_link.minidump_module_list_index = 3;\n  crashpad_module_4_link.location.DataSize = sizeof(crashpad_module_4);\n  crashpad_module_4_link.location.Rva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_module_4, sizeof(crashpad_module_4)));\n\n  MinidumpCrashpadInfo crashpad_info = {};\n  crashpad_info.version = MinidumpCrashpadInfo::kVersion;\n\n  uint32_t crashpad_module_count = 3;\n\n  crashpad_info.module_list.DataSize =\n      sizeof(MinidumpModuleCrashpadInfoList) +\n      crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink);\n  crashpad_info.module_list.Rva = static_cast<RVA>(string_file.SeekGet());\n\n  EXPECT_TRUE(\n      string_file.Write(&crashpad_module_count, sizeof(crashpad_module_count)));\n  EXPECT_TRUE(string_file.Write(&crashpad_module_0_link,\n                                sizeof(crashpad_module_0_link)));\n  EXPECT_TRUE(string_file.Write(&crashpad_module_2_link,\n                                sizeof(crashpad_module_2_link)));\n  EXPECT_TRUE(string_file.Write(&crashpad_module_4_link,\n                                sizeof(crashpad_module_4_link)));\n\n  MINIDUMP_DIRECTORY crashpad_info_directory = {};\n  crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo;\n  crashpad_info_directory.Location.DataSize = sizeof(crashpad_info);\n  crashpad_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&minidump_module_list_directory,\n                                sizeof(minidump_module_list_directory)));\n  EXPECT_TRUE(string_file.Write(&crashpad_info_directory,\n                                sizeof(crashpad_info_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 2;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  std::vector<const ModuleSnapshot*> modules = process_snapshot.Modules();\n  ASSERT_EQ(modules.size(), minidump_module_count);\n\n  for (uint32_t i = 0; i < minidump_module_count; i++) {\n    EXPECT_EQ(modules[i]->Name(), names[i]);\n    EXPECT_EQ(modules[i]->Address(), 0xbadf00dU);\n    EXPECT_EQ(modules[i]->Size(), 9001U);\n    EXPECT_EQ(modules[i]->Timestamp(), static_cast<time_t>(1970U + i));\n\n    uint16_t v0;\n    uint16_t v1;\n    uint16_t v2;\n    uint16_t v3;\n\n    modules[i]->FileVersion(&v0, &v1, &v2, &v3);\n    EXPECT_EQ(v0, 0xAABBU);\n    EXPECT_EQ(v1, 0xCCDDU);\n    EXPECT_EQ(v2, 0xEEFFU);\n    EXPECT_EQ(v3, 0x4242U);\n\n    modules[i]->SourceVersion(&v0, &v1, &v2, &v3);\n    EXPECT_EQ(v0, 0xAAAAU);\n    EXPECT_EQ(v1, 0xBBBBU);\n    EXPECT_EQ(v2, 0xCCCCU);\n    EXPECT_EQ(v3, 0xDDDDU);\n\n    EXPECT_EQ(modules[i]->GetModuleType(),\n              ModuleSnapshot::kModuleTypeExecutable);\n\n    if (i % 2) {\n      uint32_t age;\n      UUID uuid;\n      modules[i]->UUIDAndAge(&uuid, &age);\n\n      EXPECT_EQ(uuid.ToString(), \"00112233-4455-6677-8899-aabbccddeeff\");\n      EXPECT_EQ(age, 7U);\n      EXPECT_EQ(modules[i]->DebugFileName(), debug_name);\n    } else {\n      auto build_id = modules[i]->BuildID();\n      std::string build_id_text(build_id.data(),\n                                build_id.data() + build_id.size());\n      EXPECT_EQ(build_id_text, \"atestbuildidbecausewhynot\");\n    }\n  }\n\n  auto annotations_simple_map = modules[0]->AnnotationsSimpleMap();\n  EXPECT_EQ(annotations_simple_map, dictionary_0);\n\n  auto annotations_vector = modules[0]->AnnotationsVector();\n  EXPECT_TRUE(annotations_vector.empty());\n\n  annotations_simple_map = modules[1]->AnnotationsSimpleMap();\n  EXPECT_TRUE(annotations_simple_map.empty());\n\n  annotations_vector = modules[1]->AnnotationsVector();\n  EXPECT_TRUE(annotations_vector.empty());\n\n  annotations_simple_map = modules[2]->AnnotationsSimpleMap();\n  EXPECT_EQ(annotations_simple_map, dictionary_2);\n\n  annotations_vector = modules[2]->AnnotationsVector();\n  EXPECT_EQ(annotations_vector, list_annotations_2);\n\n  auto annotation_objects = modules[3]->AnnotationObjects();\n  EXPECT_EQ(annotation_objects, annotations_4);\n}\n\nTEST(ProcessSnapshotMinidump, ProcessID) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  ASSERT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  static const crashpad::ProcessID kTestProcessId = 42;\n  MINIDUMP_MISC_INFO misc_info = {};\n  misc_info.SizeOfInfo = sizeof(misc_info);\n  misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_ID;\n  misc_info.ProcessId = kTestProcessId;\n\n  MINIDUMP_DIRECTORY misc_directory = {};\n  misc_directory.StreamType = kMinidumpStreamTypeMiscInfo;\n  misc_directory.Location.DataSize = sizeof(misc_info);\n  misc_directory.Location.Rva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&misc_info, sizeof(misc_info)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 1;\n  ASSERT_TRUE(string_file.SeekSet(0));\n  ASSERT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  ASSERT_TRUE(process_snapshot.Initialize(&string_file));\n  EXPECT_EQ(process_snapshot.ProcessID(), kTestProcessId);\n}\n\nTEST(ProcessSnapshotMinidump, SnapshotTime) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.TimeDateStamp = 42;\n  ASSERT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  ASSERT_TRUE(process_snapshot.Initialize(&string_file));\n\n  timeval snapshot_time;\n  process_snapshot.SnapshotTime(&snapshot_time);\n  EXPECT_EQ(snapshot_time.tv_sec, 42);\n  EXPECT_EQ(snapshot_time.tv_usec, 0);\n}\n\nTEST(ProcessSnapshotMinidump, MiscTimes) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  ASSERT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MINIDUMP_MISC_INFO misc_info = {};\n  misc_info.SizeOfInfo = sizeof(misc_info);\n  misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_TIMES;\n  misc_info.ProcessCreateTime = 42;\n  misc_info.ProcessUserTime = 43;\n  misc_info.ProcessKernelTime = 44;\n\n  MINIDUMP_DIRECTORY misc_directory = {};\n  misc_directory.StreamType = kMinidumpStreamTypeMiscInfo;\n  misc_directory.Location.DataSize = sizeof(misc_info);\n  misc_directory.Location.Rva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&misc_info, sizeof(misc_info)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 1;\n  ASSERT_TRUE(string_file.SeekSet(0));\n  ASSERT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  ASSERT_TRUE(process_snapshot.Initialize(&string_file));\n\n  timeval start_time, user_time, kernel_time;\n  process_snapshot.ProcessStartTime(&start_time);\n  process_snapshot.ProcessCPUTimes(&user_time, &kernel_time);\n  EXPECT_EQ(static_cast<uint32_t>(start_time.tv_sec),\n            misc_info.ProcessCreateTime);\n  EXPECT_EQ(start_time.tv_usec, 0);\n  EXPECT_EQ(static_cast<uint32_t>(user_time.tv_sec), misc_info.ProcessUserTime);\n  EXPECT_EQ(user_time.tv_usec, 0);\n  EXPECT_EQ(static_cast<uint32_t>(kernel_time.tv_sec),\n            misc_info.ProcessKernelTime);\n  EXPECT_EQ(kernel_time.tv_usec, 0);\n}\n\nTEST(ProcessSnapshotMinidump, Threads) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MINIDUMP_THREAD minidump_thread = {};\n  uint32_t minidump_thread_count = 4;\n\n  minidump_thread.ThreadId = 42;\n  minidump_thread.Teb = 24;\n\n  MINIDUMP_DIRECTORY minidump_thread_list_directory = {};\n  minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;\n  minidump_thread_list_directory.Location.DataSize =\n      sizeof(MINIDUMP_THREAD_LIST) +\n      minidump_thread_count * sizeof(MINIDUMP_THREAD);\n  minidump_thread_list_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  // Fields in MINIDUMP_THREAD_LIST.\n  EXPECT_TRUE(\n      string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count)));\n  for (uint32_t minidump_thread_index = 0;\n       minidump_thread_index < minidump_thread_count;\n       ++minidump_thread_index) {\n    EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));\n    minidump_thread.ThreadId++;\n  }\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&minidump_thread_list_directory,\n                                sizeof(minidump_thread_list_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 1;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();\n  ASSERT_EQ(threads.size(), minidump_thread_count);\n\n  uint32_t thread_id = 42;\n  for (const auto& thread : threads) {\n    EXPECT_EQ(thread->ThreadID(), thread_id);\n    EXPECT_EQ(thread->ThreadSpecificDataAddress(), 24UL);\n    thread_id++;\n  }\n}\n\nTEST(ProcessSnapshotMinidump, ThreadsWithNames) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  constexpr uint32_t kMinidumpThreadCount = 4;\n  constexpr uint32_t kBaseThreadId = 42;\n\n  const std::string thread_names[kMinidumpThreadCount] = {\n      \"ariadne\",\n      \"theseus\",\n      \"pasiphae\",\n      \"minos\",\n  };\n\n  RVA64 thread_name_rva64s[kMinidumpThreadCount];\n  for (uint32_t i = 0; i < kMinidumpThreadCount; i++) {\n    thread_name_rva64s[i] = static_cast<RVA64>(string_file.SeekGet());\n    auto name16 = base::UTF8ToUTF16(thread_names[i]);\n    uint32_t size =\n        base::checked_cast<uint32_t>(sizeof(name16[0]) * name16.size());\n    EXPECT_TRUE(string_file.Write(&size, sizeof(size)));\n    EXPECT_TRUE(string_file.Write(&name16[0], size));\n  }\n\n  MINIDUMP_DIRECTORY minidump_thread_list_directory = {};\n  minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;\n  minidump_thread_list_directory.Location.DataSize =\n      sizeof(MINIDUMP_THREAD_LIST) +\n      kMinidumpThreadCount * sizeof(MINIDUMP_THREAD);\n  minidump_thread_list_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  // Fields in MINIDUMP_THREAD_LIST.\n  EXPECT_TRUE(\n      string_file.Write(&kMinidumpThreadCount, sizeof(kMinidumpThreadCount)));\n  for (uint32_t minidump_thread_index = 0;\n       minidump_thread_index < kMinidumpThreadCount;\n       ++minidump_thread_index) {\n    MINIDUMP_THREAD minidump_thread = {};\n    minidump_thread.ThreadId = kBaseThreadId + minidump_thread_index;\n    EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));\n  }\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&minidump_thread_list_directory,\n                                sizeof(minidump_thread_list_directory)));\n\n  MINIDUMP_DIRECTORY minidump_thread_name_list_directory = {};\n  minidump_thread_name_list_directory.StreamType =\n      kMinidumpStreamTypeThreadNameList;\n  minidump_thread_name_list_directory.Location.DataSize =\n      sizeof(MINIDUMP_THREAD_NAME_LIST) +\n      kMinidumpThreadCount * sizeof(MINIDUMP_THREAD_NAME);\n  minidump_thread_name_list_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  // Fields in MINIDUMP_THREAD_NAME_LIST.\n  EXPECT_TRUE(\n      string_file.Write(&kMinidumpThreadCount, sizeof(kMinidumpThreadCount)));\n  for (uint32_t minidump_thread_index = 0;\n       minidump_thread_index < kMinidumpThreadCount;\n       ++minidump_thread_index) {\n    MINIDUMP_THREAD_NAME minidump_thread_name = {0, 0};\n    minidump_thread_name.ThreadId = kBaseThreadId + minidump_thread_index;\n    minidump_thread_name.RvaOfThreadName =\n        thread_name_rva64s[minidump_thread_index];\n    EXPECT_TRUE(\n        string_file.Write(&minidump_thread_name, sizeof(minidump_thread_name)));\n  }\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory,\n                                sizeof(minidump_thread_list_directory)));\n  ASSERT_TRUE(string_file.Write(&minidump_thread_name_list_directory,\n                                sizeof(minidump_thread_name_list_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 2;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();\n  ASSERT_EQ(threads.size(), kMinidumpThreadCount);\n\n  size_t idx = 0;\n  for (const auto& thread : threads) {\n    EXPECT_EQ(thread->ThreadID(), kBaseThreadId + idx);\n    EXPECT_EQ(thread->ThreadName(), thread_names[idx]);\n    idx++;\n  }\n}\n\nTEST(ProcessSnapshotMinidump, System) {\n  const char cpu_info[] = \"GenuineIntel\";\n  uint32_t cpu_info_bytes[3];\n  memcpy(cpu_info_bytes, cpu_info, sizeof(cpu_info_bytes));\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MINIDUMP_SYSTEM_INFO minidump_system_info = {};\n\n  minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86;\n  minidump_system_info.ProcessorLevel = 3;\n  minidump_system_info.ProcessorRevision = 3;\n  minidump_system_info.NumberOfProcessors = 8;\n  minidump_system_info.ProductType = kMinidumpOSTypeServer;\n  minidump_system_info.PlatformId = kMinidumpOSFuchsia;\n  minidump_system_info.MajorVersion = 3;\n  minidump_system_info.MinorVersion = 4;\n  minidump_system_info.BuildNumber = 56;\n  minidump_system_info.CSDVersionRva = WriteString(&string_file, \"Snazzle\");\n  minidump_system_info.Cpu.X86CpuInfo.VendorId[0] = cpu_info_bytes[0];\n  minidump_system_info.Cpu.X86CpuInfo.VendorId[1] = cpu_info_bytes[1];\n  minidump_system_info.Cpu.X86CpuInfo.VendorId[2] = cpu_info_bytes[2];\n\n  MINIDUMP_MISC_INFO_5 minidump_misc_info = {};\n  std::u16string build_string;\n  ASSERT_TRUE(base::UTF8ToUTF16(\n      \"MyOSVersion; MyMachineDescription\", 33, &build_string));\n  std::copy(build_string.begin(), build_string.end(),\n            minidump_misc_info.BuildString);\n\n  MINIDUMP_DIRECTORY minidump_system_info_directory = {};\n  minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo;\n  minidump_system_info_directory.Location.DataSize =\n      sizeof(MINIDUMP_SYSTEM_INFO);\n  minidump_system_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  ASSERT_TRUE(\n      string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));\n\n  MINIDUMP_DIRECTORY minidump_misc_info_directory = {};\n  minidump_misc_info_directory.StreamType = kMinidumpStreamTypeMiscInfo;\n  minidump_misc_info_directory.Location.DataSize = sizeof(MINIDUMP_MISC_INFO_5);\n  minidump_misc_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  ASSERT_TRUE(\n      string_file.Write(&minidump_misc_info, sizeof(minidump_misc_info)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,\n                                sizeof(minidump_system_info_directory)));\n  ASSERT_TRUE(string_file.Write(&minidump_misc_info_directory,\n                                sizeof(minidump_misc_info_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 2;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  const SystemSnapshot* s = process_snapshot.System();\n\n  EXPECT_EQ(s->GetCPUArchitecture(), kCPUArchitectureX86);\n  EXPECT_EQ(s->CPURevision(), 3UL);\n  EXPECT_EQ(s->CPUVendor(), \"GenuineIntel\");\n  EXPECT_EQ(s->GetOperatingSystem(),\n            SystemSnapshot::OperatingSystem::kOperatingSystemFuchsia);\n  EXPECT_EQ(s->OSVersionFull(), \"MyOSVersion\");\n\n  int major, minor, bugfix;\n  std::string build;\n  s->OSVersion(&major, &minor, &bugfix, &build);\n\n  EXPECT_EQ(major, 3);\n  EXPECT_EQ(minor, 4);\n  EXPECT_EQ(bugfix, 56);\n  EXPECT_EQ(build, \"Snazzle\");\n}\n\nTEST(ProcessSnapshotMinidump, ThreadContextARM64) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MINIDUMP_SYSTEM_INFO minidump_system_info = {};\n\n  minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureARM64;\n  minidump_system_info.ProductType = kMinidumpOSTypeServer;\n  minidump_system_info.PlatformId = kMinidumpOSFuchsia;\n  minidump_system_info.CSDVersionRva = WriteString(&string_file, \"\");\n\n  MINIDUMP_DIRECTORY minidump_system_info_directory = {};\n  minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo;\n  minidump_system_info_directory.Location.DataSize =\n      sizeof(MINIDUMP_SYSTEM_INFO);\n  minidump_system_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  ASSERT_TRUE(\n      string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));\n\n  MINIDUMP_THREAD minidump_thread = {};\n  uint32_t minidump_thread_count = 1;\n\n  minidump_thread.ThreadId = 42;\n  minidump_thread.Teb = 24;\n\n  MinidumpContextARM64 minidump_context = GetArm64MinidumpContext();\n\n  minidump_thread.ThreadContext.DataSize = sizeof(minidump_context);\n  minidump_thread.ThreadContext.Rva = static_cast<RVA>(string_file.SeekGet());\n\n  EXPECT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context)));\n\n  MINIDUMP_DIRECTORY minidump_thread_list_directory = {};\n  minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;\n  minidump_thread_list_directory.Location.DataSize =\n      sizeof(MINIDUMP_THREAD_LIST) +\n      minidump_thread_count * sizeof(MINIDUMP_THREAD);\n  minidump_thread_list_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  // Fields in MINIDUMP_THREAD_LIST.\n  EXPECT_TRUE(\n      string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count)));\n  EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,\n                                sizeof(minidump_system_info_directory)));\n  ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory,\n                                sizeof(minidump_thread_list_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 2;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();\n  ASSERT_EQ(threads.size(), minidump_thread_count);\n\n  const CPUContext* ctx_generic = threads[0]->Context();\n\n  ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureARM64);\n\n  const CPUContextARM64* ctx = ctx_generic->arm64;\n\n  EXPECT_EQ(ctx->spsr, 0UL);\n\n  for (unsigned int i = 0; i < 31; i++) {\n    EXPECT_EQ(ctx->regs[i], i + 1);\n  }\n\n  EXPECT_EQ(ctx->sp, 32UL);\n  EXPECT_EQ(ctx->pc, 33UL);\n  EXPECT_EQ(ctx->fpcr, 98UL);\n  EXPECT_EQ(ctx->fpsr, 99UL);\n\n  for (unsigned int i = 0; i < 32; i++) {\n    EXPECT_EQ(ctx->fpsimd[i].lo, i * 2 + 34);\n    EXPECT_EQ(ctx->fpsimd[i].hi, i * 2 + 35);\n  }\n}\n\nTEST(ProcessSnapshotMinidump, ThreadContextX86_64) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MINIDUMP_SYSTEM_INFO minidump_system_info = {};\n\n  minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64;\n  minidump_system_info.ProductType = kMinidumpOSTypeServer;\n  minidump_system_info.PlatformId = kMinidumpOSFuchsia;\n  minidump_system_info.CSDVersionRva = WriteString(&string_file, \"\");\n\n  MINIDUMP_DIRECTORY minidump_system_info_directory = {};\n  minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo;\n  minidump_system_info_directory.Location.DataSize =\n      sizeof(MINIDUMP_SYSTEM_INFO);\n  minidump_system_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  ASSERT_TRUE(\n      string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));\n\n  MINIDUMP_THREAD minidump_thread = {};\n  uint32_t minidump_thread_count = 1;\n\n  minidump_thread.ThreadId = 42;\n  minidump_thread.Teb = 24;\n\n  MinidumpContextAMD64 minidump_context;\n\n  minidump_context.context_flags = kMinidumpContextAMD64Full;\n\n  minidump_context.mx_csr = 0;\n  minidump_context.cs = 1;\n  minidump_context.ds = 2;\n  minidump_context.es = 3;\n  minidump_context.fs = 4;\n  minidump_context.gs = 5;\n  minidump_context.ss = 6;\n  minidump_context.eflags = 7;\n  minidump_context.dr0 = 8;\n  minidump_context.dr1 = 9;\n  minidump_context.dr2 = 10;\n  minidump_context.dr3 = 11;\n  minidump_context.dr6 = 12;\n  minidump_context.dr7 = 13;\n  minidump_context.rax = 14;\n  minidump_context.rcx = 15;\n  minidump_context.rdx = 16;\n  minidump_context.rbx = 17;\n  minidump_context.rsp = 18;\n  minidump_context.rbp = 19;\n  minidump_context.rsi = 20;\n  minidump_context.rdi = 21;\n  minidump_context.r8 = 22;\n  minidump_context.r9 = 23;\n  minidump_context.r10 = 24;\n  minidump_context.r11 = 25;\n  minidump_context.r12 = 26;\n  minidump_context.r13 = 27;\n  minidump_context.r14 = 28;\n  minidump_context.r15 = 29;\n  minidump_context.rip = 30;\n  minidump_context.vector_control = 31;\n  minidump_context.debug_control = 32;\n  minidump_context.last_branch_to_rip = 33;\n  minidump_context.last_branch_from_rip = 34;\n  minidump_context.last_exception_to_rip = 35;\n  minidump_context.last_exception_from_rip = 36;\n  minidump_context.fxsave.fcw = 37;\n  minidump_context.fxsave.fsw = 38;\n  minidump_context.fxsave.ftw = 39;\n  minidump_context.fxsave.reserved_1 = 40;\n  minidump_context.fxsave.fop = 41;\n  minidump_context.fxsave.fpu_ip_64 = 42;\n  minidump_context.fxsave.fpu_dp_64 = 43;\n\n  for (size_t i = 0; i < std::size(minidump_context.vector_register); i++) {\n    minidump_context.vector_register[i].lo = i * 2 + 44;\n    minidump_context.vector_register[i].hi = i * 2 + 45;\n  }\n\n  for (uint8_t i = 0; i < std::size(minidump_context.fxsave.reserved_4); i++) {\n    minidump_context.fxsave.reserved_4[i] = i * 2 + 115;\n    minidump_context.fxsave.available[i] = i * 2 + 116;\n  }\n\n  for (size_t i = 0; i < std::size(minidump_context.fxsave.st_mm); i++) {\n    for (uint8_t j = 0;\n         j < std::size(minidump_context.fxsave.st_mm[0].mm_value);\n         j++) {\n      minidump_context.fxsave.st_mm[i].mm_value[j] = j + 1;\n      minidump_context.fxsave.st_mm[i].mm_reserved[j] = j + 1;\n    }\n  }\n\n  for (size_t i = 0; i < std::size(minidump_context.fxsave.xmm); i++) {\n    for (uint8_t j = 0; j < std::size(minidump_context.fxsave.xmm[0]); j++) {\n      minidump_context.fxsave.xmm[i][j] = j + 1;\n    }\n  }\n\n  minidump_thread.ThreadContext.DataSize = sizeof(minidump_context);\n  minidump_thread.ThreadContext.Rva = static_cast<RVA>(string_file.SeekGet());\n\n  EXPECT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context)));\n\n  MINIDUMP_DIRECTORY minidump_thread_list_directory = {};\n  minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;\n  minidump_thread_list_directory.Location.DataSize =\n      sizeof(MINIDUMP_THREAD_LIST) +\n      minidump_thread_count * sizeof(MINIDUMP_THREAD);\n  minidump_thread_list_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  // Fields in MINIDUMP_THREAD_LIST.\n  EXPECT_TRUE(\n      string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count)));\n  EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,\n                                sizeof(minidump_system_info_directory)));\n  ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory,\n                                sizeof(minidump_thread_list_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 2;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();\n  ASSERT_EQ(threads.size(), minidump_thread_count);\n\n  const CPUContext* ctx_generic = threads[0]->Context();\n\n  ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureX86_64);\n\n  const CPUContextX86_64* ctx = ctx_generic->x86_64;\n  EXPECT_EQ(ctx->cs, 1);\n  EXPECT_EQ(ctx->fs, 4);\n  EXPECT_EQ(ctx->gs, 5);\n  EXPECT_EQ(ctx->rflags, 7UL);\n  EXPECT_EQ(ctx->dr0, 8UL);\n  EXPECT_EQ(ctx->dr1, 9U);\n  EXPECT_EQ(ctx->dr2, 10U);\n  EXPECT_EQ(ctx->dr3, 11U);\n  EXPECT_EQ(ctx->dr4, 12U);\n  EXPECT_EQ(ctx->dr5, 13U);\n  EXPECT_EQ(ctx->dr6, 12U);\n  EXPECT_EQ(ctx->dr7, 13U);\n  EXPECT_EQ(ctx->rax, 14U);\n  EXPECT_EQ(ctx->rcx, 15U);\n  EXPECT_EQ(ctx->rdx, 16U);\n  EXPECT_EQ(ctx->rbx, 17U);\n  EXPECT_EQ(ctx->rsp, 18U);\n  EXPECT_EQ(ctx->rbp, 19U);\n  EXPECT_EQ(ctx->rsi, 20U);\n  EXPECT_EQ(ctx->rdi, 21U);\n  EXPECT_EQ(ctx->r8, 22U);\n  EXPECT_EQ(ctx->r9, 23U);\n  EXPECT_EQ(ctx->r10, 24U);\n  EXPECT_EQ(ctx->r11, 25U);\n  EXPECT_EQ(ctx->r12, 26U);\n  EXPECT_EQ(ctx->r13, 27U);\n  EXPECT_EQ(ctx->r14, 28U);\n  EXPECT_EQ(ctx->r15, 29U);\n  EXPECT_EQ(ctx->rip, 30U);\n  EXPECT_EQ(ctx->fxsave.fcw, 37U);\n  EXPECT_EQ(ctx->fxsave.fsw, 38U);\n  EXPECT_EQ(ctx->fxsave.ftw, 39U);\n  EXPECT_EQ(ctx->fxsave.reserved_1, 40U);\n  EXPECT_EQ(ctx->fxsave.fop, 41U);\n  EXPECT_EQ(ctx->fxsave.fpu_ip_64, 42U);\n  EXPECT_EQ(ctx->fxsave.fpu_dp_64, 43U);\n\n  for (uint8_t i = 0; i < std::size(ctx->fxsave.reserved_4); i++) {\n    EXPECT_EQ(ctx->fxsave.reserved_4[i], i * 2 + 115);\n    EXPECT_EQ(ctx->fxsave.available[i], i * 2 + 116);\n  }\n\n  for (size_t i = 0; i < std::size(ctx->fxsave.st_mm); i++) {\n    for (uint8_t j = 0; j < std::size(ctx->fxsave.st_mm[0].mm_value); j++) {\n      EXPECT_EQ(ctx->fxsave.st_mm[i].mm_value[j], j + 1);\n      EXPECT_EQ(ctx->fxsave.st_mm[i].mm_reserved[j], j + 1);\n    }\n  }\n\n  for (size_t i = 0; i < std::size(ctx->fxsave.xmm); i++) {\n    for (uint8_t j = 0; j < std::size(ctx->fxsave.xmm[0]); j++) {\n      EXPECT_EQ(ctx->fxsave.xmm[i][j], j + 1);\n    }\n  }\n}\n\nTEST(ProcessSnapshotMinidump, MemoryMap) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MINIDUMP_MEMORY_INFO minidump_memory_info_1 = {};\n  MINIDUMP_MEMORY_INFO minidump_memory_info_2 = {};\n  uint32_t minidump_memory_info_count = 2;\n\n  minidump_memory_info_1.BaseAddress = 1;\n  minidump_memory_info_1.AllocationBase = 2;\n  minidump_memory_info_1.AllocationProtect = 3;\n  minidump_memory_info_1.RegionSize = 4;\n  minidump_memory_info_1.State = 5;\n  minidump_memory_info_1.Protect = 6;\n  minidump_memory_info_1.Type = 6;\n\n  minidump_memory_info_2.BaseAddress = 7;\n  minidump_memory_info_2.AllocationBase = 8;\n  minidump_memory_info_2.AllocationProtect = 9;\n  minidump_memory_info_2.RegionSize = 10;\n  minidump_memory_info_2.State = 11;\n  minidump_memory_info_2.Protect = 12;\n  minidump_memory_info_2.Type = 13;\n\n  MINIDUMP_MEMORY_INFO_LIST minidump_memory_info_list = {};\n\n  minidump_memory_info_list.SizeOfHeader = sizeof(minidump_memory_info_list);\n  minidump_memory_info_list.SizeOfEntry = sizeof(MINIDUMP_MEMORY_INFO);\n  minidump_memory_info_list.NumberOfEntries = minidump_memory_info_count;\n\n  MINIDUMP_DIRECTORY minidump_memory_info_list_directory = {};\n  minidump_memory_info_list_directory.StreamType =\n      kMinidumpStreamTypeMemoryInfoList;\n  minidump_memory_info_list_directory.Location.DataSize =\n      sizeof(minidump_memory_info_list) +\n      minidump_memory_info_count * sizeof(MINIDUMP_MEMORY_INFO);\n  minidump_memory_info_list_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  EXPECT_TRUE(string_file.Write(&minidump_memory_info_list,\n                                sizeof(minidump_memory_info_list)));\n  EXPECT_TRUE(string_file.Write(&minidump_memory_info_1,\n                                sizeof(minidump_memory_info_1)));\n  EXPECT_TRUE(string_file.Write(&minidump_memory_info_2,\n                                sizeof(minidump_memory_info_2)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  EXPECT_TRUE(string_file.Write(&minidump_memory_info_list_directory,\n                                sizeof(minidump_memory_info_list_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 1;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  std::vector<const MemoryMapRegionSnapshot*> map =\n      process_snapshot.MemoryMap();\n  ASSERT_EQ(map.size(), minidump_memory_info_count);\n  EXPECT_EQ(memcmp(&map[0]->AsMinidumpMemoryInfo(),\n                   &minidump_memory_info_1,\n                   sizeof(minidump_memory_info_1)),\n            0);\n  EXPECT_EQ(memcmp(&map[1]->AsMinidumpMemoryInfo(),\n                   &minidump_memory_info_2,\n                   sizeof(minidump_memory_info_2)),\n            0);\n}\n\nTEST(ProcessSnapshotMinidump, Stacks) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  MINIDUMP_THREAD minidump_thread = {};\n  uint32_t minidump_thread_count = 1;\n\n  minidump_thread.ThreadId = 42;\n  minidump_thread.Stack.StartOfMemoryRange = 0xbeefd00d;\n\n  std::vector<uint8_t> minidump_stack = {'1',\n                                         '2',\n                                         '3',\n                                         '4',\n                                         '5',\n                                         '6',\n                                         '7',\n                                         '8',\n                                         '9',\n                                         'a',\n                                         'b',\n                                         'c',\n                                         'd',\n                                         'e',\n                                         'f'};\n\n  minidump_thread.Stack.Memory.DataSize =\n      base::checked_cast<uint32_t>(minidump_stack.size());\n  minidump_thread.Stack.Memory.Rva = static_cast<RVA>(string_file.SeekGet());\n\n  EXPECT_TRUE(string_file.Write(minidump_stack.data(), minidump_stack.size()));\n\n  MINIDUMP_DIRECTORY minidump_thread_list_directory = {};\n  minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList;\n  minidump_thread_list_directory.Location.DataSize =\n      sizeof(MINIDUMP_THREAD_LIST) +\n      minidump_thread_count * sizeof(MINIDUMP_THREAD);\n  minidump_thread_list_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  // Fields in MINIDUMP_THREAD_LIST.\n  EXPECT_TRUE(\n      string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count)));\n  EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory,\n                                sizeof(minidump_thread_list_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 1;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  std::vector<const ThreadSnapshot*> threads = process_snapshot.Threads();\n  ASSERT_EQ(threads.size(), minidump_thread_count);\n\n  ReadToVector delegate;\n  threads[0]->Stack()->Read(&delegate);\n\n  EXPECT_EQ(delegate.result, minidump_stack);\n}\n\nTEST(ProcessSnapshotMinidump, CustomMinidumpStreams) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  ASSERT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  static const char kStreamReservedData[] = \"A string\";\n  static const char kStreamUnreservedData[] = \"Another string\";\n  // In the minidump reserved range\n  constexpr MinidumpStreamType kStreamTypeReserved1 =\n      (MinidumpStreamType)0x1111;\n  // In the crashpad reserved range\n  constexpr MinidumpStreamType kStreamTypeReserved2 =\n      (MinidumpStreamType)0x43501111;\n  constexpr MinidumpStreamType kStreamTypeUnreserved =\n      (MinidumpStreamType)0xffffffff;\n\n  MINIDUMP_DIRECTORY misc_directory = {};\n  RVA reserved1_offset = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(\n      string_file.Write(kStreamReservedData, sizeof(kStreamReservedData)));\n  RVA reserved2_offset = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(\n      string_file.Write(kStreamReservedData, sizeof(kStreamReservedData)));\n  RVA unreserved_offset = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(\n      string_file.Write(kStreamUnreservedData, sizeof(kStreamUnreservedData)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  misc_directory.StreamType = kStreamTypeReserved1;\n  misc_directory.Location.DataSize = sizeof(kStreamReservedData);\n  misc_directory.Location.Rva = reserved1_offset;\n  ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));\n\n  misc_directory.StreamType = kStreamTypeReserved2;\n  misc_directory.Location.DataSize = sizeof(kStreamReservedData);\n  misc_directory.Location.Rva = reserved2_offset;\n  ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));\n\n  misc_directory.StreamType = kStreamTypeUnreserved;\n  misc_directory.Location.DataSize = sizeof(kStreamUnreservedData);\n  misc_directory.Location.Rva = unreserved_offset;\n  ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 3;\n  ASSERT_TRUE(string_file.SeekSet(0));\n  ASSERT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  ASSERT_TRUE(process_snapshot.Initialize(&string_file));\n\n  auto custom_streams = process_snapshot.CustomMinidumpStreams();\n  ASSERT_EQ(custom_streams.size(), 1U);\n  EXPECT_EQ(custom_streams[0]->stream_type(), (uint32_t)kStreamTypeUnreserved);\n\n  auto stream_data = custom_streams[0]->data();\n  EXPECT_EQ(stream_data.size(), sizeof(kStreamUnreservedData));\n  EXPECT_STREQ((char*)&stream_data.front(), kStreamUnreservedData);\n}\n\nTEST(ProcessSnapshotMinidump, Exception) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  uint32_t exception_signo =\n      static_cast<uint32_t>(-1);  // crashpad::Signals::kSimulatedSigno\n\n  MINIDUMP_EXCEPTION minidump_exception = {};\n  minidump_exception.ExceptionCode = exception_signo;\n  minidump_exception.ExceptionFlags = 2;\n  minidump_exception.ExceptionRecord = 4;\n  minidump_exception.ExceptionAddress = 0xdeedb00f;\n  minidump_exception.NumberParameters = 2;\n  minidump_exception.ExceptionInformation[0] = 51;\n  minidump_exception.ExceptionInformation[1] = 62;\n\n  MINIDUMP_SYSTEM_INFO minidump_system_info = {};\n\n  minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureARM64;\n  minidump_system_info.ProductType = kMinidumpOSTypeServer;\n  minidump_system_info.PlatformId = kMinidumpOSFuchsia;\n  minidump_system_info.CSDVersionRva = WriteString(&string_file, \"\");\n\n  MINIDUMP_DIRECTORY minidump_system_info_directory = {};\n  minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo;\n  minidump_system_info_directory.Location.DataSize =\n      sizeof(MINIDUMP_SYSTEM_INFO);\n  minidump_system_info_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  ASSERT_TRUE(\n      string_file.Write(&minidump_system_info, sizeof(minidump_system_info)));\n\n  MINIDUMP_EXCEPTION_STREAM minidump_exception_stream = {};\n  minidump_exception_stream.ThreadId = 5;\n  minidump_exception_stream.ExceptionRecord = minidump_exception;\n\n  MinidumpContextARM64 minidump_context = GetArm64MinidumpContext();\n\n  minidump_exception_stream.ThreadContext.DataSize = sizeof(minidump_context);\n  minidump_exception_stream.ThreadContext.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  ASSERT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context)));\n\n  MINIDUMP_DIRECTORY minidump_exception_directory = {};\n  minidump_exception_directory.StreamType = kMinidumpStreamTypeException;\n  minidump_exception_directory.Location.DataSize =\n      sizeof(MINIDUMP_EXCEPTION_STREAM);\n  minidump_exception_directory.Location.Rva =\n      static_cast<RVA>(string_file.SeekGet());\n\n  ASSERT_TRUE(string_file.Write(&minidump_exception_stream,\n                                sizeof(minidump_exception_stream)));\n\n  header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());\n  ASSERT_TRUE(string_file.Write(&minidump_exception_directory,\n                                sizeof(minidump_exception_directory)));\n  ASSERT_TRUE(string_file.Write(&minidump_system_info_directory,\n                                sizeof(minidump_system_info_directory)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 2;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  const ExceptionSnapshot* s = process_snapshot.Exception();\n\n  EXPECT_EQ(s->ThreadID(), 5UL);\n  EXPECT_EQ(s->Exception(), exception_signo);\n  EXPECT_EQ(s->ExceptionInfo(), 2U);\n  EXPECT_EQ(s->ExceptionAddress(), 0xdeedb00f);\n\n  const std::vector<uint64_t> codes = s->Codes();\n  EXPECT_EQ(codes.size(), 2UL);\n  EXPECT_EQ(codes[0], 51UL);\n  EXPECT_EQ(codes[1], 62UL);\n\n  const CPUContext* ctx_generic = s->Context();\n\n  ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureARM64);\n\n  const CPUContextARM64* ctx = ctx_generic->arm64;\n\n  EXPECT_EQ(ctx->spsr, 0UL);\n\n  for (unsigned int i = 0; i < 31; i++) {\n    EXPECT_EQ(ctx->regs[i], i + 1);\n  }\n\n  EXPECT_EQ(ctx->sp, 32UL);\n  EXPECT_EQ(ctx->pc, 33UL);\n  EXPECT_EQ(ctx->fpcr, 98UL);\n  EXPECT_EQ(ctx->fpsr, 99UL);\n\n  for (unsigned int i = 0; i < 32; i++) {\n    EXPECT_EQ(ctx->fpsimd[i].lo, i * 2 + 34);\n    EXPECT_EQ(ctx->fpsimd[i].hi, i * 2 + 35);\n  }\n}\n\nTEST(ProcessSnapshotMinidump, NoExceptionInMinidump) {\n  StringFile string_file;\n\n  MINIDUMP_HEADER header = {};\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  header.Signature = MINIDUMP_SIGNATURE;\n  header.Version = MINIDUMP_VERSION;\n  header.NumberOfStreams = 0;\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_TRUE(string_file.Write(&header, sizeof(header)));\n\n  ProcessSnapshotMinidump process_snapshot;\n  EXPECT_TRUE(process_snapshot.Initialize(&string_file));\n\n  const ExceptionSnapshot* s = process_snapshot.Exception();\n  EXPECT_EQ(s, nullptr);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/system_snapshot_minidump.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/system_snapshot_minidump.h\"\n\n#include \"base/notreached.h\"\n#include \"snapshot/minidump/minidump_string_reader.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nSystemSnapshotMinidump::SystemSnapshotMinidump()\n    : SystemSnapshot(), minidump_system_info_(), initialized_() {}\n\nSystemSnapshotMinidump::~SystemSnapshotMinidump() {}\n\nbool SystemSnapshotMinidump::Initialize(FileReaderInterface* file_reader,\n                                        RVA minidump_system_info_rva,\n                                        const std::string& version) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  full_version_ = version;\n\n  if (!file_reader->SeekSet(minidump_system_info_rva)) {\n    return false;\n  }\n\n  if (!file_reader->ReadExactly(&minidump_system_info_,\n                                sizeof(minidump_system_info_))) {\n    return false;\n  }\n\n  if (!ReadMinidumpUTF8String(file_reader,\n                              minidump_system_info_.CSDVersionRva,\n                              &minidump_build_name_)) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nCPUArchitecture SystemSnapshotMinidump::GetCPUArchitecture() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  switch (minidump_system_info_.ProcessorArchitecture) {\n    case kMinidumpCPUArchitectureAMD64:\n      return kCPUArchitectureX86_64;\n    case kMinidumpCPUArchitectureX86:\n    case kMinidumpCPUArchitectureX86Win64:\n      return kCPUArchitectureX86;\n    case kMinidumpCPUArchitectureARM:\n    case kMinidumpCPUArchitectureARM32Win64:\n      return kCPUArchitectureARM;\n    case kMinidumpCPUArchitectureARM64:\n    case kMinidumpCPUArchitectureARM64Breakpad:\n      return kCPUArchitectureARM64;\n    case kMinidumpCPUArchitectureMIPS:\n      return kCPUArchitectureMIPSEL;\n    // No word on how MIPS64 is signalled\n    case kMinidumpCPUArchitectureRISCV64Breakpad:\n      return kCPUArchitectureRISCV64;\n\n    default:\n      return CPUArchitecture::kCPUArchitectureUnknown;\n  }\n}\n\nuint32_t SystemSnapshotMinidump::CPURevision() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_system_info_.ProcessorRevision;\n}\n\nuint8_t SystemSnapshotMinidump::CPUCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_system_info_.NumberOfProcessors;\n}\n\nstd::string SystemSnapshotMinidump::CPUVendor() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (GetCPUArchitecture() == kCPUArchitectureX86) {\n    const char* ptr = reinterpret_cast<const char*>(\n        minidump_system_info_.Cpu.X86CpuInfo.VendorId);\n    return std::string(ptr, ptr + (3 * sizeof(uint32_t)));\n  } else {\n    return std::string();\n  }\n}\n\nvoid SystemSnapshotMinidump::CPUFrequency(uint64_t* current_hz,\n                                          uint64_t* max_hz) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nuint32_t SystemSnapshotMinidump::CPUX86Signature() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nuint64_t SystemSnapshotMinidump::CPUX86Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nuint64_t SystemSnapshotMinidump::CPUX86ExtendedFeatures() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nuint32_t SystemSnapshotMinidump::CPUX86Leaf7Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nbool SystemSnapshotMinidump::CPUX86SupportsDAZ() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nSystemSnapshot::OperatingSystem SystemSnapshotMinidump::GetOperatingSystem()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  switch (minidump_system_info_.PlatformId) {\n    case kMinidumpOSMacOSX:\n      return OperatingSystem::kOperatingSystemMacOSX;\n    case kMinidumpOSWin32s:\n    case kMinidumpOSWin32Windows:\n    case kMinidumpOSWin32NT:\n      return OperatingSystem::kOperatingSystemWindows;\n    case kMinidumpOSLinux:\n      return OperatingSystem::kOperatingSystemLinux;\n    case kMinidumpOSAndroid:\n      return OperatingSystem::kOperatingSystemAndroid;\n    case kMinidumpOSFuchsia:\n      return OperatingSystem::kOperatingSystemFuchsia;\n    default:\n      return OperatingSystem::kOperatingSystemUnknown;\n  }\n}\n\nbool SystemSnapshotMinidump::OSServer() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_system_info_.ProductType == kMinidumpOSTypeServer;\n}\n\nvoid SystemSnapshotMinidump::OSVersion(int* major,\n                                       int* minor,\n                                       int* bugfix,\n                                       std::string* build) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *major = minidump_system_info_.MajorVersion;\n  *minor = minidump_system_info_.MinorVersion;\n  *bugfix = minidump_system_info_.BuildNumber;\n  *build = minidump_build_name_;\n}\n\nstd::string SystemSnapshotMinidump::OSVersionFull() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return full_version_;\n}\n\nstd::string SystemSnapshotMinidump::MachineDescription() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nbool SystemSnapshotMinidump::NXEnabled() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nvoid SystemSnapshotMinidump::TimeZone(DaylightSavingTimeStatus* dst_status,\n                                      int* standard_offset_seconds,\n                                      int* daylight_offset_seconds,\n                                      std::string* standard_name,\n                                      std::string* daylight_name) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\nuint64_t SystemSnapshotMinidump::AddressMask() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  NOTREACHED();  // https://crashpad.chromium.org/bug/10\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/system_snapshot_minidump.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_\n\n#include <windows.h>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A SystemSnapshot based on a minidump file.\nclass SystemSnapshotMinidump : public SystemSnapshot {\n public:\n  SystemSnapshotMinidump();\n\n  SystemSnapshotMinidump(const SystemSnapshotMinidump&) = delete;\n  SystemSnapshotMinidump& operator=(const SystemSnapshotMinidump&) = delete;\n\n  ~SystemSnapshotMinidump() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] file_reader A file reader corresponding to a minidump file.\n  //!     The file reader must support seeking.\n  //! \\param[in] minidump_system_info_rva The file offset in \\a file_reader at\n  //!     which the thread’s MINIDUMP_SYSTEM_INFO structure is located.\n  //! \\param[in] version The OS version taken from the build string in\n  //!     MINIDUMP_MISC_INFO_4.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(FileReaderInterface* file_reader,\n                  RVA minidump_system_info_rva,\n                  const std::string& version);\n\n  CPUArchitecture GetCPUArchitecture() const override;\n  uint32_t CPURevision() const override;\n  uint8_t CPUCount() const override;\n  std::string CPUVendor() const override;\n  void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;\n  uint32_t CPUX86Signature() const override;\n  uint64_t CPUX86Features() const override;\n  uint64_t CPUX86ExtendedFeatures() const override;\n  uint32_t CPUX86Leaf7Features() const override;\n  bool CPUX86SupportsDAZ() const override;\n  OperatingSystem GetOperatingSystem() const override;\n  bool OSServer() const override;\n  void OSVersion(int* major,\n                 int* minor,\n                 int* bugfix,\n                 std::string* build) const override;\n  std::string OSVersionFull() const override;\n  std::string MachineDescription() const override;\n  bool NXEnabled() const override;\n  void TimeZone(DaylightSavingTimeStatus* dst_status,\n                int* standard_offset_seconds,\n                int* daylight_offset_seconds,\n                std::string* standard_name,\n                std::string* daylight_name) const override;\n  uint64_t AddressMask() const override;\n\n private:\n  MINIDUMP_SYSTEM_INFO minidump_system_info_;\n  std::string minidump_build_name_;\n  std::string full_version_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_\n"
  },
  {
    "path": "snapshot/minidump/thread_snapshot_minidump.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/minidump/thread_snapshot_minidump.h\"\n\n#include <stddef.h>\n#include <string.h>\n\n#include <algorithm>\n\n#include \"minidump/minidump_context.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nThreadSnapshotMinidump::ThreadSnapshotMinidump()\n    : ThreadSnapshot(),\n      minidump_thread_(),\n      thread_name_(),\n      context_(),\n      stack_(),\n      initialized_() {}\n\nThreadSnapshotMinidump::~ThreadSnapshotMinidump() {}\n\nbool ThreadSnapshotMinidump::Initialize(\n    FileReaderInterface* file_reader,\n    RVA minidump_thread_rva,\n    CPUArchitecture arch,\n    const std::map<uint32_t, std::string>& thread_names) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  std::vector<unsigned char> minidump_context;\n\n  if (!file_reader->SeekSet(minidump_thread_rva)) {\n    return false;\n  }\n\n  if (!file_reader->ReadExactly(&minidump_thread_, sizeof(minidump_thread_))) {\n    return false;\n  }\n\n  if (!file_reader->SeekSet(minidump_thread_.ThreadContext.Rva)) {\n    return false;\n  }\n\n  minidump_context.resize(minidump_thread_.ThreadContext.DataSize);\n\n  if (!file_reader->ReadExactly(minidump_context.data(),\n                                minidump_context.size())) {\n    return false;\n  }\n\n  if (!context_.Initialize(arch, minidump_context)) {\n    return false;\n  }\n\n  RVA stack_info_location =\n      minidump_thread_rva + offsetof(MINIDUMP_THREAD, Stack);\n\n  if (!stack_.Initialize(file_reader, stack_info_location)) {\n    return false;\n  }\n  const auto thread_name_iter = thread_names.find(minidump_thread_.ThreadId);\n  if (thread_name_iter != thread_names.end()) {\n    thread_name_ = thread_name_iter->second;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nuint64_t ThreadSnapshotMinidump::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_thread_.ThreadId;\n}\n\nstd::string ThreadSnapshotMinidump::ThreadName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_name_;\n}\n\nint ThreadSnapshotMinidump::SuspendCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_thread_.SuspendCount;\n}\n\nuint64_t ThreadSnapshotMinidump::ThreadSpecificDataAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_thread_.Teb;\n}\n\nint ThreadSnapshotMinidump::Priority() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return minidump_thread_.Priority;\n}\n\nconst CPUContext* ThreadSnapshotMinidump::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return context_.Get();\n}\n\nconst MemorySnapshot* ThreadSnapshotMinidump::Stack() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &stack_;\n}\n\nstd::vector<const MemorySnapshot*> ThreadSnapshotMinidump::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // This doesn't correspond to anything minidump can give us, with the\n  // exception of the BackingStore field in the MINIDUMP_THREAD_EX structure,\n  // which is only valid for IA-64.\n  return std::vector<const MemorySnapshot*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/minidump/thread_snapshot_minidump.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_\n#define CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_\n\n#include <windows.h>\n\n#include <map>\n\n#include \"minidump/minidump_extensions.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/minidump/memory_snapshot_minidump.h\"\n#include \"snapshot/minidump/minidump_context_converter.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ThreadSnapshot based on a thread in a minidump file.\nclass ThreadSnapshotMinidump : public ThreadSnapshot {\n public:\n  ThreadSnapshotMinidump();\n\n  ThreadSnapshotMinidump(const ThreadSnapshotMinidump&) = delete;\n  ThreadSnapshotMinidump& operator=(const ThreadSnapshotMinidump&) = delete;\n\n  ~ThreadSnapshotMinidump() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] file_reader A file reader corresponding to a minidump file.\n  //!     The file reader must support seeking.\n  //! \\param[in] minidump_thread_rva The file offset in \\a file_reader at which\n  //!     the thread’s MINIDUMP_THREAD structure is located.\n  //! \\param[in] arch The architecture of the system this thread is running on.\n  //!     Used to decode CPU Context.\n  //! \\param[in] thread_names Map from thread ID to thread name previously read\n  //!     from the minidump's MINIDUMP_THREAD_NAME_LIST.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(FileReaderInterface* file_reader,\n                  RVA minidump_thread_rva,\n                  CPUArchitecture arch,\n                  const std::map<uint32_t, std::string>& thread_names);\n\n  const CPUContext* Context() const override;\n  const MemorySnapshot* Stack() const override;\n  uint64_t ThreadID() const override;\n  std::string ThreadName() const override;\n  int SuspendCount() const override;\n  int Priority() const override;\n  uint64_t ThreadSpecificDataAddress() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  //! \\brief Initializes the CPU Context\n  //!\n  //! \\param[in] minidump_context the raw bytes of the context data from the\n  //!     minidump file.\n  //!\n  //! \\return `true` if the context could be decoded without error.\n  bool InitializeContext(const std::vector<unsigned char>& minidump_context);\n\n  MINIDUMP_THREAD minidump_thread_;\n  std::string thread_name_;\n  MinidumpContextConverter context_;\n  MemorySnapshotMinidump stack_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_\n"
  },
  {
    "path": "snapshot/module_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"snapshot/annotation_snapshot.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\n\nclass MemorySnapshot;\n\n//! \\brief Information describing a custom user data stream in a minidump.\nclass UserMinidumpStream {\n public:\n  //! \\brief Constructs a UserMinidumpStream, takes ownership of \\a memory.\n  UserMinidumpStream(uint32_t stream_type, MemorySnapshot* memory)\n      : memory_(memory), stream_type_(stream_type) {}\n\n  UserMinidumpStream(const UserMinidumpStream&) = delete;\n  UserMinidumpStream& operator=(const UserMinidumpStream&) = delete;\n\n  const MemorySnapshot* memory() const { return memory_.get(); }\n  uint32_t stream_type() const { return stream_type_; }\n\n private:\n  //! \\brief The memory representing the custom minidump stream.\n  std::unique_ptr<MemorySnapshot> memory_;\n\n  //! \\brief The stream type that the minidump stream will be tagged with.\n  uint32_t stream_type_;\n};\n\n//! \\brief An abstract interface to a snapshot representing a code module\n//!     (binary image) loaded into a snapshot process.\nclass ModuleSnapshot {\n public:\n  virtual ~ModuleSnapshot() {}\n\n  //! \\brief A module’s type.\n  enum ModuleType {\n    //! \\brief The module’s type is unknown.\n    kModuleTypeUnknown = 0,\n\n    //! \\brief The module is a main executable.\n    kModuleTypeExecutable,\n\n    //! \\brief The module is a shared library.\n    //!\n    //! \\sa kModuleTypeLoadableModule\n    kModuleTypeSharedLibrary,\n\n    //! \\brief The module is a loadable module.\n    //!\n    //! On some platforms, loadable modules are distinguished from shared\n    //! libraries. On these platforms, a shared library is a module that another\n    //! module links against directly, and a loadable module is not. Loadable\n    //! modules tend to be binary plug-ins.\n    kModuleTypeLoadableModule,\n\n    //! \\brief The module is a dynamic loader.\n    //!\n    //! This is the module responsible for loading other modules. This is\n    //! normally `dyld` for macOS and `ld.so` for Linux and other systems using\n    //! ELF.\n    kModuleTypeDynamicLoader,\n  };\n\n  //! \\brief Returns the module’s pathname.\n  virtual std::string Name() const = 0;\n\n  //! \\brief Returns the base address that the module is loaded at in the\n  //!     snapshot process.\n  virtual uint64_t Address() const = 0;\n\n  //! \\brief Returns the size that the module occupies in the snapshot process’\n  //!     address space, starting at its base address.\n  //!\n  //! For macOS snapshots, this method only reports the size of the `__TEXT`\n  //! segment, because segments may not be loaded contiguously.\n  virtual uint64_t Size() const = 0;\n\n  //! \\brief Returns the module’s timestamp, if known.\n  //!\n  //! The timestamp is typically the modification time of the file that provided\n  //! the module in `time_t` format, seconds since the POSIX epoch. If the\n  //! module’s timestamp is unknown, this method returns `0`.\n  virtual time_t Timestamp() const = 0;\n\n  //! \\brief Returns the module’s file version in the \\a version_* parameters.\n  //!\n  //! If no file version can be determined, the \\a version_* parameters are set\n  //! to `0`.\n  //!\n  //! For macOS snapshots, this is taken from the module’s `LC_ID_DYLIB` load\n  //! command for shared libraries, and is `0` for other module types.\n  virtual void FileVersion(uint16_t* version_0,\n                           uint16_t* version_1,\n                           uint16_t* version_2,\n                           uint16_t* version_3) const = 0;\n\n  //! \\brief Returns the module’s source version in the \\a version_* parameters.\n  //!\n  //! If no source version can be determined, the \\a version_* parameters are\n  //! set to `0`.\n  //!\n  //! For macOS snapshots, this is taken from the module’s `LC_SOURCE_VERSION`\n  //! load command.\n  virtual void SourceVersion(uint16_t* version_0,\n                             uint16_t* version_1,\n                             uint16_t* version_2,\n                             uint16_t* version_3) const = 0;\n\n  //! \\brief Returns the module’s type.\n  virtual ModuleType GetModuleType() const = 0;\n\n  //! \\brief Returns the module’s UUID in the \\a uuid parameter, and the age of\n  //!     that UUID in \\a age.\n  //!\n  //! A snapshot module’s UUID is taken directly from the module itself. If the\n  //! module does not have a UUID, the \\a uuid parameter will be zeroed out.\n  //!\n  //! \\a age is the number of times the UUID has been reused. This occurs on\n  //! Windows with incremental linking. On other platforms \\a age will always be\n  //! `0`.\n  //!\n  //! \\sa BuildID()\n  //! \\sa DebugFileName()\n  virtual void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const = 0;\n\n  //! \\brief Returns the module’s debug file info name.\n  //!\n  //! On Windows, this references the PDB file, which contains symbol\n  //! information held separately from the module itself. On other platforms,\n  //! this is normally the basename of the module, because the debug info file’s\n  //! name is not relevant even in split-debug scenarios.\n  //!\n  //! \\sa UUIDAndAge()\n  virtual std::string DebugFileName() const = 0;\n\n  //! \\brief Returns the module’s build ID.\n  //!\n  //! On ELF platforms, the build ID is a variable-length byte stream that\n  //! identifies a library uniquely, and is usually used to look up its debug\n  //! symbols when stored separately. This will return an empty vector if it is\n  //! unsupported.\n  //!\n  //! BuildID() and UUIDAndAge() are never available in the same place. When\n  //! UUIDAndAge() is unavailable, it will be filled out with the contents of\n  //! BuildID() (either 0-padded or truncated) and age will be zero.\n  //!\n  //! \\sa UUIDAndAge()\n  virtual std::vector<uint8_t> BuildID() const = 0;\n\n  //! \\brief Returns string annotations recorded in the module.\n  //!\n  //! This method retrieves annotations recorded in a module. These annotations\n  //! are intended for diagnostic use, including crash analysis. A module may\n  //! contain multiple annotations, so they are returned in a vector.\n  //!\n  //! For macOS snapshots, these annotations are found by interpreting the\n  //! module’s `__DATA,__crash_info` section as `crashreporter_annotations_t`.\n  //! System libraries using the crash reporter client interface may reference\n  //! annotations in this structure. Additional annotations messages may be\n  //! found in other locations, which may be module-specific. The dynamic linker\n  //! (`dyld`) can provide an annotation at its `_error_string` symbol.\n  //!\n  //! The annotations returned by this method do not duplicate those returned by\n  //! AnnotationsSimpleMap() or AnnotationObjects().\n  virtual std::vector<std::string> AnnotationsVector() const = 0;\n\n  //! \\brief Returns key-value string annotations recorded in the module.\n  //!\n  //! This method retrieves annotations recorded in a module. These annotations\n  //! are intended for diagnostic use, including crash analysis. “Simple\n  //! annotations” are structured as a sequence of key-value pairs, where all\n  //! keys and values are strings. These are referred to in Chrome as “crash\n  //! keys.”\n  //!\n  //! For macOS snapshots, these annotations are found by interpreting the\n  //! `__DATA,crashpad_info` section as `CrashpadInfo`. Clients can use the\n  //! Crashpad client interface to store annotations in this structure. Most\n  //! annotations under the client’s direct control will be retrievable by this\n  //! method. For clients such as Chrome, this includes the process type.\n  //!\n  //! The annotations returned by this method do not duplicate those returned by\n  //! AnnotationsVector() or AnnotationObjects(). Additional annotations related\n  //! to the process, system, or snapshot producer may be obtained by calling\n  //! ProcessSnapshot::AnnotationsSimpleMap().\n  virtual std::map<std::string, std::string> AnnotationsSimpleMap() const = 0;\n\n  //! \\brief Returns the typed annotation objects recorded in the module.\n  //!\n  //! This method retrieves annotations recorded in a module. These annotations\n  //! are intended for diagnostic use, including crash analysis. Annotation\n  //! objects are strongly-typed name-value pairs. The names are not unique.\n  //!\n  //! For macOS snapshots, these annotations are found by interpreting the\n  //! `__DATA,crashpad_info` section as `CrashpadInfo`. Clients can use the\n  //! Crashpad client interface to store annotations in this structure. Most\n  //! annotations under the client’s direct control will be retrievable by this\n  //! method. For clients such as Chrome, this includes the process type.\n  //!\n  //! The annotations returned by this method do not duplicate those returned by\n  //! AnnotationsVector() or AnnotationsSimpleMap().\n  virtual std::vector<AnnotationSnapshot> AnnotationObjects() const = 0;\n\n  //! \\brief Returns a set of extra memory ranges specified in the module as\n  //!     being desirable to include in the crash dump.\n  virtual std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const = 0;\n\n  //! \\brief Returns a list of custom minidump stream specified in the module to\n  //!     be included in the crash dump.\n  //!\n  //! \\return The caller does not take ownership of the returned objects, they\n  //!     are scoped to the lifetime of the ModuleSnapshot object that they were\n  //!     obtained from.\n  virtual std::vector<const UserMinidumpStream*> CustomMinidumpStreams()\n      const = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/posix/timezone.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/posix/timezone.h\"\n\n#include <stddef.h>\n#include <time.h>\n\n#include <iterator>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nvoid TimeZone(const timeval& snapshot_time,\n              SystemSnapshot::DaylightSavingTimeStatus* dst_status,\n              int* standard_offset_seconds,\n              int* daylight_offset_seconds,\n              std::string* standard_name,\n              std::string* daylight_name) {\n  tzset();\n\n  tm local;\n  PCHECK(localtime_r(&snapshot_time.tv_sec, &local)) << \"localtime_r\";\n\n  *standard_name = tzname[0];\n\n  bool found_transition = false;\n  long probe_gmtoff = local.tm_gmtoff;\n#if BUILDFLAG(IS_ANDROID)\n  // Some versions of the timezone database on Android have incorrect\n  // information (e.g. Asia/Kolkata and Pacific/Honolulu). These timezones set\n  // daylight to a non-zero value and return incorrect, >= 0 values for tm_isdst\n  // in the probes below. If tzname[1] is set to a bogus value, assume the\n  // timezone does not actually use daylight saving time.\n  if (daylight && strncmp(tzname[1], \"_TZif\", 5) != 0) {\n#else\n  if (daylight) {\n#endif\n    // Scan forward and backward, one month at a time, looking for an instance\n    // when the observance of daylight saving time is different than it is in\n    // |local|. It’s possible that no such instance will be found even with\n    // |daylight| set. This can happen in locations where daylight saving time\n    // was once observed or is expected to be observed in the future, but where\n    // no transitions to or from daylight saving time occurred or will occur\n    // within a year of the current date. Arizona, which last observed daylight\n    // saving time in 1967, is an example.\n    static constexpr int kMonthDeltas[] =\n        {0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6,\n         7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12};\n    for (size_t index = 0; index < std::size(kMonthDeltas) && !found_transition;\n         ++index) {\n      // Look at a day of each month at local noon. Set tm_isdst to -1 to avoid\n      // giving mktime() any hints about whether to consider daylight saving\n      // time in effect. mktime() accepts values of tm_mon that are outside of\n      // its normal range and behaves as expected: if tm_mon is -1, it\n      // references December of the preceding year, and if it is 12, it\n      // references January of the following year.\n      tm probe_tm = {};\n      probe_tm.tm_hour = 12;\n      probe_tm.tm_mday = std::min(local.tm_mday, 28);\n      probe_tm.tm_mon = local.tm_mon + kMonthDeltas[index];\n      probe_tm.tm_year = local.tm_year;\n      probe_tm.tm_isdst = -1;\n      if (mktime(&probe_tm) == -1) {\n        PLOG(WARNING) << \"mktime\";\n        continue;\n      }\n      if (probe_tm.tm_isdst < 0 || local.tm_isdst < 0) {\n        LOG(WARNING) << \"dst status not available\";\n        continue;\n      }\n      if (probe_tm.tm_isdst != local.tm_isdst) {\n        found_transition = true;\n        probe_gmtoff = probe_tm.tm_gmtoff;\n      }\n    }\n  }\n\n  if (found_transition) {\n    *daylight_name = tzname[1];\n    if (!local.tm_isdst) {\n      *dst_status = SystemSnapshot::kObservingStandardTime;\n      *standard_offset_seconds = local.tm_gmtoff;\n      *daylight_offset_seconds = probe_gmtoff;\n    } else {\n      *dst_status = SystemSnapshot::kObservingDaylightSavingTime;\n      *standard_offset_seconds = probe_gmtoff;\n      *daylight_offset_seconds = local.tm_gmtoff;\n    }\n  } else {\n    *daylight_name = tzname[0];\n    *dst_status = SystemSnapshot::kDoesNotObserveDaylightSavingTime;\n#if BUILDFLAG(IS_ANDROID)\n    // timezone is more reliably set correctly on Android.\n    *standard_offset_seconds = -timezone;\n    *daylight_offset_seconds = -timezone;\n#else\n    *standard_offset_seconds = local.tm_gmtoff;\n    *daylight_offset_seconds = local.tm_gmtoff;\n#endif  // BUILDFLAG(IS_ANDROID)\n  }\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/posix/timezone.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_POSIX_TIMEZONE_H_\n#define CRASHPAD_SNAPSHOT_POSIX_TIMEZONE_H_\n\n#include <sys/time.h>\n\n#include <string>\n\n#include \"snapshot/system_snapshot.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Returns time zone information from the snapshot system, based on\n//!     its locale configuration and \\a snapshot_time.\n//!\n//! \\param[in] snapshot_time The time to use collect daylight saving time status\n//!     for, given in time since Epoch.\n//! \\param[out] dst_status Whether the location observes daylight saving time,\n//!     and if so, whether it or standard time is currently being observed.\n//! \\param[out] standard_offset_seconds The number of seconds that the\n//!     location’s time zone is east (ahead) of UTC during standard time.\n//! \\param[out] daylight_offset_seconds The number of seconds that the\n//!     location’s time zone is east (ahead) of UTC during daylight saving.\n//!     time.\n//! \\param[out] standard_name The name of the time zone while standard time is\n//!     being observed.\n//! \\param[out] daylight_name The name of the time zone while daylight saving\n//!     time is being observed.\n//!\n//! \\sa SystemSnapshot::TimeZone\nvoid TimeZone(const timeval& snapshot_time,\n              SystemSnapshot::DaylightSavingTimeStatus* dst_status,\n              int* standard_offset_seconds,\n              int* daylight_offset_seconds,\n              std::string* standard_name,\n              std::string* daylight_name);\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_POSIX_TIMEZONE_H_\n"
  },
  {
    "path": "snapshot/posix/timezone_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/posix/timezone.h\"\n\n#include <stdlib.h>\n#include <sys/time.h>\n#include <time.h>\n\n#include <iterator>\n#include <string>\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass ScopedSetTZ {\n public:\n  ScopedSetTZ(const std::string& tz) {\n    const char* old_tz = getenv(kTZ);\n    old_tz_set_ = old_tz;\n    if (old_tz_set_) {\n      old_tz_.assign(old_tz);\n    }\n\n    EXPECT_EQ(setenv(kTZ, tz.c_str(), 1), 0) << ErrnoMessage(\"setenv\");\n    tzset();\n  }\n\n  ScopedSetTZ(const ScopedSetTZ&) = delete;\n  ScopedSetTZ& operator=(const ScopedSetTZ&) = delete;\n\n  ~ScopedSetTZ() {\n    if (old_tz_set_) {\n      EXPECT_EQ(setenv(kTZ, old_tz_.c_str(), 1), 0) << ErrnoMessage(\"setenv\");\n    } else {\n      EXPECT_EQ(unsetenv(kTZ), 0) << ErrnoMessage(\"unsetenv\");\n    }\n    tzset();\n  }\n\n private:\n  std::string old_tz_;\n  bool old_tz_set_;\n\n  static constexpr char kTZ[] = \"TZ\";\n};\n\nconstexpr char ScopedSetTZ::kTZ[];\n\nTEST(TimeZone, Basic) {\n  SystemSnapshot::DaylightSavingTimeStatus dst_status;\n  int standard_offset_seconds;\n  int daylight_offset_seconds;\n  std::string standard_name;\n  std::string daylight_name;\n\n  timeval snapshot_time;\n  ASSERT_EQ(gettimeofday(&snapshot_time, nullptr), 0);\n\n  internal::TimeZone(snapshot_time,\n                     &dst_status,\n                     &standard_offset_seconds,\n                     &daylight_offset_seconds,\n                     &standard_name,\n                     &daylight_name);\n\n  // |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives\n  // seconds west of UTC.\n  EXPECT_EQ(standard_offset_seconds, -timezone);\n\n  // In contemporary usage, most time zones have an integer hour offset from\n  // UTC, although several are at a half-hour offset, and two are at 15-minute\n  // offsets. Throughout history, other variations existed. See\n  // https://www.timeanddate.com/time/time-zones-interesting.html.\n  EXPECT_EQ(standard_offset_seconds % (15 * 60), 0)\n      << \"standard_offset_seconds \" << standard_offset_seconds;\n\n  if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) {\n    EXPECT_EQ(daylight_offset_seconds, standard_offset_seconds);\n    EXPECT_EQ(daylight_name, standard_name);\n  } else {\n    EXPECT_EQ(daylight_offset_seconds % (15 * 60), 0)\n        << \"daylight_offset_seconds \" << daylight_offset_seconds;\n\n    // In contemporary usage, dst_delta_seconds will almost always be one hour,\n    // except for Lord Howe Island, Australia, which uses a 30-minute delta.\n    // Throughout history, other variations existed. See\n    // https://www.timeanddate.com/time/dst/.\n    int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds;\n    if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) {\n      FAIL() << \"dst_delta_seconds \" << dst_delta_seconds;\n    }\n\n    EXPECT_NE(standard_name, daylight_name);\n  }\n\n  // Test a variety of time zones. Some of these observe daylight saving time,\n  // some don’t. Some used to but no longer do. Some have uncommon UTC offsets.\n  // standard_name and daylight_name can be nullptr where no name exists to\n  // verify, as may happen when some versions of the timezone database carry\n  // invented names and others do not.\n  static constexpr struct {\n    const char* tz;\n    bool observes_dst;\n    float standard_offset_hours;\n    float daylight_offset_hours;\n    const char* standard_name;\n    const char* daylight_name;\n  } kTestTimeZones[] = {\n      {\"America/Anchorage\", true, -9, -8, \"AKST\", \"AKDT\"},\n      {\"America/Chicago\", true, -6, -5, \"CST\", \"CDT\"},\n      {\"America/Denver\", true, -7, -6, \"MST\", \"MDT\"},\n      {\"America/Halifax\", true, -4, -3, \"AST\", \"ADT\"},\n      {\"America/Los_Angeles\", true, -8, -7, \"PST\", \"PDT\"},\n      {\"America/New_York\", true, -5, -4, \"EST\", \"EDT\"},\n      {\"America/Phoenix\", false, -7, -7, \"MST\", \"MST\"},\n      {\"Asia/Karachi\", false, 5, 5, \"PKT\", \"PKT\"},\n      {\"Asia/Kolkata\", false, 5.5, 5.5, \"IST\", \"IST\"},\n      {\"Asia/Shanghai\", false, 8, 8, \"CST\", \"CST\"},\n      {\"Asia/Tokyo\", false, 9, 9, \"JST\", \"JST\"},\n\n      // Australian timezone names have an optional \"A\" prefix, which is\n      // present for glibc and macOS, but missing on Android.\n      {\"Australia/Adelaide\", true, 9.5, 10.5, nullptr, nullptr},\n      {\"Australia/Brisbane\", false, 10, 10, nullptr, nullptr},\n      {\"Australia/Darwin\", false, 9.5, 9.5, nullptr, nullptr},\n      {\"Australia/Eucla\", false, 8.75, 8.75, nullptr, nullptr},\n      {\"Australia/Lord_Howe\", true, 10.5, 11, nullptr, nullptr},\n      {\"Australia/Perth\", false, 8, 8, nullptr, nullptr},\n      {\"Australia/Sydney\", true, 10, 11, nullptr, nullptr},\n\n      {\"Europe/Bucharest\", true, 2, 3, \"EET\", \"EEST\"},\n      {\"Europe/London\", true, 0, 1, \"GMT\", \"BST\"},\n      {\"Europe/Paris\", true, 1, 2, \"CET\", \"CEST\"},\n      {\"Europe/Reykjavik\", false, 0, 0, nullptr, nullptr},\n      {\"Pacific/Auckland\", true, 12, 13, \"NZST\", \"NZDT\"},\n      {\"Pacific/Honolulu\", false, -10, -10, \"HST\", \"HST\"},\n      {\"UTC\", false, 0, 0, \"UTC\", \"UTC\"},\n  };\n\n  for (size_t index = 0; index < std::size(kTestTimeZones); ++index) {\n    const auto& test_time_zone = kTestTimeZones[index];\n    const char* tz = test_time_zone.tz;\n    SCOPED_TRACE(base::StringPrintf(\"index %zu, tz %s\", index, tz));\n\n    {\n      ScopedSetTZ set_tz(tz);\n      internal::TimeZone(snapshot_time,\n                         &dst_status,\n                         &standard_offset_seconds,\n                         &daylight_offset_seconds,\n                         &standard_name,\n                         &daylight_name);\n    }\n\n    EXPECT_PRED2(\n        [](SystemSnapshot::DaylightSavingTimeStatus dst, bool observes) {\n          return (dst != SystemSnapshot::kDoesNotObserveDaylightSavingTime) ==\n                 observes;\n        },\n        dst_status,\n        test_time_zone.observes_dst);\n\n    EXPECT_EQ(standard_offset_seconds,\n              test_time_zone.standard_offset_hours * 60 * 60);\n    EXPECT_EQ(daylight_offset_seconds,\n              test_time_zone.daylight_offset_hours * 60 * 60);\n    if (test_time_zone.standard_name) {\n      EXPECT_EQ(standard_name, test_time_zone.standard_name);\n    }\n    if (test_time_zone.daylight_name) {\n      EXPECT_EQ(daylight_name, test_time_zone.daylight_name);\n    }\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/process_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_\n\n#include <sys/time.h>\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"snapshot/handle_snapshot.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/process/process_id.h\"\n\nnamespace crashpad {\n\nclass ExceptionSnapshot;\nclass MemoryMapRegionSnapshot;\nclass MemorySnapshot;\nclass ModuleSnapshot;\nclass ProcessMemory;\nclass SystemSnapshot;\nclass ThreadSnapshot;\nclass UnloadedModuleSnapshot;\n\n//! \\brief An abstract interface to a snapshot representing the state of a\n//!     process.\n//!\n//! This is the top-level object in a family of Snapshot objects, because it\n//! gives access to a SystemSnapshot, vectors of ModuleSnapshot and\n//! ThreadSnapshot objects, and possibly an ExceptionSnapshot. In turn,\n//! ThreadSnapshot and ExceptionSnapshot objects both give access to CPUContext\n//! objects, and ThreadSnapshot objects also give access to MemorySnapshot\n//! objects corresponding to thread stacks.\nclass ProcessSnapshot {\n public:\n  virtual ~ProcessSnapshot() {}\n\n  //! \\brief Returns the snapshot process’ process ID.\n  virtual crashpad::ProcessID ProcessID() const = 0;\n\n  //! \\brief Returns the snapshot process’ parent process’ process ID.\n  virtual crashpad::ProcessID ParentProcessID() const = 0;\n\n  //! \\brief Returns the time that the snapshot was taken in \\a snapshot_time.\n  //!\n  //! \\param[out] snapshot_time The time that the snapshot was taken. This is\n  //!     distinct from the time that a ProcessSnapshot object was created or\n  //!     initialized, although it may be that time for ProcessSnapshot objects\n  //!     representing live or recently-crashed process state.\n  virtual void SnapshotTime(timeval* snapshot_time) const = 0;\n\n  //! \\brief Returns the time that the snapshot process was started in \\a\n  //!     start_time.\n  //!\n  //! Normally, process uptime in wall clock time can be computed as\n  //! SnapshotTime() − ProcessStartTime(), but this cannot be guaranteed in\n  //! cases where the real-time clock has been set during the snapshot process’\n  //! lifetime.\n  //!\n  //! \\param[out] start_time The time that the process was started.\n  virtual void ProcessStartTime(timeval* start_time) const = 0;\n\n  //! \\brief Returns the snapshot process’ CPU usage times in \\a user_time and\n  //!     \\a system_time.\n  //!\n  //! \\param[out] user_time The time that the process has spent executing in\n  //!     user mode.\n  //! \\param[out] system_time The time that the process has spent executing in\n  //!     system (kernel) mode.\n  virtual void ProcessCPUTimes(timeval* user_time,\n                               timeval* system_time) const = 0;\n\n  //! \\brief Returns a %UUID identifying the event that the snapshot describes.\n  //!\n  //! This provides a stable identifier for a crash even as the report is\n  //! converted to different formats, provided that all formats support storing\n  //! a crash report ID. When a report is originally created, a report ID should\n  //! be assigned. From that point on, any operations involving the same report\n  //! should preserve the same report ID.\n  //!\n  //! If no identifier is available, this field will contain zeroes.\n  virtual void ReportID(UUID* client_id) const = 0;\n\n  //! \\brief Returns a %UUID identifying the client that the snapshot\n  //!     represents.\n  //!\n  //! Client identification is within the scope of the application, but it is\n  //! expected that the identifier will be unique for an instance of Crashpad\n  //! monitoring an application or set of applications for a user. The\n  //! identifier shall remain stable over time.\n  //!\n  //! If no identifier is available, this field will contain zeroes.\n  virtual void ClientID(UUID* client_id) const = 0;\n\n  //! \\brief Returns key-value string annotations recorded for the process,\n  //!     system, or snapshot producer.\n  //!\n  //! This method retrieves annotations recorded for a process. These\n  //! annotations are intended for diagnostic use, including crash analysis.\n  //! “Simple annotations” are structured as a sequence of key-value pairs,\n  //! where all keys and values are strings. These are referred to in Chrome as\n  //! “crash keys.”\n  //!\n  //! Annotations stored here may reflect the process, system, or snapshot\n  //! producer. Most annotations not under the client’s direct control will be\n  //! retrievable by this method. For clients such as Chrome, this includes the\n  //! product name and version.\n  //!\n  //! Additional per-module annotations may be obtained by calling\n  //! ModuleSnapshot::AnnotationsSimpleMap().\n  //\n  // This interface currently returns a const& because all implementations store\n  // the data within their objects in this format, and are therefore able to\n  // provide this access without requiring a copy. Future implementations may\n  // obtain data on demand or store data in a different format, at which point\n  // the cost of maintaining this data in ProcessSnapshot subclass objects will\n  // need to be taken into account, and this interface possibly revised.\n  virtual const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const = 0;\n\n  //! \\brief Returns a SystemSnapshot reflecting the characteristics of the\n  //!     system that ran the snapshot process at the time of the snapshot.\n  //!\n  //! \\return A SystemSnapshot object. The caller does not take ownership of\n  //!     this object, it is scoped to the lifetime of the ProcessSnapshot\n  //!     object that it was obtained from.\n  virtual const SystemSnapshot* System() const = 0;\n\n  //! \\brief Returns ModuleSnapshot objects reflecting the code modules (binary\n  //!     images) loaded into the snapshot process at the time of the snapshot.\n  //!\n  //! \\return A vector of ModuleSnapshot objects. The caller does not take\n  //!     ownership of these objects, they are scoped to the lifetime of the\n  //!     ProcessSnapshot object that they were obtained from.\n  virtual std::vector<const ModuleSnapshot*> Modules() const = 0;\n\n  //! \\brief Returns UnloadedModuleSnapshot objects reflecting the code modules\n  //!     the were recorded as unloaded at the time of the snapshot.\n  //!\n  //! \\return A vector of UnloadedModuleSnapshot objects.\n  virtual std::vector<UnloadedModuleSnapshot> UnloadedModules() const = 0;\n\n  //! \\brief Returns ThreadSnapshot objects reflecting the threads (lightweight\n  //!     processes) existing in the snapshot process at the time of the\n  //!     snapshot.\n  //!\n  //! \\return A vector of ThreadSnapshot objects. The caller does not take\n  //!     ownership of these objects, they are scoped to the lifetime of the\n  //!     ProcessSnapshot object that they were obtained from.\n  virtual std::vector<const ThreadSnapshot*> Threads() const = 0;\n\n  //! \\brief Returns an ExceptionSnapshot reflecting the exception that the\n  //!     snapshot process sustained to trigger the snapshot being taken.\n  //!\n  //! \\return An ExceptionSnapshot object. The caller does not take ownership of\n  //!     this object, it is scoped to the lifetime of the ProcessSnapshot\n  //!     object that it was obtained from. If the snapshot is not a result of\n  //!     an exception, returns `nullptr`.\n  virtual const ExceptionSnapshot* Exception() const = 0;\n\n  //! \\brief Returns MemoryMapRegionSnapshot objects reflecting the regions\n  //!     of the memory map in the snapshot process at the time of the snapshot.\n  //!\n  //! \\return A vector of MemoryMapRegionSnapshot objects. The caller does not\n  //!     take ownership of these objects, they are scoped to the lifetime of\n  //!     the ProcessSnapshot object that they were obtained from.\n  virtual std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const = 0;\n\n  //! \\brief Returns HandleSnapshot objects reflecting the open handles in the\n  //!     snapshot process at the time of the snapshot.\n  //!\n  //! \\return A vector of HandleSnapshot objects.\n  virtual std::vector<HandleSnapshot> Handles() const = 0;\n\n  //! \\brief Returns a vector of additional memory blocks that should be\n  //!     included in a minidump.\n  //!\n  //! \\return An vector of MemorySnapshot objects that will be included in the\n  //!     crash dump. The caller does not take ownership of these objects, they\n  //!     are scoped to the lifetime of the ProcessSnapshot object that they\n  //!     were obtained from.\n  virtual std::vector<const MemorySnapshot*> ExtraMemory() const = 0;\n\n  //! \\brief Returns a ProcessMemory object that allows accessing the process'\n  //!     memory directly.\n  //!\n  //! \\return A ProcessMemory object. The caller does not take ownership of this\n  //!     object, it is scoped to the lifetime of the ProcessSnapshot object\n  //!     that it was obtained from.\n  virtual const ProcessMemory* Memory() const = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/sanitized/memory_snapshot_sanitized.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/sanitized/memory_snapshot_sanitized.h\"\n\n#include <string.h>\n\n#include \"util/linux/pac_helper.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\nclass MemorySanitizer : public MemorySnapshot::Delegate {\n public:\n  MemorySanitizer(MemorySnapshot::Delegate* delegate,\n                  RangeSet* ranges,\n                  VMAddress address,\n                  bool is_64_bit)\n      : delegate_(delegate),\n        ranges_(ranges),\n        address_(address),\n        is_64_bit_(is_64_bit) {}\n\n  MemorySanitizer(const MemorySanitizer&) = delete;\n  MemorySanitizer& operator=(const MemorySanitizer&) = delete;\n\n  ~MemorySanitizer() = default;\n\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override {\n    if (is_64_bit_) {\n      Sanitize<uint64_t>(data, size);\n    } else {\n      Sanitize<uint32_t>(data, size);\n    }\n    return delegate_->MemorySnapshotDelegateRead(data, size);\n  }\n\n private:\n  template <typename Pointer>\n  void Sanitize(void* data, size_t size) {\n    const Pointer defaced =\n        static_cast<Pointer>(MemorySnapshotSanitized::kDefaced);\n\n    // Sanitize up to a word-aligned address.\n    const size_t aligned_offset =\n        ((address_ + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1)) - address_;\n    memcpy(data, &defaced, aligned_offset);\n\n    // Sanitize words that aren't small and don't look like pointers.\n    size_t word_count = (size - aligned_offset) / sizeof(Pointer);\n    auto words =\n        reinterpret_cast<Pointer*>(static_cast<char*>(data) + aligned_offset);\n    for (size_t index = 0; index < word_count; ++index) {\n      auto word = StripPACBits(words[index]);\n      if (word > MemorySnapshotSanitized::kSmallWordMax &&\n          !ranges_->Contains(word)) {\n        words[index] = defaced;\n      }\n    }\n\n    // Sanitize trailing bytes beyond the word-sized items.\n    const size_t sanitized_bytes =\n        aligned_offset + word_count * sizeof(Pointer);\n    memcpy(static_cast<char*>(data) + sanitized_bytes,\n           &defaced,\n           size - sanitized_bytes);\n  }\n\n  MemorySnapshot::Delegate* delegate_;\n  RangeSet* ranges_;\n  VMAddress address_;\n  bool is_64_bit_;\n};\n\n}  // namespace\n\nMemorySnapshotSanitized::MemorySnapshotSanitized(const MemorySnapshot* snapshot,\n                                                 RangeSet* ranges,\n                                                 bool is_64_bit)\n    : snapshot_(snapshot), ranges_(ranges), is_64_bit_(is_64_bit) {}\n\nMemorySnapshotSanitized::~MemorySnapshotSanitized() = default;\n\nuint64_t MemorySnapshotSanitized::Address() const {\n  return snapshot_->Address();\n}\n\nsize_t MemorySnapshotSanitized::Size() const {\n  return snapshot_->Size();\n}\n\nbool MemorySnapshotSanitized::Read(Delegate* delegate) const {\n  MemorySanitizer sanitizer(delegate, ranges_, Address(), is_64_bit_);\n  return snapshot_->Read(&sanitizer);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/sanitized/memory_snapshot_sanitized.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_\n#define CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_\n\n#include <stdint.h>\n\n#include \"snapshot/memory_snapshot.h\"\n#include \"util/misc/range_set.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A MemorySnapshot which wraps and filters sensitive information from\n//!     another MemorySnapshot.\n//!\n//! This class redacts all data from the wrapped MemorySnapshot unless:\n//! 1. The data is pointer aligned and points into an allowed address range.\n//! 2. The data is pointer aligned and is a small integer.\nclass MemorySnapshotSanitized final : public MemorySnapshot {\n public:\n  //! \\brief Redacted data is replaced with this value, casted to the\n  //!     appropriate size for a pointer in the target process.\n  static constexpr uint64_t kDefaced = 0x0defaced0defaced;\n\n  //! \\brief Pointer-aligned data smaller than this value is not redacted.\n  static constexpr uint64_t kSmallWordMax = 4096;\n\n  //! \\brief Constructs this object.\n  //!\n  //! \\param[in] snapshot The MemorySnapshot to sanitize.\n  //! \\param[in] ranges A set of allowed address ranges.\n  //! \\param[in] is_64_bit `true` if this memory is for a 64-bit process.\n  MemorySnapshotSanitized(const MemorySnapshot* snapshot,\n                          RangeSet* ranges,\n                          bool is_64_bit);\n\n  MemorySnapshotSanitized(const MemorySnapshotSanitized&) = delete;\n  MemorySnapshotSanitized& operator=(const MemorySnapshotSanitized&) = delete;\n\n  ~MemorySnapshotSanitized() override;\n\n  // MemorySnapshot:\n\n  uint64_t Address() const override;\n  size_t Size() const override;\n  bool Read(Delegate* delegate) const override;\n\n  const MemorySnapshot* MergeWithOtherSnapshot(\n      const MemorySnapshot* other) const override {\n    return nullptr;\n  }\n\n private:\n  const MemorySnapshot* snapshot_;\n  RangeSet* ranges_;\n  bool is_64_bit_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_\n"
  },
  {
    "path": "snapshot/sanitized/module_snapshot_sanitized.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/sanitized/module_snapshot_sanitized.h\"\n\n#include \"base/strings/pattern.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\nbool KeyIsAllowed(const std::string& name,\n                  const std::vector<std::string>& allowed_keys) {\n  for (const auto& key : allowed_keys) {\n    if (base::MatchPattern(name, key)) {\n      return true;\n    }\n  }\n  return false;\n}\n\n}  // namespace\n\nModuleSnapshotSanitized::ModuleSnapshotSanitized(\n    const ModuleSnapshot* snapshot,\n    const std::vector<std::string>* allowed_annotations)\n    : snapshot_(snapshot), allowed_annotations_(allowed_annotations) {}\n\nModuleSnapshotSanitized::~ModuleSnapshotSanitized() = default;\n\nstd::string ModuleSnapshotSanitized::Name() const {\n  return snapshot_->Name();\n}\n\nuint64_t ModuleSnapshotSanitized::Address() const {\n  return snapshot_->Address();\n}\n\nuint64_t ModuleSnapshotSanitized::Size() const {\n  return snapshot_->Size();\n}\n\ntime_t ModuleSnapshotSanitized::Timestamp() const {\n  return snapshot_->Timestamp();\n}\n\nvoid ModuleSnapshotSanitized::FileVersion(uint16_t* version_0,\n                                          uint16_t* version_1,\n                                          uint16_t* version_2,\n                                          uint16_t* version_3) const {\n  snapshot_->FileVersion(version_0, version_1, version_2, version_3);\n}\n\nvoid ModuleSnapshotSanitized::SourceVersion(uint16_t* version_0,\n                                            uint16_t* version_1,\n                                            uint16_t* version_2,\n                                            uint16_t* version_3) const {\n  snapshot_->SourceVersion(version_0, version_1, version_2, version_3);\n}\n\nModuleSnapshot::ModuleType ModuleSnapshotSanitized::GetModuleType() const {\n  return snapshot_->GetModuleType();\n}\n\nvoid ModuleSnapshotSanitized::UUIDAndAge(crashpad::UUID* uuid,\n                                         uint32_t* age) const {\n  snapshot_->UUIDAndAge(uuid, age);\n}\n\nstd::string ModuleSnapshotSanitized::DebugFileName() const {\n  return snapshot_->DebugFileName();\n}\n\nstd::vector<uint8_t> ModuleSnapshotSanitized::BuildID() const {\n  return snapshot_->BuildID();\n}\n\nstd::vector<std::string> ModuleSnapshotSanitized::AnnotationsVector() const {\n  // TODO(jperaza): If/when AnnotationsVector() begins to be used, determine\n  // whether and how the content should be sanitized.\n  DCHECK(snapshot_->AnnotationsVector().empty());\n  return std::vector<std::string>();\n}\n\nstd::map<std::string, std::string>\nModuleSnapshotSanitized::AnnotationsSimpleMap() const {\n  std::map<std::string, std::string> annotations =\n      snapshot_->AnnotationsSimpleMap();\n  if (allowed_annotations_) {\n    for (auto kv = annotations.begin(); kv != annotations.end();) {\n      if (KeyIsAllowed(kv->first, *allowed_annotations_)) {\n        ++kv;\n      } else {\n        kv = annotations.erase(kv);\n      }\n    }\n  }\n  return annotations;\n}\n\nstd::vector<AnnotationSnapshot> ModuleSnapshotSanitized::AnnotationObjects()\n    const {\n  std::vector<AnnotationSnapshot> annotations = snapshot_->AnnotationObjects();\n  if (allowed_annotations_) {\n    std::vector<AnnotationSnapshot> allowed;\n    for (const auto& anno : annotations) {\n      if (KeyIsAllowed(anno.name, *allowed_annotations_)) {\n        allowed.push_back(anno);\n      }\n    }\n    annotations.swap(allowed);\n  }\n  return annotations;\n}\n\nstd::set<CheckedRange<uint64_t>> ModuleSnapshotSanitized::ExtraMemoryRanges()\n    const {\n  DCHECK(snapshot_->ExtraMemoryRanges().empty());\n  return std::set<CheckedRange<uint64_t>>();\n}\n\nstd::vector<const UserMinidumpStream*>\nModuleSnapshotSanitized::CustomMinidumpStreams() const {\n  return snapshot_->CustomMinidumpStreams();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/sanitized/module_snapshot_sanitized.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_\n#define CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_\n\n#include <string>\n#include <vector>\n\n#include \"snapshot/module_snapshot.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ModuleSnapshot which wraps and filters sensitive information from\n//!     another ModuleSnapshot.\nclass ModuleSnapshotSanitized final : public ModuleSnapshot {\n public:\n  //! \\brief Constructs this object.\n  //!\n  //! \\param[in] snapshot The ModuleSnapshot to sanitize.\n  //! \\param[in] allowed_annotations A list of annotation names to allow to be\n  //!     returned by AnnotationsSimpleMap() or AnnotationObjects(). If\n  //!     `nullptr`, all annotations will be returned.\n  ModuleSnapshotSanitized(const ModuleSnapshot* snapshot,\n                          const std::vector<std::string>* allowed_annotations);\n\n  ModuleSnapshotSanitized(const ModuleSnapshotSanitized&) = delete;\n  ModuleSnapshotSanitized& operator=(const ModuleSnapshotSanitized&) = delete;\n\n  ~ModuleSnapshotSanitized() override;\n\n  // ModuleSnapshot:\n\n  std::string Name() const override;\n  uint64_t Address() const override;\n  uint64_t Size() const override;\n  time_t Timestamp() const override;\n  void FileVersion(uint16_t* version_0,\n                   uint16_t* version_1,\n                   uint16_t* version_2,\n                   uint16_t* version_3) const override;\n  void SourceVersion(uint16_t* version_0,\n                     uint16_t* version_1,\n                     uint16_t* version_2,\n                     uint16_t* version_3) const override;\n  ModuleType GetModuleType() const override;\n  void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;\n  std::string DebugFileName() const override;\n  std::vector<uint8_t> BuildID() const override;\n  std::vector<std::string> AnnotationsVector() const override;\n  std::map<std::string, std::string> AnnotationsSimpleMap() const override;\n  std::vector<AnnotationSnapshot> AnnotationObjects() const override;\n  std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;\n  std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;\n\n private:\n  const ModuleSnapshot* snapshot_;\n  const std::vector<std::string>* allowed_annotations_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_\n"
  },
  {
    "path": "snapshot/sanitized/process_snapshot_sanitized.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/sanitized/process_snapshot_sanitized.h\"\n\n#include <stdint.h>\n\n#include \"snapshot/cpu_context.h\"\n#include \"util/linux/pac_helper.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nclass StackReferencesAddressRange : public MemorySnapshot::Delegate {\n public:\n  // Returns true if stack contains a pointer aligned word in the range [low,\n  // high). The search starts at the first pointer aligned address greater than\n  // stack_pointer.\n  bool CheckStack(const MemorySnapshot* stack,\n                  VMAddress stack_pointer,\n                  VMAddress low,\n                  VMAddress high,\n                  bool is_64_bit) {\n    stack_ = stack;\n    stack_pointer_ = stack_pointer;\n    low_ = low;\n    high_ = high;\n    is_64_bit_ = is_64_bit;\n    return stack_->Read(this);\n  }\n\n  // MemorySnapshot::Delegate\n  bool MemorySnapshotDelegateRead(void* data, size_t size) {\n    return is_64_bit_ ? ScanStackForPointers<uint64_t>(data, size)\n                      : ScanStackForPointers<uint32_t>(data, size);\n  }\n\n private:\n  template <typename Pointer>\n  bool ScanStackForPointers(void* data, size_t size) {\n    size_t sp_offset;\n    if (!AssignIfInRange(&sp_offset, stack_pointer_ - stack_->Address())) {\n      return false;\n    }\n    const size_t aligned_sp_offset =\n        (sp_offset + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1);\n\n    auto words = reinterpret_cast<Pointer*>(static_cast<char*>(data) +\n                                            aligned_sp_offset);\n    size_t word_count = (size - aligned_sp_offset) / sizeof(Pointer);\n    for (size_t index = 0; index < word_count; ++index) {\n      auto word = StripPACBits(words[index]);\n      if (word >= low_ && word < high_) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  VMAddress stack_pointer_;\n  VMAddress low_;\n  VMAddress high_;\n  const MemorySnapshot* stack_;\n  bool is_64_bit_;\n};\n\n}  // namespace\n\nProcessSnapshotSanitized::ProcessSnapshotSanitized() = default;\n\nProcessSnapshotSanitized::~ProcessSnapshotSanitized() = default;\n\nbool ProcessSnapshotSanitized::Initialize(\n    const ProcessSnapshot* snapshot,\n    std::unique_ptr<const std::vector<std::string>> allowed_annotations,\n    std::unique_ptr<const std::vector<std::pair<VMAddress, VMAddress>>>\n        allowed_memory_ranges,\n    VMAddress target_module_address,\n    bool sanitize_stacks) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  snapshot_ = snapshot;\n  allowed_annotations_ = std::move(allowed_annotations);\n  sanitize_stacks_ = sanitize_stacks;\n\n  if (target_module_address) {\n    const ExceptionSnapshot* exception = snapshot_->Exception();\n    if (!exception) {\n      return false;\n    }\n\n    const ThreadSnapshot* exc_thread = nullptr;\n    for (const auto thread : snapshot_->Threads()) {\n      if (thread->ThreadID() == exception->ThreadID()) {\n        exc_thread = thread;\n        break;\n      }\n    }\n    if (!exc_thread) {\n      return false;\n    }\n\n    const ModuleSnapshot* target_module = nullptr;\n    for (const auto module : snapshot_->Modules()) {\n      if (target_module_address >= module->Address() &&\n          target_module_address < module->Address() + module->Size()) {\n        target_module = module;\n        break;\n      }\n    }\n    if (!target_module) {\n      return false;\n    }\n\n    // Only allow the snapshot if the program counter or some address on the\n    // stack points into the target module.\n    VMAddress pc = exception->Context()->InstructionPointer();\n    VMAddress module_address_low = target_module->Address();\n    VMAddress module_address_high = module_address_low + target_module->Size();\n    if ((pc < module_address_low || pc >= module_address_high) &&\n        !StackReferencesAddressRange().CheckStack(\n            exc_thread->Stack(),\n            exception->Context()->StackPointer(),\n            module_address_low,\n            module_address_high,\n            exception->Context()->Is64Bit())) {\n      return false;\n    }\n  }\n\n  if (allowed_annotations_) {\n    for (const auto module : snapshot_->Modules()) {\n      modules_.emplace_back(std::make_unique<internal::ModuleSnapshotSanitized>(\n          module, allowed_annotations_.get()));\n    }\n  }\n\n  if (sanitize_stacks_) {\n    for (const auto module : snapshot_->Modules()) {\n      address_ranges_.Insert(module->Address(), module->Size());\n    }\n\n    for (const auto thread : snapshot_->Threads()) {\n      address_ranges_.Insert(thread->Stack()->Address(),\n                             thread->Stack()->Size());\n      threads_.emplace_back(std::make_unique<internal::ThreadSnapshotSanitized>(\n          thread, &address_ranges_));\n    }\n  }\n\n  process_memory_.Initialize(snapshot_->Memory(), allowed_memory_ranges.get());\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\ncrashpad::ProcessID ProcessSnapshotSanitized::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->ProcessID();\n}\n\ncrashpad::ProcessID ProcessSnapshotSanitized::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->ParentProcessID();\n}\n\nvoid ProcessSnapshotSanitized::SnapshotTime(timeval* snapshot_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  snapshot_->SnapshotTime(snapshot_time);\n}\n\nvoid ProcessSnapshotSanitized::ProcessStartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  snapshot_->ProcessStartTime(start_time);\n}\n\nvoid ProcessSnapshotSanitized::ProcessCPUTimes(timeval* user_time,\n                                               timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  snapshot_->ProcessCPUTimes(user_time, system_time);\n}\n\nvoid ProcessSnapshotSanitized::ReportID(UUID* report_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  snapshot_->ReportID(report_id);\n}\n\nvoid ProcessSnapshotSanitized::ClientID(UUID* client_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  snapshot_->ClientID(client_id);\n}\n\nconst std::map<std::string, std::string>&\nProcessSnapshotSanitized::AnnotationsSimpleMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->AnnotationsSimpleMap();\n}\n\nconst SystemSnapshot* ProcessSnapshotSanitized::System() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->System();\n}\n\nstd::vector<const ThreadSnapshot*> ProcessSnapshotSanitized::Threads() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!sanitize_stacks_) {\n    return snapshot_->Threads();\n  }\n\n  std::vector<const ThreadSnapshot*> threads;\n  for (const auto& thread : threads_) {\n    threads.push_back(thread.get());\n  }\n  return threads;\n}\n\nstd::vector<const ModuleSnapshot*> ProcessSnapshotSanitized::Modules() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!allowed_annotations_) {\n    return snapshot_->Modules();\n  }\n\n  std::vector<const ModuleSnapshot*> modules;\n  for (const auto& module : modules_) {\n    modules.push_back(module.get());\n  }\n  return modules;\n}\n\nstd::vector<UnloadedModuleSnapshot> ProcessSnapshotSanitized::UnloadedModules()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->UnloadedModules();\n}\n\nconst ExceptionSnapshot* ProcessSnapshotSanitized::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->Exception();\n}\n\nstd::vector<const MemoryMapRegionSnapshot*>\nProcessSnapshotSanitized::MemoryMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->MemoryMap();\n}\n\nstd::vector<HandleSnapshot> ProcessSnapshotSanitized::Handles() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->Handles();\n}\n\nstd::vector<const MemorySnapshot*> ProcessSnapshotSanitized::ExtraMemory()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return snapshot_->ExtraMemory();\n}\n\nconst ProcessMemory* ProcessSnapshotSanitized::Memory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &process_memory_;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/sanitized/process_snapshot_sanitized.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_\n#define CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/sanitized/module_snapshot_sanitized.h\"\n#include \"snapshot/sanitized/thread_snapshot_sanitized.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/range_set.h\"\n#include \"util/process/process_id.h\"\n#include \"util/process/process_memory_sanitized.h\"\n\nnamespace crashpad {\n\n//! \\brief A ProcessSnapshot which wraps and filters sensitive information from\n//!     another ProcessSnapshot.\nclass ProcessSnapshotSanitized final : public ProcessSnapshot {\n public:\n  ProcessSnapshotSanitized();\n\n  ProcessSnapshotSanitized(const ProcessSnapshotSanitized&) = delete;\n  ProcessSnapshotSanitized& operator=(const ProcessSnapshotSanitized&) = delete;\n\n  ~ProcessSnapshotSanitized() override;\n\n  //! \\brief Initializes this object.\n  //!\n  //! This method must be successfully called before calling any other method on\n  //! this object.\n  //!\n  //! \\param[in] snapshot The ProcessSnapshot to sanitize.\n  //! \\param[in] allowed_annotations A list of annotations names to allow to\n  //!     be returned by AnnotationsSimpleMap() or from this object's module\n  //!     snapshots. If `nullptr`, all annotations will be returned.\n  //      These annotation names support pattern matching, eg: \"switch-*\"\n  //! \\param[in] allowed_memory_ranges A list of memory ranges to allow to be\n  //!     accessible via Memory(), or `nullptr` to allow all ranges.\n  //! \\param[in] target_module_address An address in the target process'\n  //!     address space within the bounds of a module to target. If the\n  //!     crashing thread's context and stack do not contain any pointers into\n  //!     this module's address range, this method will return `false`. If this\n  //!     value is 0, this method will not check the context or stack for\n  //!     references to any particular module.\n  //! \\param[in] sanitize_stacks If `true`, the MemorySnapshots for each\n  //!     thread's stack will be filtered using an\n  //!     internal::StackSnapshotSanitized.\n  //! \\return `false` if \\a snapshot does not meet sanitization requirements and\n  //!     should be filtered entirely. Otherwise `true`.\n  bool Initialize(\n      const ProcessSnapshot* snapshot,\n      std::unique_ptr<const std::vector<std::string>> allowed_annotations,\n      std::unique_ptr<const std::vector<std::pair<VMAddress, VMAddress>>>\n          allowed_memory_ranges,\n      VMAddress target_module_address,\n      bool sanitize_stacks);\n\n  // ProcessSnapshot:\n\n  crashpad::ProcessID ProcessID() const override;\n  crashpad::ProcessID ParentProcessID() const override;\n  void SnapshotTime(timeval* snapshot_time) const override;\n  void ProcessStartTime(timeval* start_time) const override;\n  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;\n  void ReportID(UUID* report_id) const override;\n  void ClientID(UUID* client_id) const override;\n  const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const override;\n  const SystemSnapshot* System() const override;\n  std::vector<const ThreadSnapshot*> Threads() const override;\n  std::vector<const ModuleSnapshot*> Modules() const override;\n  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;\n  const ExceptionSnapshot* Exception() const override;\n  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;\n  std::vector<HandleSnapshot> Handles() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n  const ProcessMemory* Memory() const override;\n\n private:\n  // Only used when allowed_annotations_ != nullptr.\n  std::vector<std::unique_ptr<internal::ModuleSnapshotSanitized>> modules_;\n\n  // Only used when sanitize_stacks_ == true.\n  std::vector<std::unique_ptr<internal::ThreadSnapshotSanitized>> threads_;\n\n  RangeSet address_ranges_;\n  const ProcessSnapshot* snapshot_;\n  ProcessMemorySanitized process_memory_;\n  std::unique_ptr<const std::vector<std::string>> allowed_annotations_;\n  bool sanitize_stacks_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_\n"
  },
  {
    "path": "snapshot/sanitized/process_snapshot_sanitized_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/sanitized/process_snapshot_sanitized.h\"\n\n#include <string.h>\n\n#include <iterator>\n\n#include \"base/notreached.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/address_sanitizer.h\"\n#include \"util/numeric/safe_assignment.h\"\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include <sys/syscall.h>\n\n#include \"snapshot/linux/process_snapshot_linux.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#include \"util/linux/exception_information.h\"\n#include \"util/posix/signals.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass ExceptionGenerator {\n public:\n  ExceptionGenerator(const ExceptionGenerator&) = delete;\n  ExceptionGenerator& operator=(const ExceptionGenerator&) = delete;\n\n  static ExceptionGenerator* Get() {\n    static ExceptionGenerator* instance = new ExceptionGenerator();\n    return instance;\n  }\n\n  bool Initialize(FileHandle in, FileHandle out) {\n    in_ = in;\n    out_ = out;\n    return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr);\n  }\n\n private:\n  ExceptionGenerator() = default;\n  ~ExceptionGenerator() = delete;\n\n  static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {\n    auto state = Get();\n\n    ExceptionInformation info = {};\n    info.siginfo_address = FromPointerCast<VMAddress>(siginfo);\n    info.context_address = FromPointerCast<VMAddress>(context);\n    info.thread_id = syscall(SYS_gettid);\n\n    auto info_addr = FromPointerCast<VMAddress>(&info);\n    ASSERT_TRUE(LoggingWriteFile(state->out_, &info_addr, sizeof(info_addr)));\n\n    CheckedReadFileAtEOF(state->in_);\n    Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);\n  }\n\n  FileHandle in_;\n  FileHandle out_;\n};\n\nconstexpr char kAllowedAnnotationName[] = \"name_of_allowed_anno\";\nconstexpr char kAllowedAnnotationNamePattern[] = \"name_of_another_*\";\nconstexpr char kAllowedAnnotationNamePatternActual[] = \"name_of_another_anno\";\nconstexpr char kAllowedAnnotationValue[] = \"some_value\";\nconstexpr char kNonAllowedAnnotationName[] = \"non_allowed_anno\";\nconstexpr char kNonAllowedAnnotationValue[] = \"private_annotation\";\nconstexpr char kSensitiveStackData[] = \"sensitive_stack_data\";\n\nstruct ChildTestAddresses {\n  VMAddress string_address;\n  VMAddress module_address;\n  VMAddress non_module_address;\n  VMAddress code_pointer_address;\n  VMAddress code_pointer_value;\n};\n\nvoid ChildTestFunction() {\n  FileHandle in = StdioFileHandle(StdioStream::kStandardInput);\n  FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);\n\n  static StringAnnotation<32> allowed_annotation(kAllowedAnnotationName);\n  allowed_annotation.Set(kAllowedAnnotationValue);\n\n  static StringAnnotation<32> allowed_matched_annotation(\n      kAllowedAnnotationNamePatternActual);\n  allowed_matched_annotation.Set(kAllowedAnnotationValue);\n\n  static StringAnnotation<32> non_allowed_annotation(kNonAllowedAnnotationName);\n  non_allowed_annotation.Set(kNonAllowedAnnotationValue);\n\n  char string_data[std::size(kSensitiveStackData)];\n  strcpy(string_data, kSensitiveStackData);\n\n  void (*code_pointer)(void) = ChildTestFunction;\n\n  ChildTestAddresses addrs = {};\n  addrs.string_address = FromPointerCast<VMAddress>(string_data);\n  addrs.module_address = FromPointerCast<VMAddress>(ChildTestFunction);\n  addrs.non_module_address = FromPointerCast<VMAddress>(&addrs);\n  addrs.code_pointer_address = FromPointerCast<VMAddress>(&code_pointer);\n  addrs.code_pointer_value = FromPointerCast<VMAddress>(code_pointer);\n  ASSERT_TRUE(LoggingWriteFile(out, &addrs, sizeof(addrs)));\n\n  auto gen = ExceptionGenerator::Get();\n  ASSERT_TRUE(gen->Initialize(in, out));\n\n  __builtin_trap();\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ChildToBeSanitized) {\n  ChildTestFunction();\n\n  // This isn't reachable unless assertions inside ChildTestFunction fail.\n  NOTREACHED();\n}\n\nvoid ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) {\n  bool found_allowed = false;\n  bool found_matched_allowed = false;\n  bool found_non_allowed = false;\n  for (auto module : snapshot->Modules()) {\n    for (const auto& anno : module->AnnotationObjects()) {\n      if (anno.name == kAllowedAnnotationName) {\n        found_allowed = true;\n      }\n      if (anno.name == kAllowedAnnotationNamePatternActual) {\n        found_matched_allowed = true;\n      } else if (anno.name == kNonAllowedAnnotationName) {\n        found_non_allowed = true;\n      }\n    }\n  }\n\n  EXPECT_TRUE(found_allowed);\n  EXPECT_TRUE(found_matched_allowed);\n  if (sanitized) {\n    EXPECT_FALSE(found_non_allowed);\n  } else {\n    EXPECT_TRUE(found_non_allowed);\n  }\n}\n\nvoid ExpectProcessMemory(ProcessSnapshot* snapshot,\n                         VMAddress allowed_byte,\n                         bool sanitized) {\n  auto memory = snapshot->Memory();\n\n  char out;\n  EXPECT_TRUE(memory->Read(allowed_byte, 1, &out));\n\n  bool disallowed_read = memory->Read(allowed_byte + 1, 1, &out);\n  if (sanitized) {\n    EXPECT_FALSE(disallowed_read);\n  } else {\n    EXPECT_TRUE(disallowed_read);\n  }\n}\n\nclass StackSanitizationChecker : public MemorySnapshot::Delegate {\n public:\n  StackSanitizationChecker() = default;\n  ~StackSanitizationChecker() = default;\n\n  void CheckStack(const MemorySnapshot* stack,\n                  const ChildTestAddresses& addrs,\n                  bool is_64_bit,\n                  bool sanitized) {\n    stack_ = stack;\n    addrs_ = addrs;\n    is_64_bit_ = is_64_bit;\n    sanitized_ = sanitized;\n    EXPECT_TRUE(stack_->Read(this));\n  }\n\n  // MemorySnapshot::Delegate\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override {\n    // AddressSanitizer with use-after-return detection causes stack variables\n    // to be allocated on the heap.\n#if !defined(ADDRESS_SANITIZER)\n    size_t pointer_offset;\n    if (!AssignIfInRange(&pointer_offset,\n                         addrs_.code_pointer_address - stack_->Address())) {\n      ADD_FAILURE();\n      return false;\n    }\n\n    const auto data_c = static_cast<char*>(data);\n    VMAddress pointer_value;\n    if (is_64_bit_) {\n      pointer_value = *reinterpret_cast<uint64_t*>(data_c + pointer_offset);\n    } else {\n      pointer_value = *reinterpret_cast<uint32_t*>(data_c + pointer_offset);\n    }\n    EXPECT_EQ(pointer_value, addrs_.code_pointer_value);\n\n    size_t string_offset;\n    if (!AssignIfInRange(&string_offset,\n                         addrs_.string_address - stack_->Address())) {\n      ADD_FAILURE();\n      return false;\n    }\n\n    auto string = data_c + string_offset;\n    if (sanitized_) {\n      EXPECT_STRNE(string, kSensitiveStackData);\n    } else {\n      EXPECT_STREQ(string, kSensitiveStackData);\n    }\n#endif  // !ADDRESS_SANITIZER\n    return true;\n  }\n\n private:\n  const MemorySnapshot* stack_;\n  ChildTestAddresses addrs_;\n  bool is_64_bit_;\n  bool sanitized_;\n};\n\nvoid ExpectStackData(ProcessSnapshot* snapshot,\n                     const ChildTestAddresses& addrs,\n                     bool sanitized) {\n  const ThreadSnapshot* crasher = nullptr;\n  for (const auto thread : snapshot->Threads()) {\n    if (thread->ThreadID() == snapshot->Exception()->ThreadID()) {\n      crasher = thread;\n      break;\n    }\n  }\n  ASSERT_TRUE(crasher);\n\n  const MemorySnapshot* stack = crasher->Stack();\n  StackSanitizationChecker().CheckStack(\n      stack, addrs, crasher->Context()->Is64Bit(), sanitized);\n}\n\nclass SanitizeTest : public MultiprocessExec {\n public:\n  SanitizeTest() : MultiprocessExec() {\n    SetChildTestMainFunction(\"ChildToBeSanitized\");\n    SetExpectedChildTerminationBuiltinTrap();\n  }\n\n  SanitizeTest(const SanitizeTest&) = delete;\n  SanitizeTest& operator=(const SanitizeTest&) = delete;\n\n  ~SanitizeTest() = default;\n\n private:\n  void MultiprocessParent() {\n    ChildTestAddresses addrs = {};\n    ASSERT_TRUE(\n        LoggingReadFileExactly(ReadPipeHandle(), &addrs, sizeof(addrs)));\n\n    VMAddress exception_info_addr;\n    ASSERT_TRUE(LoggingReadFileExactly(\n        ReadPipeHandle(), &exception_info_addr, sizeof(exception_info_addr)));\n\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(ChildProcess()));\n\n    ProcessSnapshotLinux snapshot;\n    ASSERT_TRUE(snapshot.Initialize(&connection));\n    ASSERT_TRUE(snapshot.InitializeException(exception_info_addr));\n\n    ExpectAnnotations(&snapshot, /* sanitized= */ false);\n    ExpectStackData(&snapshot, addrs, /* sanitized= */ false);\n    ExpectProcessMemory(&snapshot,\n                        addrs.string_address,\n                        /* sanitized= */ false);\n\n    auto allowed_annotations = std::make_unique<std::vector<std::string>>();\n    allowed_annotations->push_back(kAllowedAnnotationName);\n    allowed_annotations->push_back(kAllowedAnnotationNamePattern);\n\n    auto allowed_memory_ranges =\n        std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();\n    allowed_memory_ranges->push_back(\n        std::make_pair(addrs.string_address, addrs.string_address + 1));\n\n    ProcessSnapshotSanitized sanitized;\n    ASSERT_TRUE(sanitized.Initialize(&snapshot,\n                                     std::move(allowed_annotations),\n                                     std::move(allowed_memory_ranges),\n                                     addrs.module_address,\n                                     true));\n\n    ExpectAnnotations(&sanitized, /* sanitized= */ true);\n    ExpectStackData(&sanitized, addrs, /* sanitized= */ true);\n    ExpectProcessMemory(&sanitized,\n                        addrs.string_address,\n                        /* sanitized= */ true);\n\n    ProcessSnapshotSanitized screened_snapshot;\n    EXPECT_FALSE(screened_snapshot.Initialize(\n        &snapshot, nullptr, nullptr, addrs.non_module_address, false));\n  }\n};\n\nTEST(ProcessSnapshotSanitized, Sanitize) {\n  SanitizeTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/sanitized/sanitization_information.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/sanitized/sanitization_information.h\"\n\n#include <limits>\n\n#include \"base/logging.h\"\n#include \"client/annotation.h\"\n\nnamespace crashpad {\n\nnamespace {\n\ntemplate <typename Pointer>\nbool ReadAllowedAnnotations(const ProcessMemoryRange& memory,\n                            VMAddress list_address,\n                            std::vector<std::string>* allowed_annotations) {\n  if (!list_address) {\n    return true;\n  }\n\n  std::vector<std::string> local_allowed_annotations;\n  Pointer name_address;\n  while (memory.Read(list_address, sizeof(name_address), &name_address)) {\n    if (!name_address) {\n      allowed_annotations->swap(local_allowed_annotations);\n      return true;\n    }\n\n    std::string name;\n    if (!memory.ReadCStringSizeLimited(\n            name_address, Annotation::kNameMaxLength, &name)) {\n      return false;\n    }\n    local_allowed_annotations.push_back(name);\n    list_address += sizeof(Pointer);\n  }\n\n  return false;\n}\n\n}  // namespace\n\nbool ReadAllowedAnnotations(const ProcessMemoryRange& memory,\n                            VMAddress list_address,\n                            std::vector<std::string>* allowed_annotations) {\n  return memory.Is64Bit() ? ReadAllowedAnnotations<uint64_t>(\n                                memory, list_address, allowed_annotations)\n                          : ReadAllowedAnnotations<uint32_t>(\n                                memory, list_address, allowed_annotations);\n}\n\nbool ReadAllowedMemoryRanges(\n    const ProcessMemoryRange& memory,\n    VMAddress list_address,\n    std::vector<std::pair<VMAddress, VMAddress>>* allowed_memory_ranges) {\n  allowed_memory_ranges->clear();\n  if (!list_address) {\n    return true;\n  }\n\n  SanitizationAllowedMemoryRanges list;\n  if (!memory.Read(list_address, sizeof(list), &list)) {\n    LOG(ERROR) << \"Failed to read allowed memory ranges\";\n    return false;\n  }\n\n  if (!list.size) {\n    return true;\n  }\n\n  // An upper bound of entries that we never expect to see more than.\n  constexpr size_t kMaxListSize = 256;\n  if (list.size > kMaxListSize) {\n    LOG(ERROR) << \"list exceeded maximum, size=\" << list.size;\n    return false;\n  }\n\n  std::vector<SanitizationAllowedMemoryRanges::Range> ranges(list.size);\n  if (!memory.Read(list.entries, sizeof(ranges[0]) * list.size,\n                   ranges.data())) {\n    LOG(ERROR) << \"Failed to read allowed memory ranges\";\n    return false;\n  }\n\n  const VMAddress vm_max = memory.Is64Bit()\n                               ? std::numeric_limits<uint64_t>::max()\n                               : std::numeric_limits<uint32_t>::max();\n  std::vector<std::pair<VMAddress, VMAddress>> local_allowed_memory_ranges;\n  for (size_t i = 0; i < list.size; ++i) {\n    if (ranges[i].base > vm_max || ranges[i].length > vm_max - ranges[i].base) {\n      LOG(ERROR) << \"Invalid range: base=\" << ranges[i].base\n                 << \" length=\" << ranges[i].length;\n      return false;\n    }\n    local_allowed_memory_ranges.emplace_back(ranges[i].base,\n                                             ranges[i].base + ranges[i].length);\n  }\n\n  allowed_memory_ranges->swap(local_allowed_memory_ranges);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/sanitized/sanitization_information.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_\n#define CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"util/misc/address_types.h\"\n#include \"util/process/process_memory_range.h\"\n\nnamespace crashpad {\n\n#pragma pack(push, 1)\n\n//! \\brief Struture containing information about how snapshots should be\n//!     sanitized.\n//!\n//! \\see ProcessSnapshotSanitized\nstruct SanitizationInformation {\n  //! \\brief The address in the client process' address space of a nullptr\n  //!     terminated array of NUL-terminated strings. The string values are the\n  //!     names of allowed annotations. This value is 0 if all annotations are\n  //!     allowed.\n  VMAddress allowed_annotations_address;\n\n  //! \\brief An address in the client process' address space within a module to\n  //!     target. When a target module is used, crash dumps are discarded unless\n  //!     the crashing thread's program counter or pointer-aligned values on the\n  //!     crashing thread's stack point into the target module. This value is 0\n  //!     if there is no target module.\n  VMAddress target_module_address;\n\n  //! \\brief The address in the client process' address space of a\n  //!     \\a SanitizationAllowedMemoryRanges, a list of address ranges allowed\n  //!     to be accessed by ProcessMemorySanitized. This value is 0 if no memory\n  //!     is allowed to be read using ProcessMemorySanitized.\n  VMAddress allowed_memory_ranges_address;\n\n  //! \\brief Non-zero if stacks should be sanitized for possible PII.\n  uint8_t sanitize_stacks;\n};\n\n//! \\brief Describes a list of allowed memory ranges.\nstruct SanitizationAllowedMemoryRanges {\n  //! \\brief Describes a range of memory.\n  struct Range {\n    VMAddress base;\n    VMSize length;\n  };\n\n  //! \\brief Address of an array of |size| elements of type Range.\n  VMAddress entries;\n  VMSize size;\n};\n\n#pragma pack(pop)\n\n//! \\brief Reads a list of allowed annotations from another process.\n//!\n//! \\param[in] memory A memory reader for the target process.\n//! \\param[in] list_address The address in the target process' address space of\n//!     a nullptr terminated array of NUL-terminated strings.\n//! \\param[out] allowed_annotations The list read, valid only if this function\n//!     returns `true`.\n//! \\return `true` on success, `false` on failure with a message logged.\nbool ReadAllowedAnnotations(const ProcessMemoryRange& memory,\n                            VMAddress list_address,\n                            std::vector<std::string>* allowed_annotations);\n\n//! \\brief Reads a list of allowed memory ranges from another process.\n//!\n//! \\param[in] memory A memory reader for the target process.\n//! \\param[in] list_address The address in the target process' address space of\n//!     a nullptr terminated array of NUL-terminated strings.\n//! \\param[out] allowed_memory_ranges A list of allowed memory regions, valid\n//!     only if this function returns `true`.\n//! \\return `true` on success, `false` on failure with a message logged.\nbool ReadAllowedMemoryRanges(\n    const ProcessMemoryRange& memory,\n    VMAddress list_address,\n    std::vector<std::pair<VMAddress, VMAddress>>* allowed_memory_ranges);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_\n"
  },
  {
    "path": "snapshot/sanitized/sanitization_information_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/sanitized/sanitization_information.h\"\n\n#include <iterator>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/process/process_memory_linux.h\"\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#include \"test/linux/fake_ptrace_connection.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass AllowedAnnotationsTest : public testing::Test {\n public:\n  void SetUp() override {\n    ASSERT_TRUE(connection_.Initialize(getpid()));\n\n#if defined(ARCH_CPU_64_BITS)\n    ASSERT_TRUE(range_.Initialize(connection_.Memory(), true));\n#else\n    ASSERT_TRUE(range_.Initialize(connection_.Memory(), false));\n#endif\n  }\n\n protected:\n  bool DoReadAllowedAnnotations(const char* const* address) {\n    return ReadAllowedAnnotations(\n        range_, FromPointerCast<VMAddress>(address), &allowed_annotations_);\n  }\n\n  FakePtraceConnection connection_;\n  ProcessMemoryRange range_;\n  std::vector<std::string> allowed_annotations_;\n};\n\nconst char* const kEmptyAllowedAnnotations[] = {nullptr};\n\nTEST_F(AllowedAnnotationsTest, EmptyAllowedAnnotations) {\n  ASSERT_TRUE(DoReadAllowedAnnotations(kEmptyAllowedAnnotations));\n  EXPECT_EQ(allowed_annotations_, std::vector<std::string>());\n}\n\nconst char* const kNonEmptyAllowedAnnotations[] = {\"string1\",\n                                                   \"another_string\",\n                                                   \"\",\n                                                   nullptr};\n\nTEST_F(AllowedAnnotationsTest, NonEmptyAllowedAnnotations) {\n  ASSERT_TRUE(DoReadAllowedAnnotations(kNonEmptyAllowedAnnotations));\n  ASSERT_EQ(allowed_annotations_.size(),\n            std::size(kNonEmptyAllowedAnnotations) - 1);\n  for (size_t index = 0; index < allowed_annotations_.size(); ++index) {\n    EXPECT_EQ(allowed_annotations_[index], kNonEmptyAllowedAnnotations[index]);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/sanitized/thread_snapshot_sanitized.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/sanitized/thread_snapshot_sanitized.h\"\n\n#include \"snapshot/cpu_context.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nThreadSnapshotSanitized::ThreadSnapshotSanitized(const ThreadSnapshot* snapshot,\n                                                 RangeSet* ranges)\n    : ThreadSnapshot(),\n      snapshot_(snapshot),\n      stack_(snapshot_->Stack(), ranges, snapshot_->Context()->Is64Bit()) {}\n\nThreadSnapshotSanitized::~ThreadSnapshotSanitized() = default;\n\nconst CPUContext* ThreadSnapshotSanitized::Context() const {\n  return snapshot_->Context();\n}\n\nconst MemorySnapshot* ThreadSnapshotSanitized::Stack() const {\n  return &stack_;\n}\n\nuint64_t ThreadSnapshotSanitized::ThreadID() const {\n  return snapshot_->ThreadID();\n}\n\nstd::string ThreadSnapshotSanitized::ThreadName() const {\n  return snapshot_->ThreadName();\n}\n\nint ThreadSnapshotSanitized::SuspendCount() const {\n  return snapshot_->SuspendCount();\n}\n\nint ThreadSnapshotSanitized::Priority() const {\n  return snapshot_->Priority();\n}\n\nuint64_t ThreadSnapshotSanitized::ThreadSpecificDataAddress() const {\n  return snapshot_->ThreadSpecificDataAddress();\n}\n\nstd::vector<const MemorySnapshot*> ThreadSnapshotSanitized::ExtraMemory()\n    const {\n  // TODO(jperaza): If/when ExtraMemory() is used, decide whether and how it\n  // should be sanitized.\n  DCHECK(snapshot_->ExtraMemory().empty());\n  return std::vector<const MemorySnapshot*>();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/sanitized/thread_snapshot_sanitized.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_\n#define CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_\n\n#include \"snapshot/thread_snapshot.h\"\n\n#include <string>\n\n#include \"snapshot/sanitized/memory_snapshot_sanitized.h\"\n#include \"util/misc/range_set.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A ThreadSnapshot which wraps and filters sensitive information from\n//!     another ThreadSnapshot.\nclass ThreadSnapshotSanitized final : public ThreadSnapshot {\n public:\n  //! \\brief Constructs this object.\n  //!\n  //! \\param[in] snapshot The ThreadSnapshot to sanitize.\n  //! \\param[in] ranges A set of address ranges with which to sanitize this\n  //!     thread's stacks. \\see internal::MemorySnapshotSanitized.\n  ThreadSnapshotSanitized(const ThreadSnapshot* snapshot, RangeSet* ranges);\n\n  ThreadSnapshotSanitized(const ThreadSnapshotSanitized&) = delete;\n  ThreadSnapshotSanitized& operator=(const ThreadSnapshotSanitized&) = delete;\n\n  ~ThreadSnapshotSanitized() override;\n\n  // ThreadSnapshot:\n\n  const CPUContext* Context() const override;\n  const MemorySnapshot* Stack() const override;\n  uint64_t ThreadID() const override;\n  std::string ThreadName() const override;\n  int SuspendCount() const override;\n  int Priority() const override;\n  uint64_t ThreadSpecificDataAddress() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  const ThreadSnapshot* snapshot_;\n  MemorySnapshotSanitized stack_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_\n"
  },
  {
    "path": "snapshot/snapshot_constants.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef SNAPSHOT_SNAPSHOT_CONSTANTS_H_\n#define SNAPSHOT_SNAPSHOT_CONSTANTS_H_\n\nnamespace crashpad {\n\n//! \\brief The maximum number of crashpad::Annotations that will be read from\n//!     a client process.\n//!\n//! \\note This maximum was chosen arbitrarily and may change in the future.\nconstexpr size_t kMaxNumberOfAnnotations = 400;\n\n}  // namespace crashpad\n\n#endif  // SNAPSHOT_SNAPSHOT_CONSTANTS_H_\n"
  },
  {
    "path": "snapshot/system_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <string>\n\n#include \"snapshot/cpu_architecture.h\"\n\nnamespace crashpad {\n\n//! \\brief An abstract interface to a snapshot representing the state of a\n//!     system, comprising an operating system, CPU architecture, and various\n//!     other characteristics.\nclass SystemSnapshot {\n public:\n  virtual ~SystemSnapshot() {}\n\n  //! \\brief A system’s operating system family.\n  enum OperatingSystem {\n    //! \\brief The snapshot system’s operating system is unknown.\n    kOperatingSystemUnknown = 0,\n\n    //! \\brief macOS.\n    kOperatingSystemMacOSX,\n\n    //! \\brief Windows.\n    kOperatingSystemWindows,\n\n    //! \\brief Linux.\n    kOperatingSystemLinux,\n\n    //! \\brief Android.\n    kOperatingSystemAndroid,\n\n    //! \\brief Fuchsia.\n    kOperatingSystemFuchsia,\n\n    //! \\brief iOS.\n    kOperatingSystemIOS,\n  };\n\n  //! \\brief A system’s daylight saving time status.\n  //!\n  //! The daylight saving time status is taken partially from the system’s\n  //! locale configuration. This determines whether daylight saving time is\n  //! ever observed on the system. If it is, the snapshot’s time\n  //! (ProcessSnapshot::SnapshotTime()) is used to determine whether the system\n  //! was observing daylight saving time at the time of the snapshot.\n  enum DaylightSavingTimeStatus {\n    //! \\brief Daylight saving time is never observed on the snapshot system.\n    kDoesNotObserveDaylightSavingTime = 0,\n\n    //! \\brief Daylight saving time is observed on the snapshot system when in\n    //!     effect, but standard time was in effect at the time of the snapshot.\n    kObservingStandardTime,\n\n    //! \\brief Daylight saving time is observed on the snapshot system when in\n    //!     effect, and daylight saving time was in effect at the time of the\n    //!     snapshot.\n    kObservingDaylightSavingTime,\n  };\n\n  //! \\brief Returns the snapshot system’s CPU architecture.\n  //!\n  //! In some cases, a system may be able to run processes of multiple specific\n  //! architecture types. For example, systems based on 64-bit architectures\n  //! such as x86_64 are often able to run 32-bit code of another architecture\n  //! in the same family, such as 32-bit x86. On these systems, this method will\n  //! return the architecture of the process that the snapshot is associated\n  //! with, provided that the SystemSnapshot object was obtained from\n  //! ProcessSnapshot::System(). This renders one aspect of this method’s return\n  //! value a process attribute rather than a system attribute, but it’s defined\n  //! here rather than in ProcessSnapshot because the CPU architecture is a\n  //! better conceptual fit for the system abstraction alongside these other\n  //! related methods.\n  virtual CPUArchitecture GetCPUArchitecture() const = 0;\n\n  //! \\brief Returns the snapshot system’s CPU revision.\n  //!\n  //! For x86-family CPUs (including x86_64 and 32-bit x86), this is the CPU\n  //! family, model, and stepping ID values from `cpuid 1` `eax`. The family and\n  //! model values are adjusted to take the extended family and model IDs into\n  //! account. These values are encoded in this method’s return value with the\n  //! family in the high high 16 bits, the model in the next 8 bits, and the\n  //! stepping in the low 8 bits.\n  //!\n  //! \\return A CPU architecture-specific value identifying the CPU revision.\n  virtual uint32_t CPURevision() const = 0;\n\n  //! \\brief Returns the total number of CPUs present in the snapshot system.\n  virtual uint8_t CPUCount() const = 0;\n\n  //! \\brief Returns the vendor of the snapshot system’s CPUs.\n  //!\n  //! For x86-family CPUs (including x86_64 and 32-bit x86), this is the CPU\n  //! vendor identification string as encoded in `cpuid 0` `ebx`, `edx`, and\n  //! `ecx`.\n  //!\n  //! \\return A string identifying the vendor of the snapshot system’s CPUs.\n  virtual std::string CPUVendor() const = 0;\n\n  //! \\brief Returns frequency information about the snapshot system’s CPUs in\n  //!     \\a current_hz and \\a max_hz.\n  //!\n  //! \\param[out] current_hz The snapshot system’s CPU clock frequency in Hz at\n  //!     the time of the snapshot.\n  //! \\param[out] max_hz The snapshot system’s maximum possible CPU clock\n  //!     frequency.\n  virtual void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const = 0;\n\n  //! \\brief Returns an x86-family snapshot system’s CPU signature.\n  //!\n  //! This is the family, model, and stepping ID values as encoded in `cpuid 1`\n  //! `eax`.\n  //!\n  //! This method must only be called when GetCPUArchitecture() indicates an\n  //! x86-family CPU architecture (#kCPUArchitectureX86 or\n  //! #kCPUArchitectureX86_64).\n  //!\n  //! \\return An x86 family-specific value identifying the CPU signature.\n  virtual uint32_t CPUX86Signature() const = 0;\n\n  //! \\brief Returns an x86-family snapshot system’s CPU features.\n  //!\n  //! This is the feature information as encoded in `cpuid 1` `edx` and `ecx`.\n  //! `edx` is placed in the low half of the return value, and `ecx` is placed\n  //! in the high half.\n  //!\n  //! This method must only be called when GetCPUArchitecture() indicates an\n  //! x86-family CPU architecture (#kCPUArchitectureX86 or\n  //! #kCPUArchitectureX86_64).\n  //!\n  //! \\return An x86 family-specific value identifying CPU features.\n  //!\n  //! \\sa CPUX86ExtendedFeatures()\n  //! \\sa CPUX86Leaf7Features()\n  virtual uint64_t CPUX86Features() const = 0;\n\n  //! \\brief Returns an x86-family snapshot system’s extended CPU features.\n  //!\n  //! This is the extended feature information as encoded in `cpuid 0x80000001`\n  //! `edx` and `ecx`. `edx` is placed in the low half of the return value, and\n  //! `ecx` is placed in the high half.\n  //!\n  //! This method must only be called when GetCPUArchitecture() indicates an\n  //! x86-family CPU architecture (#kCPUArchitectureX86 or\n  //! #kCPUArchitectureX86_64).\n  //!\n  //! \\return An x86 family-specific value identifying extended CPU features.\n  //!\n  //! \\sa CPUX86Features()\n  //! \\sa CPUX86Leaf7Features()\n  virtual uint64_t CPUX86ExtendedFeatures() const = 0;\n\n  //! \\brief Returns an x86-family snapshot system’s “leaf 7” CPU features.\n  //!\n  //! This is the “leaf 7” feature information as encoded in `cpuid 7` `ebx`. If\n  //! `cpuid 7` is not supported by the snapshot CPU, this returns `0`.\n  //!\n  //! This method must only be called when GetCPUArchitecture() indicates an\n  //! x86-family CPU architecture (#kCPUArchitectureX86 or\n  //! #kCPUArchitectureX86_64).\n  //!\n  //! \\return An x86 family-specific value identifying “leaf 7” CPU features.\n  //!\n  //! \\sa CPUX86Features()\n  //! \\sa CPUX86ExtendedFeatures()\n  virtual uint32_t CPUX86Leaf7Features() const = 0;\n\n  //! \\brief Returns an x86-family snapshot system’s CPU’s support for the SSE\n  //!     DAZ (“denormals are zeros”) mode.\n  //!\n  //! This determines whether the CPU supports DAZ mode at all, not whether this\n  //! mode is enabled for any particular thread. DAZ mode support is detected by\n  //! examining the DAZ bit in the `mxcsr_mask` field of the floating-point\n  //! context saved by `fxsave`.\n  //!\n  //! This method must only be called when GetCPUArchitecture() indicates an\n  //! x86-family CPU architecture (#kCPUArchitectureX86 or\n  //! #kCPUArchitectureX86_64).\n  //!\n  //! \\return `true` if the snapshot system’s CPUs support the SSE DAZ mode,\n  //!     `false` if they do not.\n  virtual bool CPUX86SupportsDAZ() const = 0;\n\n  //! \\brief Returns the snapshot system’s operating system family.\n  virtual OperatingSystem GetOperatingSystem() const = 0;\n\n  //! \\brief Returns whether the snapshot system runs a server variant of its\n  //!     operating system.\n  virtual bool OSServer() const = 0;\n\n  //! \\brief Returns the snapshot system’s operating system version information\n  //!     in \\a major, \\a minor, \\a bugfix, and \\a build.\n  //!\n  //! \\param[out] major The snapshot system’s operating system’s first (major)\n  //!     version number component. This would be `10` for macOS 10.12.1, and\n  //!     `6` for Windows 7 (NT 6.1) SP1 version 6.1.7601.\n  //! \\param[out] minor The snapshot system’s operating system’s second (minor)\n  //!     version number component. This would be `12` for macOS 10.12.1, and\n  //!     `1` for Windows 7 (NT 6.1) SP1 version 6.1.7601.\n  //! \\param[out] bugfix The snapshot system’s operating system’s third (bugfix)\n  //!     version number component. This would be `1` for macOS 10.12.1, and\n  //!     `7601` for Windows 7 (NT 6.1) SP1 version 6.1.7601.\n  //! \\param[out] build A string further identifying an operating system\n  //!     version. For macOS 10.12.1, this would be `\"16B2657\"`. For Windows,\n  //!     this would be `\"Service Pack 1\"` if that service pack was installed.\n  //!     On Android, the `ro.build.fingerprint` system property would be\n  //!     appended. For Linux and other Unix-like systems, this would be the\n  //!     kernel version from `uname -srvm`, possibly with additional\n  //!     information appended.\n  virtual void OSVersion(int* major,\n                         int* minor,\n                         int* bugfix,\n                         std::string* build) const = 0;\n\n  //! \\brief Returns the snapshot system’s full operating system version\n  //!     information in string format.\n  //!\n  //! For macOS, the string contains values from the operating system and\n  //! kernel. A macOS 10.12.1 system snapshot would be identified as `\"Mac OS\n  //! X 10.12.1 (16B2657); Darwin 16.1.0 Darwin Kernel Version 16.1.0: Wed Oct\n  //! 19 20:31:56 PDT 2016; root:xnu-3789.21.4~4/RELEASE_X86_64 x86_64\"`.\n  virtual std::string OSVersionFull() const = 0;\n\n  //! \\brief Returns a description of the snapshot system’s hardware in string\n  //!     format.\n  //!\n  //! For macOS, the string contains the Mac model and board ID. A mid-2014 15\"\n  //! MacBook Pro would be identified as `\"MacBookPro11,3\n  //! (Mac-2BD1B31983FE1663)\"`.\n  virtual std::string MachineDescription() const = 0;\n\n  //! \\brief Returns the status of the NX (no-execute, or XD, execute-disable)\n  //!     feature on the snapshot system.\n  //!\n  //! This refers to a feature that allows mapped readable pages to be marked\n  //! as non-executable.\n  //!\n  //! \\return `true` if the snapshot system supports NX and it is enabled.\n  virtual bool NXEnabled() const = 0;\n\n  //! \\brief Returns time zone information from the snapshot system, based on\n  //!     its locale configuration and real-time clock.\n  //!\n  //! \\param[out] dst_status Whether the location observes daylight saving time,\n  //!     and if so, whether it or standard time is currently being observed.\n  //! \\param[out] standard_offset_seconds The number of seconds that the\n  //!     location’s time zone is east (ahead) of UTC during standard time.\n  //! \\param[out] daylight_offset_seconds The number of seconds that the\n  //!     location’s time zone is east (ahead) of UTC during daylight saving.\n  //!     time.\n  //! \\param[out] standard_name The name of the time zone while standard time is\n  //!     being observed.\n  //! \\param[out] daylight_name The name of the time zone while daylight saving\n  //!     time is being observed.\n  virtual void TimeZone(DaylightSavingTimeStatus* dst_status,\n                        int* standard_offset_seconds,\n                        int* daylight_offset_seconds,\n                        std::string* standard_name,\n                        std::string* daylight_name) const = 0;\n\n  //! \\brief Returns a mask indicating the range of valid addresses for a\n  //!     pointer.\n  //!\n  //! ARM64 supports storing pointer authentication codes in the upper bits of\n  //! a pointer. This mask is generated based on the number of bits in a pointer\n  //! reserved for the authentication codes. To recover an address from pointer\n  //! with an authentication code, `AND` this mask with the pointer. If the pac\n  //! sign extension bit is set, instead `~` and `OR` this mask with the\n  //! pointer.\n  //!\n  //! If the platform does not support pointer authentication, or the range of\n  //! valid addressees for a pointer was inaccessible, this field will be 0.\n  virtual uint64_t AddressMask() const = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/test/test_cpu_context.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/test/test_cpu_context.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\n// This is templatized because the CPUContextX86::Fxsave and\n// CPUContextX86_64::Fxsave are nearly identical but have different sizes for\n// the members |xmm|, |reserved_4|, and |available|.\ntemplate <typename FxsaveType>\nvoid InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) {\n  uint32_t value = *seed;\n\n  fxsave->fcw = static_cast<uint16_t>(value++);\n  fxsave->fsw = static_cast<uint16_t>(value++);\n  fxsave->ftw = static_cast<uint8_t>(value++);\n  fxsave->reserved_1 = static_cast<uint8_t>(value++);\n  fxsave->fop = static_cast<uint16_t>(value++);\n  fxsave->fpu_ip = value++;\n  fxsave->fpu_cs = static_cast<uint16_t>(value++);\n  fxsave->reserved_2 = static_cast<uint16_t>(value++);\n  fxsave->fpu_dp = value++;\n  fxsave->fpu_ds = static_cast<uint16_t>(value++);\n  fxsave->reserved_3 = static_cast<uint16_t>(value++);\n  fxsave->mxcsr = value++;\n  fxsave->mxcsr_mask = value++;\n  for (size_t st_mm_index = 0; st_mm_index < std::size(fxsave->st_mm);\n       ++st_mm_index) {\n    for (size_t byte = 0; byte < std::size(fxsave->st_mm[st_mm_index].st);\n         ++byte) {\n      fxsave->st_mm[st_mm_index].st[byte] = static_cast<uint8_t>(value++);\n    }\n    for (size_t byte = 0;\n         byte < std::size(fxsave->st_mm[st_mm_index].st_reserved);\n         ++byte) {\n      fxsave->st_mm[st_mm_index].st_reserved[byte] =\n          static_cast<uint8_t>(value);\n    }\n  }\n  for (size_t xmm_index = 0; xmm_index < std::size(fxsave->xmm); ++xmm_index) {\n    for (size_t byte = 0; byte < std::size(fxsave->xmm[xmm_index]); ++byte) {\n      fxsave->xmm[xmm_index][byte] = static_cast<uint8_t>(value++);\n    }\n  }\n  for (size_t byte = 0; byte < std::size(fxsave->reserved_4); ++byte) {\n    fxsave->reserved_4[byte] = static_cast<uint8_t>(value++);\n  }\n  for (size_t byte = 0; byte < std::size(fxsave->available); ++byte) {\n    fxsave->available[byte] = static_cast<uint8_t>(value++);\n  }\n\n  *seed = value;\n}\n\n}  // namespace\n\nvoid InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave,\n                                   uint32_t* seed) {\n  return InitializeCPUContextFxsave(fxsave, seed);\n}\n\nvoid InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave,\n                                      uint32_t* seed) {\n  return InitializeCPUContextFxsave(fxsave, seed);\n}\n\nvoid InitializeCPUContextX86(CPUContext* context, uint32_t seed) {\n  context->architecture = kCPUArchitectureX86;\n\n  if (seed == 0) {\n    memset(context->x86, 0, sizeof(*context->x86));\n    return;\n  }\n\n  uint32_t value = seed;\n\n  context->x86->eax = value++;\n  context->x86->ebx = value++;\n  context->x86->ecx = value++;\n  context->x86->edx = value++;\n  context->x86->edi = value++;\n  context->x86->esi = value++;\n  context->x86->ebp = value++;\n  context->x86->esp = value++;\n  context->x86->eip = value++;\n  context->x86->eflags = value++;\n  context->x86->cs = static_cast<uint16_t>(value++);\n  context->x86->ds = static_cast<uint16_t>(value++);\n  context->x86->es = static_cast<uint16_t>(value++);\n  context->x86->fs = static_cast<uint16_t>(value++);\n  context->x86->gs = static_cast<uint16_t>(value++);\n  context->x86->ss = static_cast<uint16_t>(value++);\n  InitializeCPUContextX86Fxsave(&context->x86->fxsave, &value);\n  context->x86->dr0 = value++;\n  context->x86->dr1 = value++;\n  context->x86->dr2 = value++;\n  context->x86->dr3 = value++;\n  context->x86->dr4 = value++;\n  context->x86->dr5 = value++;\n  context->x86->dr6 = value++;\n  context->x86->dr7 = value++;\n}\n\nvoid InitializeCPUContextX86_64(CPUContext* context, uint32_t seed) {\n  context->architecture = kCPUArchitectureX86_64;\n\n  if (seed == 0) {\n    memset(context->x86_64, 0, sizeof(*context->x86_64));\n    return;\n  }\n\n  uint32_t value = seed;\n\n  context->x86_64->rax = value++;\n  context->x86_64->rbx = value++;\n  context->x86_64->rcx = value++;\n  context->x86_64->rdx = value++;\n  context->x86_64->rdi = value++;\n  context->x86_64->rsi = value++;\n  context->x86_64->rbp = value++;\n  context->x86_64->rsp = value++;\n  context->x86_64->r8 = value++;\n  context->x86_64->r9 = value++;\n  context->x86_64->r10 = value++;\n  context->x86_64->r11 = value++;\n  context->x86_64->r12 = value++;\n  context->x86_64->r13 = value++;\n  context->x86_64->r14 = value++;\n  context->x86_64->r15 = value++;\n  context->x86_64->rip = value++;\n  context->x86_64->rflags = value++;\n  context->x86_64->cs = static_cast<uint16_t>(value++);\n  context->x86_64->fs = static_cast<uint16_t>(value++);\n  context->x86_64->gs = static_cast<uint16_t>(value++);\n  InitializeCPUContextX86_64Fxsave(&context->x86_64->fxsave, &value);\n  context->x86_64->dr0 = value++;\n  context->x86_64->dr1 = value++;\n  context->x86_64->dr2 = value++;\n  context->x86_64->dr3 = value++;\n  context->x86_64->dr4 = value++;\n  context->x86_64->dr5 = value++;\n  context->x86_64->dr6 = value++;\n  context->x86_64->dr7 = value++;\n\n  // Make sure this is sensible. When supplied the size/state come from the OS.\n  memset(&context->x86_64->xstate, 0, sizeof(context->x86_64->xstate));\n}\n\nvoid InitializeCPUContextARM(CPUContext* context, uint32_t seed) {\n  context->architecture = kCPUArchitectureARM;\n  CPUContextARM* arm = context->arm;\n\n  if (seed == 0) {\n    memset(arm, 0, sizeof(*arm));\n    return;\n  }\n\n  uint32_t value = seed;\n\n  for (size_t index = 0; index < std::size(arm->regs); ++index) {\n    arm->regs[index] = value++;\n  }\n  arm->fp = value++;\n  arm->ip = value++;\n  arm->ip = value++;\n  arm->sp = value++;\n  arm->lr = value++;\n  arm->pc = value++;\n  arm->cpsr = value++;\n\n  for (size_t index = 0; index < std::size(arm->vfp_regs.vfp); ++index) {\n    arm->vfp_regs.vfp[index] = value++;\n  }\n  arm->vfp_regs.fpscr = value++;\n\n  arm->have_fpa_regs = false;\n  arm->have_vfp_regs = true;\n}\n\nvoid InitializeCPUContextARM64(CPUContext* context, uint32_t seed) {\n  context->architecture = kCPUArchitectureARM64;\n  CPUContextARM64* arm64 = context->arm64;\n\n  if (seed == 0) {\n    memset(arm64, 0, sizeof(*arm64));\n    return;\n  }\n\n  uint32_t value = seed;\n\n  for (size_t index = 0; index < std::size(arm64->regs); ++index) {\n    arm64->regs[index] = value++;\n  }\n  arm64->sp = value++;\n  arm64->pc = value++;\n  arm64->spsr = value++;\n\n  for (size_t index = 0; index < std::size(arm64->fpsimd); ++index) {\n    arm64->fpsimd[index].lo = value++;\n    arm64->fpsimd[index].hi = value++;\n  }\n  arm64->fpsr = value++;\n  arm64->fpcr = value++;\n}\n\nvoid InitializeCPUContextMIPS(CPUContext* context, uint32_t seed) {\n  context->architecture = kCPUArchitectureMIPSEL;\n  CPUContextMIPS* mipsel = context->mipsel;\n\n  if (seed == 0) {\n    memset(mipsel, 0, sizeof(*mipsel));\n    return;\n  }\n\n  uint32_t value = seed;\n\n  for (size_t index = 0; index < std::size(mipsel->regs); ++index) {\n    mipsel->regs[index] = value++;\n  }\n\n  mipsel->mdlo = value++;\n  mipsel->mdhi = value++;\n  mipsel->cp0_epc = value++;\n  mipsel->cp0_badvaddr = value++;\n  mipsel->cp0_status = value++;\n  mipsel->cp0_cause = value++;\n\n  for (size_t index = 0; index < std::size(mipsel->fpregs.fregs); ++index) {\n    mipsel->fpregs.fregs[index]._fp_fregs = static_cast<float>(value++);\n  }\n\n  mipsel->fpcsr = value++;\n  mipsel->fir = value++;\n\n  for (size_t index = 0; index < 3; ++index) {\n    mipsel->hi[index] = value++;\n    mipsel->lo[index] = value++;\n  }\n  mipsel->dsp_control = value++;\n}\n\nvoid InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed) {\n  context->architecture = kCPUArchitectureMIPS64EL;\n  CPUContextMIPS64* mips64 = context->mips64;\n\n  if (seed == 0) {\n    memset(mips64, 0, sizeof(*mips64));\n    return;\n  }\n\n  uint64_t value = seed;\n\n  for (size_t index = 0; index < std::size(mips64->regs); ++index) {\n    mips64->regs[index] = value++;\n  }\n\n  mips64->mdlo = value++;\n  mips64->mdhi = value++;\n  mips64->cp0_epc = value++;\n  mips64->cp0_badvaddr = value++;\n  mips64->cp0_status = value++;\n  mips64->cp0_cause = value++;\n\n  for (size_t index = 0; index < std::size(mips64->fpregs.dregs); ++index) {\n    mips64->fpregs.dregs[index] = static_cast<double>(value++);\n  }\n\n  mips64->fpcsr = value++;\n  mips64->fir = value++;\n\n  for (size_t index = 0; index < 3; ++index) {\n    mips64->hi[index] = value++;\n    mips64->lo[index] = value++;\n  }\n  mips64->dsp_control = value++;\n}\n\nvoid InitializeCPUContextRISCV64(CPUContext* context, uint32_t seed) {\n  context->architecture = kCPUArchitectureRISCV64;\n  CPUContextRISCV64* riscv64 = context->riscv64;\n\n  if (seed == 0) {\n    memset(riscv64, 0, sizeof(*riscv64));\n    return;\n  }\n\n  uint32_t value = seed;\n\n  riscv64->pc = value++;\n  for (size_t index = 0; index < std::size(riscv64->regs); ++index) {\n    riscv64->regs[index] = value++;\n  }\n\n  for (size_t index = 0; index < std::size(riscv64->fpregs); ++index) {\n    riscv64->fpregs[index] = value++;\n  }\n  riscv64->fcsr = value++;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/test/test_cpu_context.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_\n#define CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_\n\n#include <stdint.h>\n\n#include \"snapshot/cpu_context.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Initializes an `fxsave` context substructure for testing.\n//!\n//! \\param[out] fxsave The structure to initialize.\n//! \\param[in,out] seed The seed value. Initializing two `fxsave` structures of\n//!     the same type with identical seed values should produce identical\n//!     structures. Initialization with a different seed value should produce\n//!     a different `fxsave` structure. If \\a seed is `0`, \\a fxsave is zeroed\n//!     out entirely. If \\a seed is nonzero, \\a fxsave will be populated\n//!     entirely with nonzero values. \\a seed will be updated by this function\n//!     to allow the caller to perform subsequent initialization of the context\n//!     structure containing \\a fxsave.\n//!\n//! \\{\nvoid InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave,\n                                   uint32_t* seed);\nvoid InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave,\n                                      uint32_t* seed);\n//! \\}\n\n//! \\brief Initializes a context structure for testing.\n//!\n//! Initialization is compatible with the initialization used by minidump\n//! context test initialization functions such as InitializeMinidumpContextX86()\n//! and InitializeMinidumpContextAMD64() for identical \\a seed values.\n//!\n//! \\param[out] context The structure to initialize.\n//! \\param[in] seed The seed value. Initializing two context structures of the\n//!     same type with identical seed values should produce identical context\n//!     structures. Initialization with a different seed value should produce\n//!     a different context structure. If \\a seed is `0`, \\a context is zeroed\n//!     out entirely except for the CPUContext::architecture field, which will\n//!     identify the context type. If \\a seed is nonzero, \\a context will be\n//!     populated entirely with nonzero values.\n//!\n//! \\{\nvoid InitializeCPUContextX86(CPUContext* context, uint32_t seed);\nvoid InitializeCPUContextX86_64(CPUContext* context, uint32_t seed);\nvoid InitializeCPUContextARM(CPUContext* context, uint32_t seed);\nvoid InitializeCPUContextARM64(CPUContext* context, uint32_t seed);\nvoid InitializeCPUContextMIPS(CPUContext* context, uint32_t seed);\nvoid InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed);\nvoid InitializeCPUContextRISCV64(CPUContext* context, uint32_t seed);\n//! \\}\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_\n"
  },
  {
    "path": "snapshot/test/test_exception_snapshot.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/test/test_exception_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\nTestExceptionSnapshot::TestExceptionSnapshot()\n    : context_union_(),\n      context_(),\n      thread_id_(0),\n      exception_(0),\n      exception_info_(0),\n      exception_address_(0),\n      codes_() {\n  context_.x86 = &context_union_.x86;\n}\n\nTestExceptionSnapshot::~TestExceptionSnapshot() {\n}\n\nconst CPUContext* TestExceptionSnapshot::Context() const {\n  return &context_;\n}\n\nuint64_t TestExceptionSnapshot::ThreadID() const {\n  return thread_id_;\n}\n\nuint32_t TestExceptionSnapshot::Exception() const {\n  return exception_;\n}\n\nuint32_t TestExceptionSnapshot::ExceptionInfo() const {\n  return exception_info_;\n}\n\nuint64_t TestExceptionSnapshot::ExceptionAddress() const {\n  return exception_address_;\n}\n\nconst std::vector<uint64_t>& TestExceptionSnapshot::Codes() const {\n  return codes_;\n}\n\nstd::vector<const MemorySnapshot*> TestExceptionSnapshot::ExtraMemory() const {\n  std::vector<const MemorySnapshot*> extra_memory;\n  for (const auto& em : extra_memory_) {\n    extra_memory.push_back(em.get());\n  }\n  return extra_memory;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/test/test_exception_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_\n\n#include <stdint.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/exception_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A test ExceptionSnapshot that can carry arbitrary data for testing\n//!     purposes.\nclass TestExceptionSnapshot final : public ExceptionSnapshot {\n public:\n  TestExceptionSnapshot();\n\n  TestExceptionSnapshot(const TestExceptionSnapshot&) = delete;\n  TestExceptionSnapshot& operator=(const TestExceptionSnapshot&) = delete;\n\n  ~TestExceptionSnapshot();\n\n  //! \\brief Obtains a pointer to the underlying mutable CPUContext structure.\n  //!\n  //! This method is intended to be used by callers to populate the CPUContext\n  //! structure.\n  //!\n  //! \\return The same pointer that Context() does, while treating the data as\n  //!     mutable.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly.\n  CPUContext* MutableContext() { return &context_; }\n\n  void SetThreadID(uint64_t thread_id) { thread_id_ = thread_id; }\n  void SetException(uint32_t exception) { exception_ = exception; }\n  void SetExceptionInfo(uint32_t exception_information) {\n    exception_info_ = exception_information;\n  }\n  void SetExceptionAddress(uint64_t exception_address) {\n    exception_address_ = exception_address;\n  }\n  void SetCodes(const std::vector<uint64_t>& codes) { codes_ = codes; }\n  void AddExtraMemory(std::unique_ptr<MemorySnapshot> extra_memory) {\n    extra_memory_.push_back(std::move(extra_memory));\n  }\n\n  // ExceptionSnapshot:\n\n  const CPUContext* Context() const override;\n  uint64_t ThreadID() const override;\n  uint32_t Exception() const override;\n  uint32_t ExceptionInfo() const override;\n  uint64_t ExceptionAddress() const override;\n  const std::vector<uint64_t>& Codes() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  union {\n    CPUContextX86 x86;\n    CPUContextX86_64 x86_64;\n  } context_union_;\n  CPUContext context_;\n  uint64_t thread_id_;\n  uint32_t exception_;\n  uint32_t exception_info_;\n  uint64_t exception_address_;\n  std::vector<uint64_t> codes_;\n  std::vector<std::unique_ptr<MemorySnapshot>> extra_memory_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/test/test_memory_map_region_snapshot.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/test/test_memory_map_region_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\nTestMemoryMapRegionSnapshot::TestMemoryMapRegionSnapshot() : memory_info_() {\n}\n\nTestMemoryMapRegionSnapshot::~TestMemoryMapRegionSnapshot() {\n}\n\nvoid TestMemoryMapRegionSnapshot::SetMindumpMemoryInfo(\n    const MINIDUMP_MEMORY_INFO& mmi) {\n  memory_info_ = mmi;\n}\n\nconst MINIDUMP_MEMORY_INFO& TestMemoryMapRegionSnapshot::AsMinidumpMemoryInfo()\n    const {\n  return memory_info_;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/test/test_memory_map_region_snapshot.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_\n\n#include <vector>\n\n#include \"snapshot/memory_map_region_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A test MemoryMapRegionSnapshot that can carry arbitrary data for\n//!     testing purposes.\nclass TestMemoryMapRegionSnapshot final : public MemoryMapRegionSnapshot {\n public:\n  TestMemoryMapRegionSnapshot();\n\n  TestMemoryMapRegionSnapshot(const TestMemoryMapRegionSnapshot&) = delete;\n  TestMemoryMapRegionSnapshot& operator=(const TestMemoryMapRegionSnapshot&) =\n      delete;\n\n  ~TestMemoryMapRegionSnapshot() override;\n\n  void SetMindumpMemoryInfo(const MINIDUMP_MEMORY_INFO& mmi);\n\n  // MemoryMapRegionSnapshot:\n  const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override;\n\n private:\n  MINIDUMP_MEMORY_INFO memory_info_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/test/test_memory_snapshot.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/test/test_memory_snapshot.h\"\n\n#include <memory>\n#include <string>\n\nnamespace crashpad {\nnamespace test {\n\nTestMemorySnapshot::TestMemorySnapshot()\n    : address_(0), size_(0), value_('\\0'), should_fail_(false) {\n}\n\nTestMemorySnapshot::~TestMemorySnapshot() {\n}\n\nuint64_t TestMemorySnapshot::Address() const {\n  return address_;\n}\n\nsize_t TestMemorySnapshot::Size() const {\n  return size_;\n}\n\nbool TestMemorySnapshot::Read(Delegate* delegate) const {\n  if (should_fail_) {\n    return false;\n  }\n\n  if (size_ == 0) {\n    return delegate->MemorySnapshotDelegateRead(nullptr, size_);\n  }\n\n  std::string buffer(size_, value_);\n  return delegate->MemorySnapshotDelegateRead(&buffer[0], size_);\n}\n\nconst MemorySnapshot* TestMemorySnapshot::MergeWithOtherSnapshot(\n    const MemorySnapshot* other) const {\n  CheckedRange<uint64_t, size_t> merged(0, 0);\n  if (!DetermineMergedRange(this, other, &merged))\n    return nullptr;\n\n  std::unique_ptr<TestMemorySnapshot> result(new TestMemorySnapshot());\n  result->SetAddress(merged.base());\n  result->SetSize(merged.size());\n  result->SetValue(value_);\n  return result.release();\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/test/test_memory_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include \"snapshot/memory_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A test MemorySnapshot that can carry arbitrary data for testing\n//!     purposes.\nclass TestMemorySnapshot final : public MemorySnapshot {\n public:\n  TestMemorySnapshot();\n\n  TestMemorySnapshot(const TestMemorySnapshot&) = delete;\n  TestMemorySnapshot& operator=(const TestMemorySnapshot&) = delete;\n\n  ~TestMemorySnapshot();\n\n  void SetAddress(uint64_t address) { address_ = address; }\n  void SetSize(size_t size) { size_ = size; }\n\n  //! \\brief Sets the value to fill the test memory region with.\n  //!\n  //! \\param[in] value The value to be written to \\a delegate when Read() is\n  //!     called. This value will be repeated Size() times.\n  void SetValue(char value) { value_ = value; }\n\n  void SetShouldFailRead(bool should_fail) { should_fail_ = true; }\n\n  // MemorySnapshot:\n\n  uint64_t Address() const override;\n  size_t Size() const override;\n  bool Read(Delegate* delegate) const override;\n  const MemorySnapshot* MergeWithOtherSnapshot(\n      const MemorySnapshot* other) const override;\n\n private:\n  uint64_t address_;\n  size_t size_;\n  char value_;\n  bool should_fail_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/test/test_module_snapshot.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/test/test_module_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\nTestModuleSnapshot::TestModuleSnapshot()\n    : name_(),\n      address_(0),\n      size_(0),\n      timestamp_(0),\n      file_version_(),\n      source_version_(),\n      module_type_(kModuleTypeUnknown),\n      age_(0),\n      uuid_(),\n      debug_file_name_(),\n      annotations_vector_(),\n      annotations_simple_map_(),\n      extra_memory_ranges_() {}\n\nTestModuleSnapshot::~TestModuleSnapshot() {}\n\nstd::string TestModuleSnapshot::Name() const {\n  return name_;\n}\n\nuint64_t TestModuleSnapshot::Address() const {\n  return address_;\n}\n\nuint64_t TestModuleSnapshot::Size() const {\n  return size_;\n}\n\ntime_t TestModuleSnapshot::Timestamp() const {\n  return timestamp_;\n}\n\nvoid TestModuleSnapshot::FileVersion(uint16_t* version_0,\n                                     uint16_t* version_1,\n                                     uint16_t* version_2,\n                                     uint16_t* version_3) const {\n  *version_0 = file_version_[0];\n  *version_1 = file_version_[1];\n  *version_2 = file_version_[2];\n  *version_3 = file_version_[3];\n}\n\nvoid TestModuleSnapshot::SourceVersion(uint16_t* version_0,\n                                       uint16_t* version_1,\n                                       uint16_t* version_2,\n                                       uint16_t* version_3) const {\n  *version_0 = source_version_[0];\n  *version_1 = source_version_[1];\n  *version_2 = source_version_[2];\n  *version_3 = source_version_[3];\n}\n\nModuleSnapshot::ModuleType TestModuleSnapshot::GetModuleType() const {\n  return module_type_;\n}\n\nvoid TestModuleSnapshot::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const {\n  *uuid = uuid_;\n  *age = age_;\n}\n\nstd::string TestModuleSnapshot::DebugFileName() const {\n  return debug_file_name_;\n}\n\nstd::vector<uint8_t> TestModuleSnapshot::BuildID() const {\n  return build_id_;\n}\n\nstd::vector<std::string> TestModuleSnapshot::AnnotationsVector() const {\n  return annotations_vector_;\n}\n\nstd::map<std::string, std::string> TestModuleSnapshot::AnnotationsSimpleMap()\n    const {\n  return annotations_simple_map_;\n}\n\nstd::vector<AnnotationSnapshot> TestModuleSnapshot::AnnotationObjects() const {\n  return annotation_objects_;\n}\n\nstd::set<CheckedRange<uint64_t>> TestModuleSnapshot::ExtraMemoryRanges() const {\n  return extra_memory_ranges_;\n}\n\nstd::vector<const UserMinidumpStream*>\nTestModuleSnapshot::CustomMinidumpStreams() const {\n  return std::vector<const UserMinidumpStream*>();\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/test/test_module_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"snapshot/module_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A test ModuleSnapshot that can carry arbitrary data for testing\n//!     purposes.\nclass TestModuleSnapshot final : public ModuleSnapshot {\n public:\n  TestModuleSnapshot();\n\n  TestModuleSnapshot(const TestModuleSnapshot&) = delete;\n  TestModuleSnapshot& operator=(const TestModuleSnapshot&) = delete;\n\n  ~TestModuleSnapshot() override;\n\n  void SetName(const std::string& name) { name_ = name; }\n  void SetAddressAndSize(uint64_t address, uint64_t size) {\n    address_ = address;\n    size_ = size;\n  }\n  void SetTimestamp(time_t timestamp) { timestamp_ = timestamp; }\n  void SetFileVersion(uint16_t file_version_0,\n                      uint16_t file_version_1,\n                      uint16_t file_version_2,\n                      uint16_t file_version_3) {\n    file_version_[0] = file_version_0;\n    file_version_[1] = file_version_1;\n    file_version_[2] = file_version_2;\n    file_version_[3] = file_version_3;\n  }\n  void SetSourceVersion(uint16_t source_version_0,\n                        uint16_t source_version_1,\n                        uint16_t source_version_2,\n                        uint16_t source_version_3) {\n    source_version_[0] = source_version_0;\n    source_version_[1] = source_version_1;\n    source_version_[2] = source_version_2;\n    source_version_[3] = source_version_3;\n  }\n  void SetModuleType(ModuleType module_type) { module_type_ = module_type; }\n  void SetUUIDAndAge(const crashpad::UUID& uuid, uint32_t age) {\n    uuid_ = uuid;\n    age_ = age;\n  }\n  void SetBuildID(const std::vector<uint8_t>& build_id) {\n    build_id_ = build_id;\n  }\n  void SetDebugFileName(const std::string& debug_file_name) {\n    debug_file_name_ = debug_file_name;\n  }\n  void SetAnnotationsVector(\n      const std::vector<std::string>& annotations_vector) {\n    annotations_vector_ = annotations_vector;\n  }\n  void SetAnnotationsSimpleMap(\n      const std::map<std::string, std::string>& annotations_simple_map) {\n    annotations_simple_map_ = annotations_simple_map;\n  }\n  void SetAnnotationObjects(\n      const std::vector<AnnotationSnapshot>& annotations) {\n    annotation_objects_ = annotations;\n  }\n  void SetExtraMemoryRanges(\n      const std::set<CheckedRange<uint64_t>>& extra_memory_ranges) {\n    extra_memory_ranges_ = extra_memory_ranges;\n  }\n\n  // ModuleSnapshot:\n\n  std::string Name() const override;\n  uint64_t Address() const override;\n  uint64_t Size() const override;\n  time_t Timestamp() const override;\n  void FileVersion(uint16_t* version_0,\n                   uint16_t* version_1,\n                   uint16_t* version_2,\n                   uint16_t* version_3) const override;\n  void SourceVersion(uint16_t* version_0,\n                     uint16_t* version_1,\n                     uint16_t* version_2,\n                     uint16_t* version_3) const override;\n  ModuleType GetModuleType() const override;\n  void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;\n  std::string DebugFileName() const override;\n  std::vector<uint8_t> BuildID() const override;\n  std::vector<std::string> AnnotationsVector() const override;\n  std::map<std::string, std::string> AnnotationsSimpleMap() const override;\n  std::vector<AnnotationSnapshot> AnnotationObjects() const override;\n  std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;\n  std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;\n\n private:\n  std::string name_;\n  uint64_t address_;\n  uint64_t size_;\n  time_t timestamp_;\n  uint16_t file_version_[4];\n  uint16_t source_version_[4];\n  ModuleType module_type_;\n  uint32_t age_;\n  crashpad::UUID uuid_;\n  std::vector<uint8_t> build_id_;\n  std::string debug_file_name_;\n  std::vector<std::string> annotations_vector_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  std::vector<AnnotationSnapshot> annotation_objects_;\n  std::set<CheckedRange<uint64_t>> extra_memory_ranges_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/test/test_process_snapshot.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"snapshot/test/test_process_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\nTestProcessSnapshot::TestProcessSnapshot()\n    : process_id_(0),\n      parent_process_id_(0),\n      snapshot_time_(),\n      process_start_time_(),\n      process_cpu_user_time_(),\n      process_cpu_system_time_(),\n      report_id_(),\n      client_id_(),\n      annotations_simple_map_(),\n      system_(),\n      threads_(),\n      modules_(),\n      exception_(),\n      memory_map_(),\n      handles_(),\n      extra_memory_(),\n      process_memory_() {\n}\n\nTestProcessSnapshot::~TestProcessSnapshot() {\n}\n\ncrashpad::ProcessID TestProcessSnapshot::ProcessID() const {\n  return process_id_;\n}\n\ncrashpad::ProcessID TestProcessSnapshot::ParentProcessID() const {\n  return parent_process_id_;\n}\n\nvoid TestProcessSnapshot::SnapshotTime(timeval* snapshot_time) const {\n  *snapshot_time = snapshot_time_;\n}\n\nvoid TestProcessSnapshot::ProcessStartTime(timeval* start_time) const {\n  *start_time = process_start_time_;\n}\n\nvoid TestProcessSnapshot::ProcessCPUTimes(timeval* user_time,\n                                          timeval* system_time) const {\n  *user_time = process_cpu_user_time_;\n  *system_time = process_cpu_system_time_;\n}\n\nvoid TestProcessSnapshot::ReportID(UUID* report_id) const {\n  *report_id = report_id_;\n}\n\nvoid TestProcessSnapshot::ClientID(UUID* client_id) const {\n  *client_id = client_id_;\n}\n\nconst std::map<std::string, std::string>&\nTestProcessSnapshot::AnnotationsSimpleMap() const {\n  return annotations_simple_map_;\n}\n\nconst SystemSnapshot* TestProcessSnapshot::System() const {\n  return system_.get();\n}\n\nstd::vector<const ThreadSnapshot*> TestProcessSnapshot::Threads() const {\n  std::vector<const ThreadSnapshot*> threads;\n  for (const auto& thread : threads_) {\n    threads.push_back(thread.get());\n  }\n  return threads;\n}\n\nstd::vector<const ModuleSnapshot*> TestProcessSnapshot::Modules() const {\n  std::vector<const ModuleSnapshot*> modules;\n  for (const auto& module : modules_) {\n    modules.push_back(module.get());\n  }\n  return modules;\n}\n\nstd::vector<UnloadedModuleSnapshot> TestProcessSnapshot::UnloadedModules()\n    const {\n  return unloaded_modules_;\n}\n\nconst ExceptionSnapshot* TestProcessSnapshot::Exception() const {\n  return exception_.get();\n}\n\nstd::vector<const MemoryMapRegionSnapshot*> TestProcessSnapshot::MemoryMap()\n    const {\n  std::vector<const MemoryMapRegionSnapshot*> memory_map;\n  for (const auto& item : memory_map_) {\n    memory_map.push_back(item.get());\n  }\n  return memory_map;\n}\n\nstd::vector<HandleSnapshot> TestProcessSnapshot::Handles() const {\n  return handles_;\n}\n\nstd::vector<const MemorySnapshot*> TestProcessSnapshot::ExtraMemory() const {\n  std::vector<const MemorySnapshot*> extra_memory;\n  for (const auto& em : extra_memory_) {\n    extra_memory.push_back(em.get());\n  }\n  return extra_memory;\n}\n\nconst ProcessMemory* TestProcessSnapshot::Memory() const {\n  return process_memory_.get();\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/test/test_process_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_\n\n#include <stdint.h>\n#include <sys/time.h>\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/memory_map_region_snapshot.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/process/process_id.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A test ProcessSnapshot that can carry arbitrary data for testing\n//!     purposes.\nclass TestProcessSnapshot final : public ProcessSnapshot {\n public:\n  TestProcessSnapshot();\n\n  TestProcessSnapshot(const TestProcessSnapshot&) = delete;\n  TestProcessSnapshot& operator=(const TestProcessSnapshot&) = delete;\n\n  ~TestProcessSnapshot() override;\n\n  void SetProcessID(crashpad::ProcessID process_id) {\n    process_id_ = process_id;\n  }\n  void SetParentProcessID(crashpad::ProcessID parent_process_id) {\n    parent_process_id_ = parent_process_id;\n  }\n  void SetSnapshotTime(const timeval& snapshot_time) {\n    snapshot_time_ = snapshot_time;\n  }\n  void SetProcessStartTime(const timeval& start_time) {\n    process_start_time_ = start_time;\n  }\n  void SetProcessCPUTimes(const timeval& user_time,\n                          const timeval& system_time) {\n    process_cpu_user_time_ = user_time;\n    process_cpu_system_time_ = system_time;\n  }\n  void SetReportID(const UUID& report_id) { report_id_ = report_id; }\n  void SetClientID(const UUID& client_id) { client_id_ = client_id; }\n  void SetAnnotationsSimpleMap(\n      const std::map<std::string, std::string>& annotations_simple_map) {\n    annotations_simple_map_ = annotations_simple_map;\n  }\n\n  //! \\brief Sets the system snapshot to be returned by System().\n  //!\n  //! \\param[in] system The system snapshot that System() will return. The\n  //!     TestProcessSnapshot object takes ownership of \\a system.\n  void SetSystem(std::unique_ptr<SystemSnapshot> system) {\n    system_ = std::move(system);\n  }\n\n  //! \\brief Adds a thread snapshot to be returned by Threads().\n  //!\n  //! \\param[in] thread The thread snapshot that will be included in Threads().\n  //!     The TestProcessSnapshot object takes ownership of \\a thread.\n  void AddThread(std::unique_ptr<ThreadSnapshot> thread) {\n    threads_.push_back(std::move(thread));\n  }\n\n  //! \\brief Adds a module snapshot to be returned by Modules().\n  //!\n  //! \\param[in] module The module snapshot that will be included in Modules().\n  //!     The TestProcessSnapshot object takes ownership of \\a module.\n  void AddModule(std::unique_ptr<ModuleSnapshot> module) {\n    modules_.push_back(std::move(module));\n  }\n\n  //! \\brief Adds an unloaded module snapshot to be returned by\n  //!     UnloadedModules().\n  //!\n  //! \\param[in] unloaded_module The unloaded module snapshot that will be\n  //!     included in UnloadedModules().\n  void AddModule(const UnloadedModuleSnapshot& unloaded_module) {\n    unloaded_modules_.push_back(unloaded_module);\n  }\n\n  //! \\brief Sets the exception snapshot to be returned by Exception().\n  //!\n  //! \\param[in] exception The exception snapshot that Exception() will return.\n  //!     The TestProcessSnapshot object takes ownership of \\a exception.\n  void SetException(std::unique_ptr<ExceptionSnapshot> exception) {\n    exception_ = std::move(exception);\n  }\n\n  //! \\brief Adds a memory map region snapshot to be returned by MemoryMap().\n  //!\n  //! \\param[in] region The memory map region snapshot that will be included in\n  //!     MemoryMap(). The TestProcessSnapshot object takes ownership of \\a\n  //!     region.\n  void AddMemoryMapRegion(std::unique_ptr<MemoryMapRegionSnapshot> region) {\n    memory_map_.push_back(std::move(region));\n  }\n\n  //! \\brief Adds a handle snapshot to be returned by Handles().\n  //!\n  //! \\param[in] handle The handle snapshot that will be included in Handles().\n  void AddHandle(const HandleSnapshot& handle) {\n    handles_.push_back(handle);\n  }\n\n  //! \\brief Add a memory snapshot to be returned by ExtraMemory().\n  //!\n  //! \\param[in] extra_memory The memory snapshot that will be included in\n  //!     ExtraMemory(). The TestProcessSnapshot object takes ownership of \\a\n  //!     extra_memory.\n  void AddExtraMemory(std::unique_ptr<MemorySnapshot> extra_memory) {\n    extra_memory_.push_back(std::move(extra_memory));\n  }\n\n  //! \\brief Add a process memory object to be returned by Memory().\n  //!\n  //! \\param[in] process_memory The memory object that will be returned by\n  //!     Memory(). The TestProcessSnapshot object takes ownership of \\a\n  //!     extra_memory.\n  void SetProcessMemory(std::unique_ptr<ProcessMemory> process_memory) {\n    process_memory_ = std::move(process_memory);\n  }\n\n  // ProcessSnapshot:\n\n  crashpad::ProcessID ProcessID() const override;\n  crashpad::ProcessID ParentProcessID() const override;\n  void SnapshotTime(timeval* snapshot_time) const override;\n  void ProcessStartTime(timeval* start_time) const override;\n  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;\n  void ReportID(UUID* report_id) const override;\n  void ClientID(UUID* client_id) const override;\n  const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const override;\n  const SystemSnapshot* System() const override;\n  std::vector<const ThreadSnapshot*> Threads() const override;\n  std::vector<const ModuleSnapshot*> Modules() const override;\n  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;\n  const ExceptionSnapshot* Exception() const override;\n  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;\n  std::vector<HandleSnapshot> Handles() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n  const ProcessMemory* Memory() const override;\n\n private:\n  crashpad::ProcessID process_id_;\n  crashpad::ProcessID parent_process_id_;\n  timeval snapshot_time_;\n  timeval process_start_time_;\n  timeval process_cpu_user_time_;\n  timeval process_cpu_system_time_;\n  UUID report_id_;\n  UUID client_id_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  std::unique_ptr<SystemSnapshot> system_;\n  std::vector<std::unique_ptr<ThreadSnapshot>> threads_;\n  std::vector<std::unique_ptr<ModuleSnapshot>> modules_;\n  std::vector<UnloadedModuleSnapshot> unloaded_modules_;\n  std::unique_ptr<ExceptionSnapshot> exception_;\n  std::vector<std::unique_ptr<MemoryMapRegionSnapshot>> memory_map_;\n  std::vector<HandleSnapshot> handles_;\n  std::vector<std::unique_ptr<MemorySnapshot>> extra_memory_;\n  std::unique_ptr<ProcessMemory> process_memory_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/test/test_system_snapshot.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/test/test_system_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\nTestSystemSnapshot::TestSystemSnapshot()\n    : cpu_architecture_(kCPUArchitectureUnknown),\n      cpu_revision_(0),\n      cpu_count_(0),\n      cpu_vendor_(),\n      cpu_frequency_current_hz_(0),\n      cpu_frequency_max_hz_(0),\n      cpu_x86_signature_(0),\n      cpu_x86_features_(0),\n      cpu_x86_extended_features_(0),\n      cpu_x86_leaf_7_features_(0),\n      cpu_x86_supports_daz_(false),\n      operating_system_(kOperatingSystemUnknown),\n      os_server_(false),\n      os_version_major_(0),\n      os_version_minor_(0),\n      os_version_bugfix_(0),\n      os_version_build_(),\n      os_version_full_(),\n      address_mask_(0),\n      nx_enabled_(false),\n      machine_description_(),\n      time_zone_dst_status_(kDoesNotObserveDaylightSavingTime),\n      time_zone_standard_offset_seconds_(0),\n      time_zone_daylight_offset_seconds_(0),\n      time_zone_standard_name_(),\n      time_zone_daylight_name_() {}\n\nTestSystemSnapshot::~TestSystemSnapshot() {\n}\n\nCPUArchitecture TestSystemSnapshot::GetCPUArchitecture() const {\n  return cpu_architecture_;\n}\n\nuint32_t TestSystemSnapshot::CPURevision() const {\n  return cpu_revision_;\n}\n\nuint8_t TestSystemSnapshot::CPUCount() const {\n  return cpu_count_;\n}\n\nstd::string TestSystemSnapshot::CPUVendor() const {\n  return cpu_vendor_;\n}\n\nvoid TestSystemSnapshot::CPUFrequency(uint64_t* current_hz,\n                                      uint64_t* max_hz) const {\n  *current_hz = cpu_frequency_current_hz_;\n  *max_hz = cpu_frequency_max_hz_;\n}\n\nuint32_t TestSystemSnapshot::CPUX86Signature() const {\n  return cpu_x86_signature_;\n}\n\nuint64_t TestSystemSnapshot::CPUX86Features() const {\n  return cpu_x86_features_;\n}\n\nuint64_t TestSystemSnapshot::CPUX86ExtendedFeatures() const {\n  return cpu_x86_extended_features_;\n}\n\nuint32_t TestSystemSnapshot::CPUX86Leaf7Features() const {\n  return cpu_x86_leaf_7_features_;\n}\n\nbool TestSystemSnapshot::CPUX86SupportsDAZ() const {\n  return cpu_x86_supports_daz_;\n}\n\nSystemSnapshot::OperatingSystem TestSystemSnapshot::GetOperatingSystem() const {\n  return operating_system_;\n}\n\nbool TestSystemSnapshot::OSServer() const {\n  return os_server_;\n}\n\nvoid TestSystemSnapshot::OSVersion(\n    int* major, int* minor, int* bugfix, std::string* build) const {\n  *major = os_version_major_;\n  *minor = os_version_minor_;\n  *bugfix = os_version_bugfix_;\n  *build = os_version_build_;\n}\n\nstd::string TestSystemSnapshot::OSVersionFull() const {\n  return os_version_full_;\n}\n\nbool TestSystemSnapshot::NXEnabled() const {\n  return nx_enabled_;\n}\n\nstd::string TestSystemSnapshot::MachineDescription() const {\n  return machine_description_;\n}\n\nvoid TestSystemSnapshot::TimeZone(DaylightSavingTimeStatus* dst_status,\n                                  int* standard_offset_seconds,\n                                  int* daylight_offset_seconds,\n                                  std::string* standard_name,\n                                  std::string* daylight_name) const {\n  *dst_status = time_zone_dst_status_;\n  *standard_offset_seconds = time_zone_standard_offset_seconds_;\n  *daylight_offset_seconds = time_zone_daylight_offset_seconds_;\n  *standard_name = time_zone_standard_name_;\n  *daylight_name = time_zone_daylight_name_;\n}\n\nuint64_t TestSystemSnapshot::AddressMask() const {\n  return address_mask_;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/test/test_system_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"snapshot/system_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A test SystemSnapshot that can carry arbitrary data for testing\n//!     purposes.\nclass TestSystemSnapshot final : public SystemSnapshot {\n public:\n  TestSystemSnapshot();\n\n  TestSystemSnapshot(const TestSystemSnapshot&) = delete;\n  TestSystemSnapshot& operator=(const TestSystemSnapshot&) = delete;\n\n  ~TestSystemSnapshot() override;\n\n  void SetCPUArchitecture(CPUArchitecture cpu_architecture) {\n    cpu_architecture_ = cpu_architecture;\n  }\n  void SetCPURevision(uint32_t cpu_revision) { cpu_revision_ = cpu_revision; }\n  void SetCPUCount(uint8_t cpu_count) { cpu_count_ = cpu_count; }\n  void SetCPUVendor(const std::string& cpu_vendor) { cpu_vendor_ = cpu_vendor; }\n  void SetCPUFrequency(uint64_t current_hz, uint64_t max_hz) {\n    cpu_frequency_current_hz_ = current_hz;\n    cpu_frequency_max_hz_ = max_hz;\n  }\n  void SetCPUX86Signature(uint32_t cpu_x86_signature) {\n    cpu_x86_signature_ = cpu_x86_signature;\n  }\n  void SetCPUX86Features(uint64_t cpu_x86_features) {\n    cpu_x86_features_ = cpu_x86_features;\n  }\n  void SetCPUX86ExtendedFeatures(uint64_t cpu_x86_extended_features) {\n    cpu_x86_extended_features_ = cpu_x86_extended_features;\n  }\n  void SetCPUX86Leaf7Features(uint32_t cpu_x86_leaf_7_features) {\n    cpu_x86_leaf_7_features_ = cpu_x86_leaf_7_features;\n  }\n  void SetCPUX86SupportsDAZ(bool cpu_x86_supports_daz) {\n    cpu_x86_supports_daz_ = cpu_x86_supports_daz;\n  }\n  void SetOperatingSystem(OperatingSystem operating_system) {\n    operating_system_ = operating_system;\n  }\n  void SetOSServer(bool os_server) { os_server_ = os_server; }\n  void SetOSVersion(\n      int major, int minor, int bugfix, const std::string& build) {\n    os_version_major_ = major;\n    os_version_minor_ = minor;\n    os_version_bugfix_ = bugfix;\n    os_version_build_ = build;\n  }\n  void SetOSVersionFull(const std::string& os_version_full) {\n    os_version_full_ = os_version_full;\n  }\n  void SetNXEnabled(bool nx_enabled) { nx_enabled_ = nx_enabled; }\n  void SetMachineDescription(const std::string& machine_description) {\n    machine_description_ = machine_description;\n  }\n  void SetTimeZone(DaylightSavingTimeStatus dst_status,\n                   int standard_offset_seconds,\n                   int daylight_offset_seconds,\n                   const std::string& standard_name,\n                   const std::string& daylight_name) {\n    time_zone_dst_status_ = dst_status;\n    time_zone_standard_offset_seconds_ = standard_offset_seconds;\n    time_zone_daylight_offset_seconds_ = daylight_offset_seconds;\n    time_zone_standard_name_ = standard_name;\n    time_zone_daylight_name_ = daylight_name;\n  }\n\n  void SetAddressMask(uint64_t mask) { address_mask_ = mask; }\n\n  // SystemSnapshot:\n\n  CPUArchitecture GetCPUArchitecture() const override;\n  uint32_t CPURevision() const override;\n  uint8_t CPUCount() const override;\n  std::string CPUVendor() const override;\n  void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;\n  uint32_t CPUX86Signature() const override;\n  uint64_t CPUX86Features() const override;\n  uint64_t CPUX86ExtendedFeatures() const override;\n  uint32_t CPUX86Leaf7Features() const override;\n  bool CPUX86SupportsDAZ() const override;\n  OperatingSystem GetOperatingSystem() const override;\n  bool OSServer() const override;\n  void OSVersion(\n      int* major, int* minor, int* bugfix, std::string* build) const override;\n  std::string OSVersionFull() const override;\n  bool NXEnabled() const override;\n  std::string MachineDescription() const override;\n  void TimeZone(DaylightSavingTimeStatus* dst_status,\n                int* standard_offset_seconds,\n                int* daylight_offset_seconds,\n                std::string* standard_name,\n                std::string* daylight_name) const override;\n  uint64_t AddressMask() const override;\n\n private:\n  CPUArchitecture cpu_architecture_;\n  uint32_t cpu_revision_;\n  uint8_t cpu_count_;\n  std::string cpu_vendor_;\n  uint64_t cpu_frequency_current_hz_;\n  uint64_t cpu_frequency_max_hz_;\n  uint32_t cpu_x86_signature_;\n  uint64_t cpu_x86_features_;\n  uint64_t cpu_x86_extended_features_;\n  uint32_t cpu_x86_leaf_7_features_;\n  bool cpu_x86_supports_daz_;\n  OperatingSystem operating_system_;\n  bool os_server_;\n  int os_version_major_;\n  int os_version_minor_;\n  int os_version_bugfix_;\n  std::string os_version_build_;\n  std::string os_version_full_;\n  uint64_t address_mask_;\n  bool nx_enabled_;\n  std::string machine_description_;\n  DaylightSavingTimeStatus time_zone_dst_status_;\n  int time_zone_standard_offset_seconds_;\n  int time_zone_daylight_offset_seconds_;\n  std::string time_zone_standard_name_;\n  std::string time_zone_daylight_name_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/test/test_thread_snapshot.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/test/test_thread_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\nTestThreadSnapshot::TestThreadSnapshot()\n    : context_union_(),\n      context_(),\n      stack_(),\n      thread_id_(0),\n      suspend_count_(0),\n      priority_(0),\n      thread_specific_data_address_(0) {\n  context_.x86 = &context_union_.x86;\n}\n\nTestThreadSnapshot::~TestThreadSnapshot() {\n}\n\nconst CPUContext* TestThreadSnapshot::Context() const {\n  return &context_;\n}\n\nconst MemorySnapshot* TestThreadSnapshot::Stack() const {\n  return stack_.get();\n}\n\nuint64_t TestThreadSnapshot::ThreadID() const {\n  return thread_id_;\n}\n\nstd::string TestThreadSnapshot::ThreadName() const {\n  return thread_name_;\n}\n\nint TestThreadSnapshot::SuspendCount() const {\n  return suspend_count_;\n}\n\nint TestThreadSnapshot::Priority() const {\n  return priority_;\n}\n\nuint64_t TestThreadSnapshot::ThreadSpecificDataAddress() const {\n  return thread_specific_data_address_;\n}\n\nstd::vector<const MemorySnapshot*> TestThreadSnapshot::ExtraMemory() const {\n  std::vector<const MemorySnapshot*> extra_memory;\n  for (const auto& em : extra_memory_) {\n    extra_memory.push_back(em.get());\n  }\n  return extra_memory;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/test/test_thread_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_\n\n#include <stdint.h>\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A test ThreadSnapshot that can carry arbitrary data for testing\n//!     purposes.\nclass TestThreadSnapshot final : public ThreadSnapshot {\n public:\n  TestThreadSnapshot();\n\n  TestThreadSnapshot(const TestThreadSnapshot&) = delete;\n  TestThreadSnapshot& operator=(const TestThreadSnapshot&) = delete;\n\n  ~TestThreadSnapshot();\n\n  //! \\brief Obtains a pointer to the underlying mutable CPUContext structure.\n  //!\n  //! This method is intended to be used by callers to populate the CPUContext\n  //! structure.\n  //!\n  //! \\return The same pointer that Context() does, while treating the data as\n  //!     mutable.\n  //!\n  //! \\attention This returns a non-`const` pointer to this object’s private\n  //!     data so that a caller can populate the context structure directly.\n  //!     This is done because providing setter interfaces to each field in the\n  //!     context structure would be unwieldy and cumbersome. Care must be taken\n  //!     to populate the context structure correctly.\n  CPUContext* MutableContext() { return &context_; }\n\n  //! \\brief Sets the memory region to be returned by Stack().\n  //!\n  //! \\param[in] stack The memory region that Stack() will return. The\n  //!     TestThreadSnapshot object takes ownership of \\a stack.\n  void SetStack(std::unique_ptr<MemorySnapshot> stack) {\n    stack_ = std::move(stack);\n  }\n\n  void SetThreadID(uint64_t thread_id) { thread_id_ = thread_id; }\n  void SetThreadName(const std::string& thread_name) {\n    thread_name_ = thread_name;\n  }\n  void SetSuspendCount(int suspend_count) { suspend_count_ = suspend_count; }\n  void SetPriority(int priority) { priority_ = priority; }\n  void SetThreadSpecificDataAddress(uint64_t thread_specific_data_address) {\n    thread_specific_data_address_ = thread_specific_data_address;\n  }\n\n  //! \\brief Add a memory snapshot to be returned by ExtraMemory().\n  //!\n  //! \\param[in] extra_memory The memory snapshot that will be included in\n  //!     ExtraMemory(). The TestThreadSnapshot object takes ownership of \\a\n  //!     extra_memory.\n  void AddExtraMemory(std::unique_ptr<MemorySnapshot> extra_memory) {\n    extra_memory_.push_back(std::move(extra_memory));\n  }\n\n  // ThreadSnapshot:\n\n  const CPUContext* Context() const override;\n  const MemorySnapshot* Stack() const override;\n  uint64_t ThreadID() const override;\n  std::string ThreadName() const override;\n  int SuspendCount() const override;\n  int Priority() const override;\n  uint64_t ThreadSpecificDataAddress() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  union {\n    CPUContextX86 x86;\n    CPUContextX86_64 x86_64;\n  } context_union_;\n  CPUContext context_;\n  std::unique_ptr<MemorySnapshot> stack_;\n  uint64_t thread_id_;\n  std::string thread_name_;\n  int suspend_count_;\n  int priority_;\n  uint64_t thread_specific_data_address_;\n  std::vector<std::unique_ptr<MemorySnapshot>> extra_memory_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/thread_snapshot.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <vector>\n\nnamespace crashpad {\n\nstruct CPUContext;\nclass MemorySnapshot;\n\n//! \\brief An abstract interface to a snapshot representing a thread\n//!     (lightweight process) present in a snapshot process.\nclass ThreadSnapshot {\n public:\n  virtual ~ThreadSnapshot() {}\n\n  //! \\brief Returns a CPUContext object corresponding to the thread’s CPU\n  //!     context.\n  //!\n  //! The caller does not take ownership of this object, it is scoped to the\n  //! lifetime of the ThreadSnapshot object that it was obtained from.\n  virtual const CPUContext* Context() const = 0;\n\n  //! \\brief Returns a MemorySnapshot object corresponding to the memory region\n  //!     that contains the thread’s stack, or `nullptr` if no stack region is\n  //!     available.\n  //!\n  //! The caller does not take ownership of this object, it is scoped to the\n  //! lifetime of the ThreadSnapshot object that it was obtained from.\n  virtual const MemorySnapshot* Stack() const = 0;\n\n  //! \\brief Returns the thread’s identifier.\n  //!\n  //! %Thread identifiers are at least unique within a process, and may be\n  //! unique system-wide.\n  virtual uint64_t ThreadID() const = 0;\n\n  //! \\brief Returns the thread's name.\n  virtual std::string ThreadName() const = 0;\n\n  //! \\brief Returns the thread’s suspend count.\n  //!\n  //! A suspend count of `0` denotes a schedulable (not suspended) thread.\n  virtual int SuspendCount() const = 0;\n\n  //! \\brief Returns the thread’s priority.\n  //!\n  //! Threads with higher priorities will have higher priority values.\n  virtual int Priority() const = 0;\n\n  //! \\brief Returns the base address of a region used to store thread-specific\n  //!     data.\n  virtual uint64_t ThreadSpecificDataAddress() const = 0;\n\n  //! \\brief Returns a vector of additional memory blocks that should be\n  //!     included in a minidump.\n  //!\n  //! \\return A vector of MemorySnapshot objects that will be included in the\n  //!     crash dump. The caller does not take ownership of these objects, they\n  //!     are scoped to the lifetime of the ThreadSnapshot object that they\n  //!     were obtained from.\n  virtual std::vector<const MemorySnapshot*> ExtraMemory() const = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/unloaded_module_snapshot.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/unloaded_module_snapshot.h\"\n\nnamespace crashpad {\n\nUnloadedModuleSnapshot::UnloadedModuleSnapshot(uint64_t address,\n                                               uint64_t size,\n                                               uint32_t checksum,\n                                               uint32_t timestamp,\n                                               const std::string& name)\n    : name_(name),\n      address_(address),\n      size_(size),\n      checksum_(checksum),\n      timestamp_(timestamp) {}\n\nUnloadedModuleSnapshot::~UnloadedModuleSnapshot() {\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/unloaded_module_snapshot.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_\n#define CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_\n\n#include <stdint.h>\n\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief Information about an unloaded module that was previously loaded into\n//!     a snapshot process.\nclass UnloadedModuleSnapshot {\n public:\n  UnloadedModuleSnapshot(uint64_t address,\n                  uint64_t size,\n                  uint32_t checksum,\n                  uint32_t timestamp,\n                  const std::string& name);\n  ~UnloadedModuleSnapshot();\n\n  //! \\brief The base address of the module in the target processes' address\n  //!     space.\n  uint64_t Address() const { return address_; }\n\n  //! \\brief The size of the module.\n  uint64_t Size() const { return size_; }\n\n  //! \\brief The checksum of the image.\n  uint32_t Checksum() const { return checksum_; }\n\n  //! \\brief The time and date stamp in `time_t` format.\n  uint32_t Timestamp() const { return timestamp_; }\n\n  //! \\brief The name of the module.\n  std::string Name() const { return name_; }\n\n private:\n  std::string name_;\n  uint64_t address_;\n  uint64_t size_;\n  uint32_t checksum_;\n  uint32_t timestamp_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_\n"
  },
  {
    "path": "snapshot/win/capture_memory_delegate_win.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/capture_memory_delegate_win.h\"\n\n#include <utility>\n\n#include \"base/numerics/safe_conversions.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nCaptureMemoryDelegateWin::CaptureMemoryDelegateWin(\n    ProcessReaderWin* process_reader,\n    const ProcessReaderWin::Thread& thread,\n    std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots,\n    uint32_t* budget_remaining)\n    : stack_(thread.stack_region_address, thread.stack_region_size),\n      process_reader_(process_reader),\n      snapshots_(snapshots),\n      budget_remaining_(budget_remaining) {}\n\nbool CaptureMemoryDelegateWin::Is64Bit() const {\n  return process_reader_->Is64Bit();\n}\n\nbool CaptureMemoryDelegateWin::ReadMemory(uint64_t at,\n                                          uint64_t num_bytes,\n                                          void* into) const {\n  return process_reader_->Memory()->Read(\n      at, base::checked_cast<size_t>(num_bytes), into);\n}\n\nstd::vector<CheckedRange<uint64_t>> CaptureMemoryDelegateWin::GetReadableRanges(\n    const CheckedRange<uint64_t, uint64_t>& range) const {\n  return process_reader_->GetProcessInfo().GetReadableRanges(range);\n}\n\nvoid CaptureMemoryDelegateWin::AddNewMemorySnapshot(\n    const CheckedRange<uint64_t, uint64_t>& range) {\n  // Don't bother storing this memory if it points back into the stack.\n  if (stack_.ContainsRange(range))\n    return;\n  if (range.size() == 0)\n    return;\n  if (!budget_remaining_ || *budget_remaining_ == 0)\n    return;\n  snapshots_->push_back(std::make_unique<internal::MemorySnapshotGeneric>());\n  internal::MemorySnapshotGeneric* snapshot = snapshots_->back().get();\n  snapshot->Initialize(process_reader_->Memory(), range.base(), range.size());\n  if (!base::IsValueInRangeForNumericType<int64_t>(range.size())) {\n    *budget_remaining_ = 0;\n  } else {\n    int64_t temp = *budget_remaining_;\n    temp -= range.size();\n    *budget_remaining_ = base::saturated_cast<uint32_t>(temp);\n  }\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/capture_memory_delegate_win.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_\n\n#include \"snapshot/capture_memory.h\"\n\n#include <stdint.h>\n\n#include <memory>\n#include <vector>\n\n#include \"snapshot/win/process_reader_win.h\"\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nclass MemorySnapshotGeneric;\n\nclass CaptureMemoryDelegateWin : public CaptureMemory::Delegate {\n public:\n  //! \\brief A MemoryCaptureDelegate for Windows.\n  //!\n  //! \\param[in] process_reader A ProcessReaderWin for the target process.\n  //! \\param[in] thread The thread being inspected. Memory ranges overlapping\n  //!     this thread's stack will be ignored on the assumption that they're\n  //!     already captured elsewhere.\n  //! \\param[in] snapshots A vector of MemorySnapshotGeneric to which the\n  //!     captured memory will be added.\n  //! \\param[in] budget_remaining If non-null, a pointer to the remaining number\n  //!     of bytes to capture. If this is `0`, no further memory will be\n  //!     captured.\n  CaptureMemoryDelegateWin(\n      ProcessReaderWin* process_reader,\n      const ProcessReaderWin::Thread& thread,\n      std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots,\n      uint32_t* budget_remaining);\n\n  // MemoryCaptureDelegate:\n  bool Is64Bit() const override;\n  bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override;\n  std::vector<CheckedRange<uint64_t>> GetReadableRanges(\n      const CheckedRange<uint64_t, uint64_t>& range) const override;\n  void AddNewMemorySnapshot(\n      const CheckedRange<uint64_t, uint64_t>& range) override;\n\n private:\n  CheckedRange<uint64_t, uint64_t> stack_;\n  ProcessReaderWin* process_reader_;  // weak\n  std::vector<std::unique_ptr<MemorySnapshotGeneric>>* snapshots_;  // weak\n  uint32_t* budget_remaining_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_\n"
  },
  {
    "path": "snapshot/win/cpu_context_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/cpu_context_win.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n\n#include \"base/logging.h\"\n#include \"snapshot/cpu_context.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Validation for casts used with CPUContextX86::FsaveToFxsave().\nstatic_assert(sizeof(CPUContextX86::Fsave) ==\n                  offsetof(WOW64_FLOATING_SAVE_AREA, Cr0NpxState),\n              \"WoW64 fsave types must be equivalent\");\n#if defined(ARCH_CPU_X86)\nstatic_assert(sizeof(CPUContextX86::Fsave) ==\n                  offsetof(FLOATING_SAVE_AREA, Spare0),\n              \"fsave types must be equivalent\");\n#endif  // ARCH_CPU_X86\n\ntemplate <typename T>\nbool HasContextPart(const T* context, uint32_t bits) {\n  return (context->ContextFlags & bits) == bits;\n}\n\ntemplate <class T>\nvoid CommonInitializeX86Context(const T* context, CPUContextX86* out) {\n  // This function assumes that the WOW64_CONTEXT_* and x86 CONTEXT_* values\n  // for ContextFlags are identical. This can be tested when targeting 32-bit\n  // x86.\n#if defined(ARCH_CPU_X86)\n  static_assert(sizeof(CONTEXT) == sizeof(WOW64_CONTEXT),\n                \"type mismatch: CONTEXT\");\n#define ASSERT_WOW64_EQUIVALENT(x)                        \\\n  do {                                                    \\\n    static_assert(x == WOW64_##x, \"value mismatch: \" #x); \\\n  } while (false)\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_i386);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_i486);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_CONTROL);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_INTEGER);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_SEGMENTS);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_FLOATING_POINT);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_DEBUG_REGISTERS);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_EXTENDED_REGISTERS);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_FULL);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_ALL);\n  ASSERT_WOW64_EQUIVALENT(CONTEXT_XSTATE);\n#undef ASSERT_WOW64_EQUIVALENT\n#endif  // ARCH_CPU_X86\n\n  memset(out, 0, sizeof(*out));\n\n  LOG_IF(ERROR, !HasContextPart(context, WOW64_CONTEXT_i386))\n      << \"non-x86 context\";\n\n  if (HasContextPart(context, WOW64_CONTEXT_CONTROL)) {\n    out->ebp = context->Ebp;\n    out->eip = context->Eip;\n    out->cs = static_cast<uint16_t>(context->SegCs);\n    out->eflags = context->EFlags;\n    out->esp = context->Esp;\n    out->ss = static_cast<uint16_t>(context->SegSs);\n  }\n\n  if (HasContextPart(context, WOW64_CONTEXT_INTEGER)) {\n    out->eax = context->Eax;\n    out->ebx = context->Ebx;\n    out->ecx = context->Ecx;\n    out->edx = context->Edx;\n    out->edi = context->Edi;\n    out->esi = context->Esi;\n  }\n\n  if (HasContextPart(context, WOW64_CONTEXT_SEGMENTS)) {\n    out->ds = static_cast<uint16_t>(context->SegDs);\n    out->es = static_cast<uint16_t>(context->SegEs);\n    out->fs = static_cast<uint16_t>(context->SegFs);\n    out->gs = static_cast<uint16_t>(context->SegGs);\n  }\n\n  if (HasContextPart(context, WOW64_CONTEXT_DEBUG_REGISTERS)) {\n    out->dr0 = context->Dr0;\n    out->dr1 = context->Dr1;\n    out->dr2 = context->Dr2;\n    out->dr3 = context->Dr3;\n\n    // DR4 and DR5 are obsolete synonyms for DR6 and DR7, see\n    // https://en.wikipedia.org/wiki/X86_debug_register.\n    out->dr4 = context->Dr6;\n    out->dr5 = context->Dr7;\n\n    out->dr6 = context->Dr6;\n    out->dr7 = context->Dr7;\n  }\n\n  if (HasContextPart(context, WOW64_CONTEXT_EXTENDED_REGISTERS)) {\n    static_assert(sizeof(out->fxsave) == sizeof(context->ExtendedRegisters),\n                  \"fxsave types must be equivalent\");\n    memcpy(&out->fxsave, &context->ExtendedRegisters, sizeof(out->fxsave));\n  } else if (HasContextPart(context, WOW64_CONTEXT_FLOATING_POINT)) {\n    // The static_assert that validates this cast can’t be here because it\n    // relies on field names that vary based on the template parameter.\n    CPUContextX86::FsaveToFxsave(\n        *reinterpret_cast<const CPUContextX86::Fsave*>(&context->FloatSave),\n        &out->fxsave);\n  }\n}\n\n#if defined(ARCH_CPU_X86_64)\nDWORD64 CallGetEnabledXStateFeatures() {\n  // GetEnabledXStateFeatures needs Windows 7 SP1.\n  HINSTANCE kernel32 = GetModuleHandle(L\"Kernel32.dll\");\n  decltype(GetEnabledXStateFeatures)* get_enabled_xstate_features =\n      reinterpret_cast<decltype(GetEnabledXStateFeatures)*>(\n          GetProcAddress(kernel32, \"GetEnabledXStateFeatures\"));\n  if (!get_enabled_xstate_features)\n    return 0;\n  return get_enabled_xstate_features();\n}\n#endif  // defined(ARCH_CPU_X64)\n\n}  // namespace\n\n#if defined(ARCH_CPU_X86)\n\nvoid InitializeX86Context(const CONTEXT* context, CPUContextX86* out) {\n  CommonInitializeX86Context(context, out);\n}\n\n#elif defined(ARCH_CPU_X86_64)\n\nvoid InitializeX86Context(const WOW64_CONTEXT* context, CPUContextX86* out) {\n  CommonInitializeX86Context(context, out);\n}\n\nvoid InitializeX64Context(const CONTEXT* context, CPUContextX86_64* out) {\n  memset(out, 0, sizeof(*out));\n\n  LOG_IF(ERROR, !HasContextPart(context, CONTEXT_AMD64)) << \"non-x64 context\";\n\n  if (HasContextPart(context, CONTEXT_CONTROL)) {\n    out->cs = context->SegCs;\n    out->rflags = context->EFlags;\n    out->rip = context->Rip;\n    out->rsp = context->Rsp;\n    // SegSs ignored.\n  }\n\n  if (HasContextPart(context, CONTEXT_INTEGER)) {\n    out->rax = context->Rax;\n    out->rbx = context->Rbx;\n    out->rcx = context->Rcx;\n    out->rdx = context->Rdx;\n    out->rdi = context->Rdi;\n    out->rsi = context->Rsi;\n    out->rbp = context->Rbp;\n    out->r8 = context->R8;\n    out->r9 = context->R9;\n    out->r10 = context->R10;\n    out->r11 = context->R11;\n    out->r12 = context->R12;\n    out->r13 = context->R13;\n    out->r14 = context->R14;\n    out->r15 = context->R15;\n  }\n\n  if (HasContextPart(context, CONTEXT_SEGMENTS)) {\n    out->fs = context->SegFs;\n    out->gs = context->SegGs;\n    // SegDs ignored.\n    // SegEs ignored.\n  }\n\n  if (HasContextPart(context, CONTEXT_DEBUG_REGISTERS)) {\n    out->dr0 = context->Dr0;\n    out->dr1 = context->Dr1;\n    out->dr2 = context->Dr2;\n    out->dr3 = context->Dr3;\n\n    // DR4 and DR5 are obsolete synonyms for DR6 and DR7, see\n    // https://en.wikipedia.org/wiki/X86_debug_register.\n    out->dr4 = context->Dr6;\n    out->dr5 = context->Dr7;\n\n    out->dr6 = context->Dr6;\n    out->dr7 = context->Dr7;\n  }\n\n  if (HasContextPart(context, CONTEXT_FLOATING_POINT)) {\n    static_assert(sizeof(out->fxsave) == sizeof(context->FltSave),\n                  \"types must be equivalent\");\n    memcpy(&out->fxsave, &context->FltSave, sizeof(out->fxsave));\n  }\n}\n\nvoid InitializeX64XStateCet(const CONTEXT* context,\n                            XSAVE_CET_U_FORMAT* cet_u,\n                            CPUContextX86_64* out) {\n  if (HasContextPart(context, CONTEXT_XSTATE)) {\n    if (cet_u) {\n      out->xstate.enabled_features |= XSTATE_MASK_CET_U;\n      out->xstate.cet_u.cetmsr = cet_u->Ia32CetUMsr;\n      out->xstate.cet_u.ssp = cet_u->Ia32Pl3SspMsr;\n    }\n  }\n}\n\nbool IsXStateFeatureEnabled(DWORD64 features) {\n  static DWORD64 flags = CallGetEnabledXStateFeatures();\n  return (flags & features) == features;\n}\n\n#elif defined(ARCH_CPU_ARM64)\n\nvoid InitializeARM64Context(const CONTEXT* context, CPUContextARM64* out) {\n  memset(out, 0, sizeof(*out));\n\n  LOG_IF(ERROR, !HasContextPart(context, CONTEXT_ARM64)) << \"non-arm64 context\";\n\n  if (HasContextPart(context, CONTEXT_CONTROL)) {\n    out->spsr = context->Cpsr;\n    out->pc = context->Pc;\n    out->regs[30] = context->Lr;\n    out->sp = context->Sp;\n    out->regs[29] = context->Fp;\n  }\n\n  if (HasContextPart(context, CONTEXT_INTEGER)) {\n    memcpy(&out->regs[0], &context->X0, 18 * sizeof(context->X0));\n    // Don't copy x18 which is reserved as platform register.\n    memcpy(&out->regs[19], &context->X19, 10 * sizeof(context->X0));\n  }\n\n  if (HasContextPart(context, CONTEXT_FLOATING_POINT)) {\n    static_assert(sizeof(out->fpsimd) == sizeof(context->V),\n                  \"types must be equivalent\");\n    memcpy(&out->fpsimd, &context->V, sizeof(out->fpsimd));\n  }\n}\n\n#else\n#error Unsupported Windows Arch\n#endif  // ARCH_CPU_X86\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/cpu_context_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_\n\n#include <windows.h>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\nstruct CPUContextX86;\nstruct CPUContextX86_64;\nstruct CPUContextARM64;\n\n#if defined(ARCH_CPU_X86) || DOXYGEN\n\n//! \\brief Initializes a CPUContextX86 structure from a native context structure\n//!     on Windows.\nvoid InitializeX86Context(const CONTEXT* context, CPUContextX86* out);\n\n#endif  // ARCH_CPU_X86\n\n#if defined(ARCH_CPU_X86_64) || DOXYGEN\n\n//! \\brief Initializes a CPUContextX86 structure from a native context structure\n//!     on Windows.\nvoid InitializeX86Context(const WOW64_CONTEXT* context, CPUContextX86* out);\n\n//! \\brief Initializes a CPUContextX86_64 structure from a native context\n//!     structure on Windows.\n//! Only reads a max of sizeof(CONTEXT) so will not initialize extended values.\nvoid InitializeX64Context(const CONTEXT* context, CPUContextX86_64* out);\n\n//! \\brief Initializes CET fields of a CPUContextX86_64 structure from\n//!     an xsave location if |context| flags support cet_u values.\nvoid InitializeX64XStateCet(const CONTEXT* context,\n                            XSAVE_CET_U_FORMAT* cet_u,\n                            CPUContextX86_64* out);\n\n//! \\brief Wraps GetXStateEnabledFeatures(), returns true if the specified set\n//!     of flags are all supported.\nbool IsXStateFeatureEnabled(DWORD64 feature);\n\n#endif  // ARCH_CPU_X86_64\n\n#if defined(ARCH_CPU_ARM64) || DOXYGEN\n\n//! \\brief Initializes a CPUContextARM64 structure from a native context\n//!     structure on Windows.\nvoid InitializeARM64Context(const CONTEXT* context, CPUContextARM64* out);\n\n#endif  // ARCH_CPU_ARM64\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_\n"
  },
  {
    "path": "snapshot/win/cpu_context_win_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/cpu_context_win.h\"\n\n#include <windows.h>\n\n#include <iterator>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"test/hex_string.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\ntemplate <typename T>\nvoid TestInitializeX86Context() {\n  T context = {0};\n  context.ContextFlags = WOW64_CONTEXT_INTEGER |\n                         WOW64_CONTEXT_DEBUG_REGISTERS |\n                         WOW64_CONTEXT_EXTENDED_REGISTERS;\n  context.Eax = 1;\n  context.Dr0 = 3;\n  context.ExtendedRegisters[4] = 2;  // FTW\n\n  // Test the simple case, where everything in the CPUContextX86 argument is set\n  // directly from the supplied thread, float, and debug state parameters.\n  {\n    CPUContextX86 cpu_context_x86 = {};\n    InitializeX86Context(&context, &cpu_context_x86);\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u);\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n}\n\ntemplate <typename T>\nvoid TestInitializeX86Context_FsaveWithoutFxsave() {\n  T context = {0};\n  context.ContextFlags = WOW64_CONTEXT_INTEGER |\n                         WOW64_CONTEXT_FLOATING_POINT |\n                         WOW64_CONTEXT_DEBUG_REGISTERS;\n  context.Eax = 1;\n\n  // In fields that are wider than they need to be, set the high bits to ensure\n  // that they’re masked off appropriately in the output.\n  context.FloatSave.ControlWord = 0xffff027f;\n  context.FloatSave.StatusWord = 0xffff0004;\n  context.FloatSave.TagWord = 0xffffa9ff;\n  context.FloatSave.ErrorOffset = 0x01234567;\n  context.FloatSave.ErrorSelector = 0x0bad0003;\n  context.FloatSave.DataOffset = 0x89abcdef;\n  context.FloatSave.DataSelector = 0xffff0007;\n  context.FloatSave.RegisterArea[77] = 0x80;\n  context.FloatSave.RegisterArea[78] = 0xff;\n  context.FloatSave.RegisterArea[79] = 0x7f;\n\n  context.Dr0 = 3;\n\n  {\n    CPUContextX86 cpu_context_x86 = {};\n    InitializeX86Context(&context, &cpu_context_x86);\n\n    EXPECT_EQ(cpu_context_x86.eax, 1u);\n\n    EXPECT_EQ(cpu_context_x86.fxsave.fcw, 0x027f);\n    EXPECT_EQ(cpu_context_x86.fxsave.fsw, 0x0004);\n    EXPECT_EQ(cpu_context_x86.fxsave.ftw, 0x00f0);\n    EXPECT_EQ(cpu_context_x86.fxsave.fop, 0x0bad);\n    EXPECT_EQ(cpu_context_x86.fxsave.fpu_ip, 0x01234567u);\n    EXPECT_EQ(cpu_context_x86.fxsave.fpu_cs, 0x0003);\n    EXPECT_EQ(cpu_context_x86.fxsave.fpu_dp, 0x89abcdefu);\n    EXPECT_EQ(cpu_context_x86.fxsave.fpu_ds, 0x0007);\n    for (size_t st_mm = 0; st_mm < 7; ++st_mm) {\n      EXPECT_EQ(\n          BytesToHexString(cpu_context_x86.fxsave.st_mm[st_mm].st,\n                           std::size(cpu_context_x86.fxsave.st_mm[st_mm].st)),\n          std::string(std::size(cpu_context_x86.fxsave.st_mm[st_mm].st) * 2,\n                      '0'))\n          << \"st_mm \" << st_mm;\n    }\n    EXPECT_EQ(BytesToHexString(cpu_context_x86.fxsave.st_mm[7].st,\n                               std::size(cpu_context_x86.fxsave.st_mm[7].st)),\n              \"0000000000000080ff7f\");\n\n    EXPECT_EQ(cpu_context_x86.dr0, 3u);\n  }\n}\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\n#if defined(ARCH_CPU_X86_64)\n\nTEST(CPUContextWin, InitializeX64Context) {\n  CONTEXT context = {0};\n  context.Rax = 10;\n  context.FltSave.TagWord = 11;\n  context.Dr0 = 12;\n  context.ContextFlags =\n      CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS;\n\n  // Test the simple case, where everything in the CPUContextX86_64 argument is\n  // set directly from the supplied thread, float, and debug state parameters.\n  {\n    CPUContextX86_64 cpu_context_x86_64 = {};\n    InitializeX64Context(&context, &cpu_context_x86_64);\n    EXPECT_EQ(cpu_context_x86_64.rax, 10u);\n    EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u);\n    EXPECT_EQ(cpu_context_x86_64.dr0, 12u);\n  }\n}\n\n#endif  // ARCH_CPU_X86_64\n\nTEST(CPUContextWin, InitializeX86Context) {\n#if defined(ARCH_CPU_X86)\n  TestInitializeX86Context<CONTEXT>();\n#else  // ARCH_CPU_X86\n  TestInitializeX86Context<WOW64_CONTEXT>();\n#endif  // ARCH_CPU_X86\n}\n\nTEST(CPUContextWin, InitializeX86Context_FsaveWithoutFxsave) {\n#if defined(ARCH_CPU_X86)\n  TestInitializeX86Context_FsaveWithoutFxsave<CONTEXT>();\n#else  // ARCH_CPU_X86\n  TestInitializeX86Context_FsaveWithoutFxsave<WOW64_CONTEXT>();\n#endif  // ARCH_CPU_X86\n}\n\n#endif  // ARCH_CPU_X86_FAMILY\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/crashpad_snapshot_test_annotations.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <windows.h>\n\n#include \"base/check.h\"\n#include \"client/annotation.h\"\n#include \"client/annotation_list.h\"\n#include \"client/crashpad_info.h\"\n#include \"util/file/file_io.h\"\n\nint wmain(int argc, wchar_t* argv[]) {\n  crashpad::CrashpadInfo* crashpad_info =\n      crashpad::CrashpadInfo::GetCrashpadInfo();\n\n  // This is \"leaked\" to crashpad_info.\n  crashpad::SimpleStringDictionary* simple_annotations =\n      new crashpad::SimpleStringDictionary();\n  simple_annotations->SetKeyValue(\"#TEST# pad\", \"break\");\n  simple_annotations->SetKeyValue(\"#TEST# key\", \"value\");\n  simple_annotations->SetKeyValue(\"#TEST# pad\", \"crash\");\n  simple_annotations->SetKeyValue(\"#TEST# x\", \"y\");\n  simple_annotations->SetKeyValue(\"#TEST# longer\", \"shorter\");\n  simple_annotations->SetKeyValue(\"#TEST# empty_value\", \"\");\n\n  crashpad_info->set_simple_annotations(simple_annotations);\n\n  // Set the annotation objects.\n  crashpad::AnnotationList::Register();\n\n  static crashpad::StringAnnotation<32> annotation_one(\"#TEST# one\");\n  static crashpad::StringAnnotation<32> annotation_two(\"#TEST# two\");\n  static crashpad::StringAnnotation<32> annotation_three(\"#TEST# same-name\");\n  static crashpad::StringAnnotation<32> annotation_four(\"#TEST# same-name\");\n\n  annotation_one.Set(\"moocow\");\n  annotation_two.Set(\"this will be cleared\");\n  annotation_three.Set(\"same-name 3\");\n  annotation_four.Set(\"same-name 4\");\n  annotation_two.Clear();\n\n  // Tell the parent that the environment has been set up.\n  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);\n  PCHECK(out != INVALID_HANDLE_VALUE) << \"GetStdHandle\";\n  char c = ' ';\n  crashpad::CheckedWriteFile(out, &c, sizeof(c));\n\n  HANDLE in = GetStdHandle(STD_INPUT_HANDLE);\n  PCHECK(in != INVALID_HANDLE_VALUE) << \"GetStdHandle\";\n  crashpad::CheckedReadFileExactly(in, &c, sizeof(c));\n  CHECK(c == 'd' || c == ' ');\n\n  // If 'd' we crash with a debug break, otherwise exit normally.\n  if (c == 'd')\n    __debugbreak();\n\n  return 0;\n}\n"
  },
  {
    "path": "snapshot/win/crashpad_snapshot_test_crashing_child.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <intrin.h>\n#include <windows.h>\n\n#include \"base/check_op.h\"\n#include \"client/crashpad_client.h\"\n#include \"util/misc/capture_context.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/context_wrappers.h\"\n\nint wmain(int argc, wchar_t* argv[]) {\n  CHECK_EQ(argc, 2);\n\n  crashpad::CrashpadClient client;\n  CHECK(client.SetHandlerIPCPipe(argv[1]));\n\n  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);\n  PCHECK(out != INVALID_HANDLE_VALUE) << \"GetStdHandle\";\n\n  CONTEXT context;\n  crashpad::CaptureContext(&context);\n  crashpad::WinVMAddress break_address =\n      reinterpret_cast<crashpad::WinVMAddress>(\n          crashpad::ProgramCounterFromCONTEXT(&context));\n\n  // This does not used CheckedWriteFile() because at high optimization\n  // settings, a lot of logging code can be inlined, causing there to be a large\n  // number of instructions between where the IP is captured and the actual\n  // __debugbreak(). Instead call Windows' WriteFile() to minimize the amount of\n  // code here. Because the next line is going to crash in any case, there's\n  // minimal difference in behavior aside from an indication of what broke when\n  // the other end experiences a ReadFile() error.\n  DWORD bytes_written;\n  WriteFile(\n      out, &break_address, sizeof(break_address), &bytes_written, nullptr);\n\n  __debugbreak();\n\n  return 0;\n}\n"
  },
  {
    "path": "snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <intrin.h>\n#include <windows.h>\n\n#include \"base/check_op.h\"\n#include \"client/crashpad_client.h\"\n#include \"client/simulate_crash.h\"\n#include \"util/misc/capture_context.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/context_wrappers.h\"\n\nint wmain(int argc, wchar_t* argv[]) {\n  CHECK_EQ(argc, 2);\n\n  crashpad::CrashpadClient client;\n  CHECK(client.SetHandlerIPCPipe(argv[1]));\n\n  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);\n  PCHECK(out != INVALID_HANDLE_VALUE) << \"GetStdHandle\";\n\n  CONTEXT context;\n  crashpad::CaptureContext(&context);\n  crashpad::WinVMAddress break_address =\n      reinterpret_cast<crashpad::WinVMAddress>(\n          crashpad::ProgramCounterFromCONTEXT(&context));\n\n  // This does not used CheckedWriteFile() because at high optimization\n  // settings, a lot of logging code can be inlined, causing there to be a large\n  // number of instructions between where the IP is captured and the actual\n  // __debugbreak(). Instead call Windows' WriteFile() to minimize the amount of\n  // code here. Because the next line is going to crash in any case, there's\n  // minimal difference in behavior aside from an indication of what broke when\n  // the other end experiences a ReadFile() error.\n  DWORD bytes_written;\n  WriteFile(\n      out, &break_address, sizeof(break_address), &bytes_written, nullptr);\n\n  CRASHPAD_SIMULATE_CRASH();\n\n  return 0;\n}\n"
  },
  {
    "path": "snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <windows.h>\n\n#include \"base/check.h\"\n#include \"client/crashpad_info.h\"\n#include \"util/file/file_io.h\"\n\n\nint wmain(int argc, wchar_t* argv[]) {\n  using namespace crashpad;\n\n  CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();\n\n  // This is \"leaked\" to crashpad_info.\n  SimpleAddressRangeBag* extra_ranges = new SimpleAddressRangeBag();\n  extra_ranges->Insert(CheckedRange<uint64_t>(0, 1));\n  extra_ranges->Insert(CheckedRange<uint64_t>(1, 0));\n  extra_ranges->Insert(CheckedRange<uint64_t>(0x1000000000ULL, 0x1000));\n  extra_ranges->Insert(CheckedRange<uint64_t>(0x2000, 0x2000000000ULL));\n  extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));\n  extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));\n  extra_ranges->Insert(CheckedRange<uint64_t>(1234, 5678));\n  crashpad_info->set_extra_memory_ranges(extra_ranges);\n\n  // Tell the parent that the environment has been set up.\n  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);\n  PCHECK(out != INVALID_HANDLE_VALUE) << \"GetStdHandle\";\n  char c = ' ';\n  CheckedWriteFile(out, &c, sizeof(c));\n\n  HANDLE in = GetStdHandle(STD_INPUT_HANDLE);\n  PCHECK(in != INVALID_HANDLE_VALUE) << \"GetStdHandle\";\n  CheckedReadFileExactly(in, &c, sizeof(c));\n  CHECK(c == 'd' || c == ' ');\n\n  // If 'd' we crash with a debug break, otherwise exit normally.\n  if (c == 'd')\n    __debugbreak();\n\n  return 0;\n}\n"
  },
  {
    "path": "snapshot/win/crashpad_snapshot_test_image_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <windows.h>\n\n#include <iterator>\n\n#include \"base/logging.h\"\n#include \"client/crashpad_info.h\"\n#include \"util/file/file_io.h\"\n#include \"util/synchronization/semaphore.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace {\n\nDWORD WINAPI LotsOfReferencesThreadProc(void* param) {\n  crashpad::Semaphore* semaphore =\n      reinterpret_cast<crashpad::Semaphore*>(param);\n\n  // Allocate a bunch of pointers to things on the stack.\n  int* pointers[1000];\n  for (size_t i = 0; i < std::size(pointers); ++i) {\n    pointers[i] = new int[2048];\n  }\n\n  semaphore->Signal();\n  Sleep(INFINITE);\n  return 0;\n}\n\n}  // namespace\n\nint wmain(int argc, wchar_t* argv[]) {\n  CHECK_EQ(argc, 2);\n\n  crashpad::ScopedKernelHANDLE done(CreateEvent(nullptr, true, false, argv[1]));\n  PCHECK(done.is_valid()) << \"CreateEvent\";\n\n  PCHECK(LoadLibrary(L\"crashpad_snapshot_test_image_reader_module.dll\"))\n      << \"LoadLibrary\";\n\n  // Create threads with lots of stack pointers to memory. This is used to\n  // verify the cap on pointed-to memory.\n  crashpad::Semaphore semaphore(0);\n  crashpad::ScopedKernelHANDLE threads[100];\n  for (size_t i = 0; i < std::size(threads); ++i) {\n    threads[i].reset(CreateThread(nullptr,\n                                  0,\n                                  &LotsOfReferencesThreadProc,\n                                  reinterpret_cast<void*>(&semaphore),\n                                  0,\n                                  nullptr));\n    if (!threads[i].is_valid()) {\n      PLOG(ERROR) << \"CreateThread\";\n      return 1;\n    }\n  }\n\n  for (size_t i = 0; i < std::size(threads); ++i) {\n    semaphore.Wait();\n  }\n\n  crashpad::CrashpadInfo* crashpad_info =\n      crashpad::CrashpadInfo::GetCrashpadInfo();\n  crashpad_info->set_gather_indirectly_referenced_memory(\n      crashpad::TriState::kEnabled, 100000);\n\n  // Tell the parent process we're ready to proceed.\n  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);\n  PCHECK(out != INVALID_HANDLE_VALUE) << \"GetStdHandle\";\n  char c = ' ';\n  crashpad::CheckedWriteFile(out, &c, sizeof(c));\n\n  // Parent process says we can exit.\n  PCHECK(WaitForSingleObject(done.get(), INFINITE) == WAIT_OBJECT_0)\n      << \"WaitForSingleObject\";\n\n  return 0;\n}\n"
  },
  {
    "path": "snapshot/win/crashpad_snapshot_test_image_reader_module.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <windows.h>\n\nBOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) {\n  return TRUE;\n}\n"
  },
  {
    "path": "snapshot/win/end_to_end_test.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport platform\nimport pywintypes\nimport random\nimport re\nimport struct\nimport subprocess\nimport sys\nimport tempfile\nimport time\nimport win32con\nimport win32pipe\nimport winerror\n\ng_temp_dirs = []\ng_had_failures = False\n\n\ndef MakeTempDir():\n    global g_temp_dirs\n    new_dir = tempfile.mkdtemp()\n    g_temp_dirs.append(new_dir)\n    return new_dir\n\n\ndef CleanUpTempDirs():\n    global g_temp_dirs\n    for d in g_temp_dirs:\n        subprocess.call(['rmdir', '/s', '/q', d], shell=True)\n\n\ndef FindInstalledWindowsApplication(app_path):\n    search_paths = [\n        os.getenv('PROGRAMFILES(X86)'),\n        os.getenv('PROGRAMFILES'),\n        os.getenv('PROGRAMW6432'),\n        os.getenv('LOCALAPPDATA')\n    ]\n    search_paths += os.getenv('PATH', '').split(os.pathsep)\n\n    for search_path in search_paths:\n        if not search_path:\n            continue\n        path = os.path.join(search_path, app_path)\n        if os.path.isfile(path):\n            return path\n\n    return None\n\n\ndef GetCdbPath():\n    \"\"\"Search in some reasonable places to find cdb.exe. Searches x64 before x86\n    and newer versions before older versions.\n    \"\"\"\n    possible_paths = (\n        os.path.join('Windows Kits', '10', 'Debuggers', 'x64'),\n        os.path.join('Windows Kits', '10', 'Debuggers', 'x86'),\n        os.path.join('Windows Kits', '8.1', 'Debuggers', 'x64'),\n        os.path.join('Windows Kits', '8.1', 'Debuggers', 'x86'),\n        os.path.join('Windows Kits', '8.0', 'Debuggers', 'x64'),\n        os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'),\n        'Debugging Tools For Windows (x64)',\n        'Debugging Tools For Windows (x86)',\n        'Debugging Tools For Windows',\n    )\n    for possible_path in possible_paths:\n        app_path = os.path.join(possible_path, 'cdb.exe')\n        app_path = FindInstalledWindowsApplication(app_path)\n        if app_path:\n            return app_path\n    return None\n\n\ndef Win32_20H1():\n    (major, _, build) = platform.win32_ver()[1].split('.')\n    if int(major) < 10:\n        return False\n    if int(build) >= 19041:\n        return True\n    return False\n\n\ndef NamedPipeExistsAndReady(pipe_name):\n    \"\"\"Returns False if pipe_name does not exist. If pipe_name does exist,\n    blocks until the pipe is ready to service clients, and then returns True.\n\n    This is used as a drop-in replacement for os.path.exists() and os.access()\n    to test for the pipe's existence. Both of those calls tickle the pipe in a\n    way that appears to the server to be a client connecting, triggering error\n    messages when no data is received.\n\n    Although this function only needs to test pipe existence (waiting for\n    CreateNamedPipe()), it actually winds up testing pipe readiness (waiting for\n    ConnectNamedPipe()). This is unnecessary but harmless.\n    \"\"\"\n    try:\n        win32pipe.WaitNamedPipe(pipe_name, win32pipe.NMPWAIT_WAIT_FOREVER)\n    except pywintypes.error as e:\n        if e.winerror == winerror.ERROR_FILE_NOT_FOUND:\n            return False\n        raise\n    return True\n\n\ndef GetDumpFromProgram(out_dir, pipe_name, executable_name, expect_exit_code,\n                       *args):\n    \"\"\"Initialize a crash database, and run |executable_name| connecting to a\n    crash handler. If pipe_name is set, crashpad_handler will be started first.\n    If pipe_name is empty, the executable is responsible for starting\n    crashpad_handler. *args will be passed after other arguments to\n    executable_name. If the child process does not exit with |expect_exit_code|,\n    an exception will be raised. Returns the path to the minidump generated by\n    crashpad_handler for further testing.\n    \"\"\"\n    test_database = MakeTempDir()\n    handler = None\n\n    try:\n        subprocess.check_call([\n            os.path.join(out_dir, 'crashpad_database_util.exe'), '--create',\n            '--database=' + test_database\n        ])\n\n        if pipe_name is not None:\n            handler = subprocess.Popen([\n                os.path.join(out_dir, 'crashpad_handler.com'),\n                '--pipe-name=' + pipe_name, '--database=' + test_database\n            ])\n\n            # Wait until the server is ready.\n            printed = False\n            while not NamedPipeExistsAndReady(pipe_name):\n                if not printed:\n                    print('Waiting for crashpad_handler to be ready...')\n                    printed = True\n                time.sleep(0.001)\n\n            command = [os.path.join(out_dir, executable_name), pipe_name\n                      ] + list(args)\n        else:\n            command = ([\n                os.path.join(out_dir, executable_name),\n                os.path.join(out_dir, 'crashpad_handler.com'), test_database\n            ] + list(args))\n        print('Running %s' % os.path.basename(command[0]))\n        exit_code = subprocess.call(command)\n\n        # Some win32con codes are negative signed integers, whereas all exit\n        # codes are unsigned integers. Convert from signed to unsigned.\n        if expect_exit_code < 0:\n            expect_exit_code = struct.unpack('I',\n                                             struct.pack('i',\n                                                         expect_exit_code))[0]\n\n        if exit_code != expect_exit_code:\n            raise subprocess.CalledProcessError(exit_code, executable_name)\n\n        out = subprocess.check_output([\n            os.path.join(out_dir, 'crashpad_database_util.exe'),\n            '--database=' + test_database,\n            '--show-pending-reports',\n            '--show-all-report-info',\n        ],\n                                      text=True)\n        for line in out.splitlines():\n            if line.strip().startswith('Path:'):\n                return line.partition(':')[2].strip()\n    finally:\n        if handler:\n            handler.kill()\n\n\ndef GetDumpFromCrashyProgram(out_dir, pipe_name):\n    return GetDumpFromProgram(out_dir, pipe_name, 'crashy_program.exe',\n                              win32con.EXCEPTION_ACCESS_VIOLATION)\n\n\ndef GetDumpFromOtherProgram(out_dir, pipe_name, *args):\n    return GetDumpFromProgram(out_dir, pipe_name, 'crash_other_program.exe', 0,\n                              *args)\n\n\ndef GetDumpFromSignal(out_dir, pipe_name, *args):\n    STATUS_FATAL_APP_EXIT = 0x40000015  # Not known by win32con.\n    return GetDumpFromProgram(out_dir, pipe_name, 'crashy_signal.exe',\n                              STATUS_FATAL_APP_EXIT, *args)\n\n\ndef GetDumpFromSelfDestroyingProgram(out_dir, pipe_name):\n    return GetDumpFromProgram(out_dir, pipe_name, 'self_destroying_program.exe',\n                              win32con.EXCEPTION_BREAKPOINT)\n\n\ndef GetDumpFromZ7Program(out_dir, pipe_name):\n    return GetDumpFromProgram(out_dir, pipe_name, 'crashy_z7_loader.exe',\n                              win32con.EXCEPTION_ACCESS_VIOLATION)\n\n\ndef GetDumpFromHeapCorruptingProgram(out_dir, pipe_name):\n    STATUS_HEAP_CORRUPTION = 0xC0000374\n    return GetDumpFromProgram(out_dir, pipe_name, 'heap_corrupting_program.exe',\n                              STATUS_HEAP_CORRUPTION)\n\n\ndef GetDumpFromFastFailProgram(out_dir, pipe_name, *args):\n    STATUS_STACK_BUFFER_OVERRUN = 0xc0000409\n    return GetDumpFromProgram(out_dir, pipe_name, 'fastfail_program.exe',\n                              STATUS_STACK_BUFFER_OVERRUN, *args)\n\n\nclass CdbRun(object):\n    \"\"\"Run cdb.exe passing it a cdb command and capturing the output.\n    `Check()` searches for regex patterns in sequence allowing verification of\n    expected output.\n    \"\"\"\n\n    def __init__(self, cdb_path, dump_path, command):\n        # Run a command line that loads the dump, runs the specified cdb\n        # command, and then quits, and capturing stdout.\n        self.out = subprocess.check_output(\n            [cdb_path, '-z', dump_path, '-c', command + ';q'], text=True)\n\n    def Check(self, pattern, message, re_flags=0, must_not_match=False):\n        match_obj = re.search(pattern, self.out, flags=re_flags)\n        if match_obj and not must_not_match:\n            # Matched. Consume up to end of match.\n            self.out = self.out[match_obj.end(0):]\n            print('ok - %s' % message)\n            sys.stdout.flush()\n        elif must_not_match and not match_obj:\n            # Did not match and did not want to match.\n            print('ok - %s' % message)\n            sys.stdout.flush()\n        else:\n            print('-' * 80, file=sys.stderr)\n            print('FAILED - %s' % message, file=sys.stderr)\n            print('-' * 80, file=sys.stderr)\n            if must_not_match:\n                print('unexpected match:\\n  %s' % pattern, file=sys.stderr)\n            else:\n                print('did not match:\\n  %s' % pattern, file=sys.stderr)\n            print('-' * 80, file=sys.stderr)\n            print('remaining output was:\\n  %s' % self.out, file=sys.stderr)\n            print('-' * 80, file=sys.stderr)\n            sys.stderr.flush()\n            global g_had_failures\n            g_had_failures = True\n\n    def Find(self, pattern, re_flags=0):\n        match_obj = re.search(pattern, self.out, flags=re_flags)\n        if match_obj:\n            # Matched. Consume up to end of match.\n            self.out = self.out[match_obj.end(0):]\n            return match_obj\n        return None\n\n\ndef RunTests(cdb_path, dump_path, start_handler_dump_path, destroyed_dump_path,\n             pipe_name):\n    \"\"\"Runs various tests in sequence. Runs a new cdb instance on the dump for\n    each block of tests to reduce the chances that output from one command is\n    confused for output from another.\n    \"\"\"\n    out = CdbRun(cdb_path, dump_path, '.ecxr')\n    out.Check('This dump file has an exception of interest stored in it',\n              'captured exception')\n\n    # When SomeCrashyFunction is inlined, cdb doesn't demangle its namespace as\n    # \"`anonymous namespace'\" and instead gives the decorated form.\n    out.Check(\n        'crashy_program!crashpad::(`anonymous namespace\\'|\\?A0x[0-9a-f]+)::'\n        'SomeCrashyFunction', 'exception at correct location')\n\n    out = CdbRun(cdb_path, start_handler_dump_path, '.ecxr')\n    out.Check('This dump file has an exception of interest stored in it',\n              'captured exception (using StartHandler())')\n    out.Check(\n        'crashy_program!crashpad::(`anonymous namespace\\'|\\?A0x[0-9a-f]+)::'\n        'SomeCrashyFunction',\n        'exception at correct location (using StartHandler())')\n\n    out = CdbRun(cdb_path, dump_path, '!peb')\n    out.Check(r'PEB at', 'found the PEB')\n    out.Check(r'Ldr\\.InMemoryOrderModuleList:.*\\d+ \\. \\d+',\n              'PEB_LDR_DATA saved')\n    out.Check(r'Base TimeStamp                     Module',\n              'module list present')\n    pipe_name_escaped = pipe_name.replace('\\\\', '\\\\\\\\')\n    out.Check(r'CommandLine: *\\'.*crashy_program\\.exe *' + pipe_name_escaped,\n              'some PEB data is correct')\n    out.Check(r'SystemRoot=C:\\\\Windows', 'some of environment captured',\n              re.IGNORECASE)\n\n    out = CdbRun(cdb_path, dump_path, '?? @$peb->ProcessParameters')\n    out.Check(r' ImagePathName *: _UNICODE_STRING \".*\\\\crashy_program\\.exe\"',\n              'PEB->ProcessParameters.ImagePathName string captured')\n    out.Check(\n        ' DesktopInfo *: '\n        '_UNICODE_STRING \"(?!--- memory read error at address ).*\"',\n        'PEB->ProcessParameters.DesktopInfo string captured')\n\n    out = CdbRun(cdb_path, dump_path, '!teb')\n    out.Check(r'TEB at', 'found the TEB')\n    out.Check(r'ExceptionList:\\s+[0-9a-fA-F]+', 'some valid teb data')\n    out.Check(r'LastErrorValue:\\s+2', 'correct LastErrorValue')\n\n    out = CdbRun(cdb_path, dump_path, '!gle')\n    out.Check(\n        'LastErrorValue: \\(Win32\\) 0x2 \\(2\\) - The system cannot find the '\n        'file specified.', '!gle gets last error')\n    out.Check(\n        'LastStatusValue: \\(NTSTATUS\\) 0xc000000f - {File Not Found}  The '\n        'file %hs does not exist.', '!gle gets last ntstatus')\n\n    if False:\n        # TODO(scottmg): Re-enable when we grab ntdll!RtlCriticalSectionList.\n        out = CdbRun(cdb_path, dump_path, '!locks')\n        out.Check(\n            r'CritSec crashy_program!crashpad::`anonymous namespace\\'::'\n            r'g_test_critical_section', 'lock was captured')\n        if platform.win32_ver()[0] != '7':\n            # We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7.\n            out.Check(r'\\*\\*\\* Locked',\n                      'lock debug info was captured, and is locked')\n\n    out = CdbRun(cdb_path, dump_path, '!handle')\n    out.Check(r'\\d+ Handles', 'captured handles')\n    out.Check(r'Event\\s+\\d+', 'capture some event handles')\n    out.Check(r'File\\s+\\d+', 'capture some file handles')\n\n    out = CdbRun(cdb_path, dump_path, 'lm')\n    out.Check(r'Unloaded modules:', 'captured some unloaded modules')\n    out.Check(r'lz32\\.dll', 'found expected unloaded module lz32')\n    out.Check(r'wmerror\\.dll', 'found expected unloaded module wmerror')\n\n    out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2')\n    out.Check(r'Ldr\\.InMemoryOrderModuleList:.*\\d+ \\. \\d+',\n              'PEB_LDR_DATA saved')\n    out.Check(r'ntdll\\.dll', 'ntdll present', re.IGNORECASE)\n\n    # Check that there is no stack trace in the self-destroyed process. Confirm\n    # that the top is where we expect it (that's based only on IP), but\n    # subsequent stack entries will not be available. This confirms that we have\n    # a mostly valid dump, but that the stack was omitted.\n    out.Check(\n        r'self_destroying_program!crashpad::`anonymous namespace\\'::'\n        r'FreeOwnStackAndBreak.*\\nquit:',\n        'at correct location, no additional stack entries')\n\n    # Dump memory pointed to be EDI on the background suspended thread. We don't\n    # know the index of the thread because the system may have started other\n    # threads, so first do a run to extract the thread index that's suspended,\n    # and then another run to dump the data pointed to by EDI for that thread.\n    out = CdbRun(cdb_path, dump_path, '.ecxr;~')\n    match_obj = out.Find(r'(\\d+)\\s+Id: [0-9a-f.]+ Suspend: 1 Teb:')\n    if match_obj:\n        thread = match_obj.group(1)\n        out = CdbRun(cdb_path, dump_path, '.ecxr;~' + thread + 's;db /c14 edi')\n    out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50',\n              'data pointed to by registers captured')\n\n    # Move up one stack frame after jumping to the exception, and examine\n    # memory.\n    out = CdbRun(cdb_path, dump_path,\n                 '.ecxr; .f+; dd /c100 poi(offset_pointer)-20')\n    out.Check(\n        r'80000078 00000079 8000007a 0000007b 8000007c 0000007d 8000007e '\n        r'0000007f 80000080 00000081 80000082 00000083 80000084 00000085 '\n        r'80000086 00000087 80000088 00000089 8000008a 0000008b 8000008c '\n        r'0000008d 8000008e 0000008f 80000090 00000091 80000092 00000093 '\n        r'80000094 00000095 80000096 00000097',\n        'data pointed to by stack captured')\n\n    # Attempt to retrieve the value of g_extra_memory_pointer (by name), and\n    # then examine the memory at which it points. Both should have been saved.\n    out = CdbRun(\n        cdb_path, dump_path,\n        'dd poi(crashy_program!crashpad::g_extra_memory_pointer)+0x1f30 '\n        'L8')\n    out.Check(r'0000655e 0000656b 00006578 00006585',\n              'extra memory range captured')\n\n    out = CdbRun(cdb_path, dump_path, '.dumpdebug')\n    out.Check(r'type \\?\\?\\? \\(333333\\), size 00001000', 'first user stream')\n    out.Check(r'type \\?\\?\\? \\(222222\\), size 00000080', 'second user stream')\n\n\ndef Run7zDumpTest(cdb_path, z7_dump_path):\n    \"\"\"Validate output when non-pdb symbols are in a module.\"\"\"\n    out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm')\n    out.Check('This dump file has an exception of interest stored in it',\n              'captured exception in z7 module')\n    # Older versions of cdb display relative to exports for /Z7 modules,\n    # newer ones just display the offset.\n    out.Check(r'z7_test(!CrashMe\\+0xe|\\+0x100e):',\n              'exception in z7 at correct location')\n    out.Check(r'z7_test  C \\(codeview symbols\\)     z7_test\\.dll',\n              'expected non-pdb symbol format')\n\n\ndef RunOtherProgramTests(cdb_path, other_program_path,\n                         other_program_no_exception_path):\n    out = CdbRun(cdb_path, other_program_path, '.ecxr;k;~')\n    out.Check('Unknown exception - code deadbea7',\n              'other program dump exception code')\n    out.Check('!Sleep', 'other program reasonable location')\n    out.Check(\"hanging_program!`anonymous namespace'::Thread1\",\n              'other program dump right thread')\n    count = 0\n    while True:\n        match_obj = out.Find(r'Id.*Suspend: (\\d+) ')\n        if match_obj:\n            if match_obj.group(1) != '0':\n                out.Check(r'FAILED', 'all suspend counts should be 0')\n            else:\n                count += 1\n        else:\n            break\n    assert count > 2\n\n    out = CdbRun(cdb_path, other_program_no_exception_path, '.ecxr;k')\n    out.Check('Unknown exception - code 0cca11ed',\n              'other program with no exception given')\n    out.Check('!RaiseException', 'other program in RaiseException()')\n\n\ndef RunSigAbrtTest(cdb_path, sigabrt_main_path, sigabrt_background_path):\n    \"\"\"Validate that abort signals are collected.\"\"\"\n    out = CdbRun(cdb_path, sigabrt_main_path, '.ecxr')\n    out.Check('code 40000015', 'got sigabrt signal')\n    out.Check('::HandleAbortSignal', '  stack in expected location')\n\n    out = CdbRun(cdb_path, sigabrt_background_path, '.ecxr')\n    out.Check('code 40000015', 'got sigabrt signal from background thread')\n\n\ndef RunHeapCorruptionTest(cdb_path, heap_path):\n    \"\"\"Runs tests on heap corruption caught using the vectored handler.\"\"\"\n    out = CdbRun(cdb_path, heap_path, '.ecxr;k')\n    out.Check('code c0000374', 'captured exception from heap corruption crash')\n    out.Check('::HeapCorruptionCrash', 'See expected throwing function')\n    out = CdbRun(cdb_path, heap_path, '.ecxr;k')\n\n\ndef RunFastFailDumpTest(cdb_path, fastfail_path):\n    \"\"\"Runs tests on __fastfail() caught using the runtime exception helper.\"\"\"\n    out = CdbRun(cdb_path, fastfail_path, '.ecxr;k')\n    out.Check('This dump file has an exception of interest stored in it',\n              'captured exception from __fastfail() crash()')\n    out.Check(r'Subcode: 0x4d \\(unknown subcode\\)', 'See expected subcode.')\n    out.Check('FastFailCrash', 'See expected throwing function.')\n    out = CdbRun(cdb_path, fastfail_path, '.ecxr;k')\n\n\ndef RunCfgDumpTest(cdb_path, cfg_path):\n    \"\"\"Runs tests on a cfg crash caught using the runtime exception helper.\"\"\"\n    out = CdbRun(cdb_path, cfg_path, '.ecxr;k')\n    out.Check('This dump file has an exception of interest stored in it',\n              'captured exception from cfg crash()')\n    out.Check('Subcode: 0xa FAST_FAIL_GUARD_ICALL_CHECK_FAILURE',\n              'See expected cfg error code.')\n    out.Check('RtlFailFast',\n              'See expected Windows exception throwing function.')\n    out.Check('::CfgCrash', 'expected crashy function is on the stack.')\n    out = CdbRun(cdb_path, cfg_path, '.ecxr;k')\n    out.Check(r'CallRffeManyTimes',\n              'Do not see the function we fiddled the pointer for.',\n              must_not_match=True)\n\n\ndef main(args):\n    try:\n        if len(args) != 1:\n            print('must supply binary dir', file=sys.stderr)\n            return 1\n\n        cdb_path = GetCdbPath()\n        if not cdb_path:\n            print('could not find cdb', file=sys.stderr)\n            return 1\n\n        # Make sure we can download Windows symbols.\n        if not os.environ.get('_NT_SYMBOL_PATH'):\n            symbol_dir = MakeTempDir()\n            protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http'\n            os.environ['_NT_SYMBOL_PATH'] = (\n                'SRV*' + symbol_dir + '*' + protocol +\n                '://msdl.microsoft.com/download/symbols')\n\n        pipe_name = r'\\\\.\\pipe\\end-to-end_%s_%s' % (os.getpid(),\n                                                    str(random.getrandbits(64)))\n\n        # Basic tests.\n        crashy_dump_path = GetDumpFromCrashyProgram(args[0], pipe_name)\n        if not crashy_dump_path:\n            return 1\n\n        start_handler_dump_path = GetDumpFromCrashyProgram(args[0], None)\n        if not start_handler_dump_path:\n            return 1\n\n        destroyed_dump_path = GetDumpFromSelfDestroyingProgram(\n            args[0], pipe_name)\n        if not destroyed_dump_path:\n            return 1\n\n        RunTests(cdb_path, crashy_dump_path, start_handler_dump_path,\n                 destroyed_dump_path, pipe_name)\n\n        # Other program dumps.\n        other_program_path = GetDumpFromOtherProgram(args[0], pipe_name)\n        if not other_program_path:\n            return 1\n\n        other_program_no_exception_path = GetDumpFromOtherProgram(\n            args[0], pipe_name, 'noexception')\n        if not other_program_no_exception_path:\n            return 1\n\n        RunOtherProgramTests(cdb_path, other_program_path,\n                             other_program_no_exception_path)\n\n        # SIGABRT.\n        sigabrt_main_path = GetDumpFromSignal(args[0], pipe_name, 'main')\n        if not sigabrt_main_path:\n            return 1\n\n        sigabrt_background_path = GetDumpFromSignal(args[0], pipe_name,\n                                                    'background')\n        if not sigabrt_background_path:\n            return 1\n\n        RunSigAbrtTest(cdb_path, sigabrt_main_path, sigabrt_background_path)\n\n        # Can only build the z7 program on x86.\n        if not args[0].endswith('_x64'):\n            z7_dump_path = GetDumpFromZ7Program(args[0], pipe_name)\n            if not z7_dump_path:\n                return 1\n            Run7zDumpTest(cdb_path, z7_dump_path)\n\n        heap_path = GetDumpFromHeapCorruptingProgram(args[0], pipe_name)\n        if not heap_path:\n            return 1\n        RunHeapCorruptionTest(cdb_path, heap_path)\n\n        # __fastfail() & CFG crash caught by WerRuntimeExceptionHelperModule.\n        # TODO(crashpad:458) These are not working when launched from python.\n        if (False and Win32_20H1()):\n            cfg_path = GetDumpFromFastFailProgram(args[0], pipe_name, \"cf\")\n            if not cfg_path:\n                return 1\n            RunCfgDumpTest(cdb_path, cfg_path)\n            fastfail_path = GetDumpFromFastFailProgram(args[0], pipe_name, \"ff\")\n            if not fastfail_path:\n                return 1\n            RunFastFailDumpTest(cdb_path, fastfail_path)\n\n        return 1 if g_had_failures else 0\n    finally:\n        CleanUpTempDirs()\n\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv[1:]))\n"
  },
  {
    "path": "snapshot/win/exception_snapshot_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/exception_snapshot_win.h\"\n\n#include <algorithm>\n\n#include \"base/logging.h\"\n#include \"snapshot/capture_memory.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"snapshot/win/capture_memory_delegate_win.h\"\n#include \"snapshot/win/cpu_context_win.h\"\n#include \"snapshot/win/process_reader_win.h\"\n#include \"util/win/exception_codes.h\"\n#include \"util/win/nt_internals.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n\n#if defined(ARCH_CPU_X86_FAMILY)\n#if defined(ARCH_CPU_32_BITS)\nusing Context32 = CONTEXT;\n#elif defined(ARCH_CPU_64_BITS)\nusing Context32 = WOW64_CONTEXT;\n#endif\n\nvoid NativeContextToCPUContext32(const Context32* context_record,\n                                 CPUContext* context,\n                                 CPUContextUnion* context_union) {\n  context->architecture = kCPUArchitectureX86;\n  context->x86 = &context_union->x86;\n  InitializeX86Context(context_record, context->x86);\n}\n#endif  // ARCH_CPU_X86_FAMILY\n\n#if defined(ARCH_CPU_64_BITS)\nvoid NativeContextToCPUContext64(const CONTEXT* context_record,\n                                 CPUContext* context,\n                                 CPUContextUnion* context_union) {\n#if defined(ARCH_CPU_X86_64)\n  context->architecture = kCPUArchitectureX86_64;\n  context->x86_64 = &context_union->x86_64;\n  // Note that the context here is not extended, even if the flags suggest so,\n  // as we only copied in sizeof(CONTEXT).\n  InitializeX64Context(context_record, context->x86_64);\n  // TODO(1250098) plumb through ssp via message from crashed process. For now\n  // we zero this if CET is available in the capturing process as otherwise\n  // WinDBG will show the relevant thread's ssp for the exception which will\n  // likely be more confusing than showing a zero value.\n  if (IsXStateFeatureEnabled(XSTATE_MASK_CET_U)) {\n    XSAVE_CET_U_FORMAT cet_u_fake;\n    cet_u_fake.Ia32CetUMsr = 0;\n    cet_u_fake.Ia32Pl3SspMsr = 0;\n    InitializeX64XStateCet(context_record, &cet_u_fake, context->x86_64);\n  }\n#elif defined(ARCH_CPU_ARM64)\n  context->architecture = kCPUArchitectureARM64;\n  context->arm64 = &context_union->arm64;\n  InitializeARM64Context(context_record, context->arm64);\n#else\n#error Unsupported Windows 64-bit Arch\n#endif\n}\n#endif\n\n}  // namespace\n\nExceptionSnapshotWin::ExceptionSnapshotWin()\n    : ExceptionSnapshot(),\n      context_union_(),\n      context_(),\n      codes_(),\n      extra_memory_(),\n      thread_id_(0),\n      exception_address_(0),\n      exception_flags_(0),\n      exception_code_(0),\n      initialized_() {\n}\n\nExceptionSnapshotWin::~ExceptionSnapshotWin() {\n}\n\nbool ExceptionSnapshotWin::Initialize(\n    ProcessReaderWin* process_reader,\n    DWORD thread_id,\n    WinVMAddress exception_pointers_address,\n    uint32_t* gather_indirectly_referenced_memory_cap) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  const ProcessReaderWin::Thread* thread = nullptr;\n  for (const auto& loop_thread : process_reader->Threads()) {\n    if (thread_id == loop_thread.id) {\n      thread = &loop_thread;\n      break;\n    }\n  }\n\n  if (!thread) {\n    LOG(ERROR) << \"thread ID \" << thread_id << \" not found in process\";\n    return false;\n  } else {\n    thread_id_ = thread_id;\n  }\n\n#if defined(ARCH_CPU_32_BITS)\n  const bool is_64_bit = false;\n#elif defined(ARCH_CPU_64_BITS)\n  const bool is_64_bit = process_reader->Is64Bit();\n  if (is_64_bit) {\n    if (!InitializeFromExceptionPointers<EXCEPTION_RECORD64,\n                                         process_types::EXCEPTION_POINTERS64>(\n            process_reader,\n            exception_pointers_address,\n            thread_id,\n            &NativeContextToCPUContext64)) {\n      return false;\n    }\n  }\n#endif\n\n#if !defined(ARCH_CPU_ARM64)\n  if (!is_64_bit) {\n    if (!InitializeFromExceptionPointers<EXCEPTION_RECORD32,\n                                         process_types::EXCEPTION_POINTERS32>(\n            process_reader,\n            exception_pointers_address,\n            thread_id,\n            &NativeContextToCPUContext32)) {\n      return false;\n    }\n  }\n#endif\n\n  CaptureMemoryDelegateWin capture_memory_delegate(\n      process_reader,\n      *thread,\n      &extra_memory_,\n      gather_indirectly_referenced_memory_cap);\n  CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst CPUContext* ExceptionSnapshotWin::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nuint64_t ExceptionSnapshotWin::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_id_;\n}\n\nuint32_t ExceptionSnapshotWin::Exception() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_code_;\n}\n\nuint32_t ExceptionSnapshotWin::ExceptionInfo() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_flags_;\n}\n\nuint64_t ExceptionSnapshotWin::ExceptionAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return exception_address_;\n}\n\nconst std::vector<uint64_t>& ExceptionSnapshotWin::Codes() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return codes_;\n}\n\nstd::vector<const MemorySnapshot*> ExceptionSnapshotWin::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> result;\n  result.reserve(extra_memory_.size());\n  for (const auto& em : extra_memory_) {\n    result.push_back(em.get());\n  }\n  return result;\n}\n\ntemplate <class ExceptionRecordType,\n          class ExceptionPointersType,\n          class ContextType>\nbool ExceptionSnapshotWin::InitializeFromExceptionPointers(\n    ProcessReaderWin* process_reader,\n    WinVMAddress exception_pointers_address,\n    DWORD exception_thread_id,\n    void (*native_to_cpu_context)(const ContextType* context_record,\n                                  CPUContext* context,\n                                  CPUContextUnion* context_union)) {\n  ExceptionPointersType exception_pointers;\n  if (!process_reader->Memory()->Read(exception_pointers_address,\n                                      sizeof(exception_pointers),\n                                      &exception_pointers)) {\n    LOG(ERROR) << \"EXCEPTION_POINTERS read failed\";\n    return false;\n  }\n  if (!exception_pointers.ExceptionRecord) {\n    LOG(ERROR) << \"null ExceptionRecord\";\n    return false;\n  }\n\n  ExceptionRecordType first_record;\n  if (!process_reader->Memory()->Read(\n          static_cast<WinVMAddress>(exception_pointers.ExceptionRecord),\n          sizeof(first_record),\n          &first_record)) {\n    LOG(ERROR) << \"ExceptionRecord\";\n    return false;\n  }\n\n  const bool triggered_by_client =\n      first_record.ExceptionCode == ExceptionCodes::kTriggeredExceptionCode &&\n      first_record.NumberParameters == 2;\n  if (triggered_by_client)\n    process_reader->DecrementThreadSuspendCounts(exception_thread_id);\n\n  if (triggered_by_client && first_record.ExceptionInformation[0] != 0) {\n    // This special exception code indicates that the target was crashed by\n    // another client calling CrashpadClient::DumpAndCrashTargetProcess(). In\n    // this case the parameters are a thread id and an exception code which we\n    // use to fabricate a new exception record.\n    using ArgumentType = decltype(first_record.ExceptionInformation[0]);\n    const ArgumentType blame_thread_id = first_record.ExceptionInformation[0];\n    exception_code_ = static_cast<DWORD>(first_record.ExceptionInformation[1]);\n    exception_flags_ = EXCEPTION_NONCONTINUABLE;\n    for (const auto& thread : process_reader->Threads()) {\n      if (thread.id == blame_thread_id) {\n        thread_id_ = blame_thread_id;\n        native_to_cpu_context(thread.context.context<const ContextType>(),\n                              &context_,\n                              &context_union_);\n        exception_address_ = context_.InstructionPointer();\n        break;\n      }\n    }\n\n    if (exception_address_ == 0) {\n      LOG(WARNING) << \"thread \" << blame_thread_id << \" not found\";\n      return false;\n    }\n  } else {\n    // Normal case.\n    exception_code_ = first_record.ExceptionCode;\n    exception_flags_ = first_record.ExceptionFlags;\n    exception_address_ = first_record.ExceptionAddress;\n\n    const DWORD number_parameters = std::min<DWORD>(\n        first_record.NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS);\n    for (DWORD i = 0; i < number_parameters; ++i) {\n      codes_.push_back(first_record.ExceptionInformation[i]);\n    }\n    if (first_record.ExceptionRecord) {\n      // https://crashpad.chromium.org/bug/43\n      LOG(WARNING) << \"dropping chained ExceptionRecord\";\n    }\n\n    ContextType context_record;\n    if (!process_reader->Memory()->Read(\n            static_cast<WinVMAddress>(exception_pointers.ContextRecord),\n            sizeof(context_record),\n            &context_record)) {\n      LOG(ERROR) << \"ContextRecord\";\n      return false;\n    }\n\n    native_to_cpu_context(&context_record, &context_, &context_union_);\n  }\n\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/exception_snapshot_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_\n\n#include <windows.h>\n\n#include <stdint.h>\n\n#include <memory>\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/win/thread_snapshot_win.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/process_structs.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderWin;\n\nnamespace internal {\n\nclass MemorySnapshotGeneric;\n\nunion CPUContextUnion {\n#if defined(ARCH_CPU_X86_FAMILY)\n  CPUContextX86 x86;\n  CPUContextX86_64 x86_64;\n#elif defined(ARCH_CPU_ARM64)\n  CPUContextARM64 arm64;\n#endif\n};\n\nclass ExceptionSnapshotWin final : public ExceptionSnapshot {\n public:\n  ExceptionSnapshotWin();\n\n  ExceptionSnapshotWin(const ExceptionSnapshotWin&) = delete;\n  ExceptionSnapshotWin& operator=(const ExceptionSnapshotWin&) = delete;\n\n  ~ExceptionSnapshotWin() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderWin for the process that\n  //!     sustained the exception.\n  //! \\param[in] thread_id The thread ID in which the exception occurred.\n  //! \\param[in] exception_pointers The address of an `EXCEPTION_POINTERS`\n  //!     record in the target process, passed through from the exception\n  //!     handler.\n  //! \\param[inout] gather_indirectly_referenced_memory_cap The remaining budget\n  //!     for indirectly referenced memory, honored on entry and updated on\n  //!     return.\n  //!\n  //! \\note If the exception was triggered by\n  //!     CrashpadClient::DumpAndCrashTargetProcess(), this has the side-effect\n  //!     of correcting the thread suspend counts for \\a process_reader.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(ProcessReaderWin* process_reader,\n                  DWORD thread_id,\n                  WinVMAddress exception_pointers,\n                  uint32_t* gather_indirectly_referenced_memory_cap);\n\n  // ExceptionSnapshot:\n\n  const CPUContext* Context() const override;\n  uint64_t ThreadID() const override;\n  uint32_t Exception() const override;\n  uint32_t ExceptionInfo() const override;\n  uint64_t ExceptionAddress() const override;\n  const std::vector<uint64_t>& Codes() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  template <class ExceptionRecordType,\n            class ExceptionPointersType,\n            class ContextType>\n  bool InitializeFromExceptionPointers(\n      ProcessReaderWin* process_reader,\n      WinVMAddress exception_pointers_address,\n      DWORD exception_thread_id,\n      void (*native_to_cpu_context)(const ContextType* context_record,\n                                    CPUContext* context,\n                                    CPUContextUnion* context_union));\n\n  CPUContextUnion context_union_;\n  CPUContext context_;\n  std::vector<uint64_t> codes_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>> extra_memory_;\n  uint64_t thread_id_;\n  uint64_t exception_address_;\n  uint32_t exception_flags_;\n  DWORD exception_code_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_\n"
  },
  {
    "path": "snapshot/win/exception_snapshot_win_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/exception_snapshot_win.h\"\n\n#include <windows.h>\n\n#include <string>\n\n#include \"base/files/file_path.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/win/exception_snapshot_win.h\"\n#include \"snapshot/win/process_snapshot_win.h\"\n#include \"test/errors.h\"\n#include \"test/test_paths.h\"\n#include \"test/win/child_launcher.h\"\n#include \"util/file/file_io.h\"\n#include \"util/thread/thread.h\"\n#include \"util/win/exception_handler_server.h\"\n#include \"util/win/registration_protocol_win.h\"\n#include \"util/win/scoped_handle.h\"\n#include \"util/win/scoped_process_suspend.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Runs the ExceptionHandlerServer on a background thread.\nclass RunServerThread : public Thread {\n public:\n  // Instantiates a thread which will invoke server->Run(delegate);\n  RunServerThread(ExceptionHandlerServer* server,\n                  ExceptionHandlerServer::Delegate* delegate)\n      : server_(server), delegate_(delegate) {}\n\n  RunServerThread(const RunServerThread&) = delete;\n  RunServerThread& operator=(const RunServerThread&) = delete;\n\n  ~RunServerThread() override {}\n\n private:\n  // Thread:\n  void ThreadMain() override { server_->Run(delegate_); }\n\n  ExceptionHandlerServer* server_;\n  ExceptionHandlerServer::Delegate* delegate_;\n};\n\n// During destruction, ensures that the server is stopped and the background\n// thread joined.\nclass ScopedStopServerAndJoinThread {\n public:\n  ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, Thread* thread)\n      : server_(server), thread_(thread) {}\n\n  ScopedStopServerAndJoinThread(const ScopedStopServerAndJoinThread&) = delete;\n  ScopedStopServerAndJoinThread& operator=(\n      const ScopedStopServerAndJoinThread&) = delete;\n\n  ~ScopedStopServerAndJoinThread() {\n    server_->Stop();\n    thread_->Join();\n  }\n\n private:\n  ExceptionHandlerServer* server_;\n  Thread* thread_;\n};\n\nclass CrashingDelegate : public ExceptionHandlerServer::Delegate {\n public:\n  CrashingDelegate(HANDLE server_ready, HANDLE completed_test_event)\n      : server_ready_(server_ready),\n        completed_test_event_(completed_test_event),\n        break_near_(0) {}\n\n  CrashingDelegate(const CrashingDelegate&) = delete;\n  CrashingDelegate& operator=(const CrashingDelegate&) = delete;\n\n  ~CrashingDelegate() {}\n\n  void set_break_near(WinVMAddress break_near) { break_near_ = break_near; }\n\n  void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); }\n\n  unsigned int ExceptionHandlerServerException(\n      HANDLE process,\n      WinVMAddress exception_information_address,\n      WinVMAddress debug_critical_section_address) override {\n    ScopedProcessSuspend suspend(process);\n    ProcessSnapshotWin snapshot;\n    snapshot.Initialize(process,\n                        ProcessSuspensionState::kSuspended,\n                        exception_information_address,\n                        debug_critical_section_address);\n\n    // Confirm the exception record was read correctly.\n    EXPECT_NE(snapshot.Exception()->ThreadID(), 0u);\n    EXPECT_EQ(EXCEPTION_BREAKPOINT, snapshot.Exception()->Exception());\n\n    // Verify the exception happened at the expected location with a bit of\n    // slop space to allow for reading the current PC before the exception\n    // happens. See TestCrashingChild().\n#if !defined(NDEBUG)\n    // Debug build is likely not optimized and contains more instructions.\n    constexpr uint64_t kAllowedOffset = 200;\n#else\n    constexpr uint64_t kAllowedOffset = 100;\n#endif\n    EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_);\n    EXPECT_LT(snapshot.Exception()->ExceptionAddress(),\n              break_near_ + kAllowedOffset);\n\n    SetEvent(completed_test_event_);\n\n    return snapshot.Exception()->Exception();\n  }\n\n private:\n  HANDLE server_ready_;  // weak\n  HANDLE completed_test_event_;  // weak\n  WinVMAddress break_near_;\n};\n\nvoid TestCrashingChild(TestPaths::Architecture architecture) {\n  // Set up the registration server on a background thread.\n  ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr));\n  ASSERT_TRUE(server_ready.is_valid()) << ErrorMessage(\"CreateEvent\");\n  ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));\n  ASSERT_TRUE(completed.is_valid()) << ErrorMessage(\"CreateEvent\");\n  CrashingDelegate delegate(server_ready.get(), completed.get());\n\n  ExceptionHandlerServer exception_handler_server(true);\n  std::wstring pipe_name(L\"\\\\\\\\.\\\\pipe\\\\test_name\");\n  exception_handler_server.SetPipeName(pipe_name);\n  RunServerThread server_thread(&exception_handler_server, &delegate);\n  server_thread.Start();\n  ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(\n      &exception_handler_server, &server_thread);\n\n  EXPECT_EQ(WaitForSingleObject(server_ready.get(), INFINITE), WAIT_OBJECT_0)\n      << ErrorMessage(\"WaitForSingleObject\");\n\n  // Spawn a child process, passing it the pipe name to connect to.\n  base::FilePath child_test_executable =\n      TestPaths::BuildArtifact(L\"snapshot\",\n                               L\"crashing_child\",\n                               TestPaths::FileType::kExecutable,\n                               architecture);\n  ChildLauncher child(child_test_executable, pipe_name);\n  ASSERT_NO_FATAL_FAILURE(child.Start());\n\n  // The child tells us (approximately) where it will crash.\n  WinVMAddress break_near_address;\n  LoggingReadFileExactly(child.stdout_read_handle(),\n                         &break_near_address,\n                         sizeof(break_near_address));\n  delegate.set_break_near(break_near_address);\n\n  // Wait for the child to crash and the exception information to be validated.\n  EXPECT_EQ(WaitForSingleObject(completed.get(), INFINITE), WAIT_OBJECT_0)\n      << ErrorMessage(\"WaitForSingleObject\");\n\n  EXPECT_EQ(child.WaitForExit(), EXCEPTION_BREAKPOINT);\n}\n\n#if defined(ADDRESS_SANITIZER)\n// https://crbug.com/845011\n#define MAYBE_ChildCrash DISABLED_ChildCrash\n#else\n#define MAYBE_ChildCrash ChildCrash\n#endif\nTEST(ExceptionSnapshotWinTest, MAYBE_ChildCrash) {\n  TestCrashingChild(TestPaths::Architecture::kDefault);\n}\n\n#if defined(ARCH_CPU_64_BITS)\nTEST(ExceptionSnapshotWinTest, ChildCrashWOW64) {\n  if (!TestPaths::Has32BitBuildArtifacts()) {\n    GTEST_SKIP();\n  }\n\n  TestCrashingChild(TestPaths::Architecture::k32Bit);\n}\n#endif  // ARCH_CPU_64_BITS\n\nclass SimulateDelegate : public ExceptionHandlerServer::Delegate {\n public:\n  SimulateDelegate(HANDLE server_ready, HANDLE completed_test_event)\n      : server_ready_(server_ready),\n        completed_test_event_(completed_test_event),\n        dump_near_(0) {}\n\n  SimulateDelegate(const SimulateDelegate&) = delete;\n  SimulateDelegate& operator=(const SimulateDelegate&) = delete;\n\n  ~SimulateDelegate() {}\n\n  void set_dump_near(WinVMAddress dump_near) { dump_near_ = dump_near; }\n\n  void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); }\n\n  unsigned int ExceptionHandlerServerException(\n      HANDLE process,\n      WinVMAddress exception_information_address,\n      WinVMAddress debug_critical_section_address) override {\n    ScopedProcessSuspend suspend(process);\n    ProcessSnapshotWin snapshot;\n    snapshot.Initialize(process,\n                        ProcessSuspensionState::kSuspended,\n                        exception_information_address,\n                        debug_critical_section_address);\n    EXPECT_TRUE(snapshot.Exception());\n    EXPECT_EQ(snapshot.Exception()->Exception(), 0x517a7edu);\n\n    // Verify the dump was captured at the expected location with some slop\n    // space.\n#if defined(ADDRESS_SANITIZER)\n    // ASan instrumentation inserts more instructions between the expected\n    // location and what's reported. https://crbug.com/845011.\n    constexpr uint64_t kAllowedOffset = 500;\n#elif !defined(NDEBUG)\n    // Debug build is likely not optimized and contains more instructions.\n    constexpr uint64_t kAllowedOffset = 200;\n#else\n    constexpr uint64_t kAllowedOffset = 100;\n#endif\n    EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(),\n              dump_near_);\n    EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(),\n              dump_near_ + kAllowedOffset);\n\n    EXPECT_EQ(snapshot.Exception()->ExceptionAddress(),\n              snapshot.Exception()->Context()->InstructionPointer());\n\n    SetEvent(completed_test_event_);\n\n    return 0;\n  }\n\n private:\n  HANDLE server_ready_;  // weak\n  HANDLE completed_test_event_;  // weak\n  WinVMAddress dump_near_;\n};\n\nvoid TestDumpWithoutCrashingChild(TestPaths::Architecture architecture) {\n  // Set up the registration server on a background thread.\n  ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr));\n  ASSERT_TRUE(server_ready.is_valid()) << ErrorMessage(\"CreateEvent\");\n  ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));\n  ASSERT_TRUE(completed.is_valid()) << ErrorMessage(\"CreateEvent\");\n  SimulateDelegate delegate(server_ready.get(), completed.get());\n\n  ExceptionHandlerServer exception_handler_server(true);\n  std::wstring pipe_name(L\"\\\\\\\\.\\\\pipe\\\\test_name\");\n  exception_handler_server.SetPipeName(pipe_name);\n  RunServerThread server_thread(&exception_handler_server, &delegate);\n  server_thread.Start();\n  ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(\n      &exception_handler_server, &server_thread);\n\n  EXPECT_EQ(WaitForSingleObject(server_ready.get(), INFINITE), WAIT_OBJECT_0)\n      << ErrorMessage(\"WaitForSingleObject\");\n\n  // Spawn a child process, passing it the pipe name to connect to.\n  base::FilePath child_test_executable =\n      TestPaths::BuildArtifact(L\"snapshot\",\n                               L\"dump_without_crashing\",\n                               TestPaths::FileType::kExecutable,\n                               architecture);\n  ChildLauncher child(child_test_executable, pipe_name);\n  ASSERT_NO_FATAL_FAILURE(child.Start());\n\n  // The child tells us (approximately) where it will capture a dump.\n  WinVMAddress dump_near_address;\n  LoggingReadFileExactly(child.stdout_read_handle(),\n                         &dump_near_address,\n                         sizeof(dump_near_address));\n  delegate.set_dump_near(dump_near_address);\n\n  // Wait for the child to crash and the exception information to be validated.\n  EXPECT_EQ(WaitForSingleObject(completed.get(), INFINITE), WAIT_OBJECT_0)\n      << ErrorMessage(\"WaitForSingleObject\");\n\n  EXPECT_EQ(child.WaitForExit(), 0u);\n}\n\n#if defined(ADDRESS_SANITIZER)\n// https://crbug.com/845011\n#define MAYBE_ChildDumpWithoutCrashing DISABLED_ChildDumpWithoutCrashing\n#else\n#define MAYBE_ChildDumpWithoutCrashing ChildDumpWithoutCrashing\n#endif\nTEST(SimulateCrash, MAYBE_ChildDumpWithoutCrashing) {\n  TestDumpWithoutCrashingChild(TestPaths::Architecture::kDefault);\n}\n\n#if defined(ARCH_CPU_64_BITS)\nTEST(SimulateCrash, ChildDumpWithoutCrashingWOW64) {\n  if (!TestPaths::Has32BitBuildArtifacts()) {\n    GTEST_SKIP();\n  }\n\n  TestDumpWithoutCrashingChild(TestPaths::Architecture::k32Bit);\n}\n#endif  // ARCH_CPU_64_BITS\n\nTEST(ExceptionSnapshot, TooManyExceptionParameters) {\n  ProcessReaderWin process_reader;\n  ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),\n                                        ProcessSuspensionState::kRunning));\n\n  // Construct a fake exception record and CPU context.\n  auto exception_record = std::make_unique<EXCEPTION_RECORD>();\n  exception_record->ExceptionCode = STATUS_FATAL_APP_EXIT;\n  exception_record->ExceptionFlags = EXCEPTION_NONCONTINUABLE;\n  exception_record->ExceptionAddress = reinterpret_cast<PVOID>(0xFA15E);\n  // One more than is permitted in the struct.\n  exception_record->NumberParameters = EXCEPTION_MAXIMUM_PARAMETERS + 1;\n  for (int i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; ++i) {\n    exception_record->ExceptionInformation[i] = 1000 + i;\n  }\n\n  auto cpu_context = std::make_unique<internal::CPUContextUnion>();\n\n  auto exception_pointers = std::make_unique<EXCEPTION_POINTERS>();\n  exception_pointers->ExceptionRecord =\n      reinterpret_cast<PEXCEPTION_RECORD>(exception_record.get());\n  exception_pointers->ContextRecord =\n      reinterpret_cast<PCONTEXT>(cpu_context.get());\n\n  internal::ExceptionSnapshotWin snapshot;\n  ASSERT_TRUE(snapshot.Initialize(\n      &process_reader,\n      GetCurrentThreadId(),\n      reinterpret_cast<WinVMAddress>(exception_pointers.get()),\n      nullptr));\n\n  EXPECT_EQ(STATUS_FATAL_APP_EXIT, snapshot.Exception());\n  EXPECT_EQ(static_cast<uint32_t>(EXCEPTION_NONCONTINUABLE),\n            snapshot.ExceptionInfo());\n  EXPECT_EQ(0xFA15Eu, snapshot.ExceptionAddress());\n  EXPECT_EQ(static_cast<size_t>(EXCEPTION_MAXIMUM_PARAMETERS),\n            snapshot.Codes().size());\n  for (size_t i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; ++i) {\n    EXPECT_EQ(1000 + i, snapshot.Codes()[i]);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/extra_memory_ranges_test.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/module_snapshot_win.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include <string>\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simple_address_range_bag.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/win/process_snapshot_win.h\"\n#include \"test/test_paths.h\"\n#include \"test/win/child_launcher.h\"\n#include \"util/file/file_io.h\"\n#include \"util/win/process_info.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nenum TestType {\n  // Don't crash, just test the CrashpadInfo interface.\n  kDontCrash = 0,\n\n  // The child process should crash by __debugbreak().\n  kCrashDebugBreak,\n};\n\nvoid TestExtraMemoryRanges(TestType type,\n                           TestPaths::Architecture architecture) {\n  // Spawn a child process, passing it the pipe name to connect to.\n  base::FilePath child_test_executable =\n      TestPaths::BuildArtifact(L\"snapshot\",\n                               L\"extra_memory_ranges\",\n                               TestPaths::FileType::kExecutable,\n                               architecture);\n  ChildLauncher child(child_test_executable, L\"\");\n  ASSERT_NO_FATAL_FAILURE(child.Start());\n\n  // Wait for the child process to indicate that it's done setting up its\n  // annotations via the CrashpadInfo interface.\n  char c;\n  CheckedReadFileExactly(child.stdout_read_handle(), &c, sizeof(c));\n\n  ProcessSnapshotWin snapshot;\n  ASSERT_TRUE(snapshot.Initialize(\n      child.process_handle(), ProcessSuspensionState::kRunning, 0, 0));\n\n  // Verify the extra memory ranges set via the CrashpadInfo interface.\n  std::set<CheckedRange<uint64_t>> all_ranges;\n  for (const auto* module : snapshot.Modules()) {\n    for (const auto& range : module->ExtraMemoryRanges())\n      all_ranges.insert(range);\n  }\n\n  EXPECT_EQ(all_ranges.size(), 5u);\n  EXPECT_NE(all_ranges.find(CheckedRange<uint64_t>(0, 1)), all_ranges.end());\n  EXPECT_NE(all_ranges.find(CheckedRange<uint64_t>(1, 0)), all_ranges.end());\n  EXPECT_NE(all_ranges.find(CheckedRange<uint64_t>(1234, 5678)),\n            all_ranges.end());\n  EXPECT_NE(all_ranges.find(CheckedRange<uint64_t>(0x1000000000ULL, 0x1000)),\n            all_ranges.end());\n  EXPECT_NE(all_ranges.find(CheckedRange<uint64_t>(0x2000, 0x2000000000ULL)),\n            all_ranges.end());\n\n  // Tell the child process to continue.\n  DWORD expected_exit_code;\n  switch (type) {\n    case kDontCrash:\n      c = ' ';\n      expected_exit_code = 0;\n      break;\n    case kCrashDebugBreak:\n      c = 'd';\n      expected_exit_code = STATUS_BREAKPOINT;\n      break;\n    default:\n      FAIL();\n  }\n  CheckedWriteFile(child.stdin_write_handle(), &c, sizeof(c));\n\n  EXPECT_EQ(child.WaitForExit(), expected_exit_code);\n}\n\nTEST(ExtraMemoryRanges, DontCrash) {\n  TestExtraMemoryRanges(kDontCrash, TestPaths::Architecture::kDefault);\n}\n\nTEST(ExtraMemoryRanges, CrashDebugBreak) {\n  TestExtraMemoryRanges(kCrashDebugBreak, TestPaths::Architecture::kDefault);\n}\n\n#if defined(ARCH_CPU_64_BITS)\nTEST(ExtraMemoryRanges, DontCrashWOW64) {\n  if (!TestPaths::Has32BitBuildArtifacts()) {\n    GTEST_SKIP();\n  }\n\n  TestExtraMemoryRanges(kDontCrash, TestPaths::Architecture::k32Bit);\n}\n\nTEST(ExtraMemoryRanges, CrashDebugBreakWOW64) {\n  if (!TestPaths::Has32BitBuildArtifacts()) {\n    GTEST_SKIP();\n  }\n\n  TestExtraMemoryRanges(kCrashDebugBreak, TestPaths::Architecture::k32Bit);\n}\n#endif  // ARCH_CPU_64_BITS\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/memory_map_region_snapshot_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/memory_map_region_snapshot_win.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nMemoryMapRegionSnapshotWin::MemoryMapRegionSnapshotWin(\n    const MEMORY_BASIC_INFORMATION64& mbi)\n    : memory_info_() {\n  memory_info_.BaseAddress = mbi.BaseAddress;\n  memory_info_.AllocationBase = mbi.AllocationBase;\n  memory_info_.AllocationProtect = mbi.AllocationProtect;\n  memory_info_.RegionSize = mbi.RegionSize;\n  memory_info_.State = mbi.State;\n  memory_info_.Protect = mbi.Protect;\n  memory_info_.Type = mbi.Type;\n}\n\nMemoryMapRegionSnapshotWin::~MemoryMapRegionSnapshotWin() {\n}\n\nconst MINIDUMP_MEMORY_INFO& MemoryMapRegionSnapshotWin::AsMinidumpMemoryInfo()\n    const {\n  return memory_info_;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/memory_map_region_snapshot_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_\n\n#include \"snapshot/memory_map_region_snapshot.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nclass MemoryMapRegionSnapshotWin : public MemoryMapRegionSnapshot {\n public:\n  explicit MemoryMapRegionSnapshotWin(const MEMORY_BASIC_INFORMATION64& mbi);\n  ~MemoryMapRegionSnapshotWin() override;\n\n  virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override;\n\n private:\n  MINIDUMP_MEMORY_INFO memory_info_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_\n"
  },
  {
    "path": "snapshot/win/module_snapshot_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/module_snapshot_win.h\"\n\n#include <utility>\n\n#include \"base/strings/utf_string_conversions.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simple_address_range_bag.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"snapshot/win/pe_image_annotations_reader.h\"\n#include \"snapshot/win/pe_image_reader.h\"\n#include \"util/misc/tri_state.h\"\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nModuleSnapshotWin::ModuleSnapshotWin()\n    : ModuleSnapshot(),\n      name_(),\n      pdb_name_(),\n      uuid_(),\n      memory_range_(),\n      streams_(),\n      vs_fixed_file_info_(),\n      initialized_vs_fixed_file_info_(),\n      process_reader_(nullptr),\n      pe_image_reader_(),\n      crashpad_info_(),\n      timestamp_(0),\n      age_(0),\n      initialized_() {}\n\nModuleSnapshotWin::~ModuleSnapshotWin() {}\n\nbool ModuleSnapshotWin::Initialize(\n    ProcessReaderWin* process_reader,\n    const ProcessInfo::Module& process_reader_module) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  process_reader_ = process_reader;\n  name_ = process_reader_module.name;\n  timestamp_ = process_reader_module.timestamp;\n  pe_image_reader_.reset(new PEImageReader());\n  if (!pe_image_reader_->Initialize(process_reader_,\n                                    process_reader_module.dll_base,\n                                    process_reader_module.size,\n                                    base::WideToUTF8(name_))) {\n    return false;\n  }\n\n  DWORD age_dword;\n  if (pe_image_reader_->DebugDirectoryInformation(\n          &uuid_, &age_dword, &pdb_name_)) {\n    static_assert(sizeof(DWORD) == sizeof(uint32_t), \"unexpected age size\");\n    age_ = age_dword;\n  } else {\n    // If we fully supported all old debugging formats, we would want to extract\n    // and emit a different type of CodeView record here (as old Microsoft tools\n    // would do). As we don't expect to ever encounter a module that wouldn't be\n    // using .PDB that we actually have symbols for, we simply set a plausible\n    // name here, but this will never correspond to symbols that we have.\n    pdb_name_ = base::WideToUTF8(name_);\n  }\n\n  if (!memory_range_.Initialize(process_reader_->Memory(),\n                                process_reader_->Is64Bit())) {\n    return false;\n  }\n\n  WinVMAddress crashpad_info_address;\n  WinVMSize crashpad_info_size;\n  if (pe_image_reader_->GetCrashpadInfoSection(&crashpad_info_address,\n                                               &crashpad_info_size)) {\n    ProcessMemoryRange info_range;\n    info_range.Initialize(memory_range_);\n    info_range.RestrictRange(crashpad_info_address,\n                             crashpad_info_address + crashpad_info_size);\n\n    auto info = std::make_unique<CrashpadInfoReader>();\n    if (info->Initialize(&info_range, crashpad_info_address)) {\n      crashpad_info_ = std::move(info);\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nvoid ModuleSnapshotWin::GetCrashpadOptions(CrashpadInfoClientOptions* options) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (process_reader_->Is64Bit())\n    GetCrashpadOptionsInternal<process_types::internal::Traits64>(options);\n  else\n    GetCrashpadOptionsInternal<process_types::internal::Traits32>(options);\n}\n\nstd::string ModuleSnapshotWin::Name() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return base::WideToUTF8(name_);\n}\n\nuint64_t ModuleSnapshotWin::Address() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return pe_image_reader_->Address();\n}\n\nuint64_t ModuleSnapshotWin::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return pe_image_reader_->Size();\n}\n\ntime_t ModuleSnapshotWin::Timestamp() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return timestamp_;\n}\n\nvoid ModuleSnapshotWin::FileVersion(uint16_t* version_0,\n                                    uint16_t* version_1,\n                                    uint16_t* version_2,\n                                    uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo();\n  if (ffi) {\n    *version_0 = ffi->dwFileVersionMS >> 16;\n    *version_1 = ffi->dwFileVersionMS & 0xffff;\n    *version_2 = ffi->dwFileVersionLS >> 16;\n    *version_3 = ffi->dwFileVersionLS & 0xffff;\n  } else {\n    *version_0 = 0;\n    *version_1 = 0;\n    *version_2 = 0;\n    *version_3 = 0;\n  }\n}\n\nvoid ModuleSnapshotWin::SourceVersion(uint16_t* version_0,\n                                      uint16_t* version_1,\n                                      uint16_t* version_2,\n                                      uint16_t* version_3) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo();\n  if (ffi) {\n    *version_0 = ffi->dwProductVersionMS >> 16;\n    *version_1 = ffi->dwProductVersionMS & 0xffff;\n    *version_2 = ffi->dwProductVersionLS >> 16;\n    *version_3 = ffi->dwProductVersionLS & 0xffff;\n  } else {\n    *version_0 = 0;\n    *version_1 = 0;\n    *version_2 = 0;\n    *version_3 = 0;\n  }\n}\n\nModuleSnapshot::ModuleType ModuleSnapshotWin::GetModuleType() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo();\n  if (ffi) {\n    switch (ffi->dwFileType) {\n      case VFT_APP:\n        return ModuleSnapshot::kModuleTypeExecutable;\n      case VFT_DLL:\n        return ModuleSnapshot::kModuleTypeSharedLibrary;\n      case VFT_DRV:\n      case VFT_VXD:\n        return ModuleSnapshot::kModuleTypeLoadableModule;\n    }\n  }\n  return ModuleSnapshot::kModuleTypeUnknown;\n}\n\nvoid ModuleSnapshotWin::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *uuid = uuid_;\n  *age = age_;\n}\n\nstd::string ModuleSnapshotWin::DebugFileName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return pdb_name_;\n}\n\nstd::vector<uint8_t> ModuleSnapshotWin::BuildID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return std::vector<uint8_t>();\n}\n\nstd::vector<std::string> ModuleSnapshotWin::AnnotationsVector() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // These correspond to system-logged things on Mac. We don't currently track\n  // any of these on Windows, but could in the future. See\n  // https://crashpad.chromium.org/bug/38.\n  return std::vector<std::string>();\n}\n\nstd::map<std::string, std::string> ModuleSnapshotWin::AnnotationsSimpleMap()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  PEImageAnnotationsReader annotations_reader(\n      process_reader_, pe_image_reader_.get(), name_);\n  return annotations_reader.SimpleMap();\n}\n\nstd::vector<AnnotationSnapshot> ModuleSnapshotWin::AnnotationObjects() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  PEImageAnnotationsReader annotations_reader(\n      process_reader_, pe_image_reader_.get(), name_);\n  return annotations_reader.AnnotationsList();\n}\n\nstd::set<CheckedRange<uint64_t>> ModuleSnapshotWin::ExtraMemoryRanges() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::set<CheckedRange<uint64_t>> ranges;\n  if (process_reader_->Is64Bit())\n    GetCrashpadExtraMemoryRanges<process_types::internal::Traits64>(&ranges);\n  else\n    GetCrashpadExtraMemoryRanges<process_types::internal::Traits32>(&ranges);\n  return ranges;\n}\n\nstd::vector<const UserMinidumpStream*>\nModuleSnapshotWin::CustomMinidumpStreams() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  streams_.clear();\n  if (process_reader_->Is64Bit()) {\n    GetCrashpadUserMinidumpStreams<process_types::internal::Traits64>(\n        &streams_);\n  } else {\n    GetCrashpadUserMinidumpStreams<process_types::internal::Traits32>(\n        &streams_);\n  }\n\n  std::vector<const UserMinidumpStream*> result;\n  for (const auto& stream : streams_) {\n    result.push_back(stream.get());\n  }\n  return result;\n}\n\ntemplate <class Traits>\nvoid ModuleSnapshotWin::GetCrashpadOptionsInternal(\n    CrashpadInfoClientOptions* options) {\n  if (!crashpad_info_) {\n    options->crashpad_handler_behavior = TriState::kUnset;\n    options->system_crash_reporter_forwarding = TriState::kUnset;\n    options->gather_indirectly_referenced_memory = TriState::kUnset;\n    options->indirectly_referenced_memory_cap = 0;\n    return;\n  }\n\n  options->crashpad_handler_behavior =\n      crashpad_info_->CrashpadHandlerBehavior();\n  options->system_crash_reporter_forwarding =\n      crashpad_info_->SystemCrashReporterForwarding();\n  options->gather_indirectly_referenced_memory =\n      crashpad_info_->GatherIndirectlyReferencedMemory();\n  options->indirectly_referenced_memory_cap =\n      crashpad_info_->IndirectlyReferencedMemoryCap();\n}\n\nconst VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (initialized_vs_fixed_file_info_.is_uninitialized()) {\n    initialized_vs_fixed_file_info_.set_invalid();\n    if (pe_image_reader_->VSFixedFileInfo(&vs_fixed_file_info_)) {\n      initialized_vs_fixed_file_info_.set_valid();\n    }\n  }\n\n  return initialized_vs_fixed_file_info_.is_valid() ? &vs_fixed_file_info_\n                                                    : nullptr;\n}\n\ntemplate <class Traits>\nvoid ModuleSnapshotWin::GetCrashpadExtraMemoryRanges(\n    std::set<CheckedRange<uint64_t>>* ranges) const {\n  if (!crashpad_info_ || !crashpad_info_->ExtraMemoryRanges())\n    return;\n\n  std::vector<SimpleAddressRangeBag::Entry> simple_ranges(\n      SimpleAddressRangeBag::num_entries);\n  if (!process_reader_->Memory()->Read(\n          crashpad_info_->ExtraMemoryRanges(),\n          simple_ranges.size() * sizeof(simple_ranges[0]),\n          &simple_ranges[0])) {\n    LOG(WARNING) << \"could not read simple address_ranges from \"\n                 << base::WideToUTF8(name_);\n    return;\n  }\n\n  for (const auto& entry : simple_ranges) {\n    if (entry.base != 0 || entry.size != 0) {\n      // Deduplication here is fine.\n      ranges->insert(CheckedRange<uint64_t>(entry.base, entry.size));\n    }\n  }\n}\n\ntemplate <class Traits>\nvoid ModuleSnapshotWin::GetCrashpadUserMinidumpStreams(\n    std::vector<std::unique_ptr<const UserMinidumpStream>>* streams) const {\n  if (!crashpad_info_)\n    return;\n\n  for (uint64_t cur = crashpad_info_->UserDataMinidumpStreamHead(); cur;) {\n    internal::UserDataMinidumpStreamListEntry list_entry;\n    if (!process_reader_->Memory()->Read(\n            cur, sizeof(list_entry), &list_entry)) {\n      LOG(WARNING) << \"could not read user data stream entry from \"\n                   << base::WideToUTF8(name_);\n      return;\n    }\n\n    if (list_entry.size != 0) {\n      std::unique_ptr<internal::MemorySnapshotGeneric> memory(\n          new internal::MemorySnapshotGeneric());\n      memory->Initialize(\n          process_reader_->Memory(), list_entry.base_address, list_entry.size);\n      streams->push_back(std::make_unique<UserMinidumpStream>(\n          list_entry.stream_type, memory.release()));\n    }\n\n    cur = list_entry.next;\n  }\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/module_snapshot_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_\n\n#include <windows.h>\n#include <stdint.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/crashpad_types/crashpad_info_reader.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"snapshot/win/process_reader_win.h\"\n#include \"util/misc/initialization_state.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/win/process_info.h\"\n\nnamespace crashpad {\n\nclass PEImageReader;\nstruct UUID;\n\nnamespace internal {\n\n//! \\brief A ModuleSnapshot of a code module (binary image) loaded into a\n//!     running (or crashed) process on a Windows system.\nclass ModuleSnapshotWin final : public ModuleSnapshot {\n public:\n  ModuleSnapshotWin();\n\n  ModuleSnapshotWin(const ModuleSnapshotWin&) = delete;\n  ModuleSnapshotWin& operator=(const ModuleSnapshotWin&) = delete;\n\n  ~ModuleSnapshotWin() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderWin for the process containing\n  //!     the module.\n  //! \\param[in] process_reader_module The module within the ProcessReaderWin\n  //!     for which the snapshot should be created.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(ProcessReaderWin* process_reader,\n                  const ProcessInfo::Module& process_reader_module);\n\n  //! \\brief Returns options from the module's CrashpadInfo structure.\n  //!\n  //! \\param[out] options Options set in the module's CrashpadInfo structure.\n  void GetCrashpadOptions(CrashpadInfoClientOptions* options);\n\n  //! \\brief Returns the PEImageReader used to read this module. Only valid\n  //!     after Initialize() is called.\n  const PEImageReader& pe_image_reader() const { return *pe_image_reader_; }\n\n  // ModuleSnapshot:\n\n  std::string Name() const override;\n  uint64_t Address() const override;\n  uint64_t Size() const override;\n  time_t Timestamp() const override;\n  void FileVersion(uint16_t* version_0,\n                   uint16_t* version_1,\n                   uint16_t* version_2,\n                   uint16_t* version_3) const override;\n  void SourceVersion(uint16_t* version_0,\n                     uint16_t* version_1,\n                     uint16_t* version_2,\n                     uint16_t* version_3) const override;\n  ModuleType GetModuleType() const override;\n  void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;\n  std::string DebugFileName() const override;\n  std::vector<uint8_t> BuildID() const override;\n  std::vector<std::string> AnnotationsVector() const override;\n  std::map<std::string, std::string> AnnotationsSimpleMap() const override;\n  std::vector<AnnotationSnapshot> AnnotationObjects() const override;\n  std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;\n  std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;\n\n private:\n  template <class Traits>\n  void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options);\n\n  template <class Traits>\n  void GetCrashpadExtraMemoryRanges(\n      std::set<CheckedRange<uint64_t>>* ranges) const;\n\n  template <class Traits>\n  void GetCrashpadUserMinidumpStreams(\n      std::vector<std::unique_ptr<const UserMinidumpStream>>* streams) const;\n\n  // Initializes vs_fixed_file_info_ if it has not yet been initialized, and\n  // returns a pointer to it. Returns nullptr on failure, with a message logged\n  // on the first call.\n  const VS_FIXEDFILEINFO* VSFixedFileInfo() const;\n\n  std::wstring name_;\n  std::string pdb_name_;\n  UUID uuid_;\n  ProcessMemoryRange memory_range_;\n  // Too const-y: https://crashpad.chromium.org/bug/9.\n  mutable std::vector<std::unique_ptr<const UserMinidumpStream>> streams_;\n  // VSFixedFileInfo() is logically const, but updates these members on the\n  // call. See https://crashpad.chromium.org/bug/9.\n  mutable VS_FIXEDFILEINFO vs_fixed_file_info_;\n  mutable InitializationState initialized_vs_fixed_file_info_;\n  ProcessReaderWin* process_reader_;  // weak\n  std::unique_ptr<PEImageReader> pe_image_reader_;\n  std::unique_ptr<CrashpadInfoReader> crashpad_info_;\n  time_t timestamp_;\n  uint32_t age_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_\n"
  },
  {
    "path": "snapshot/win/module_snapshot_win_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/module_snapshot_win.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/annotation_snapshot.h\"\n#include \"snapshot/win/pe_image_reader.h\"\n#include \"snapshot/win/process_reader_win.h\"\n#include \"test/test_paths.h\"\n#include \"test/win/child_launcher.h\"\n#include \"util/file/file_io.h\"\n#include \"util/win/process_info.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nenum TestType {\n  // Don't crash, just test the CrashpadInfo interface.\n  kDontCrash = 0,\n\n  // The child process should crash by __debugbreak().\n  kCrashDebugBreak,\n};\n\nvoid TestAnnotationsOnCrash(TestType type,\n                            TestPaths::Architecture architecture) {\n  // Spawn a child process, passing it the pipe name to connect to.\n  base::FilePath child_test_executable =\n      TestPaths::BuildArtifact(L\"snapshot\",\n                               L\"annotations\",\n                               TestPaths::FileType::kExecutable,\n                               architecture);\n  ChildLauncher child(child_test_executable, L\"\");\n  ASSERT_NO_FATAL_FAILURE(child.Start());\n\n  // Wait for the child process to indicate that it's done setting up its\n  // annotations via the CrashpadInfo interface.\n  char c;\n  CheckedReadFileExactly(child.stdout_read_handle(), &c, sizeof(c));\n\n  ProcessReaderWin process_reader;\n  ASSERT_TRUE(process_reader.Initialize(child.process_handle(),\n                                        ProcessSuspensionState::kRunning));\n\n  // Read all the kinds of annotations referenced from the CrashpadInfo\n  // structure.\n  const std::vector<ProcessInfo::Module>& modules = process_reader.Modules();\n  std::map<std::string, std::string> all_annotations_simple_map;\n  std::vector<AnnotationSnapshot> all_annotation_objects;\n  for (const ProcessInfo::Module& module : modules) {\n    internal::ModuleSnapshotWin module_snapshot;\n    module_snapshot.Initialize(&process_reader, module);\n\n    std::map<std::string, std::string> module_annotations_simple_map =\n        module_snapshot.AnnotationsSimpleMap();\n    all_annotations_simple_map.insert(module_annotations_simple_map.begin(),\n                                      module_annotations_simple_map.end());\n\n    auto module_annotations_list = module_snapshot.AnnotationObjects();\n    all_annotation_objects.insert(all_annotation_objects.end(),\n                                  module_annotations_list.begin(),\n                                  module_annotations_list.end());\n  }\n\n  // Verify the \"simple map\" annotations.\n  EXPECT_GE(all_annotations_simple_map.size(), 5u);\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# pad\"], \"crash\");\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# key\"], \"value\");\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# x\"], \"y\");\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# longer\"], \"shorter\");\n  EXPECT_EQ(all_annotations_simple_map[\"#TEST# empty_value\"], \"\");\n\n  // Verify the typed annotation objects.\n  EXPECT_EQ(all_annotation_objects.size(), 3u);\n  bool saw_same_name_3 = false, saw_same_name_4 = false;\n  for (const auto& annotation : all_annotation_objects) {\n    EXPECT_EQ(annotation.type,\n              static_cast<uint16_t>(Annotation::Type::kString));\n    std::string value(reinterpret_cast<const char*>(annotation.value.data()),\n                      annotation.value.size());\n\n    if (annotation.name == \"#TEST# one\") {\n      EXPECT_EQ(value, \"moocow\");\n    } else if (annotation.name == \"#TEST# same-name\") {\n      if (value == \"same-name 3\") {\n        EXPECT_FALSE(saw_same_name_3);\n        saw_same_name_3 = true;\n      } else if (value == \"same-name 4\") {\n        EXPECT_FALSE(saw_same_name_4);\n        saw_same_name_4 = true;\n      } else {\n        ADD_FAILURE() << \"unexpected annotation value \" << value;\n      }\n    } else {\n      ADD_FAILURE() << \"unexpected annotation \" << annotation.name;\n    }\n  }\n\n  // Tell the child process to continue.\n  DWORD expected_exit_code;\n  switch (type) {\n    case kDontCrash:\n      c = ' ';\n      expected_exit_code = 0;\n      break;\n    case kCrashDebugBreak:\n      c = 'd';\n      expected_exit_code = STATUS_BREAKPOINT;\n      break;\n    default:\n      FAIL();\n  }\n  CheckedWriteFile(child.stdin_write_handle(), &c, sizeof(c));\n\n  EXPECT_EQ(child.WaitForExit(), expected_exit_code);\n}\n\nTEST(ModuleSnapshotWinTest, DontCrash) {\n  TestAnnotationsOnCrash(kDontCrash, TestPaths::Architecture::kDefault);\n}\n\nTEST(ModuleSnapshotWinTest, CrashDebugBreak) {\n  TestAnnotationsOnCrash(kCrashDebugBreak, TestPaths::Architecture::kDefault);\n}\n\n#if defined(ARCH_CPU_64_BITS)\nTEST(ModuleSnapshotWinTest, DontCrashWOW64) {\n  if (!TestPaths::Has32BitBuildArtifacts()) {\n    GTEST_SKIP();\n  }\n\n  TestAnnotationsOnCrash(kDontCrash, TestPaths::Architecture::k32Bit);\n}\n\nTEST(ModuleSnapshotWinTest, CrashDebugBreakWOW64) {\n  if (!TestPaths::Has32BitBuildArtifacts()) {\n    GTEST_SKIP();\n  }\n\n  TestAnnotationsOnCrash(kCrashDebugBreak, TestPaths::Architecture::k32Bit);\n}\n#endif  // ARCH_CPU_64_BITS\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/pe_image_annotations_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/pe_image_annotations_reader.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/logging.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"client/annotation.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"snapshot/snapshot_constants.h\"\n#include \"snapshot/win/pe_image_reader.h\"\n#include \"snapshot/win/process_reader_win.h\"\n#include \"util/win/process_structs.h\"\n\nnamespace crashpad {\n\nnamespace process_types {\n\ntemplate <class Traits>\nstruct Annotation {\n  typename Traits::Pointer link_node;\n  typename Traits::Pointer name;\n  typename Traits::Pointer value;\n  uint32_t size;\n  uint16_t type;\n};\n\ntemplate <class Traits>\nstruct AnnotationList {\n  typename Traits::Pointer tail_pointer;\n  Annotation<Traits> head;\n  Annotation<Traits> tail;\n};\n\n}  // namespace process_types\n\nPEImageAnnotationsReader::PEImageAnnotationsReader(\n    ProcessReaderWin* process_reader,\n    const PEImageReader* pe_image_reader,\n    const std::wstring& name)\n    : name_(name),\n      process_reader_(process_reader),\n      pe_image_reader_(pe_image_reader) {\n}\n\nstd::map<std::string, std::string> PEImageAnnotationsReader::SimpleMap() const {\n  std::map<std::string, std::string> simple_map_annotations;\n  if (process_reader_->Is64Bit()) {\n    ReadCrashpadSimpleAnnotations<process_types::internal::Traits64>(\n        &simple_map_annotations);\n  } else {\n    ReadCrashpadSimpleAnnotations<process_types::internal::Traits32>(\n        &simple_map_annotations);\n  }\n  return simple_map_annotations;\n}\n\nstd::vector<AnnotationSnapshot> PEImageAnnotationsReader::AnnotationsList()\n    const {\n  std::vector<AnnotationSnapshot> annotations;\n  if (process_reader_->Is64Bit()) {\n    ReadCrashpadAnnotationsList<process_types::internal::Traits64>(\n        &annotations);\n  } else {\n    ReadCrashpadAnnotationsList<process_types::internal::Traits32>(\n        &annotations);\n  }\n  return annotations;\n}\n\ntemplate <class Traits>\nvoid PEImageAnnotationsReader::ReadCrashpadSimpleAnnotations(\n    std::map<std::string, std::string>* simple_map_annotations) const {\n  process_types::CrashpadInfo<Traits> crashpad_info;\n  if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) ||\n      !crashpad_info.simple_annotations) {\n    return;\n  }\n\n  std::vector<SimpleStringDictionary::Entry>\n      simple_annotations(SimpleStringDictionary::num_entries);\n  if (!process_reader_->Memory()->Read(\n          crashpad_info.simple_annotations,\n          simple_annotations.size() * sizeof(simple_annotations[0]),\n          &simple_annotations[0])) {\n    LOG(WARNING) << \"could not read simple annotations from \"\n                 << base::WideToUTF8(name_);\n    return;\n  }\n\n  for (const auto& entry : simple_annotations) {\n    size_t key_length = strnlen(entry.key, sizeof(entry.key));\n    if (key_length) {\n      std::string key(entry.key, key_length);\n      std::string value(entry.value, strnlen(entry.value, sizeof(entry.value)));\n      if (!simple_map_annotations->insert(std::make_pair(key, value)).second) {\n        LOG(INFO) << \"duplicate simple annotation \" << key << \" in \"\n                  << base::WideToUTF8(name_);\n      }\n    }\n  }\n}\n\n// TODO(rsesek): When there is a platform-agnostic remote memory reader\n// interface available, use it so that the implementation is not duplicated\n// in the MachOImageAnnotationsReader.\ntemplate <class Traits>\nvoid PEImageAnnotationsReader::ReadCrashpadAnnotationsList(\n    std::vector<AnnotationSnapshot>* vector_annotations) const {\n  process_types::CrashpadInfo<Traits> crashpad_info;\n  if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) ||\n      !crashpad_info.annotations_list) {\n    return;\n  }\n\n  process_types::AnnotationList<Traits> annotation_list_object;\n  if (!process_reader_->Memory()->Read(crashpad_info.annotations_list,\n                                       sizeof(annotation_list_object),\n                                       &annotation_list_object)) {\n    LOG(WARNING) << \"could not read annotations list object in \"\n                 << base::WideToUTF8(name_);\n    return;\n  }\n\n  process_types::Annotation<Traits> current = annotation_list_object.head;\n  for (size_t index = 0;\n       current.link_node != annotation_list_object.tail_pointer &&\n       index < kMaxNumberOfAnnotations;\n       ++index) {\n    if (!process_reader_->Memory()->Read(\n            current.link_node, sizeof(current), &current)) {\n      LOG(WARNING) << \"could not read annotation at index \" << index << \" in \"\n                   << base::WideToUTF8(name_);\n      return;\n    }\n\n    if (current.size == 0) {\n      continue;\n    }\n\n    AnnotationSnapshot snapshot;\n    snapshot.type = current.type;\n\n    char name[Annotation::kNameMaxLength];\n    if (!process_reader_->Memory()->Read(current.name, std::size(name), name)) {\n      LOG(WARNING) << \"could not read annotation name at index \" << index\n                   << \" in \" << base::WideToUTF8(name_);\n      continue;\n    }\n\n    size_t name_length = strnlen(name, Annotation::kNameMaxLength);\n    snapshot.name = std::string(name, name_length);\n\n    size_t value_length =\n        std::min(static_cast<size_t>(current.size), Annotation::kValueMaxSize);\n    snapshot.value.resize(value_length);\n    if (!process_reader_->Memory()->Read(\n            current.value, value_length, snapshot.value.data())) {\n      LOG(WARNING) << \"could not read annotation value at index \" << index\n                   << \" in \" << base::WideToUTF8(name_);\n      continue;\n    }\n\n    vector_annotations->push_back(std::move(snapshot));\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/pe_image_annotations_reader.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_\n#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"snapshot/annotation_snapshot.h\"\n\nnamespace crashpad {\n\nclass PEImageReader;\nclass ProcessReaderWin;\n\n//! \\brief A reader of annotations stored in a PE image mapped into another\n//!     process.\n//!\n//! These annotations are stored for the benefit of crash reporters, and provide\n//! information thought to be potentially useful for crash analysis.\n//!\n//! Currently, this class can decode information stored only in the CrashpadInfo\n//! structure. This format is used by Crashpad clients. The \"simple annotations\"\n//! are recovered from any module with a compatible data section, and are\n//! included in the annotations returned by SimpleMap().\nclass PEImageAnnotationsReader {\n public:\n  //! \\brief Constructs the object.\n  //!\n  //! \\param[in] process_reader The reader for the remote process.\n  //! \\param[in] pe_image_reader The PEImageReader for the PE image file\n  //!     contained within the remote process.\n  //! \\param[in] name The module's name, a string to be used in logged messages.\n  //!     This string is for diagnostic purposes only, and may be empty.\n  PEImageAnnotationsReader(ProcessReaderWin* process_reader,\n                           const PEImageReader* pe_image_reader,\n                           const std::wstring& name);\n\n  PEImageAnnotationsReader(const PEImageAnnotationsReader&) = delete;\n  PEImageAnnotationsReader& operator=(const PEImageAnnotationsReader&) = delete;\n\n  ~PEImageAnnotationsReader() {}\n\n  //! \\brief Returns the module's annotations that are organized as key-value\n  //!     pairs, where all keys and values are strings.\n  std::map<std::string, std::string> SimpleMap() const;\n\n  //! \\brief Returns the module's annotations that are organized as a list of\n  //!     typed annotation objects.\n  std::vector<AnnotationSnapshot> AnnotationsList() const;\n\n private:\n  // Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap().\n  template <class Traits>\n  void ReadCrashpadSimpleAnnotations(\n      std::map<std::string, std::string>* simple_map_annotations) const;\n\n  // Reads CrashpadInfo::annotations_list_ on behalf of AnnotationsList().\n  template <class Traits>\n  void ReadCrashpadAnnotationsList(\n      std::vector<AnnotationSnapshot>* vector_annotations) const;\n\n  std::wstring name_;\n  ProcessReaderWin* process_reader_;  // weak\n  const PEImageReader* pe_image_reader_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_\n"
  },
  {
    "path": "snapshot/win/pe_image_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/pe_image_reader.h\"\n\n#include <stddef.h>\n#include <string.h>\n\n#include <algorithm>\n#include <iterator>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"client/crashpad_info.h\"\n#include \"snapshot/win/pe_image_resource_reader.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/pdb_structures.h\"\n#include \"util/win/process_structs.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Map from Traits to an IMAGE_NT_HEADERSxx.\ntemplate <class Traits>\nstruct NtHeadersForTraits;\n\ntemplate <>\nstruct NtHeadersForTraits<process_types::internal::Traits32> {\n  using type = IMAGE_NT_HEADERS32;\n};\n\ntemplate <>\nstruct NtHeadersForTraits<process_types::internal::Traits64> {\n  using type = IMAGE_NT_HEADERS64;\n};\n\n}  // namespace\n\nPEImageReader::PEImageReader()\n    : module_subrange_reader_(),\n      initialized_() {\n}\n\nPEImageReader::~PEImageReader() {\n}\n\nbool PEImageReader::Initialize(ProcessReaderWin* process_reader,\n                               WinVMAddress address,\n                               WinVMSize size,\n                               const std::string& module_name) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!module_subrange_reader_.Initialize(\n          process_reader, address, size, module_name)) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool PEImageReader::GetCrashpadInfoSection(WinVMAddress* address,\n                                           WinVMSize* size) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (module_subrange_reader_.Is64Bit()) {\n    return GetCrashpadInfoSectionInternal<process_types::internal::Traits64>(\n        address, size);\n  } else {\n    return GetCrashpadInfoSectionInternal<process_types::internal::Traits32>(\n        address, size);\n  }\n}\n\ntemplate <class Traits>\nbool PEImageReader::GetCrashpadInfo(\n    process_types::CrashpadInfo<Traits>* crashpad_info) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  IMAGE_SECTION_HEADER section;\n  if (!GetSectionByName<typename NtHeadersForTraits<Traits>::type>(\"CPADinfo\",\n                                                                   &section)) {\n    return false;\n  }\n\n  if (section.Misc.VirtualSize <\n      offsetof(process_types::CrashpadInfo<Traits>, size) +\n          sizeof(crashpad_info->size)) {\n    LOG(WARNING) << \"small crashpad info section size \"\n                 << section.Misc.VirtualSize << \", \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  const WinVMAddress crashpad_info_address = Address() + section.VirtualAddress;\n  const WinVMSize crashpad_info_size =\n      std::min(static_cast<WinVMSize>(sizeof(*crashpad_info)),\n               static_cast<WinVMSize>(section.Misc.VirtualSize));\n  if (!module_subrange_reader_.ReadMemory(\n          crashpad_info_address, crashpad_info_size, crashpad_info)) {\n    LOG(WARNING) << \"could not read crashpad info from \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  if (crashpad_info->size < sizeof(*crashpad_info)) {\n    // Zero out anything beyond the structure’s declared size.\n    memset(reinterpret_cast<char*>(crashpad_info) + crashpad_info->size,\n           0,\n           sizeof(*crashpad_info) - crashpad_info->size);\n  }\n\n  if (crashpad_info->signature != CrashpadInfo::kSignature ||\n      crashpad_info->version != 1) {\n    LOG(WARNING) << base::StringPrintf(\n        \"unexpected crashpad info signature 0x%x, version %u in %s\",\n        crashpad_info->signature,\n        crashpad_info->version,\n        module_subrange_reader_.name().c_str());\n    return false;\n  }\n\n  // Don’t require strict equality, to leave wiggle room for sloppy linkers.\n  if (crashpad_info->size > section.Misc.VirtualSize) {\n    LOG(WARNING) << \"crashpad info struct size \" << crashpad_info->size\n                 << \" large for section size \" << section.Misc.VirtualSize\n                 << \" in \" << module_subrange_reader_.name();\n    return false;\n  }\n\n  if (crashpad_info->size > sizeof(*crashpad_info)) {\n    // This isn’t strictly a problem, because unknown fields will simply be\n    // ignored, but it may be of diagnostic interest.\n    LOG(INFO) << \"large crashpad info size \" << crashpad_info->size << \", \"\n              << module_subrange_reader_.name();\n  }\n\n  return true;\n}\n\nbool PEImageReader::DebugDirectoryInformation(UUID* uuid,\n                                              DWORD* age,\n                                              std::string* pdbname) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  IMAGE_DATA_DIRECTORY data_directory;\n  if (!ImageDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG, &data_directory))\n    return false;\n\n  IMAGE_DEBUG_DIRECTORY debug_directory;\n  if (data_directory.Size % sizeof(debug_directory) != 0)\n    return false;\n  for (size_t offset = 0; offset < data_directory.Size;\n       offset += sizeof(debug_directory)) {\n    if (!module_subrange_reader_.ReadMemory(\n            Address() + data_directory.VirtualAddress + offset,\n            sizeof(debug_directory),\n            &debug_directory)) {\n      LOG(WARNING) << \"could not read data directory from \"\n                   << module_subrange_reader_.name();\n      return false;\n    }\n\n    if (debug_directory.Type != IMAGE_DEBUG_TYPE_CODEVIEW)\n      continue;\n\n    if (debug_directory.AddressOfRawData) {\n      if (debug_directory.SizeOfData < sizeof(CodeViewRecordPDB70)) {\n        LOG(WARNING) << \"CodeView debug entry of unexpected size in \"\n                     << module_subrange_reader_.name();\n        continue;\n      }\n\n      auto data = base::HeapArray<char>::Uninit(debug_directory.SizeOfData);\n      if (!module_subrange_reader_.ReadMemory(\n              Address() + debug_directory.AddressOfRawData,\n              data.size(),\n              data.data())) {\n        LOG(WARNING) << \"could not read debug directory from \"\n                     << module_subrange_reader_.name();\n        return false;\n      }\n\n      if (*reinterpret_cast<DWORD*>(data.data()) !=\n          CodeViewRecordPDB70::kSignature) {\n        LOG(WARNING) << \"encountered non-7.0 CodeView debug record in \"\n                     << module_subrange_reader_.name();\n        continue;\n      }\n\n      CodeViewRecordPDB70* codeview =\n          reinterpret_cast<CodeViewRecordPDB70*>(data.data());\n      *uuid = codeview->uuid;\n      *age = codeview->age;\n      // This is a NUL-terminated string encoded in the codepage of the system\n      // where the binary was linked. We have no idea what that was, so we just\n      // assume ASCII.\n      *pdbname = std::string(reinterpret_cast<char*>(&codeview->pdb_name[0]));\n      return true;\n    } else if (debug_directory.PointerToRawData) {\n      // This occurs for non-PDB based debug information. We simply ignore these\n      // as we don't expect to encounter modules that will be in this format\n      // for which we'll actually have symbols. See\n      // https://crashpad.chromium.org/bug/47.\n    }\n  }\n\n  return false;\n}\n\nbool PEImageReader::VSFixedFileInfo(\n    VS_FIXEDFILEINFO* vs_fixed_file_info) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  IMAGE_DATA_DIRECTORY data_directory;\n  if (!ImageDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE,\n                               &data_directory)) {\n    return false;\n  }\n\n  PEImageResourceReader resource_reader;\n  if (!resource_reader.Initialize(module_subrange_reader_, data_directory)) {\n    return false;\n  }\n\n  WinVMAddress address;\n  WinVMSize size;\n  if (!resource_reader.FindResourceByID(\n          FromPointerCast<uint16_t>(VS_FILE_INFO),  // RT_VERSION\n          VS_VERSION_INFO,\n          MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),\n          &address,\n          &size,\n          nullptr)) {\n    return false;\n  }\n\n  // This structure is not declared anywhere in the SDK, but is documented at\n  // https://msdn.microsoft.com/library/ms647001.aspx.\n  struct VS_VERSIONINFO {\n    WORD wLength;\n    WORD wValueLength;\n    WORD wType;\n\n    // The structure documentation on MSDN doesn’t show the [16], but it does\n    // say that it’s supposed to be L\"VS_VERSION_INFO\", which is is in fact a\n    // 16-character string (including its NUL terminator).\n    WCHAR szKey[16];\n\n    WORD Padding1;\n    VS_FIXEDFILEINFO Value;\n\n    // Don’t include Children or the Padding2 that precedes it, because they may\n    // not be present.\n    // WORD Padding2;\n    // WORD Children;\n  };\n  VS_VERSIONINFO version_info;\n\n  if (size < sizeof(version_info)) {\n    LOG(WARNING) << \"version info size \" << size\n                 << \" too small for structure of size \" << sizeof(version_info)\n                 << \" in \" << module_subrange_reader_.name();\n    return false;\n  }\n\n  if (!module_subrange_reader_.ReadMemory(\n          address, sizeof(version_info), &version_info)) {\n    LOG(WARNING) << \"could not read version info from \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  if (version_info.wLength < sizeof(version_info) ||\n      version_info.wValueLength != sizeof(version_info.Value) ||\n      version_info.wType != 0 ||\n      wcsncmp(version_info.szKey,\n              L\"VS_VERSION_INFO\",\n              std::size(version_info.szKey)) != 0) {\n    LOG(WARNING) << \"unexpected VS_VERSIONINFO in \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  if (version_info.Value.dwSignature != VS_FFI_SIGNATURE ||\n      version_info.Value.dwStrucVersion != VS_FFI_STRUCVERSION) {\n    LOG(WARNING) << \"unexpected VS_FIXEDFILEINFO in \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  *vs_fixed_file_info = version_info.Value;\n  vs_fixed_file_info->dwFileFlags &= vs_fixed_file_info->dwFileFlagsMask;\n  return true;\n}\n\ntemplate <class Traits>\nbool PEImageReader::GetCrashpadInfoSectionInternal(WinVMAddress* address,\n                                                   WinVMSize* size) const {\n  IMAGE_SECTION_HEADER section;\n  if (!GetSectionByName<typename NtHeadersForTraits<Traits>::type>(\"CPADinfo\",\n                                                                   &section)) {\n    return false;\n  }\n\n  process_types::CrashpadInfo<Traits> crashpad_info;\n  if (section.Misc.VirtualSize <\n      offsetof(process_types::CrashpadInfo<Traits>, size) +\n          sizeof(crashpad_info.size)) {\n    LOG(WARNING) << \"small crashpad info section size \"\n                 << section.Misc.VirtualSize << \", \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  *address = Address() + section.VirtualAddress;\n  *size = std::min<WinVMSize>(sizeof(crashpad_info), section.Misc.VirtualSize);\n\n  return true;\n}\n\ntemplate <class NtHeadersType>\nbool PEImageReader::ReadNtHeaders(NtHeadersType* nt_headers,\n                                  WinVMAddress* nt_headers_address) const {\n  IMAGE_DOS_HEADER dos_header;\n  if (!module_subrange_reader_.ReadMemory(\n          Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) {\n    LOG(WARNING) << \"could not read dos header from \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) {\n    LOG(WARNING) << \"invalid e_magic in dos header of \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  WinVMAddress local_nt_headers_address = Address() + dos_header.e_lfanew;\n  if (!module_subrange_reader_.ReadMemory(\n          local_nt_headers_address, sizeof(NtHeadersType), nt_headers)) {\n    LOG(WARNING) << \"could not read nt headers from \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {\n    LOG(WARNING) << \"invalid signature in nt headers of \"\n                 << module_subrange_reader_.name();\n    return false;\n  }\n\n  if (nt_headers_address)\n    *nt_headers_address = local_nt_headers_address;\n\n  return true;\n}\n\ntemplate <class NtHeadersType>\nbool PEImageReader::GetSectionByName(const std::string& name,\n                                     IMAGE_SECTION_HEADER* section) const {\n  if (name.size() > sizeof(section->Name)) {\n    LOG(WARNING) << \"supplied section name too long \" << name;\n    return false;\n  }\n\n  NtHeadersType nt_headers;\n  WinVMAddress nt_headers_address;\n  if (!ReadNtHeaders(&nt_headers, &nt_headers_address))\n    return false;\n\n  WinVMAddress first_section_address =\n      nt_headers_address + offsetof(NtHeadersType, OptionalHeader) +\n      nt_headers.FileHeader.SizeOfOptionalHeader;\n  for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) {\n    WinVMAddress section_address =\n        first_section_address + sizeof(IMAGE_SECTION_HEADER) * i;\n    if (!module_subrange_reader_.ReadMemory(\n            section_address, sizeof(IMAGE_SECTION_HEADER), section)) {\n      LOG(WARNING) << \"could not read section \" << i << \" from \"\n                   << module_subrange_reader_.name();\n      return false;\n    }\n    if (strncmp(reinterpret_cast<const char*>(section->Name),\n                name.c_str(),\n                sizeof(section->Name)) == 0) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nbool PEImageReader::ImageDataDirectoryEntry(size_t index,\n                                            IMAGE_DATA_DIRECTORY* entry) const {\n  bool rv;\n  if (module_subrange_reader_.Is64Bit()) {\n    rv = ImageDataDirectoryEntryT<IMAGE_NT_HEADERS64>(index, entry);\n  } else {\n    rv = ImageDataDirectoryEntryT<IMAGE_NT_HEADERS32>(index, entry);\n  }\n\n  return rv && entry->VirtualAddress != 0 && entry->Size != 0;\n}\n\ntemplate <class NtHeadersType>\nbool PEImageReader::ImageDataDirectoryEntryT(\n    size_t index,\n    IMAGE_DATA_DIRECTORY* entry) const {\n  NtHeadersType nt_headers;\n  if (!ReadNtHeaders(&nt_headers, nullptr)) {\n    return false;\n  }\n\n  if (nt_headers.FileHeader.SizeOfOptionalHeader <\n          offsetof(decltype(nt_headers.OptionalHeader), DataDirectory[index]) +\n              sizeof(nt_headers.OptionalHeader.DataDirectory[index]) ||\n      nt_headers.OptionalHeader.NumberOfRvaAndSizes <= index) {\n    return false;\n  }\n\n  *entry = nt_headers.OptionalHeader.DataDirectory[index];\n  return true;\n}\n\n// Explicit instantiations with the only 2 valid template arguments to avoid\n// putting the body of the function in the header.\ntemplate bool PEImageReader::GetCrashpadInfo<process_types::internal::Traits32>(\n    process_types::CrashpadInfo<process_types::internal::Traits32>*\n        crashpad_info) const;\ntemplate bool PEImageReader::GetCrashpadInfo<process_types::internal::Traits64>(\n    process_types::CrashpadInfo<process_types::internal::Traits64>*\n        crashpad_info) const;\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/pe_image_reader.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_\n#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_\n\n#include <windows.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <string>\n\n#include \"snapshot/win/process_subrange_reader.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/process_structs.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderWin;\n\nnamespace process_types {\n\ntemplate <class Traits>\nstruct CrashpadInfo {\n  uint32_t signature;\n  uint32_t size;\n  uint32_t version;\n  uint32_t indirectly_referenced_memory_cap;\n  uint32_t padding_0;\n  uint8_t crashpad_handler_behavior;  // TriState.\n  uint8_t system_crash_reporter_forwarding;  // TriState.\n  uint8_t gather_indirectly_referenced_memory;  // TriState.\n  uint8_t padding_1;\n  typename Traits::Pointer extra_address_ranges;\n  typename Traits::Pointer simple_annotations;\n  typename Traits::Pointer user_data_minidump_stream_head;\n  typename Traits::Pointer annotations_list;\n};\n\n}  // namespace process_types\n\n//! \\brief A reader for PE images mapped into another process.\n//!\n//! This class is capable of reading both 32-bit and 64-bit images based on the\n//! bitness of the remote process.\n//!\n//! \\sa PEImageAnnotationsReader\n//! \\sa PEImageResourceReader\nclass PEImageReader {\n public:\n  PEImageReader();\n\n  PEImageReader(const PEImageReader&) = delete;\n  PEImageReader& operator=(const PEImageReader&) = delete;\n\n  ~PEImageReader();\n\n  //! \\brief Initializes the reader.\n  //!\n  //! This method must be called only once on an object. This method must be\n  //! called successfully before any other method in this class may be called.\n  //!\n  //! \\param[in] process_reader The reader for the remote process.\n  //! \\param[in] address The address, in the remote process' address space,\n  //!     where the `IMAGE_DOS_HEADER` is located.\n  //! \\param[in] size The size of the image.\n  //! \\param[in] module_name The module's name, a string to be used in logged\n  //!     messages. This string is for diagnostic purposes.\n  //!\n  //! \\return `true` if the image was read successfully, `false` otherwise, with\n  //!     an appropriate message logged.\n  bool Initialize(ProcessReaderWin* process_reader,\n                  WinVMAddress address,\n                  WinVMSize size,\n                  const std::string& module_name);\n\n  //! \\brief Returns the image's load address.\n  //!\n  //! This is the value passed as \\a address to Initialize().\n  WinVMAddress Address() const { return module_subrange_reader_.Base(); }\n\n  //! \\brief Returns the image's size.\n  //!\n  //! This is the value passed as \\a size to Initialize().\n  WinVMSize Size() const { return module_subrange_reader_.Size(); }\n\n  //! \\brief Obtains the module's CrashpadInfo structure address and size.\n  //!\n  //! \\param[out] address The CrashpadInfo structure address.\n  //! \\param[out] size The CrashpadInfo structure size.\n  //!\n  //! \\return `true` on success, `false` on failure. If the module does not have\n  //!     a `CPADinfo` section, this will return `false` without logging any\n  //!     messages. Other failures will result in messages being logged.\n  bool GetCrashpadInfoSection(WinVMAddress* address, WinVMSize* size) const;\n\n  //! \\brief Obtains the module's CrashpadInfo structure.\n  //!\n  //! \\return `true` on success, `false` on failure. If the module does not have\n  //!     a `CPADinfo` section, this will return `false` without logging any\n  //!     messages. Other failures will result in messages being logged.\n  template <class Traits>\n  bool GetCrashpadInfo(\n      process_types::CrashpadInfo<Traits>* crashpad_info) const;\n\n  //! \\brief Obtains information from the module's debug directory, if any.\n  //!\n  //! \\param[out] uuid The unique identifier of the executable/PDB.\n  //! \\param[out] age The age field for the pdb (the number of times it's been\n  //!     relinked).\n  //! \\param[out] pdbname Name of the pdb file.\n  //!\n  //! \\return `true` on success, with the parameters set appropriately. `false`\n  //!     on failure. This method may return `false` without logging anything in\n  //!     the case of a module that does not contain relevant debugging\n  //!     information but is otherwise properly structured.\n  bool DebugDirectoryInformation(UUID* uuid,\n                                 DWORD* age,\n                                 std::string* pdbname) const;\n\n  //! \\brief Obtains the module’s `VS_FIXEDFILEINFO`, containing its version and\n  //!     type information.\n  //!\n  //! The data obtained from this method should be equivalent to what could be\n  //! obtained by calling GetModuleVersionAndType(). Avoiding that function\n  //! ensures that the data in the module loaded into the remote process will be\n  //! used as-is, without the risks associated with loading the module into the\n  //! reading process.\n  //!\n  //! \\param[out] vs_fixed_file_info The VS_FIXEDFILEINFO on success.\n  //!     VS_FIXEDFILEINFO::dwFileFlags will have been masked with\n  //!     VS_FIXEDFILEINFO::dwFileFlagsMask already.\n  //!\n  //! \\return `true` on success. `false` if the module does not contain this\n  //!     information, without logging any messages. `false` on failure, with\n  //!     a message logged.\n  bool VSFixedFileInfo(VS_FIXEDFILEINFO* vs_fixed_file_info) const;\n\n private:\n  //! \\brief Performs the internal logic for GetCrashpadInfoSection().\n  //!\n  //! \\sa GetCrashpadInfoSection\n  template <class Traits>\n  bool GetCrashpadInfoSectionInternal(WinVMAddress* address,\n                                      WinVMSize* size) const;\n\n  //! \\brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image.\n  //!\n  //! \\param[out] nt_headers The contents of the templated NtHeadersType\n  //!     structure read from the remote process.\n  //! \\param[out] nt_headers_address The address of the templated NtHeadersType\n  //!     structure in the remote process’ address space. If this information is\n  //!     not needed, this parameter may be `nullptr`.\n  //!\n  //! \\return `true` on success, with \\a nt_headers and optionally \\a\n  //!     nt_headers_address set appropriately. `false` on failure, with a\n  //!     message logged.\n  template <class NtHeadersType>\n  bool ReadNtHeaders(NtHeadersType* nt_headers,\n                     WinVMAddress* nt_headers_address) const;\n\n  //! \\brief Finds a given section by name in the image.\n  template <class NtHeadersType>\n  bool GetSectionByName(const std::string& name,\n                        IMAGE_SECTION_HEADER* section) const;\n\n  //! \\brief Finds the `IMAGE_DATA_DIRECTORY` in\n  //!     `IMAGE_OPTIONAL_HEADER::DataDirectory` at the specified \\a index.\n  //!\n  //! \\param[in] index An `IMAGE_DIRECTORY_ENTRY_*` constant specifying the\n  //!     data to be returned.\n  //! \\param[out] entry The `IMAGE_DATA_DIRECTORY` found within the module.\n  //!\n  //! \\return `true` on success, with \\a entry set appropriately. `false` if the\n  //!     module does not contain the specified information, without logging a\n  //!     message. `false` on failure, with a message logged.\n  bool ImageDataDirectoryEntry(size_t index, IMAGE_DATA_DIRECTORY* entry) const;\n\n  //! \\brief A templatized helper for ImageDataDirectoryEntry() to account for\n  //!     differences in \\a NtHeadersType.\n  template <class NtHeadersType>\n  bool ImageDataDirectoryEntryT(size_t index,\n                                IMAGE_DATA_DIRECTORY* entry) const;\n\n  ProcessSubrangeReader module_subrange_reader_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_\n"
  },
  {
    "path": "snapshot/win/pe_image_reader_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/pe_image_reader.h\"\n\n#ifndef PSAPI_VERSION\n#define PSAPI_VERSION 2\n#endif\n#include <psapi.h>\n\n#include \"base/files/file_path.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/win/process_reader_win.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_module_handle.h\"\n#include \"test/test_paths.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/win/get_module_information.h\"\n#include \"util/win/module_version.h\"\n#include \"util/win/process_info.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(PEImageReader, DebugDirectory) {\n  base::FilePath module_path =\n      TestPaths::BuildArtifact(L\"snapshot\",\n                               L\"image_reader_module\",\n                               TestPaths::FileType::kLoadableModule);\n  ScopedModuleHandle module_handle(LoadLibrary(module_path.value().c_str()));\n  ASSERT_TRUE(module_handle.valid()) << ErrorMessage(\"LoadLibrary\");\n\n  PEImageReader pe_image_reader;\n  ProcessReaderWin process_reader;\n  ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),\n                                        ProcessSuspensionState::kRunning));\n\n  MODULEINFO module_info;\n  ASSERT_TRUE(CrashpadGetModuleInformation(GetCurrentProcess(),\n                                           module_handle.get(),\n                                           &module_info,\n                                           sizeof(module_info)))\n      << ErrorMessage(\"GetModuleInformation\");\n  EXPECT_EQ(module_info.lpBaseOfDll, module_handle.get());\n\n  base::FilePath module_basename = module_path.BaseName();\n  ASSERT_TRUE(pe_image_reader.Initialize(\n      &process_reader,\n      FromPointerCast<WinVMAddress>(module_handle.get()),\n      module_info.SizeOfImage,\n      base::WideToUTF8(module_basename.value())));\n\n  UUID uuid;\n  DWORD age;\n  std::string pdbname;\n  ASSERT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname));\n  std::string module_name =\n      base::WideToUTF8(module_basename.RemoveFinalExtension().value());\n  EXPECT_NE(pdbname.find(module_name), std::string::npos);\n  const std::string suffix(\".pdb\");\n  EXPECT_EQ(\n      pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix),\n      0);\n}\n\nvoid TestVSFixedFileInfo(ProcessReaderWin* process_reader,\n                         const ProcessInfo::Module& module,\n                         bool known_dll) {\n  PEImageReader pe_image_reader;\n  ASSERT_TRUE(pe_image_reader.Initialize(process_reader,\n                                         module.dll_base,\n                                         module.size,\n                                         base::WideToUTF8(module.name)));\n\n  VS_FIXEDFILEINFO observed;\n  const bool observed_rv = pe_image_reader.VSFixedFileInfo(&observed);\n  ASSERT_TRUE(observed_rv || !known_dll);\n\n  if (observed_rv) {\n    EXPECT_EQ(observed.dwSignature, static_cast<DWORD>(VS_FFI_SIGNATURE));\n    EXPECT_EQ(observed.dwStrucVersion, static_cast<DWORD>(VS_FFI_STRUCVERSION));\n    EXPECT_EQ(observed.dwFileFlags & ~observed.dwFileFlagsMask, 0u);\n    if (known_dll) {\n      EXPECT_EQ(observed.dwFileOS, static_cast<DWORD>(VOS_NT_WINDOWS32));\n      EXPECT_EQ(observed.dwFileType, static_cast<DWORD>(VFT_DLL));\n    } else {\n      EXPECT_NE(observed.dwFileOS & VOS_NT_WINDOWS32, 0u);\n\n      // VFT_DRV/VFT2_DRV_NETWORK is for nsi.dll, “network service interface.”\n      // It’s not normally loaded, but has been observed to be loaded in some\n      // cases.\n      EXPECT_TRUE(observed.dwFileType == VFT_APP ||\n                  observed.dwFileType == VFT_DLL ||\n                  (observed.dwFileType == VFT_DRV &&\n                   observed.dwFileSubtype == VFT2_DRV_NETWORK))\n          << base::StringPrintf(\"type 0x%lx, subtype 0x%lx\",\n                                observed.dwFileType,\n                                observed.dwFileSubtype);\n    }\n  }\n\n  base::FilePath module_path(module.name);\n\n  const DWORD version = GetVersion();\n  const int major_version = LOBYTE(LOWORD(version));\n  const int minor_version = HIBYTE(LOWORD(version));\n  if (major_version > 6 || (major_version == 6 && minor_version >= 2)) {\n    // Windows 8 or later.\n    //\n    // Use BaseName() to ensure that GetModuleVersionAndType() finds the\n    // already-loaded module with the specified name. Otherwise, dwFileVersionMS\n    // may not match. This appears to be related to the changes made in Windows\n    // 8.1 to GetVersion() and GetVersionEx() for non-manifested applications\n    module_path = module_path.BaseName();\n  }\n\n  VS_FIXEDFILEINFO expected;\n  const bool expected_rv = GetModuleVersionAndType(module_path, &expected);\n  ASSERT_TRUE(expected_rv || !known_dll);\n\n  EXPECT_EQ(observed_rv, expected_rv);\n\n  if (observed_rv && expected_rv) {\n    EXPECT_EQ(observed.dwSignature, expected.dwSignature);\n    EXPECT_EQ(observed.dwStrucVersion, expected.dwStrucVersion);\n    EXPECT_EQ(observed.dwFileVersionMS, expected.dwFileVersionMS);\n    EXPECT_EQ(observed.dwFileVersionLS, expected.dwFileVersionLS);\n    EXPECT_EQ(observed.dwProductVersionMS, expected.dwProductVersionMS);\n    EXPECT_EQ(observed.dwProductVersionLS, expected.dwProductVersionLS);\n    EXPECT_EQ(observed.dwFileFlagsMask, expected.dwFileFlagsMask);\n    EXPECT_EQ(observed.dwFileFlags, expected.dwFileFlags);\n    EXPECT_EQ(observed.dwFileOS, expected.dwFileOS);\n    EXPECT_EQ(observed.dwFileType, expected.dwFileType);\n    EXPECT_EQ(observed.dwFileSubtype, expected.dwFileSubtype);\n    EXPECT_EQ(observed.dwFileDateMS, expected.dwFileDateMS);\n    EXPECT_EQ(observed.dwFileDateLS, expected.dwFileDateLS);\n  }\n}\n\nTEST(PEImageReader, VSFixedFileInfo_OneModule) {\n  ProcessReaderWin process_reader;\n  ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),\n                                        ProcessSuspensionState::kRunning));\n\n  static constexpr wchar_t kModuleName[] = L\"kernel32.dll\";\n  const HMODULE module_handle = GetModuleHandle(kModuleName);\n  ASSERT_TRUE(module_handle) << ErrorMessage(\"GetModuleHandle\");\n\n  MODULEINFO module_info;\n  ASSERT_TRUE(CrashpadGetModuleInformation(\n      GetCurrentProcess(), module_handle, &module_info, sizeof(module_info)))\n      << ErrorMessage(\"GetModuleInformation\");\n  EXPECT_EQ(module_info.lpBaseOfDll, module_handle);\n\n  ProcessInfo::Module module;\n  module.name = kModuleName;\n  module.dll_base = FromPointerCast<WinVMAddress>(module_info.lpBaseOfDll);\n  module.size = module_info.SizeOfImage;\n\n  TestVSFixedFileInfo(&process_reader, module, true);\n}\n\nTEST(PEImageReader, VSFixedFileInfo_AllModules) {\n  ProcessReaderWin process_reader;\n  ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),\n                                        ProcessSuspensionState::kRunning));\n\n  const std::vector<ProcessInfo::Module>& modules = process_reader.Modules();\n  EXPECT_GT(modules.size(), 2u);\n\n  for (const auto& module : modules) {\n    SCOPED_TRACE(base::WideToUTF8(module.name));\n    TestVSFixedFileInfo(&process_reader, module, false);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/pe_image_resource_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/pe_image_resource_reader.h\"\n\n#include <algorithm>\n#include <memory>\n\n#include \"base/logging.h\"\n\nnamespace {\n\nvoid AddLanguageAndNeutralSublanguage(std::vector<uint16_t>* languages,\n                                      uint16_t language) {\n  languages->push_back(language);\n  if (SUBLANGID(language) != SUBLANG_NEUTRAL) {\n    languages->push_back(MAKELANGID(PRIMARYLANGID(language), SUBLANG_NEUTRAL));\n  }\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nPEImageResourceReader::PEImageResourceReader()\n    : resources_subrange_reader_(),\n      module_base_(0),\n      initialized_() {\n}\n\nPEImageResourceReader::~PEImageResourceReader() {}\n\nbool PEImageResourceReader::Initialize(\n    const ProcessSubrangeReader& module_subrange_reader,\n    const IMAGE_DATA_DIRECTORY& resources_directory_entry) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  module_base_ = module_subrange_reader.Base();\n\n  if (!resources_subrange_reader_.InitializeSubrange(\n          module_subrange_reader,\n          module_base_ + resources_directory_entry.VirtualAddress,\n          resources_directory_entry.Size,\n          \"resources\")) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool PEImageResourceReader::FindResourceByID(uint16_t type,\n                                             uint16_t name,\n                                             uint16_t language,\n                                             WinVMAddress* address,\n                                             WinVMSize* size,\n                                             uint32_t* code_page) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // The root resource directory is at the beginning of the resources area\n  // within the module.\n  const uint32_t name_directory_offset =\n      GetEntryFromResourceDirectoryByID(0, type, true);\n  if (!name_directory_offset) {\n    return false;\n  }\n\n  const uint32_t language_directory_offset =\n      GetEntryFromResourceDirectoryByID(name_directory_offset, name, true);\n  if (!language_directory_offset) {\n    return false;\n  }\n\n  // The definition of IMAGE_RESOURCE_DIRECTORY_ENTRY in <winnt.h> has a comment\n  // saying that its offsets are relative to “the resource directory of the data\n  // associated with this directory entry”. That could be interpreted to mean\n  // that language_directory_offset is relative to name_directory_offset, since\n  // the language directory entry is found within the name directory. This is\n  // not correct. All resource offsets are relative to the resources area within\n  // the module.\n  const uint32_t data_offset = GetEntryFromResourceDirectoryByLanguage(\n      language_directory_offset, language);\n  if (!data_offset) {\n    return false;\n  }\n\n  IMAGE_RESOURCE_DATA_ENTRY data_entry;\n  if (!resources_subrange_reader_.ReadMemory(\n          resources_subrange_reader_.Base() + data_offset,\n          sizeof(data_entry),\n          &data_entry)) {\n    LOG(WARNING) << \"could not read resource data entry from \"\n                 << resources_subrange_reader_.name();\n    return false;\n  }\n\n  // The definition of IMAGE_RESOURCE_DATA_ENTRY in <winnt.h> has a comment\n  // saying that OffsetToData is relative to the beginning of the resource data.\n  // This is not correct. It’s module-relative.\n  *address = module_base_ + data_entry.OffsetToData;\n  *size = data_entry.Size;\n  if (code_page) {\n    *code_page = data_entry.CodePage;\n  }\n\n  return true;\n}\n\nuint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByID(\n    uint32_t language_directory_offset,\n    uint16_t id,\n    bool want_subdirectory) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_id;\n  if (!ReadResourceDirectory(\n          language_directory_offset, nullptr, nullptr, &entries_by_id)) {\n    return 0;\n  }\n\n  const auto entry_it =\n      std::find_if(entries_by_id.begin(),\n                   entries_by_id.end(),\n                   [id](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) {\n                     return !entry.NameIsString && entry.Id == id;\n                   });\n  if (entry_it != entries_by_id.end()) {\n    if ((entry_it->DataIsDirectory != 0) != want_subdirectory) {\n      LOG(WARNING) << \"expected \" << (want_subdirectory ? \"\" : \"non-\")\n                   << \"directory for entry id \" << id << \" in \"\n                   << resources_subrange_reader_.name();\n      return 0;\n    }\n\n    return entry_it->DataIsDirectory ? entry_it->OffsetToDirectory\n                                     : entry_it->OffsetToData;\n  }\n\n  return 0;\n}\n\nuint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByLanguage(\n    uint32_t resource_directory_offset,\n    uint16_t language) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> entries_by_language;\n  if (!ReadResourceDirectory(\n          resource_directory_offset, nullptr, nullptr, &entries_by_language)) {\n    return 0;\n  }\n\n  if (entries_by_language.empty()) {\n    return 0;\n  }\n\n  // https://msdn.microsoft.com/library/cc194810.aspx\n  //\n  // TODO(mark): It seems like FindResourceEx() might do something more complex.\n  // It would be best to mimic its behavior.\n  std::vector<uint16_t> try_languages;\n  if (PRIMARYLANGID(language) != LANG_NEUTRAL) {\n    AddLanguageAndNeutralSublanguage(&try_languages, language);\n  } else {\n    if (SUBLANGID(language) != SUBLANG_SYS_DEFAULT) {\n      AddLanguageAndNeutralSublanguage(&try_languages,\n                                       LANGIDFROMLCID(GetThreadLocale()));\n      AddLanguageAndNeutralSublanguage(&try_languages,\n                                       LANGIDFROMLCID(GetUserDefaultLCID()));\n    }\n    if (SUBLANGID(language) != SUBLANG_DEFAULT) {\n      AddLanguageAndNeutralSublanguage(&try_languages,\n                                       LANGIDFROMLCID(GetSystemDefaultLCID()));\n    }\n  }\n\n  try_languages.push_back(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));\n  try_languages.push_back(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT));\n\n  for (const auto try_language : try_languages) {\n    const auto entry_it = std::find_if(\n        entries_by_language.begin(),\n        entries_by_language.end(),\n        [try_language](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) {\n          return !entry.NameIsString && entry.Id == try_language;\n        });\n    if (entry_it != entries_by_language.end()) {\n      if (entry_it->DataIsDirectory) {\n        LOG(WARNING) << \"expected non-directory for entry language \"\n                     << try_language << \" in \"\n                     << resources_subrange_reader_.name();\n        return 0;\n      }\n\n      return entry_it->OffsetToData;\n    }\n  }\n\n  // Fall back to the first entry in the list.\n  const auto& entry = entries_by_language.front();\n  if (entry.DataIsDirectory) {\n    LOG(WARNING) << \"expected non-directory for entry in \"\n                 << resources_subrange_reader_.name();\n    return 0;\n  }\n\n  return entry.OffsetToData;\n}\n\nbool PEImageResourceReader::ReadResourceDirectory(\n    uint32_t resource_directory_offset,\n    IMAGE_RESOURCE_DIRECTORY* resource_directory,\n    std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* named_entries,\n    std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* id_entries) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // resource_directory is optional, but it’s still needed locally even if the\n  // caller isn’t interested in it.\n  std::unique_ptr<IMAGE_RESOURCE_DIRECTORY> local_resource_directory;\n  if (!resource_directory) {\n    local_resource_directory.reset(new IMAGE_RESOURCE_DIRECTORY);\n    resource_directory = local_resource_directory.get();\n  }\n\n  const WinVMAddress address =\n      resources_subrange_reader_.Base() + resource_directory_offset;\n\n  if (!resources_subrange_reader_.ReadMemory(\n          address, sizeof(*resource_directory), resource_directory)) {\n    LOG(WARNING) << \"could not read resource directory from \"\n                 << resources_subrange_reader_.name();\n    return false;\n  }\n\n  if (named_entries) {\n    named_entries->clear();\n    named_entries->resize(resource_directory->NumberOfNamedEntries);\n    if (!named_entries->empty() &&\n        !resources_subrange_reader_.ReadMemory(\n            address + sizeof(*resource_directory),\n            named_entries->size() * sizeof((*named_entries)[0]),\n            &(*named_entries)[0])) {\n      LOG(WARNING) << \"could not read resource directory named entries from \"\n                   << resources_subrange_reader_.name();\n      return false;\n    }\n  }\n\n  if (id_entries) {\n    id_entries->clear();\n    id_entries->resize(resource_directory->NumberOfIdEntries);\n    if (!id_entries->empty() &&\n        !resources_subrange_reader_.ReadMemory(\n            address + sizeof(*resource_directory) +\n                resource_directory->NumberOfNamedEntries *\n                    sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY),\n            id_entries->size() * sizeof((*id_entries)[0]),\n            &(*id_entries)[0])) {\n      LOG(WARNING) << \"could not read resource directory ID entries from \"\n                   << resources_subrange_reader_.name();\n      return false;\n    }\n  }\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/pe_image_resource_reader.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_\n#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_\n\n#include <windows.h>\n#include <stdint.h>\n\n#include <vector>\n\n#include \"snapshot/win/process_subrange_reader.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/win/address_types.h\"\n\nnamespace crashpad {\n\n//! \\brief A reader for resources stored in PE images mapped into another\n//!     process.\n//!\n//! \\sa PEImageReader\nclass PEImageResourceReader {\n public:\n  PEImageResourceReader();\n\n  PEImageResourceReader(const PEImageResourceReader&) = delete;\n  PEImageResourceReader& operator=(const PEImageResourceReader&) = delete;\n\n  ~PEImageResourceReader();\n\n  //! \\brief Initializes the resource reader.\n  //!\n  //! \\param[in] module_subrange_reader The reader for the module.\n  //! \\param[in] resources_directory_entry The module’s `IMAGE_DATA_DIRECTORY`\n  //!     for its resources area. This is taken from the module’s\n  //!     `IMAGE_OPTIONAL_HEADER::DataDirectory` at index\n  //!     `IMAGE_DIRECTORY_ENTRY_RESOURCE`.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Initialize(const ProcessSubrangeReader& module_subrange_reader,\n                  const IMAGE_DATA_DIRECTORY& resources_directory_entry);\n\n  //! \\brief Locates a resource in a module by its ID.\n  //!\n  //! This method is similar to `FindResourceEx()`, but it operates on modules\n  //! loaded in a remote process’ address space. It is not necessary to\n  //! `LoadLibrary()` a module into a process in order to use this method.\n  //!\n  //! No support is provided at present for locating resources by \\a type or \\a\n  //! name using strings as opposed to integer identifiers.\n  //!\n  //! Languages are scanned in the order determined by\n  //! GetEntryFromResourceDirectoryByLanguage().\n  //!\n  //! \\param[in] type The integer identifier of the resource type, as in the\n  //!     `lpType` parameter of `FindResourceEx()`.\n  //! \\param[in] name The integer identifier of the resource, as in the `lpName`\n  //!     parameter of `FindResourceEx()`.\n  //! \\param[in] language The language of the resource, as in the `wLanguage`\n  //!     parameter of `FindResourceEx()`.\n  //! \\param[out] address The address, in the remote process’ address space, of\n  //!     the resource data.\n  //! \\param[out] size The size of the resource data.\n  //! \\param[out] code_page The code page used to encode textual resource data.\n  //!     This parameter is optional.\n  //!\n  //! \\return `true` on success, with the out parameters set appropriately.\n  //!     `false` if the resource was not found, without logging any messages.\n  //!     `false` on failure, with a message logged.\n  bool FindResourceByID(uint16_t type,\n                        uint16_t name,\n                        uint16_t language,\n                        WinVMAddress* address,\n                        WinVMSize* size,\n                        uint32_t* code_page) const;\n\n private:\n  //! \\brief Locates a resource directory entry within a resource directory by\n  //!     integer ID.\n  //!\n  //! \\param[in] resource_directory_offset The offset, in the module’s resources\n  //!     area, of the resource directory to search.\n  //! \\param[in] id The integer identifier of the resource to search for.\n  //! \\param[in] want_subdirectory `true` if the resource directory entry is\n  //!     expected to be a resource directory itself, `false` otherwise.\n  //!\n  //! \\return The offset, in the module’s resources area, of the entry that was\n  //!     found. On failure, `0`. `0` is technically a valid offset, but it\n  //!     corresponds to the root resource directory, which should never be the\n  //!     offset of another resource directory entry. If \\a id was not found,\n  //!     `0` will be returned without logging anything. For other failures, a\n  //!     message will be logged.\n  uint32_t GetEntryFromResourceDirectoryByID(uint32_t resource_directory_offset,\n                                             uint16_t id,\n                                             bool want_subdirectory) const;\n\n  //! \\brief Locates a resource directory entry within a resource directory by\n  //!     language.\n  //!\n  //! This method is similar to GetEntryFromResourceDirectoryByID() with \\a\n  //! want_subdirectory set to `false`. Attempts are made to locate the resource\n  //! by using these languages:\n  //! <ul>\n  //!     <li>If \\a language is `LANG_NEUTRAL`:</li>\n  //!     <ul>\n  //!         <li>Unless `SUBLANG_SYS_DEFAULT` is specified, the language of the\n  //!             thread’s locale, with its normal sublanguage and with\n  //!             `SUBLANG_NEUTRAL`.</li>\n  //!         <li>Unless `SUBLANG_SYS_DEFAULT` is specified, the language of the\n  //!             user’s default locale, with its normal sublanguage and with\n  //!             `SUBLANG_NEUTRAL`.</li>\n  //!         <li>Unless `SUBLANG_DEFAULT` is specified, the language of the\n  //!             system’s default locale, with its normal sublanguage and with\n  //!             `SUBLANG_NEUTRAL`.</li>\n  //!     </ul>\n  //!     <li>If \\a language is not `LANG_NEUTRAL`:</li>\n  //!     <ul>\n  //!         <li>\\a language</li>\n  //!         <li>\\a language, with `SUBLANG_NEUTRAL`</li>\n  //!     </ul>\n  //!     <li>`LANG_NEUTRAL` with `SUBLANG_NEUTRAL`</li>\n  //!     <li>`LANG_ENGLISH` with `SUBLANG_DEFAULT`</li>\n  //!     <li>If none of the above match, the first language found</li>\n  //! </ul>\n  //!\n  //! If only a specific language is desired without any fallbacks, call\n  //! GetEntryFromResourceDirectoryByID() with the language directory’s offset\n  //! instead, passing the desired language in the \\a id parameter, and `false`\n  //! for \\a want_subdirectory.\n  //!\n  //! \\param[in] language_directory_offset The offset, in the module’s resources\n  //!     area, of the resource directory to search.\n  //! \\param[in] language The language of the resource to search for.\n  //!\n  //! \\return The return value is as in GetEntryFromResourceDirectoryByID().\n  uint32_t GetEntryFromResourceDirectoryByLanguage(\n      uint32_t language_directory_offset,\n      uint16_t language) const;\n\n  //! \\brief Reads a resource directory.\n  //!\n  //! \\param[in] resource_directory_offset The offset, in the module’s resources\n  //!     area, of the resource directory to read.\n  //! \\param[out] resource_directory The `IMAGE_RESOURCE_DIRECTORY` structure.\n  //!     This parameter is optional.\n  //! \\param[out] named_entries A vector of \\a\n  //!     resource_directory->NumberOfNamedEntries\n  //!     `IMAGE_RESOURCE_DIRECTORY_ENTRY` items that follow the resource\n  //!     directory. This parameter is optional.\n  //! \\param[out] id_entries A vector of \\a\n  //!     resource_directory->NumberOfIdEntries `IMAGE_RESOURCE_DIRECTORY_ENTRY`\n  //!     items that follow the named entries. This parameter is optional.\n  //!\n  //! \\return `true` on success, with the out parameters set appropriately.\n  //!     `false` on failure with a message logged.\n  bool ReadResourceDirectory(\n      uint32_t resource_directory_offset,\n      IMAGE_RESOURCE_DIRECTORY* resource_directory,\n      std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* named_entries,\n      std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY>* id_entries) const;\n\n  ProcessSubrangeReader resources_subrange_reader_;\n  WinVMAddress module_base_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_\n"
  },
  {
    "path": "snapshot/win/process_reader_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/process_reader_win.h\"\n\n#include <string.h>\n#include <winternl.h>\n\n#include <memory>\n\n#include \"base/check_op.h\"\n#include \"base/notreached.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"snapshot/win/cpu_context_win.h\"\n#include \"util/misc/capture_context.h\"\n#include \"util/misc/time.h\"\n#include \"util/win/get_function.h\"\n#include \"util/win/nt_internals.h\"\n#include \"util/win/ntstatus_logging.h\"\n#include \"util/win/process_structs.h\"\n#include \"util/win/scoped_handle.h\"\n#include \"util/win/scoped_local_alloc.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Gets a pointer to the process information structure after a given one, or\n// null when iteration is complete, assuming they've been retrieved in a block\n// via NtQuerySystemInformation().\ntemplate <class Traits>\nprocess_types::SYSTEM_PROCESS_INFORMATION<Traits>* NextProcess(\n    process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process) {\n  ULONG offset = process->NextEntryOffset;\n  if (offset == 0)\n    return nullptr;\n  return reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(\n      reinterpret_cast<uint8_t*>(process) + offset);\n}\n\n//! \\brief Retrieves the SYSTEM_PROCESS_INFORMATION for a given process.\n//!\n//! The returned pointer points into the memory block stored by \\a buffer.\n//! Ownership of \\a buffer is transferred to the caller.\n//!\n//! \\return Pointer to the process' data, or nullptr if it was not found or on\n//!     error. On error, a message will be logged.\ntemplate <class Traits>\nprocess_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation(\n    HANDLE process_handle,\n    std::unique_ptr<uint8_t[]>* buffer) {\n  ULONG buffer_size = 16384;\n  ULONG actual_size;\n  buffer->reset(new uint8_t[buffer_size]);\n  NTSTATUS status;\n  // This must be in retry loop, as we're racing with process creation on the\n  // system to find a buffer large enough to hold all process information.\n  for (int tries = 0; tries < 20; ++tries) {\n    status = crashpad::NtQuerySystemInformation(\n        SystemProcessInformation,\n        reinterpret_cast<void*>(buffer->get()),\n        buffer_size,\n        &actual_size);\n    if (status == STATUS_BUFFER_TOO_SMALL ||\n        status == STATUS_INFO_LENGTH_MISMATCH) {\n      DCHECK_GT(actual_size, buffer_size);\n\n      // Add a little extra to try to avoid an additional loop iteration. We're\n      // racing with system-wide process creation between here and the next call\n      // to NtQuerySystemInformation().\n      buffer_size = actual_size + 4096;\n\n      // Free the old buffer before attempting to allocate a new one.\n      buffer->reset();\n\n      buffer->reset(new uint8_t[buffer_size]);\n    } else {\n      break;\n    }\n  }\n\n  if (!NT_SUCCESS(status)) {\n    NTSTATUS_LOG(ERROR, status) << \"NtQuerySystemInformation\";\n    return nullptr;\n  }\n\n  DCHECK_LE(actual_size, buffer_size);\n\n  process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process =\n      reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(\n          buffer->get());\n  DWORD process_id = GetProcessId(process_handle);\n  for (;;) {\n    if (process->UniqueProcessId == process_id)\n      return process;\n    process = NextProcess(process);\n    if (!process)\n      break;\n  }\n\n  LOG(ERROR) << \"process \" << process_id << \" not found\";\n  return nullptr;\n}\n\ntemplate <class Traits>\nHANDLE OpenThread(\n    const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info) {\n  HANDLE handle;\n  ACCESS_MASK query_access =\n      THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION;\n  OBJECT_ATTRIBUTES object_attributes;\n  InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr);\n  NTSTATUS status = crashpad::NtOpenThread(\n      &handle, query_access, &object_attributes, &thread_info.ClientId);\n  if (!NT_SUCCESS(status)) {\n    NTSTATUS_LOG(ERROR, status) << \"NtOpenThread\";\n    return nullptr;\n  }\n  return handle;\n}\n\n// It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a\n// side-effect of returning the SuspendCount of the thread on success, so we\n// fill out these two pieces of semi-unrelated data in the same function.\ntemplate <class Traits>\nbool FillThreadContextAndSuspendCount(HANDLE thread_handle,\n                                      ProcessReaderWin::Thread* thread,\n                                      ProcessSuspensionState suspension_state,\n                                      bool is_64_reading_32) {\n  // Don't suspend the thread if it's this thread. This is really only for test\n  // binaries, as we won't be walking ourselves, in general.\n  bool is_current_thread = thread->id ==\n                           reinterpret_cast<process_types::TEB<Traits>*>(\n                               NtCurrentTeb())->ClientId.UniqueThread;\n\n  if (is_current_thread) {\n    DCHECK(suspension_state == ProcessSuspensionState::kRunning);\n    thread->suspend_count = 0;\n    DCHECK(!is_64_reading_32);\n    thread->context.InitializeFromCurrentThread();\n  } else {\n    DWORD previous_suspend_count = SuspendThread(thread_handle);\n    if (previous_suspend_count == static_cast<DWORD>(-1)) {\n      PLOG(ERROR) << \"SuspendThread\";\n      return false;\n    }\n    if (previous_suspend_count <= 0 &&\n        suspension_state == ProcessSuspensionState::kSuspended) {\n      LOG(WARNING) << \"Thread \" << thread->id\n                   << \" should be suspended, but previous_suspend_count is \"\n                   << previous_suspend_count;\n      thread->suspend_count = 0;\n    } else {\n      thread->suspend_count =\n          previous_suspend_count -\n          (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0);\n    }\n\n#if defined(ARCH_CPU_32_BITS)\n    if (!thread->context.InitializeNative(thread_handle))\n      return false;\n#endif  // ARCH_CPU_32_BITS\n\n#if defined(ARCH_CPU_64_BITS)\n    if (is_64_reading_32) {\n      if (!thread->context.InitializeWow64(thread_handle))\n        return false;\n#if defined(ARCH_CPU_X86_64)\n    } else if (IsXStateFeatureEnabled(XSTATE_MASK_CET_U)) {\n      if (!thread->context.InitializeXState(thread_handle, XSTATE_MASK_CET_U))\n        return false;\n#endif  // ARCH_CPU_X86_64\n    } else {\n      if (!thread->context.InitializeNative(thread_handle))\n        return false;\n    }\n#endif  // ARCH_CPU_64_BITS\n\n    if (!ResumeThread(thread_handle)) {\n      PLOG(ERROR) << \"ResumeThread\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n}  // namespace\n\nProcessReaderWin::ThreadContext::ThreadContext()\n    : offset_(0), initialized_(false), data_() {}\n\nvoid ProcessReaderWin::ThreadContext::InitializeFromCurrentThread() {\n  data_.resize(sizeof(CONTEXT));\n  initialized_ = true;\n  CaptureContext(context<CONTEXT>());\n}\n\nbool ProcessReaderWin::ThreadContext::InitializeNative(HANDLE thread_handle) {\n  data_.resize(sizeof(CONTEXT));\n  initialized_ = true;\n  context<CONTEXT>()->ContextFlags = CONTEXT_ALL;\n  if (!GetThreadContext(thread_handle, context<CONTEXT>())) {\n    PLOG(ERROR) << \"GetThreadContext\";\n    return false;\n  }\n  return true;\n}\n\n#if defined(ARCH_CPU_64_BITS)\nbool ProcessReaderWin::ThreadContext::InitializeWow64(HANDLE thread_handle) {\n  data_.resize(sizeof(WOW64_CONTEXT));\n  initialized_ = true;\n  context<WOW64_CONTEXT>()->ContextFlags = CONTEXT_ALL;\n  if (!Wow64GetThreadContext(thread_handle, context<WOW64_CONTEXT>())) {\n    PLOG(ERROR) << \"Wow64GetThreadContext\";\n    return false;\n  }\n  return true;\n}\n#endif\n\n#if defined(ARCH_CPU_X86_64)\nbool ProcessReaderWin::ThreadContext::InitializeXState(\n    HANDLE thread_handle,\n    ULONG64 XStateCompactionMask) {\n  // InitializeContext2 needs Windows 10 build 20348.\n  static const auto initialize_context_2 =\n      GET_FUNCTION(L\"kernel32.dll\", ::InitializeContext2);\n  if (!initialize_context_2)\n    return false;\n  // We want CET_U xstate to get the ssp, only possible when supported.\n  PCONTEXT ret_context = nullptr;\n  DWORD context_size = 0;\n  if (!initialize_context_2(nullptr,\n                            CONTEXT_ALL | CONTEXT_XSTATE,\n                            &ret_context,\n                            &context_size,\n                            XStateCompactionMask) &&\n      GetLastError() != ERROR_INSUFFICIENT_BUFFER) {\n    PLOG(ERROR) << \"InitializeContext2 - getting required size\";\n    return false;\n  }\n  // NB: ret_context may not be data.begin().\n  data_.resize(context_size);\n  if (!initialize_context_2(data_.data(),\n                            CONTEXT_ALL | CONTEXT_XSTATE,\n                            &ret_context,\n                            &context_size,\n                            XStateCompactionMask)) {\n    PLOG(ERROR) << \"InitializeContext2 - initializing\";\n    return false;\n  }\n  offset_ = reinterpret_cast<unsigned char*>(ret_context) - data_.data();\n  initialized_ = true;\n\n  if (!GetThreadContext(thread_handle, ret_context)) {\n    PLOG(ERROR) << \"GetThreadContext\";\n    return false;\n  }\n\n  return true;\n}\n#endif  // defined(ARCH_CPU_X86_64)\n\nProcessReaderWin::Thread::Thread()\n    : context(),\n      name(),\n      id(0),\n      teb_address(0),\n      teb_size(0),\n      stack_region_address(0),\n      stack_region_size(0),\n      suspend_count(0),\n      priority_class(0),\n      priority(0) {}\n\nProcessReaderWin::ProcessReaderWin()\n    : process_(INVALID_HANDLE_VALUE),\n      process_info_(),\n      process_memory_(),\n      threads_(),\n      modules_(),\n      suspension_state_(),\n      initialized_threads_(false),\n      initialized_() {\n}\n\nProcessReaderWin::~ProcessReaderWin() {\n}\n\nbool ProcessReaderWin::Initialize(HANDLE process,\n                                  ProcessSuspensionState suspension_state) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  process_ = process;\n  suspension_state_ = suspension_state;\n  if (!process_info_.Initialize(process))\n    return false;\n  if (!process_memory_.Initialize(process))\n    return false;\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessReaderWin::StartTime(timeval* start_time) const {\n  FILETIME creation, exit, kernel, user;\n  if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {\n    PLOG(ERROR) << \"GetProcessTimes\";\n    return false;\n  }\n  *start_time = FiletimeToTimevalEpoch(creation);\n  return true;\n}\n\nbool ProcessReaderWin::CPUTimes(timeval* user_time,\n                                timeval* system_time) const {\n  FILETIME creation, exit, kernel, user;\n  if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) {\n    PLOG(ERROR) << \"GetProcessTimes\";\n    return false;\n  }\n  *user_time = FiletimeToTimevalInterval(user);\n  *system_time = FiletimeToTimevalInterval(kernel);\n  return true;\n}\n\nconst std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (initialized_threads_)\n    return threads_;\n\n  initialized_threads_ = true;\n\n#if defined(ARCH_CPU_64_BITS)\n  ReadThreadData<process_types::internal::Traits64>(process_info_.IsWow64());\n#else\n  ReadThreadData<process_types::internal::Traits32>(false);\n#endif\n\n  return threads_;\n}\n\nconst std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (!process_info_.Modules(&modules_)) {\n    LOG(ERROR) << \"couldn't retrieve modules\";\n  }\n\n  return modules_;\n}\n\nconst ProcessInfo& ProcessReaderWin::GetProcessInfo() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_info_;\n}\n\nvoid ProcessReaderWin::DecrementThreadSuspendCounts(uint64_t except_thread_id) {\n  Threads();\n  for (auto& thread : threads_) {\n    if (thread.id != except_thread_id) {\n      DCHECK_GT(thread.suspend_count, 0u);\n      --thread.suspend_count;\n    }\n  }\n}\n\ntemplate <class Traits>\nvoid ProcessReaderWin::ReadThreadData(bool is_64_reading_32) {\n  DCHECK(threads_.empty());\n\n  std::unique_ptr<uint8_t[]> buffer;\n  process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information =\n      GetProcessInformation<Traits>(process_, &buffer);\n  if (!process_information)\n    return;\n\n  for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) {\n    const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info =\n        process_information->Threads[i];\n    ProcessReaderWin::Thread thread;\n    thread.id = thread_info.ClientId.UniqueThread;\n\n    ScopedKernelHANDLE thread_handle(OpenThread(thread_info));\n    if (!thread_handle.is_valid())\n      continue;\n\n    if (!FillThreadContextAndSuspendCount<Traits>(thread_handle.get(),\n                                                  &thread,\n                                                  suspension_state_,\n                                                  is_64_reading_32)) {\n      continue;\n    }\n\n    // TODO(scottmg): I believe we could reverse engineer the PriorityClass from\n    // the Priority, BasePriority, and\n    // https://msdn.microsoft.com/library/ms685100.aspx. MinidumpThreadWriter\n    // doesn't handle it yet in any case, so investigate both of those at the\n    // same time if it's useful.\n    thread.priority_class = NORMAL_PRIORITY_CLASS;\n\n    thread.priority = thread_info.Priority;\n\n    process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info;\n    NTSTATUS status = crashpad::NtQueryInformationThread(\n        thread_handle.get(),\n        static_cast<THREADINFOCLASS>(ThreadBasicInformation),\n        &thread_basic_info,\n        sizeof(thread_basic_info),\n        nullptr);\n    if (!NT_SUCCESS(status)) {\n      NTSTATUS_LOG(ERROR, status) << \"NtQueryInformationThread\";\n      continue;\n    }\n\n    // Read the TIB (Thread Information Block) which is the first element of the\n    // TEB, for its stack fields.\n    process_types::NT_TIB<Traits> tib;\n    thread.teb_address = thread_basic_info.TebBaseAddress;\n    thread.teb_size = sizeof(process_types::TEB<Traits>);\n    if (process_memory_.Read(thread.teb_address, sizeof(tib), &tib)) {\n      WinVMAddress base = 0;\n      WinVMAddress limit = 0;\n      // If we're reading a WOW64 process, then the TIB we just retrieved is the\n      // x64 one. The first word of the x64 TIB points at the x86 TIB. See\n      // https://msdn.microsoft.com/library/dn424783.aspx.\n      if (is_64_reading_32) {\n        process_types::NT_TIB<process_types::internal::Traits32> tib32;\n        thread.teb_address = tib.Wow64Teb;\n        thread.teb_size =\n            sizeof(process_types::TEB<process_types::internal::Traits32>);\n        if (process_memory_.Read(thread.teb_address, sizeof(tib32), &tib32)) {\n          base = tib32.StackBase;\n          limit = tib32.StackLimit;\n        }\n      } else {\n        base = tib.StackBase;\n        limit = tib.StackLimit;\n      }\n\n      // Note, \"backwards\" because of direction of stack growth.\n      thread.stack_region_address = limit;\n      if (limit > base) {\n        LOG(ERROR) << \"invalid stack range: \" << base << \" - \" << limit;\n        thread.stack_region_size = 0;\n      } else {\n        thread.stack_region_size = base - limit;\n      }\n    }\n    // On Windows 10 build 1607 and later, read the thread name.\n    static const auto get_thread_description =\n        GET_FUNCTION(L\"kernel32.dll\", ::GetThreadDescription);\n    if (get_thread_description) {\n      wchar_t* thread_description;\n      HRESULT hr =\n          get_thread_description(thread_handle.get(), &thread_description);\n      if (SUCCEEDED(hr)) {\n        ScopedLocalAlloc thread_description_owner(thread_description);\n        thread.name = base::WideToUTF8(thread_description);\n      } else {\n        LOG(WARNING) << \"GetThreadDescription: \"\n                     << logging::SystemErrorCodeToString(hr);\n      }\n    }\n    threads_.push_back(thread);\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/process_reader_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_\n\n#include <windows.h>\n#include <sys/time.h>\n\n#include <string>\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory_win.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/process_info.h\"\n\nnamespace crashpad {\n\n//! \\brief State of process being read by ProcessReaderWin.\nenum class ProcessSuspensionState : bool {\n  //! \\brief The process has not been suspended.\n  kRunning,\n\n  //! \\brief The process is suspended.\n  kSuspended,\n};\n\n//! \\brief Accesses information about another process, identified by a `HANDLE`.\nclass ProcessReaderWin {\n public:\n  //! \\brief Helper to make the context copyable and resizable.\n  class ThreadContext {\n   public:\n    ThreadContext();\n    ~ThreadContext() {}\n\n    template <typename T>\n    T* context() const {\n      DCHECK(initialized_);\n      return reinterpret_cast<T*>(\n          const_cast<unsigned char*>(data_.data() + offset_));\n    }\n#if defined(ARCH_CPU_64_BITS)\n    bool InitializeWow64(HANDLE thread_handle);\n#endif  // ARCH_CPU_64_BITS\n#if defined(ARCH_CPU_X86_64)\n    // Initializes internal structures for extended compacted contexts.\n    bool InitializeXState(HANDLE thread_handle, ULONG64 XStateCompactionMask);\n#endif  // ARCH_CPU_X86_64\n    void InitializeFromCurrentThread();\n    bool InitializeNative(HANDLE thread_handle);\n\n   private:\n    // This is usually 0 but Windows might cause it to be positive when\n    // fetching the extended context. This needs to be adjusted after\n    // calls to InitializeContext2().\n    size_t offset_;\n    bool initialized_;\n    std::vector<unsigned char> data_;\n  };\n\n  //! \\brief Contains information about a thread that belongs to a process.\n  struct Thread {\n    Thread();\n    ~Thread() {}\n\n    ThreadContext context;\n    std::string name;\n    uint64_t id;\n    WinVMAddress teb_address;\n    WinVMSize teb_size;\n    WinVMAddress stack_region_address;\n    WinVMSize stack_region_size;\n    uint32_t suspend_count;\n    uint32_t priority_class;\n    uint32_t priority;\n  };\n\n  ProcessReaderWin();\n\n  ProcessReaderWin(const ProcessReaderWin&) = delete;\n  ProcessReaderWin& operator=(const ProcessReaderWin&) = delete;\n\n  ~ProcessReaderWin();\n\n  //! \\brief Initializes this object. This method must be called before any\n  //!     other.\n  //!\n  //! \\param[in] process Process handle, must have `PROCESS_QUERY_INFORMATION`,\n  //!     `PROCESS_VM_READ`, and `PROCESS_DUP_HANDLE` access.\n  //! \\param[in] suspension_state Whether \\a process has already been suspended\n  //!     by the caller. Typically, this will be\n  //!     ProcessSuspensionState::kSuspended, except for testing uses and where\n  //!     the reader is reading itself.\n  //!\n  //! \\return `true` on success, indicating that this object will respond\n  //!     validly to further method calls. `false` on failure. On failure, no\n  //!     further method calls should be made.\n  //!\n  //! \\sa ScopedProcessSuspend\n  bool Initialize(HANDLE process, ProcessSuspensionState suspension_state);\n\n  //! \\return `true` if the target task is a 64-bit process.\n  bool Is64Bit() const { return process_info_.Is64Bit(); }\n\n  //! \\brief Return a memory reader for the target process.\n  const ProcessMemoryWin* Memory() const { return &process_memory_; }\n\n  //! \\brief Determines the target process' start time.\n  //!\n  //! \\param[out] start_time The time that the process started.\n  //!\n  //! \\return `true` on success, `false` on failure, with a warning logged.\n  bool StartTime(timeval* start_time) const;\n\n  //! \\brief Determines the target process' execution time.\n  //!\n  //! \\param[out] user_time The amount of time the process has executed code in\n  //!     user mode.\n  //! \\param[out] system_time The amount of time the process has executed code\n  //!     in kernel mode.\n  //!\n  //! \\return `true` on success, `false` on failure, with a warning logged.\n  bool CPUTimes(timeval* user_time, timeval* system_time) const;\n\n  //! \\return The threads that are in the process. The first element (at index\n  //!     `0`) corresponds to the main thread.\n  const std::vector<Thread>& Threads();\n\n  //! \\return The modules loaded in the process. The first element (at index\n  //!     `0`) corresponds to the main executable.\n  const std::vector<ProcessInfo::Module>& Modules();\n\n  //! \\return A ProcessInfo object for the process being read.\n  const ProcessInfo& GetProcessInfo() const;\n\n  //! \\brief Decrements the thread suspend counts for all thread ids other than\n  //!     \\a except_thread_id.\n  //!\n  //! Used to adjust the thread suspend count to correspond to the actual values\n  //! for the process before Crashpad got involved.\n  void DecrementThreadSuspendCounts(uint64_t except_thread_id);\n\n private:\n  template <class Traits>\n  void ReadThreadData(bool is_64_reading_32);\n\n  HANDLE process_;\n  ProcessInfo process_info_;\n  ProcessMemoryWin process_memory_;\n  std::vector<Thread> threads_;\n  std::vector<ProcessInfo::Module> modules_;\n  ProcessSuspensionState suspension_state_;\n  bool initialized_threads_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_\n"
  },
  {
    "path": "snapshot/win/process_reader_win_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/process_reader_win.h\"\n\n#include <windows.h>\n#include <string.h>\n\n#include <algorithm>\n#include <array>\n#include <iterator>\n#include <set>\n#include <string>\n\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"test/scoped_set_thread_name.h\"\n#include \"test/win/win_multiprocess.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/synchronization/semaphore.h\"\n#include \"util/thread/thread.h\"\n#include \"util/win/context_wrappers.h\"\n#include \"util/win/scoped_process_suspend.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing ::testing::IsSupersetOf;\n\nTEST(ProcessReaderWin, SelfBasic) {\n  ProcessReaderWin process_reader;\n  ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),\n                                        ProcessSuspensionState::kRunning));\n\n#if !defined(ARCH_CPU_64_BITS)\n  EXPECT_FALSE(process_reader.Is64Bit());\n#else\n  EXPECT_TRUE(process_reader.Is64Bit());\n#endif\n\n  EXPECT_EQ(process_reader.GetProcessInfo().ProcessID(), GetCurrentProcessId());\n\n  static constexpr char kTestMemory[] = \"Some test memory\";\n  char buffer[std::size(kTestMemory)];\n  ASSERT_TRUE(process_reader.Memory()->Read(\n      reinterpret_cast<uintptr_t>(kTestMemory), sizeof(kTestMemory), &buffer));\n  EXPECT_STREQ(kTestMemory, buffer);\n}\n\nconstexpr char kTestMemory[] = \"Read me from another process\";\n\nclass ProcessReaderChild final : public WinMultiprocess {\n public:\n  ProcessReaderChild() : WinMultiprocess() {}\n\n  ProcessReaderChild(const ProcessReaderChild&) = delete;\n  ProcessReaderChild& operator=(const ProcessReaderChild&) = delete;\n\n  ~ProcessReaderChild() {}\n\n private:\n  void WinMultiprocessParent() override {\n    ProcessReaderWin process_reader;\n    ASSERT_TRUE(process_reader.Initialize(ChildProcess(),\n                                          ProcessSuspensionState::kRunning));\n\n#if !defined(ARCH_CPU_64_BITS)\n    EXPECT_FALSE(process_reader.Is64Bit());\n#else\n    EXPECT_TRUE(process_reader.Is64Bit());\n#endif\n\n    WinVMAddress address;\n    CheckedReadFileExactly(ReadPipeHandle(), &address, sizeof(address));\n\n    char buffer[sizeof(kTestMemory)];\n    ASSERT_TRUE(\n        process_reader.Memory()->Read(address, sizeof(kTestMemory), &buffer));\n    EXPECT_EQ(strcmp(kTestMemory, buffer), 0);\n  }\n\n  void WinMultiprocessChild() override {\n    WinVMAddress address = FromPointerCast<WinVMAddress>(kTestMemory);\n    CheckedWriteFile(WritePipeHandle(), &address, sizeof(address));\n\n    // Wait for the parent to signal that it's OK to exit by closing its end of\n    // the pipe.\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n};\n\nTEST(ProcessReaderWin, ChildBasic) {\n  WinMultiprocess::Run<ProcessReaderChild>();\n}\n\nTEST(ProcessReaderWin, SelfOneThread) {\n  const ScopedSetThreadName scoped_set_thread_name(\"SelfBasic\");\n  ProcessReaderWin process_reader;\n  ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),\n                                        ProcessSuspensionState::kRunning));\n\n  const std::vector<ProcessReaderWin::Thread>& threads =\n      process_reader.Threads();\n\n  // If other tests ran in this process previously, threads may have been\n  // created and may still be running. This check must look for at least one\n  // thread, not exactly one thread.\n  ASSERT_GE(threads.size(), 1u);\n\n  EXPECT_EQ(threads[0].id, GetCurrentThreadId());\n  if (ScopedSetThreadName::IsSupported()) {\n    EXPECT_EQ(threads[0].name, \"SelfBasic\");\n  }\n  EXPECT_NE(ProgramCounterFromCONTEXT(threads[0].context.context<CONTEXT>()),\n            nullptr);\n  EXPECT_EQ(threads[0].suspend_count, 0u);\n}\n\nclass ProcessReaderChildThreadSuspendCount final : public WinMultiprocess {\n public:\n  ProcessReaderChildThreadSuspendCount() : WinMultiprocess() {}\n\n  ProcessReaderChildThreadSuspendCount(\n      const ProcessReaderChildThreadSuspendCount&) = delete;\n  ProcessReaderChildThreadSuspendCount& operator=(\n      const ProcessReaderChildThreadSuspendCount&) = delete;\n\n  ~ProcessReaderChildThreadSuspendCount() {}\n\n private:\n  enum : unsigned int { kCreatedThreads = 3 };\n\n  class SleepingThread : public Thread {\n   public:\n    explicit SleepingThread(const std::string& thread_name)\n        : done_(nullptr), thread_name_(thread_name) {}\n\n    void SetHandle(Semaphore* done) {\n      done_= done;\n    }\n\n    void ThreadMain() override {\n      const ScopedSetThreadName scoped_set_thread_name(thread_name_);\n      done_->Wait();\n    }\n\n   private:\n    Semaphore* done_;\n    const std::string thread_name_;\n  };\n\n  void WinMultiprocessParent() override {\n    char c;\n    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));\n    ASSERT_EQ(c, ' ');\n\n    {\n      ProcessReaderWin process_reader;\n      ASSERT_TRUE(process_reader.Initialize(ChildProcess(),\n                                            ProcessSuspensionState::kRunning));\n\n      const auto& threads = process_reader.Threads();\n      ASSERT_GE(threads.size(), kCreatedThreads + 1);\n\n      for (const auto& thread : threads) {\n        EXPECT_EQ(thread.suspend_count, 0u);\n      }\n\n      if (ScopedSetThreadName::IsSupported()) {\n        EXPECT_EQ(threads[0].name, \"WinMultiprocessChild-Main\");\n\n        const std::set<std::string> expected_thread_names = {\n            \"WinMultiprocessChild-1\",\n            \"WinMultiprocessChild-2\",\n            \"WinMultiprocessChild-3\",\n        };\n        // Windows can create threads besides the ones created in\n        // WinMultiprocessChild(), so keep track of the (non-main) thread names\n        // and make sure all the expected names are present.\n        std::set<std::string> thread_names;\n        for (size_t i = 1; i < threads.size(); i++) {\n          if (!threads[i].name.empty()) {\n            thread_names.emplace(threads[i].name);\n          }\n        }\n\n        EXPECT_THAT(thread_names, IsSupersetOf(expected_thread_names));\n      }\n    }\n\n    {\n      ScopedProcessSuspend suspend(ChildProcess());\n\n      ProcessReaderWin process_reader;\n      ASSERT_TRUE(process_reader.Initialize(\n          ChildProcess(), ProcessSuspensionState::kSuspended));\n\n      // Confirm that thread counts are adjusted correctly for the process being\n      // suspended.\n      const auto& threads = process_reader.Threads();\n      ASSERT_GE(threads.size(), kCreatedThreads + 1);\n      for (const auto& thread : threads) {\n        EXPECT_EQ(thread.suspend_count, 0u);\n      }\n    }\n  }\n\n  void WinMultiprocessChild() override {\n    const ScopedSetThreadName scoped_set_thread_name(\n        \"WinMultiprocessChild-Main\");\n\n    // Create three dummy threads so we can confirm we read successfully read\n    // more than just the main thread.\n    std::array<SleepingThread, kCreatedThreads> threads = {\n        SleepingThread(std::string(\"WinMultiprocessChild-1\")),\n        SleepingThread(std::string(\"WinMultiprocessChild-2\")),\n        SleepingThread(std::string(\"WinMultiprocessChild-3\")),\n    };\n    Semaphore done(0);\n    for (auto& thread : threads)\n      thread.SetHandle(&done);\n    for (auto& thread : threads)\n      thread.Start();\n\n    char c = ' ';\n    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));\n\n    // Wait for the parent to signal that it's OK to exit by closing its end of\n    // the pipe.\n    CheckedReadFileAtEOF(ReadPipeHandle());\n\n    for (size_t i = 0; i < std::size(threads); ++i)\n      done.Signal();\n    for (auto& thread : threads)\n      thread.Join();\n  }\n};\n\nTEST(ProcessReaderWin, ChildThreadSuspendCounts) {\n  WinMultiprocess::Run<ProcessReaderChildThreadSuspendCount>();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/process_snapshot_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/process_snapshot_win.h\"\n\n#include <stddef.h>\n#include <wchar.h>\n\n#include <algorithm>\n#include <iterator>\n#include <string_view>\n#include <utility>\n\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/time.h\"\n#include \"util/win/nt_internals.h\"\n#include \"util/win/registration_protocol_win.h\"\n\nnamespace crashpad {\n\nProcessSnapshotWin::ProcessSnapshotWin()\n    : ProcessSnapshot(),\n      system_(),\n      threads_(),\n      modules_(),\n      exception_(),\n      memory_map_(),\n      process_reader_(),\n      report_id_(),\n      client_id_(),\n      annotations_simple_map_(),\n      snapshot_time_(),\n      options_(),\n      initialized_() {}\n\nProcessSnapshotWin::~ProcessSnapshotWin() {\n}\n\nbool ProcessSnapshotWin::Initialize(\n    HANDLE process,\n    ProcessSuspensionState suspension_state,\n    WinVMAddress exception_information_address,\n    WinVMAddress debug_critical_section_address) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  GetTimeOfDay(&snapshot_time_);\n\n  if (!process_reader_.Initialize(process, suspension_state))\n    return false;\n\n  client_id_.InitializeToZero();\n  system_.Initialize(&process_reader_);\n\n  if (process_reader_.Is64Bit()) {\n    InitializePebData<process_types::internal::Traits64>(\n        debug_critical_section_address);\n  } else {\n    InitializePebData<process_types::internal::Traits32>(\n        debug_critical_section_address);\n  }\n\n  InitializeModules();\n  InitializeUnloadedModules();\n\n  GetCrashpadOptionsInternal(&options_);\n  uint32_t* budget_remaining_pointer =\n      options_.gather_indirectly_referenced_memory == TriState::kEnabled\n          ? &options_.indirectly_referenced_memory_cap\n          : nullptr;\n\n  if (exception_information_address != 0) {\n    ExceptionInformation exception_information = {};\n    if (!process_reader_.Memory()->Read(exception_information_address,\n                                        sizeof(exception_information),\n                                        &exception_information)) {\n      LOG(WARNING) << \"ReadMemory ExceptionInformation failed\";\n      return false;\n    }\n\n    exception_.reset(new internal::ExceptionSnapshotWin());\n    if (!exception_->Initialize(&process_reader_,\n                                exception_information.thread_id,\n                                exception_information.exception_pointers,\n                                budget_remaining_pointer)) {\n      exception_.reset();\n      return false;\n    }\n  }\n\n  InitializeThreads(budget_remaining_pointer);\n\n  for (const MEMORY_BASIC_INFORMATION64& mbi :\n       process_reader_.GetProcessInfo().MemoryInfo()) {\n    memory_map_.push_back(\n        std::make_unique<internal::MemoryMapRegionSnapshotWin>(mbi));\n  }\n\n  for (const auto& module : modules_) {\n    for (const auto& range : module->ExtraMemoryRanges()) {\n      AddMemorySnapshot(range.base(), range.size(), &extra_memory_);\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nvoid ProcessSnapshotWin::GetCrashpadOptions(\n    CrashpadInfoClientOptions* options) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *options = options_;\n}\n\ncrashpad::ProcessID ProcessSnapshotWin::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.GetProcessInfo().ProcessID();\n}\n\ncrashpad::ProcessID ProcessSnapshotWin::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.GetProcessInfo().ParentProcessID();\n}\n\nvoid ProcessSnapshotWin::SnapshotTime(timeval* snapshot_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *snapshot_time = snapshot_time_;\n}\n\nvoid ProcessSnapshotWin::ProcessStartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  process_reader_.StartTime(start_time);\n}\n\nvoid ProcessSnapshotWin::ProcessCPUTimes(timeval* user_time,\n                                         timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  process_reader_.CPUTimes(user_time, system_time);\n}\n\nvoid ProcessSnapshotWin::ReportID(UUID* report_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *report_id = report_id_;\n}\n\nvoid ProcessSnapshotWin::ClientID(UUID* client_id) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *client_id = client_id_;\n}\n\nconst std::map<std::string, std::string>&\nProcessSnapshotWin::AnnotationsSimpleMap() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return annotations_simple_map_;\n}\n\nconst SystemSnapshot* ProcessSnapshotWin::System() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &system_;\n}\n\nstd::vector<const ThreadSnapshot*> ProcessSnapshotWin::Threads() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ThreadSnapshot*> threads;\n  for (const auto& thread : threads_) {\n    threads.push_back(thread.get());\n  }\n  return threads;\n}\n\nstd::vector<const ModuleSnapshot*> ProcessSnapshotWin::Modules() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const ModuleSnapshot*> modules;\n  for (const auto& module : modules_) {\n    modules.push_back(module.get());\n  }\n  return modules;\n}\n\nstd::vector<UnloadedModuleSnapshot> ProcessSnapshotWin::UnloadedModules()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return unloaded_modules_;\n}\n\nconst ExceptionSnapshot* ProcessSnapshotWin::Exception() const {\n  return exception_.get();\n}\n\nstd::vector<const MemoryMapRegionSnapshot*> ProcessSnapshotWin::MemoryMap()\n    const {\n  std::vector<const MemoryMapRegionSnapshot*> memory_map;\n  for (const auto& item : memory_map_) {\n    memory_map.push_back(item.get());\n  }\n  return memory_map;\n}\n\nstd::vector<HandleSnapshot> ProcessSnapshotWin::Handles() const {\n  std::vector<HandleSnapshot> result;\n  for (const auto& handle : process_reader_.GetProcessInfo().Handles()) {\n    HandleSnapshot snapshot;\n    // This is probably not strictly correct, but these are not localized so we\n    // expect them all to be in ASCII range anyway. This will need to be more\n    // carefully done if the object name is added.\n    snapshot.type_name = base::WideToUTF8(handle.type_name);\n    snapshot.handle = handle.handle;\n    snapshot.attributes = handle.attributes;\n    snapshot.granted_access = handle.granted_access;\n    snapshot.pointer_count = handle.pointer_count;\n    snapshot.handle_count = handle.handle_count;\n    result.push_back(snapshot);\n  }\n  return result;\n}\n\nstd::vector<const MemorySnapshot*> ProcessSnapshotWin::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> extra_memory;\n  for (const auto& em : extra_memory_) {\n    extra_memory.push_back(em.get());\n  }\n  return extra_memory;\n}\n\nconst ProcessMemory* ProcessSnapshotWin::Memory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_reader_.Memory();\n}\n\nvoid ProcessSnapshotWin::InitializeThreads(uint32_t* budget_remaining_pointer) {\n  const std::vector<ProcessReaderWin::Thread>& process_reader_threads =\n      process_reader_.Threads();\n  for (const ProcessReaderWin::Thread& process_reader_thread :\n       process_reader_threads) {\n    auto thread = std::make_unique<internal::ThreadSnapshotWin>();\n    if (thread->Initialize(&process_reader_,\n                           process_reader_thread,\n                           budget_remaining_pointer)) {\n      threads_.push_back(std::move(thread));\n    }\n  }\n}\n\nvoid ProcessSnapshotWin::InitializeModules() {\n  const std::vector<ProcessInfo::Module>& process_reader_modules =\n      process_reader_.Modules();\n  for (const ProcessInfo::Module& process_reader_module :\n       process_reader_modules) {\n    auto module = std::make_unique<internal::ModuleSnapshotWin>();\n    if (module->Initialize(&process_reader_, process_reader_module)) {\n      modules_.push_back(std::move(module));\n    }\n  }\n}\n\nvoid ProcessSnapshotWin::InitializeUnloadedModules() {\n  // As documented by https://msdn.microsoft.com/library/cc678403.aspx, we can\n  // retrieve the location for our unload events, and use that address in the\n  // target process. Unfortunately, this of course only works for 64-reading-64\n  // and 32-reading-32, so at the moment, we simply do not retrieve unloaded\n  // modules for 64-reading-32. See https://crashpad.chromium.org/bug/89.\n\n#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)\n  if (!process_reader_.Is64Bit()) {\n    LOG(ERROR)\n        << \"reading unloaded modules across bitness not currently supported\";\n    return;\n  }\n  using Traits = process_types::internal::Traits64;\n#elif defined(ARCH_CPU_X86)\n  using Traits = process_types::internal::Traits32;\n#else\n#error port\n#endif\n\n  ULONG* element_size;\n  ULONG* element_count;\n  void* event_trace_address;\n  RtlGetUnloadEventTraceEx(&element_size, &element_count, &event_trace_address);\n\n  if (*element_size < sizeof(RTL_UNLOAD_EVENT_TRACE<Traits>)) {\n    LOG(ERROR) << \"unexpected unloaded module list element size\";\n    return;\n  }\n\n  const WinVMAddress address_in_target_process =\n      FromPointerCast<WinVMAddress>(event_trace_address);\n\n  Traits::Pointer pointer_to_array;\n  if (!process_reader_.Memory()->Read(address_in_target_process,\n                                      sizeof(pointer_to_array),\n                                      &pointer_to_array)) {\n    LOG(ERROR) << \"failed to read target address\";\n    return;\n  }\n\n  // No unloaded modules.\n  if (pointer_to_array == 0)\n    return;\n\n  const size_t data_size = *element_size * *element_count;\n  std::vector<uint8_t> data(data_size);\n  if (!process_reader_.Memory()->Read(pointer_to_array, data_size, &data[0])) {\n    LOG(ERROR) << \"failed to read unloaded module data\";\n    return;\n  }\n\n  for (ULONG i = 0; i < *element_count; ++i) {\n    const uint8_t* base_address = &data[i * *element_size];\n    const auto& uet =\n        *reinterpret_cast<const RTL_UNLOAD_EVENT_TRACE<Traits>*>(base_address);\n    if (uet.ImageName[0] != 0) {\n      unloaded_modules_.push_back(UnloadedModuleSnapshot(\n          uet.BaseAddress,\n          uet.SizeOfImage,\n          uet.CheckSum,\n          uet.TimeDateStamp,\n          base::WideToUTF8(std::wstring_view(\n              uet.ImageName,\n              wcsnlen(uet.ImageName, std::size(uet.ImageName))))));\n    }\n  }\n}\n\nvoid ProcessSnapshotWin::GetCrashpadOptionsInternal(\n    CrashpadInfoClientOptions* options) {\n  CrashpadInfoClientOptions local_options;\n\n  for (const auto& module : modules_) {\n    CrashpadInfoClientOptions module_options;\n    module->GetCrashpadOptions(&module_options);\n\n    if (local_options.crashpad_handler_behavior == TriState::kUnset) {\n      local_options.crashpad_handler_behavior =\n          module_options.crashpad_handler_behavior;\n    }\n    if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {\n      local_options.system_crash_reporter_forwarding =\n          module_options.system_crash_reporter_forwarding;\n    }\n    if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {\n      local_options.gather_indirectly_referenced_memory =\n          module_options.gather_indirectly_referenced_memory;\n      local_options.indirectly_referenced_memory_cap =\n          module_options.indirectly_referenced_memory_cap;\n    }\n\n    // If non-default values have been found for all options, the loop can end\n    // early.\n    if (local_options.crashpad_handler_behavior != TriState::kUnset &&\n        local_options.system_crash_reporter_forwarding != TriState::kUnset &&\n        local_options.gather_indirectly_referenced_memory != TriState::kUnset) {\n      break;\n    }\n  }\n\n  *options = local_options;\n}\n\ntemplate <class Traits>\nvoid ProcessSnapshotWin::InitializePebData(\n    WinVMAddress debug_critical_section_address) {\n  WinVMAddress peb_address;\n  WinVMSize peb_size;\n  process_reader_.GetProcessInfo().Peb(&peb_address, &peb_size);\n  AddMemorySnapshot(peb_address, peb_size, &extra_memory_);\n\n  process_types::PEB<Traits> peb_data;\n  if (!process_reader_.Memory()->Read(\n          peb_address, base::checked_cast<size_t>(peb_size), &peb_data)) {\n    LOG(ERROR) << \"ReadMemory PEB\";\n    return;\n  }\n\n  process_types::PEB_LDR_DATA<Traits> peb_ldr_data;\n  AddMemorySnapshot(peb_data.Ldr, sizeof(peb_ldr_data), &extra_memory_);\n  if (!process_reader_.Memory()->Read(\n          peb_data.Ldr, sizeof(peb_ldr_data), &peb_ldr_data)) {\n    LOG(ERROR) << \"ReadMemory PEB_LDR_DATA\";\n  } else {\n    // Walk the LDR structure to retrieve its pointed-to data.\n    AddMemorySnapshotForLdrLIST_ENTRY(\n        peb_ldr_data.InLoadOrderModuleList,\n        offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>, InLoadOrderLinks),\n        &extra_memory_);\n    AddMemorySnapshotForLdrLIST_ENTRY(\n        peb_ldr_data.InMemoryOrderModuleList,\n        offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,\n                 InMemoryOrderLinks),\n        &extra_memory_);\n    AddMemorySnapshotForLdrLIST_ENTRY(\n        peb_ldr_data.InInitializationOrderModuleList,\n        offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,\n                 InInitializationOrderLinks),\n        &extra_memory_);\n  }\n\n  process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;\n  if (!process_reader_.Memory()->Read(peb_data.ProcessParameters,\n                                      sizeof(process_parameters),\n                                      &process_parameters)) {\n    LOG(ERROR) << \"ReadMemory RTL_USER_PROCESS_PARAMETERS\";\n    return;\n  }\n  AddMemorySnapshot(\n      peb_data.ProcessParameters, sizeof(process_parameters), &extra_memory_);\n\n  AddMemorySnapshotForUNICODE_STRING(\n      process_parameters.CurrentDirectory.DosPath, &extra_memory_);\n  AddMemorySnapshotForUNICODE_STRING(process_parameters.DllPath,\n                                     &extra_memory_);\n  AddMemorySnapshotForUNICODE_STRING(process_parameters.ImagePathName,\n                                     &extra_memory_);\n  AddMemorySnapshotForUNICODE_STRING(process_parameters.CommandLine,\n                                     &extra_memory_);\n  AddMemorySnapshotForUNICODE_STRING(process_parameters.WindowTitle,\n                                     &extra_memory_);\n  AddMemorySnapshotForUNICODE_STRING(process_parameters.DesktopInfo,\n                                     &extra_memory_);\n  AddMemorySnapshotForUNICODE_STRING(process_parameters.ShellInfo,\n                                     &extra_memory_);\n  AddMemorySnapshotForUNICODE_STRING(process_parameters.RuntimeData,\n                                     &extra_memory_);\n  AddMemorySnapshot(\n      process_parameters.Environment,\n      DetermineSizeOfEnvironmentBlock(process_parameters.Environment),\n      &extra_memory_);\n\n  // Walk the loader lock which is directly referenced by the PEB.\n  ReadLock<Traits>(peb_data.LoaderLock, &extra_memory_);\n\n  // TODO(scottmg): Use debug_critical_section_address to walk the list of\n  // locks (see history of this file for walking code). In some configurations\n  // this can walk many thousands of locks, so we may want to get some\n  // annotation from the client for which locks to grab. Unfortunately, without\n  // walking the list, the !locks command in windbg won't work because it\n  // requires the lock pointed to by ntdll!RtlCriticalSectionList, which we\n  // won't have captured.\n}\n\nvoid ProcessSnapshotWin::AddMemorySnapshot(\n    WinVMAddress address,\n    WinVMSize size,\n    std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into) {\n  if (size == 0)\n    return;\n\n  if (!process_reader_.GetProcessInfo().LoggingRangeIsFullyReadable(\n          CheckedRange<WinVMAddress, WinVMSize>(address, size))) {\n    return;\n  }\n\n  // If we have already added this exact range, don't add it again. This is\n  // useful for the LDR module lists which are a set of doubly-linked lists, all\n  // pointing to the same module name strings.\n  // TODO(scottmg): A more general version of this, handling overlapping,\n  // contained, etc. https://crashpad.chromium.org/bug/61.\n  for (const auto& memory_snapshot : *into) {\n    if (memory_snapshot->Address() == address &&\n        memory_snapshot->Size() == size) {\n      return;\n    }\n  }\n\n  into->push_back(std::make_unique<internal::MemorySnapshotGeneric>());\n  into->back()->Initialize(process_reader_.Memory(), address, size);\n}\n\ntemplate <class Traits>\nvoid ProcessSnapshotWin::AddMemorySnapshotForUNICODE_STRING(\n    const process_types::UNICODE_STRING<Traits>& us,\n    std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into) {\n  AddMemorySnapshot(us.Buffer, us.Length, into);\n}\n\ntemplate <class Traits>\nvoid ProcessSnapshotWin::AddMemorySnapshotForLdrLIST_ENTRY(\n    const process_types::LIST_ENTRY<Traits>& le,\n    size_t offset_of_member,\n    std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into) {\n  // Walk the doubly-linked list of entries, adding the list memory itself, as\n  // well as pointed-to strings.\n  typename Traits::Pointer last = le.Blink;\n  process_types::LDR_DATA_TABLE_ENTRY<Traits> entry;\n  typename Traits::Pointer cur = le.Flink;\n  for (;;) {\n    // |cur| is the pointer to LIST_ENTRY embedded in the LDR_DATA_TABLE_ENTRY.\n    // So we need to offset back to the beginning of the structure.\n    if (!process_reader_.Memory()->Read(\n            cur - offset_of_member, sizeof(entry), &entry)) {\n      return;\n    }\n    AddMemorySnapshot(cur - offset_of_member, sizeof(entry), into);\n    AddMemorySnapshotForUNICODE_STRING(entry.FullDllName, into);\n    AddMemorySnapshotForUNICODE_STRING(entry.BaseDllName, into);\n\n    process_types::LIST_ENTRY<Traits>* links =\n        reinterpret_cast<process_types::LIST_ENTRY<Traits>*>(\n            reinterpret_cast<unsigned char*>(&entry) + offset_of_member);\n    cur = links->Flink;\n    if (cur == last)\n      break;\n  }\n}\n\nWinVMSize ProcessSnapshotWin::DetermineSizeOfEnvironmentBlock(\n    WinVMAddress start_of_environment_block) {\n  // https://blogs.msdn.microsoft.com/oldnewthing/20100203-00/?p=15083: On newer\n  // OSs there's no stated limit, but in practice grabbing 32k characters should\n  // be more than enough.\n  std::wstring env_block;\n  env_block.resize(32768);\n  size_t bytes_read = process_reader_.Memory()->ReadAvailableMemory(\n      start_of_environment_block,\n      env_block.size() * sizeof(env_block[0]),\n      &env_block[0]);\n  env_block.resize(\n      static_cast<unsigned int>(bytes_read / sizeof(env_block[0])));\n  static constexpr wchar_t terminator[] = {0, 0};\n  size_t at = env_block.find(std::wstring(terminator, std::size(terminator)));\n  if (at != std::wstring::npos)\n    env_block.resize(at + std::size(terminator));\n\n  return env_block.size() * sizeof(env_block[0]);\n}\n\ntemplate <class Traits>\nvoid ProcessSnapshotWin::ReadLock(\n    WinVMAddress start,\n    std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into) {\n  // We're walking the RTL_CRITICAL_SECTION_DEBUG ProcessLocksList, but starting\n  // from an actual RTL_CRITICAL_SECTION, so start by getting to the first\n  // RTL_CRITICAL_SECTION_DEBUG.\n\n  process_types::RTL_CRITICAL_SECTION<Traits> critical_section;\n  if (!process_reader_.Memory()->Read(\n          start, sizeof(critical_section), &critical_section)) {\n    LOG(ERROR) << \"failed to read RTL_CRITICAL_SECTION\";\n    return;\n  }\n\n  AddMemorySnapshot(\n      start, sizeof(process_types::RTL_CRITICAL_SECTION<Traits>), into);\n\n  constexpr decltype(critical_section.DebugInfo) kInvalid =\n      static_cast<decltype(critical_section.DebugInfo)>(-1);\n  if (critical_section.DebugInfo == kInvalid)\n    return;\n\n  AddMemorySnapshot(critical_section.DebugInfo,\n                    sizeof(process_types::RTL_CRITICAL_SECTION_DEBUG<Traits>),\n                    into);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/process_snapshot_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_\n\n#include <windows.h>\n#include <stdint.h>\n#include <sys/time.h>\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"client/crashpad_info.h\"\n#include \"snapshot/crashpad_info_client_options.h\"\n#include \"snapshot/exception_snapshot.h\"\n#include \"snapshot/memory_map_region_snapshot.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"snapshot/module_snapshot.h\"\n#include \"snapshot/process_snapshot.h\"\n#include \"snapshot/system_snapshot.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"snapshot/unloaded_module_snapshot.h\"\n#include \"snapshot/win/exception_snapshot_win.h\"\n#include \"snapshot/win/memory_map_region_snapshot_win.h\"\n#include \"snapshot/win/module_snapshot_win.h\"\n#include \"snapshot/win/system_snapshot_win.h\"\n#include \"snapshot/win/thread_snapshot_win.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/process/process_id.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/process_structs.h\"\n\nnamespace crashpad {\n\n//! \\brief A ProcessSnapshot of a running (or crashed) process running on a\n//!     Windows system.\nclass ProcessSnapshotWin final : public ProcessSnapshot {\n public:\n  ProcessSnapshotWin();\n\n  ProcessSnapshotWin(const ProcessSnapshotWin&) = delete;\n  ProcessSnapshotWin& operator=(const ProcessSnapshotWin&) = delete;\n\n  ~ProcessSnapshotWin() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process The handle to create a snapshot from.\n  //! \\param[in] suspension_state Whether \\a process has been suspended by the\n  //!     caller.\n  //! \\param[in] exception_information_address The address in the client\n  //!     process's address space of an ExceptionInformation structure. May be\n  //!     `0`, in which case no exception data will be recorded.\n  //! \\param[in] debug_critical_section_address The address in the target\n  //!     process's address space of a `CRITICAL_SECTION` allocated with valid\n  //!     `.DebugInfo`. Used as a starting point to walk the process's locks.\n  //!     May be `0`.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  //!\n  //! \\sa ScopedProcessSuspend\n  bool Initialize(HANDLE process,\n                  ProcessSuspensionState suspension_state,\n                  WinVMAddress exception_information_address,\n                  WinVMAddress debug_critical_section_address);\n\n  //! \\brief Sets the value to be returned by ReportID().\n  //!\n  //! The crash report ID is under the control of the snapshot producer, which\n  //! may call this method to set the report ID. If this is not done, ReportID()\n  //! will return an identifier consisting entirely of zeroes.\n  void SetReportID(const UUID& report_id) { report_id_ = report_id; }\n\n  //! \\brief Sets the value to be returned by ClientID().\n  //!\n  //! The client ID is under the control of the snapshot producer, which may\n  //! call this method to set the client ID. If this is not done, ClientID()\n  //! will return an identifier consisting entirely of zeroes.\n  void SetClientID(const UUID& client_id) { client_id_ = client_id; }\n\n  //! \\brief Sets the value to be returned by AnnotationsSimpleMap().\n  //!\n  //! All process annotations are under the control of the snapshot producer,\n  //! which may call this method to establish these annotations. Contrast this\n  //! with module annotations, which are under the control of the process being\n  //! snapshotted.\n  void SetAnnotationsSimpleMap(\n      const std::map<std::string, std::string>& annotations_simple_map) {\n    annotations_simple_map_ = annotations_simple_map;\n  }\n\n  //! \\brief Returns options from CrashpadInfo structures found in modules in\n  //!     the process.\n  //!\n  //! \\param[out] options Options set in CrashpadInfo structures in modules in\n  //!     the process.\n  void GetCrashpadOptions(CrashpadInfoClientOptions* options);\n\n  // ProcessSnapshot:\n\n  crashpad::ProcessID ProcessID() const override;\n  crashpad::ProcessID ParentProcessID() const override;\n  void SnapshotTime(timeval* snapshot_time) const override;\n  void ProcessStartTime(timeval* start_time) const override;\n  void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override;\n  void ReportID(UUID* report_id) const override;\n  void ClientID(UUID* client_id) const override;\n  const std::map<std::string, std::string>& AnnotationsSimpleMap()\n      const override;\n  const SystemSnapshot* System() const override;\n  std::vector<const ThreadSnapshot*> Threads() const override;\n  std::vector<const ModuleSnapshot*> Modules() const override;\n  std::vector<UnloadedModuleSnapshot> UnloadedModules() const override;\n  const ExceptionSnapshot* Exception() const override;\n  std::vector<const MemoryMapRegionSnapshot*> MemoryMap() const override;\n  std::vector<HandleSnapshot> Handles() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n  const ProcessMemory* Memory() const override;\n\n private:\n  // Initializes threads_ on behalf of Initialize().\n  void InitializeThreads(uint32_t* indirectly_referenced_memory_cap);\n\n  // Initializes modules_ on behalf of Initialize().\n  void InitializeModules();\n\n  // Initializes unloaded_modules_ on behalf of Initialize().\n  void InitializeUnloadedModules();\n\n  // Initializes options_ on behalf of Initialize().\n  void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options);\n\n  // Initializes various memory blocks reachable from the PEB on behalf of\n  // Initialize().\n  template <class Traits>\n  void InitializePebData(WinVMAddress debug_critical_section_address);\n\n  void AddMemorySnapshot(\n      WinVMAddress address,\n      WinVMSize size,\n      std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into);\n\n  template <class Traits>\n  void AddMemorySnapshotForUNICODE_STRING(\n      const process_types::UNICODE_STRING<Traits>& us,\n      std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into);\n\n  template <class Traits>\n  void AddMemorySnapshotForLdrLIST_ENTRY(\n      const process_types::LIST_ENTRY<Traits>& le,\n      size_t offset_of_member,\n      std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into);\n\n  WinVMSize DetermineSizeOfEnvironmentBlock(\n      WinVMAddress start_of_environment_block);\n\n  // Starting from the address of a CRITICAL_SECTION, add a lock and, if valid,\n  // its .DebugInfo field to the snapshot.\n  template <class Traits>\n  void ReadLock(\n      WinVMAddress start,\n      std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>>* into);\n\n  internal::SystemSnapshotWin system_;\n  std::vector<std::unique_ptr<internal::MemorySnapshotGeneric>> extra_memory_;\n  std::vector<std::unique_ptr<internal::ThreadSnapshotWin>> threads_;\n  std::vector<std::unique_ptr<internal::ModuleSnapshotWin>> modules_;\n  std::vector<UnloadedModuleSnapshot> unloaded_modules_;\n  std::unique_ptr<internal::ExceptionSnapshotWin> exception_;\n  std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotWin>>\n      memory_map_;\n  ProcessReaderWin process_reader_;\n  UUID report_id_;\n  UUID client_id_;\n  std::map<std::string, std::string> annotations_simple_map_;\n  timeval snapshot_time_;\n  CrashpadInfoClientOptions options_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_\n"
  },
  {
    "path": "snapshot/win/process_snapshot_win_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/process_snapshot_win.h\"\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/win/pe_image_reader.h\"\n#include \"snapshot/win/process_reader_win.h\"\n#include \"test/errors.h\"\n#include \"test/test_paths.h\"\n#include \"test/win/child_launcher.h\"\n#include \"util/file/file_io.h\"\n#include \"util/win/scoped_handle.h\"\n#include \"util/win/scoped_process_suspend.h\"\n#include \"util/win/scoped_set_event.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid TestImageReaderChild(const TestPaths::Architecture architecture) {\n  UUID done_uuid;\n  done_uuid.InitializeWithNew();\n  ScopedKernelHANDLE done(\n      CreateEvent(nullptr, true, false, done_uuid.ToWString().c_str()));\n  ASSERT_TRUE(done.is_valid()) << ErrorMessage(\"CreateEvent\");\n\n  base::FilePath child_test_executable =\n      TestPaths::BuildArtifact(L\"snapshot\",\n                               L\"image_reader\",\n                               TestPaths::FileType::kExecutable,\n                               architecture);\n  ChildLauncher child(child_test_executable, done_uuid.ToWString());\n  ASSERT_NO_FATAL_FAILURE(child.Start());\n\n  ScopedSetEvent set_done(done.get());\n\n  char c;\n  ASSERT_TRUE(\n      LoggingReadFileExactly(child.stdout_read_handle(), &c, sizeof(c)));\n  ASSERT_EQ(c, ' ');\n\n  {\n    ScopedProcessSuspend suspend(child.process_handle());\n\n    ProcessSnapshotWin process_snapshot;\n    ASSERT_TRUE(process_snapshot.Initialize(\n        child.process_handle(), ProcessSuspensionState::kSuspended, 0, 0));\n\n    ASSERT_GE(process_snapshot.Modules().size(), 2u);\n\n    UUID uuid;\n    DWORD age;\n    std::string pdbname;\n    const std::string suffix(\".pdb\");\n\n    // Check the main .exe to see that we can retrieve its sections.\n    auto module = reinterpret_cast<const internal::ModuleSnapshotWin*>(\n        process_snapshot.Modules()[0]);\n    ASSERT_TRUE(module->pe_image_reader().DebugDirectoryInformation(\n        &uuid, &age, &pdbname));\n    EXPECT_NE(pdbname.find(\"crashpad_snapshot_test_image_reader\"),\n              std::string::npos);\n    EXPECT_EQ(\n        pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix),\n        0);\n\n    // Check the dll it loads too.\n    module = reinterpret_cast<const internal::ModuleSnapshotWin*>(\n        process_snapshot.Modules().back());\n    ASSERT_TRUE(module->pe_image_reader().DebugDirectoryInformation(\n        &uuid, &age, &pdbname));\n    EXPECT_NE(pdbname.find(\"crashpad_snapshot_test_image_reader_module\"),\n              std::string::npos);\n    EXPECT_EQ(\n        pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix),\n        0);\n\n    // Sum the size of the extra memory in all the threads and confirm it's near\n    // the limit that the child process set in its CrashpadInfo.\n    EXPECT_GE(process_snapshot.Threads().size(), 100u);\n\n    size_t extra_memory_total = 0;\n    for (const auto* thread : process_snapshot.Threads()) {\n      for (const auto* extra_memory : thread->ExtraMemory()) {\n        extra_memory_total += extra_memory->Size();\n      }\n    }\n\n    // Confirm that less than 1.2M of extra data was gathered. The cap is set to\n    // only 100K, but there are other \"extra memory\" regions that aren't\n    // included in the cap. (Completely uncapped it would be > 10M.)\n    EXPECT_LT(extra_memory_total, 1200000u);\n  }\n\n  // Tell the child it can terminate.\n  EXPECT_TRUE(set_done.Set());\n\n  EXPECT_EQ(child.WaitForExit(), 0u);\n}\n\nTEST(ProcessSnapshotTest, CrashpadInfoChild) {\n  TestImageReaderChild(TestPaths::Architecture::kDefault);\n}\n\n#if defined(ARCH_CPU_64_BITS)\nTEST(ProcessSnapshotTest, CrashpadInfoChildWOW64) {\n  if (!TestPaths::Has32BitBuildArtifacts()) {\n    GTEST_SKIP();\n  }\n\n  TestImageReaderChild(TestPaths::Architecture::k32Bit);\n}\n#endif\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/process_subrange_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/process_subrange_reader.h\"\n\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"snapshot/win/process_reader_win.h\"\n\nnamespace crashpad {\n\nProcessSubrangeReader::ProcessSubrangeReader()\n    : name_(),\n      range_(),\n      process_reader_(nullptr) {\n}\n\nProcessSubrangeReader::~ProcessSubrangeReader() {\n}\n\nbool ProcessSubrangeReader::Initialize(ProcessReaderWin* process_reader,\n                                       WinVMAddress base,\n                                       WinVMSize size,\n                                       const std::string& name) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!InitializeInternal(process_reader, base, size, name)) {\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessSubrangeReader::InitializeSubrange(\n    const ProcessSubrangeReader& that,\n    WinVMAddress base,\n    WinVMSize size,\n    const std::string& sub_name) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  INITIALIZATION_STATE_DCHECK_VALID(that.initialized_);\n\n  if (!InitializeInternal(\n          that.process_reader_, base, size, that.name_ + \" \" + sub_name)) {\n    return false;\n  }\n\n  if (!that.range_.ContainsRange(range_)) {\n    LOG(WARNING) << \"range \" << range_.AsString() << \" outside of  range \"\n                 << that.range_.AsString() << \" for \" << name_;\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessSubrangeReader::ReadMemory(WinVMAddress address,\n                                       WinVMSize size,\n                                       void* into) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  CheckedWinAddressRange read_range(process_reader_->Is64Bit(), address, size);\n  if (!read_range.IsValid()) {\n    LOG(WARNING) << \"invalid read range \" << read_range.AsString();\n    return false;\n  }\n\n  if (!range_.ContainsRange(read_range)) {\n    LOG(WARNING) << \"attempt to read outside of \" << name_ << \" range \"\n                 << range_.AsString() << \" at range \" << read_range.AsString();\n    return false;\n  }\n\n  return process_reader_->Memory()->Read(\n      address, base::checked_cast<size_t>(size), into);\n}\n\nbool ProcessSubrangeReader::InitializeInternal(ProcessReaderWin* process_reader,\n                                               WinVMAddress base,\n                                               WinVMSize size,\n                                               const std::string& name) {\n  range_.SetRange(process_reader->Is64Bit(), base, size);\n  if (!range_.IsValid()) {\n    LOG(WARNING) << \"invalid range \" << range_.AsString() << \" for \" << name;\n    return false;\n  }\n\n  name_ = name;\n  process_reader_ = process_reader;\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/process_subrange_reader.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_\n\n#include <string>\n\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/checked_win_address_range.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderWin;\n\n//! \\brief A wrapper for ProcessReaderWin that only allows a specific subrange\n//!     to be read from.\n//!\n//! This class is useful to restrict reads to a specific address range, such as\n//! the address range occupied by a loaded module, or a specific section within\n//! a module.\nclass ProcessSubrangeReader {\n public:\n  ProcessSubrangeReader();\n\n  ProcessSubrangeReader(const ProcessSubrangeReader&) = delete;\n  ProcessSubrangeReader& operator=(const ProcessSubrangeReader&) = delete;\n\n  ~ProcessSubrangeReader();\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A reader for a remote process.\n  //! \\param[in] base The base address for the range that reads should be\n  //!     restricted to.\n  //! \\param[in] size The size of the range that reads should be restricted to.\n  //! \\param[in] name The range’s name, a string to be used in logged messages.\n  //!     This string is for diagnostic purposes.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged. The\n  //!     other methods in this class must not be called unless this method or\n  //!     InitializeSubrange() has returned true.\n  bool Initialize(ProcessReaderWin* process_reader,\n                  WinVMAddress base,\n                  WinVMSize size,\n                  const std::string& name);\n\n  //! \\brief Initializes the object to a subrange of an existing\n  //!     ProcessSubrangeReader.\n  //!\n  //! The subrange identified by \\a base and \\a size must be contained within\n  //! the subrange in \\a that.\n  //!\n  //! \\param[in] that The existing ProcessSubrangeReader to base the new object\n  //!     on.\n  //! \\param[in] base The base address for the range that reads should be\n  //!     restricted to.\n  //! \\param[in] size The size of the range that reads should be restricted to.\n  //! \\param[in] sub_name A description of the subrange, which will be appended\n  //!     to the \\a name in \\a that and used in logged messages. This string is\n  //!     for diagnostic purposes.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged. The\n  //!     other methods in this class must not be called unless this method or\n  //!     Initialize() has returned true.\n  bool InitializeSubrange(const ProcessSubrangeReader& that,\n                          WinVMAddress base,\n                          WinVMSize size,\n                          const std::string& sub_name);\n\n  bool Is64Bit() const { return range_.Is64Bit(); }\n  WinVMAddress Base() const { return range_.Base(); }\n  WinVMAddress Size() const { return range_.Size(); }\n  const std::string& name() const { return name_; }\n\n  //! \\brief Reads memory from the remote process.\n  //!\n  //! The range specified by \\a address and \\a size must be contained within\n  //! the range that this object is permitted to read.\n  //!\n  //! \\param[in] address The address to read from.\n  //! \\param[in] size The size of data to read, in bytes.\n  //! \\param[out] into The buffer to read data into.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool ReadMemory(WinVMAddress address, WinVMSize size, void* into) const;\n\n private:\n  // Common helper for Initialize() and InitializeSubrange().\n  bool InitializeInternal(ProcessReaderWin* process_reader,\n                          WinVMAddress base,\n                          WinVMSize size,\n                          const std::string& name);\n\n  std::string name_;\n  CheckedWinAddressRange range_;\n  ProcessReaderWin* process_reader_;  // weak\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_\n"
  },
  {
    "path": "snapshot/win/system_snapshot_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/system_snapshot_win.h\"\n\n#include <intrin.h>\n#include <powrprof.h>\n#include <windows.h>\n#include <winnt.h>\n\n// Must be after windows.h.\n#include <versionhelpers.h>\n\n#include <algorithm>\n#include <utility>\n#include <vector>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n#include \"util/win/module_version.h\"\n#include \"util/win/scoped_registry_key.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n//! \\brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileFlags\n//!     value.\nstd::string GetStringForFileFlags(uint32_t file_flags) {\n  std::string result;\n  DCHECK_EQ(file_flags & VS_FF_INFOINFERRED, 0u);\n  if (file_flags & VS_FF_DEBUG)\n    result += \"Debug,\";\n  if (file_flags & VS_FF_PATCHED)\n    result += \"Patched,\";\n  if (file_flags & VS_FF_PRERELEASE)\n    result += \"Prerelease,\";\n  if (file_flags & VS_FF_PRIVATEBUILD)\n    result += \"Private,\";\n  if (file_flags & VS_FF_SPECIALBUILD)\n    result += \"Special,\";\n  if (!result.empty())\n    return result.substr(0, result.size() - 1);  // Remove trailing comma.\n  return result;\n}\n\n//! \\brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileOS value.\nstd::string GetStringForFileOS(uint32_t file_os) {\n  // There are a variety of ancient things this could theoretically be. In\n  // practice, we're always going to get VOS_NT_WINDOWS32 here.\n  if ((file_os & VOS_NT_WINDOWS32) == VOS_NT_WINDOWS32)\n    return \"Windows NT\";\n  else\n    return \"Unknown\";\n}\n\n//! \\brief Reads a DWORD from the registry and returns it as an int.\nbool ReadRegistryDWORD(HKEY key, const wchar_t* name, int* out_value) {\n  DWORD type;\n  DWORD local_value;\n  DWORD size = sizeof(local_value);\n  if (RegQueryValueExW(key,\n                       name,\n                       nullptr,\n                       &type,\n                       reinterpret_cast<BYTE*>(&local_value),\n                       &size) == ERROR_SUCCESS &&\n      type == REG_DWORD) {\n    *out_value = static_cast<int>(local_value);\n    return true;\n  }\n  return false;\n}\n\n//! \\brief Reads a string from the registry and returns it as an int.\nbool ReadRegistryDWORDFromSZ(HKEY key, const char* name, int* out_value) {\n  char string_value[11];\n  DWORD type;\n  // Leave space for a terminating zero.\n  DWORD size = sizeof(string_value) - sizeof(string_value[0]);\n  // Use the 'A' version of this function so that we can use\n  // StringToNumber.\n  if (RegQueryValueExA(key,\n                       name,\n                       nullptr,\n                       &type,\n                       reinterpret_cast<BYTE*>(&string_value),\n                       &size) == ERROR_SUCCESS &&\n      type == REG_SZ) {\n    // Make sure the string is null-terminated.\n    string_value[size / sizeof(string_value[0])] = '\\0';\n    unsigned local_value;\n    if (StringToNumber(string_value, &local_value)) {\n      *out_value = local_value;\n      return true;\n    }\n  }\n  return false;\n}\n\n}  // namespace\n\nnamespace internal {\n\nSystemSnapshotWin::SystemSnapshotWin()\n    : SystemSnapshot(),\n      os_version_full_(),\n      os_version_build_(),\n      process_reader_(nullptr),\n      os_version_major_(0),\n      os_version_minor_(0),\n      os_version_bugfix_(0),\n      os_server_(false),\n      initialized_() {}\n\nSystemSnapshotWin::~SystemSnapshotWin() {}\n\nvoid SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  process_reader_ = process_reader;\n\n  // We use both IsWindowsServer() (which uses VerifyVersionInfo() internally)\n  // and GetModuleVersionAndType() (which uses VerQueryValue() internally).\n  // VerifyVersionInfo() is not trustworthy after Windows 8 (depending on the\n  // application manifest) so its data is used only to fill the os_server_\n  // field, and the rest comes from the version information stamped on\n  // kernel32.dll and from the registry.\n  os_server_ = IsWindowsServer();\n\n  // kernel32.dll used to be a good way to get a non-lying version number, but\n  // kernel32.dll has been refactored into multiple DLLs so it sometimes does\n  // not get updated when a new version of Windows ships, especially on\n  // Windows 11. Additionally, pairs of releases such as 19041/19042\n  // (20H1/20H2) actually have identical code and have their differences\n  // enabled by a configuration setting. Therefore the recommended way to get\n  // OS version information on recent versions of Windows is to read it from the\n  // registry. If any of the version-number components are missing from the\n  // registry (on Windows 7, for instance) then kernel32.dll is used as a\n  // fallback.\n  bool version_data_found = false;\n  int os_version_build = 0;\n  HKEY key;\n  if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,\n                    L\"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\",\n                    0,\n                    KEY_QUERY_VALUE,\n                    &key) == ERROR_SUCCESS) {\n    ScopedRegistryKey scoped_key(key);\n\n    // Read the four components of the version from the registry.\n    // UBR apparently stands for Update Build Revision and it goes up every\n    // month when patches are installed. The full version is stored in the\n    // registry as:\n    // CurrentMajorVersionNumber.CurrentMinorVersionNumber.CurrentBuildNumber.UBR\n    if (ReadRegistryDWORD(\n            key, L\"CurrentMajorVersionNumber\", &os_version_major_) &&\n        ReadRegistryDWORD(\n            key, L\"CurrentMinorVersionNumber\", &os_version_minor_) &&\n        ReadRegistryDWORDFromSZ(\n            key, \"CurrentBuildNumber\", &os_version_bugfix_) &&\n        ReadRegistryDWORD(key, L\"UBR\", &os_version_build)) {\n      // Since we found all four components in the registry we don't need\n      // to read them from kernel32.dll.\n      version_data_found = true;\n    }\n  }\n\n  static constexpr wchar_t kSystemDll[] = L\"kernel32.dll\";\n  VS_FIXEDFILEINFO ffi;\n  if (GetModuleVersionAndType(base::FilePath(kSystemDll), &ffi)) {\n    std::string flags_string = GetStringForFileFlags(ffi.dwFileFlags);\n    std::string os_name = GetStringForFileOS(ffi.dwFileOS);\n    if (!version_data_found) {\n      os_version_major_ = ffi.dwFileVersionMS >> 16;\n      os_version_minor_ = ffi.dwFileVersionMS & 0xffff;\n      os_version_bugfix_ = ffi.dwFileVersionLS >> 16;\n      os_version_build = static_cast<int>(ffi.dwFileVersionLS & 0xffff);\n    }\n\n    os_version_build_ = base::StringPrintf(\"%u\", os_version_build);\n\n    os_version_full_ = base::StringPrintf(\n        \"%s %u.%u.%u.%s%s\",\n        os_name.c_str(),\n        os_version_major_,\n        os_version_minor_,\n        os_version_bugfix_,\n        os_version_build_.c_str(),\n        flags_string.empty()\n            ? \"\"\n            : (std::string(\" (\") + flags_string + \")\").c_str());\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n}\n\nCPUArchitecture SystemSnapshotWin::GetCPUArchitecture() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  return process_reader_->Is64Bit() ? kCPUArchitectureX86_64\n                                    : kCPUArchitectureX86;\n#elif defined(ARCH_CPU_ARM64)\n  return kCPUArchitectureARM64;\n#else\n#error Unsupported Windows Arch\n#endif\n}\n\nuint32_t SystemSnapshotWin::CPURevision() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  uint32_t raw = CPUX86Signature();\n  uint8_t stepping = raw & 0xf;\n  uint8_t model = (raw & 0xf0) >> 4;\n  uint8_t family = (raw & 0xf00) >> 8;\n  uint8_t extended_model = static_cast<uint8_t>((raw & 0xf0000) >> 16);\n  uint16_t extended_family = (raw & 0xff00000) >> 20;\n\n  // For families before 15, extended_family are simply reserved bits.\n  if (family < 15)\n    extended_family = 0;\n  // extended_model is only used for families 6 and 15.\n  if (family != 6 && family != 15)\n    extended_model = 0;\n\n  uint16_t adjusted_family = family + extended_family;\n  uint8_t adjusted_model = model + (extended_model << 4);\n  return (adjusted_family << 16) | (adjusted_model << 8) | stepping;\n#elif defined(ARCH_CPU_ARM64)\n  SYSTEM_INFO system_info;\n  GetSystemInfo(&system_info);\n\n  return system_info.wProcessorRevision;\n#else\n#error Unsupported Windows Arch\n#endif\n}\n\nuint8_t SystemSnapshotWin::CPUCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  SYSTEM_INFO system_info;\n  GetSystemInfo(&system_info);\n  if (!base::IsValueInRangeForNumericType<uint8_t>(\n          system_info.dwNumberOfProcessors)) {\n    LOG(WARNING) << \"dwNumberOfProcessors exceeds uint8_t storage\";\n  }\n  return base::saturated_cast<uint8_t>(system_info.dwNumberOfProcessors);\n}\n\nstd::string SystemSnapshotWin::CPUVendor() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  int cpu_info[4];\n  __cpuid(cpu_info, 0);\n  char vendor[12];\n  *reinterpret_cast<int*>(vendor) = cpu_info[1];\n  *reinterpret_cast<int*>(vendor + 4) = cpu_info[3];\n  *reinterpret_cast<int*>(vendor + 8) = cpu_info[2];\n  return std::string(vendor, sizeof(vendor));\n#elif defined(ARCH_CPU_ARM64)\n  HKEY key;\n\n  if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,\n                    L\"HARDWARE\\\\DESCRIPTION\\\\System\\\\CentralProcessor\\\\0\",\n                    0,\n                    KEY_QUERY_VALUE,\n                    &key) != ERROR_SUCCESS) {\n    return std::string();\n  }\n\n  crashpad::ScopedRegistryKey scoped_key(key);\n  DWORD type;\n  char16_t vendor_identifier[1024];\n  DWORD vendor_identifier_size = sizeof(vendor_identifier);\n\n  if (RegQueryValueEx(key,\n                      L\"VendorIdentifier\",\n                      nullptr,\n                      &type,\n                      reinterpret_cast<BYTE*>(vendor_identifier),\n                      &vendor_identifier_size) != ERROR_SUCCESS ||\n      type != REG_SZ) {\n    return std::string();\n  }\n\n  std::string return_value;\n  DCHECK_EQ(vendor_identifier_size % sizeof(char16_t), 0u);\n  base::UTF16ToUTF8(vendor_identifier,\n                    vendor_identifier_size / sizeof(char16_t),\n                    &return_value);\n\n  return return_value.c_str();\n#else\n#error Unsupported Windows Arch\n#endif\n}\n\nvoid SystemSnapshotWin::CPUFrequency(uint64_t* current_hz,\n                                     uint64_t* max_hz) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  int num_cpus = CPUCount();\n  DCHECK_GT(num_cpus, 0);\n  std::vector<PROCESSOR_POWER_INFORMATION> info(num_cpus);\n  if (CallNtPowerInformation(ProcessorInformation,\n                             nullptr,\n                             0,\n                             &info[0],\n                             sizeof(PROCESSOR_POWER_INFORMATION) * num_cpus) !=\n      0) {\n    *current_hz = 0;\n    *max_hz = 0;\n    return;\n  }\n  constexpr uint64_t kMhzToHz = static_cast<uint64_t>(1E6);\n  *current_hz = std::max_element(info.begin(),\n                                 info.end(),\n                                 [](const PROCESSOR_POWER_INFORMATION& a,\n                                    const PROCESSOR_POWER_INFORMATION& b) {\n                                   return a.CurrentMhz < b.CurrentMhz;\n                                 })->CurrentMhz *\n                kMhzToHz;\n  *max_hz = std::max_element(info.begin(),\n                             info.end(),\n                             [](const PROCESSOR_POWER_INFORMATION& a,\n                                const PROCESSOR_POWER_INFORMATION& b) {\n                               return a.MaxMhz < b.MaxMhz;\n                             })->MaxMhz *\n            kMhzToHz;\n}\n\nuint32_t SystemSnapshotWin::CPUX86Signature() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  int cpu_info[4];\n  // We will never run on any processors that don't support at least function 1.\n  __cpuid(cpu_info, 1);\n  return cpu_info[0];\n#else\n  NOTREACHED();\n#endif\n}\n\nuint64_t SystemSnapshotWin::CPUX86Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  int cpu_info[4];\n  // We will never run on any processors that don't support at least function 1.\n  __cpuid(cpu_info, 1);\n  return (static_cast<uint64_t>(cpu_info[2]) << 32) |\n         static_cast<uint64_t>(cpu_info[3]);\n#else\n  NOTREACHED();\n#endif\n}\n\nuint64_t SystemSnapshotWin::CPUX86ExtendedFeatures() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  int cpu_info[4];\n  // We will never run on any processors that don't support at least extended\n  // function 1.\n  __cpuid(cpu_info, 0x80000001);\n  return (static_cast<uint64_t>(cpu_info[2]) << 32) |\n         static_cast<uint64_t>(cpu_info[3]);\n#else\n  NOTREACHED();\n#endif\n}\n\nuint32_t SystemSnapshotWin::CPUX86Leaf7Features() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  int cpu_info[4];\n\n  // Make sure leaf 7 can be called.\n  __cpuid(cpu_info, 0);\n  if (cpu_info[0] < 7)\n    return 0;\n\n  __cpuidex(cpu_info, 7, 0);\n  return cpu_info[1];\n#else\n  NOTREACHED();\n#endif\n}\n\nbool SystemSnapshotWin::CPUX86SupportsDAZ() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  // The correct way to check for denormals-as-zeros (DAZ) support is to examine\n  // mxcsr mask, which can be done with fxsave. See Intel Software Developer's\n  // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 \"Checking for the\n  // DAZ Flag in the MXCSR Register\". Note that since this function tests for\n  // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would\n  // indicate whether DAZ is actually enabled, which is a per-thread context\n  // concern.\n\n  // Test for fxsave support.\n  uint64_t features = CPUX86Features();\n  if (!(features & (UINT64_C(1) << 24))) {\n    return false;\n  }\n\n  // Call fxsave.\n  __declspec(align(16)) uint32_t extended_registers[128];\n  _fxsave(&extended_registers);\n  uint32_t mxcsr_mask = extended_registers[7];\n\n  // Test the DAZ bit.\n  return (mxcsr_mask & (1 << 6)) != 0;\n#else\n  NOTREACHED();\n#endif\n}\n\nSystemSnapshot::OperatingSystem SystemSnapshotWin::GetOperatingSystem() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kOperatingSystemWindows;\n}\n\nbool SystemSnapshotWin::OSServer() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return os_server_;\n}\n\nvoid SystemSnapshotWin::OSVersion(int* major,\n                                  int* minor,\n                                  int* bugfix,\n                                  std::string* build) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *major = os_version_major_;\n  *minor = os_version_minor_;\n  *bugfix = os_version_bugfix_;\n  build->assign(os_version_build_);\n}\n\nstd::string SystemSnapshotWin::OSVersionFull() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return os_version_full_;\n}\n\nstd::string SystemSnapshotWin::MachineDescription() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(scottmg): Not sure if there's anything sensible to put here.\n  return std::string();\n}\n\nbool SystemSnapshotWin::NXEnabled() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return !!IsProcessorFeaturePresent(PF_NX_ENABLED);\n}\n\nvoid SystemSnapshotWin::TimeZone(DaylightSavingTimeStatus* dst_status,\n                                 int* standard_offset_seconds,\n                                 int* daylight_offset_seconds,\n                                 std::string* standard_name,\n                                 std::string* daylight_name) const {\n  // This returns the current time zone status rather than the status at the\n  // time of the snapshot. This differs from the Mac implementation.\n  TIME_ZONE_INFORMATION time_zone_information;\n  *dst_status = static_cast<DaylightSavingTimeStatus>(\n      GetTimeZoneInformation(&time_zone_information));\n  *standard_offset_seconds =\n      (time_zone_information.Bias + time_zone_information.StandardBias) * -60;\n  *daylight_offset_seconds =\n      (time_zone_information.Bias + time_zone_information.DaylightBias) * -60;\n  *standard_name = base::WideToUTF8(time_zone_information.StandardName);\n  *daylight_name = base::WideToUTF8(time_zone_information.DaylightName);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/system_snapshot_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_\n\n#include <stdint.h>\n#include <sys/time.h>\n\n#include <string>\n\n#include \"snapshot/system_snapshot.h\"\n#include \"snapshot/win/process_reader_win.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderWin;\n\nnamespace internal {\n\n//! \\brief A SystemSnapshot of the running system, when the system runs Windows.\nclass SystemSnapshotWin final : public SystemSnapshot {\n public:\n  SystemSnapshotWin();\n\n  SystemSnapshotWin(const SystemSnapshotWin&) = delete;\n  SystemSnapshotWin& operator=(const SystemSnapshotWin&) = delete;\n\n  ~SystemSnapshotWin() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A reader for the process being snapshotted.\n  //!\n  //!     It seems odd that a system snapshot implementation would need a\n  //!     ProcessReaderWin, but some of the information reported about the\n  //!     system depends on the process it's being reported for. For example,\n  //!     the architecture returned by GetCPUArchitecture() should be the\n  //!     architecture of the process, which may be different than the native\n  //!     architecture of the system: an x86_64 system can run both x86_64 and\n  //!     32-bit x86 processes.\n  void Initialize(ProcessReaderWin* process_reader);\n\n  // SystemSnapshot:\n\n  CPUArchitecture GetCPUArchitecture() const override;\n  uint32_t CPURevision() const override;\n  uint8_t CPUCount() const override;\n  std::string CPUVendor() const override;\n  void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override;\n  uint32_t CPUX86Signature() const override;\n  uint64_t CPUX86Features() const override;\n  uint64_t CPUX86ExtendedFeatures() const override;\n  uint32_t CPUX86Leaf7Features() const override;\n  bool CPUX86SupportsDAZ() const override;\n  OperatingSystem GetOperatingSystem() const override;\n  bool OSServer() const override;\n  void OSVersion(int* major,\n                 int* minor,\n                 int* bugfix,\n                 std::string* build) const override;\n  std::string OSVersionFull() const override;\n  bool NXEnabled() const override;\n  std::string MachineDescription() const override;\n  void TimeZone(DaylightSavingTimeStatus* dst_status,\n                int* standard_offset_seconds,\n                int* daylight_offset_seconds,\n                std::string* standard_name,\n                std::string* daylight_name) const override;\n  uint64_t AddressMask() const override { return 0; }\n\n private:\n  std::string os_version_full_;\n  std::string os_version_build_;\n  ProcessReaderWin* process_reader_;  // weak\n  int os_version_major_;\n  int os_version_minor_;\n  int os_version_bugfix_;\n  bool os_server_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_\n"
  },
  {
    "path": "snapshot/win/system_snapshot_win_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/system_snapshot_win.h\"\n\n#include <sys/time.h>\n#include <time.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"snapshot/win/process_reader_win.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass SystemSnapshotWinTest : public testing::Test {\n public:\n  SystemSnapshotWinTest()\n      : Test(),\n        process_reader_(),\n        system_snapshot_() {\n  }\n\n  SystemSnapshotWinTest(const SystemSnapshotWinTest&) = delete;\n  SystemSnapshotWinTest& operator=(const SystemSnapshotWinTest&) = delete;\n\n  const internal::SystemSnapshotWin& system_snapshot() const {\n    return system_snapshot_;\n  }\n\n  // testing::Test:\n  void SetUp() override {\n    ASSERT_TRUE(process_reader_.Initialize(GetCurrentProcess(),\n                                           ProcessSuspensionState::kRunning));\n    system_snapshot_.Initialize(&process_reader_);\n  }\n\n private:\n  ProcessReaderWin process_reader_;\n  internal::SystemSnapshotWin system_snapshot_;\n};\n\nTEST_F(SystemSnapshotWinTest, GetCPUArchitecture) {\n  CPUArchitecture cpu_architecture = system_snapshot().GetCPUArchitecture();\n\n#if defined(ARCH_CPU_X86)\n  EXPECT_EQ(cpu_architecture, kCPUArchitectureX86);\n#elif defined(ARCH_CPU_X86_64)\n  EXPECT_EQ(cpu_architecture, kCPUArchitectureX86_64);\n#elif defined(ARCH_CPU_ARM64)\n  EXPECT_EQ(cpu_architecture, kCPUArchitectureARM64);\n#else\n#error Unsupported Windows Arch\n#endif\n}\n\nTEST_F(SystemSnapshotWinTest, CPUCount) {\n  EXPECT_GE(system_snapshot().CPUCount(), 1);\n}\n\nTEST_F(SystemSnapshotWinTest, CPUVendor) {\n  std::string cpu_vendor = system_snapshot().CPUVendor();\n#if defined(ARCH_CPU_X86_FAMILY)\n  EXPECT_TRUE(cpu_vendor == \"GenuineIntel\" || cpu_vendor == \"AuthenticAMD\");\n#elif defined(ARCH_CPU_ARM64)\n  EXPECT_FALSE(cpu_vendor.empty());\n#else\n#error Unsupported Windows Arch\n#endif\n}\n\n#if defined(ARCH_CPU_X86_FAMILY)\nTEST_F(SystemSnapshotWinTest, CPUX86SupportsDAZ) {\n  // Most SSE2+ machines support Denormals-Are-Zero. This may fail if run on\n  // older machines.\n  EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ());\n}\n#endif\n\nTEST_F(SystemSnapshotWinTest, GetOperatingSystem) {\n  EXPECT_EQ(system_snapshot().GetOperatingSystem(),\n            SystemSnapshot::kOperatingSystemWindows);\n}\n\nTEST_F(SystemSnapshotWinTest, OSVersion) {\n  int major;\n  int minor;\n  int bugfix;\n  std::string build;\n  system_snapshot().OSVersion(&major, &minor, &bugfix, &build);\n\n  EXPECT_GE(major, 5);\n  if (major == 5)\n    EXPECT_GE(minor, 1);\n  if (major == 6)\n    EXPECT_TRUE(minor >= 0 && minor <= 3);\n}\n\nTEST_F(SystemSnapshotWinTest, OSVersionFull) {\n  EXPECT_FALSE(system_snapshot().OSVersionFull().empty());\n}\n\nTEST_F(SystemSnapshotWinTest, MachineDescription) {\n  EXPECT_TRUE(system_snapshot().MachineDescription().empty());\n}\n\nTEST_F(SystemSnapshotWinTest, TimeZone) {\n  SystemSnapshot::DaylightSavingTimeStatus dst_status;\n  int standard_offset_seconds;\n  int daylight_offset_seconds;\n  std::string standard_name;\n  std::string daylight_name;\n\n  system_snapshot().TimeZone(&dst_status,\n                             &standard_offset_seconds,\n                             &daylight_offset_seconds,\n                             &standard_name,\n                             &daylight_name);\n\n  // |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives\n  // seconds west of UTC.\n  long timezone = 0;\n  _get_timezone(&timezone);\n  EXPECT_EQ(standard_offset_seconds, -timezone);\n\n  // In contemporary usage, most time zones have an integer hour offset from\n  // UTC, although several are at a half-hour offset, and two are at 15-minute\n  // offsets. Throughout history, other variations existed. See\n  // https://www.timeanddate.com/time/time-zones-interesting.html.\n  EXPECT_EQ(standard_offset_seconds % (15 * 60), 0)\n      << \"standard_offset_seconds \" << standard_offset_seconds;\n\n  // dst_status of kDoesNotObserveDaylightSavingTime can mean only that the\n  // adjustment is not automatic, as opposed to daylight/standard differences\n  // not existing at all. So it cannot be asserted that the two offsets are the\n  // same in that case.\n\n  EXPECT_EQ(daylight_offset_seconds % (15 * 60), 0)\n      << \"daylight_offset_seconds \" << daylight_offset_seconds;\n\n  // In contemporary usage, dst_delta_seconds will almost always be one hour,\n  // except for Lord Howe Island, Australia, which uses a 30-minute delta.\n  // Throughout history, other variations existed. See\n  // https://www.timeanddate.com/time/dst/.\n  int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds;\n  if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) {\n    FAIL() << \"dst_delta_seconds \" << dst_delta_seconds;\n  }\n\n  if (dst_status != SystemSnapshot::kDoesNotObserveDaylightSavingTime) {\n    EXPECT_NE(standard_name, daylight_name);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/thread_snapshot_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/win/thread_snapshot_win.h\"\n\n#include <iterator>\n#include <vector>\n\n#include \"base/check_op.h\"\n#include \"base/memory/page_size.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"snapshot/capture_memory.h\"\n#include \"snapshot/win/capture_memory_delegate_win.h\"\n#include \"snapshot/win/cpu_context_win.h\"\n#include \"snapshot/win/process_reader_win.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\n#if defined(ARCH_CPU_X86_64)\n\nXSAVE_CET_U_FORMAT* LocateXStateCetU(CONTEXT* context) {\n  // GetEnabledXStateFeatures needs Windows 7 SP1.\n  static auto locate_xstate_feature = []() {\n    HINSTANCE kernel32 = GetModuleHandle(L\"Kernel32.dll\");\n    return reinterpret_cast<decltype(LocateXStateFeature)*>(\n        GetProcAddress(kernel32, \"LocateXStateFeature\"));\n  }();\n  if (!locate_xstate_feature)\n    return nullptr;\n\n  DWORD cet_u_size = 0;\n  return reinterpret_cast<XSAVE_CET_U_FORMAT*>(\n      locate_xstate_feature(context, XSTATE_CET_U, &cet_u_size));\n}\n#endif  // defined(ARCH_CPU_X86_64)\n}  // namespace\n\nThreadSnapshotWin::ThreadSnapshotWin()\n    : ThreadSnapshot(),\n      context_(),\n      stack_(),\n      teb_(),\n      thread_(),\n      initialized_() {\n}\n\nThreadSnapshotWin::~ThreadSnapshotWin() {\n}\n\nbool ThreadSnapshotWin::Initialize(\n    ProcessReaderWin* process_reader,\n    const ProcessReaderWin::Thread& process_reader_thread,\n    uint32_t* gather_indirectly_referenced_memory_bytes_remaining) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  thread_ = process_reader_thread;\n  if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable(\n          CheckedRange<WinVMAddress, WinVMSize>(thread_.stack_region_address,\n                                                thread_.stack_region_size))) {\n    stack_.Initialize(process_reader->Memory(),\n                      thread_.stack_region_address,\n                      thread_.stack_region_size);\n  } else {\n    stack_.Initialize(process_reader->Memory(), 0, 0);\n  }\n\n  if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable(\n          CheckedRange<WinVMAddress, WinVMSize>(thread_.teb_address,\n                                                thread_.teb_size))) {\n    teb_.Initialize(\n        process_reader->Memory(), thread_.teb_address, thread_.teb_size);\n  } else {\n    teb_.Initialize(process_reader->Memory(), 0, 0);\n  }\n\n#if defined(ARCH_CPU_X86)\n  context_.architecture = kCPUArchitectureX86;\n  context_.x86 = &context_union_.x86;\n  InitializeX86Context(process_reader_thread.context.context<CONTEXT>(),\n                       context_.x86);\n#elif defined(ARCH_CPU_X86_64)\n  if (process_reader->Is64Bit()) {\n    context_.architecture = kCPUArchitectureX86_64;\n    context_.x86_64 = &context_union_.x86_64;\n    CONTEXT* context = process_reader_thread.context.context<CONTEXT>();\n    InitializeX64Context(context, context_.x86_64);\n    // Capturing process must have CET enabled. If captured process does not,\n    // then this will not set any state in the context snapshot.\n    if (IsXStateFeatureEnabled(XSTATE_MASK_CET_U)) {\n      XSAVE_CET_U_FORMAT* cet_u = LocateXStateCetU(context);\n      if (cet_u && cet_u->Ia32CetUMsr && cet_u->Ia32Pl3SspMsr) {\n        InitializeX64XStateCet(context, cet_u, context_.x86_64);\n      }\n    }\n  } else {\n    context_.architecture = kCPUArchitectureX86;\n    context_.x86 = &context_union_.x86;\n    InitializeX86Context(process_reader_thread.context.context<WOW64_CONTEXT>(),\n                         context_.x86);\n  }\n#elif defined(ARCH_CPU_ARM64)\n  context_.architecture = kCPUArchitectureARM64;\n  context_.arm64 = &context_union_.arm64;\n  InitializeARM64Context(process_reader_thread.context.context<CONTEXT>(),\n                         context_.arm64);\n#else\n#error Unsupported Windows Arch\n#endif  // ARCH_CPU_X86\n\n#if defined(ARCH_CPU_X86_64)\n  // Unconditionally store page around ssp if it is present.\n  if (process_reader->Is64Bit() && context_.x86_64->xstate.cet_u.ssp) {\n    WinVMAddress page_size =\n        base::checked_cast<WinVMAddress>(base::GetPageSize());\n    WinVMAddress page_mask = ~(page_size - 1);\n    WinVMAddress ssp_base = context_.x86_64->xstate.cet_u.ssp & page_mask;\n    if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable(\n            CheckedRange<WinVMAddress, WinVMSize>(ssp_base, page_size))) {\n      auto region = std::make_unique<MemorySnapshotGeneric>();\n      region->Initialize(process_reader->Memory(), ssp_base, page_size);\n      pointed_to_memory_.push_back(std::move(region));\n    }\n  }\n#endif  // ARCH_CPU_X86_64\n\n  CaptureMemoryDelegateWin capture_memory_delegate(\n      process_reader,\n      thread_,\n      &pointed_to_memory_,\n      gather_indirectly_referenced_memory_bytes_remaining);\n  CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);\n  if (gather_indirectly_referenced_memory_bytes_remaining) {\n    CaptureMemory::PointedToByMemoryRange(stack_, &capture_memory_delegate);\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nconst CPUContext* ThreadSnapshotWin::Context() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &context_;\n}\n\nconst MemorySnapshot* ThreadSnapshotWin::Stack() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &stack_;\n}\n\nuint64_t ThreadSnapshotWin::ThreadID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_.id;\n}\n\nstd::string ThreadSnapshotWin::ThreadName() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_.name;\n}\n\nint ThreadSnapshotWin::SuspendCount() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_.suspend_count;\n}\n\nint ThreadSnapshotWin::Priority() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_.priority;\n}\n\nuint64_t ThreadSnapshotWin::ThreadSpecificDataAddress() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return thread_.teb_address;\n}\n\nstd::vector<const MemorySnapshot*> ThreadSnapshotWin::ExtraMemory() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  std::vector<const MemorySnapshot*> result;\n  result.reserve(1 + pointed_to_memory_.size());\n  result.push_back(&teb_);\n  for (const auto& pointed_to_memory : pointed_to_memory_) {\n    result.push_back(pointed_to_memory.get());\n  }\n  return result;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "snapshot/win/thread_snapshot_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_\n#define CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_\n\n#include <stdint.h>\n\n#include <memory>\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n#include \"snapshot/memory_snapshot.h\"\n#include \"snapshot/memory_snapshot_generic.h\"\n#include \"snapshot/thread_snapshot.h\"\n#include \"snapshot/win/process_reader_win.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\nclass ProcessReaderWin;\n\nnamespace internal {\n\n//! \\brief A ThreadSnapshot of a thread in a running (or crashed) process on a\n//!     Windows system.\nclass ThreadSnapshotWin final : public ThreadSnapshot {\n public:\n  ThreadSnapshotWin();\n\n  ThreadSnapshotWin(const ThreadSnapshotWin&) = delete;\n  ThreadSnapshotWin& operator=(const ThreadSnapshotWin&) = delete;\n\n  ~ThreadSnapshotWin() override;\n\n  //! \\brief Initializes the object.\n  //!\n  //! \\param[in] process_reader A ProcessReaderWin for the process containing\n  //!     the thread.\n  //! \\param[in] process_reader_thread The thread within the ProcessReaderWin\n  //!     for which the snapshot should be created.\n  //! \\param[in,out] gather_indirectly_referenced_memory_bytes_remaining If\n  //!     non-null, add extra memory regions to the snapshot pointed to by the\n  //!     thread's stack. The size of the regions added is subtracted from the\n  //!     count, and when it's `0`, no more regions will be added.\n  //!\n  //! \\return `true` if the snapshot could be created, `false` otherwise with\n  //!     an appropriate message logged.\n  bool Initialize(\n      ProcessReaderWin* process_reader,\n      const ProcessReaderWin::Thread& process_reader_thread,\n      uint32_t* gather_indirectly_referenced_memory_bytes_remaining);\n\n  // ThreadSnapshot:\n\n  const CPUContext* Context() const override;\n  const MemorySnapshot* Stack() const override;\n  uint64_t ThreadID() const override;\n  std::string ThreadName() const override;\n  int SuspendCount() const override;\n  int Priority() const override;\n  uint64_t ThreadSpecificDataAddress() const override;\n  std::vector<const MemorySnapshot*> ExtraMemory() const override;\n\n private:\n  union {\n#if defined(ARCH_CPU_X86_FAMILY)\n    CPUContextX86 x86;\n    CPUContextX86_64 x86_64;\n#elif defined(ARCH_CPU_ARM64)\n    CPUContextARM64 arm64;\n#else\n#error Unsupported Windows Arch\n#endif\n  } context_union_;\n  CPUContext context_;\n  MemorySnapshotGeneric stack_;\n  MemorySnapshotGeneric teb_;\n  ProcessReaderWin::Thread thread_;\n  InitializationStateDcheck initialized_;\n  std::vector<std::unique_ptr<MemorySnapshotGeneric>> pointed_to_memory_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_\n"
  },
  {
    "path": "snapshot/x86/cpuid_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"snapshot/x86/cpuid_reader.h\"\n\n#include <stddef.h>\n\n#include \"build/build_config.h\"\n#include \"snapshot/cpu_context.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <immintrin.h>\n#include <intrin.h>\n#endif  // BUILDFLAG(IS_WIN)\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\nnamespace crashpad {\nnamespace internal {\n\nCpuidReader::CpuidReader()\n    : features_(0),\n      extended_features_(0),\n      vendor_(),\n      max_leaf_(0),\n      signature_(0) {\n  uint32_t cpuinfo[4];\n  Cpuid(cpuinfo, 0);\n  max_leaf_ = cpuinfo[0];\n  vendor_.append(reinterpret_cast<char*>(&cpuinfo[1]), 4);\n  vendor_.append(reinterpret_cast<char*>(&cpuinfo[3]), 4);\n  vendor_.append(reinterpret_cast<char*>(&cpuinfo[2]), 4);\n\n  Cpuid(cpuinfo, 1);\n  signature_ = cpuinfo[0];\n  features_ = (static_cast<uint64_t>(cpuinfo[2]) << 32) |\n              static_cast<uint64_t>(cpuinfo[3]);\n\n  Cpuid(cpuinfo, 0x80000001);\n  extended_features_ = (static_cast<uint64_t>(cpuinfo[2]) << 32) |\n                       static_cast<uint64_t>(cpuinfo[3]);\n}\n\nCpuidReader::~CpuidReader() {}\n\nuint32_t CpuidReader::Revision() const {\n  uint8_t stepping = signature_ & 0xf;\n  uint8_t model = (signature_ & 0xf0) >> 4;\n  uint8_t family = (signature_ & 0xf00) >> 8;\n  uint8_t extended_model = static_cast<uint8_t>((signature_ & 0xf0000) >> 16);\n  uint16_t extended_family = (signature_ & 0xff00000) >> 20;\n\n  // For families before 15, extended_family are simply reserved bits.\n  if (family < 15)\n    extended_family = 0;\n  // extended_model is only used for families 6 and 15.\n  if (family != 6 && family != 15)\n    extended_model = 0;\n\n  uint16_t adjusted_family = family + extended_family;\n  uint8_t adjusted_model = model + (extended_model << 4);\n  return (adjusted_family << 16) | (adjusted_model << 8) | stepping;\n}\n\nuint32_t CpuidReader::Leaf7Features() const {\n  if (max_leaf_ < 7) {\n    return 0;\n  }\n  uint32_t cpuinfo[4];\n  Cpuid(cpuinfo, 7);\n  return cpuinfo[1];\n}\n\nbool CpuidReader::SupportsDAZ() const {\n  // The correct way to check for denormals-as-zeros (DAZ) support is to examine\n  // mxcsr mask, which can be done with fxsave. See Intel Software Developer’s\n  // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the\n  // DAZ Flag in the MXCSR Register”. Note that since this function tests for\n  // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would\n  // indicate whether DAZ is actually enabled, which is a per-thread context\n  // concern.\n\n  // Test for fxsave support.\n  if (!(features_ & (UINT64_C(1) << 24))) {\n    return false;\n  }\n\n#if defined(ARCH_CPU_X86)\n  using Fxsave = CPUContextX86::Fxsave;\n#elif defined(ARCH_CPU_X86_64)\n  using Fxsave = CPUContextX86_64::Fxsave;\n#endif\n\n#if BUILDFLAG(IS_WIN)\n  __declspec(align(16)) Fxsave fxsave = {};\n#else\n  Fxsave fxsave __attribute__((aligned(16))) = {};\n#endif\n\n  static_assert(sizeof(fxsave) == 512, \"fxsave size\");\n  static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28,\n                \"mxcsr_mask offset\");\n\n#if BUILDFLAG(IS_WIN)\n  _fxsave(&fxsave);\n#else\n  asm(\"fxsave %0\" : \"=m\"(fxsave));\n#endif\n\n  // Test the DAZ bit.\n  return (fxsave.mxcsr_mask & (1 << 6)) != 0;\n}\n\nvoid CpuidReader::Cpuid(uint32_t cpuinfo[4], uint32_t leaf) const {\n#if BUILDFLAG(IS_WIN)\n  __cpuid(reinterpret_cast<int*>(cpuinfo), leaf);\n#else\n  asm(\"cpuid\"\n      : \"=a\"(cpuinfo[0]), \"=b\"(cpuinfo[1]), \"=c\"(cpuinfo[2]), \"=d\"(cpuinfo[3])\n      : \"a\"(leaf), \"b\"(0), \"c\"(0), \"d\"(0));\n#endif  // BUILDFLAG(IS_WIN)\n}\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // ARCH_CPU_X86_FAMILY\n"
  },
  {
    "path": "snapshot/x86/cpuid_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_\n#define CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Reads x86-family CPU information by calling `cpuid`.\nclass CpuidReader {\n public:\n  CpuidReader();\n  ~CpuidReader();\n\n  //! \\see SystemSnapshot::CPURevision\n  uint32_t Revision() const;\n\n  //! \\see SystemSnapshot::CPUVendor\n  std::string Vendor() const { return vendor_; }\n\n  //! \\see SystemSnapshot::CPUX86Signature\n  uint32_t Signature() const { return signature_; }\n\n  //! \\see SystemSnapshot::CPUX86Features\n  uint64_t Features() const { return features_; }\n\n  //! \\see SystemSnapshot::CPUX86ExtendedFeatures\n  uint64_t ExtendedFeatures() const { return extended_features_; }\n\n  //! \\see SystemSnapshot::CPUX86Leaf7Features\n  uint32_t Leaf7Features() const;\n\n  //! \\see SystemSnapshot::NXEnabled\n  bool NXEnabled() const { return (ExtendedFeatures() & (1 << 20)) != 0; }\n\n  //! \\see SystemSnapshot::CPUX86SupportsDAZ\n  bool SupportsDAZ() const;\n\n private:\n  void Cpuid(uint32_t cpuinfo[4], uint32_t leaf) const;\n\n  uint64_t features_;\n  uint64_t extended_features_;\n  std::string vendor_;\n  uint32_t max_leaf_;\n  uint32_t signature_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // ARCH_CPU_X86_FAMILY\n\n#endif  // CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_\n"
  },
  {
    "path": "test/BUILD.gn",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../build/crashpad_buildconfig.gni\")\n\nstatic_library(\"test\") {\n  testonly = true\n\n  sources = [\n    \"errors.cc\",\n    \"errors.h\",\n    \"file.cc\",\n    \"file.h\",\n    \"filesystem.cc\",\n    \"filesystem.h\",\n    \"gtest_death.h\",\n    \"hex_string.cc\",\n    \"hex_string.h\",\n    \"main_arguments.cc\",\n    \"main_arguments.h\",\n    \"multiprocess.h\",\n    \"multiprocess_exec.cc\",\n    \"multiprocess_exec.h\",\n    \"process_type.cc\",\n    \"process_type.h\",\n    \"scoped_guarded_page.h\",\n    \"scoped_module_handle.cc\",\n    \"scoped_module_handle.h\",\n    \"scoped_set_thread_name.h\",\n    \"scoped_temp_dir.cc\",\n    \"scoped_temp_dir.h\",\n    \"test_paths.cc\",\n    \"test_paths.h\",\n  ]\n\n  if (crashpad_is_posix || crashpad_is_fuchsia) {\n    sources += [\n      \"scoped_guarded_page_posix.cc\",\n      \"scoped_temp_dir_posix.cc\",\n    ]\n\n    if (!crashpad_is_fuchsia && !crashpad_is_ios) {\n      sources += [\n        \"multiprocess_exec_posix.cc\",\n        \"multiprocess_posix.cc\",\n      ]\n    }\n  }\n\n  # TODO(crbug.com/812974): Remove !crashpad_is_fuchsia when Fuchsia is no\n  # longer treated as a posix platform.\n  if (crashpad_is_posix && !crashpad_is_fuchsia) {\n    sources += [ \"scoped_set_thread_name_posix.cc\" ]\n  }\n\n  if (crashpad_is_apple) {\n    sources += [\n      \"mac/mach_errors.cc\",\n      \"mac/mach_errors.h\",\n    ]\n  }\n\n  if (crashpad_is_mac) {\n    sources += [\n      \"mac/dyld.cc\",\n      \"mac/dyld.h\",\n      \"mac/exception_swallower.cc\",\n      \"mac/exception_swallower.h\",\n      \"mac/mach_multiprocess.cc\",\n      \"mac/mach_multiprocess.h\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    sources -= [\n      \"multiprocess.h\",\n      \"multiprocess_exec.cc\",\n      \"multiprocess_exec.h\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    sources += [\n      \"linux/fake_ptrace_connection.cc\",\n      \"linux/fake_ptrace_connection.h\",\n      \"linux/get_tls.cc\",\n      \"linux/get_tls.h\",\n    ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [\n      \"multiprocess_exec_win.cc\",\n      \"scoped_guarded_page_win.cc\",\n      \"scoped_set_thread_name_win.cc\",\n      \"scoped_temp_dir_win.cc\",\n      \"win/child_launcher.cc\",\n      \"win/child_launcher.h\",\n      \"win/win_child_process.cc\",\n      \"win/win_child_process.h\",\n      \"win/win_multiprocess.cc\",\n      \"win/win_multiprocess.h\",\n      \"win/win_multiprocess_with_temp_dir.cc\",\n      \"win/win_multiprocess_with_temp_dir.h\",\n    ]\n  }\n\n  if (crashpad_is_fuchsia) {\n    sources += [\n      \"multiprocess_exec_fuchsia.cc\",\n      \"scoped_set_thread_name_fuchsia.cc\",\n    ]\n  }\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  configs += [\n    \"../build:crashpad_is_in_chromium\",\n    \"../build:crashpad_is_in_fuchsia\",\n  ]\n\n  data = [ \"test_paths_test_data_root.txt\" ]\n\n  deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"../compat\",\n    \"../third_party/googletest\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_mac) {\n    libs = [ \"bsm\" ]\n    deps += [\n      \"../handler\",\n      \"../snapshot\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    deps += [ \":test_bundle_data\" ]\n  }\n\n  if (crashpad_is_win) {\n    libs = [ \"shell32.lib\" ]\n  }\n\n  if (crashpad_is_fuchsia) {\n    public_deps = [ \"../third_party/fuchsia\" ]\n    if (crashpad_is_in_fuchsia) {\n      deps += [ \"//sdk/lib/fdio\" ]\n    }\n  }\n}\n\nif (crashpad_is_ios) {\n  bundle_data(\"test_bundle_data\") {\n    testonly = true\n\n    sources = [ \"test_paths_test_data_root.txt\" ]\n\n    outputs = [ \"{{bundle_resources_dir}}/crashpad_test_data/\" +\n                \"{{source_root_relative_dir}}/{{source_file_part}}\" ]\n  }\n}\n\nsource_set(\"test_test\") {\n  testonly = true\n\n  sources = [\n    \"hex_string_test.cc\",\n    \"main_arguments_test.cc\",\n    \"multiprocess_exec_test.cc\",\n    \"scoped_guarded_page_test.cc\",\n    \"scoped_temp_dir_test.cc\",\n    \"test_paths_test.cc\",\n  ]\n\n  # TODO(crbug.com/812974): Remove !crashpad_is_fuchsia when Fuchsia is no\n  # longer treated as a posix platform.\n  if (crashpad_is_posix && !crashpad_is_fuchsia && !crashpad_is_ios) {\n    sources += [ \"multiprocess_posix_test.cc\" ]\n  }\n\n  if (crashpad_is_mac) {\n    sources += [ \"mac/mach_multiprocess_test.cc\" ]\n  }\n\n  if (crashpad_is_ios) {\n    sources -= [\n      \"multiprocess_exec_test.cc\",\n      \"scoped_guarded_page_test.cc\",\n    ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [\n      \"win/win_child_process_test.cc\",\n      \"win/win_multiprocess_test.cc\",\n    ]\n  }\n\n  deps = [\n    \":test\",\n    \"$mini_chromium_source_parent:base\",\n    \"../compat\",\n    \"../third_party/googletest\",\n    \"../third_party/googletest:googlemock\",\n    \"../util\",\n  ]\n\n  data_deps = [ \":crashpad_test_test_multiprocess_exec_test_child\" ]\n\n  if (crashpad_is_ios) {\n    data_deps -= [ \":crashpad_test_test_multiprocess_exec_test_child\" ]\n  }\n}\n\nif (!crashpad_is_ios) {\n  crashpad_executable(\"crashpad_test_test_multiprocess_exec_test_child\") {\n    sources = [ \"multiprocess_exec_test_child.cc\" ]\n\n    deps = [ \"$mini_chromium_source_parent:base\" ]\n  }\n}\n\nstatic_library(\"googlemock_main\") {\n  testonly = true\n  sources = [ \"gtest_main.cc\" ]\n  configs += [ \"../build:crashpad_is_in_chromium\" ]\n  defines = [ \"CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK\" ]\n  deps = [\n    \":test\",\n    \"$mini_chromium_source_parent:base\",\n    \"$mini_chromium_source_parent:base_test_support\",\n    \"../third_party/googletest\",\n    \"../third_party/googletest:googlemock\",\n  ]\n  if (crashpad_is_android) {\n    deps += [ \"../util\" ]\n  }\n  if (crashpad_is_ios) {\n    deps += [ \"ios:google_test_setup\" ]\n  }\n}\n\nstatic_library(\"googletest_main\") {\n  testonly = true\n  sources = [ \"gtest_main.cc\" ]\n  configs += [ \"../build:crashpad_is_in_chromium\" ]\n  defines = [ \"CRASHPAD_TEST_LAUNCHER_GOOGLETEST\" ]\n  deps = [\n    \":test\",\n    \"$mini_chromium_source_parent:base\",\n    \"$mini_chromium_source_parent:base_test_support\",\n    \"../third_party/googletest\",\n  ]\n  if (crashpad_is_android) {\n    deps += [ \"../util\" ]\n  }\n  if (crashpad_is_ios) {\n    deps += [ \"ios:google_test_setup\" ]\n  }\n}\n"
  },
  {
    "path": "test/errors.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/errors.h\"\n\n#include <errno.h>\n\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include \"base/posix/safe_strerror.h\"\n#elif BUILDFLAG(IS_WIN)\n#include <string.h>\n#include <windows.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\n\nstd::string ErrnoMessage(int err, const std::string& base) {\n#if BUILDFLAG(IS_POSIX)\n  std::string err_as_string = base::safe_strerror(errno);\n  const char* err_string = err_as_string.c_str();\n#elif BUILDFLAG(IS_WIN)\n  char err_string[256];\n  strerror_s(err_string, errno);\n#endif\n  return base::StringPrintf(\"%s%s%s (%d)\",\n                            base.c_str(),\n                            base.empty() ? \"\" : \": \",\n                            err_string,\n                            err);\n}\n\nstd::string ErrnoMessage(const std::string& base) {\n  return ErrnoMessage(errno, base);\n}\n\n#if BUILDFLAG(IS_WIN)\nstd::string ErrorMessage(const std::string& base) {\n  return base::StringPrintf(\n      \"%s%s%s\",\n      base.c_str(),\n      base.empty() ? \"\" : \": \",\n      logging::SystemErrorCodeToString(GetLastError()).c_str());\n}\n#endif\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/errors.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_ERRORS_H_\n#define CRASHPAD_TEST_ERRORS_H_\n\n#include <string>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\nnamespace test {\n\n// These functions format messages in a similar way to the PLOG and PCHECK\n// family of logging macros in base/logging.h. They exist to interoperate with\n// Google Test assertions, which don’t interoperate with logging but can be\n// streamed to.\n//\n// Where non-test code could do:\n//   PCHECK(rv == 0) << \"close\";\n// Google Test-based test code can do:\n//   EXPECT_EQ(rv, 0) << ErrnoMessage(\"close\");\n\n//! \\brief Formats an error message using an `errno` value.\n//!\n//! The returned string will combine the \\a base string, if supplied, with a\n//! textual and numeric description of the error.\n//!\n//! The message is formatted using `strerror()`. \\a err may be `0` or outside of\n//! the range of known error codes, and the message returned will contain the\n//! string that `strerror()` uses in these cases.\n//!\n//! \\param[in] err The error code, usable as an `errno` value.\n//! \\param[in] base A string to prepend to the error description.\n//!\n//! \\return A string of the format `\"Operation not permitted (1)\"` if \\a err has\n//!     the value `EPERM` on a system where this is defined to be `1`. If \\a\n//!     base is not empty, it will be prepended to this string, separated by a\n//!     colon.\nstd::string ErrnoMessage(int err, const std::string& base = std::string());\n\n//! \\brief Formats an error message using `errno`.\n//!\n//! The returned string will combine the \\a base string, if supplied, with a\n//! textual and numeric description of the error.\n//!\n//! The message is formatted using `strerror()`. `errno` may be `0` or outside\n//! of the range of known error codes, and the message returned will contain the\n//! string that `strerror()` uses in these cases.\n//!\n//! \\param[in] base A string to prepend to the error description.\n//!\n//! \\return A string of the format `\"Operation not permitted (1)\"` if `errno`\n//!     has the value `EPERM` on a system where this is defined to be `1`. If\n//!     \\a base is not empty, it will be prepended to this string, separated by\n//!     a colon.\nstd::string ErrnoMessage(const std::string& base = std::string());\n\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n//! \\brief Formats an error message using `GetLastError()`.\n//!\n//! The returned string will combine the \\a base string, if supplied, with a\n//! textual and numeric description of the error. The format is the same as the\n//! `PLOG()` formatting in base.\nstd::string ErrorMessage(const std::string& base = std::string());\n#endif\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_ERRORS_H_\n"
  },
  {
    "path": "test/file.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/file.h\"\n\n#include <errno.h>\n#include <sys/stat.h>\n\n#include \"base/logging.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\n\nbool FileExists(const base::FilePath& path) {\n#if BUILDFLAG(IS_POSIX)\n  struct stat st;\n  int rv = lstat(path.value().c_str(), &st);\n  static constexpr char stat_function[] = \"lstat\";\n#elif BUILDFLAG(IS_WIN)\n  struct _stat st;\n  int rv = _wstat(path.value().c_str(), &st);\n  static constexpr char stat_function[] = \"_wstat\";\n#else\n#error \"Not implemented\"\n#endif\n  if (rv < 0) {\n    EXPECT_EQ(errno, ENOENT) << ErrnoMessage(stat_function) << \" \"\n                             << path.value();\n    return false;\n  }\n  return true;\n}\n\nbool RemoveFileIfExists(const base::FilePath& path) {\n#if BUILDFLAG(IS_POSIX)\n  if (unlink(path.value().c_str()) != 0 && errno != ENOENT) {\n    PLOG(ERROR) << \"unlink \" << path.value();\n    return false;\n  }\n#elif BUILDFLAG(IS_WIN)\n  if (!DeleteFile(path.value().c_str()) &&\n      GetLastError() != ERROR_FILE_NOT_FOUND) {\n    PLOG(ERROR) << \"DeleteFile \" << base::WideToUTF8(path.value());\n    return false;\n  }\n#else\n#error \"Not implemented\"\n#endif\n  return true;\n}\n\nFileOffset FileSize(const base::FilePath& path) {\n#if BUILDFLAG(IS_POSIX)\n  struct stat st;\n  int rv = lstat(path.value().c_str(), &st);\n  static constexpr char stat_function[] = \"lstat\";\n#elif BUILDFLAG(IS_WIN)\n  struct _stati64 st;\n  int rv = _wstati64(path.value().c_str(), &st);\n  static constexpr char stat_function[] = \"_wstati64\";\n#else\n#error \"Not implemented\"\n#endif\n  if (rv < 0) {\n    ADD_FAILURE() << ErrnoMessage(stat_function) << \" \" << path.value();\n    return -1;\n  }\n  return st.st_size;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/file.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_FILE_H_\n#define CRASHPAD_TEST_FILE_H_\n\n#include \"base/files/file_path.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Determines whether a file exists.\n//!\n//! \\param[in] path The path to check for existence.\n//!\n//! \\return `true` if \\a path exists. `false` if it does not exist. If an error\n//!     other than “file not found” occurs when searching for \\a path, returns\n//!     `false` with a Google Test failure added.\nbool FileExists(const base::FilePath& path);\n\n//! \\brief Removes a file if it exists, logging a message on failure.\n//!\n//! \\param[in] path The path to the file to remove.\n//! \\return `true` on success. `false` on failure with a message logged.\nbool RemoveFileIfExists(const base::FilePath& path);\n\n//! \\brief Determines the size of a file.\n//!\n//! \\param[in] path The path of the file to check. The file must exist.\n//!\n//! \\return The size of the file at \\a path. If the file does not exist, or an\n//!     error occurs when attempting to determine its size, returns `-1` with a\n//!     Google Test failure added.\nFileOffset FileSize(const base::FilePath& path);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_FILE_H_\n"
  },
  {
    "path": "test/filesystem.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/filesystem.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n\n#include \"base/logging.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/misc/time.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include <unistd.h>\n\n#include \"base/posix/eintr_wrapper.h\"\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\n#if BUILDFLAG(IS_WIN)\n\n// Detects the flags necessary to create symbolic links and stores them in\n// |flags| if non-nullptr, and returns true on success. If symbolic links can’t\n// be created, returns false.\nbool SymbolicLinkFlags(DWORD* flags) {\n  static DWORD symbolic_link_flags = []() {\n    ScopedTempDir temp_dir_;\n\n    base::FilePath target_path = temp_dir_.path().Append(L\"target\");\n    base::FilePath symlink_path = temp_dir_.path().Append(L\"symlink\");\n    if (::CreateSymbolicLink(\n            symlink_path.value().c_str(), target_path.value().c_str(), 0)) {\n      return 0;\n    }\n\n    DWORD error = GetLastError();\n    if (error == ERROR_PRIVILEGE_NOT_HELD) {\n      if (::CreateSymbolicLink(symlink_path.value().c_str(),\n                               target_path.value().c_str(),\n                               SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) {\n        return SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;\n      }\n\n      // This may fail with ERROR_INVALID_PARAMETER if the OS is too old to\n      // understand SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, so keep\n      // ERROR_PRIVILEGE_NOT_HELD for |error|.\n    }\n\n    // Don’t use ErrorMessage() here because the second CreateSymbolicLink() may\n    // have scrambled it. Use the saved |error| value instead.\n    EXPECT_EQ(error, static_cast<DWORD>(ERROR_PRIVILEGE_NOT_HELD))\n        << \"CreateSymbolicLink: \" << logging::SystemErrorCodeToString(error);\n    return -1;\n  }();\n\n  if (symbolic_link_flags == static_cast<DWORD>(-1)) {\n    return false;\n  }\n\n  if (flags) {\n    *flags = symbolic_link_flags;\n  }\n\n  return true;\n}\n\n#endif  // BUILDFLAG(IS_WIN)\n\n}  // namespace\n\nbool CreateFile(const base::FilePath& file) {\n  ScopedFileHandle fd(LoggingOpenFileForWrite(\n      file, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));\n  EXPECT_TRUE(fd.is_valid());\n  return fd.is_valid();\n}\n\nbool PathExists(const base::FilePath& path) {\n#if BUILDFLAG(IS_POSIX)\n  struct stat st;\n  if (lstat(path.value().c_str(), &st) != 0) {\n    EXPECT_EQ(errno, ENOENT) << ErrnoMessage(\"lstat \") << path.value();\n    return false;\n  }\n  return true;\n#elif BUILDFLAG(IS_WIN)\n  if (GetFileAttributes(path.value().c_str()) == INVALID_FILE_ATTRIBUTES) {\n    EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_FILE_NOT_FOUND))\n        << ErrorMessage(\"GetFileAttributes \") << base::WideToUTF8(path.value());\n    return false;\n  }\n  return true;\n#endif\n}\n\nbool SetFileModificationTime(const base::FilePath& path,\n                             const timespec& mtime) {\n#if BUILDFLAG(IS_APPLE)\n  // utimensat() isn't available on macOS until 10.13, so lutimes() is used\n  // instead.\n  struct stat st;\n  if (lstat(path.value().c_str(), &st) != 0) {\n    PLOG(ERROR) << \"lstat \" << path.value();\n    return false;\n  }\n  timeval times[2];\n  EXPECT_TRUE(TimespecToTimeval(st.st_atimespec, &times[0]));\n  EXPECT_TRUE(TimespecToTimeval(mtime, &times[1]));\n  if (lutimes(path.value().c_str(), times) != 0) {\n    PLOG(ERROR) << \"lutimes \" << path.value();\n    return false;\n  }\n  return true;\n#elif BUILDFLAG(IS_POSIX)\n  timespec times[2];\n  times[0].tv_sec = 0;\n  times[0].tv_nsec = UTIME_OMIT;\n  times[1] = mtime;\n  if (utimensat(AT_FDCWD, path.value().c_str(), times, AT_SYMLINK_NOFOLLOW) !=\n      0) {\n    PLOG(ERROR) << \"utimensat \" << path.value();\n    return false;\n  }\n  return true;\n#elif BUILDFLAG(IS_WIN)\n  DWORD flags = FILE_FLAG_OPEN_REPARSE_POINT;\n  if (IsDirectory(path, true)) {\n    // required for directory handles\n    flags |= FILE_FLAG_BACKUP_SEMANTICS;\n  }\n\n  ScopedFileHandle handle(::CreateFile(path.value().c_str(),\n                                       GENERIC_WRITE,\n                                       FILE_SHARE_READ | FILE_SHARE_WRITE,\n                                       nullptr,\n                                       OPEN_EXISTING,\n                                       flags,\n                                       nullptr));\n  if (!handle.is_valid()) {\n    PLOG(ERROR) << \"CreateFile \" << base::WideToUTF8(path.value());\n    return false;\n  }\n\n  FILETIME filetime = TimespecToFiletimeEpoch(mtime);\n  if (!SetFileTime(handle.get(), nullptr, nullptr, &filetime)) {\n    PLOG(ERROR) << \"SetFileTime \" << base::WideToUTF8(path.value());\n    return false;\n  }\n  return true;\n#endif  // BUILDFLAG(IS_APPLE)\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nbool CanCreateSymbolicLinks() {\n#if BUILDFLAG(IS_POSIX)\n  return true;\n#elif BUILDFLAG(IS_WIN)\n  return SymbolicLinkFlags(nullptr);\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\nbool CreateSymbolicLink(const base::FilePath& target_path,\n                        const base::FilePath& symlink_path) {\n#if BUILDFLAG(IS_POSIX)\n  int rv = HANDLE_EINTR(\n      symlink(target_path.value().c_str(), symlink_path.value().c_str()));\n  if (rv != 0) {\n    PLOG(ERROR) << \"symlink\";\n    return false;\n  }\n  return true;\n#elif BUILDFLAG(IS_WIN)\n  DWORD symbolic_link_flags = 0;\n  SymbolicLinkFlags(&symbolic_link_flags);\n  if (!::CreateSymbolicLink(\n          symlink_path.value().c_str(),\n          target_path.value().c_str(),\n          symbolic_link_flags |\n              (IsDirectory(target_path, true) ? SYMBOLIC_LINK_FLAG_DIRECTORY\n                                              : 0))) {\n    PLOG(ERROR) << \"CreateSymbolicLink\";\n    return false;\n  }\n  return true;\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/filesystem.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_FILESYSTEM_H_\n#define CRASHPAD_TEST_FILESYSTEM_H_\n\n#include \"base/files/file_path.h\"\n\n#include <time.h>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Creates an empty file at path \\a filepath.\nbool CreateFile(const base::FilePath& filepath);\n\n//! \\brief Returns `true` if a filesystem node exists at path \\a path.\nbool PathExists(const base::FilePath& path);\n\n//! \\brief Sets the modification time for a file, directory, or symbolic link.\n//!\n//! \\param[in] path The path to the file to set the modification time for.\n//! \\param[in] mtime The new modification time for the file.\n//! \\return `true` on success. Otherwise `false` with a message logged.\nbool SetFileModificationTime(const base::FilePath& path, const timespec& mtime);\n\n#if !BUILDFLAG(IS_FUCHSIA) || DOXYGEN\n// There are no symbolic links on Fuchsia. Don’t bother declaring or defining\n// symbolic link-related functions at all, because it’s an error to even pretend\n// that symbolic links might be available on Fuchsia.\n\n//! \\brief Determines whether it should be possible to create symbolic links.\n//!\n//! It is always possible to create symbolic links on POSIX.\n//!\n//! On Windows, it is only possible to create symbolic links when running as an\n//! administrator, or as a non-administrator when running Windows 10 build 15063\n//! (1703, Creators Update) or later, provided that developer mode is enabled\n//! and `SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE` is used. This function\n//! tests the creation of a symbolic link and returns true on success, and false\n//! on failure. If the symbolic link could not be created for a reason other\n//! than the expected lack of privilege, a message is logged.\n//!\n//! Additional background: <a\n//! href=\"https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/\">Symlinks\n//! in Windows 10!</a>\nbool CanCreateSymbolicLinks();\n\n//! \\brief Creates a new symbolic link.\n//!\n//! \\param[in] target_path The target for the link.\n//! \\param[in] symlink_path The name for the new link.\n//! \\return `true` on success. Otherwise `false` with a message logged.\nbool CreateSymbolicLink(const base::FilePath& target_path,\n                        const base::FilePath& symlink_path);\n\n#endif  // !BUILDFLAG(IS_FUCHSIA) || DOXYGEN\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_FILESYSTEM_H_\n"
  },
  {
    "path": "test/fuchsia_crashpad_tests.cml",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n{\n    include: [\n        \"//src/sys/test_runners/elf/ambient_exec.shard.cml\",\n        \"inspect/offer.shard.cml\",\n        \"syslog/client.shard.cml\",\n    ],\n    program: {\n        binary: \"bin/crashpad_tests\",\n\n        // For ProcessSnapshotFuchsiaTest.AddressSpaceMapping.\n        job_policy_ambient_mark_vmo_exec: \"true\",\n    },\n    children: [\n        {\n            name: \"dns_resolver\",\n            url: \"#meta/dns_resolver.cm\",\n        },\n        {\n            name: \"netstack\",\n            url: \"#meta/netstack.cm\",\n        },\n        {\n            name: \"crashpad_test\",\n            url: \"#meta/crashpad_test.cm\",\n        },\n    ],\n    use: [\n        {\n            protocol: [ \"fuchsia.net.name.Lookup\" ],\n            from: \"#dns_resolver\",\n        },\n        {\n            protocol: [ \"fuchsia.posix.socket.Provider\" ],\n            from: \"#netstack\",\n        },\n        {\n            protocol: [ \"fuchsia.process.Launcher\" ],\n        },\n        {\n            storage: \"tmp\",\n            path: \"/tmp\",\n        },\n    ],\n    offer: [\n        {\n            protocol: \"fuchsia.net.name.Lookup\",\n            from: \"#dns_resolver\",\n            to: \"#crashpad_test\",\n        },\n        {\n            protocol: \"fuchsia.net.routes.State\",\n            from: \"#netstack\",\n            to: \"#dns_resolver\",\n        },\n        {\n            protocol: \"fuchsia.posix.socket.Provider\",\n            from: \"#netstack\",\n            to: \"#crashpad_test\",\n        },\n        {\n            storage: \"cache\",\n            from: \"parent\",\n            to: [ \"#netstack\" ],\n        },\n        {\n            storage: \"tmp\",\n            from: \"parent\",\n            to: \"#crashpad_test\",\n            rights: [ \"rw*\" ],\n        },\n    ],\n}\n"
  },
  {
    "path": "test/gtest_death.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_GTEST_DEATH_H_\n#define CRASHPAD_TEST_GTEST_DEATH_H_\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include \"test/mac/exception_swallower.h\"\n#endif\n\n//! \\file\n\n#if BUILDFLAG(IS_MAC) || DOXYGEN\n\n//! \\brief Wraps the Google Test `ASSERT_DEATH_IF_SUPPORTED()` macro to make\n//!     assertions about death caused by crashes.\n//!\n//! On macOS, this macro prevents the system’s crash reporter from handling\n//! crashes that occur in \\a statement. Crashes are normally visible to the\n//! system’s crash reporter, but it is undesirable for intentional\n//! ASSERT_DEATH_CRASH() crashes to be handled by any crash reporter.\n//!\n//! `ASSERT_DEATH_IF_SUPPORTED()` is used instead of `ASSERT_DEATH()` to support\n//! platforms where death tests are not implemented by Google Test (e.g.\n//! Fuchsia). On platforms where death tests are not implemented, a warning will\n//! be logged and the remainder of the test body skipped.\n//!\n//! \\sa ASSERT_DEATH_CHECK()\n//! \\sa EXPECT_DEATH_CRASH()\n#define ASSERT_DEATH_CRASH(statement, regex)                     \\\n  do {                                                           \\\n    crashpad::test::ExceptionSwallower exception_swallower;      \\\n    ASSERT_DEATH_IF_SUPPORTED(                                   \\\n        crashpad::test::ExceptionSwallower::SwallowExceptions(); \\\n        { statement; }, regex);                                  \\\n  } while (false)\n\n//! \\brief Wraps the Google Test `EXPECT_DEATH_IF_SUPPORTED()` macro to make\n//!     assertions about death caused by crashes.\n//!\n//! On macOS, this macro prevents the system’s crash reporter from handling\n//! crashes that occur in \\a statement. Crashes are normally visible to the\n//! system’s crash reporter, but it is undesirable for intentional\n//! EXPECT_DEATH_CRASH() crashes to be handled by any crash reporter.\n//!\n//! `EXPECT_DEATH_IF_SUPPORTED()` is used instead of `EXPECT_DEATH()` to support\n//! platforms where death tests are not implemented by Google Test (e.g.\n//! Fuchsia). On platforms where death tests are not implemented, a warning will\n//! be logged and the remainder of the test body skipped.\n//!\n//! \\sa EXPECT_DEATH_CHECK()\n//! \\sa ASSERT_DEATH_CRASH()\n#define EXPECT_DEATH_CRASH(statement, regex)                              \\\n  do {                                                                    \\\n    crashpad::test::ExceptionSwallower exception_swallower;               \\\n    EXPECT_DEATH(crashpad::test::ExceptionSwallower::SwallowExceptions(); \\\n                 { statement; },                                          \\\n                 regex);                                                  \\\n  } while (false)\n\n#else  // BUILDFLAG(IS_MAC)\n\n#define ASSERT_DEATH_CRASH(statement, regex) \\\n  ASSERT_DEATH_IF_SUPPORTED(statement, regex)\n#define EXPECT_DEATH_CRASH(statement, regex) \\\n  EXPECT_DEATH_IF_SUPPORTED(statement, regex)\n\n#endif  // BUILDFLAG(IS_MAC)\n\n#if !(!defined(MINI_CHROMIUM_BASE_LOGGING_H_) && \\\n      defined(OFFICIAL_BUILD) &&                 \\\n      defined(NDEBUG)) ||                        \\\n    DOXYGEN\n\n//! \\brief Wraps the ASSERT_DEATH_CRASH() macro to make assertions about death\n//!     caused by `CHECK()` failures.\n//!\n//! In an in-Chromium build in the official configuration, `CHECK()` does not\n//! print its condition or streamed messages. In that case, this macro uses an\n//! empty \\a regex pattern when calling ASSERT_DEATH_CRASH() to avoid looking\n//! for any particular output on the standard error stream. In other build\n//! configurations, the \\a regex pattern is left intact.\n//!\n//! On macOS, `CHECK()` failures normally show up as crashes to the system’s\n//! crash reporter, but it is undesirable for intentional ASSERT_DEATH_CHECK()\n//! crashes to be handled by any crash reporter, so this is implemented in\n//! terms of ASSERT_DEATH_CRASH() instead of `ASSERT_DEATH()`.\n//!\n//! \\sa EXPECT_DEATH_CHECK()\n#define ASSERT_DEATH_CHECK(statement, regex) \\\n  ASSERT_DEATH_CRASH(statement, regex)\n\n//! \\brief Wraps the EXPECT_DEATH_CRASH() macro to make assertions about death\n//!     caused by `CHECK()` failures.\n//!\n//! In an in-Chromium build in the official configuration, `CHECK()` does not\n//! print its condition or streamed messages. In that case, this macro uses an\n//! empty \\a regex pattern when calling EXPECT_DEATH_CRASH() to avoid looking\n//! for any particular output on the standard error stream. In other build\n//! configurations, the \\a regex pattern is left intact.\n//!\n//! On macOS, `CHECK()` failures normally show up as crashes to the system’s\n//! crash reporter, but it is undesirable for intentional EXPECT_DEATH_CHECK()\n//! crashes to be handled by any crash reporter, so this is implemented in\n//! terms of EXPECT_DEATH_CRASH() instead of `EXPECT_DEATH()`.\n//!\n//! \\sa ASSERT_DEATH_CHECK()\n#define EXPECT_DEATH_CHECK(statement, regex) \\\n  EXPECT_DEATH_CRASH(statement, regex)\n\n#else  // !(!MINI_CHROMIUM_BASE_LOGGING_H_ && OFFICIAL_BUILD && NDEBUG)\n\n#define ASSERT_DEATH_CHECK(statement, regex) ASSERT_DEATH_CRASH(statement, \"\")\n#define EXPECT_DEATH_CHECK(statement, regex) EXPECT_DEATH_CRASH(statement, \"\")\n\n#endif  // !(!MINI_CHROMIUM_BASE_LOGGING_H_ && OFFICIAL_BUILD && NDEBUG)\n\n#endif  // CRASHPAD_TEST_GTEST_DEATH_H_\n"
  },
  {
    "path": "test/gtest_main.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/main_arguments.h\"\n#include \"test/multiprocess_exec.h\"\n\n#if defined(CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK)\n#include \"gmock/gmock.h\"\n#endif  // CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK\n\n#if BUILDFLAG(IS_ANDROID)\n#include \"util/linux/initial_signal_dispositions.h\"\n#endif  // BUILDFLAG(IS_ANDROID)\n\n#if BUILDFLAG(IS_IOS)\n#include \"test/ios/google_test_setup.h\"\n#endif\n\n#if BUILDFLAG(IS_WIN)\n#include \"test/win/win_child_process.h\"\n#endif  // BUILDFLAG(IS_WIN)\n\n#if defined(CRASHPAD_IS_IN_CHROMIUM)\n#include \"base/functional/bind.h\"\n#include \"base/test/launcher/unit_test_launcher.h\"\n#include \"base/test/test_suite.h\"\n#endif  // CRASHPAD_IS_IN_CHROMIUM\n\nnamespace {\n\n#if !BUILDFLAG(IS_IOS)\nbool GetChildTestFunctionName(std::string* child_func_name) {\n  constexpr size_t arg_length =\n      sizeof(crashpad::test::internal::kChildTestFunction) - 1;\n  for (const auto& it : crashpad::test::GetMainArguments()) {\n    if (it.compare(\n            0, arg_length, crashpad::test::internal::kChildTestFunction) == 0) {\n      *child_func_name = it.substr(arg_length);\n      return true;\n    }\n  }\n  return false;\n}\n#endif  // !BUILDFLAG(IS_IOS)\n\n}  // namespace\n\nint main(int argc, char* argv[]) {\n#if BUILDFLAG(IS_ANDROID)\n  crashpad::InitializeSignalDispositions();\n#endif  // BUILDFLAG(IS_ANDROID)\n\n  crashpad::test::InitializeMainArguments(argc, argv);\n\n#if !BUILDFLAG(IS_IOS)\n  std::string child_func_name;\n  if (GetChildTestFunctionName(&child_func_name)) {\n    return crashpad::test::internal::CheckedInvokeMultiprocessChild(\n        child_func_name);\n  }\n#endif  // !BUILDFLAG(IS_IOS)\n\n#if defined(CRASHPAD_IS_IN_CHROMIUM)\n\n#if BUILDFLAG(IS_WIN)\n  // Chromium’s test launcher interferes with WinMultiprocess-based tests. Allow\n  // their child processes to be launched by the standard Google Test-based test\n  // runner.\n  const bool use_chromium_test_launcher =\n      !crashpad::test::WinChildProcess::IsChildProcess();\n#elif BUILDFLAG(IS_ANDROID)\n  constexpr bool use_chromium_test_launcher = false;\n#else  // BUILDFLAG(IS_WIN)\n  constexpr bool use_chromium_test_launcher = true;\n#endif  // BUILDFLAG(IS_WIN)\n\n  if (use_chromium_test_launcher) {\n    // This supports --test-launcher-summary-output, which writes a JSON file\n    // containing test details needed by Swarming.\n    base::TestSuite test_suite(argc, argv);\n    return base::LaunchUnitTests(\n        argc,\n        argv,\n        base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));\n  }\n\n#endif  // CRASHPAD_IS_IN_CHROMIUM\n\n  // base::TestSuite initializes logging when using Chromium's test launcher.\n  logging::LoggingSettings settings;\n  settings.logging_dest =\n      logging::LOG_TO_STDERR | logging::LOG_TO_SYSTEM_DEBUG_LOG;\n  logging::InitLogging(settings);\n\n#if defined(CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK)\n  testing::InitGoogleMock(&argc, argv);\n#elif defined(CRASHPAD_TEST_LAUNCHER_GOOGLETEST)\n  testing::InitGoogleTest(&argc, argv);\n#else  // CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK\n#error #define CRASHPAD_TEST_LAUNCHER_GOOGLETEST or \\\n    CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK\n#endif  // CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK\n\n#if BUILDFLAG(IS_IOS)\n  // iOS needs to run tests within the context of an app, so call a helper that\n  // invokes UIApplicationMain().  The application delegate will call\n  // RUN_ALL_TESTS() and exit before returning control to this function.\n  crashpad::test::IOSLaunchApplicationAndRunTests(argc, argv);\n#else\n  return RUN_ALL_TESTS();\n#endif\n}\n"
  },
  {
    "path": "test/hex_string.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/hex_string.h\"\n\n#include \"base/strings/stringprintf.h\"\n\nnamespace crashpad {\nnamespace test {\n\nstd::string BytesToHexString(const void* bytes, size_t length) {\n  const unsigned char* bytes_c = reinterpret_cast<const unsigned char*>(bytes);\n\n  std::string hex_string;\n  hex_string.reserve(length * 2);\n  for (size_t index = 0; index < length; ++index) {\n    hex_string.append(base::StringPrintf(\"%02x\", bytes_c[index]));\n  }\n\n  return hex_string;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/hex_string.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_HEX_STRING_H_\n#define CRASHPAD_TEST_HEX_STRING_H_\n\n#include <sys/types.h>\n\n#include <string>\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Returns a hexadecimal string corresponding to \\a bytes and \\a length.\n//!\n//! Example usage:\n//! \\code\n//!   uint8_t expected[10];\n//!   uint8_t observed[10];\n//!   // …\n//!   EXPECT_EQ(BytesToHexString(observed, std::size(observed)),\n//!             BytesToHexString(expected, std::size(expected)));\n//! \\endcode\nstd::string BytesToHexString(const void* bytes, size_t length);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_HEX_STRING_H_\n"
  },
  {
    "path": "test/hex_string_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/hex_string.h\"\n\n#include <iterator>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(HexString, HexString) {\n  EXPECT_EQ(BytesToHexString(nullptr, 0), \"\");\n\n  static constexpr char kBytes[] = \"Abc123xyz \\x0a\\x7f\\xf0\\x9f\\x92\\xa9_\";\n  EXPECT_EQ(BytesToHexString(kBytes, std::size(kBytes)),\n            \"41626331323378797a200a7ff09f92a95f00\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/ios/BUILD.gn",
    "content": "# Copyright 2020 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../build/crashpad_buildconfig.gni\")\n\nif (crashpad_is_in_chromium) {\n  import(\"//build/config/ios/ios_test_runner_xcuitest.gni\")\n  import(\"//build/config/ios/rules.gni\")\n} else if (crashpad_is_standalone) {\n  import(\"//third_party/mini_chromium/mini_chromium/build/ios/rules.gni\")\n}\n\ngroup(\"all_tests\") {\n  testonly = true\n  deps = [\n    \":ios_crash_xcuitests_module\",\n    \"host:ios_crash_xcuitests\",\n  ]\n}\n\nsource_set(\"google_test_runner_shared_headers\") {\n  testonly = true\n  sources = [ \"cptest_google_test_runner_delegate.h\" ]\n}\n\nsource_set(\"google_test_runner\") {\n  testonly = true\n  sources = [ \"cptest_google_test_runner.mm\" ]\n  configs += [ \"../..:crashpad_config\" ]\n  deps = [\n    \"../$mini_chromium_source_parent:base\",\n    \"../../build:apple_enable_arc\",\n    \"../../build:ios_xctest\",\n    \"../../test/ios:google_test_runner_shared_headers\",\n  ]\n  frameworks = [ \"UIKit.framework\" ]\n}\n\nsource_set(\"google_test_setup\") {\n  testonly = true\n  sources = [\n    \"google_test_setup.h\",\n    \"google_test_setup.mm\",\n  ]\n  configs += [ \"../..:crashpad_config\" ]\n  deps = [\n    \":google_test_runner_shared_headers\",\n    \"../$mini_chromium_source_parent:base\",\n    \"../../build:apple_enable_arc\",\n    \"../../third_party/googletest\",\n  ]\n  frameworks = [ \"UIKit.framework\" ]\n}\n\nsource_set(\"xcuitests\") {\n  testonly = true\n  sources = [ \"crash_type_xctest.mm\" ]\n  configs += [\n    \"../..:crashpad_config\",\n    \"../../build:crashpad_is_in_chromium\",\n  ]\n\n  deps = [\n    \"../../build:apple_enable_arc\",\n    \"../../build:ios_xctest\",\n    \"../../client:common\",\n    \"../../test/ios/host:app_shared_sources\",\n    \"../../third_party/edo\",\n    \"../../util\",\n  ]\n}\n\nios_test_runner_xcuitest(\"ios_crash_xcuitests_module\") {\n  xcode_test_application_name = \"ios_crash_xcuitests\"\n  deps = [ \":xcuitests\" ]\n  data_deps = [ \"host:ios_crash_xcuitests\" ]\n}\n"
  },
  {
    "path": "test/ios/cptest_google_test_runner.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <UIKit/UIKit.h>\n#import <XCTest/XCTest.h>\n\n#include \"base/check.h\"\n#import \"test/ios/cptest_google_test_runner_delegate.h\"\n\n@interface CPTestGoogleTestRunner : XCTestCase\n@end\n\n@implementation CPTestGoogleTestRunner\n\n- (void)testRunGoogleTests {\n  id appDelegate = UIApplication.sharedApplication.delegate;\n  DCHECK([appDelegate\n      conformsToProtocol:@protocol(CPTestGoogleTestRunnerDelegate)]);\n\n  id<CPTestGoogleTestRunnerDelegate> runnerDelegate =\n      static_cast<id<CPTestGoogleTestRunnerDelegate>>(appDelegate);\n  DCHECK(runnerDelegate.supportsRunningGoogleTestsWithXCTest);\n  XCTAssertTrue([runnerDelegate runGoogleTests] == 0);\n}\n\n@end\n"
  },
  {
    "path": "test/ios/cptest_google_test_runner_delegate.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_\n#define CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_\n\n@protocol CPTestGoogleTestRunnerDelegate\n\n// Returns YES if this delegate supports running Google Test tests via a call to\n// |runGoogleTests|.\n@property(nonatomic, readonly, assign)\n    BOOL supportsRunningGoogleTestsWithXCTest;\n\n// Runs Google Test tests and returns the final exit code.\n- (int)runGoogleTests;\n\n@end\n\n#endif  // CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_H_\n"
  },
  {
    "path": "test/ios/crash_type_xctest.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <XCTest/XCTest.h>\n#include <objc/runtime.h>\n#include <sys/sysctl.h>\n\n#include <vector>\n\n#import \"Service/Sources/EDOClientService.h\"\n#include \"build/build_config.h\"\n#include \"client/length_delimited_ring_buffer.h\"\n#import \"test/ios/host/cptest_shared_object.h\"\n#include \"util/mac/sysctl.h\"\n#include \"util/mach/exception_types.h\"\n#include \"util/mach/mach_extensions.h\"\n\nnamespace crashpad {\nnamespace {\n\n#if TARGET_OS_SIMULATOR\n// macOS 14.0 is 23A344, macOS 13.6.5 is 22G621, so if the first two characters\n// in the kern.osversion are > 22, this build will reproduce the simulator bug\n// in crbug.com/328282286\n// macOS 14.0 is 23A344, macOS 13.6.5 is 22G621, so if the first two\n// characters in the kern.osversion are > 22, this build will reproduce the\n// simulator bug in crbug.com/328282286\n// This now reproduces on macOS 15.4 24E248 as well for iOS17 simulators.\nbool HasMacOSBrokeDYLDTaskInfo() {\n  if (__builtin_available(iOS 18, *)) {\n    return false;\n  }\n  std::string build = crashpad::ReadStringSysctlByName(\"kern.osversion\", false);\n  if (std::stoi(build.substr(0, 2)) >= 24) {\n    return true;\n  }\n  if (__builtin_available(iOS 17, *)) {\n    return false;\n  }\n  return std::stoi(build.substr(0, 2)) > 22;\n}\n#endif\n\n}  // namespace\n}  // namespace crashpad\n\n@interface CPTestTestCase : XCTestCase {\n  XCUIApplication* app_;\n  CPTestSharedObject* rootObject_;\n}\n@end\n\n@implementation CPTestTestCase\n\n+ (void)setUp {\n  [CPTestTestCase swizzleHandleCrashUnderSymbol];\n  [CPTestTestCase swizleMayTerminateOutOfBandWithoutCrashReport];\n\n  // Override EDO default error handler.  Without this, the default EDO error\n  // handler will throw an error and fail the test.\n  EDOSetClientErrorHandler(^(NSError* error){\n      // Do nothing.\n  });\n}\n\n// Swizzle away the -[XCUIApplicationImpl handleCrashUnderSymbol:] callback.\n// Without this, any time the host app is intentionally crashed, the test is\n// immediately failed.\n+ (void)swizzleHandleCrashUnderSymbol {\n  SEL originalSelector = NSSelectorFromString(@\"handleCrashUnderSymbol:\");\n  SEL swizzledSelector = @selector(handleCrashUnderSymbol:);\n  Method originalMethod = class_getInstanceMethod(\n      objc_getClass(\"XCUIApplicationImpl\"), originalSelector);\n  Method swizzledMethod =\n      class_getInstanceMethod([self class], swizzledSelector);\n  method_exchangeImplementations(originalMethod, swizzledMethod);\n}\n\n// Swizzle away the time consuming 'Checking for crash reports corresponding to'\n// from -[XCUIApplicationProcess swizleMayTerminateOutOfBandWithoutCrashReport]\n// that is unnecessary for these tests.\n+ (void)swizleMayTerminateOutOfBandWithoutCrashReport {\n  SEL originalSelector =\n      NSSelectorFromString(@\"mayTerminateOutOfBandWithoutCrashReport\");\n  SEL swizzledSelector = @selector(mayTerminateOutOfBandWithoutCrashReport);\n  Method originalMethod = class_getInstanceMethod(\n      objc_getClass(\"XCUIApplicationProcess\"), originalSelector);\n  Method swizzledMethod =\n      class_getInstanceMethod([self class], swizzledSelector);\n  method_exchangeImplementations(originalMethod, swizzledMethod);\n}\n\n// This gets called after tearDown, so there's no straightforward way to\n// test that this is called. However, not swizzling this out will cause every\n// crashing test to fail.\n- (void)handleCrashUnderSymbol:(id)arg1 {\n}\n\n- (BOOL)mayTerminateOutOfBandWithoutCrashReport {\n  return YES;\n}\n\n- (void)setUp {\n  app_ = [[XCUIApplication alloc] init];\n  if ([self.name isEqualToString:@\"-[CPTestTestCase testExtensionStreams]\"]) {\n    app_.launchArguments = @[ @\"--test-extension-streams\" ];\n  } else if ([self.name isEqualToString:\n                            @\"-[CPTestTestCase testCrashWithExtraMemory]\"]) {\n    app_.launchArguments = @[ @\"--test-extra_memory\" ];\n  }\n  [app_ launch];\n  rootObject_ = [EDOClientService rootObjectWithPort:12345];\n  [rootObject_ clearPendingReports];\n  XCTAssertEqual([rootObject_ pendingReportCount], 0);\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n}\n\n- (void)verifyCrashReportException:(uint32_t)exception {\n  // Confirm the app is not running.\n  XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]);\n  XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning);\n\n  // Restart app to get the report signal.\n  [app_ launch];\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n  rootObject_ = [EDOClientService rootObjectWithPort:12345];\n  XCTAssertEqual([rootObject_ pendingReportCount], 1);\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportException:&report_exception]);\n  XCTAssertEqual(report_exception.unsignedIntValue, exception);\n\n  NSString* rawLogContents = [rootObject_ rawLogContents];\n  XCTAssertFalse([rawLogContents containsString:@\"allocator used in handler.\"]);\n}\n\n- (void)testEDO {\n  NSString* result = [rootObject_ testEDO];\n  XCTAssertEqualObjects(result, @\"crashpad\");\n}\n\n- (void)testKillAbort {\n  [rootObject_ crashKillAbort];\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGABRT);\n}\n\n- (void)testTrap {\n  [rootObject_ crashTrap];\n#if !BUILDFLAG(IS_IOS_TVOS)\n#if defined(ARCH_CPU_X86_64)\n  [self verifyCrashReportException:EXC_BAD_INSTRUCTION];\n#elif defined(ARCH_CPU_ARM64)\n  [self verifyCrashReportException:EXC_BREAKPOINT];\n#else\n#error Port to your CPU architecture\n#endif\n#else  // !BUILDFLAG(IS_IOS_TVOS)\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGTRAP);\n#endif\n}\n\n- (void)testAbort {\n  [rootObject_ crashAbort];\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGABRT);\n}\n\n- (void)testBadAccess {\n  [rootObject_ crashBadAccess];\n#if !BUILDFLAG(IS_IOS_TVOS)\n  [self verifyCrashReportException:EXC_BAD_ACCESS];\n#else\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGSEGV);\n#endif\n}\n\n- (void)testException {\n  [rootObject_ crashException];\n  // After https://reviews.llvm.org/D141222 exceptions call\n  // __libcpp_verbose_abort, which Chromium sets to `brk 0` in release.\n  // After https://crrev.com/c/5375084, Chromium does not set `brk 0` for local\n  // release builds and official DCHECK builds.\n#if defined(CRASHPAD_IS_IN_CHROMIUM) && defined(NDEBUG) && \\\n    defined(OFFICIAL_BUILD) && !defined(DCHECK_ALWAYS_ON)\n  [self verifyCrashReportException:SIGABRT];\n#else\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGABRT);\n#endif\n}\n\n// TODO(crbug.com/478240087): Fails on intel. Investigate and re-enable.\n#if defined(__x86_64__)\n#define MAYBE_testNSException DISABLED_testNSException\n#else\n#define MAYBE_testNSException testNSException\n#endif\n- (void)MAYBE_testNSException {\n  [rootObject_ crashNSException];\n  [self verifyCrashReportException:crashpad::kMachExceptionFromNSException];\n  NSDictionary* dict = [rootObject_ getAnnotations];\n  NSString* userInfo =\n      [dict[@\"objects\"][0] valueForKeyPath:@\"exceptionUserInfo\"];\n  XCTAssertTrue([userInfo containsString:@\"Error Object=<CPTestSharedObject\"]);\n  XCTAssertTrue([[dict[@\"objects\"][1] valueForKeyPath:@\"exceptionReason\"]\n      isEqualToString:@\"Intentionally throwing error.\"]);\n  XCTAssertTrue([[dict[@\"objects\"][2] valueForKeyPath:@\"exceptionName\"]\n      isEqualToString:@\"NSInternalInconsistencyException\"]);\n}\n\n// TODO(crbug.com/478240087): Fails on intel. Investigate and re-enable.\n#if defined(__x86_64__)\n#define MAYBE_testNotAnNSException DISABLED_testNotAnNSException\n#else\n#define MAYBE_testNotAnNSException testNotAnNSException\n#endif\n- (void)MAYBE_testNotAnNSException {\n  [rootObject_ crashNotAnNSException];\n  // When @throwing something other than an NSException the\n  // UncaughtExceptionHandler is not called, so the application SIGABRTs.\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGABRT);\n}\n\n- (void)testUnhandledNSException {\n  [rootObject_ crashUnhandledNSException];\n  [self verifyCrashReportException:crashpad::kMachExceptionFromNSException];\n  NSDictionary* dict = [rootObject_ getAnnotations];\n  NSString* uncaught_flag =\n      [dict[@\"objects\"][0] valueForKeyPath:@\"UncaughtNSException\"];\n  XCTAssertTrue([uncaught_flag containsString:@\"true\"]);\n  NSString* userInfo =\n      [dict[@\"objects\"][1] valueForKeyPath:@\"exceptionUserInfo\"];\n  XCTAssertTrue([userInfo containsString:@\"Error Object=<CPTestSharedObject\"]);\n  XCTAssertTrue([[dict[@\"objects\"][2] valueForKeyPath:@\"exceptionReason\"]\n      isEqualToString:@\"Intentionally throwing error.\"]);\n  XCTAssertTrue([[dict[@\"objects\"][3] valueForKeyPath:@\"exceptionName\"]\n      isEqualToString:@\"NSInternalInconsistencyException\"]);\n}\n\n- (void)testcrashUnrecognizedSelectorAfterDelay {\n  [rootObject_ crashUnrecognizedSelectorAfterDelay];\n  [self verifyCrashReportException:crashpad::kMachExceptionFromNSException];\n  NSDictionary* dict = [rootObject_ getAnnotations];\n  XCTAssertTrue([[dict[@\"objects\"][0] valueForKeyPath:@\"exceptionReason\"]\n      containsString:\n          @\"CPTestSharedObject does_not_exist]: unrecognized selector\"]);\n  XCTAssertTrue([[dict[@\"objects\"][1] valueForKeyPath:@\"exceptionName\"]\n      isEqualToString:@\"NSInvalidArgumentException\"]);\n}\n\n- (void)testCatchUIGestureEnvironmentNSException {\n  // Tap the button with the string UIGestureEnvironmentException.\n#if !BUILDFLAG(IS_IOS_TVOS)\n  [app_.buttons[@\"UIGestureEnvironmentException\"] tap];\n#else\n  // tvOS does not have [XCUIElement tap]. This version assumes there is just\n  // one big button with \"UIGestureEnvironmentException\" as title, so we can\n  // just press Select on the remote to activate it.\n  [XCUIRemote.sharedRemote pressButton:XCUIRemoteButtonSelect];\n#endif\n  [self verifyCrashReportException:crashpad::kMachExceptionFromNSException];\n  NSDictionary* dict = [rootObject_ getAnnotations];\n  XCTAssertTrue([[dict[@\"objects\"][0] valueForKeyPath:@\"exceptionReason\"]\n      containsString:@\"NSArray0 objectAtIndex:]: index 42 beyond bounds\"]);\n  XCTAssertTrue([[dict[@\"objects\"][1] valueForKeyPath:@\"exceptionName\"]\n      isEqualToString:@\"NSRangeException\"]);\n}\n\n- (void)testCatchNSException {\n  [rootObject_ catchNSException];\n\n  // The app should not crash\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n\n  // No report should be generated.\n  [rootObject_ processIntermediateDumps];\n  XCTAssertEqual([rootObject_ pendingReportCount], 0);\n}\n\n// TODO(crbug.com/478240087): Fails on intel. Investigate and re-enable.\n#if defined(__x86_64__)\n#define MAYBE_testCrashCoreAutoLayoutSinkhole \\\n  DISABLED_testCrashCoreAutoLayoutSinkhole\n#else\n#define MAYBE_testCrashCoreAutoLayoutSinkhole testCrashCoreAutoLayoutSinkhole\n#endif\n- (void)MAYBE_testCrashCoreAutoLayoutSinkhole {\n  [rootObject_ crashCoreAutoLayoutSinkhole];\n  [self verifyCrashReportException:crashpad::kMachExceptionFromNSException];\n  NSDictionary* dict = [rootObject_ getAnnotations];\n  XCTAssertTrue([[dict[@\"objects\"][0] valueForKeyPath:@\"exceptionReason\"]\n      containsString:@\"Unable to activate constraint with anchors\"]);\n  XCTAssertTrue([[dict[@\"objects\"][1] valueForKeyPath:@\"exceptionName\"]\n      isEqualToString:@\"NSGenericException\"]);\n}\n\n// This test cannot run correctly on tvOS: it is impossible to catch this stack\n// overflow as a Mach exception (like we do on iOS), and sigaltstack() is also\n// forbidden so we cannot detect it with POSIX signals either.\n// Per xnu-11215.81.4/bsd/uxkern/ux_exception.c's handle_ux_exception(), when a\n// stack overflow is detected but no alternate stack is specified, the kernel\n// will reset SIGSEGV to SIG_DFL before delivering the signal, so we are never\n// able to capture this crash. Even if we do pass SA_ONSTACK to sigaction(), we\n// will just crash a bit later, as we are still using the same stack that has\n// already overflown.\n#if !BUILDFLAG(IS_IOS_TVOS)\n- (void)testRecursion {\n  [rootObject_ crashRecursion];\n  [self verifyCrashReportException:EXC_BAD_ACCESS];\n}\n#endif\n\n- (void)testClientAnnotations {\n  [rootObject_ crashKillAbort];\n\n  // Set app launch args to trigger different client annotations.\n  NSArray<NSString*>* old_args = app_.launchArguments;\n  app_.launchArguments = @[ @\"--alternate-client-annotations\" ];\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGABRT);\n\n  app_.launchArguments = old_args;\n\n  // Confirm the initial crash took the standard annotations.\n  NSDictionary* dict = [rootObject_ getProcessAnnotations];\n  XCTAssertTrue([dict[@\"crashpad\"] isEqualToString:@\"yes\"]);\n  XCTAssertTrue([dict[@\"plat\"] isEqualToString:@\"iOS\"]);\n  XCTAssertTrue([dict[@\"prod\"] isEqualToString:@\"xcuitest\"]);\n  XCTAssertTrue([dict[@\"ver\"] isEqualToString:@\"1\"]);\n\n  // Confirm passing alternate client annotation args works.\n  [rootObject_ clearPendingReports];\n  [rootObject_ crashKillAbort];\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGABRT);\n\n  dict = [rootObject_ getProcessAnnotations];\n  XCTAssertTrue([dict[@\"crashpad\"] isEqualToString:@\"no\"]);\n  XCTAssertTrue([dict[@\"plat\"] isEqualToString:@\"macOS\"]);\n  XCTAssertTrue([dict[@\"prod\"] isEqualToString:@\"some_app\"]);\n  XCTAssertTrue([dict[@\"ver\"] isEqualToString:@\"42\"]);\n}\n\n#if TARGET_OS_SIMULATOR\n- (void)testCrashWithCrashInfoMessage {\n  if (@available(iOS 15.0, *)) {\n    // Figure out how to test this on iOS15.\n    return;\n  }\n  [rootObject_ crashWithCrashInfoMessage];\n  [self verifyCrashReportException:EXC_BAD_ACCESS];\n  NSDictionary* dict = [rootObject_ getAnnotations];\n  NSString* dyldMessage = dict[@\"vector\"][0];\n  XCTAssertTrue([dyldMessage isEqualToString:@\"dyld: in dlsym()\"]);\n}\n#endif\n\n// TODO(justincohen): Codesign crashy_initializer.so so it can run on devices.\n#if TARGET_OS_SIMULATOR\n- (void)testCrashWithDyldErrorString {\n  if (@available(iOS 15.0, *)) {\n    // iOS 15 uses dyld4, which doesn't use CRSetCrashLogMessage2\n    return;\n  }\n  [rootObject_ crashWithDyldErrorString];\n#if defined(ARCH_CPU_X86_64)\n  [self verifyCrashReportException:EXC_BAD_INSTRUCTION];\n#elif defined(ARCH_CPU_ARM64)\n  [self verifyCrashReportException:EXC_BREAKPOINT];\n#else\n#error Port to your CPU architecture\n#endif\n  NSArray* vector = [rootObject_ getAnnotations][@\"vector\"];\n  // This message is set by dyld-353.2.1/src/ImageLoaderMachO.cpp\n  // ImageLoaderMachO::doInitialization().\n  NSString* module = @\"crashpad_snapshot_test_module_crashy_initializer.so\";\n  XCTAssertTrue([vector[0] hasSuffix:module]);\n}\n#endif\n\n- (void)testCrashWithAnnotations {\n#if TARGET_OS_SIMULATOR\n  // This test will fail on <iOS17 simulators when running on macOS >=14.3 or\n  // <iOS18 simulators when running on macOS >=15.4 due to a bug in Simulator.\n  // crbug.com/328282286\n  if (crashpad::HasMacOSBrokeDYLDTaskInfo()) {\n    return;\n  }\n#endif\n\n  [rootObject_ crashWithAnnotations];\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  NSNumber* report_exception;\n  XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]);\n  XCTAssertEqual(report_exception.intValue, SIGABRT);\n\n  NSDictionary* dict = [rootObject_ getAnnotations];\n  NSDictionary* simpleMap = dict[@\"simplemap\"];\n  XCTAssertTrue([simpleMap[@\"#TEST# empty_value\"] isEqualToString:@\"\"]);\n  XCTAssertTrue([simpleMap[@\"#TEST# key\"] isEqualToString:@\"value\"]);\n  XCTAssertTrue([simpleMap[@\"#TEST# longer\"] isEqualToString:@\"shorter\"]);\n  XCTAssertTrue([simpleMap[@\"#TEST# pad\"] isEqualToString:@\"crash\"]);\n  XCTAssertTrue([simpleMap[@\"#TEST# x\"] isEqualToString:@\"y\"]);\n\n  XCTAssertTrue([[dict[@\"objects\"][0] valueForKeyPath:@\"#TEST# same-name\"]\n      isEqualToString:@\"same-name 4\"]);\n  XCTAssertTrue([[dict[@\"objects\"][1] valueForKeyPath:@\"#TEST# same-name\"]\n      isEqualToString:@\"same-name 3\"]);\n  XCTAssertTrue([[dict[@\"objects\"][2] valueForKeyPath:@\"#TEST# one\"]\n      isEqualToString:@\"moocow\"]);\n  // Ensure `ring_buffer` is present but not `busy_ring_buffer`.\n  XCTAssertEqual(1u, [dict[@\"ringbuffers\"] count]);\n  NSData* ringBufferNSData =\n      [dict[@\"ringbuffers\"][0] valueForKeyPath:@\"#TEST# ring_buffer\"];\n  crashpad::RingBufferData ringBufferData;\n  XCTAssertTrue(ringBufferData.DeserializeFromBuffer(ringBufferNSData.bytes,\n                                                     ringBufferNSData.length));\n  crashpad::LengthDelimitedRingBufferReader reader(ringBufferData);\n\n  std::vector<uint8_t> ringBufferEntry;\n  XCTAssertTrue(reader.Pop(ringBufferEntry));\n  NSString* firstEntry = [[NSString alloc] initWithBytes:ringBufferEntry.data()\n                                                  length:ringBufferEntry.size()\n                                                encoding:NSUTF8StringEncoding];\n  XCTAssertEqualObjects(firstEntry, @\"hello\");\n  ringBufferEntry.clear();\n\n  XCTAssertTrue(reader.Pop(ringBufferEntry));\n  NSString* secondEntry = [[NSString alloc] initWithBytes:ringBufferEntry.data()\n                                                   length:ringBufferEntry.size()\n                                                 encoding:NSUTF8StringEncoding];\n  XCTAssertEqualObjects(secondEntry, @\"goodbye\");\n  ringBufferEntry.clear();\n\n  XCTAssertFalse(reader.Pop(ringBufferEntry));\n}\n\n- (void)testCrashWithExtraMemory {\n#if TARGET_OS_SIMULATOR\n  // This test will fail on <iOS17 simulators when running on macOS >=14.3 or\n  // <iOS18 simulators when running on macOS >=15.4 due to a bug in Simulator.\n  // crbug.com/328282286\n  if (crashpad::HasMacOSBrokeDYLDTaskInfo()) {\n    return;\n  }\n#endif\n\n  [rootObject_ crashKillAbort];\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n\n  NSDictionary* dict = [rootObject_ getExtraMemory];\n  BOOL found = NO;\n  for (NSString* key in dict) {\n    if ([dict[key] isEqualToString:@\"hello world\"]) {\n      found = YES;\n      break;\n    }\n  }\n  XCTAssertTrue(found);\n}\n\n- (void)testExtensionStreams {\n#if TARGET_OS_SIMULATOR\n  // This test will fail on <iOS17 simulators when running on macOS >=14.3 or\n  // <iOS18 simulators when running on macOS >=15.4 due to a bug in Simulator.\n  // crbug.com/328282286\n  if (crashpad::HasMacOSBrokeDYLDTaskInfo()) {\n    return;\n  }\n#endif\n  [rootObject_ crashKillAbort];\n  [self verifyCrashReportException:EXC_SOFT_SIGNAL];\n  XCTAssertTrue([rootObject_ hasExtensionStream]);\n}\n\n- (void)testDumpWithoutCrash {\n  [rootObject_ generateDumpWithoutCrash:10 threads:3];\n\n  // The app should not crash\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n  XCTAssertEqual([rootObject_ pendingReportCount], 30);\n}\n\n- (void)testSimultaneousCrash {\n  [rootObject_ crashConcurrentSignalAndMach];\n\n  // Confirm the app is not running.\n  XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]);\n  XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning);\n\n  [app_ launch];\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n  rootObject_ = [EDOClientService rootObjectWithPort:12345];\n  XCTAssertEqual([rootObject_ pendingReportCount], 1);\n}\n\n- (void)testSimultaneousNSException {\n  [rootObject_ catchConcurrentNSException];\n\n  // The app should not crash\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n\n  // No report should be generated.\n  [rootObject_ processIntermediateDumps];\n  XCTAssertEqual([rootObject_ pendingReportCount], 0);\n}\n\n- (void)testCrashInHandlerReentrant {\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n  rootObject_ = [EDOClientService rootObjectWithPort:12345];\n\n  [rootObject_ crashInHandlerReentrant];\n\n  // Confirm the app is not running.\n  XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]);\n  XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning);\n\n  [app_ launch];\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n  rootObject_ = [EDOClientService rootObjectWithPort:12345];\n\n  XCTAssertEqual([rootObject_ pendingReportCount], 0);\n\n  NSString* rawLogContents = [rootObject_ rawLogContents];\n  NSString* errmsg = @\"Cannot DumpExceptionFromSignal without writer\";\n  XCTAssertTrue([rawLogContents containsString:errmsg]);\n}\n\n- (void)testFailureWhenHandlerAllocates {\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n  rootObject_ = [EDOClientService rootObjectWithPort:12345];\n\n  [rootObject_ allocateWithForbiddenAllocators];\n\n  // Confirm the app is not running.\n  XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]);\n  XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning);\n\n  [app_ launch];\n  XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground);\n  rootObject_ = [EDOClientService rootObjectWithPort:12345];\n\n  XCTAssertEqual([rootObject_ pendingReportCount], 0);\n\n  NSString* rawLogContents = [rootObject_ rawLogContents];\n  XCTAssertTrue([rawLogContents containsString:@\"allocator used in handler.\"]);\n}\n\n@end\n"
  },
  {
    "path": "test/ios/google_test_setup.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_\n#define CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Runs all registered tests in the context of a UIKit application.\n//!\n//! Invokes UIApplicationMain() to launch the iOS application and runs all\n//! registered tests after the application finishes\n//! launching. UIApplicationMain() brings up the main runloop and never returns,\n//! so therefore this function never returns either.  It invokes _exit() to\n//! terminate the application after tests have completed.\nvoid IOSLaunchApplicationAndRunTests(int argc, char* argv[]);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_\n"
  },
  {
    "path": "test/ios/google_test_setup.mm",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/ios/google_test_setup.h\"\n\n#import <UIKit/UIKit.h>\n\n#include \"base/check.h\"\n#include \"gtest/gtest.h\"\n#include \"test/ios/cptest_google_test_runner_delegate.h\"\n\n@interface UIApplication (Testing)\n- (void)_terminateWithStatus:(int)status;\n@end\n\nnamespace {\n\n// The iOS watchdog timer will kill an app that doesn't spin the main event loop\n// often enough. This uses a Google Test TestEventListener to spin the current\n// loop after each test finishes. However, if any individual test takes too\n// long, it is still possible that the app will get killed.\nclass IOSRunLoopListener : public testing::EmptyTestEventListener {\n public:\n  virtual void OnTestEnd(const testing::TestInfo& test_info) {\n    @autoreleasepool {\n      // At the end of the test, spin the default loop for a moment.\n      NSDate* stop_date = [NSDate dateWithTimeIntervalSinceNow:0.001];\n      [[NSRunLoop currentRunLoop] runUntilDate:stop_date];\n    }\n  }\n};\n\nvoid RegisterTestEndListener() {\n  testing::TestEventListeners& listeners =\n      testing::UnitTest::GetInstance()->listeners();\n  listeners.Append(new IOSRunLoopListener);\n}\n\n}  // namespace\n\n@interface CPTestUnitTestApplicationDelegate\n    : NSObject <CPTestGoogleTestRunnerDelegate>\n@property(nonatomic, readwrite, strong) UIWindow* window;\n- (void)runTests;\n@end\n\n@implementation CPTestUnitTestApplicationDelegate\n\n- (BOOL)application:(UIApplication*)application\n    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {\n  self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];\n  self.window.backgroundColor = UIColor.whiteColor;\n  [self.window makeKeyAndVisible];\n\n  UIViewController* controller = [[UIViewController alloc] init];\n  [self.window setRootViewController:controller];\n\n  // Add a label with the app name.\n  UILabel* label = [[UILabel alloc] initWithFrame:controller.view.bounds];\n  label.text = [[NSProcessInfo processInfo] processName];\n  label.textAlignment = NSTextAlignmentCenter;\n  label.textColor = UIColor.blackColor;\n  [controller.view addSubview:label];\n\n  // Queue up the test run.\n  if (![self supportsRunningGoogleTestsWithXCTest]) {\n    // When running in XCTest mode, XCTest will invoke |runGoogleTest| directly.\n    // Otherwise, schedule a call to |runTests|.\n    [self performSelector:@selector(runTests) withObject:nil afterDelay:0.1];\n  }\n\n  return YES;\n}\n\n- (BOOL)supportsRunningGoogleTestsWithXCTest {\n  return getenv(\"XCTestConfigurationFilePath\") != nullptr;\n}\n\n- (int)runGoogleTests {\n  RegisterTestEndListener();\n  int exitStatus = RUN_ALL_TESTS();\n  return exitStatus;\n}\n\n- (void)runTests {\n  DCHECK(![self supportsRunningGoogleTestsWithXCTest]);\n\n  int exitStatus = [self runGoogleTests];\n\n  // If a test app is too fast, it will exit before Instruments has has a\n  // a chance to initialize and no test results will be seen.\n  // TODO(crbug.com/137010): Figure out how much time is actually needed, and\n  // sleep only to make sure that much time has elapsed since launch.\n  [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];\n  self.window = nil;\n\n  // Use the hidden selector to try and cleanly take down the app (otherwise\n  // things can think the app crashed even on a zero exit status).\n  UIApplication* application = [UIApplication sharedApplication];\n  [application _terminateWithStatus:exitStatus];\n\n  exit(exitStatus);\n}\n\n@end\n\nnamespace crashpad {\nnamespace test {\n\nvoid IOSLaunchApplicationAndRunTests(int argc, char* argv[]) {\n  @autoreleasepool {\n    int exit_status = UIApplicationMain(\n        argc, argv, nil, @\"CPTestUnitTestApplicationDelegate\");\n    exit(exit_status);\n  }\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/ios/host/BUILD.gn",
    "content": "# Copyright 2020 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../../build/crashpad_buildconfig.gni\")\n\nif (crashpad_is_in_chromium) {\n  import(\"//build/config/ios/rules.gni\")\n} else if (crashpad_is_standalone) {\n  import(\"//third_party/mini_chromium/mini_chromium/build/ios/rules.gni\")\n}\n\nsource_set(\"app_shared_sources\") {\n  testonly = true\n  sources = [ \"cptest_shared_object.h\" ]\n  configs += [ \"../../..:crashpad_config\" ]\n  deps = [ \"../../../build:apple_enable_arc\" ]\n  frameworks = [ \"UIKit.framework\" ]\n}\n\nstatic_library(\"app_host_sources\") {\n  testonly = true\n  sources = [\n    \"cptest_application_delegate.h\",\n    \"cptest_application_delegate.mm\",\n    \"cptest_crash_view_controller.h\",\n    \"cptest_crash_view_controller.mm\",\n    \"handler_forbidden_allocators.cc\",\n    \"handler_forbidden_allocators.h\",\n    \"main.mm\",\n  ]\n  configs += [ \"../../..:crashpad_config\" ]\n  deps = [\n    \":app_shared_sources\",\n    \"../../../build:apple_enable_arc\",\n    \"../../../client\",\n    \"../../../minidump:test_support\",\n    \"../../../snapshot\",\n    \"../../../test\",\n    \"../../../third_party/edo\",\n  ]\n  frameworks = [\n    \"Foundation.framework\",\n    \"UIKit.framework\",\n  ]\n}\n\n# TODO(justincohen): Codesign crashy_initializer.so so it can run on devices.\nbundle_data(\"crashy_module_bundle\") {\n  testonly = true\n  sources =\n      [ \"$root_out_dir/crashpad_snapshot_test_module_crashy_initializer.so\" ]\n  outputs = [ \"{{bundle_contents_dir}}/crashpad_snapshot_test_module_crashy_initializer.so\" ]\n  public_deps =\n      [ \"../../../snapshot:crashpad_snapshot_test_module_crashy_initializer\" ]\n}\n\nios_app_bundle(\"ios_crash_xcuitests\") {\n  testonly = true\n  info_plist = \"Info.plist\"\n  if (crashpad_is_in_chromium) {\n    bundle_identifier = shared_bundle_id_for_test_apps\n  }\n  deps = [\n    \":app_host_sources\",\n    \":crashy_module_bundle\",\n  ]\n}\n"
  },
  {
    "path": "test/ios/host/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>en</string>\n  <key>CFBundleDisplayName</key>\n  <string>${PRODUCT_NAME}</string>\n  <key>CFBundleExecutable</key>\n  <string>${EXECUTABLE_NAME}</string>\n  <key>CFBundleIdentifier</key>\n  <string>${BUNDLE_IDENTIFIER}</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>${PRODUCT_NAME}</string>\n  <key>CFBundlePackageType</key>\n  <string>APPL</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>LSRequiresIPhoneOS</key>\n  <true/>\n  <key>UILaunchImages</key>\n  <array>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{320, 480}</string>\n    </dict>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{320, 568}</string>\n    </dict>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{375, 667}</string>\n    </dict>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{414, 736}</string>\n    </dict>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{375, 812}</string>\n    </dict>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{414, 896}</string>\n    </dict>\n  </array>\n  <key>UILaunchImages~ipad</key>\n  <array>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{768, 1024}</string>\n    </dict>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{1024, 1366}</string>\n    </dict>\n    <dict>\n      <key>UILaunchImageMinimumOSVersion</key>\n      <string>9.0</string>\n      <key>UILaunchImageName</key>\n      <string>Default</string>\n      <key>UILaunchImageSize</key>\n      <string>{832, 1114}</string>\n    </dict>\n  </array>\n  <key>UISupportedInterfaceOrientation</key>\n  <array>\n    <string>UIInterfaceOrientationPortrait</string>\n    <string>UIInterfaceOrientationLandscapeLeft</string>\n    <string>UIInterfaceOrientationLandscapeRight</string>\n  </array>\n</dict>\n</plist>\n"
  },
  {
    "path": "test/ios/host/cptest_application_delegate.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_\n#define CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_\n\n#import <UIKit/UIKit.h>\n\n@interface CPTestApplicationDelegate : UIResponder <UIApplicationDelegate>\n@end\n\n#endif  // CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_\n"
  },
  {
    "path": "test/ios/host/cptest_application_delegate.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"test/ios/host/cptest_application_delegate.h\"\n#include <dispatch/dispatch.h>\n#include <dlfcn.h>\n#include <mach-o/dyld.h>\n#include <mach-o/dyld_images.h>\n#include <mach-o/nlist.h>\n#include <objc/objc-exception.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <thread>\n#include <utility>\n#include <vector>\n\n#import \"Service/Sources/EDOHostNamingService.h\"\n#import \"Service/Sources/EDOHostService.h\"\n#import \"Service/Sources/NSObject+EDOValueObject.h\"\n#include \"base/logging.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"client/annotation.h\"\n#include \"client/annotation_list.h\"\n#include \"client/crash_report_database.h\"\n#include \"client/crashpad_client.h\"\n#include \"client/crashpad_info.h\"\n#include \"client/ring_buffer_annotation.h\"\n#include \"client/simple_string_dictionary.h\"\n#include \"client/simulate_crash.h\"\n#include \"minidump/test/minidump_user_extension_stream_util.h\"\n#include \"snapshot/minidump/process_snapshot_minidump.h\"\n#include \"test/file.h\"\n#import \"test/ios/host/cptest_crash_view_controller.h\"\n#import \"test/ios/host/cptest_shared_object.h\"\n#import \"test/ios/host/handler_forbidden_allocators.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/ios/raw_logging.h\"\n#include \"util/thread/thread.h\"\n\nusing OperationStatus = crashpad::CrashReportDatabase::OperationStatus;\nusing Report = crashpad::CrashReportDatabase::Report;\n\nnamespace {\n\nclass ReadToString : public crashpad::MemorySnapshot::Delegate {\n public:\n  std::string result;\n\n  bool MemorySnapshotDelegateRead(void* data, size_t size) override {\n    result = std::string(reinterpret_cast<const char*>(data), size);\n    return true;\n  }\n};\n\nstatic constexpr char kExpectedStreamData[] = \"Injected extension stream!\";\n\nclass TestUserStreamDataSource : public crashpad::UserStreamDataSource {\n public:\n  TestUserStreamDataSource() {}\n\n  TestUserStreamDataSource(const TestUserStreamDataSource&) = delete;\n  TestUserStreamDataSource& operator=(const TestUserStreamDataSource&) = delete;\n\n  std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>\n  ProduceStreamData(crashpad::ProcessSnapshot* process_snapshot) override;\n};\n\nstd::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>\nTestUserStreamDataSource::ProduceStreamData(\n    crashpad::ProcessSnapshot* process_snapshot) {\n  return std::make_unique<crashpad::test::BufferExtensionStreamDataSource>(\n      0xCAFEBABE, kExpectedStreamData, sizeof(kExpectedStreamData));\n}\n\nconstexpr crashpad::Annotation::Type kRingBufferType =\n    crashpad::Annotation::UserDefinedType(42);\n\nbase::FilePath GetDatabaseDir() {\n  base::FilePath database_dir([NSFileManager.defaultManager\n                                  URLsForDirectory:NSDocumentDirectory\n                                         inDomains:NSUserDomainMask]\n                                  .lastObject.path.UTF8String);\n  return database_dir.Append(\"crashpad\");\n}\n\nbase::FilePath GetRawLogOutputFile() {\n  base::FilePath document_directory([NSFileManager.defaultManager\n                                        URLsForDirectory:NSDocumentDirectory\n                                               inDomains:NSUserDomainMask]\n                                        .lastObject.path.UTF8String);\n  return document_directory.Append(\"raw_log_output.txt\");\n}\n\nstd::unique_ptr<crashpad::CrashReportDatabase> GetDatabase() {\n  base::FilePath database_dir = GetDatabaseDir();\n  std::unique_ptr<crashpad::CrashReportDatabase> database =\n      crashpad::CrashReportDatabase::Initialize(database_dir);\n  return database;\n}\n\nOperationStatus GetPendingReports(std::vector<Report>* pending_reports) {\n  std::unique_ptr<crashpad::CrashReportDatabase> database(GetDatabase());\n  return database->GetPendingReports(pending_reports);\n}\n\nstd::unique_ptr<crashpad::ProcessSnapshotMinidump>\nGetProcessSnapshotMinidumpFromSinglePending() {\n  std::vector<Report> pending_reports;\n  OperationStatus status = GetPendingReports(&pending_reports);\n  if (status != crashpad::CrashReportDatabase::kNoError ||\n      pending_reports.size() != 1) {\n    return nullptr;\n  }\n\n  auto reader = std::make_unique<crashpad::FileReader>();\n  auto process_snapshot = std::make_unique<crashpad::ProcessSnapshotMinidump>();\n  if (!reader->Open(pending_reports[0].file_path) ||\n      !process_snapshot->Initialize(reader.get())) {\n    return nullptr;\n  }\n  return process_snapshot;\n}\n\nUIWindow* GetAnyWindow() {\n#if defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0\n  UIWindowScene* scene = reinterpret_cast<UIWindowScene*>(\n      [UIApplication sharedApplication].connectedScenes.anyObject);\n  if (@available(iOS 15.0, tvOS 15.0, *)) {\n    return scene.keyWindow;\n  } else {\n    return [scene.windows firstObject];\n  }\n\n#else\n  return [UIApplication sharedApplication].windows[0];\n#endif\n}\n\n[[clang::optnone]] void recurse(int counter) {\n  // Fill up the stack faster.\n  int arr[1024];\n  arr[0] = counter;\n  if (counter > INT_MAX)\n    return;\n  recurse(++counter);\n}\n\n}  // namespace\n\n@interface CPTestApplicationDelegate ()\n- (void)processIntermediateDumps;\n@property(copy, nonatomic) NSString* raw_log_output;\n@end\n\n@implementation CPTestApplicationDelegate {\n  crashpad::CrashpadClient client_;\n  crashpad::ScopedFileHandle raw_logging_file_;\n  crashpad::SimpleAddressRangeBag extra_ranges_;\n  std::unique_ptr<std::string> extra_memory_string_;\n}\n\n@synthesize window = _window;\n\n- (BOOL)application:(UIApplication*)application\n    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {\n  base::FilePath raw_log_file_path = GetRawLogOutputFile();\n  NSString* path =\n      [NSString stringWithUTF8String:raw_log_file_path.value().c_str()];\n  self.raw_log_output =\n      [[NSString alloc] initWithContentsOfFile:path\n                                      encoding:NSUTF8StringEncoding\n                                         error:NULL];\n  raw_logging_file_.reset(\n      LoggingOpenFileForWrite(raw_log_file_path,\n                              crashpad::FileWriteMode::kTruncateOrCreate,\n                              crashpad::FilePermissions::kOwnerOnly));\n  crashpad::internal::SetFileHandleForTesting(raw_logging_file_.get());\n\n  // Start up crashpad.\n  std::map<std::string, std::string> annotations = {\n      {\"prod\", \"xcuitest\"}, {\"ver\", \"1\"}, {\"plat\", \"iOS\"}, {\"crashpad\", \"yes\"}};\n  NSArray<NSString*>* arguments = [[NSProcessInfo processInfo] arguments];\n  if ([arguments containsObject:@\"--alternate-client-annotations\"]) {\n    annotations = {{\"prod\", \"some_app\"},\n                   {\"ver\", \"42\"},\n                   {\"plat\", \"macOS\"},\n                   {\"crashpad\", \"no\"}};\n  }\n  if (client_.StartCrashpadInProcessHandler(\n          GetDatabaseDir(),\n          \"\",\n          annotations,\n          crashpad::CrashpadClient::\n              ProcessPendingReportsObservationCallback())) {\n    crashpad::UserStreamDataSources user_stream_data_sources;\n    if ([arguments containsObject:@\"--test-extension-streams\"]) {\n      user_stream_data_sources.push_back(\n          std::make_unique<TestUserStreamDataSource>());\n    }\n    client_.ProcessIntermediateDumps({}, &user_stream_data_sources);\n  }\n\n  if ([arguments containsObject:@\"--test-extra_memory\"]) {\n    crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(\n        &extra_ranges_);\n    extra_memory_string_ = std::make_unique<std::string>(\"hello world\");\n    extra_ranges_.Insert((void*)extra_memory_string_->c_str(), 11);\n  }\n\n  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];\n  [self.window makeKeyAndVisible];\n  self.window.backgroundColor = UIColor.greenColor;\n\n  CPTestCrashViewController* controller =\n      [[CPTestCrashViewController alloc] init];\n  self.window.rootViewController = controller;\n\n  // Start up EDO.\n  [EDOHostService serviceWithPort:12345\n                       rootObject:[[CPTestSharedObject alloc] init]\n                            queue:dispatch_get_main_queue()];\n\n  return YES;\n}\n\n- (void)processIntermediateDumps {\n  client_.ProcessIntermediateDumps();\n}\n\n@end\n\n@implementation CPTestSharedObject\n\n- (NSString*)testEDO {\n  return @\"crashpad\";\n}\n\n- (void)processIntermediateDumps {\n  CPTestApplicationDelegate* delegate =\n      (CPTestApplicationDelegate*)UIApplication.sharedApplication.delegate;\n  [delegate processIntermediateDumps];\n}\n\n- (void)clearPendingReports {\n  std::unique_ptr<crashpad::CrashReportDatabase> database(GetDatabase());\n  std::vector<crashpad::CrashReportDatabase::Report> pending_reports;\n  database->GetPendingReports(&pending_reports);\n  for (auto report : pending_reports) {\n    database->DeleteReport(report.uuid);\n  }\n}\n\n- (int)pendingReportCount {\n  std::vector<Report> pending_reports;\n  OperationStatus status = GetPendingReports(&pending_reports);\n  if (status != crashpad::CrashReportDatabase::kNoError) {\n    return -1;\n  }\n  return pending_reports.size();\n}\n\n- (bool)pendingReportException:(NSNumber**)exception {\n  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();\n  if (!process_snapshot || !process_snapshot->Exception()->Exception())\n    return false;\n  *exception = [NSNumber\n      numberWithUnsignedInt:process_snapshot->Exception()->Exception()];\n  return true;\n}\n\n- (bool)pendingReportExceptionInfo:(NSNumber**)exception_info {\n  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();\n  if (!process_snapshot || !process_snapshot->Exception()->ExceptionInfo())\n    return false;\n\n  *exception_info = [NSNumber\n      numberWithUnsignedInt:process_snapshot->Exception()->ExceptionInfo()];\n  return true;\n}\n\n- (NSDictionary*)getAnnotations {\n  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();\n  if (!process_snapshot)\n    return @{};\n\n  NSDictionary* dict = @{\n    @\"simplemap\" : [@{} mutableCopy],\n    @\"vector\" : [@[] mutableCopy],\n    @\"objects\" : [@[] mutableCopy],\n    @\"ringbuffers\" : [@[] mutableCopy],\n  };\n  for (const auto* module : process_snapshot->Modules()) {\n    for (const auto& kv : module->AnnotationsSimpleMap()) {\n      [dict[@\"simplemap\"] setValue:@(kv.second.c_str())\n                            forKey:@(kv.first.c_str())];\n    }\n    for (const std::string& annotation : module->AnnotationsVector()) {\n      [dict[@\"vector\"] addObject:@(annotation.c_str())];\n    }\n    for (const auto& annotation : module->AnnotationObjects()) {\n      if (annotation.type ==\n          static_cast<uint16_t>(crashpad::Annotation::Type::kString)) {\n        std::string value(\n            reinterpret_cast<const char*>(annotation.value.data()),\n            annotation.value.size());\n        [dict[@\"objects\"]\n            addObject:@{@(annotation.name.c_str()) : @(value.c_str())}];\n      } else if (annotation.type == static_cast<uint16_t>(kRingBufferType)) {\n        NSData* data = [NSData dataWithBytes:annotation.value.data()\n                                      length:annotation.value.size()];\n        [dict[@\"ringbuffers\"] addObject:@{@(annotation.name.c_str()) : data}];\n      }\n    }\n  }\n  return [dict passByValue];\n}\n\n- (NSDictionary*)getExtraMemory {\n  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();\n  if (!process_snapshot)\n    return @{};\n\n  NSDictionary* dict = [@{} mutableCopy];\n\n  for (auto memory : process_snapshot->ExtraMemory()) {\n    ReadToString delegate;\n    if (memory->Size() > 0 && memory->Read(&delegate) &&\n        !delegate.result.empty()) {\n      NSString* key = [@(memory->Address()) stringValue];\n      NSString* value = @(delegate.result.c_str());\n      if (value.length) {\n        [dict setValue:value forKey:key];\n      }\n    }\n  }\n  return [dict passByValue];\n}\n\n- (BOOL)hasExtensionStream {\n  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();\n  if (!process_snapshot)\n    return NO;\n\n  auto streams = process_snapshot->CustomMinidumpStreams();\n  for (const auto& stream : streams) {\n    if (stream->stream_type() == 0xCAFEBABE) {\n      return memcmp(kExpectedStreamData,\n                    stream->data().data(),\n                    sizeof(kExpectedStreamData)) == 0;\n    }\n  }\n  return NO;\n}\n\n- (NSDictionary*)getProcessAnnotations {\n  auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending();\n  if (!process_snapshot)\n    return @{};\n\n  NSDictionary* dict = [@{} mutableCopy];\n  for (const auto& kv : process_snapshot->AnnotationsSimpleMap()) {\n    [dict setValue:@(kv.second.c_str()) forKey:@(kv.first.c_str())];\n  }\n\n  return [dict passByValue];\n}\n\n// Use [[clang::optnone]] here to get consistent exception codes, otherwise the\n// exception can change depending on optimization level.\n- (void)crashBadAccess [[clang::optnone]] {\n  strcpy(nullptr, \"bla\");\n}\n\n- (void)crashKillAbort {\n  crashpad::test::ReplaceAllocatorsWithHandlerForbidden();\n  kill(getpid(), SIGABRT);\n}\n\n- (void)crashTrap {\n  crashpad::test::ReplaceAllocatorsWithHandlerForbidden();\n  __builtin_trap();\n}\n\n- (void)crashAbort {\n  crashpad::test::ReplaceAllocatorsWithHandlerForbidden();\n  abort();\n}\n\n- (void)crashException {\n  std::vector<int> empty_vector = {};\n  std::ignore = empty_vector.at(42);\n}\n\n- (void)crashNSException {\n  // EDO has its own sinkhole which will suppress this attempt at an NSException\n  // crash, so dispatch this out of the sinkhole.\n  dispatch_async(dispatch_get_main_queue(), ^{\n    NSError* error = [NSError errorWithDomain:@\"com.crashpad.xcuitests\"\n                                         code:200\n                                     userInfo:@{@\"Error Object\" : self}];\n\n    [[NSException exceptionWithName:NSInternalInconsistencyException\n                             reason:@\"Intentionally throwing error.\"\n                           userInfo:@{NSUnderlyingErrorKey : error}] raise];\n  });\n}\n\n- (void)crashNotAnNSException {\n  @throw @\"Boom\";\n}\n\n- (void)crashUnhandledNSException {\n  std::thread t([self]() {\n    @autoreleasepool {\n      @try {\n        NSError* error = [NSError errorWithDomain:@\"com.crashpad.xcuitests\"\n                                             code:200\n                                         userInfo:@{@\"Error Object\" : self}];\n\n        [[NSException exceptionWithName:NSInternalInconsistencyException\n                                 reason:@\"Intentionally throwing error.\"\n                               userInfo:@{NSUnderlyingErrorKey : error}] raise];\n      } @catch (id reason_exception) {\n        // Intentionally use throw here to intentionally make a sinkhole that\n        // will be missed by ObjcPreprocessor.\n        objc_exception_throw(reason_exception);\n      }\n    }\n  });\n  t.join();\n}\n\n- (void)crashUnrecognizedSelectorAfterDelay {\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wundeclared-selector\"\n  [self performSelector:@selector(does_not_exist) withObject:nil afterDelay:1];\n#pragma clang diagnostic pop\n}\n\n- (void)catchNSException {\n  @try {\n    NSArray* empty_array = @[];\n    [empty_array objectAtIndex:42];\n  } @catch (NSException* exception) {\n  } @finally {\n  }\n}\n\n- (void)crashCoreAutoLayoutSinkhole {\n  // EDO has its own sinkhole which will suppress this attempt at an NSException\n  // crash, so dispatch this out of the sinkhole.\n  dispatch_async(dispatch_get_main_queue(), ^{\n    UIView* unattachedView = [[UIView alloc] init];\n    UIWindow* window = GetAnyWindow();\n    [NSLayoutConstraint activateConstraints:@[\n      [window.rootViewController.view.bottomAnchor\n          constraintEqualToAnchor:unattachedView.bottomAnchor],\n    ]];\n  });\n}\n\n- (void)crashRecursion {\n  recurse(0);\n}\n\n- (void)crashWithCrashInfoMessage {\n  dlsym(nullptr, nullptr);\n}\n\n- (void)crashWithDyldErrorString {\n  std::string crashy_initializer =\n      base::SysNSStringToUTF8([[NSBundle mainBundle]\n          pathForResource:@\"crashpad_snapshot_test_module_crashy_initializer\"\n                   ofType:@\"so\"]);\n  dlopen(crashy_initializer.c_str(), RTLD_LAZY | RTLD_LOCAL);\n}\n\n- (void)crashWithAnnotations {\n  // This is “leaked” to crashpad_info.\n  crashpad::SimpleStringDictionary* simple_annotations =\n      new crashpad::SimpleStringDictionary();\n  simple_annotations->SetKeyValue(\"#TEST# pad\", \"break\");\n  simple_annotations->SetKeyValue(\"#TEST# key\", \"value\");\n  simple_annotations->SetKeyValue(\"#TEST# pad\", \"crash\");\n  simple_annotations->SetKeyValue(\"#TEST# x\", \"y\");\n  simple_annotations->SetKeyValue(\"#TEST# longer\", \"shorter\");\n  simple_annotations->SetKeyValue(\"#TEST# empty_value\", \"\");\n\n  crashpad::CrashpadInfo* crashpad_info =\n      crashpad::CrashpadInfo::GetCrashpadInfo();\n\n  crashpad_info->set_simple_annotations(simple_annotations);\n\n  crashpad::AnnotationList::Register();  // This is “leaked” to crashpad_info.\n\n  static crashpad::StringAnnotation<32> test_annotation_one{\"#TEST# one\"};\n  static crashpad::StringAnnotation<32> test_annotation_two{\"#TEST# two\"};\n  static crashpad::StringAnnotation<32> test_annotation_three{\n      \"#TEST# same-name\"};\n  static crashpad::StringAnnotation<32> test_annotation_four{\n      \"#TEST# same-name\"};\n  static crashpad::RingBufferAnnotation<32> test_ring_buffer_annotation(\n      kRingBufferType, \"#TEST# ring_buffer\");\n  static crashpad::RingBufferAnnotation<32> test_busy_ring_buffer_annotation(\n      kRingBufferType, \"#TEST# busy_ring_buffer\");\n\n  test_annotation_one.Set(\"moocow\");\n  test_annotation_two.Set(\"this will be cleared\");\n  test_annotation_three.Set(\"same-name 3\");\n  test_annotation_four.Set(\"same-name 4\");\n  test_annotation_two.Clear();\n  test_ring_buffer_annotation.Push(\"hello\", 5);\n  test_ring_buffer_annotation.Push(\"goodbye\", 7);\n  test_busy_ring_buffer_annotation.Push(\"busy\", 4);\n  // Take the scoped spin guard on `test_busy_ring_buffer_annotation` to mimic\n  // an in-flight `Push()` so its contents are not included in the dump.\n  auto guard = test_busy_ring_buffer_annotation.TryCreateScopedSpinGuard(\n      /*timeout_nanos=*/0);\n  abort();\n}\n\nclass RaceThread : public crashpad::Thread {\n public:\n  explicit RaceThread() : Thread() {}\n\n  void SetCount(int count) { count_ = count; }\n\n private:\n  void ThreadMain() override {\n    for (int i = 0; i < count_; ++i) {\n      CRASHPAD_SIMULATE_CRASH();\n    }\n  }\n\n  int count_;\n};\n\n- (void)generateDumpWithoutCrash:(int)dump_count threads:(int)threads {\n  std::vector<RaceThread> race_threads(threads);\n  for (RaceThread& race_thread : race_threads) {\n    race_thread.SetCount(dump_count);\n    race_thread.Start();\n  }\n\n  for (RaceThread& race_thread : race_threads) {\n    race_thread.Join();\n  }\n}\n\nclass CrashThread : public crashpad::Thread {\n public:\n  explicit CrashThread(bool signal) : Thread(), signal_(signal) {}\n\n private:\n  void ThreadMain() override {\n    sleep(1);\n    if (signal_) {\n      abort();\n    } else {\n      __builtin_trap();\n    }\n  }\n  bool signal_;\n};\n\n- (void)crashConcurrentSignalAndMach {\n  CrashThread signal_thread(true);\n  CrashThread mach_thread(false);\n  signal_thread.Start();\n  mach_thread.Start();\n  signal_thread.Join();\n  mach_thread.Join();\n}\n\nclass ThrowNSExceptionThread : public crashpad::Thread {\n public:\n  explicit ThrowNSExceptionThread() : Thread() {}\n\n private:\n  void ThreadMain() override {\n    for (int i = 0; i < 300; ++i) {\n      @try {\n        NSArray* empty_array = @[];\n        [empty_array objectAtIndex:42];\n      } @catch (NSException* exception) {\n      } @finally {\n      }\n    }\n  }\n};\n\n- (void)catchConcurrentNSException {\n  std::vector<ThrowNSExceptionThread> race_threads(30);\n  for (ThrowNSExceptionThread& race_thread : race_threads) {\n    race_thread.Start();\n  }\n\n  for (ThrowNSExceptionThread& race_thread : race_threads) {\n    race_thread.Join();\n  }\n}\n\n- (void)crashInHandlerReentrant {\n  crashpad::CrashpadClient client_;\n  client_.SetExceptionCallbackForTesting(abort);\n\n  // Trigger an exception.\n  [self crashTrap];\n}\n\n- (void)allocateWithForbiddenAllocators {\n  crashpad::test::ReplaceAllocatorsWithHandlerForbidden();\n  (void)malloc(10);\n}\n\n- (NSString*)rawLogContents {\n  CPTestApplicationDelegate* delegate =\n      (CPTestApplicationDelegate*)UIApplication.sharedApplication.delegate;\n  return delegate.raw_log_output;\n}\n\n@end\n"
  },
  {
    "path": "test/ios/host/cptest_crash_view_controller.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_\n#define CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_\n\n#import <UIKit/UIKit.h>\n\n@interface CPTestCrashViewController : UIViewController\n@end\n\n#endif  // CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_\n"
  },
  {
    "path": "test/ios/host/cptest_crash_view_controller.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"test/ios/host/cptest_crash_view_controller.h\"\n\n@implementation CPTestCrashViewController\n\n- (void)loadView {\n  self.view = [[UIView alloc] init];\n\n  UIStackView* buttonStack = [[UIStackView alloc] init];\n  buttonStack.axis = UILayoutConstraintAxisVertical;\n  buttonStack.spacing = 6;\n\n  UIButton* button = [UIButton new];\n  [button setTitle:@\"UIGestureEnvironmentException\"\n          forState:UIControlStateNormal];\n  UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc]\n      initWithTarget:self\n              action:@selector(throwUIGestureEnvironmentException)];\n  [button addGestureRecognizer:tapGesture];\n  [button setTranslatesAutoresizingMaskIntoConstraints:NO];\n\n  [buttonStack addArrangedSubview:button];\n\n  [self.view addSubview:buttonStack];\n\n  [buttonStack setTranslatesAutoresizingMaskIntoConstraints:NO];\n\n  [NSLayoutConstraint activateConstraints:@[\n    [buttonStack.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],\n    [buttonStack.topAnchor constraintEqualToAnchor:self.view.topAnchor],\n    [buttonStack.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],\n    [buttonStack.trailingAnchor\n        constraintEqualToAnchor:self.view.trailingAnchor],\n  ]];\n}\n\n- (void)throwUIGestureEnvironmentException {\n  NSArray* empty_array = @[];\n  [empty_array objectAtIndex:42];\n}\n\n- (void)viewDidLoad {\n  [super viewDidLoad];\n  self.view.backgroundColor = UIColor.redColor;\n}\n\n@end\n"
  },
  {
    "path": "test/ios/host/cptest_shared_object.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_\n#define CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_\n\n#import <UIKit/UIKit.h>\n\n@interface CPTestSharedObject : NSObject\n\n// Returns the string \"crashpad\" for testing EDO.\n- (NSString*)testEDO;\n\n// Tell Crashpad to process intermediate dumps.\n- (void)processIntermediateDumps;\n\n// Clear pending reports from Crashpad database.\n- (void)clearPendingReports;\n\n// Returns the number of pending reports, or -1 if there's an error getting\n// report.\n- (int)pendingReportCount;\n\n// Returns true if there's a single pending report and sets the exception code\n// in the out |exception| parameter. Returns false if there's a different number\n// of pending reports.\n- (bool)pendingReportException:(NSNumber**)exception;\n\n// Returns true if there's a single pending report and sets the second-level\n// exception code in the out |exception_info| parameter. Returns false if\n// there's a different number of pending reports.\n- (bool)pendingReportExceptionInfo:(NSNumber**)exception_info;\n\n// Return an NSDictionary with a dictionary named \"simplemap\", an array named\n// \"vector\" an array named \"objects\", and an array named \"ringbuffers\",\n// representing the combination of all modules AnnotationsSimpleMap,\n// AnnotationsVector and AnnotationObjects (String and RingBuffer type)\n// respectively.\n- (NSDictionary*)getAnnotations;\n\n// Return an NSDictionary with a dictionary representing all key value pairs of\n// ExtraMemory MemorySnapshots where the data can be converted to an NSString.\n- (NSDictionary*)getExtraMemory;\n\n// Returns YES if a minidump contains the expected custom stream data.\n- (BOOL)hasExtensionStream;\n\n// Return an NSDictionary representing the ProcessSnapshotMinidump\n// AnnotationsSimpleMap.\n- (NSDictionary*)getProcessAnnotations;\n\n// Triggers an EXC_BAD_ACCESS exception and crash.\n- (void)crashBadAccess;\n\n// Triggers a crash with a call to kill(SIGABRT). This crash runs with\n// ReplaceAllocatorsWithHandlerForbidden.\n- (void)crashKillAbort;\n\n// Trigger a crash with a __builtin_trap. This crash runs with\n// ReplaceAllocatorsWithHandlerForbidden.\n- (void)crashTrap;\n\n// Trigger a crash with an abort(). This crash runs with\n// ReplaceAllocatorsWithHandlerForbidden.\n- (void)crashAbort;\n\n// Trigger a crash with an uncaught exception.\n- (void)crashException;\n\n// Trigger a crash with an uncaught NSException.\n- (void)crashNSException;\n\n// Trigger a crash throwing something that isn't an NSException (an NSString).\n- (void)crashNotAnNSException;\n\n// Trigger a crash with an uncaught and unhandled NSException.\n- (void)crashUnhandledNSException;\n\n// Trigger an unrecognized selector after delay.\n- (void)crashUnrecognizedSelectorAfterDelay;\n\n// Trigger a caught NSException, this will not crash\n- (void)catchNSException;\n\n// Trigger an NSException with sinkholes in CoreAutoLayout.\n- (void)crashCoreAutoLayoutSinkhole;\n\n// Trigger a crash with an infinite recursion.\n- (void)crashRecursion;\n\n// Trigger a crash dlsym that contains a crash_info message.\n- (void)crashWithCrashInfoMessage;\n\n// Trigger an error that will to the dyld error string `_error_string`\n- (void)crashWithDyldErrorString;\n\n// Trigger a crash after writing various annotations.\n- (void)crashWithAnnotations;\n\n// Triggers a DumpWithoutCrash |dump_count| times in each of |threads| threads.\n- (void)generateDumpWithoutCrash:(int)dump_count threads:(int)threads;\n\n// Triggers a simulataneous Mach exception and signal in different threads.\n- (void)crashConcurrentSignalAndMach;\n\n// Triggers simultaneous caught NSExceptions\n- (void)catchConcurrentNSException;\n\n// Triggers a SIGABRT signal while handling an NSException to test reentrant\n// exceptions.\n- (void)crashInHandlerReentrant;\n\n// Runs with ReplaceAllocatorsWithHandlerForbidden and allocates memory, testing\n// that the handler forbidden allocator works.\n- (void)allocateWithForbiddenAllocators;\n\n// Return the contents of the RawLog output from the previous run of the host\n// application.\n- (NSString*)rawLogContents;\n\n@end\n\n#endif  // CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_\n"
  },
  {
    "path": "test/ios/host/handler_forbidden_allocators.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import \"test/ios/host/handler_forbidden_allocators.h\"\n\n#include <CoreFoundation/CoreFoundation.h>\n#include <malloc/malloc.h>\n#include <pthread.h>\n\n#include <limits>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check_op.h\"\n#include \"client/crashpad_client.h\"\n#include \"util/ios/raw_logging.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nuint64_t g_main_thread = 0;\nuint64_t g_mach_exception_thread = 0;\n\n// Somewhat simplified logic copied from Chromium's\n// base/allocator/partition_allocator/shim/malloc_zone_functions_apple.h. The\n// arrays g_original_zones and g_original_zones_ptr stores all information about\n// malloc zones before they are shimmed. This information needs to be accessed\n// during dispatch back into the zone.\nconstexpr int kMaxZoneCount = 30;\nmalloc_zone_t g_original_zones[kMaxZoneCount];\nmalloc_zone_t* g_original_zones_ptr[kMaxZoneCount];\nunsigned int g_zone_count = 0;\n\nstruct _malloc_zone_t original_zone_for_zone(struct _malloc_zone_t* zone) {\n  for (unsigned int i = 0; i < g_zone_count; ++i) {\n    if (g_original_zones_ptr[i] == zone) {\n      return g_original_zones[i];\n    }\n  }\n  return g_original_zones[0];\n}\n\nbool is_handler_thread() {\n  uint64_t thread_self;\n  pthread_threadid_np(pthread_self(), &thread_self);\n  return (thread_self == g_main_thread ||\n          thread_self == g_mach_exception_thread);\n}\n\nvoid* handler_forbidden_malloc(struct _malloc_zone_t* zone, size_t size) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_malloc allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  return original_zone_for_zone(zone).malloc(zone, size);\n}\n\nvoid* handler_forbidden_calloc(struct _malloc_zone_t* zone,\n                               size_t num_items,\n                               size_t size) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_calloc allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  return original_zone_for_zone(zone).calloc(zone, num_items, size);\n}\n\nvoid* handler_forbidden_valloc(struct _malloc_zone_t* zone, size_t size) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_valloc allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  return original_zone_for_zone(zone).valloc(zone, size);\n}\n\nvoid handler_forbidden_free(struct _malloc_zone_t* zone, void* ptr) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_free allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  original_zone_for_zone(zone).free(zone, ptr);\n}\n\nvoid* handler_forbidden_realloc(struct _malloc_zone_t* zone,\n                                void* ptr,\n                                size_t size) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_realloc allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  return original_zone_for_zone(zone).realloc(zone, ptr, size);\n}\n\nvoid handler_forbidden_destroy(struct _malloc_zone_t* zone) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_destroy allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  original_zone_for_zone(zone).destroy(zone);\n}\n\nvoid* handler_forbidden_memalign(struct _malloc_zone_t* zone,\n                                 size_t alignment,\n                                 size_t size) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_memalign allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  return original_zone_for_zone(zone).memalign(zone, alignment, size);\n}\n\nunsigned handler_forbidden_batch_malloc(struct _malloc_zone_t* zone,\n                                        size_t size,\n                                        void** results,\n                                        unsigned num_requested) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\n        \"handler_forbidden_batch_malloc allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  return original_zone_for_zone(zone).batch_malloc(\n      zone, size, results, num_requested);\n}\n\nvoid handler_forbidden_batch_free(struct _malloc_zone_t* zone,\n                                  void** to_be_freed,\n                                  unsigned num_to_be_freed) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_batch_free allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  original_zone_for_zone(zone).batch_free(zone, to_be_freed, num_to_be_freed);\n}\n\nvoid handler_forbidden_free_definite_size(struct _malloc_zone_t* zone,\n                                          void* ptr,\n                                          size_t size) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\n        \"handler_forbidden_free_definite_size allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  original_zone_for_zone(zone).free_definite_size(zone, ptr, size);\n}\n\nsize_t handler_forbidden_pressure_relief(struct _malloc_zone_t* zone,\n                                         size_t goal) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\n        \"handler_forbidden_pressure_relief allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  return original_zone_for_zone(zone).pressure_relief(zone, goal);\n}\n\nboolean_t handler_forbidden_claimed_address(struct _malloc_zone_t* zone,\n                                            void* ptr) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\n        \"handler_forbidden_claimed_address allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n\n  if (original_zone_for_zone(zone).claimed_address) {\n    return original_zone_for_zone(zone).claimed_address(zone, ptr);\n  }\n\n  // If the fast API 'claimed_address' is not implemented in the specified zone,\n  // fall back to 'size' function, which also tells whether the given address\n  // belongs to the zone or not although it'd be slow.\n  return original_zone_for_zone(zone).size(zone, ptr);\n}\n\n#if defined(__IPHONE_16_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_1\n// The fallback function to be called when try_free_default_function receives a\n// pointer which doesn't belong to the allocator.\nvoid TryFreeDefaultFallbackToFindZoneAndFree(void* ptr) {\n  unsigned int zone_count = 0;\n  vm_address_t* zones = nullptr;\n  kern_return_t result =\n      malloc_get_all_zones(mach_task_self(), nullptr, &zones, &zone_count);\n  MACH_CHECK(result == KERN_SUCCESS, result) << \"malloc_get_all_zones\";\n\n  // \"find_zone_and_free\" expected by try_free_default.\n  //\n  // libmalloc's zones call find_registered_zone() in case the default one\n  // doesn't handle the allocation. We can't, so we try to emulate it. See the\n  // implementation in libmalloc/src/malloc.c for details.\n  // https://github.com/apple-oss-distributions/libmalloc/blob/main/src/malloc.c\n  for (unsigned int i = 0; i < zone_count; ++i) {\n    malloc_zone_t* zone = reinterpret_cast<malloc_zone_t*>(zones[i]);\n    if (size_t size = zone->size(zone, ptr)) {\n      if (zone->version >= 6 && zone->free_definite_size) {\n        zone->free_definite_size(zone, ptr, size);\n      } else {\n        zone->free(zone, ptr);\n      }\n      return;\n    }\n  }\n}\n\nvoid handler_forbidden_try_free_default(struct _malloc_zone_t* zone,\n                                        void* ptr) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\n        \"handler_forbidden_try_free_default allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n\n  if (original_zone_for_zone(zone).try_free_default) {\n    return original_zone_for_zone(zone).try_free_default(zone, ptr);\n  }\n  TryFreeDefaultFallbackToFindZoneAndFree(ptr);\n}\n#endif\n\nsize_t handler_forbidden_size(struct _malloc_zone_t* zone, const void* ptr) {\n  if (is_handler_thread()) {\n    CRASHPAD_RAW_LOG(\"handler_forbidden_size allocator used in handler.\");\n    exit(EXIT_FAILURE);\n  }\n  return original_zone_for_zone(zone).size(zone, ptr);\n}\n\nbool DeprotectMallocZone(malloc_zone_t* default_zone,\n                         vm_address_t* reprotection_start,\n                         vm_size_t* reprotection_length,\n                         vm_prot_t* reprotection_value) {\n  mach_port_t unused;\n  *reprotection_start = reinterpret_cast<vm_address_t>(default_zone);\n  struct vm_region_basic_info_64 info;\n  mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;\n  kern_return_t result = vm_region_64(mach_task_self(),\n                                      reprotection_start,\n                                      reprotection_length,\n                                      VM_REGION_BASIC_INFO_64,\n                                      reinterpret_cast<vm_region_info_t>(&info),\n                                      &count,\n                                      &unused);\n  if (result != KERN_SUCCESS) {\n    MACH_LOG(ERROR, result) << \"vm_region_64\";\n    return false;\n  }\n\n  // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but\n  // balance it with a deallocate in case this ever changes. See\n  // the VM_REGION_BASIC_INFO_64 case in vm_map_region() in 10.15's\n  // https://opensource.apple.com/source/xnu/xnu-6153.11.26/osfmk/vm/vm_map.c .\n  mach_port_deallocate(mach_task_self(), unused);\n\n  if (!(info.max_protection & VM_PROT_WRITE)) {\n    LOG(ERROR) << \"Invalid max_protection \" << info.max_protection;\n    return false;\n  }\n\n  // Does the region fully enclose the zone pointers? Possibly unwarranted\n  // simplification used: using the size of a full version 10 malloc zone rather\n  // than the actual smaller size if the passed-in zone is not version 10.\n  DCHECK_LE(*reprotection_start, reinterpret_cast<vm_address_t>(default_zone));\n  vm_size_t zone_offset = reinterpret_cast<vm_address_t>(default_zone) -\n                          reinterpret_cast<vm_address_t>(*reprotection_start);\n  DCHECK_LE(zone_offset + sizeof(malloc_zone_t), *reprotection_length);\n\n  if (info.protection & VM_PROT_WRITE) {\n    // No change needed; the zone is already writable.\n    *reprotection_start = 0;\n    *reprotection_length = 0;\n    *reprotection_value = VM_PROT_NONE;\n  } else {\n    *reprotection_value = info.protection;\n    result = vm_protect(mach_task_self(),\n                        *reprotection_start,\n                        *reprotection_length,\n                        false,\n                        info.protection | VM_PROT_WRITE);\n    if (result != KERN_SUCCESS) {\n      MACH_LOG(ERROR, result) << \"vm_protect\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid ReplaceZoneFunctions(malloc_zone_t* zone, const malloc_zone_t* functions) {\n  // Remove protection.\n  vm_address_t reprotection_start = 0;\n  vm_size_t reprotection_length = 0;\n  vm_prot_t reprotection_value = VM_PROT_NONE;\n  bool success = DeprotectMallocZone(\n      zone, &reprotection_start, &reprotection_length, &reprotection_value);\n  if (!success) {\n    return;\n  }\n\n  zone->size = functions->size;\n  zone->malloc = functions->malloc;\n  zone->calloc = functions->calloc;\n  zone->valloc = functions->valloc;\n  zone->free = functions->free;\n  zone->realloc = functions->realloc;\n  zone->destroy = functions->destroy;\n  zone->batch_malloc = functions->batch_malloc;\n  zone->batch_free = functions->batch_free;\n  zone->memalign = functions->memalign;\n  zone->free_definite_size = functions->free_definite_size;\n  zone->pressure_relief = functions->pressure_relief;\n  zone->claimed_address = functions->claimed_address;\n#if defined(__IPHONE_16_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_1\n  if (zone->version >= 13 && functions->try_free_default) {\n    zone->try_free_default = functions->try_free_default;\n  }\n#endif\n\n  // Restore protection if it was active.\n  if (reprotection_start) {\n    kern_return_t result = vm_protect(mach_task_self(),\n                                      reprotection_start,\n                                      reprotection_length,\n                                      false,\n                                      reprotection_value);\n    if (result != KERN_SUCCESS) {\n      MACH_LOG(ERROR, result) << \"vm_protect\";\n      return;\n    }\n  }\n}\n\n}  // namespace\n\nvoid ReplaceAllocatorsWithHandlerForbidden() {\n  pthread_threadid_np(pthread_self(), &g_main_thread);\n\n  CrashpadClient crashpad_client;\n  g_mach_exception_thread = crashpad_client.GetThreadIdForTesting();\n\n  malloc_zone_t new_functions = {};\n  new_functions.size = handler_forbidden_size;\n  new_functions.malloc = handler_forbidden_malloc;\n  new_functions.calloc = handler_forbidden_calloc;\n  new_functions.valloc = handler_forbidden_valloc;\n  new_functions.free = handler_forbidden_free;\n  new_functions.realloc = handler_forbidden_realloc;\n  new_functions.destroy = handler_forbidden_destroy;\n  new_functions.batch_malloc = handler_forbidden_batch_malloc;\n  new_functions.batch_free = handler_forbidden_batch_free;\n  new_functions.memalign = handler_forbidden_memalign;\n  new_functions.free_definite_size = handler_forbidden_free_definite_size;\n  new_functions.pressure_relief = handler_forbidden_pressure_relief;\n  new_functions.claimed_address = handler_forbidden_claimed_address;\n#if defined(__IPHONE_16_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_1\n  new_functions.try_free_default = handler_forbidden_try_free_default;\n#endif\n  malloc_zone_t* default_zone = malloc_default_zone();\n  g_original_zones_ptr[g_zone_count] = default_zone;\n  ReplaceZoneFunctions(&g_original_zones[g_zone_count++], default_zone);\n  ReplaceZoneFunctions(default_zone, &new_functions);\n\n  malloc_zone_t* purgeable_zone = malloc_default_purgeable_zone();\n  g_original_zones_ptr[g_zone_count] = purgeable_zone;\n  ReplaceZoneFunctions(&g_original_zones[g_zone_count++], purgeable_zone);\n  ReplaceZoneFunctions(purgeable_zone, &new_functions);\n\n  vm_address_t* zones;\n  unsigned int count;\n  kern_return_t kr =\n      malloc_get_all_zones(mach_task_self(), nullptr, &zones, &count);\n  if (kr != KERN_SUCCESS)\n    return;\n  for (unsigned int i = 0; i < count; ++i) {\n    malloc_zone_t* zone = reinterpret_cast<malloc_zone_t*>(zones[i]);\n    g_original_zones_ptr[g_zone_count] = zone;\n    ReplaceZoneFunctions(&g_original_zones[g_zone_count++], zone);\n    ReplaceZoneFunctions(zone, &new_functions);\n\n    if (g_zone_count >= kMaxZoneCount)\n      break;\n  }\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/ios/host/handler_forbidden_allocators.h",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_IOS_HANDLER_FORBIDDEN_ALLOCATIONS_H_\n#define CRASHPAD_TEST_IOS_HANDLER_FORBIDDEN_ALLOCATIONS_H_\n\nnamespace crashpad {\nnamespace test {\n\n// Override malloc_default_zone and malloc_default_purgeable_zone with functions\n// that immediately exit if called from the same thread that this helper is\n// called from or from the Crashpad Mach exception handler thread indicated by\n// GetThreadIdForTesting. This is used to ensure the allocator is not used by\n// the Crashpad InProcessHandler.\nvoid ReplaceAllocatorsWithHandlerForbidden();\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_IOS_HANDLER_FORBIDDEN_ALLOCATIONS_H_\n"
  },
  {
    "path": "test/ios/host/main.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#import <UIKit/UIKit.h>\n\n#import \"test/ios/host/cptest_application_delegate.h\"\n\nint main(int argc, char* argv[]) {\n  NSString* appDelegateClassName;\n  @autoreleasepool {\n    // Setup code that might create autoreleased objects goes here.\n    appDelegateClassName = NSStringFromClass([CPTestApplicationDelegate class]);\n  }\n  return UIApplicationMain(argc, argv, nil, appDelegateClassName);\n}\n"
  },
  {
    "path": "test/linux/fake_ptrace_connection.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/linux/fake_ptrace_connection.h\"\n\n#include <utility>\n\n#include \"base/notreached.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\n\nFakePtraceConnection::FakePtraceConnection()\n    : PtraceConnection(),\n      attachments_(),\n      pid_(-1),\n      is_64_bit_(false),\n      initialized_() {}\n\nFakePtraceConnection::~FakePtraceConnection() {}\n\nbool FakePtraceConnection::Initialize(pid_t pid) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!Attach(pid)) {\n    return false;\n  }\n  pid_ = pid;\n\n#if defined(ARCH_CPU_64_BITS)\n  is_64_bit_ = true;\n#else\n  is_64_bit_ = false;\n#endif\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\npid_t FakePtraceConnection::GetProcessID() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return pid_;\n}\n\nbool FakePtraceConnection::Attach(pid_t tid) {\n  bool inserted = attachments_.insert(tid).second;\n  EXPECT_TRUE(inserted);\n  return inserted;\n}\n\nbool FakePtraceConnection::Is64Bit() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return is_64_bit_;\n}\n\nbool FakePtraceConnection::GetThreadInfo(pid_t tid, ThreadInfo* info) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  bool attached = attachments_.find(tid) != attachments_.end();\n  EXPECT_TRUE(attached);\n  return attached;\n}\n\nbool FakePtraceConnection::ReadFileContents(const base::FilePath& path,\n                                            std::string* contents) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return LoggingReadEntireFile(path, contents);\n}\n\nProcessMemoryLinux* FakePtraceConnection::Memory() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!memory_) {\n    memory_ = std::make_unique<ProcessMemoryLinux>(this);\n  }\n  return memory_.get();\n}\n\nbool FakePtraceConnection::Threads(std::vector<pid_t>* threads) {\n  // TODO(jperaza): Implement this if/when it's needed.\n  NOTREACHED();\n}\n\nssize_t FakePtraceConnection::ReadUpTo(VMAddress address,\n                                       size_t size,\n                                       void* buffer) {\n  NOTREACHED();\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/linux/fake_ptrace_connection.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_LINUX_FAKE_PTRACE_CONNECTION_H_\n#define CRASHPAD_TEST_LINUX_FAKE_PTRACE_CONNECTION_H_\n\n#include <sys/types.h>\n\n#include <memory>\n#include <set>\n\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory_linux.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Stands-in where real PtraceConnections aren't available.\n//!\n//! This class performs basic EXPECTs that it is used correctly, but does not\n//! execute any real `ptrace` calls or attachments.\nclass FakePtraceConnection : public PtraceConnection {\n public:\n  FakePtraceConnection();\n\n  FakePtraceConnection(const FakePtraceConnection&) = delete;\n  FakePtraceConnection& operator=(const FakePtraceConnection&) = delete;\n\n  ~FakePtraceConnection();\n\n  //! \\brief Initializes this connection for the process whose process ID is\n  //!     \\a pid.\n  //!\n  //! \\param[in] pid The process ID of the process to connect to.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Initialize(pid_t pid);\n\n  // PtraceConnection:\n\n  pid_t GetProcessID() override;\n  bool Attach(pid_t tid) override;\n\n  //! \\brief Returns `true` if the current process is 64-bit.\n  bool Is64Bit() override;\n\n  //! \\brief Does not modify \\a info.\n  bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;\n\n  bool ReadFileContents(const base::FilePath& path,\n                        std::string* contents) override;\n\n  //! \\brief Attempts to create a ProcessMemory when called, calling\n  //!     ADD_FAILURE() and returning `nullptr` on failure.\n  ProcessMemoryLinux* Memory() override;\n\n  //! \\todo Not yet implemented.\n  bool Threads(std::vector<pid_t>* threads) override;\n\n  //! \\\\todo Not yet implemented.\n  ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) override;\n\n private:\n  std::set<pid_t> attachments_;\n  std::unique_ptr<ProcessMemoryLinux> memory_;\n  pid_t pid_;\n  bool is_64_bit_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_LINUX_FAKE_PTRACE_CONNECTION_H_\n"
  },
  {
    "path": "test/linux/get_tls.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/linux/get_tls.h\"\n\n#include \"build/build_config.h\"\n#include \"util/misc/from_pointer_cast.h\"\n\nnamespace crashpad {\nnamespace test {\n\nLinuxVMAddress GetTLS() {\n  LinuxVMAddress tls;\n#if defined(ARCH_CPU_ARMEL)\n  // 0xffff0fe0 is the address of the kernel user helper __kuser_get_tls().\n  auto kuser_get_tls = reinterpret_cast<void* (*)()>(0xffff0fe0);\n  tls = FromPointerCast<LinuxVMAddress>(kuser_get_tls());\n#elif defined(ARCH_CPU_ARM64)\n  // Linux/aarch64 places the tls address in system register tpidr_el0.\n  asm(\"mrs %0, tpidr_el0\" : \"=r\"(tls));\n#elif defined(ARCH_CPU_X86)\n  uint32_t tls_32;\n  asm(\"movl %%gs:0x0, %0\" : \"=r\"(tls_32));\n  tls = tls_32;\n#elif defined(ARCH_CPU_X86_64)\n  asm(\"movq %%fs:0x0, %0\" : \"=r\"(tls));\n#elif defined(ARCH_CPU_MIPSEL)\n  uint32_t tls_32;\n  asm(\"rdhwr   $3,$29\\n\\t\"\n      \"move    %0,$3\\n\\t\"\n      : \"=r\"(tls_32)\n      :\n      : \"$3\");\n  tls = tls_32;\n#elif defined(ARCH_CPU_MIPS64EL)\n  asm(\"rdhwr   $3,$29\\n\\t\"\n      \"move    %0,$3\\n\\t\"\n      : \"=r\"(tls)\n      :\n      : \"$3\");\n#elif defined(ARCH_CPU_RISCV64)\n  asm(\"mv %0, tp\" : \"=r\"(tls));\n#else\n#error Port.\n#endif  // ARCH_CPU_ARMEL\n  return tls;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/linux/get_tls.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_LINUX_GET_TLS_H_\n#define CRASHPAD_TEST_LINUX_GET_TLS_H_\n\n#include \"util/linux/address_types.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Return the thread-local storage address for the current thread.\nLinuxVMAddress GetTLS();\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_LINUX_GET_TLS_H_\n"
  },
  {
    "path": "test/mac/dyld.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/mac/dyld.h\"\n\n#include <Availability.h>\n#include <dlfcn.h>\n#include <mach-o/dyld.h>\n#include <mach/mach.h>\n#include <stdint.h>\n\n#include \"base/logging.h\"\n#include \"snapshot/mac/process_reader_mac.h\"\n#include \"test/scoped_module_handle.h\"\n#include \"util/numeric/safe_assignment.h\"\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_13\nextern \"C\" {\n\n// A non-public dyld API, declared in 10.12.4\n// dyld-433.5/include/mach-o/dyld_priv.h. The code still exists in 10.13, but\n// its symbol is no longer public, so it can’t be used there.\nconst dyld_all_image_infos* _dyld_get_all_image_infos()\n    __attribute__((weak_import));\n\n}  // extern \"C\"\n#endif\n\nnamespace crashpad {\nnamespace test {\n\nconst dyld_all_image_infos* DyldGetAllImageInfos() {\n#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_13\n  // When building with the pre-10.13 SDK, the weak_import declaration above is\n  // available and a symbol will be present in the SDK to link against. If the\n  // old interface is also available at run time (running on pre-10.13), use it.\n  if (_dyld_get_all_image_infos) {\n    return _dyld_get_all_image_infos();\n  }\n#elif __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13\n  // When building with the 10.13 SDK or later, but able to run on pre-10.13,\n  // look for _dyld_get_all_image_infos in the same module that provides\n  // _dyld_image_count. There’s no symbol in the SDK to link against, so this is\n  // a little more involved than the pre-10.13 SDK case above.\n  Dl_info dli;\n  if (!dladdr(reinterpret_cast<void*>(_dyld_image_count), &dli)) {\n    LOG(WARNING) << \"dladdr: failed\";\n  } else {\n    ScopedModuleHandle module(\n        dlopen(dli.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD));\n    if (!module.valid()) {\n      LOG(WARNING) << \"dlopen: \" << dlerror();\n    } else {\n      using DyldGetAllImageInfosType = const dyld_all_image_infos*(*)();\n      const auto _dyld_get_all_image_infos =\n          module.LookUpSymbol<DyldGetAllImageInfosType>(\n              \"_dyld_get_all_image_infos\");\n      if (_dyld_get_all_image_infos) {\n        return _dyld_get_all_image_infos();\n      }\n    }\n  }\n#endif\n\n  // On 10.13 and later, do it the hard way.\n  ProcessReaderMac process_reader;\n  if (!process_reader.Initialize(mach_task_self())) {\n    return nullptr;\n  }\n\n  mach_vm_address_t all_image_info_addr_m =\n      process_reader.DyldAllImageInfo(nullptr);\n  if (!all_image_info_addr_m) {\n    return nullptr;\n  }\n\n  uintptr_t all_image_info_addr_u;\n  if (!AssignIfInRange(&all_image_info_addr_u, all_image_info_addr_m)) {\n    LOG(ERROR) << \"all_image_info_addr_m \" << all_image_info_addr_m\n               << \" out of range\";\n    return nullptr;\n  }\n\n  return reinterpret_cast<const dyld_all_image_infos*>(all_image_info_addr_u);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/mac/dyld.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_MAC_DYLD_H_\n#define CRASHPAD_TEST_MAC_DYLD_H_\n\n#include <mach-o/dyld_images.h>\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Calls or emulates the `_dyld_get_all_image_infos()` private/internal\n//!     function.\n//!\n//! \\return A pointer to this process’ dyld_all_image_infos structure, or\n//!     `nullptr` on failure with a message logged.\nconst dyld_all_image_infos* DyldGetAllImageInfos();\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_MAC_DYLD_H_\n"
  },
  {
    "path": "test/mac/exception_swallower.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/mac/exception_swallower.h\"\n\n#include <errno.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include <string>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/check_op.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"handler/mac/exception_handler_server.h\"\n#include \"util/mach/bootstrap.h\"\n#include \"util/mach/exc_server_variants.h\"\n#include \"util/mach/exception_ports.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/random_string.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nconstexpr char kServiceEnvironmentVariable[] =\n    \"CRASHPAD_EXCEPTION_SWALLOWER_SERVICE\";\n\nExceptionSwallower* g_exception_swallower;\n\n// Like getenv(), but fails a CHECK() if the underlying function fails. It’s not\n// considered a failure for |name| to be unset in the environment. In that case,\n// nullptr is returned.\nconst char* CheckedGetenv(const char* name) {\n  errno = 0;\n  const char* value;\n  PCHECK((value = getenv(name)) || errno == 0) << \"getenv\";\n  return value;\n}\n\n}  // namespace\n\nclass ExceptionSwallower::ExceptionSwallowerThread\n    : public Thread,\n      public UniversalMachExcServer::Interface {\n public:\n  explicit ExceptionSwallowerThread(\n      base::apple::ScopedMachReceiveRight receive_right)\n      : Thread(),\n        UniversalMachExcServer::Interface(),\n        exception_handler_server_(std::move(receive_right), true),\n        pid_(getpid()) {\n    Start();\n  }\n\n  ExceptionSwallowerThread(const ExceptionSwallowerThread&) = delete;\n  ExceptionSwallowerThread& operator=(const ExceptionSwallowerThread&) = delete;\n\n  ~ExceptionSwallowerThread() override {}\n\n  void Stop() { exception_handler_server_.Stop(); }\n\n  // Returns the process ID that the thread is running in. This is used to\n  // detect misuses that place the exception swallower server thread and code\n  // that wants its exceptions swallowed in the same process.\n  pid_t ProcessID() const { return pid_; }\n\n private:\n  // Thread:\n\n  void ThreadMain() override { exception_handler_server_.Run(this); }\n\n  // UniversalMachExcServer::Interface:\n\n  kern_return_t CatchMachException(exception_behavior_t behavior,\n                                   exception_handler_t exception_port,\n                                   thread_t thread,\n                                   task_t task,\n                                   exception_type_t exception,\n                                   const mach_exception_data_type_t* code,\n                                   mach_msg_type_number_t code_count,\n                                   thread_state_flavor_t* flavor,\n                                   ConstThreadState old_state,\n                                   mach_msg_type_number_t old_state_count,\n                                   thread_state_t new_state,\n                                   mach_msg_type_number_t* new_state_count,\n                                   const mach_msg_trailer_t* trailer,\n                                   bool* destroy_complex_request) override {\n    *destroy_complex_request = true;\n\n    // Swallow.\n\n    ExcServerCopyState(\n        behavior, old_state, old_state_count, new_state, new_state_count);\n    return ExcServerSuccessfulReturnValue(exception, behavior, false);\n  }\n\n  ExceptionHandlerServer exception_handler_server_;\n  pid_t pid_;\n};\n\nExceptionSwallower::ExceptionSwallower() : exception_swallower_thread_() {\n  CHECK(!g_exception_swallower);\n  g_exception_swallower = this;\n\n  if (CheckedGetenv(kServiceEnvironmentVariable)) {\n    // The environment variable is already set, so just proceed with the\n    // existing service. This normally happens when the Google Test “threadsafe”\n    // death test style is chosen, because the test child process will\n    // re-execute code already run in the test parent process. See\n    // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-test-styles.\n    return;\n  }\n\n  std::string service_name =\n      base::StringPrintf(\"org.chromium.crashpad.test.exception_swallower.%d.%s\",\n                         getpid(),\n                         RandomString().c_str());\n  base::apple::ScopedMachReceiveRight receive_right(\n      BootstrapCheckIn(service_name));\n  CHECK(receive_right.is_valid());\n\n  exception_swallower_thread_.reset(\n      new ExceptionSwallowerThread(std::move(receive_right)));\n\n  PCHECK(setenv(kServiceEnvironmentVariable, service_name.c_str(), 1) == 0)\n      << \"setenv\";\n}\n\nExceptionSwallower::~ExceptionSwallower() {\n  PCHECK(unsetenv(kServiceEnvironmentVariable) == 0) << \"unsetenv\";\n\n  exception_swallower_thread_->Stop();\n  exception_swallower_thread_->Join();\n\n  CHECK_EQ(g_exception_swallower, this);\n  g_exception_swallower = nullptr;\n}\n\n// static\nvoid ExceptionSwallower::SwallowExceptions() {\n  // The exception swallower thread can’t be in this process, because the\n  // EXC_CRASH or EXC_CORPSE_NOTIFY exceptions that it needs to swallow will be\n  // delivered after a crash has occurred and none of its threads will be\n  // scheduled to run.\n  CHECK(!g_exception_swallower ||\n        !g_exception_swallower->exception_swallower_thread_ ||\n        g_exception_swallower->exception_swallower_thread_->ProcessID() !=\n            getpid());\n\n  const char* service_name = CheckedGetenv(kServiceEnvironmentVariable);\n  CHECK(service_name);\n\n  base::apple::ScopedMachSendRight exception_swallower_port(\n      BootstrapLookUp(service_name));\n  CHECK(exception_swallower_port.is_valid());\n\n  ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask,\n                                      TASK_NULL);\n\n  // The mask is similar to the one used by CrashpadClient::UseHandler(), but\n  // EXC_CORPSE_NOTIFY is added. This is done for the benefit of tests that\n  // crash intentionally with their own custom exception port set for EXC_CRASH.\n  // In that case, depending on the actions taken by the EXC_CRASH handler, the\n  // exception may be transformed by the kernel into an EXC_CORPSE_NOTIFY, which\n  // would be sent to an EXC_CORPSE_NOTIFY handler, normally the system’s crash\n  // reporter at the task or host level. See 10.13.0\n  // xnu-4570.1.46/bsd/kern/kern_exit.c proc_prepareexit(). Swallowing\n  // EXC_CORPSE_NOTIFY at the task level prevents such exceptions from reaching\n  // the system’s crash reporter.\n  CHECK(task_exception_ports.SetExceptionPort(\n      (EXC_MASK_CRASH |\n       EXC_MASK_RESOURCE |\n       EXC_MASK_GUARD |\n       EXC_MASK_CORPSE_NOTIFY) & ExcMaskValid(),\n      exception_swallower_port.get(),\n      EXCEPTION_DEFAULT,\n      THREAD_STATE_NONE));\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/mac/exception_swallower.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_\n#define CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_\n\n#include <memory>\n\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Swallows `EXC_CRASH` and `EXC_CORPSE_NOTIFY` exceptions in test child\n//!     processes.\n//!\n//! This class is intended to be used by test code that crashes intentionally.\n//!\n//! On macOS, the system’s crash reporter normally saves crash reports for all\n//! crashes in test code, by virtue of being set as the `EXC_CRASH` or\n//! `EXC_CORPSE_NOTIFY` handler. This litters the user’s\n//! `~/Library/Logs/DiagnosticReports` directory and can be time-consuming.\n//! Reports generated for code that crashes intentionally have no value, and\n//! many Crashpad tests do crash intentionally.\n//!\n//! Instantiate an ExceptionSwallower object in a parent test process (a process\n//! where `TEST()`, `TEST_F()`, and `TEST_P()` execute) to create an exception\n//! swallower server running on a dedicated thread. A service mapping for this\n//! server will be published with the bootstrap server and made available in the\n//! `CRASHPAD_EXCEPTION_SWALLOWER_SERVICE` environment variable. In a child\n//! process, call SwallowExceptions() to look up this service and set it as the\n//! `EXC_CRASH` and `EXC_CORPSE_NOTIFY` handler. When these exceptions are\n//! raised in the child process, they’ll be handled by the exception swallower\n//! server, which performs no action but reports that exceptions were\n//! successfully handled so that the system’s crash reporter, ReportCrash, will\n//! not be invoked.\n//!\n//! At most one ExceptionSwallower may be instantiated in a process at a time.\n//! If `CRASHPAD_EXCEPTION_SWALLOWER_SERVICE` is already set, ExceptionSwallower\n//! leaves it in place and takes no additional action.\n//!\n//! Crashpad’s ASSERT_DEATH_CRASH(), EXPECT_DEATH_CRASH(), ASSERT_DEATH_CHECK(),\n//! and EXPECT_DEATH_CHECK() macros make use of this class on macOS, as does the\n//! Multiprocess test interface.\nclass ExceptionSwallower {\n public:\n  ExceptionSwallower();\n\n  ExceptionSwallower(const ExceptionSwallower&) = delete;\n  ExceptionSwallower& operator=(const ExceptionSwallower&) = delete;\n\n  ~ExceptionSwallower();\n\n  //! \\brief In a test child process, arranges to swallow `EXC_CRASH` and\n  //!     `EXC_CORPSE_NOTIFY` exceptions.\n  //!\n  //! This must be called in a test child process. It must not be called from a\n  //! parent test process directly. Parent test processes are those that execute\n  //! `TEST()`, `TEST_F()`, and `TEST_P()`. Test child processes execute\n  //! ASSERT_DEATH_CRASH(), EXPECT_DEATH_CRASH(), ASSERT_DEATH_CHECK(),\n  //! EXPECT_DEATH_CHECK(), and Multiprocess::RunChild().\n  //!\n  //! It is an error to call this in a test child process without having first\n  //! instantiated an ExceptionSwallower object in a parent test project. It is\n  //! also an error to call this in a parent test process.\n  static void SwallowExceptions();\n\n private:\n  class ExceptionSwallowerThread;\n\n  std::unique_ptr<ExceptionSwallowerThread> exception_swallower_thread_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_\n"
  },
  {
    "path": "test/mac/mach_errors.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/mac/mach_errors.h\"\n\n#include \"base/strings/stringprintf.h\"\n\nnamespace {\n\nstd::string FormatBase(const std::string& base) {\n  if (base.empty()) {\n    return std::string();\n  }\n\n  return base::StringPrintf(\"%s: \", base.c_str());\n}\n\nstd::string FormatMachErrorNumber(mach_error_t mach_err) {\n  // For the os/kern subsystem, give the error number in decimal as in\n  // <mach/kern_return.h>. Otherwise, give it in hexadecimal to make it easier\n  // to visualize the various bits. See <mach/error.h>.\n  if (mach_err >= 0 && mach_err < KERN_RETURN_MAX) {\n    return base::StringPrintf(\" (%d)\", mach_err);\n  }\n  return base::StringPrintf(\" (0x%08x)\", mach_err);\n}\n\n}  // namespace\n\nnamespace crashpad {\nnamespace test {\n\nstd::string MachErrorMessage(mach_error_t mach_err, const std::string& base) {\n  return base::StringPrintf(\"%s%s%s\",\n                            FormatBase(base).c_str(),\n                            mach_error_string(mach_err),\n                            FormatMachErrorNumber(mach_err).c_str());\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/mac/mach_errors.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_MAC_MACH_ERRORS_H_\n#define CRASHPAD_TEST_MAC_MACH_ERRORS_H_\n\n#include <mach/mach.h>\n\n#include <string>\n\nnamespace crashpad {\nnamespace test {\n\n// This function formats messages in a similar way to the Mach error logging\n// macros in base/apple/mach_logging.h. It exists to interoperate with Google\n// Test assertions, which don’t interoperate with logging but can be streamed\n// to.\n//\n// Where non-test code could do:\n//   MACH_CHECK(kr == KERN_SUCCESS, kr) << \"vm_deallocate\";\n// Google Test-based test code can do:\n//   EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_deallocate\");\n\n//! \\brief Formats a Mach error message.\n//!\n//! The returned string will combine the \\a base string, if supplied, with a\n//! textual and numeric description of the error.\n//!\n//! \\param[in] mach_err The Mach error code, which may be a `kern_return_t` or\n//!     related type.\n//! \\param[in] base A string to prepend to the error description.\n//!\n//! \\return A string of the format `\"(os/kern) invalid address (1)\"` if \\a\n//!     mach_err has the value `KERN_INVALID_ADDRESS` on a system where this is\n//!     defined to be 1. If \\a base is not empty, it will be prepended to this\n//!     string, separated by a colon.\nstd::string MachErrorMessage(mach_error_t mach_err,\n                             const std::string& base = std::string());\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_MAC_MACH_ERRORS_H_\n"
  },
  {
    "path": "test/mac/mach_multiprocess.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/mac/mach_multiprocess.h\"\n\n#include <Availability.h>\n#include <bsm/libbsm.h>\n\n#include <memory>\n#include <string>\n#include <tuple>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/auto_reset.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"util/file/file_io.h\"\n#include \"util/mach/bootstrap.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/misc/random_string.h\"\n#include \"util/misc/scoped_forbid_return.h\"\n\nnamespace {\n\n// The “hello” message contains a send right to the child process’ task port.\nstruct SendHelloMessage : public mach_msg_base_t {\n  mach_msg_port_descriptor_t port_descriptor;\n};\n\nstruct ReceiveHelloMessage : public SendHelloMessage {\n  union {\n    mach_msg_trailer_t trailer;\n    mach_msg_audit_trailer_t audit_trailer;\n  };\n};\n\n}  // namespace\n\nnamespace crashpad {\nnamespace test {\n\nnamespace internal {\n\nstruct MachMultiprocessInfo {\n  MachMultiprocessInfo()\n      : service_name(),\n        local_port(MACH_PORT_NULL),\n        remote_port(MACH_PORT_NULL),\n        child_task(TASK_NULL) {\n  }\n\n  std::string service_name;\n  base::apple::ScopedMachReceiveRight local_port;\n  base::apple::ScopedMachSendRight remote_port;\n  base::apple::ScopedMachSendRight child_task;  // valid only in parent\n};\n\n}  // namespace internal\n\nMachMultiprocess::MachMultiprocess() : Multiprocess(), info_(nullptr) {\n}\n\nvoid MachMultiprocess::Run() {\n  ASSERT_EQ(info_, nullptr);\n  std::unique_ptr<internal::MachMultiprocessInfo> info(\n      new internal::MachMultiprocessInfo);\n  base::AutoReset<internal::MachMultiprocessInfo*> reset_info(&info_,\n                                                              info.get());\n\n  return Multiprocess::Run();\n}\n\nMachMultiprocess::~MachMultiprocess() {\n}\n\nvoid MachMultiprocess::PreFork() {\n  ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork());\n\n  // Set up the parent port and register it with the bootstrap server before\n  // forking, so that it’s guaranteed to be there when the child attempts to\n  // look it up.\n  info_->service_name = \"org.chromium.crashpad.test.mach_multiprocess.\";\n  info_->service_name.append(RandomString());\n\n  info_->local_port = BootstrapCheckIn(info_->service_name);\n  ASSERT_TRUE(info_->local_port.is_valid());\n}\n\nmach_port_t MachMultiprocess::LocalPort() const {\n  EXPECT_TRUE(info_->local_port.is_valid());\n  return info_->local_port.get();\n}\n\nmach_port_t MachMultiprocess::RemotePort() const {\n  EXPECT_TRUE(info_->remote_port.is_valid());\n  return info_->remote_port.get();\n}\n\ntask_t MachMultiprocess::ChildTask() const {\n  EXPECT_TRUE(info_->child_task.is_valid());\n  return info_->child_task.get();\n}\n\nvoid MachMultiprocess::MultiprocessParent() {\n  ReceiveHelloMessage message = {};\n\n  kern_return_t kr = mach_msg(&message.header,\n                              MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,\n                              0,\n                              sizeof(message),\n                              info_->local_port.get(),\n                              MACH_MSG_TIMEOUT_NONE,\n                              MACH_PORT_NULL);\n  ASSERT_EQ(kr, MACH_MSG_SUCCESS) << MachErrorMessage(kr, \"mach_msg\");\n\n  // Comb through the entire message, checking every field against its expected\n  // value.\n  EXPECT_EQ(message.header.msgh_bits,\n            MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) |\n                MACH_MSGH_BITS_COMPLEX);\n  ASSERT_EQ(message.header.msgh_size, sizeof(SendHelloMessage));\n  EXPECT_EQ(message.header.msgh_local_port, info_->local_port);\n  ASSERT_EQ(message.body.msgh_descriptor_count, 1u);\n  EXPECT_EQ(message.port_descriptor.disposition,\n            implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND));\n  ASSERT_EQ(\n      message.port_descriptor.type,\n      implicit_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR));\n  ASSERT_EQ(message.audit_trailer.msgh_trailer_type,\n            implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0));\n  ASSERT_EQ(message.audit_trailer.msgh_trailer_size,\n            sizeof(message.audit_trailer));\n  EXPECT_EQ(message.audit_trailer.msgh_seqno, 0u);\n\n  // Check the audit trailer’s values for sanity. This is a little bit of\n  // overkill, but because the service was registered with the bootstrap server\n  // and other processes will be able to look it up and send messages to it,\n  // these checks disambiguate genuine failures later on in the test from those\n  // that would occur if an errant process sends a message to this service.\n#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8\n  uid_t audit_auid;\n  uid_t audit_euid;\n  gid_t audit_egid;\n  uid_t audit_ruid;\n  gid_t audit_rgid;\n  pid_t audit_pid;\n  au_asid_t audit_asid;\n  audit_token_to_au32(message.audit_trailer.msgh_audit,\n                      &audit_auid,\n                      &audit_euid,\n                      &audit_egid,\n                      &audit_ruid,\n                      &audit_rgid,\n                      &audit_pid,\n                      &audit_asid,\n                      nullptr);\n#else\n  uid_t audit_auid = audit_token_to_auid(message.audit_trailer.msgh_audit);\n  uid_t audit_euid = audit_token_to_euid(message.audit_trailer.msgh_audit);\n  gid_t audit_egid = audit_token_to_egid(message.audit_trailer.msgh_audit);\n  uid_t audit_ruid = audit_token_to_ruid(message.audit_trailer.msgh_audit);\n  gid_t audit_rgid = audit_token_to_rgid(message.audit_trailer.msgh_audit);\n  pid_t audit_pid = audit_token_to_pid(message.audit_trailer.msgh_audit);\n  au_asid_t audit_asid = audit_token_to_asid(message.audit_trailer.msgh_audit);\n#endif\n  EXPECT_EQ(audit_euid, geteuid());\n  EXPECT_EQ(audit_egid, getegid());\n  EXPECT_EQ(audit_ruid, getuid());\n  EXPECT_EQ(audit_rgid, getgid());\n  ASSERT_EQ(audit_pid, ChildPID());\n\n  ASSERT_EQ(AuditPIDFromMachMessageTrailer(&message.trailer), ChildPID());\n\n  auditinfo_addr_t audit_info;\n  int rv = getaudit_addr(&audit_info, sizeof(audit_info));\n  ASSERT_EQ(rv, 0) << ErrnoMessage(\"getaudit_addr\");\n  EXPECT_EQ(audit_auid, audit_info.ai_auid);\n  EXPECT_EQ(audit_asid, audit_info.ai_asid);\n\n  // Retrieve the remote port from the message header, and the child’s task port\n  // from the message body.\n  info_->remote_port.reset(message.header.msgh_remote_port);\n  info_->child_task.reset(message.port_descriptor.name);\n\n  // Verify that the child’s task port is what it purports to be.\n  int mach_pid;\n  kr = pid_for_task(info_->child_task.get(), &mach_pid);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"pid_for_task\");\n  ASSERT_EQ(mach_pid, ChildPID());\n\n  MachMultiprocessParent();\n\n  info_->remote_port.reset();\n  info_->local_port.reset();\n}\n\nvoid MachMultiprocess::MultiprocessChild() {\n  ScopedForbidReturn forbid_return;\n\n  // local_port is not valid in the forked child process.\n  std::ignore = info_->local_port.release();\n\n  info_->local_port.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_NE(info_->local_port, kMachPortNull);\n\n  // The remote port can be obtained from the bootstrap server.\n  info_->remote_port = BootstrapLookUp(info_->service_name);\n  ASSERT_NE(info_->remote_port, kMachPortNull);\n\n  // The “hello” message will provide the parent with its remote port, a send\n  // right to the child task’s local port receive right. It will also carry a\n  // send right to the child task’s task port.\n  SendHelloMessage message = {};\n  message.header.msgh_bits =\n      MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |\n      MACH_MSGH_BITS_COMPLEX;\n  message.header.msgh_size = sizeof(message);\n  message.header.msgh_remote_port = info_->remote_port.get();\n  message.header.msgh_local_port = info_->local_port.get();\n  message.body.msgh_descriptor_count = 1;\n  message.port_descriptor.name = mach_task_self();\n  message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;\n  message.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;\n\n  kern_return_t kr = mach_msg(&message.header,\n                              MACH_SEND_MSG,\n                              message.header.msgh_size,\n                              0,\n                              MACH_PORT_NULL,\n                              MACH_MSG_TIMEOUT_NONE,\n                              MACH_PORT_NULL);\n  ASSERT_EQ(kr, MACH_MSG_SUCCESS) << MachErrorMessage(kr, \"mach_msg\");\n\n  MachMultiprocessChild();\n\n  info_->remote_port.reset();\n  info_->local_port.reset();\n\n  // Close the write pipe now, for cases where the parent is waiting on it to\n  // be closed as an indication that the child has finished.\n  CloseWritePipe();\n\n  // Wait for the parent process to close its end of the pipe. The child process\n  // needs to remain alive until then because the parent process will attempt to\n  // verify it using the task port it has access to via ChildTask().\n  CheckedReadFileAtEOF(ReadPipeHandle());\n\n  if (testing::Test::HasFailure()) {\n    // Trigger the ScopedForbidReturn destructor.\n    return;\n  }\n\n  forbid_return.Disarm();\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/mac/mach_multiprocess.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_\n#define CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_\n\n#include <mach/mach.h>\n#include <unistd.h>\n\n#include \"test/multiprocess.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace internal {\nstruct MachMultiprocessInfo;\n}  // namespace internal\n\n//! \\brief Manages a Mach-aware multiprocess test.\n//!\n//! This is similar to the base Multiprocess test, but adds Mach features. The\n//! parent process has access to the child process’ task port. The parent and\n//! child processes are able to communicate via Mach IPC: each process has a\n//! receive right to its “local port” and a send right to a “remote port”, and\n//! messages sent to the remote port in one process can be received on the local\n//! port in the partner process.\n//!\n//! Subclasses are expected to implement the parent and child by overriding the\n//! appropriate methods.\nclass MachMultiprocess : public Multiprocess {\n public:\n  MachMultiprocess();\n\n  MachMultiprocess(const MachMultiprocess&) = delete;\n  MachMultiprocess& operator=(const MachMultiprocess&) = delete;\n\n  void Run();\n\n protected:\n  ~MachMultiprocess();\n\n  // Multiprocess:\n  void PreFork() override;\n\n  //! \\brief Returns a receive right for the local port.\n  //!\n  //! This method may be called by either the parent or the child process. It\n  //! returns a receive right, with a corresponding send right held in the\n  //! opposing process.\n  mach_port_t LocalPort() const;\n\n  //! \\brief Returns a send right for the remote port.\n  //!\n  //! This method may be called by either the parent or the child process. It\n  //! returns a send right, with the corresponding receive right held in the\n  //! opposing process.\n  mach_port_t RemotePort() const;\n\n  //! \\brief Returns a send right for the child’s task port.\n  //!\n  //! This method may only be called by the parent process.\n  task_t ChildTask() const;\n\n private:\n  // Multiprocess:\n\n  //! \\brief Runs the parent side of the test.\n  //!\n  //! This method establishes the parent’s environment and calls\n  //! MachMultiprocessParent().\n  //!\n  //! Subclasses must override MachMultiprocessParent() instead of this method.\n  void MultiprocessParent() final;\n\n  //! \\brief Runs the child side of the test.\n  //!\n  //! This method establishes the child’s environment and calls\n  //! MachMultiprocessChild(). If any failure (via fatal or nonfatal Google Test\n  //! assertion) is detected, the child will exit with a failure status.\n  //!\n  //! Subclasses must override MachMultiprocessChild() instead of this method.\n  void MultiprocessChild() final;\n\n  //! \\brief The subclass-provided parent routine.\n  //!\n  //! Test failures should be reported via Google Test: `EXPECT_*()`,\n  //! `ASSERT_*()`, `FAIL()`, etc.\n  //!\n  //! This method must not use a `wait()`-family system call to wait for the\n  //! child process to exit, as this is handled by the superclass.\n  //!\n  //! Subclasses must implement this method to define how the parent operates.\n  virtual void MachMultiprocessParent() = 0;\n\n  //! \\brief The subclass-provided child routine.\n  //!\n  //! Test failures should be reported via Google Test: `EXPECT_*()`,\n  //! `ASSERT_*()`, `FAIL()`, etc.\n  //!\n  //! Subclasses must implement this method to define how the child operates.\n  virtual void MachMultiprocessChild() = 0;\n\n  internal::MachMultiprocessInfo* info_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_\n"
  },
  {
    "path": "test/mac/mach_multiprocess_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/mac/mach_multiprocess.h\"\n\n#include <unistd.h>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass TestMachMultiprocess final : public MachMultiprocess {\n public:\n  TestMachMultiprocess() : MachMultiprocess() {}\n\n  TestMachMultiprocess(const TestMachMultiprocess&) = delete;\n  TestMachMultiprocess& operator=(const TestMachMultiprocess&) = delete;\n\n  ~TestMachMultiprocess() {}\n\n private:\n  // MachMultiprocess will have already exercised the Mach ports for IPC and the\n  // child task port.\n  void MachMultiprocessParent() override {}\n\n  void MachMultiprocessChild() override {}\n};\n\nTEST(MachMultiprocess, MachMultiprocess) {\n  TestMachMultiprocess mach_multiprocess;\n  mach_multiprocess.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/main_arguments.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/main_arguments.h\"\n\n#include \"base/check.h\"\n\nnamespace crashpad {\nnamespace test {\n\nconst std::vector<std::string>* g_arguments;\n\nvoid InitializeMainArguments(int argc, char* argv[]) {\n  CHECK(!g_arguments);\n  CHECK(argc);\n  CHECK(argv);\n\n  g_arguments = new const std::vector<std::string>(argv, argv + argc);\n}\n\nconst std::vector<std::string>& GetMainArguments() {\n  CHECK(g_arguments);\n  return *g_arguments;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/main_arguments.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_MAIN_ARGUMENTS_H_\n#define CRASHPAD_TEST_MAIN_ARGUMENTS_H_\n\n#include <string>\n#include <vector>\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Saves the arguments to `main()` for later use.\n//!\n//! Call this function from a test program’s `main()` function so that tests\n//! that require access to these variables can retrieve them from\n//! GetMainArguments().\n//!\n//! The contents of \\a argv, limited to \\a argc elements, will be copied, so\n//! that subsequent modifications to these variables by `main()` will not affect\n//! the state returned by GetMainArguments().\n//!\n//! This function must be called exactly once during the lifetime of a test\n//! program.\nvoid InitializeMainArguments(int argc, char* argv[]);\n\n//! \\brief Retrieves pointers to the arguments to `main()`.\n//!\n//! Tests that need to access the original values of a test program’s `main()`\n//! function’s parameters at process creation can use this function to retrieve\n//! them, provided that `main()` called InitializeMainArguments() before making\n//! any changes to its arguments.\nconst std::vector<std::string>& GetMainArguments();\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_MAIN_ARGUMENTS_H_\n"
  },
  {
    "path": "test/main_arguments_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/main_arguments.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(MainArguments, GetMainArguments) {\n  // Make sure that InitializeMainArguments() has been called and that\n  // GetMainArguments() provides reasonable values.\n  const std::vector<std::string>& arguments = GetMainArguments();\n\n  ASSERT_FALSE(arguments.empty());\n  EXPECT_FALSE(arguments[0].empty());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/multiprocess.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_MULTIPROCESS_H_\n#define CRASHPAD_TEST_MULTIPROCESS_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include \"build/build_config.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace internal {\nstruct MultiprocessInfo;\n}  // namespace internal\n\n#if BUILDFLAG(IS_FUCHSIA)\nusing ReturnCodeType = int64_t;\n#else\nusing ReturnCodeType = int;\n#endif\n\n//! \\brief Manages a multiprocess test.\n//!\n//! These tests are `fork()`-based. The parent and child processes are able to\n//! communicate via a pair of POSIX pipes.\n//!\n//! Subclasses are expected to implement the parent and child by overriding the\n//! appropriate methods.\n//!\n//! On Windows and Fuchsia, this class is only an internal implementation\n//! detail of MultiprocessExec and all tests must use that class.\nclass Multiprocess {\n public:\n  //! \\brief The termination type for a child process.\n  enum TerminationReason : bool {\n    //! \\brief The child terminated normally.\n    //!\n    //! A normal return happens when a test returns from RunChild(), or for\n    //! tests that `exec()`, returns from `main()`. This also happens for tests\n    //! that call `exit()` or `_exit()`.\n    kTerminationNormal = false,\n\n#if !BUILDFLAG(IS_FUCHSIA)  // There are no signals on Fuchsia.\n    //! \\brief The child terminated by signal.\n    //!\n    //! Signal termination happens as a result of a crash, a call to `abort()`,\n    //! assertion failure (including Google Test assertions), etc.\n    kTerminationSignal,\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n  };\n\n  Multiprocess();\n\n  Multiprocess(const Multiprocess&) = delete;\n  Multiprocess& operator=(const Multiprocess&) = delete;\n\n  //! \\brief Runs the test.\n  //!\n  //! This method establishes the proper testing environment by calling\n  //! PreFork(), then calls `fork()`. In the parent process, it calls\n  //! RunParent(), and in the child process, it calls RunChild().\n  //!\n  //! This method uses Google Test assertions to validate the testing\n  //! environment. If the testing environment cannot be set up properly, it is\n  //! possible that MultiprocessParent() or MultiprocessChild() will not be\n  //! called. In the parent process, this method also waits for the child\n  //! process to exit after MultiprocessParent() returns, and verifies that it\n  //! exited in accordance with the expectations set by\n  //! SetExpectedChildTermination().\n  void Run();\n\n  //! \\brief Sets the expected termination reason and code.\n  //!\n  //! The default expected termination reason is\n  //! TerminationReason::kTerminationNormal, and the default expected\n  //! termination code is `EXIT_SUCCESS` (`0`).\n  //!\n  //! This method does not need to be called if the default termination\n  //! expectation is appropriate, but if this method is called, it must be\n  //! called before Run().\n  //!\n  //! \\param[in] reason Whether to expect the child to terminate normally or\n  //!     as a result of a signal.\n  //! \\param[in] code If \\a reason is TerminationReason::kTerminationNormal,\n  //!     this is the expected exit status of the child. If \\a reason is\n  //!     TerminationReason::kTerminationSignal, this is the signal that is\n  //!     expected to kill the child. On Linux platforms, SIG_DFL will be\n  //!     installed for \\a code in the child process.\n  void SetExpectedChildTermination(TerminationReason reason,\n                                   ReturnCodeType code);\n\n#if !BUILDFLAG(IS_WIN)\n  //! \\brief Sets termination reason and code appropriately for a child that\n  //!     terminates via `__builtin_trap()`.\n  void SetExpectedChildTerminationBuiltinTrap();\n#endif  // !BUILDFLAG(IS_WIN)\n\n protected:\n  ~Multiprocess();\n\n  //! \\brief Establishes the proper testing environment prior to forking.\n  //!\n  //! Subclasses that solely implement a test should not need to override this\n  //! method. Subclasses that do not implement tests but instead implement\n  //! additional testing features on top of this class may override this method\n  //! provided that they call the superclass’ implementation first as follows:\n  //!\n  //! \\code\n  //!   void PreFork() override {\n  //!     ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork());\n  //!\n  //!     // Place subclass-specific pre-fork code here.\n  //!   }\n  //! \\endcode\n  //!\n  //! Subclass implementations may signal failure by raising their own fatal\n  //! Google Test assertions.\n  virtual void PreFork()\n#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)\n      = 0\n#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA)\n      ;\n\n#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA)\n  //! \\brief Returns the child process’ process ID.\n  //!\n  //! This method may only be called by the parent process.\n  pid_t ChildPID() const;\n#endif  // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA)\n\n  //! \\brief Returns the read pipe’s file handle.\n  //!\n  //! This method may be called by either the parent or the child process.\n  //! Anything written to the write pipe in the partner process will appear\n  //! on this file handle in this process.\n  //!\n  //! It is an error to call this after CloseReadPipe() has been called.\n  //!\n  //! \\return The read pipe’s file handle.\n  FileHandle ReadPipeHandle() const;\n\n  //! \\brief Returns the write pipe’s file handle.\n  //!\n  //! This method may be called by either the parent or the child process.\n  //! Anything written to this file handle in this process will appear on\n  //! the read pipe in the partner process.\n  //!\n  //! It is an error to call this after CloseWritePipe() has been called.\n  //!\n  //! \\return The write pipe’s file handle.\n  FileHandle WritePipeHandle() const;\n\n  //! \\brief Closes the read pipe.\n  //!\n  //! This method may be called by either the parent or the child process. An\n  //! attempt to write to the write pipe in the partner process will fail with\n  //! `EPIPE` or `SIGPIPE`. ReadPipeHandle() must not be called after this.\n  void CloseReadPipe();\n\n  //! \\brief Closes the write pipe.\n  //!\n  //! This method may be called by either the parent or the child process. An\n  //! attempt to read from the read pipe in the partner process will indicate\n  //! end-of-file. WritePipeHandle() must not be called after this.\n  void CloseWritePipe();\n\n  void set_info(internal::MultiprocessInfo* info) { info_ = info; }\n  internal::MultiprocessInfo* info() { return info_; }\n\n private:\n  //! \\brief Runs the parent side of the test.\n  //!\n  //! This method establishes the parent’s environment and calls\n  //! MultiprocessParent().\n  void RunParent();\n\n  //! \\brief Runs the child side of the test.\n  //!\n  //! This method establishes the child’s environment, calls\n  //! MultiprocessChild(), and exits cleanly by calling `_exit(0)`. However, if\n  //! any failure (via fatal or nonfatal Google Test assertion) is detected, the\n  //! child will exit with a failure status.\n  void RunChild();\n\n  //! \\brief The subclass-provided parent routine.\n  //!\n  //! Test failures should be reported via Google Test: `EXPECT_*()`,\n  //! `ASSERT_*()`, `FAIL()`, etc.\n  //!\n  //! This method must not use a `wait()`-family system call to wait for the\n  //! child process to exit, as this is handled by this class.\n  //!\n  //! Subclasses must implement this method to define how the parent operates.\n  virtual void MultiprocessParent() = 0;\n\n  //! \\brief The subclass-provided child routine.\n  //!\n  //! Test failures should be reported via Google Test: `EXPECT_*()`,\n  //! `ASSERT_*()`, `FAIL()`, etc.\n  //!\n  //! Subclasses must implement this method to define how the child operates.\n  //! Subclasses may exit with a failure status by using `LOG(FATAL)`,\n  //! `abort()`, or similar. They may exit cleanly by returning from this method\n  //! or by calling `_exit(0)`. Under no circumstances may `exit()` be called\n  //! by the child without having the child process `exec()`. Use\n  //! MultiprocessExec if the child should call `exec()`.\n  virtual void MultiprocessChild() = 0;\n\n  internal::MultiprocessInfo* info_;\n  ReturnCodeType code_;\n  TerminationReason reason_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_MULTIPROCESS_H_\n"
  },
  {
    "path": "test/multiprocess_exec.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/multiprocess_exec.h\"\n\n#include <map>\n\n#include \"base/check.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"test/main_arguments.h\"\n#include \"test/test_paths.h\"\n#include \"util/stdlib/map_insert.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace internal {\n\nnamespace {\n\nstd::map<std::string, int(*)()>* GetMultiprocessFunctionMap() {\n  static auto* map = new std::map<std::string, int(*)()>();\n  return map;\n}\n\n}  // namespace\n\nAppendMultiprocessTest::AppendMultiprocessTest(const std::string& test_name,\n                                               int (*main_function_pointer)()) {\n  CHECK(MapInsertOrReplace(\n      GetMultiprocessFunctionMap(), test_name, main_function_pointer, nullptr))\n      << test_name << \" already registered\";\n}\n\nint CheckedInvokeMultiprocessChild(const std::string& test_name) {\n  const auto* functions = internal::GetMultiprocessFunctionMap();\n  auto it = functions->find(test_name);\n  CHECK(it != functions->end())\n      << \"child main \" << test_name << \" not registered\";\n  return (*it->second)();\n}\n\n}  // namespace internal\n\nvoid MultiprocessExec::SetChildTestMainFunction(\n    const std::string& function_name) {\n  std::vector<std::string> rest(GetMainArguments().begin() + 1,\n                                GetMainArguments().end());\n  rest.push_back(internal::kChildTestFunction + function_name);\n\n#if BUILDFLAG(IS_WIN)\n  // Instead of using argv[0] on Windows, use the actual binary name. This is\n  // necessary because if originally the test isn't run with \".exe\" on the\n  // command line, then argv[0] also won't include \".exe\". This argument is used\n  // as the lpApplicationName argument to CreateProcess(), and so will fail.\n  SetChildCommand(TestPaths::Executable(), &rest);\n#else\n  SetChildCommand(base::FilePath(GetMainArguments()[0]), &rest);\n#endif\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/multiprocess_exec.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_MULTIPROCESS_EXEC_H_\n#define CRASHPAD_TEST_MULTIPROCESS_EXEC_H_\n\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"test/multiprocess.h\"\n#include \"test/process_type.h\"\n\n//! \\file\n\nnamespace crashpad {\nnamespace test {\n\nnamespace internal {\n\n//! \\brief Command line argument used to indicate that a child test function\n//!     should be run.\nconstexpr char kChildTestFunction[] = \"--child-test-function=\";\n\n\n//! \\brief Helper class used by CRASHPAD_CHILD_TEST_MAIN() to insert a child\n//!     function into the global mapping.\nclass AppendMultiprocessTest {\n public:\n  AppendMultiprocessTest(const std::string& test_name,\n                         int (*main_function_pointer)());\n};\n\n//! \\brief Used to run a child test function by name, registered by\n//!     CRASHPAD_CHILD_TEST_MAIN().\n//!\n//! \\return The exit code of the child process after running the function named\n//!     by \\a test_name. Aborts with a CHECK() if \\a test_name wasn't\n//!     registered.\nint CheckedInvokeMultiprocessChild(const std::string& test_name);\n\n}  // namespace internal\n\n//! \\brief Registers a function that can be invoked as a child process by\n//!     MultiprocessExec.\n//!\n//! Used as:\n//!\n//! \\code\n//! CRASHPAD_CHILD_TEST_MAIN(MyChildTestBody) {\n//!    ... child body ...\n//! }\n//! \\endcode\n//!\n//! In the main (parent) test body, this function can be run in a child process\n//! via MultiprocessExec::SetChildTestMainFunction().\n#define CRASHPAD_CHILD_TEST_MAIN(test_main)                       \\\n  int test_main();                                                \\\n  namespace {                                                     \\\n  ::crashpad::test::internal::AppendMultiprocessTest              \\\n      AddMultiprocessTest##_##test_main(#test_main, (test_main)); \\\n  } /* namespace */                                               \\\n  int test_main()\n\n//! \\brief Manages an `exec()`-based multiprocess test.\n//!\n//! These tests are based on `fork()` and `exec()`. The parent process is able\n//! to communicate with the child in the same manner as a base-class\n//! Multiprocess parent. The read and write pipes appear in the child process on\n//! stdin and stdout, respectively.\n//!\n//! Subclasses are expected to implement the parent in the same was as a\n//! base-class Multiprocess parent. The child must be implemented in an\n//! executable to be set by SetChildCommand().\nclass MultiprocessExec : public Multiprocess {\n public:\n  MultiprocessExec();\n\n  MultiprocessExec(const MultiprocessExec&) = delete;\n  MultiprocessExec& operator=(const MultiprocessExec&) = delete;\n\n  //! \\brief Sets the command to `exec()` in the child.\n  //!\n  //! This method must be called before the test can be Run().\n  //!\n  //! This method is useful when a custom executable is required for the child\n  //! binary, however, SetChildTestMainFunction() should generally be preferred.\n  //!\n  //! \\param[in] command The executable’s pathname.\n  //! \\param[in] arguments The command-line arguments to pass to the child\n  //!     process in its `argv[]` vector. This vector must begin at `argv[1]`,\n  //!     as \\a command is implicitly used as `argv[0]`. This argument may be\n  //!     `nullptr` if no command-line arguments are to be passed.\n  //!\n  //! \\sa SetChildTestMainFunction\n  void SetChildCommand(const base::FilePath& command,\n                       const std::vector<std::string>* arguments);\n\n  //! \\brief Calls SetChildCommand() to run a child test main function\n  //!     registered with CRASHPAD_CHILD_TEST_MAIN().\n  //!\n  //! This uses the same launch mechanism as SetChildCommand(), but coordinates\n  //! with test/gtest_main.cc to allow for simple registration of a child\n  //! processes' entry point via the helper macro, rather than needing to create\n  //! a separate build target.\n  //!\n  //! \\param[in] function_name The name of the function as passed to\n  //!     CRASHPAD_CHILD_TEST_MAIN().\n  void SetChildTestMainFunction(const std::string& function_name);\n\n  //! \\brief Returns a ProcessType representing the child process.\n  //!\n  //! This method is only valid during the body of MultiprocessParent().\n  //!\n  //! \\return A platform-specific type representing the child process. This\n  //!     method can fail on macOS because access to a child's task port\n  //!     requires the task_for_pid entitlement.\n  ProcessType ChildProcess();\n\n protected:\n  ~MultiprocessExec();\n\n  // Multiprocess:\n  void PreFork() override;\n\n private:\n  // Multiprocess:\n  void MultiprocessChild() override;\n\n  base::FilePath command_;\n  std::vector<std::string> arguments_;\n#if BUILDFLAG(IS_POSIX)\n  std::vector<const char*> argv_;\n#elif BUILDFLAG(IS_WIN)\n  std::wstring command_line_;\n#endif  // BUILDFLAG(IS_POSIX)\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_MULTIPROCESS_EXEC_H_\n"
  },
  {
    "path": "test/multiprocess_exec_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/multiprocess_exec.h\"\n\n#include <lib/fdio/io.h>\n#include <lib/fdio/spawn.h>\n#include <lib/zx/process.h>\n#include <lib/zx/time.h>\n#include <zircon/processargs.h>\n\n#include \"base/files/scoped_file.h\"\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nvoid AddPipe(fdio_spawn_action_t* action, int target_fd, int* fd_out) {\n  zx_handle_t handle = ZX_HANDLE_INVALID;\n  zx_status_t status = fdio_pipe_half(fd_out, &handle);\n  ZX_CHECK(status == ZX_OK, status) << \"fdio_pipe_half\";\n  action->action = FDIO_SPAWN_ACTION_ADD_HANDLE;\n  action->h.id = PA_HND(PA_FD, target_fd);\n  action->h.handle = handle;\n}\n\n}  // namespace\n\nnamespace internal {\n\nstruct MultiprocessInfo {\n  MultiprocessInfo() {}\n  base::ScopedFD stdin_write;\n  base::ScopedFD stdout_read;\n  zx::process child;\n};\n\n}  // namespace internal\n\nMultiprocess::Multiprocess()\n    : info_(nullptr), code_(EXIT_SUCCESS), reason_(kTerminationNormal) {}\n\nvoid Multiprocess::Run() {\n  // Set up and spawn the child process.\n  ASSERT_NO_FATAL_FAILURE(PreFork());\n  RunChild();\n\n  // And then run the parent actions in this process.\n  RunParent();\n\n  // Wait until the child exits.\n  zx_signals_t signals;\n  ASSERT_EQ(\n      info_->child.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), &signals),\n      ZX_OK);\n  ASSERT_EQ(signals, ZX_TASK_TERMINATED);\n\n  // Get the child's exit code.\n  zx_info_process_t proc_info;\n  zx_status_t status = info_->child.get_info(\n      ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_object_get_info\";\n    ADD_FAILURE() << \"Unable to get exit code of child\";\n  } else {\n    if (code_ != proc_info.return_code) {\n      ADD_FAILURE() << \"Child exited with code \" << proc_info.return_code\n                    << \", expected exit with code \" << code_;\n    }\n  }\n}\n\nvoid Multiprocess::SetExpectedChildTermination(TerminationReason reason,\n                                               ReturnCodeType code) {\n  EXPECT_EQ(info_, nullptr)\n      << \"SetExpectedChildTermination() must be called before Run()\";\n  reason_ = reason;\n  code_ = code;\n}\n\nvoid Multiprocess::SetExpectedChildTerminationBuiltinTrap() {\n  constexpr ReturnCodeType kExpectedReturnCode = ZX_TASK_RETCODE_EXCEPTION_KILL;\n  SetExpectedChildTermination(kTerminationNormal, kExpectedReturnCode);\n}\n\nMultiprocess::~Multiprocess() {\n  delete info_;\n}\n\nFileHandle Multiprocess::ReadPipeHandle() const {\n  return info_->stdout_read.get();\n}\n\nFileHandle Multiprocess::WritePipeHandle() const {\n  return info_->stdin_write.get();\n}\n\nvoid Multiprocess::CloseReadPipe() {\n  info_->stdout_read.reset();\n}\n\nvoid Multiprocess::CloseWritePipe() {\n  info_->stdin_write.reset();\n}\n\nvoid Multiprocess::RunParent() {\n  MultiprocessParent();\n\n  info_->stdout_read.reset();\n  info_->stdin_write.reset();\n}\n\nvoid Multiprocess::RunChild() {\n  MultiprocessChild();\n}\n\nMultiprocessExec::MultiprocessExec()\n    : Multiprocess(), command_(), arguments_(), argv_() {}\n\nvoid MultiprocessExec::SetChildCommand(\n    const base::FilePath& command,\n    const std::vector<std::string>* arguments) {\n  command_ = command;\n  if (arguments) {\n    arguments_ = *arguments;\n  } else {\n    arguments_.clear();\n  }\n}\n\nMultiprocessExec::~MultiprocessExec() {}\n\nvoid MultiprocessExec::PreFork() {\n  ASSERT_FALSE(command_.empty());\n\n  ASSERT_TRUE(argv_.empty());\n\n  argv_.push_back(command_.value().c_str());\n  for (const std::string& argument : arguments_) {\n    argv_.push_back(argument.c_str());\n  }\n  argv_.push_back(nullptr);\n\n  ASSERT_EQ(info(), nullptr);\n  set_info(new internal::MultiprocessInfo());\n}\n\nvoid MultiprocessExec::MultiprocessChild() {\n  constexpr size_t kActionCount = 3;\n  fdio_spawn_action_t actions[kActionCount];\n\n  int stdin_parent_side = -1;\n  AddPipe(&actions[0], STDIN_FILENO, &stdin_parent_side);\n  info()->stdin_write.reset(stdin_parent_side);\n\n  int stdout_parent_side = -1;\n  AddPipe(&actions[1], STDOUT_FILENO, &stdout_parent_side);\n  info()->stdout_read.reset(stdout_parent_side);\n\n  actions[2].action = FDIO_SPAWN_ACTION_CLONE_FD;\n  actions[2].fd.local_fd = STDERR_FILENO;\n  actions[2].fd.target_fd = STDERR_FILENO;\n\n  // Pass the filesystem namespace, parent environment, and default job to the\n  // child, but don't include any other file handles, preferring to set them\n  // up explicitly below.\n  uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_STDIO;\n\n  char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];\n  zx::process child;\n  zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID,\n                                      flags,\n                                      command_.value().c_str(),\n                                      argv_.data(),\n                                      nullptr,\n                                      kActionCount,\n                                      actions,\n                                      child.reset_and_get_address(),\n                                      error_message);\n  ZX_CHECK(status == ZX_OK, status) << \"fdio_spawn_etc: \" << error_message;\n  info()->child = std::move(child);\n}\n\nProcessType MultiprocessExec::ChildProcess() {\n  return zx::unowned_process(info()->child);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/multiprocess_exec_posix.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/multiprocess_exec.h\"\n\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"util/misc/scoped_forbid_return.h\"\n#include \"util/posix/close_multiple.h\"\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#include <stdio_ext.h>\n#endif\n\n#if BUILDFLAG(IS_APPLE)\n#include \"util/mach/task_for_pid.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\n\nMultiprocessExec::MultiprocessExec()\n    : Multiprocess(),\n      command_(),\n      arguments_(),\n      argv_() {\n}\n\nvoid MultiprocessExec::SetChildCommand(\n    const base::FilePath& command,\n    const std::vector<std::string>* arguments) {\n  command_ = command;\n  if (arguments) {\n    arguments_ = *arguments;\n  } else {\n    arguments_.clear();\n  }\n}\n\nMultiprocessExec::~MultiprocessExec() {\n}\n\nvoid MultiprocessExec::PreFork() {\n  ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork());\n\n  ASSERT_FALSE(command_.empty());\n\n  // Build up the argv vector. This is done in PreFork() instead of\n  // MultiprocessChild() because although the result is only needed in the child\n  // process, building it is a hazardous operation in that process.\n  ASSERT_TRUE(argv_.empty());\n\n  argv_.push_back(command_.value().c_str());\n  for (const std::string& argument : arguments_) {\n    argv_.push_back(argument.c_str());\n  }\n  argv_.push_back(nullptr);\n}\n\nvoid MultiprocessExec::MultiprocessChild() {\n  // Make sure that stdin, stdout, and stderr are FDs 0, 1, and 2, respectively.\n  // All FDs above this will be closed.\n  static_assert(STDIN_FILENO == 0, \"stdin must be fd 0\");\n  static_assert(STDOUT_FILENO == 1, \"stdout must be fd 1\");\n  static_assert(STDERR_FILENO == 2, \"stderr must be fd 2\");\n\n  // Move the read pipe to stdin.\n  FileHandle read_handle = ReadPipeHandle();\n  ASSERT_NE(read_handle, STDIN_FILENO);\n  ASSERT_NE(read_handle, STDOUT_FILENO);\n  ASSERT_EQ(fileno(stdin), STDIN_FILENO);\n\n  int rv;\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  __fpurge(stdin);\n#else\n  rv = fpurge(stdin);\n  ASSERT_EQ(rv, 0) << ErrnoMessage(\"fpurge\");\n#endif\n\n  rv = HANDLE_EINTR(dup2(read_handle, STDIN_FILENO));\n  ASSERT_EQ(rv, STDIN_FILENO) << ErrnoMessage(\"dup2\");\n\n  // Move the write pipe to stdout.\n  FileHandle write_handle = WritePipeHandle();\n  ASSERT_NE(write_handle, STDIN_FILENO);\n  ASSERT_NE(write_handle, STDOUT_FILENO);\n  ASSERT_EQ(fileno(stdout), STDOUT_FILENO);\n\n  // Make a copy of the original stdout file descriptor so that in case there’s\n  // an execv() failure, the original stdout can be restored so that Google Test\n  // messages directed to stdout go to the right place. Mark it as\n  // close-on-exec, so that the child won’t see it after a successful exec(),\n  // but it will still be available in this process after an unsuccessful\n  // exec().\n  int dup_orig_stdout_fd = dup(STDOUT_FILENO);\n  ASSERT_GE(dup_orig_stdout_fd, 0) << ErrnoMessage(\"dup\");\n\n  rv = fcntl(dup_orig_stdout_fd, F_SETFD, FD_CLOEXEC);\n  ASSERT_NE(rv, -1) << ErrnoMessage(\"fcntl\");\n\n  rv = HANDLE_EINTR(fflush(stdout));\n  ASSERT_EQ(rv, 0) << ErrnoMessage(\"fflush\");\n\n  rv = HANDLE_EINTR(dup2(write_handle, STDOUT_FILENO));\n  ASSERT_EQ(rv, STDOUT_FILENO) << ErrnoMessage(\"dup2\");\n\n  CloseMultipleNowOrOnExec(STDERR_FILENO + 1, dup_orig_stdout_fd);\n\n  // Start the new program, replacing this one. execv() has a weird declaration\n  // where its argv argument is declared as char* const*. In reality, the\n  // implementation behaves as if the argument were const char* const*, and this\n  // behavior is required by the standard. See\n  // http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html\n  // (search for “constant”).\n  execv(argv_[0], const_cast<char* const*>(&argv_[0]));\n\n  // This should not normally be reached. Getting here means that execv()\n  // failed.\n\n  // Be sure not to return until FAIL() is reached.\n  ScopedForbidReturn forbid_return;\n\n  // Put the original stdout back. Close the copy of the write pipe FD that’s\n  // currently on stdout first, so that in case the dup2() that restores the\n  // original stdout fails, stdout isn’t left attached to the pipe when the\n  // FAIL() statement executes.\n  HANDLE_EINTR(fflush(stdout));\n  IGNORE_EINTR(close(STDOUT_FILENO));\n  HANDLE_EINTR(dup2(dup_orig_stdout_fd, STDOUT_FILENO));\n  IGNORE_EINTR(close(dup_orig_stdout_fd));\n\n  forbid_return.Disarm();\n  FAIL() << ErrnoMessage(\"execv\") << \": \" << argv_[0];\n}\n\nProcessType MultiprocessExec::ChildProcess() {\n#if BUILDFLAG(IS_APPLE)\n  return TaskForPID(ChildPID());\n#else\n  return ChildPID();\n#endif\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/multiprocess_exec_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/multiprocess_exec.h\"\n\n#include \"base/logging.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass TestMultiprocessExec final : public MultiprocessExec {\n public:\n  TestMultiprocessExec() : MultiprocessExec() {}\n\n  TestMultiprocessExec(const TestMultiprocessExec&) = delete;\n  TestMultiprocessExec& operator=(const TestMultiprocessExec&) = delete;\n\n  ~TestMultiprocessExec() {}\n\n private:\n  void MultiprocessParent() override {\n    // Use Logging*File() instead of Checked*File() so that the test can fail\n    // gracefully with a Google Test assertion if the child does not execute\n    // properly.\n\n    char c = 'z';\n    ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &c, 1));\n\n    ASSERT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, 1));\n    EXPECT_EQ(c, 'Z');\n  }\n};\n\nTEST(MultiprocessExec, MultiprocessExec) {\n  TestMultiprocessExec multiprocess_exec;\n  base::FilePath child_test_executable = TestPaths::BuildArtifact(\n      FILE_PATH_LITERAL(\"test\"),\n      FILE_PATH_LITERAL(\"multiprocess_exec_test_child\"),\n      TestPaths::FileType::kExecutable);\n  multiprocess_exec.SetChildCommand(child_test_executable, nullptr);\n  multiprocess_exec.Run();\n}\n\n\nCRASHPAD_CHILD_TEST_MAIN(SimpleMultiprocess) {\n  char c;\n  CheckedReadFileExactly(StdioFileHandle(StdioStream::kStandardInput), &c, 1);\n  LOG_IF(FATAL, c != 'z');\n\n  c = 'Z';\n  CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), &c, 1);\n  return 0;\n}\n\nTEST(MultiprocessExec, MultiprocessExecSimpleChild) {\n  TestMultiprocessExec exec;\n  exec.SetChildTestMainFunction(\"SimpleMultiprocess\");\n  exec.Run();\n}\n\nCRASHPAD_CHILD_TEST_MAIN(SimpleMultiprocessReturnsNonZero) {\n  return 123;\n}\n\nclass TestMultiprocessExecEmpty final : public MultiprocessExec {\n public:\n  TestMultiprocessExecEmpty() = default;\n\n  TestMultiprocessExecEmpty(const TestMultiprocessExecEmpty&) = delete;\n  TestMultiprocessExecEmpty& operator=(const TestMultiprocessExecEmpty&) =\n      delete;\n\n  ~TestMultiprocessExecEmpty() = default;\n\n private:\n  void MultiprocessParent() override {}\n};\n\nTEST(MultiprocessExec, MultiprocessExecSimpleChildReturnsNonZero) {\n  TestMultiprocessExecEmpty exec;\n  exec.SetChildTestMainFunction(\"SimpleMultiprocessReturnsNonZero\");\n  exec.SetExpectedChildTermination(\n      Multiprocess::TerminationReason::kTerminationNormal, 123);\n  exec.Run();\n}\n\n#if !BUILDFLAG(IS_WIN)\n\nCRASHPAD_CHILD_TEST_MAIN(BuiltinTrapChild) {\n  __builtin_trap();\n  return EXIT_SUCCESS;\n}\n\nclass TestBuiltinTrapTermination final : public MultiprocessExec {\n public:\n  TestBuiltinTrapTermination() {\n    SetChildTestMainFunction(\"BuiltinTrapChild\");\n    SetExpectedChildTerminationBuiltinTrap();\n  }\n\n  TestBuiltinTrapTermination(const TestBuiltinTrapTermination&) = delete;\n  TestBuiltinTrapTermination& operator=(const TestBuiltinTrapTermination&) =\n      delete;\n\n  ~TestBuiltinTrapTermination() = default;\n\n private:\n  void MultiprocessParent() override {}\n};\n\nTEST(MultiprocessExec, BuiltinTrapTermination) {\n  TestBuiltinTrapTermination test;\n  test.Run();\n}\n\n#endif  // !BUILDFLAG(IS_WIN)\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/multiprocess_exec_test_child.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <errno.h>\n#include <limits.h>\n#include <stdio.h>\n#include <sys/types.h>\n\n#include <algorithm>\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#if !BUILDFLAG(IS_FUCHSIA)\n#include <sys/resource.h>\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n#include <unistd.h>\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif\n\nint main(int argc, char* argv[]) {\n#if BUILDFLAG(IS_POSIX)\n\n#if BUILDFLAG(IS_FUCHSIA)\n  // getrlimit() is not implemented on Fuchsia. By construction, the child only\n  // receieves specific fds that it's given, but check low values as mild\n  // verification.\n  int last_fd = 1024;\n#else\n  rlimit rlimit_nofile;\n  if (getrlimit(RLIMIT_NOFILE, &rlimit_nofile) != 0) {\n    LOG(FATAL) << \"getrlimit\";\n  }\n  int last_fd = static_cast<int>(rlimit_nofile.rlim_cur);\n#endif  // BUILDFLAG(IS_FUCHSIA)\n\n  // Make sure that there’s nothing open at any FD higher than 3. All FDs other\n  // than stdin, stdout, and stderr should have been closed prior to or at\n  // exec().\n  for (int fd = STDERR_FILENO + 1; fd < last_fd; ++fd) {\n    if (close(fd) == 0 || errno != EBADF) {\n      LOG(FATAL) << \"close\";\n    }\n  }\n\n  // Read a byte from stdin, expecting it to be a specific value.\n  char c;\n  ssize_t rv = read(STDIN_FILENO, &c, 1);\n  if (rv != 1 || c != 'z') {\n    LOG(FATAL) << \"read\";\n  }\n\n  // Write a byte to stdout.\n  c = 'Z';\n  rv = write(STDOUT_FILENO, &c, 1);\n  if (rv != 1) {\n    LOG(FATAL) << \"write\";\n  }\n#elif BUILDFLAG(IS_WIN)\n  // TODO(scottmg): Verify that only the handles we expect to be open, are.\n\n  // Read a byte from stdin, expecting it to be a specific value.\n  char c;\n  DWORD bytes_read;\n  HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);\n  if (!ReadFile(stdin_handle, &c, 1, &bytes_read, nullptr) ||\n      bytes_read != 1 || c != 'z') {\n    LOG(FATAL) << \"ReadFile\";\n  }\n\n  // Write a byte to stdout.\n  c = 'Z';\n  DWORD bytes_written;\n  if (!WriteFile(\n          GetStdHandle(STD_OUTPUT_HANDLE), &c, 1, &bytes_written, nullptr) ||\n      bytes_written != 1) {\n    LOG(FATAL) << \"WriteFile\";\n  }\n#endif  // BUILDFLAG(IS_POSIX)\n\n  return 0;\n}\n"
  },
  {
    "path": "test/multiprocess_exec_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/multiprocess_exec.h\"\n\n#include <sys/types.h>\n\n#include \"base/check.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"util/win/command_line.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace internal {\n\nstruct MultiprocessInfo {\n  MultiprocessInfo() {}\n  ScopedFileHANDLE pipe_c2p_read;\n  ScopedFileHANDLE pipe_c2p_write;\n  ScopedFileHANDLE pipe_p2c_read;\n  ScopedFileHANDLE pipe_p2c_write;\n  PROCESS_INFORMATION process_info;\n};\n\n}  // namespace internal\n\nMultiprocess::Multiprocess()\n    : info_(nullptr),\n      code_(EXIT_SUCCESS),\n      reason_(kTerminationNormal) {\n}\n\nvoid Multiprocess::Run() {\n  // Set up and spawn the child process.\n  ASSERT_NO_FATAL_FAILURE(PreFork());\n  RunChild();\n\n  // And then run the parent actions in this process.\n  RunParent();\n\n  // Reap the child.\n  WaitForSingleObject(info_->process_info.hProcess, INFINITE);\n  CloseHandle(info_->process_info.hThread);\n  CloseHandle(info_->process_info.hProcess);\n}\n\nvoid Multiprocess::SetExpectedChildTermination(TerminationReason reason,\n                                               ReturnCodeType code) {\n  EXPECT_EQ(info_, nullptr)\n      << \"SetExpectedChildTermination() must be called before Run()\";\n  reason_ = reason;\n  code_ = code;\n}\n\nMultiprocess::~Multiprocess() {\n  delete info_;\n}\n\nFileHandle Multiprocess::ReadPipeHandle() const {\n  // This is the parent case, it's stdin in the child.\n  return info_->pipe_c2p_read.get();\n}\n\nFileHandle Multiprocess::WritePipeHandle() const {\n  // This is the parent case, it's stdout in the child.\n  return info_->pipe_p2c_write.get();\n}\n\nvoid Multiprocess::CloseReadPipe() {\n  info_->pipe_c2p_read.reset();\n}\n\nvoid Multiprocess::CloseWritePipe() {\n  info_->pipe_p2c_write.reset();\n}\n\nvoid Multiprocess::RunParent() {\n  MultiprocessParent();\n\n  info_->pipe_c2p_read.reset();\n  info_->pipe_p2c_write.reset();\n}\n\nvoid Multiprocess::RunChild() {\n  MultiprocessChild();\n\n  info_->pipe_c2p_write.reset();\n  info_->pipe_p2c_read.reset();\n}\n\nMultiprocessExec::MultiprocessExec()\n    : Multiprocess(), command_(), arguments_(), command_line_() {\n}\n\nvoid MultiprocessExec::SetChildCommand(\n    const base::FilePath& command,\n    const std::vector<std::string>* arguments) {\n  command_ = command;\n  if (arguments) {\n    arguments_ = *arguments;\n  } else {\n    arguments_.clear();\n  }\n}\n\nMultiprocessExec::~MultiprocessExec() {\n}\n\nvoid MultiprocessExec::PreFork() {\n  ASSERT_FALSE(command_.empty());\n\n  command_line_.clear();\n  AppendCommandLineArgument(command_.value(), &command_line_);\n  for (size_t i = 0; i < arguments_.size(); ++i) {\n    AppendCommandLineArgument(base::UTF8ToWide(arguments_[i]), &command_line_);\n  }\n\n  // Make pipes for child-to-parent and parent-to-child communication. Mark them\n  // as inheritable via the SECURITY_ATTRIBUTES, but use SetHandleInformation to\n  // ensure that the parent sides are not inherited.\n  ASSERT_EQ(info(), nullptr);\n  set_info(new internal::MultiprocessInfo());\n\n  SECURITY_ATTRIBUTES security_attributes = {0};\n  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\n  security_attributes.bInheritHandle = TRUE;\n\n  HANDLE c2p_read, c2p_write;\n  PCHECK(CreatePipe(&c2p_read, &c2p_write, &security_attributes, 0));\n  PCHECK(SetHandleInformation(c2p_read, HANDLE_FLAG_INHERIT, 0));\n  info()->pipe_c2p_read.reset(c2p_read);\n  info()->pipe_c2p_write.reset(c2p_write);\n\n  HANDLE p2c_read, p2c_write;\n  PCHECK(CreatePipe(&p2c_read, &p2c_write, &security_attributes, 0));\n  PCHECK(SetHandleInformation(p2c_write, HANDLE_FLAG_INHERIT, 0));\n  info()->pipe_p2c_read.reset(p2c_read);\n  info()->pipe_p2c_write.reset(p2c_write);\n}\n\nvoid MultiprocessExec::MultiprocessChild() {\n  STARTUPINFO startup_info = {0};\n  startup_info.cb = sizeof(startup_info);\n  startup_info.hStdInput = info()->pipe_p2c_read.get();\n  startup_info.hStdOutput = info()->pipe_c2p_write.get();\n  startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);\n  startup_info.dwFlags = STARTF_USESTDHANDLES;\n  PCHECK(CreateProcess(command_.value().c_str(),\n                       &command_line_[0],  // This cannot be constant, per MSDN.\n                       nullptr,\n                       nullptr,\n                       TRUE,\n                       0,\n                       nullptr,\n                       nullptr,\n                       &startup_info,\n                       &info()->process_info));\n}\n\nProcessType MultiprocessExec::ChildProcess() {\n  return info()->process_info.hProcess;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/multiprocess_posix.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/multiprocess.h\"\n\n#include <signal.h>\n#include <stdlib.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <memory>\n#include <string>\n\n#include \"base/auto_reset.h\"\n#include \"base/check_op.h\"\n#include \"base/files/scoped_file.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"util/misc/scoped_forbid_return.h\"\n#include \"util/posix/signals.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include \"test/mac/exception_swallower.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\n\nnamespace internal {\n\nstruct MultiprocessInfo {\n  MultiprocessInfo()\n      : pipe_c2p_read(-1),\n        pipe_c2p_write(-1),\n        pipe_p2c_read(-1),\n        pipe_p2c_write(-1),\n        child_pid(0) {}\n\n  base::ScopedFD pipe_c2p_read;  // child to parent\n  base::ScopedFD pipe_c2p_write;  // child to parent\n  base::ScopedFD pipe_p2c_read;  // parent to child\n  base::ScopedFD pipe_p2c_write;  // parent to child\n  pid_t child_pid;  // valid only in parent\n};\n\n}  // namespace internal\n\nMultiprocess::Multiprocess()\n    : info_(nullptr),\n      code_(EXIT_SUCCESS),\n      reason_(kTerminationNormal) {\n}\n\nvoid Multiprocess::Run() {\n  ASSERT_EQ(info_, nullptr);\n  std::unique_ptr<internal::MultiprocessInfo> info(\n      new internal::MultiprocessInfo);\n  base::AutoReset<internal::MultiprocessInfo*> reset_info(&info_, info.get());\n\n  ASSERT_NO_FATAL_FAILURE(PreFork());\n\n#if BUILDFLAG(IS_APPLE)\n  // If the child is expected to crash, set up an exception swallower to swallow\n  // the exception instead of allowing it to be seen by the system’s crash\n  // reporter.\n  std::unique_ptr<ExceptionSwallower> exception_swallower;\n  if (reason_ == kTerminationSignal && Signals::IsCrashSignal(code_)) {\n    exception_swallower.reset(new ExceptionSwallower());\n  }\n#endif  // BUILDFLAG(IS_APPLE)\n\n  pid_t pid = fork();\n  ASSERT_GE(pid, 0) << ErrnoMessage(\"fork\");\n\n  if (pid > 0) {\n    info_->child_pid = pid;\n\n    RunParent();\n\n    // Waiting for the child happens here instead of in RunParent() because even\n    // if RunParent() returns early due to a Google Test fatal assertion\n    // failure, the child should still be reaped.\n\n    // This will make the parent hang up on the child as much as would be\n    // visible from the child’s perspective. The child’s side of the pipe will\n    // be broken, the child’s remote port will become a dead name, and an\n    // attempt by the child to look up the service will fail. If this weren’t\n    // done, the child might hang while waiting for a parent that has already\n    // triggered a fatal assertion failure to do something.\n    info.reset();\n    info_ = nullptr;\n\n    int status;\n    pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));\n    ASSERT_EQ(wait_pid, pid) << ErrnoMessage(\"waitpid\");\n\n    TerminationReason reason;\n    int code;\n    std::string message;\n    if (WIFEXITED(status)) {\n      reason = kTerminationNormal;\n      code = WEXITSTATUS(status);\n      message = base::StringPrintf(\"Child exited with code %d\", code);\n    } else if (WIFSIGNALED(status)) {\n      reason = kTerminationSignal;\n      code = WTERMSIG(status);\n      message =\n          base::StringPrintf(\"Child terminated by signal %d (%s)%s\",\n                             code,\n                             strsignal(code),\n                             WCOREDUMP(status) ? \" (core dumped)\" : \"\");\n    } else {\n      FAIL() << base::StringPrintf(\"Unknown termination reason 0x%x\", status);\n    }\n\n    if (reason_ == kTerminationNormal) {\n      message += base::StringPrintf(\", expected exit with code %d\", code_);\n    } else if (reason_ == kTerminationSignal) {\n      message += base::StringPrintf(\", expected termination by signal %d (%s)\",\n                                    code_,\n                                    strsignal(code_));\n    }\n\n    if (reason != reason_ || code != code_) {\n      ADD_FAILURE() << message;\n    }\n  } else {\n#if BUILDFLAG(IS_APPLE)\n    if (exception_swallower.get()) {\n      ExceptionSwallower::SwallowExceptions();\n    }\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n    if (reason_ == kTerminationSignal && Signals::IsCrashSignal(code_)) {\n      Signals::InstallDefaultHandler(code_);\n    }\n#endif  // BUILDFLAG(IS_APPLE)\n\n    RunChild();\n  }\n}\n\nvoid Multiprocess::SetExpectedChildTermination(TerminationReason reason,\n                                               ReturnCodeType code) {\n  EXPECT_EQ(info_, nullptr)\n      << \"SetExpectedChildTermination() must be called before Run()\";\n  reason_ = reason;\n  code_ = code;\n}\n\nvoid Multiprocess::SetExpectedChildTerminationBuiltinTrap() {\n#if defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_MIPS_FAMILY)\n  SetExpectedChildTermination(kTerminationSignal, SIGTRAP);\n#else\n  SetExpectedChildTermination(kTerminationSignal, SIGILL);\n#endif\n}\n\nMultiprocess::~Multiprocess() {\n}\n\nvoid Multiprocess::PreFork() {\n  int pipe_fds_c2p[2];\n  int rv = pipe(pipe_fds_c2p);\n  ASSERT_EQ(rv, 0) << ErrnoMessage(\"pipe\");\n\n  info_->pipe_c2p_read.reset(pipe_fds_c2p[0]);\n  info_->pipe_c2p_write.reset(pipe_fds_c2p[1]);\n\n  int pipe_fds_p2c[2];\n  rv = pipe(pipe_fds_p2c);\n  ASSERT_EQ(rv, 0) << ErrnoMessage(\"pipe\");\n\n  info_->pipe_p2c_read.reset(pipe_fds_p2c[0]);\n  info_->pipe_p2c_write.reset(pipe_fds_p2c[1]);\n}\n\npid_t Multiprocess::ChildPID() const {\n  EXPECT_NE(info_->child_pid, 0);\n  return info_->child_pid;\n}\n\nFileHandle Multiprocess::ReadPipeHandle() const {\n  int fd = info_->child_pid ? info_->pipe_c2p_read.get()\n                            : info_->pipe_p2c_read.get();\n  CHECK_NE(fd, -1);\n  return fd;\n}\n\nFileHandle Multiprocess::WritePipeHandle() const {\n  int fd = info_->child_pid ? info_->pipe_p2c_write.get()\n                            : info_->pipe_c2p_write.get();\n  CHECK_NE(fd, -1);\n  return fd;\n}\n\nvoid Multiprocess::CloseReadPipe() {\n  if (info_->child_pid) {\n    info_->pipe_c2p_read.reset();\n  } else {\n    info_->pipe_p2c_read.reset();\n  }\n}\n\nvoid Multiprocess::CloseWritePipe() {\n  if (info_->child_pid) {\n    info_->pipe_p2c_write.reset();\n  } else {\n    info_->pipe_c2p_write.reset();\n  }\n}\n\nvoid Multiprocess::RunParent() {\n  // The parent uses the read end of c2p and the write end of p2c.\n  info_->pipe_c2p_write.reset();\n  info_->pipe_p2c_read.reset();\n\n  MultiprocessParent();\n\n  info_->pipe_c2p_read.reset();\n  info_->pipe_p2c_write.reset();\n}\n\nvoid Multiprocess::RunChild() {\n  ScopedForbidReturn forbid_return;\n\n  // The child uses the write end of c2p and the read end of p2c.\n  info_->pipe_c2p_read.reset();\n  info_->pipe_p2c_write.reset();\n\n  MultiprocessChild();\n\n  info_->pipe_c2p_write.reset();\n  info_->pipe_p2c_read.reset();\n\n  if (testing::Test::HasFailure()) {\n    // Trigger the ScopedForbidReturn destructor.\n    return;\n  }\n\n  // In a forked child, exit() is unsafe. Use _exit() instead.\n  _exit(EXIT_SUCCESS);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/multiprocess_posix_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/multiprocess.h\"\n\n#include <signal.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass TestMultiprocess final : public Multiprocess {\n public:\n  TestMultiprocess() : Multiprocess() {}\n\n  TestMultiprocess(const TestMultiprocess&) = delete;\n  TestMultiprocess& operator=(const TestMultiprocess&) = delete;\n\n  ~TestMultiprocess() {}\n\n private:\n  // Multiprocess:\n\n  void MultiprocessParent() override {\n    FileHandle read_handle = ReadPipeHandle();\n    char c;\n    CheckedReadFileExactly(read_handle, &c, 1);\n    EXPECT_EQ(c, 'M');\n\n    pid_t pid;\n    CheckedReadFileExactly(read_handle, &pid, sizeof(pid));\n    EXPECT_EQ(ChildPID(), pid);\n\n    c = 'm';\n    CheckedWriteFile(WritePipeHandle(), &c, 1);\n\n    // The child will close its end of the pipe and exit. Make sure that the\n    // parent sees EOF.\n    CheckedReadFileAtEOF(read_handle);\n  }\n\n  void MultiprocessChild() override {\n    FileHandle write_handle = WritePipeHandle();\n\n    char c = 'M';\n    CheckedWriteFile(write_handle, &c, 1);\n\n    pid_t pid = getpid();\n    CheckedWriteFile(write_handle, &pid, sizeof(pid));\n\n    CheckedReadFileExactly(ReadPipeHandle(), &c, 1);\n    EXPECT_EQ(c, 'm');\n  }\n};\n\nTEST(Multiprocess, Multiprocess) {\n  TestMultiprocess multiprocess;\n  multiprocess.Run();\n}\n\nclass TestMultiprocessUnclean final : public Multiprocess {\n public:\n  enum TerminationType {\n    kExitSuccess = 0,\n    kExitFailure,\n    kExit2,\n    kAbort,\n  };\n\n  explicit TestMultiprocessUnclean(TerminationType type)\n      : Multiprocess(),\n        type_(type) {\n    if (type_ == kAbort) {\n      SetExpectedChildTermination(kTerminationSignal, SIGABRT);\n    } else {\n      SetExpectedChildTermination(kTerminationNormal, ExitCode());\n    }\n  }\n\n  TestMultiprocessUnclean(const TestMultiprocessUnclean&) = delete;\n  TestMultiprocessUnclean& operator=(const TestMultiprocessUnclean&) = delete;\n\n  ~TestMultiprocessUnclean() {}\n\n private:\n  int ExitCode() const {\n    return type_;\n  }\n\n  // Multiprocess:\n\n  void MultiprocessParent() override {\n  }\n\n  void MultiprocessChild() override {\n    if (type_ == kAbort) {\n      abort();\n    } else {\n      _exit(ExitCode());\n    }\n  }\n\n  TerminationType type_;\n};\n\nTEST(Multiprocess, SuccessfulExit) {\n  TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitSuccess);\n  multiprocess.Run();\n}\n\nTEST(Multiprocess, UnsuccessfulExit) {\n  TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitFailure);\n  multiprocess.Run();\n}\n\nTEST(Multiprocess, Exit2) {\n  TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExit2);\n  multiprocess.Run();\n}\n\nTEST(Multiprocess, AbortSignal) {\n  TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kAbort);\n  multiprocess.Run();\n}\n\nclass TestMultiprocessClosePipe final : public Multiprocess {\n public:\n  enum WhoCloses {\n    kParentCloses = 0,\n    kChildCloses,\n  };\n  enum WhatCloses {\n    kReadCloses = 0,\n    kWriteCloses,\n    kReadAndWriteClose,\n  };\n\n  TestMultiprocessClosePipe(WhoCloses who_closes, WhatCloses what_closes)\n      : Multiprocess(),\n        who_closes_(who_closes),\n        what_closes_(what_closes) {\n    // Fails under \"threadsafe\" mode on macOS 10.11.\n    GTEST_FLAG_SET(death_test_style, \"fast\");\n  }\n\n  TestMultiprocessClosePipe(const TestMultiprocessClosePipe&) = delete;\n  TestMultiprocessClosePipe& operator=(const TestMultiprocessClosePipe&) =\n      delete;\n\n  ~TestMultiprocessClosePipe() {}\n\n private:\n  void VerifyInitial() {\n    ASSERT_NE(ReadPipeHandle(), -1);\n    ASSERT_NE(WritePipeHandle(), -1);\n  }\n\n  // Verifies that the partner process did what it was supposed to do. This must\n  // only be called when who_closes_ names the partner process, not this\n  // process.\n  //\n  // If the partner was supposed to close its write pipe, the read pipe will be\n  // checked to ensure that it shows end-of-file.\n  //\n  // If the partner was supposed to close its read pipe, the write pipe will be\n  // checked to ensure that a checked write causes death. This can only be done\n  // if the partner also provides some type of signal when it has closed its\n  // read pipe, which is done in the form of it closing its write pipe, causing\n  // the read pipe in this process to show end-of-file.\n  void VerifyPartner() {\n    if (what_closes_ == kWriteCloses) {\n      CheckedReadFileAtEOF(ReadPipeHandle());\n    } else if (what_closes_ == kReadAndWriteClose) {\n      CheckedReadFileAtEOF(ReadPipeHandle());\n      char c = '\\0';\n\n      // This will raise SIGPIPE. If fatal (the normal case), that will cause\n      // process termination. If SIGPIPE is being handled somewhere, the write\n      // will still fail and set errno to EPIPE, and CheckedWriteFile() will\n      // abort execution. Regardless of how SIGPIPE is handled, the process will\n      // be terminated. Because the actual termination mechanism is not known,\n      // no regex can be specified.\n      EXPECT_DEATH_CHECK(CheckedWriteFile(WritePipeHandle(), &c, 1), \"\");\n    }\n  }\n\n  void Close() {\n    switch (what_closes_) {\n      case kReadCloses:\n        CloseReadPipe();\n        EXPECT_NE(WritePipeHandle(), -1);\n        EXPECT_DEATH_CHECK(ReadPipeHandle(), \"fd\");\n        break;\n      case kWriteCloses:\n        CloseWritePipe();\n        EXPECT_NE(ReadPipeHandle(), -1);\n        EXPECT_DEATH_CHECK(WritePipeHandle(), \"fd\");\n        break;\n      case kReadAndWriteClose:\n        CloseReadPipe();\n        CloseWritePipe();\n        EXPECT_DEATH_CHECK(ReadPipeHandle(), \"fd\");\n        EXPECT_DEATH_CHECK(WritePipeHandle(), \"fd\");\n        break;\n    }\n  }\n\n  // Multiprocess:\n\n  void MultiprocessParent() override {\n    ASSERT_NO_FATAL_FAILURE(VerifyInitial());\n\n    if (who_closes_ == kParentCloses) {\n      Close();\n    } else {\n      VerifyPartner();\n    }\n  }\n\n  void MultiprocessChild() override {\n    ASSERT_NO_FATAL_FAILURE(VerifyInitial());\n\n    if (who_closes_ == kChildCloses) {\n      Close();\n    } else {\n      VerifyPartner();\n    }\n  }\n\n  WhoCloses who_closes_;\n  WhatCloses what_closes_;\n};\n\nTEST(MultiprocessDeathTest, ParentClosesReadPipe) {\n  TestMultiprocessClosePipe multiprocess(\n      TestMultiprocessClosePipe::kParentCloses,\n      TestMultiprocessClosePipe::kReadCloses);\n  multiprocess.Run();\n}\n\nTEST(MultiprocessDeathTest, ParentClosesWritePipe) {\n  TestMultiprocessClosePipe multiprocess(\n      TestMultiprocessClosePipe::kParentCloses,\n      TestMultiprocessClosePipe::kWriteCloses);\n  multiprocess.Run();\n}\n\nTEST(MultiprocessDeathTest, ParentClosesReadAndWritePipe) {\n  TestMultiprocessClosePipe multiprocess(\n      TestMultiprocessClosePipe::kParentCloses,\n      TestMultiprocessClosePipe::kReadAndWriteClose);\n  multiprocess.Run();\n}\n\nTEST(MultiprocessDeathTest, ChildClosesReadPipe) {\n  TestMultiprocessClosePipe multiprocess(\n      TestMultiprocessClosePipe::kChildCloses,\n      TestMultiprocessClosePipe::kReadCloses);\n  multiprocess.Run();\n}\n\nTEST(MultiprocessDeathTest, ChildClosesWritePipe) {\n  TestMultiprocessClosePipe multiprocess(\n      TestMultiprocessClosePipe::kChildCloses,\n      TestMultiprocessClosePipe::kWriteCloses);\n  multiprocess.Run();\n}\n\nTEST(MultiprocessDeathTest, ChildClosesReadAndWritePipe) {\n  TestMultiprocessClosePipe multiprocess(\n      TestMultiprocessClosePipe::kChildCloses,\n      TestMultiprocessClosePipe::kReadAndWriteClose);\n  multiprocess.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/process_type.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/process_type.h\"\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include <lib/zx/process.h>\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include <unistd.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\n\nProcessType GetSelfProcess() {\n#if BUILDFLAG(IS_FUCHSIA)\n  return zx::process::self();\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  return getpid();\n#elif BUILDFLAG(IS_WIN)\n  return GetCurrentProcess();\n#elif BUILDFLAG(IS_APPLE)\n  return mach_task_self();\n#endif\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/process_type.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_PROCESS_TYPE_H_\n#define CRASHPAD_TEST_PROCESS_TYPE_H_\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include <lib/zx/process.h>\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include <sys/types.h>\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#elif BUILDFLAG(IS_APPLE)\n#include <mach/mach.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\n\n#if BUILDFLAG(IS_FUCHSIA)\nusing ProcessType = zx::unowned_process;\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \\\n    BUILDFLAG(IS_ANDROID) || DOXYGEN\n//! \\brief Alias for platform-specific type to represent a process.\nusing ProcessType = pid_t;\n#elif BUILDFLAG(IS_WIN)\nusing ProcessType = HANDLE;\n#elif BUILDFLAG(IS_APPLE)\nusing ProcessType = task_t;\n#else\n#error Port.\n#endif\n\n//! \\brief Get a ProcessType representing the current process.\nProcessType GetSelfProcess();\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_PROCESS_TYPE_H_\n"
  },
  {
    "path": "test/scoped_guarded_page.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_SCOPED_GUARDED_PAGE_\n#define CRASHPAD_TEST_SCOPED_GUARDED_PAGE_\n\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A RAII object that allocates a read-write page with an inacessible\n//!     page following it.\n//!\n//! Upon construction, a mapping will be created. Failure to create the mapping\n//! is fatal. On destruction, the mapping is freed.\n//!\n//! This object should not be used in multi-threded contexts, the POSIX\n//! implementation can not be made thread-safe.\nclass ScopedGuardedPage {\n public:\n  ScopedGuardedPage();\n\n  ScopedGuardedPage(const ScopedGuardedPage&) = delete;\n  ScopedGuardedPage& operator=(const ScopedGuardedPage&) = delete;\n\n  ~ScopedGuardedPage();\n\n  //! \\brief Returns the address of the read-write page.\n  //!\n  //! \\return The address of the read-write page.\n  void* Pointer() const { return ptr_; }\n\n private:\n  void* ptr_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_SCOPED_GUARDED_PAGE_\n"
  },
  {
    "path": "test/scoped_guarded_page_posix.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_guarded_page.h\"\n\n#include <sys/mman.h>\n\n#include \"base/check.h\"\n#include \"base/memory/page_size.h\"\n\nnamespace crashpad {\nnamespace test {\n\nScopedGuardedPage::ScopedGuardedPage() {\n  ptr_ = mmap(nullptr,\n              base::GetPageSize() * 2,\n              PROT_READ | PROT_WRITE,\n              MAP_PRIVATE | MAP_ANONYMOUS,\n              -1,\n              0);\n  PCHECK(ptr_ != MAP_FAILED) << \"mmap\";\n\n  // Simply mprotect()ing the guard page PROT_NONE does not make it inaccessible\n  // using ptrace() or /proc/$pid/mem so we munmap() the following page instead.\n  // Unfortunately, this means that the guarded page is not thread safe from\n  // other threads mapping a single page into the empty region.\n  char* second_page = static_cast<char*>(ptr_) + base::GetPageSize();\n  PCHECK(munmap(second_page, base::GetPageSize()) >= 0) << \"munmap\";\n}\n\nScopedGuardedPage::~ScopedGuardedPage() {\n  PCHECK(munmap(ptr_, base::GetPageSize()) >= 0) << \"munmap\";\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_guarded_page_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_guarded_page.h\"\n\n#include \"base/memory/page_size.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ScopedGuardedPage, BasicFunctionality) {\n  GTEST_FLAG_SET(death_test_style, \"threadsafe\");\n\n  ScopedGuardedPage page;\n  char* address = (char*)page.Pointer();\n  EXPECT_NE(address, nullptr);\n  address[0] = 0;\n  address[base::GetPageSize() - 1] = 0;\n  EXPECT_DEATH_CRASH({ address[base::GetPageSize()] = 0; }, \"\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_guarded_page_win.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_guarded_page.h\"\n\n#include <windows.h>\n\n#include \"base/check.h\"\n#include \"base/memory/page_size.h\"\n\nnamespace crashpad {\nnamespace test {\n\nScopedGuardedPage::ScopedGuardedPage() {\n  const size_t page_size = base::GetPageSize();\n  ptr_ = VirtualAlloc(nullptr, page_size * 2, MEM_RESERVE, PAGE_NOACCESS);\n  PCHECK(ptr_ != nullptr) << \"VirtualAlloc\";\n\n  PCHECK(VirtualAlloc(ptr_, page_size, MEM_COMMIT, PAGE_READWRITE) != nullptr)\n      << \"VirtualAlloc\";\n}\n\nScopedGuardedPage::~ScopedGuardedPage() {\n  PCHECK(VirtualFree(ptr_, 0, MEM_RELEASE)) << \"VirtualFree\";\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_module_handle.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_module_handle.h\"\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\nnamespace test {\n\n// static\nvoid ScopedModuleHandle::Impl::Close(ModuleHandle handle) {\n#if BUILDFLAG(IS_POSIX)\n  if (dlclose(handle) != 0) {\n    LOG(ERROR) << \"dlclose: \" << dlerror();\n  }\n#elif BUILDFLAG(IS_WIN)\n  if (!FreeLibrary(handle)) {\n    PLOG(ERROR) << \"FreeLibrary\";\n  }\n#else\n#error Port\n#endif\n}\n\nScopedModuleHandle::ScopedModuleHandle(ModuleHandle handle) : handle_(handle) {}\n\nScopedModuleHandle::ScopedModuleHandle(ScopedModuleHandle&& other)\n    : handle_(other.handle_) {\n  other.handle_ = nullptr;\n}\n\nScopedModuleHandle::~ScopedModuleHandle() {\n  if (valid()) {\n    Impl::Close(handle_);\n  }\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_module_handle.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_\n#define CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include <dlfcn.h>\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Maintains ownership of a loadable module handle, releasing it as\n//!     appropriate on destruction.\nclass ScopedModuleHandle {\n private:\n  class Impl {\n   public:\n    Impl() = delete;\n    Impl(const Impl&) = delete;\n    Impl& operator=(const Impl&) = delete;\n\n#if BUILDFLAG(IS_POSIX)\n    using ModuleHandle = void*;\n\n    static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) {\n      return dlsym(handle, symbol_name);\n    }\n#elif BUILDFLAG(IS_WIN)\n    using ModuleHandle = HMODULE;\n\n    static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) {\n      return reinterpret_cast<void*>(GetProcAddress(handle, symbol_name));\n    }\n#endif\n\n    static void Close(ModuleHandle handle);\n  };\n\n public:\n  using ModuleHandle = Impl::ModuleHandle;\n\n  explicit ScopedModuleHandle(ModuleHandle handle);\n  ScopedModuleHandle(ScopedModuleHandle&& handle);\n\n  ScopedModuleHandle(const ScopedModuleHandle&) = delete;\n  ScopedModuleHandle& operator=(const ScopedModuleHandle&) = delete;\n\n  ~ScopedModuleHandle();\n\n  //! \\return The module handle being managed.\n  ModuleHandle get() const { return handle_; }\n\n  //! \\return `true` if this object manages a valid loadable module handle.\n  bool valid() const { return handle_ != nullptr; }\n\n  //! \\return The value of the symbol named by \\a symbol_name, or `nullptr` on\n  //!     failure.\n  template <typename T>\n  T LookUpSymbol(const char* symbol_name) const {\n    return reinterpret_cast<T>(Impl::LookUpSymbol(handle_, symbol_name));\n  }\n\n private:\n  ModuleHandle handle_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_\n"
  },
  {
    "path": "test/scoped_set_thread_name.h",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_\n#define CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_\n\n#include <string>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! Sets the name of the current thread for the lifetime of this object.\nclass ScopedSetThreadName final {\n public:\n  explicit ScopedSetThreadName(const std::string& new_thread_name);\n\n  ScopedSetThreadName(const ScopedSetThreadName&) = delete;\n  ScopedSetThreadName& operator=(const ScopedSetThreadName&) = delete;\n\n  ~ScopedSetThreadName();\n\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n  //! \\brief Returns `true` if Windows supports setting and getting thread name.\n  static bool IsSupported();\n#endif\n\n private:\n#if BUILDFLAG(IS_WIN)\n  std::wstring original_name_;\n#else\n  const std::string original_name_;\n#endif\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_SCOPED_SET_THREAD_NAME_H_\n"
  },
  {
    "path": "test/scoped_set_thread_name_fuchsia.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_set_thread_name.h\"\n\n#include <string>\n\n#include <lib/zx/thread.h>\n#include <zircon/syscalls/object.h>\n#include <zircon/types.h>\n\n#include \"base/check_op.h\"\n#include \"base/fuchsia/fuchsia_logging.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nstd::string GetCurrentThreadName() {\n  std::string result(ZX_MAX_NAME_LEN, '\\0');\n  const zx_status_t status = zx::thread::self()->get_property(\n      ZX_PROP_NAME, result.data(), result.length());\n  ZX_CHECK(status == ZX_OK, status) << \"get_property(ZX_PROP_NAME)\";\n  const auto result_nul_idx = result.find('\\0');\n  CHECK_NE(result_nul_idx, std::string::npos)\n      << \"get_property() did not NUL terminate\";\n  result.resize(result_nul_idx);\n  return result;\n}\n\n}  // namespace\n\nScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name)\n    : original_name_(GetCurrentThreadName()) {\n  // Fuchsia silently truncates the thread name if it's too long.\n  CHECK_LT(new_thread_name.length(), ZX_MAX_NAME_LEN);\n  const zx_status_t status = zx::thread::self()->set_property(\n      ZX_PROP_NAME, new_thread_name.c_str(), new_thread_name.length());\n  ZX_CHECK(status == ZX_OK, status) << \"set_property(ZX_PROP_NAME)\";\n}\n\nScopedSetThreadName::~ScopedSetThreadName() {\n  const zx_status_t status = zx::thread::self()->set_property(\n      ZX_PROP_NAME, original_name_.c_str(), original_name_.length());\n  ZX_CHECK(status == ZX_OK, status) << \"set_property(ZX_PROP_NAME)\";\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_set_thread_name_posix.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_set_thread_name.h\"\n\n#include <errno.h>\n#include <pthread.h>\n\n#include <ostream>\n#include <string>\n\n#include \"base/check.h\"\n#include \"base/check_op.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <mach/thread_info.h>\n#elif BUILDFLAG(IS_ANDROID)\n#include <sys/prctl.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\n#if BUILDFLAG(IS_APPLE)\nconstexpr size_t kPthreadNameMaxLen = MAXTHREADNAMESIZE;\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)\n// The kernel headers define this in linux/sched.h as TASK_COMM_LEN, but the\n// userspace copy of that header does not define it.\nconstexpr size_t kPthreadNameMaxLen = 16;\n#else\n#error Port to your platform\n#endif\n\nvoid SetCurrentThreadName(const std::string& thread_name) {\n#if BUILDFLAG(IS_APPLE)\n  // Apple's pthread_setname_np() sets errno instead of returning it.\n  PCHECK(pthread_setname_np(thread_name.c_str()) == 0) << \"pthread_setname_np\";\n#elif BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 24\n  // pthread_setname_np() requires Android API 24 or later.\n  CHECK_LT(thread_name.length(), kPthreadNameMaxLen);\n  PCHECK(prctl(PR_SET_NAME, thread_name.c_str()) == 0) << \"prctl(PR_SET_NAME)\";\n#else\n  PCHECK((errno = pthread_setname_np(pthread_self(), thread_name.c_str())) == 0)\n      << \"pthread_setname_np\";\n#endif\n}\n\nstd::string GetCurrentThreadName() {\n  std::string result(kPthreadNameMaxLen, '\\0');\n#if BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 24\n  static constexpr char kGetThreadNameFunctionName[] = \"prctl\";\n  PCHECK(prctl(PR_GET_NAME, result.data()) == 0) << \"prctl(PR_GET_NAME)\";\n#else\n  static constexpr char kGetThreadNameFunctionName[] = \"pthread_getname_np\";\n  PCHECK((errno = pthread_getname_np(\n              pthread_self(), result.data(), result.length())) == 0)\n      << \"pthread_getname_np\";\n#endif\n  const auto result_nul_idx = result.find('\\0');\n  CHECK(result_nul_idx != std::string::npos)\n      << kGetThreadNameFunctionName << \" did not NUL terminate\";\n  result.resize(result_nul_idx);\n  return result;\n}\n\n}  // namespace\n\nScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name)\n    : original_name_(GetCurrentThreadName()) {\n  SetCurrentThreadName(new_thread_name);\n}\n\nScopedSetThreadName::~ScopedSetThreadName() {\n  SetCurrentThreadName(original_name_);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_set_thread_name_win.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_set_thread_name.h\"\n\n#include <windows.h>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"util/win/get_function.h\"\n#include \"util/win/scoped_local_alloc.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nauto GetThreadDescriptionFuncPtr() {\n  static const auto get_thread_description =\n      GET_FUNCTION(L\"kernel32.dll\", ::GetThreadDescription);\n  return get_thread_description;\n}\n\nauto SetThreadDescriptionFuncPtr() {\n  static const auto set_thread_description =\n      GET_FUNCTION(L\"kernel32.dll\", ::SetThreadDescription);\n  return set_thread_description;\n}\n\nstd::wstring GetCurrentThreadName() {\n  wchar_t* thread_description;\n  const auto get_thread_description = GetThreadDescriptionFuncPtr();\n  DCHECK(get_thread_description);\n  HRESULT hr = get_thread_description(GetCurrentThread(), &thread_description);\n  CHECK(SUCCEEDED(hr)) << \"GetThreadDescription: \"\n                       << logging::SystemErrorCodeToString(hr);\n  ScopedLocalAlloc thread_description_owner(thread_description);\n  return std::wstring(thread_description);\n}\n\nvoid SetCurrentThreadName(const std::wstring& new_thread_name) {\n  const auto set_thread_description = SetThreadDescriptionFuncPtr();\n  DCHECK(set_thread_description);\n  HRESULT hr =\n      set_thread_description(GetCurrentThread(), new_thread_name.c_str());\n  CHECK(SUCCEEDED(hr)) << \"SetThreadDescription: \"\n                       << logging::SystemErrorCodeToString(hr);\n}\n\n}  // namespace\n\nScopedSetThreadName::ScopedSetThreadName(const std::string& new_thread_name)\n    : original_name_() {\n  if (IsSupported()) {\n    original_name_.assign(GetCurrentThreadName());\n    SetCurrentThreadName(base::UTF8ToWide(new_thread_name));\n  }\n}\n\nScopedSetThreadName::~ScopedSetThreadName() {\n  if (IsSupported()) {\n    SetCurrentThreadName(original_name_);\n  }\n}\n\n// static\nbool ScopedSetThreadName::IsSupported() {\n  return GetThreadDescriptionFuncPtr() && SetThreadDescriptionFuncPtr();\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_temp_dir.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_temp_dir.h\"\n\n#include \"gtest/gtest.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n\nnamespace crashpad {\nnamespace test {\n\nScopedTempDir::ScopedTempDir() : path_(CreateTemporaryDirectory()) {\n}\n\nScopedTempDir::~ScopedTempDir() {\n  RecursivelyDeleteTemporaryDirectory(path());\n}\n\n// static\nvoid ScopedTempDir::RecursivelyDeleteTemporaryDirectory(\n    const base::FilePath& path) {\n  DirectoryReader reader;\n  ASSERT_TRUE(reader.Open(path));\n  DirectoryReader::Result result;\n  base::FilePath entry;\n  while ((result = reader.NextFile(&entry)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath entry_path(path.Append(entry));\n    if (IsDirectory(entry_path, false)) {\n      RecursivelyDeleteTemporaryDirectory(entry_path);\n    } else {\n      EXPECT_TRUE(LoggingRemoveFile(entry_path));\n    }\n  }\n  EXPECT_EQ(result, DirectoryReader::Result::kNoMoreFiles);\n  EXPECT_TRUE(LoggingRemoveDirectory(path));\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_temp_dir.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_SCOPED_TEMP_DIR_\n#define CRASHPAD_TEST_SCOPED_TEMP_DIR_\n\n#include \"base/files/file_path.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief A RAII object that creates a temporary directory for testing.\n//!\n//! Upon construction, a temporary directory will be created. Failure to create\n//! the directory is fatal. On destruction, the directory and all its contents\n//! will be removed.\nclass ScopedTempDir {\n public:\n  ScopedTempDir();\n\n  ScopedTempDir(const ScopedTempDir&) = delete;\n  ScopedTempDir& operator=(const ScopedTempDir&) = delete;\n\n  ~ScopedTempDir();\n\n  //! \\brief Returns the path of the temporary directory.\n  //!\n  //! \\return The temporary directory path.\n  const base::FilePath& path() const { return path_; }\n\n  //! \\brief Moves the temporary directory to a new temporary location.\n  void Rename();\n\n private:\n  //! \\brief Creates the temporary directory and asserts success of the\n  //!     operation.\n  static base::FilePath CreateTemporaryDirectory();\n\n  //! \\brief Removes all files and subdirectories at the given \\a path,\n  //!     including the \\a path itself.\n  //!\n  //! Failures are recorded by Google Test expectations.\n  //!\n  //! \\param[in] path The path to delete, along with its contents. This must\n  //!     reference a directory.\n  static void RecursivelyDeleteTemporaryDirectory(const base::FilePath& path);\n\n  base::FilePath path_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_SCOPED_TEMP_DIR_\n"
  },
  {
    "path": "test/scoped_temp_dir_posix.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_temp_dir.h\"\n\n#include <dirent.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <string>\n\n#include \"base/check.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n\nnamespace crashpad {\nnamespace test {\n\nvoid ScopedTempDir::Rename() {\n  base::FilePath move_to = CreateTemporaryDirectory();\n  PCHECK(rename(path_.value().c_str(), move_to.value().c_str()) == 0);\n  path_ = move_to;\n}\n\n// static\nbase::FilePath ScopedTempDir::CreateTemporaryDirectory() {\n  char* tmpdir = getenv(\"TMPDIR\");\n  std::string dir;\n  if (tmpdir && tmpdir[0] != '\\0') {\n    dir.assign(tmpdir);\n  } else {\n#if BUILDFLAG(IS_ANDROID)\n    dir.assign(\"/data/local/tmp\");\n#else\n    dir.assign(\"/tmp\");\n#endif\n  }\n\n  if (dir[dir.size() - 1] != '/') {\n    dir.append(1, '/');\n  }\n  dir.append(\"org.chromium.crashpad.test.XXXXXX\");\n\n  PCHECK(mkdtemp(&dir[0])) << \"mkdtemp \" << dir;\n  return base::FilePath(dir);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_temp_dir_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_temp_dir.h\"\n\n#include <fcntl.h>\n#include <string.h>\n\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/file.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include <unistd.h>\n#elif BUILDFLAG(IS_WIN)\n#include <direct.h>\n#include <io.h>\n#endif  // BUILDFLAG(IS_POSIX)\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid CreateFile(const base::FilePath& path) {\n#if BUILDFLAG(IS_POSIX)\n  int fd = HANDLE_EINTR(creat(path.value().c_str(), 0644));\n  ASSERT_GE(fd, 0) << ErrnoMessage(\"creat\") << \" \" << path.value();\n\n  // gcc refuses to compile ASSERT_EQ(IGNORE_EINTR(close(fd)), 0).\n  int close_rv = IGNORE_EINTR(close(fd));\n  ASSERT_EQ(close_rv, 0) << ErrnoMessage(\"close\") << \" \" << path.value();\n#elif BUILDFLAG(IS_WIN)\n  int fd = _wcreat(path.value().c_str(), _S_IREAD | _S_IWRITE);\n  ASSERT_GE(fd, 0) << ErrnoMessage(\"_wcreat\") << \" \" << path.value();\n  ASSERT_EQ(_close(fd), 0) << ErrnoMessage(\"_close\") << \" \" << path.value();\n#else\n#error \"Not implemented\"\n#endif\n  EXPECT_TRUE(FileExists(path));\n}\n\nvoid CreateDirectory(const base::FilePath& path) {\n#if BUILDFLAG(IS_POSIX)\n  ASSERT_EQ(mkdir(path.value().c_str(), 0755), 0) << ErrnoMessage(\"mkdir\")\n                                                  << \" \" << path.value();\n#elif BUILDFLAG(IS_WIN)\n  ASSERT_EQ(_wmkdir(path.value().c_str()), 0) << ErrnoMessage(\"_wmkdir\") << \" \"\n                                              << path.value();\n#else\n#error \"Not implemented\"\n#endif\n  ASSERT_TRUE(FileExists(path));\n}\n\nTEST(ScopedTempDir, Empty) {\n  base::FilePath path;\n  {\n    ScopedTempDir dir;\n    path = dir.path();\n    EXPECT_TRUE(FileExists(path));\n  }\n  EXPECT_FALSE(FileExists(path));\n}\n\nTEST(ScopedTempDir, WithTwoFiles) {\n  base::FilePath parent, file1, file2;\n\n  {\n    ScopedTempDir dir;\n    parent = dir.path();\n    ASSERT_TRUE(FileExists(parent));\n\n    file1 = parent.Append(FILE_PATH_LITERAL(\"test1\"));\n    CreateFile(file1);\n\n    file2 = parent.Append(FILE_PATH_LITERAL(\"test 2\"));\n    CreateFile(file2);\n  }\n\n  EXPECT_FALSE(FileExists(file1));\n  EXPECT_FALSE(FileExists(file2));\n  EXPECT_FALSE(FileExists(parent));\n}\n\nTEST(ScopedTempDir, WithRecursiveDirectory) {\n  base::FilePath parent, file1, child_dir, file2;\n\n  {\n    ScopedTempDir dir;\n    parent = dir.path();\n    ASSERT_TRUE(FileExists(parent));\n\n    file1 = parent.Append(FILE_PATH_LITERAL(\".first-level file\"));\n    CreateFile(file1);\n\n    child_dir = parent.Append(FILE_PATH_LITERAL(\"subdir\"));\n    CreateDirectory(child_dir);\n\n    file2 = child_dir.Append(FILE_PATH_LITERAL(\"second level file\"));\n    CreateFile(file2);\n  }\n\n  EXPECT_FALSE(FileExists(file1));\n  EXPECT_FALSE(FileExists(file2));\n  EXPECT_FALSE(FileExists(child_dir));\n  EXPECT_FALSE(FileExists(parent));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/scoped_temp_dir_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/scoped_temp_dir.h\"\n\n#include <wchar.h>\n#include <windows.h>\n\n#include <string>\n\n#include \"base/check.h\"\n#include \"base/notreached.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"util/misc/random_string.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nbase::FilePath GenerateCandidateName() {\n  wchar_t temp_path[MAX_PATH + 1];\n  DWORD path_len = GetTempPath(MAX_PATH, temp_path);\n  PCHECK(path_len != 0) << \"GetTempPath\";\n  base::FilePath system_temp_dir(temp_path);\n  std::wstring new_dir_name = base::UTF8ToWide(base::StringPrintf(\n      \"crashpad.test.%lu.%s\", GetCurrentProcessId(), RandomString().c_str()));\n  return system_temp_dir.Append(new_dir_name);\n}\n\nconstexpr int kRetries = 50;\n\n}  // namespace\n\nvoid ScopedTempDir::Rename() {\n  for (int count = 0; count < kRetries; ++count) {\n    // Try to move to a new temporary directory with a randomly generated name.\n    // If the one we try exists, retry with a new name until we reach some\n    // limit.\n    base::FilePath target_path = GenerateCandidateName();\n    if (MoveFileEx(path_.value().c_str(), target_path.value().c_str(), 0)) {\n      path_ = target_path;\n      return;\n    }\n  }\n\n  NOTREACHED() << \"Couldn't move to a new unique temp dir\";\n}\n\n// static\nbase::FilePath ScopedTempDir::CreateTemporaryDirectory() {\n  for (int count = 0; count < kRetries; ++count) {\n    // Try to create a new temporary directory with random generated name. If\n    // the one we generate exists, keep trying another path name until we reach\n    // some limit.\n    base::FilePath path_to_create = GenerateCandidateName();\n    if (CreateDirectory(path_to_create.value().c_str(), nullptr))\n      return path_to_create;\n  }\n\n  NOTREACHED() << \"Couldn't create a new unique temp dir\";\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/test_paths.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/test_paths.h\"\n\n#include <stdlib.h>\n#include <sys/stat.h>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/paths.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nbool IsTestDataRoot(const base::FilePath& candidate) {\n  const base::FilePath marker_path =\n      candidate.Append(FILE_PATH_LITERAL(\"test\"))\n          .Append(FILE_PATH_LITERAL(\"test_paths_test_data_root.txt\"));\n\n#if !BUILDFLAG(IS_WIN)\n  struct stat stat_buf;\n  int rv = stat(marker_path.value().c_str(), &stat_buf);\n#else\n  struct _stat stat_buf;\n  int rv = _wstat(marker_path.value().c_str(), &stat_buf);\n#endif\n\n  return rv == 0;\n}\n\nbase::FilePath TestDataRootInternal() {\n#if BUILDFLAG(IS_FUCHSIA)\n  base::FilePath asset_path(\"/pkg/data\");\n  if (!IsTestDataRoot(asset_path)) {\n    LOG(WARNING) << \"test data root seems invalid, continuing anyway\";\n  }\n  return asset_path;\n#else  // BUILDFLAG(IS_FUCHSIA)\n#if !BUILDFLAG(IS_WIN)\n  const char* environment_value = getenv(\"CRASHPAD_TEST_DATA_ROOT\");\n#else  // BUILDFLAG(IS_WIN)\n  const wchar_t* environment_value = _wgetenv(L\"CRASHPAD_TEST_DATA_ROOT\");\n#endif\n\n  if (environment_value) {\n    // It was specified explicitly, so use it even if it seems incorrect.\n    if (!IsTestDataRoot(base::FilePath(environment_value))) {\n      LOG(WARNING) << \"CRASHPAD_TEST_DATA_ROOT seems invalid, honoring anyway\";\n    }\n\n    return base::FilePath(environment_value);\n  }\n\n  base::FilePath executable_path;\n  if (Paths::Executable(&executable_path)) {\n#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)\n    // On Android and iOS, test data is in a crashpad_test_data directory\n    // adjacent to the main executable. On iOS, this refers to the main\n    // executable file inside the .app bundle, so crashpad_test_data is also\n    // inside the bundle.\n    base::FilePath candidate = executable_path.DirName()\n                               .Append(\"crashpad_test_data\");\n#else  // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDRID)\n    // In a standalone build, the test executable is usually at\n    // out/{Debug,Release} relative to the Crashpad root.\n    base::FilePath candidate =\n        base::FilePath(executable_path.DirName()\n                           .Append(base::FilePath::kParentDirectory)\n                           .Append(base::FilePath::kParentDirectory));\n#endif  // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)\n    if (IsTestDataRoot(candidate)) {\n      return candidate;\n    }\n\n    // In an in-Chromium build, the test executable is usually at\n    // out/{Debug,Release} relative to the Chromium root, and the Crashpad root\n    // is at third_party/crashpad/crashpad relative to the Chromium root.\n    candidate = candidate.Append(FILE_PATH_LITERAL(\"third_party\"))\n                    .Append(FILE_PATH_LITERAL(\"crashpad\"))\n                    .Append(FILE_PATH_LITERAL(\"crashpad\"));\n    if (IsTestDataRoot(candidate)) {\n      return candidate;\n    }\n  }\n\n  // If nothing else worked, use the current directory, issuing a warning if it\n  // doesn’t seem right.\n  if (!IsTestDataRoot(base::FilePath(base::FilePath::kCurrentDirectory))) {\n    LOG(WARNING) << \"could not locate a valid test data root\";\n  }\n\n  return base::FilePath(base::FilePath::kCurrentDirectory);\n#endif  // BUILDFLAG(IS_FUCHSIA)\n}\n\n#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)\n\n// Returns the pathname of a directory containing 32-bit test build output.\n//\n// It would be better for this to be named 32BitOutputDirectory(), but that’s\n// not a legal name.\nbase::FilePath Output32BitDirectory() {\n  const wchar_t* environment_value = _wgetenv(L\"CRASHPAD_TEST_32_BIT_OUTPUT\");\n  if (!environment_value) {\n    return base::FilePath();\n  }\n\n  return base::FilePath(environment_value);\n}\n\n#endif  // BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)\n\n}  // namespace\n\n// static\nbase::FilePath TestPaths::Executable() {\n  base::FilePath executable_path;\n  CHECK(Paths::Executable(&executable_path));\n#if defined(CRASHPAD_IS_IN_FUCHSIA)\n  executable_path = base::FilePath(\"/pkg/bin/app\");\n#endif\n  return executable_path;\n}\n\n// static\nbase::FilePath TestPaths::ExpectedExecutableBasename(\n    const base::FilePath::StringType& name) {\n#if BUILDFLAG(IS_FUCHSIA)\n  // Apps in Fuchsia packages are always named \"app\".\n  return base::FilePath(\"app\");\n#else  // BUILDFLAG(IS_FUCHSIA)\n#if defined(CRASHPAD_IS_IN_CHROMIUM)\n  base::FilePath::StringType executable_name(\n      FILE_PATH_LITERAL(\"crashpad_tests\"));\n#else  // CRASHPAD_IS_IN_CHROMIUM\n  base::FilePath::StringType executable_name(name);\n#endif  // CRASHPAD_IS_IN_CHROMIUM\n\n#if BUILDFLAG(IS_WIN)\n  executable_name += FILE_PATH_LITERAL(\".exe\");\n#endif  // BUILDFLAG(IS_WIN)\n\n  return base::FilePath(executable_name);\n#endif  // BUILDFLAG(IS_FUCHSIA)\n}\n\n// static\nbase::FilePath TestPaths::TestDataRoot() {\n  static base::FilePath* test_data_root =\n      new base::FilePath(TestDataRootInternal());\n  return *test_data_root;\n}\n\n// static\nbase::FilePath TestPaths::BuildArtifact(\n    const base::FilePath::StringType& module,\n    const base::FilePath::StringType& artifact,\n    FileType file_type,\n    Architecture architecture) {\n  base::FilePath directory;\n  switch (architecture) {\n    case Architecture::kDefault:\n      directory = Executable().DirName();\n      break;\n\n#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)\n    case Architecture::k32Bit:\n      directory = Output32BitDirectory();\n      CHECK(!directory.empty());\n      break;\n#endif  // BUILDFLAG(IS_WIN) && ARCH_CPU_64_BITS\n  }\n\n  base::FilePath::StringType test_name =\n      FILE_PATH_LITERAL(\"crashpad_\") + module + FILE_PATH_LITERAL(\"_test\");\n#if !defined(CRASHPAD_IS_IN_CHROMIUM) && !BUILDFLAG(IS_FUCHSIA)\n  CHECK(Executable().BaseName().RemoveFinalExtension().value() == test_name);\n#endif  // !CRASHPAD_IS_IN_CHROMIUM\n\n  base::FilePath::StringType extension;\n  switch (file_type) {\n    case FileType::kNone:\n      break;\n\n    case FileType::kExecutable:\n#if BUILDFLAG(IS_WIN)\n      extension = FILE_PATH_LITERAL(\".exe\");\n#elif BUILDFLAG(IS_FUCHSIA)\n      directory = base::FilePath(FILE_PATH_LITERAL(\"/pkg/bin\"));\n#endif  // BUILDFLAG(IS_WIN)\n      break;\n\n    case FileType::kLoadableModule:\n#if BUILDFLAG(IS_WIN)\n      extension = FILE_PATH_LITERAL(\".dll\");\n#else  // BUILDFLAG(IS_WIN)\n      extension = FILE_PATH_LITERAL(\".so\");\n#endif  // BUILDFLAG(IS_WIN)\n\n#if BUILDFLAG(IS_FUCHSIA)\n      // TODO(scottmg): .so files are currently deployed into /boot/lib, where\n      // they'll be found (without a path) by the loader. Application packaging\n      // infrastructure is in progress, so this will likely change again in the\n      // future.\n      directory = base::FilePath();\n#endif\n      break;\n\n    case FileType::kCertificate:\n#if defined(CRASHPAD_IS_IN_FUCHSIA)\n      directory = base::FilePath(FILE_PATH_LITERAL(\"/pkg/data\"));\n#endif\n      extension = FILE_PATH_LITERAL(\".pem\");\n      break;\n  }\n\n  return directory.Append(test_name + FILE_PATH_LITERAL(\"_\") + artifact +\n                          extension);\n}\n\n#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)\n\n// static\nbool TestPaths::Has32BitBuildArtifacts() {\n  return !Output32BitDirectory().empty();\n}\n\n#endif  // BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/test_paths.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_TEST_PATHS_H_\n#define CRASHPAD_TEST_TEST_PATHS_H_\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Functions to obtain paths from within tests.\nclass TestPaths {\n public:\n  //! \\brief The type of file requested of BuildArtifact().\n  //!\n  //! This is used to establish the file extension used by the returned path.\n  enum class FileType {\n    //! \\brief No file extension is requested.\n    kNone = 0,\n\n    //! \\brief `.exe` will be used on Windows, and no file extension will be\n    //!     used on other platforms.\n    kExecutable,\n\n    //! \\brief `.dll` will be used on Windows, and `.so` will be used on other\n    //!     platforms.\n    kLoadableModule,\n\n    //! \\brief `.pem` used for all platforms.\n    kCertificate,\n  };\n\n  //! \\brief The architecture of the file requested of BuildArtifact().\n  enum class Architecture {\n    //! \\brief The default architecture is requested. This is usually the same\n    //!     architecture as the running process.\n    kDefault = 0,\n\n#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)) || DOXYGEN\n    //! \\brief The 32-bit variant is requested.\n    //!\n    //! On Windows, when running 64-bit code, the 32-bit variant can be\n    //! requested. Before doing so, Has32BitBuildArtifacts() must be called and\n    //! must return `true`. Otherwise, execution will be aborted.\n    k32Bit,\n#endif  // BUILDFLAG(IS_WIN) && ARCH_CPU_64_BITS\n  };\n\n  TestPaths() = delete;\n  TestPaths(const TestPaths&) = delete;\n  TestPaths& operator=(const TestPaths&) = delete;\n\n  //! \\brief Returns the pathname of the currently-running test executable.\n  //!\n  //! On failure, aborts execution.\n  static base::FilePath Executable();\n\n  //! \\brief Returns the expected basename of the currently-running test\n  //!     executable.\n  //!\n  //! In Crashpad’s standalone build, this returns \\a name, with the system’s\n  //! extension for executables (`.exe`) appended if appropriate.\n  //!\n  //! When building in Chromium, \\a name is ignored, and the name of the\n  //! monolithic test executable (`crashpad_tests`) is returned, with the\n  //! system’s extension for executables appended if appropriate.\n  //!\n  //! Only use this function to determine test expectations.\n  //!\n  //! Do not use this function to obtain the name of the currently running test\n  //! executable, use Executable() instead. Do not use this function to locate\n  //! other build artifacts, use BuildArtifact() instead.\n  static base::FilePath ExpectedExecutableBasename(\n      const base::FilePath::StringType& name);\n\n  //! \\brief Returns the pathname of the test data root.\n  //!\n  //! If the `CRASHPAD_TEST_DATA_ROOT` environment variable is set, its value\n  //! will be returned. Otherwise, this function will attempt to locate the test\n  //! data root relative to the executable path. If this fails, it will fall\n  //! back to returning the current working directory.\n  //!\n  //! At present, the test data root is normally the root of the Crashpad source\n  //! tree, although this may not be the case indefinitely. This function may\n  //! only be used to locate test data, not for arbitrary access to source\n  //! files.\n  static base::FilePath TestDataRoot();\n\n  //! \\brief Returns the pathname of a build artifact.\n  //!\n  //! \\param[in] module The name of the Crashpad module associated with the\n  //!     artifact, such as `\"util\"` or `\"snapshot\"`. \\a module must correspond\n  //!     to the module of the calling code, or execution will be aborted.\n  //! \\param[in] artifact The name of the specific artifact.\n  //! \\param[in] file_type The artifact’s type, used to establish the returned\n  //!     path’s extension.\n  //! \\param[in] architecture The artifact’s architecture.\n  //!\n  //! \\return The computed pathname to the build artifact.\n  //!\n  //! For example, the following snippet will return a path to\n  //! `crashpad_snapshot_test_module.so` or `crashpad_snapshot_test_module.dll`\n  //! (depending on platform) in the same directory as the currently running\n  //! executable:\n  //!\n  //! \\code\n  //!    base::FilePath path = TestPaths::BuildArtifact(\n  //!        FILE_PATH_LITERAL(\"snapshot\"),\n  //!        FILE_PATH_LITERAL(\"module\"),\n  //!        TestPaths::FileType::kLoadableModule);\n  //! \\endcode\n  static base::FilePath BuildArtifact(\n      const base::FilePath::StringType& module,\n      const base::FilePath::StringType& artifact,\n      FileType file_type,\n      Architecture architecture = Architecture::kDefault);\n\n#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)) || DOXYGEN\n  //! \\return `true` if 32-bit build artifacts are available.\n  //!\n  //! Tests that require the use of 32-bit build output should call this\n  //! function to determine whether that output is available. This function is\n  //! only provided to aid 64-bit test code in locating 32-bit output. Only if\n  //! this function indicates that 32-bit output is available, 64-bit test code\n  //! may call BuildArtifact() with Architecture::k32Bit to obtain a path to the\n  //! 32-bit output.\n  //!\n  //! 32-bit test code may assume the existence of 32-bit build output, which\n  //! can be found its own directory, and located by calling BuildArtifact()\n  //! with Architecture::kDefault.\n  static bool Has32BitBuildArtifacts();\n#endif  // BUILDFLAG(IS_WIN) && ARCH_CPU_64_BITS\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_TEST_PATHS_H_\n"
  },
  {
    "path": "test/test_paths_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/test_paths.h\"\n\n#include \"base/files/file_path.h\"\n#include \"gtest/gtest.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(TestPaths, TestDataRoot) {\n  base::FilePath test_data_root = TestPaths::TestDataRoot();\n  ScopedFileHandle file(LoggingOpenFileForRead(\n      test_data_root.Append(FILE_PATH_LITERAL(\"test\"))\n          .Append(FILE_PATH_LITERAL(\"test_paths_test_data_root.txt\"))));\n  EXPECT_TRUE(file.is_valid());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/test_paths_test_data_root.txt",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nThis file is used by TestPaths::TestDataRoot() to locate the test data root. It\nis present at a known path from the test data root.\n"
  },
  {
    "path": "test/win/child_launcher.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/win/child_launcher.h\"\n\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"util/win/command_line.h\"\n\nnamespace crashpad {\nnamespace test {\n\nChildLauncher::ChildLauncher(const base::FilePath& executable,\n                             const std::wstring& command_line)\n    : executable_(executable),\n      command_line_(command_line),\n      process_handle_(),\n      main_thread_handle_(),\n      stdout_read_handle_(),\n      stdin_write_handle_() {}\n\nChildLauncher::~ChildLauncher() {\n  if (process_handle_.is_valid())\n    WaitForExit();\n}\n\nvoid ChildLauncher::Start() {\n  ASSERT_FALSE(process_handle_.is_valid());\n  ASSERT_FALSE(main_thread_handle_.is_valid());\n  ASSERT_FALSE(stdout_read_handle_.is_valid());\n\n  // Create pipes for the stdin/stdout of the child.\n  SECURITY_ATTRIBUTES security_attributes = {0};\n  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\n  security_attributes.bInheritHandle = true;\n\n  HANDLE stdout_read;\n  HANDLE stdout_write;\n  ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0))\n      << ErrorMessage(\"CreatePipe\");\n  stdout_read_handle_.reset(stdout_read);\n  ScopedFileHANDLE write_handle(stdout_write);\n  ASSERT_TRUE(\n      SetHandleInformation(stdout_read_handle_.get(), HANDLE_FLAG_INHERIT, 0))\n      << ErrorMessage(\"SetHandleInformation\");\n\n  HANDLE stdin_read;\n  HANDLE stdin_write;\n  ASSERT_TRUE(CreatePipe(&stdin_read, &stdin_write, &security_attributes, 0))\n      << ErrorMessage(\"CreatePipe\");\n  stdin_write_handle_.reset(stdin_write);\n  ScopedFileHANDLE read_handle(stdin_read);\n  ASSERT_TRUE(\n      SetHandleInformation(stdin_write_handle_.get(), HANDLE_FLAG_INHERIT, 0))\n      << ErrorMessage(\"SetHandleInformation\");\n\n  STARTUPINFO startup_info = {0};\n  startup_info.cb = sizeof(startup_info);\n  startup_info.hStdInput = read_handle.get();\n  startup_info.hStdOutput = write_handle.get();\n  startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);\n  EXPECT_NE(startup_info.hStdError, INVALID_HANDLE_VALUE)\n      << ErrorMessage(\"GetStdHandle\");\n  startup_info.dwFlags = STARTF_USESTDHANDLES;\n  PROCESS_INFORMATION process_information;\n  std::wstring command_line;\n  AppendCommandLineArgument(executable_.value(), &command_line);\n  command_line += L\" \";\n  command_line += command_line_;\n  ASSERT_TRUE(CreateProcess(executable_.value().c_str(),\n                            &command_line[0],\n                            nullptr,\n                            nullptr,\n                            true,\n                            0,\n                            nullptr,\n                            nullptr,\n                            &startup_info,\n                            &process_information))\n      << ErrorMessage(\"CreateProcess\");\n  // Take ownership of the two process handles returned.\n  main_thread_handle_.reset(process_information.hThread);\n  process_handle_.reset(process_information.hProcess);\n}\n\nDWORD ChildLauncher::WaitForExit() {\n  EXPECT_TRUE(process_handle_.is_valid());\n  EXPECT_EQ(WaitForSingleObject(process_handle_.get(), INFINITE), WAIT_OBJECT_0)\n      << ErrorMessage(\"WaitForSingleObject\");\n  DWORD exit_code = 0;\n  EXPECT_TRUE(GetExitCodeProcess(process_handle_.get(), &exit_code))\n      << ErrorMessage(\"GetExitCodeProcess\");\n  process_handle_.reset();\n  return exit_code;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/win/child_launcher.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_\n#define CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_\n\n#include <windows.h>\n\n#include <string>\n\n#include \"base/files/file_path.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Creates a child process for testing. Uses Google Test `ASSERT_*` to\n//!     indicate failure. The child's output is passed through a pipe and is\n//!     available via stdout_read_handle(), and the child's input is attached to\n//!     a second pipe available via stdin_write_handle().\nclass ChildLauncher {\n public:\n  //! \\brief Creates the object. \\a executable will be escaped and prepended to\n  //!     \\a command_line to build the command line of the child.\n  ChildLauncher(const base::FilePath& executable,\n                const std::wstring& command_line);\n\n  ~ChildLauncher();\n\n  //! \\brief Starts the child process, after which the handle functions below\n  //!     will be valid.\n  //!\n  //! Errors are signaled via Google Test assertions. This method may be invoked\n  //! via `ASSERT_NO_FATAL_FAILURE()` to assert that it succeeds.\n  void Start();\n\n  //! \\brief Waits for the child process to exit.\n  //!\n  //! \\return The process exit code.\n  DWORD WaitForExit();\n\n  //! \\brief The child process's `HANDLE`.\n  HANDLE process_handle() const { return process_handle_.get(); }\n\n  //! \\brief The child process's main thread's `HANDLE`.\n  HANDLE main_thread_handle() const { return main_thread_handle_.get(); }\n\n  //! \\brief The read end of a pipe attached to the child's stdout.\n  HANDLE stdout_read_handle() const { return stdout_read_handle_.get(); }\n\n  //! \\brief The write end of a pipe attached to the child's stdin.\n  HANDLE stdin_write_handle() const { return stdin_write_handle_.get(); }\n\n private:\n  base::FilePath executable_;\n  std::wstring command_line_;\n  ScopedKernelHANDLE process_handle_;\n  ScopedKernelHANDLE main_thread_handle_;\n  ScopedFileHANDLE stdout_read_handle_;\n  ScopedFileHANDLE stdin_write_handle_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_\n"
  },
  {
    "path": "test/win/win_child_process.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/win/win_child_process.h\"\n\n#include <windows.h>\n#include <shellapi.h>\n\n#include <string>\n#include <utility>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"test/test_paths.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n#include \"util/string/split_string.h\"\n#include \"util/win/handle.h\"\n#include \"util/win/scoped_local_alloc.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nconstexpr char kIsMultiprocessChild[] = \"--is-multiprocess-child\";\n\nbool GetSwitch(const char* switch_name, std::string* value) {\n  int num_args;\n  wchar_t** args = CommandLineToArgvW(GetCommandLine(), &num_args);\n  ScopedLocalAlloc scoped_args(args);  // Take ownership.\n  if (!args) {\n    PLOG(FATAL) << \"CommandLineToArgvW\";\n  }\n\n  std::string switch_name_with_equals(switch_name);\n  switch_name_with_equals += \"=\";\n  for (int i = 1; i < num_args; ++i) {\n    const wchar_t* arg = args[i];\n    std::string arg_as_utf8 = base::WideToUTF8(arg);\n    if (arg_as_utf8.compare(\n            0, switch_name_with_equals.size(), switch_name_with_equals) == 0) {\n      if (value)\n        *value = arg_as_utf8.substr(switch_name_with_equals.size());\n      return true;\n    }\n  }\n\n  return false;\n}\n\nScopedKernelHANDLE LaunchCommandLine(wchar_t* command_line) {\n  STARTUPINFO startup_info = {0};\n  startup_info.cb = sizeof(startup_info);\n  startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);\n  startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);\n  startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);\n  startup_info.dwFlags = STARTF_USESTDHANDLES;\n  PROCESS_INFORMATION process_info;\n  if (!CreateProcess(TestPaths::Executable().value().c_str(),\n                     &command_line[0],  // This cannot be constant, per MSDN.\n                     nullptr,\n                     nullptr,\n                     true,  // Inherit handles.\n                     0,\n                     nullptr,\n                     nullptr,\n                     &startup_info,\n                     &process_info)) {\n    PLOG(ERROR) << \"CreateProcess\";\n    return ScopedKernelHANDLE();\n  }\n  if (!CloseHandle(process_info.hThread)) {\n    PLOG(ERROR) << \"CloseHandle\";\n    if (!CloseHandle(process_info.hProcess))\n      PLOG(ERROR) << \"CloseHandle\";\n    return ScopedKernelHANDLE();\n  }\n  return ScopedKernelHANDLE(process_info.hProcess);\n}\n\nbool UnsetHandleInheritance(HANDLE handle) {\n  if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0)) {\n    PLOG(ERROR) << \"SetHandleInformation\";\n    ADD_FAILURE() << \"SetHandleInformation\";\n    return false;\n  }\n  return true;\n}\n\nbool CreateInheritablePipe(ScopedFileHANDLE* read_handle,\n                           bool read_inheritable,\n                           ScopedFileHANDLE* write_handle,\n                           bool write_inheritable) {\n  // Mark both sides as inheritable via the SECURITY_ATTRIBUTES and use\n  // SetHandleInformation as necessary to restrict inheritance of either side.\n  SECURITY_ATTRIBUTES security_attributes = {0};\n  security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\n  security_attributes.bInheritHandle = true;\n\n  HANDLE read, write;\n  BOOL result = CreatePipe(&read, &write, &security_attributes, 0);\n  if (!result) {\n    PLOG(ERROR) << \"CreatePipe\";\n    ADD_FAILURE() << \"CreatePipe failed\";\n    return false;\n  }\n  ScopedFileHANDLE temp_read(read);\n  ScopedFileHANDLE temp_write(write);\n\n  if (!read_inheritable && !UnsetHandleInheritance(temp_read.get()))\n    return false;\n  if (!write_inheritable && !UnsetHandleInheritance(temp_write.get()))\n    return false;\n\n  *read_handle = std::move(temp_read);\n  *write_handle = std::move(temp_write);\n\n  return true;\n}\n\n}  // namespace\n\nWinChildProcess::WinChildProcess() {\n  std::string switch_value;\n  CHECK(GetSwitch(kIsMultiprocessChild, &switch_value));\n\n  // Set up the handles we inherited from the parent. These are inherited from\n  // the parent and so are open and have the same value as in the parent. The\n  // values are passed to the child on the command line.\n  std::string left, right;\n  CHECK(SplitStringFirst(switch_value, '|', &left, &right));\n\n  // left and right were formatted as 0x%x, so they need to be converted as\n  // unsigned ints.\n  unsigned int write, read;\n  CHECK(StringToNumber(left, &write));\n  CHECK(StringToNumber(right, &read));\n\n  pipe_write_.reset(IntToHandle(write));\n  pipe_read_.reset(IntToHandle(read));\n\n  // Notify the parent that it's OK to proceed. We only need to wait to get to\n  // the process entry point, but this is the easiest place we can notify.\n  char c = ' ';\n  CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));\n}\n\n// static\nbool WinChildProcess::IsChildProcess() {\n  return GetSwitch(kIsMultiprocessChild, nullptr);\n}\n\n// static\nstd::unique_ptr<WinChildProcess::Handles> WinChildProcess::Launch() {\n  // Make pipes for child-to-parent and parent-to-child communication.\n  std::unique_ptr<Handles> handles_for_parent(new Handles);\n  ScopedFileHANDLE read_for_child;\n  ScopedFileHANDLE write_for_child;\n\n  if (!CreateInheritablePipe(\n          &handles_for_parent->read, false, &write_for_child, true)) {\n    return std::unique_ptr<Handles>();\n  }\n\n  if (!CreateInheritablePipe(\n          &read_for_child, true, &handles_for_parent->write, false)) {\n    return std::unique_ptr<Handles>();\n  }\n\n  // Build a command line for the child process that tells it only to run the\n  // current test, and to pass down the values of the pipe handles. Use\n  // --gtest_also_run_disabled_tests because the test may be DISABLED_, but if\n  // it managed to run in the parent, disabled tests must be running.\n  const ::testing::TestInfo* const test_info =\n      ::testing::UnitTest::GetInstance()->current_test_info();\n  std::wstring command_line =\n      TestPaths::Executable().value() +\n      base::UTF8ToWide(base::StringPrintf(\n          \" --gtest_filter=%s.%s %s=0x%x|0x%x --gtest_also_run_disabled_tests\",\n          test_info->test_suite_name(),\n          test_info->name(),\n          kIsMultiprocessChild,\n          HandleToInt(write_for_child.get()),\n          HandleToInt(read_for_child.get())));\n\n  // Command-line buffer cannot be constant, per CreateProcess signature.\n  handles_for_parent->process = LaunchCommandLine(&command_line[0]);\n  if (!handles_for_parent->process.is_valid())\n    return std::unique_ptr<Handles>();\n\n  // Block until the child process has launched. CreateProcess() returns\n  // immediately, and test code expects process initialization to have\n  // completed so it can, for example, read the process memory.\n  char c;\n  if (!LoggingReadFileExactly(handles_for_parent->read.get(), &c, sizeof(c))) {\n    ADD_FAILURE() << \"LoggedReadFile\";\n    return std::unique_ptr<Handles>();\n  }\n\n  if (c != ' ') {\n    ADD_FAILURE() << \"invalid data read from child\";\n    return std::unique_ptr<Handles>();\n  }\n\n  return handles_for_parent;\n}\n\nFileHandle WinChildProcess::ReadPipeHandle() const {\n  return pipe_read_.get();\n}\n\nFileHandle WinChildProcess::WritePipeHandle() const {\n  return pipe_write_.get();\n}\n\nvoid WinChildProcess::CloseReadPipe() {\n  pipe_read_.reset();\n}\n\nvoid WinChildProcess::CloseWritePipe() {\n  pipe_write_.reset();\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/win/win_child_process.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_\n#define CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_\n\n#include <memory>\n\n#include \"util/file/file_io.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Facilitates the launching of child processes from unit tests.\nclass WinChildProcess {\n public:\n  //! \\brief Groups handles used to communicate with, observe, and manage a\n  //!     child process.\n  struct Handles {\n    //! \\brief A handle to read from an anonymous pipe shared with the child\n    //!     process.\n    ScopedFileHANDLE read;\n    //! \\brief A handle to write to an anonymous pipe shared with the child\n    //!     process.\n    ScopedFileHANDLE write;\n    //! \\brief A handle to the child process.\n    ScopedKernelHANDLE process;\n  };\n\n  WinChildProcess();\n\n  WinChildProcess(const WinChildProcess&) = delete;\n  WinChildProcess& operator=(const WinChildProcess&) = delete;\n\n  virtual ~WinChildProcess() {}\n\n  //! \\brief Returns true if the current process is a child process.\n  static bool IsChildProcess();\n\n  //! \\brief Runs the child process defined by T if the current process is a\n  //!     child process; does not return in that case. Otherwise, returns.\n  template <class T>\n  static void EntryPoint() {\n    if (IsChildProcess()) {\n      // The static_cast here will cause a compiler failure if T is not a\n      // subclass of WinChildProcess. It's the constructor of WinChildProcess\n      // that performs the pipe handshake with the parent process (without which\n      // we would have a hang).\n      T child_process;\n      int result = static_cast<WinChildProcess*>(&child_process)->Run();\n      exit(result);\n    }\n  }\n\n  //! \\brief Launches a child process and returns the Handles for that process.\n  //!     The process is guaranteed to be executing by the time this method\n  //!     returns. Returns `nullptr` and logs a Google Test failure in case of\n  //!     failure.\n  static std::unique_ptr<Handles> Launch();\n\n protected:\n  //! \\brief Returns a handle to read from an anonymous pipe shared with the\n  //!     parent process.\n  //!\n  //! It is an error to call this after CloseReadPipe() has been called.\n  //!\n  //! \\return The read pipe's file handle.\n  FileHandle ReadPipeHandle() const;\n\n  //! \\brief Returns a handle to write to an anonymous pipe shared with the\n  //!     parent process.\n  //!\n  //! It is an error to call this after CloseWritePipe() has been called.\n  //!\n  //! \\return The write pipe's file handle.\n  FileHandle WritePipeHandle() const;\n\n  //! \\brief Closes the read pipe.\n  //!\n  //! ReadPipeHandle() must not be called after this.\n  void CloseReadPipe();\n\n  //! \\brief Closes the write pipe.\n  //!\n  //! An attempt to read from the read pipe in the parent process will indicate\n  //! end-of-file. WritePipeHandle() must not be called after this.\n  void CloseWritePipe();\n\n private:\n  //! \\brief The subclass-provided child routine.\n  //!\n  //! Subclasses must implement this method to define how the child operates.\n  //! Subclasses may exit with a failure status by using `LOG(FATAL)`,\n  //! `abort()`, or similar. They may also exit by returning their exit code\n  //! from this method. It is up to the client to observe and interpret the\n  //! child's exit code.\n  //!\n  //! \\return The child process exit code.\n  virtual int Run() = 0;\n\n  ScopedFileHANDLE pipe_read_;\n  ScopedFileHANDLE pipe_write_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_\n"
  },
  {
    "path": "test/win/win_child_process_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/win/win_child_process.h\"\n\n#include <windows.h>\n#include <stdlib.h>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nint ReadInt(HANDLE handle) {\n  int value = 0;\n  DWORD bytes_read = 0;\n  EXPECT_TRUE(::ReadFile(handle, &value, sizeof(value), &bytes_read, nullptr));\n  EXPECT_EQ(bytes_read, sizeof(value));\n  return value;\n}\n\nvoid WriteInt(HANDLE handle, int value) {\n  DWORD bytes_written = 0;\n  EXPECT_TRUE(\n      ::WriteFile(handle, &value, sizeof(value), &bytes_written, nullptr));\n  EXPECT_EQ(bytes_written, sizeof(value));\n}\n\nclass TestWinChildProcess final : public WinChildProcess {\n public:\n  TestWinChildProcess() : WinChildProcess() {}\n\n  TestWinChildProcess(const TestWinChildProcess&) = delete;\n  TestWinChildProcess& operator=(const TestWinChildProcess&) = delete;\n\n  ~TestWinChildProcess() {}\n\n private:\n  // WinChildProcess will have already exercised the pipes.\n  int Run() override {\n    int value = ReadInt(ReadPipeHandle());\n    WriteInt(WritePipeHandle(), value);\n    return testing::Test::HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS;\n  }\n};\n\nTEST(WinChildProcessTest, WinChildProcess) {\n  WinChildProcess::EntryPoint<TestWinChildProcess>();\n\n  std::unique_ptr<WinChildProcess::Handles> handles = WinChildProcess::Launch();\n  WriteInt(handles->write.get(), 1);\n  ASSERT_EQ(ReadInt(handles->read.get()), 1);\n}\n\nTEST(WinChildProcessTest, MultipleChildren) {\n  WinChildProcess::EntryPoint<TestWinChildProcess>();\n\n  std::unique_ptr<WinChildProcess::Handles> handles_1 =\n      WinChildProcess::Launch();\n  std::unique_ptr<WinChildProcess::Handles> handles_2 =\n      WinChildProcess::Launch();\n  std::unique_ptr<WinChildProcess::Handles> handles_3 =\n      WinChildProcess::Launch();\n\n  WriteInt(handles_1->write.get(), 1);\n  WriteInt(handles_2->write.get(), 2);\n  WriteInt(handles_3->write.get(), 3);\n  ASSERT_EQ(ReadInt(handles_3->read.get()), 3);\n  ASSERT_EQ(ReadInt(handles_2->read.get()), 2);\n  ASSERT_EQ(ReadInt(handles_1->read.get()), 1);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/win/win_multiprocess.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/win/win_multiprocess.h\"\n\n#include <shellapi.h>\n\n#include \"base/check.h\"\n#include \"base/scoped_generic.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n#include \"util/string/split_string.h\"\n\nnamespace crashpad {\nnamespace test {\n\nWinMultiprocess::WinMultiprocess()\n    : exit_code_(EXIT_SUCCESS),\n      child_handles_(nullptr),\n      child_process_helper_(nullptr) {}\n\nWinMultiprocess::~WinMultiprocess() {\n}\n\nvoid WinMultiprocess::SetExpectedChildExitCode(unsigned int exit_code) {\n  exit_code_ = exit_code;\n}\n\nFileHandle WinMultiprocess::ReadPipeHandle() const {\n  if (child_handles_)\n    return child_handles_->read.get();\n  CHECK(child_process_helper_);\n  return child_process_helper_->ReadPipeHandleForwarder();\n}\n\nFileHandle WinMultiprocess::WritePipeHandle() const {\n  if (child_handles_)\n    return child_handles_->write.get();\n  CHECK(child_process_helper_);\n  return child_process_helper_->WritePipeHandleForwarder();\n}\n\nvoid WinMultiprocess::CloseReadPipe() {\n  if (child_handles_) {\n    child_handles_->read.reset();\n  } else {\n    CHECK(child_process_helper_);\n    child_process_helper_->CloseReadPipeForwarder();\n  }\n}\n\nvoid WinMultiprocess::CloseWritePipe() {\n  if (child_handles_) {\n    child_handles_->write.reset();\n  } else {\n    CHECK(child_process_helper_);\n    child_process_helper_->CloseWritePipeForwarder();\n  }\n}\n\nHANDLE WinMultiprocess::ChildProcess() const {\n  CHECK(child_handles_);\n  return child_handles_->process.get();\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/win/win_multiprocess.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_\n#define CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_\n\n#include <windows.h>\n\n#include \"gtest/gtest.h\"\n#include \"test/win/win_child_process.h\"\n#include \"util/file/file_io.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Manages a multiprocess test on Windows.\nclass WinMultiprocess {\n public:\n  WinMultiprocess();\n\n  WinMultiprocess(const WinMultiprocess&) = delete;\n  WinMultiprocess& operator=(const WinMultiprocess&) = delete;\n\n  //! \\brief Runs the test.\n  //!\n  //! This method establishes the testing environment by respawning the process\n  //! as a child with additional flags.\n  //!\n  //! In the parent process, WinMultiprocessParent() is run, and in the child\n  //! WinMultiprocessChild().\n  template <class T>\n  static void Run() {\n    ASSERT_NO_FATAL_FAILURE(\n        WinChildProcess::EntryPoint<ChildProcessHelper<T>>());\n    // If WinChildProcess::EntryPoint returns, we are in the parent process.\n    T parent_process;\n    WinMultiprocess* parent_multiprocess = &parent_process;\n\n    parent_multiprocess->WinMultiprocessParentBeforeChild();\n\n    std::unique_ptr<WinChildProcess::Handles> child_handles =\n        WinChildProcess::Launch();\n    ASSERT_TRUE(child_handles.get());\n    parent_process.child_handles_ = child_handles.get();\n    parent_multiprocess->WinMultiprocessParent();\n\n    // Close our side of the handles now that we're done. The child can\n    // use this to know when it's safe to complete.\n    child_handles->read.reset();\n    child_handles->write.reset();\n\n    // Wait for the child to complete.\n    ASSERT_EQ(WaitForSingleObject(child_handles->process.get(), INFINITE),\n              WAIT_OBJECT_0);\n\n    DWORD exit_code;\n    ASSERT_TRUE(GetExitCodeProcess(child_handles->process.get(), &exit_code));\n    ASSERT_EQ(exit_code, parent_process.exit_code_);\n\n    parent_multiprocess->WinMultiprocessParentAfterChild(\n        child_handles->process.get());\n  }\n\n protected:\n  virtual ~WinMultiprocess();\n\n  //! \\brief Sets the expected exit code of the child process.\n  //!\n  //! The default expected termination code is `EXIT_SUCCESS` (`0`).\n  //!\n  //! \\param[in] exit_code The expected exit status of the child.\n  void SetExpectedChildExitCode(unsigned int exit_code);\n\n  //! \\brief Returns the read pipe's file handle.\n  //!\n  //! This method may be called by either the parent or the child process.\n  //! Anything written to the write pipe in the partner process will appear\n  //! on this file handle in this process.\n  //!\n  //! It is an error to call this after CloseReadPipe() has been called.\n  //!\n  //! \\return The read pipe's file handle.\n  FileHandle ReadPipeHandle() const;\n\n  //! \\brief Returns the write pipe's file handle.\n  //!\n  //! This method may be called by either the parent or the child process.\n  //! Anything written to this file handle in this process will appear on\n  //! the read pipe in the partner process.\n  //!\n  //! It is an error to call this after CloseWritePipe() has been called.\n  //!\n  //! \\return The write pipe's file handle.\n  FileHandle WritePipeHandle() const;\n\n  //! \\brief Closes the read pipe.\n  //!\n  //! This method may be called by either the parent or the child process.\n  //! ReadPipeHandle() must not be called after this.\n  void CloseReadPipe();\n\n  //! \\brief Closes the write pipe.\n  //!\n  //! This method may be called by either the parent or the child process. An\n  //! attempt to read from the read pipe in the partner process will indicate\n  //! end-of-file. WritePipeHandle() must not be called after this.\n  void CloseWritePipe();\n\n  //! \\brief Returns a handle to the child process.\n  //!\n  //! This method may only be called by the parent process.\n  HANDLE ChildProcess() const;\n\n private:\n  // Implements an adapter to provide WinMultiprocess with access to the\n  // anonymous pipe handles from WinChildProcess.\n  class ChildProcessHelperBase : public WinChildProcess {\n   public:\n    ChildProcessHelperBase() {}\n\n    ChildProcessHelperBase(const ChildProcessHelperBase&) = delete;\n    ChildProcessHelperBase& operator=(const ChildProcessHelperBase&) = delete;\n\n    ~ChildProcessHelperBase() override {}\n\n    void CloseWritePipeForwarder() { CloseWritePipe(); }\n    void CloseReadPipeForwarder() { CloseReadPipe(); }\n    FileHandle ReadPipeHandleForwarder() const { return ReadPipeHandle(); }\n    FileHandle WritePipeHandleForwarder() const { return WritePipeHandle(); }\n  };\n\n  // Forwards WinChildProcess::Run to T::WinMultiprocessChild.\n  template <class T>\n  class ChildProcessHelper : public ChildProcessHelperBase {\n   public:\n    ChildProcessHelper() {}\n\n    ChildProcessHelper(const ChildProcessHelper&) = delete;\n    ChildProcessHelper& operator=(const ChildProcessHelper&) = delete;\n\n    ~ChildProcessHelper() override {}\n\n   private:\n    int Run() override {\n      T child_process;\n      child_process.child_process_helper_ = this;\n      static_cast<WinMultiprocess*>(&child_process)->WinMultiprocessChild();\n      if (testing::Test::HasFailure())\n        return 255;\n      return EXIT_SUCCESS;\n    }\n  };\n\n  //! \\brief The subclass-provided parent routine.\n  //!\n  //! Test failures should be reported via Google Test: `EXPECT_*()`,\n  //! `ASSERT_*()`, `FAIL()`, etc.\n  //!\n  //! This method need not use `WaitForSingleObject()`-family call to wait for\n  //! the child process to exit, as this is handled by this class.\n  //!\n  //! Subclasses must implement this method to define how the parent operates.\n  virtual void WinMultiprocessParent() = 0;\n\n  //! \\brief The optional routine run in parent before the child is spawned.\n  //!\n  //! Subclasses may implement this method to prepare the environment for\n  //! the child process.\n  virtual void WinMultiprocessParentBeforeChild() {}\n\n  //! \\brief The optional routine run in parent after the child exits.\n  //!\n  //! Subclasses may implement this method to clean up the environment after\n  //! the child process has exited.\n  //!\n  //! \\param[in] child A handle to the exited child process.\n  virtual void WinMultiprocessParentAfterChild(HANDLE child) {}\n\n  //! \\brief The subclass-provided child routine.\n  //!\n  //! Test failures should be reported via Google Test: `EXPECT_*()`,\n  //! `ASSERT_*()`, `FAIL()`, etc.\n  //!\n  //! Subclasses must implement this method to define how the child operates.\n  //! Subclasses may exit with a failure status by using `LOG(FATAL)`,\n  //! `abort()`, or similar. They may exit cleanly by returning from this\n  //! method.\n  virtual void WinMultiprocessChild() = 0;\n\n  unsigned int exit_code_;\n  WinChildProcess::Handles* child_handles_;\n  ChildProcessHelperBase* child_process_helper_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_\n"
  },
  {
    "path": "test/win/win_multiprocess_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/win/win_multiprocess.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\ntemplate <int ExitCode>\nclass TestWinMultiprocess final : public WinMultiprocess {\n public:\n  TestWinMultiprocess() {}\n\n  TestWinMultiprocess(const TestWinMultiprocess&) = delete;\n  TestWinMultiprocess& operator=(const TestWinMultiprocess&) = delete;\n\n private:\n  // WinMultiprocess will have already exercised the pipes.\n  void WinMultiprocessParent() override { SetExpectedChildExitCode(ExitCode); }\n\n  void WinMultiprocessChild() override {\n    exit(ExitCode);\n  }\n};\n\nclass TestWinMultiprocessChildAsserts final : public WinMultiprocess {\n public:\n  TestWinMultiprocessChildAsserts() {}\n\n  TestWinMultiprocessChildAsserts(const TestWinMultiprocessChildAsserts&) =\n      delete;\n  TestWinMultiprocessChildAsserts& operator=(\n      const TestWinMultiprocessChildAsserts&) = delete;\n\n private:\n  void WinMultiprocessParent() override { SetExpectedChildExitCode(255); }\n  void WinMultiprocessChild() override {\n    ASSERT_FALSE(true);\n  }\n};\n\nclass TestWinMultiprocessChildExpects final : public WinMultiprocess {\n public:\n  TestWinMultiprocessChildExpects() {}\n\n  TestWinMultiprocessChildExpects(const TestWinMultiprocessChildExpects&) =\n      delete;\n  TestWinMultiprocessChildExpects& operator=(\n      const TestWinMultiprocessChildExpects&) = delete;\n\n private:\n  void WinMultiprocessParent() override { SetExpectedChildExitCode(255); }\n  void WinMultiprocessChild() override {\n    EXPECT_FALSE(true);\n  }\n};\n\nTEST(WinMultiprocess, WinMultiprocess) {\n  WinMultiprocess::Run<TestWinMultiprocess<0>>();\n}\n\nTEST(WinMultiprocess, WinMultiprocessNonSuccessExitCode) {\n  WinMultiprocess::Run<TestWinMultiprocess<100>>();\n}\n\nTEST(WinMultiprocessChildFails, ChildExpectFailure) {\n  WinMultiprocess::Run<TestWinMultiprocessChildExpects>();\n}\n\nTEST(WinMultiprocessChildFails, ChildAssertFailure) {\n  WinMultiprocess::Run<TestWinMultiprocessChildAsserts>();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/win/win_multiprocess_with_temp_dir.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"test/win/win_multiprocess_with_temp_dir.h\"\n\n#include <tlhelp32.h>\n\n#include \"base/logging.h\"\n#include \"test/errors.h\"\n#include \"util/process/process_id.h\"\n#include \"util/win/process_info.h\"\n\nnamespace crashpad {\nnamespace test {\n\nnamespace {\n\nconstexpr wchar_t kTempDirEnvName[] = L\"CRASHPAD_TEST_TEMP_DIR\";\n\n// Returns the process IDs of all processes that have |parent_pid| as\n// parent process ID.\nstd::vector<ProcessID> GetPotentialChildProcessesOf(ProcessID parent_pid) {\n  ScopedFileHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));\n  if (!snapshot.is_valid()) {\n    ADD_FAILURE() << ErrorMessage(\"CreateToolhelp32Snapshot\");\n    return std::vector<ProcessID>();\n  }\n\n  PROCESSENTRY32 entry = {sizeof(entry)};\n  if (!Process32First(snapshot.get(), &entry)) {\n    ADD_FAILURE() << ErrorMessage(\"Process32First\");\n    return std::vector<ProcessID>();\n  }\n\n  std::vector<ProcessID> child_pids;\n  do {\n    if (entry.th32ParentProcessID == parent_pid)\n      child_pids.push_back(entry.th32ProcessID);\n  } while (Process32Next(snapshot.get(), &entry));\n\n  return child_pids;\n}\n\nULARGE_INTEGER GetProcessCreationTime(HANDLE process) {\n  ULARGE_INTEGER ret = {};\n  FILETIME creation_time;\n  FILETIME dummy;\n  if (GetProcessTimes(process, &creation_time, &dummy, &dummy, &dummy)) {\n    ret.LowPart = creation_time.dwLowDateTime;\n    ret.HighPart = creation_time.dwHighDateTime;\n  } else {\n    ADD_FAILURE() << ErrorMessage(\"GetProcessTimes\");\n  }\n\n  return ret;\n}\n\n// Waits for the processes directly created by |parent| - and specifically\n// not their offspring. For this to work without race, |parent| has to be\n// suspended or have exited.\nvoid WaitForAllChildProcessesOf(HANDLE parent) {\n  ProcessID parent_pid = GetProcessId(parent);\n  std::vector<ProcessID> child_pids = GetPotentialChildProcessesOf(parent_pid);\n\n  ULARGE_INTEGER parent_creationtime = GetProcessCreationTime(parent);\n  for (ProcessID child_pid : child_pids) {\n    // Try and open the process. This may fail for reasons such as:\n    // 1. The process isn't |parent|'s child process, but rather a\n    //    higher-privilege sub-process of an earlier process that had\n    //    |parent|'s PID.\n    // 2. The process no longer exists, e.g. it exited after enumeration.\n    ScopedKernelHANDLE child_process(\n        OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | SYNCHRONIZE,\n                    false,\n                    child_pid));\n    if (!child_process.is_valid())\n      continue;\n\n    // Check that the child now has the right parent PID, as its PID may have\n    // been reused after the enumeration above.\n    ProcessInfo child_info;\n    if (!child_info.Initialize(child_process.get())) {\n      // This can happen if child_process has exited after the handle is opened.\n      LOG(ERROR) << \"ProcessInfo::Initialize, pid: \" << child_pid;\n      continue;\n    }\n\n    if (parent_pid != child_info.ParentProcessID()) {\n      // The child's process ID was reused after enumeration.\n      continue;\n    }\n\n    // We successfully opened |child_process| and it has |parent|'s PID for\n    // parent process ID. However, this could still be a sub-process of another\n    // process that earlier had |parent|'s PID. To make sure, check that\n    // |child_process| was created after |parent_process|.\n    ULARGE_INTEGER process_creationtime =\n        GetProcessCreationTime(child_process.get());\n    if (process_creationtime.QuadPart < parent_creationtime.QuadPart)\n      continue;\n\n    DWORD err = WaitForSingleObject(child_process.get(), INFINITE);\n    if (err == WAIT_FAILED) {\n      ADD_FAILURE() << ErrorMessage(\"WaitForSingleObject\");\n    } else if (err != WAIT_OBJECT_0) {\n      ADD_FAILURE() << \"WaitForSingleObject returned \" << err;\n    }\n  }\n}\n\n}  // namespace\n\nWinMultiprocessWithTempDir::WinMultiprocessWithTempDir()\n    : WinMultiprocess(), temp_dir_env_(kTempDirEnvName) {}\n\nvoid WinMultiprocessWithTempDir::WinMultiprocessParentBeforeChild() {\n  temp_dir_ = std::make_unique<ScopedTempDir>();\n  temp_dir_env_.SetValue(temp_dir_->path().value().c_str());\n}\n\nvoid WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(HANDLE child) {\n  WaitForAllChildProcessesOf(child);\n  temp_dir_.reset();\n}\n\nbase::FilePath WinMultiprocessWithTempDir::GetTempDirPath() const {\n  return base::FilePath(temp_dir_env_.GetValue());\n}\n\nWinMultiprocessWithTempDir::ScopedEnvironmentVariable::\n    ScopedEnvironmentVariable(const wchar_t* name)\n    : name_(name) {\n  original_value_ = GetValueImpl(&was_defined_);\n}\n\nWinMultiprocessWithTempDir::ScopedEnvironmentVariable::\n    ~ScopedEnvironmentVariable() {\n  if (was_defined_)\n    SetValue(original_value_.data());\n  else\n    SetValue(nullptr);\n}\n\nstd::wstring WinMultiprocessWithTempDir::ScopedEnvironmentVariable::GetValue()\n    const {\n  bool dummy;\n  return GetValueImpl(&dummy);\n}\n\nstd::wstring\nWinMultiprocessWithTempDir::ScopedEnvironmentVariable::GetValueImpl(\n    bool* is_defined) const {\n  // The length returned is inclusive of the terminating zero, except\n  // if the variable doesn't exist, in which case the return value is zero.\n  DWORD len = GetEnvironmentVariable(name_, nullptr, 0);\n  if (len == 0) {\n    *is_defined = false;\n    return L\"\";\n  }\n\n  *is_defined = true;\n\n  std::wstring ret;\n  ret.resize(len);\n  // The length returned on success is exclusive of the terminating zero.\n  len = GetEnvironmentVariable(name_, &ret[0], len);\n  ret.resize(len);\n\n  return ret;\n}\n\nvoid WinMultiprocessWithTempDir::ScopedEnvironmentVariable::SetValue(\n    const wchar_t* new_value) const {\n  SetEnvironmentVariable(name_, new_value);\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "test/win/win_multiprocess_with_temp_dir.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_\n#define CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_\n\n#include <wchar.h>\n#include <windows.h>\n\n#include <memory>\n#include <string>\n\n#include \"base/files/file_path.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"test/win/win_multiprocess.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Manages a multiprocess test on Windows with a parent-created\n//!     temporary directory.\n//!\n//! This class creates a temp directory in the parent process for the use of\n//! the subprocess and its children. To ensure a raceless rundown, it waits on\n//! the child process and any processes directly created by the child before\n//! deleting the temporary directory.\nclass WinMultiprocessWithTempDir : public WinMultiprocess {\n public:\n  WinMultiprocessWithTempDir();\n\n  WinMultiprocessWithTempDir(const WinMultiprocessWithTempDir&) = delete;\n  WinMultiprocessWithTempDir& operator=(const WinMultiprocessWithTempDir&) =\n      delete;\n\n protected:\n  void WinMultiprocessParentBeforeChild() override;\n  void WinMultiprocessParentAfterChild(HANDLE child) override;\n\n  //! \\brief Returns the path of the temp directory.\n  base::FilePath GetTempDirPath() const;\n\n private:\n  class ScopedEnvironmentVariable {\n   public:\n    explicit ScopedEnvironmentVariable(const wchar_t* name);\n\n    ScopedEnvironmentVariable(const ScopedEnvironmentVariable&) = delete;\n    ScopedEnvironmentVariable& operator=(const ScopedEnvironmentVariable&) =\n        delete;\n\n    ~ScopedEnvironmentVariable();\n\n    std::wstring GetValue() const;\n\n    // Sets this environment variable to |new_value|. If |new_value| is nullptr\n    // this environment variable will be undefined.\n    void SetValue(const wchar_t* new_value) const;\n\n   private:\n    std::wstring GetValueImpl(bool* is_defined) const;\n\n    std::wstring original_value_;\n    const wchar_t* name_;\n    bool was_defined_;\n  };\n\n  std::unique_ptr<ScopedTempDir> temp_dir_;\n  ScopedEnvironmentVariable temp_dir_env_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_\n"
  },
  {
    "path": "third_party/cpp-httplib/BUILD.gn",
    "content": "# Copyright 2018 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nsource_set(\"cpp-httplib\") {\n  testonly = true\n  include_dirs = [ \"cpp-httplib\" ]\n  sources = [ \"cpp-httplib/httplib.h\" ]\n  deps = [ \"../zlib\" ]\n}\n"
  },
  {
    "path": "third_party/cpp-httplib/README.crashpad",
    "content": "Name: cpp-httplib\nShort Name: cpp-httplib\nURL: https://github.com/yhirose/cpp-httplib\nVersion: 0.27.0\nRevision: eacc1ca98e5fef25184c7d417e8417225e05e65d\nLicense: MIT\nLicense File: cpp-httplib/LICENSE\nSecurity Critical: no (test only)\nShipped: no\n\nDescription:\nA C++11 single-file header-only cross-platform HTTP/HTTPS library.\n\nLocal Modifications:\n - Exclude all subdirectories and build-related files.\n - Patch httplib.h to use #include \"third_party/zlib/zlib_crashpad.h\" instead of\n   <zlib.h>.\n - #define CPPHTTPLIB_NO_EXCEPTIONS 1.\n - Revert upstream patches that drop support for 32-bit Windows:\n    - 52163ed9823c Fix #2148 (#2173)\n    - 9dbaed75efff Fix #2175 (#2177)\n    - 1f110b54d8b3 Chang #error to #warning for the 32-bit environment check\n                   except 32-bit Windows\n    - 0b3758ec36be Fix problem with Windows version check\n    - fbee136dca54 Fix #2193. Allow _WIN32\n    - dffce89514c2 Fix 32-bit MSVC compiler error due to unknown command\n                   #warning (#2202)\n - Fix sign compare error with sk_X509_OBJECT_num from BoringSSL.\n"
  },
  {
    "path": "third_party/cpp-httplib/cpp-httplib/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2017 yhirose\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "third_party/cpp-httplib/cpp-httplib/README.md",
    "content": "cpp-httplib\n===========\n\n[![](https://github.com/yhirose/cpp-httplib/workflows/test/badge.svg)](https://github.com/yhirose/cpp-httplib/actions)\n\nA C++11 single-file header-only cross platform HTTP/HTTPS library.\n\nIt's extremely easy to set up. Just include the **httplib.h** file in your code!\n\n> [!IMPORTANT]\n> This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want.\n\nSimple examples\n---------------\n\n#### Server (Multi-threaded)\n\n```c++\n#define CPPHTTPLIB_OPENSSL_SUPPORT\n#include \"path/to/httplib.h\"\n\n// HTTP\nhttplib::Server svr;\n\n// HTTPS\nhttplib::SSLServer svr;\n\nsvr.Get(\"/hi\", [](const httplib::Request &, httplib::Response &res) {\n  res.set_content(\"Hello World!\", \"text/plain\");\n});\n\nsvr.listen(\"0.0.0.0\", 8080);\n```\n\n#### Client\n\n```c++\n#define CPPHTTPLIB_OPENSSL_SUPPORT\n#include \"path/to/httplib.h\"\n\n// HTTP\nhttplib::Client cli(\"http://yhirose.github.io\");\n\n// HTTPS\nhttplib::Client cli(\"https://yhirose.github.io\");\n\nauto res = cli.Get(\"/hi\");\nres->status;\nres->body;\n```\n\nSSL Support\n-----------\n\nSSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.\n\n> [!NOTE]\n> cpp-httplib currently supports only version 3.0 or later. Please see [this page](https://www.openssl.org/policies/releasestrat.html) to get more information.\n\n> [!TIP]\n> For macOS: cpp-httplib now can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`.\n\n```c++\n#define CPPHTTPLIB_OPENSSL_SUPPORT\n#include \"path/to/httplib.h\"\n\n// Server\nhttplib::SSLServer svr(\"./cert.pem\", \"./key.pem\");\n\n// Client\nhttplib::Client cli(\"https://localhost:1234\"); // scheme + host\nhttplib::SSLClient cli(\"localhost:1234\"); // host\nhttplib::SSLClient cli(\"localhost\", 1234); // host, port\n\n// Use your CA bundle\ncli.set_ca_cert_path(\"./ca-bundle.crt\");\n\n// Disable cert verification\ncli.enable_server_certificate_verification(false);\n\n// Disable host verification\ncli.enable_server_hostname_verification(false);\n```\n\n> [!NOTE]\n> When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself.\n\n### SSL Error Handling\n\nWhen SSL operations fail, cpp-httplib provides detailed error information through two separate error fields:\n\n```c++\n#define CPPHTTPLIB_OPENSSL_SUPPORT\n#include \"path/to/httplib.h\"\n\nhttplib::Client cli(\"https://example.com\");\n\nauto res = cli.Get(\"/\");\nif (!res) {\n  // Check the error type\n  const auto err = res.error();\n\n  switch (err) {\n    case httplib::Error::SSLConnection:\n      std::cout << \"SSL connection failed, SSL error: \"\n                << res.ssl_error() << std::endl;\n      break;\n\n    case httplib::Error::SSLLoadingCerts:\n      std::cout << \"SSL cert loading failed, OpenSSL error: \"\n                << std::hex << res.ssl_openssl_error() << std::endl;\n      break;\n\n    case httplib::Error::SSLServerVerification:\n      std::cout << \"SSL verification failed, X509 error: \"\n                << res.ssl_openssl_error() << std::endl;\n      break;\n\n    case httplib::Error::SSLServerHostnameVerification:\n      std::cout << \"SSL hostname verification failed, X509 error: \"\n                << res.ssl_openssl_error() << std::endl;\n      break;\n\n    default:\n      std::cout << \"HTTP error: \" << httplib::to_string(err) << std::endl;\n  }\n}\n```\n\nServer\n------\n\n```c++\n#include <httplib.h>\n\nint main(void)\n{\n  using namespace httplib;\n\n  Server svr;\n\n  svr.Get(\"/hi\", [](const Request& req, Response& res) {\n    res.set_content(\"Hello World!\", \"text/plain\");\n  });\n\n  // Match the request path against a regular expression\n  // and extract its captures\n  svr.Get(R\"(/numbers/(\\d+))\", [&](const Request& req, Response& res) {\n    auto numbers = req.matches[1];\n    res.set_content(numbers, \"text/plain\");\n  });\n\n  // Capture the second segment of the request path as \"id\" path param\n  svr.Get(\"/users/:id\", [&](const Request& req, Response& res) {\n    auto user_id = req.path_params.at(\"id\");\n    res.set_content(user_id, \"text/plain\");\n  });\n\n  // Extract values from HTTP headers and URL query params\n  svr.Get(\"/body-header-param\", [](const Request& req, Response& res) {\n    if (req.has_header(\"Content-Length\")) {\n      auto val = req.get_header_value(\"Content-Length\");\n    }\n    if (req.has_param(\"key\")) {\n      auto val = req.get_param_value(\"key\");\n    }\n    res.set_content(req.body, \"text/plain\");\n  });\n\n  // If the handler takes time to finish, you can also poll the connection state\n  svr.Get(\"/task\", [&](const Request& req, Response& res) {\n    const char * result = nullptr;\n    process.run(); // for example, starting an external process\n    while (result == nullptr) {\n      sleep(1);\n      if (req.is_connection_closed()) {\n        process.kill(); // kill the process\n        return;\n      }\n      result = process.stdout(); // != nullptr if the process finishes\n    }\n    res.set_content(result, \"text/plain\");\n  });\n\n  svr.Get(\"/stop\", [&](const Request& req, Response& res) {\n    svr.stop();\n  });\n\n  svr.listen(\"localhost\", 1234);\n}\n```\n\n`Post`, `Put`, `Delete` and `Options` methods are also supported.\n\n### Bind a socket to multiple interfaces and any available port\n\n```cpp\nint port = svr.bind_to_any_port(\"0.0.0.0\");\nsvr.listen_after_bind();\n```\n\n### Static File Server\n\n```cpp\n// Mount / to ./www directory\nauto ret = svr.set_mount_point(\"/\", \"./www\");\nif (!ret) {\n  // The specified base directory doesn't exist...\n}\n\n// Mount /public to ./www directory\nret = svr.set_mount_point(\"/public\", \"./www\");\n\n// Mount /public to ./www1 and ./www2 directories\nret = svr.set_mount_point(\"/public\", \"./www1\"); // 1st order to search\nret = svr.set_mount_point(\"/public\", \"./www2\"); // 2nd order to search\n\n// Remove mount /\nret = svr.remove_mount_point(\"/\");\n\n// Remove mount /public\nret = svr.remove_mount_point(\"/public\");\n```\n\n```cpp\n// User defined file extension and MIME type mappings\nsvr.set_file_extension_and_mimetype_mapping(\"cc\", \"text/x-c\");\nsvr.set_file_extension_and_mimetype_mapping(\"cpp\", \"text/x-c\");\nsvr.set_file_extension_and_mimetype_mapping(\"hh\", \"text/x-h\");\n```\n\nThe following are built-in mappings:\n\n| Extension  |          MIME Type          | Extension  |          MIME Type          |\n| :--------- | :-------------------------- | :--------- | :-------------------------- |\n| css        | text/css                    | mpga       | audio/mpeg                  |\n| csv        | text/csv                    | weba       | audio/webm                  |\n| txt        | text/plain                  | wav        | audio/wave                  |\n| vtt        | text/vtt                    | otf        | font/otf                    |\n| html, htm  | text/html                   | ttf        | font/ttf                    |\n| apng       | image/apng                  | woff       | font/woff                   |\n| avif       | image/avif                  | woff2      | font/woff2                  |\n| bmp        | image/bmp                   | 7z         | application/x-7z-compressed |\n| gif        | image/gif                   | atom       | application/atom+xml        |\n| png        | image/png                   | pdf        | application/pdf             |\n| svg        | image/svg+xml               | mjs, js    | text/javascript             |\n| webp       | image/webp                  | json       | application/json            |\n| ico        | image/x-icon                | rss        | application/rss+xml         |\n| tif        | image/tiff                  | tar        | application/x-tar           |\n| tiff       | image/tiff                  | xhtml, xht | application/xhtml+xml       |\n| jpeg, jpg  | image/jpeg                  | xslt       | application/xslt+xml        |\n| mp4        | video/mp4                   | xml        | application/xml             |\n| mpeg       | video/mpeg                  | gz         | application/gzip            |\n| webm       | video/webm                  | zip        | application/zip             |\n| mp3        | audio/mp3                   | wasm       | application/wasm            |\n\n> [!WARNING]\n> These static file server methods are not thread-safe.\n\n### File request handler\n\n```cpp\n// The handler is called right before the response is sent to a client\nsvr.set_file_request_handler([](const Request &req, Response &res) {\n  ...\n});\n```\n\n### Logging\n\ncpp-httplib provides separate logging capabilities for access logs and error logs, similar to web servers like Nginx and Apache.\n\n#### Access Logging\n\nAccess loggers capture successful HTTP requests and responses:\n\n```cpp\nsvr.set_logger([](const httplib::Request& req, const httplib::Response& res) {\n  std::cout << req.method << \" \" << req.path << \" -> \" << res.status << std::endl;\n});\n```\n\n#### Pre-compression Logging\n\nYou can also set a pre-compression logger to capture request/response data before compression is applied:\n\n```cpp\nsvr.set_pre_compression_logger([](const httplib::Request& req, const httplib::Response& res) {\n  // Log before compression - res.body contains uncompressed content\n  // Content-Encoding header is not yet set\n  your_pre_compression_logger(req, res);\n});\n```\n\nThe pre-compression logger is only called when compression would be applied. For responses without compression, only the access logger is called.\n\n#### Error Logging\n\nError loggers capture failed requests and connection issues. Unlike access loggers, error loggers only receive the Error and Request information, as errors typically occur before a meaningful Response can be generated.\n\n```cpp\nsvr.set_error_logger([](const httplib::Error& err, const httplib::Request* req) {\n  std::cerr << httplib::to_string(err) << \" while processing request\";\n  if (req) {\n    std::cerr << \", client: \" << req->get_header_value(\"X-Forwarded-For\")\n              << \", request: '\" << req->method << \" \" << req->path << \" \" << req->version << \"'\"\n              << \", host: \" << req->get_header_value(\"Host\");\n  }\n  std::cerr << std::endl;\n});\n```\n\n### Error handler\n\n```cpp\nsvr.set_error_handler([](const auto& req, auto& res) {\n  auto fmt = \"<p>Error Status: <span style='color:red;'>%d</span></p>\";\n  char buf[BUFSIZ];\n  snprintf(buf, sizeof(buf), fmt, res.status);\n  res.set_content(buf, \"text/html\");\n});\n```\n\n### Exception handler\nThe exception handler gets called if a user routing handler throws an error.\n\n```cpp\nsvr.set_exception_handler([](const auto& req, auto& res, std::exception_ptr ep) {\n  auto fmt = \"<h1>Error 500</h1><p>%s</p>\";\n  char buf[BUFSIZ];\n  try {\n    std::rethrow_exception(ep);\n  } catch (std::exception &e) {\n    snprintf(buf, sizeof(buf), fmt, e.what());\n  } catch (...) { // See the following NOTE\n    snprintf(buf, sizeof(buf), fmt, \"Unknown Exception\");\n  }\n  res.set_content(buf, \"text/html\");\n  res.status = StatusCode::InternalServerError_500;\n});\n```\n\n> [!CAUTION]\n> if you don't provide the `catch (...)` block for a rethrown exception pointer, an uncaught exception will end up causing the server crash. Be careful!\n\n### Pre routing handler\n\n```cpp\nsvr.set_pre_routing_handler([](const auto& req, auto& res) {\n  if (req.path == \"/hello\") {\n    res.set_content(\"world\", \"text/html\");\n    return Server::HandlerResponse::Handled;\n  }\n  return Server::HandlerResponse::Unhandled;\n});\n```\n\n### Post routing handler\n\n```cpp\nsvr.set_post_routing_handler([](const auto& req, auto& res) {\n  res.set_header(\"ADDITIONAL_HEADER\", \"value\");\n});\n```\n\n### Pre request handler\n\n```cpp\nsvr.set_pre_request_handler([](const auto& req, auto& res) {\n  if (req.matched_route == \"/user/:user\") {\n    auto user = req.path_params.at(\"user\");\n    if (user != \"john\") {\n      res.status = StatusCode::Forbidden_403;\n      res.set_content(\"error\", \"text/html\");\n      return Server::HandlerResponse::Handled;\n    }\n  }\n  return Server::HandlerResponse::Unhandled;\n});\n```\n\n### Form data handling\n\n#### URL-encoded form data ('application/x-www-form-urlencoded')\n\n```cpp\nsvr.Post(\"/form\", [&](const auto& req, auto& res) {\n  // URL query parameters and form-encoded data are accessible via req.params\n  std::string username = req.get_param_value(\"username\");\n  std::string password = req.get_param_value(\"password\");\n\n  // Handle multiple values with same name\n  auto interests = req.get_param_values(\"interests\");\n\n  // Check existence\n  if (req.has_param(\"newsletter\")) {\n    // Handle newsletter subscription\n  }\n});\n```\n\n#### 'multipart/form-data' POST data\n\n```cpp\nsvr.Post(\"/multipart\", [&](const Request& req, Response& res) {\n  // Access text fields (from form inputs without files)\n  std::string username = req.form.get_field(\"username\");\n  std::string bio = req.form.get_field(\"bio\");\n\n  // Access uploaded files\n  if (req.form.has_file(\"avatar\")) {\n    const auto& file = req.form.get_file(\"avatar\");\n    std::cout << \"Uploaded file: \" << file.filename\n              << \" (\" << file.content_type << \") - \"\n              << file.content.size() << \" bytes\" << std::endl;\n\n    // Access additional headers if needed\n    for (const auto& header : file.headers) {\n      std::cout << \"Header: \" << header.first << \" = \" << header.second << std::endl;\n    }\n\n    // Save to disk\n    std::ofstream ofs(file.filename, std::ios::binary);\n    ofs << file.content;\n  }\n\n  // Handle multiple values with same name\n  auto tags = req.form.get_fields(\"tags\");  // e.g., multiple checkboxes\n  for (const auto& tag : tags) {\n    std::cout << \"Tag: \" << tag << std::endl;\n  }\n\n  auto documents = req.form.get_files(\"documents\");  // multiple file upload\n  for (const auto& doc : documents) {\n    std::cout << \"Document: \" << doc.filename\n              << \" (\" << doc.content.size() << \" bytes)\" << std::endl;\n  }\n\n  // Check existence before accessing\n  if (req.form.has_field(\"newsletter\")) {\n    std::cout << \"Newsletter subscription: \" << req.form.get_field(\"newsletter\") << std::endl;\n  }\n\n  // Get counts for validation\n  if (req.form.get_field_count(\"tags\") > 5) {\n    res.status = StatusCode::BadRequest_400;\n    res.set_content(\"Too many tags\", \"text/plain\");\n    return;\n  }\n\n  // Summary\n  std::cout << \"Received \" << req.form.fields.size() << \" text fields and \"\n            << req.form.files.size() << \" files\" << std::endl;\n\n  res.set_content(\"Upload successful\", \"text/plain\");\n});\n```\n\n### Receive content with a content receiver\n\n```cpp\nsvr.Post(\"/content_receiver\",\n  [&](const Request &req, Response &res, const ContentReader &content_reader) {\n    if (req.is_multipart_form_data()) {\n      // NOTE: `content_reader` is blocking until every form data field is read\n      // This approach allows streaming processing of large files\n      std::vector<FormData> items;\n      content_reader(\n        [&](const FormData &item) {\n          items.push_back(item);\n          return true;\n        },\n        [&](const char *data, size_t data_length) {\n          items.back().content.append(data, data_length);\n          return true;\n        });\n\n      // Process the received items\n      for (const auto& item : items) {\n        if (item.filename.empty()) {\n          // Text field\n          std::cout << \"Field: \" << item.name << \" = \" << item.content << std::endl;\n        } else {\n          // File\n          std::cout << \"File: \" << item.name << \" (\" << item.filename << \") - \"\n                    << item.content.size() << \" bytes\" << std::endl;\n        }\n      }\n    } else {\n      std::string body;\n      content_reader([&](const char *data, size_t data_length) {\n        body.append(data, data_length);\n        return true;\n      });\n    }\n  });\n```\n\n### Send content with the content provider\n\n```cpp\nconst size_t DATA_CHUNK_SIZE = 4;\n\nsvr.Get(\"/stream\", [&](const Request &req, Response &res) {\n  auto data = new std::string(\"abcdefg\");\n\n  res.set_content_provider(\n    data->size(), // Content length\n    \"text/plain\", // Content type\n    [&, data](size_t offset, size_t length, DataSink &sink) {\n      const auto &d = *data;\n      sink.write(&d[offset], std::min(length, DATA_CHUNK_SIZE));\n      return true; // return 'false' if you want to cancel the process.\n    },\n    [data](bool success) { delete data; });\n});\n```\n\nWithout content length:\n\n```cpp\nsvr.Get(\"/stream\", [&](const Request &req, Response &res) {\n  res.set_content_provider(\n    \"text/plain\", // Content type\n    [&](size_t offset, DataSink &sink) {\n      if (/* there is still data */) {\n        std::vector<char> data;\n        // prepare data...\n        sink.write(data.data(), data.size());\n      } else {\n        sink.done(); // No more data\n      }\n      return true; // return 'false' if you want to cancel the process.\n    });\n});\n```\n\n### Chunked transfer encoding\n\n```cpp\nsvr.Get(\"/chunked\", [&](const Request& req, Response& res) {\n  res.set_chunked_content_provider(\n    \"text/plain\",\n    [](size_t offset, DataSink &sink) {\n      sink.write(\"123\", 3);\n      sink.write(\"345\", 3);\n      sink.write(\"789\", 3);\n      sink.done(); // No more data\n      return true; // return 'false' if you want to cancel the process.\n    }\n  );\n});\n```\n\nWith trailer:\n\n```cpp\nsvr.Get(\"/chunked\", [&](const Request& req, Response& res) {\n  res.set_header(\"Trailer\", \"Dummy1, Dummy2\");\n  res.set_chunked_content_provider(\n    \"text/plain\",\n    [](size_t offset, DataSink &sink) {\n      sink.write(\"123\", 3);\n      sink.write(\"345\", 3);\n      sink.write(\"789\", 3);\n      sink.done_with_trailer({\n        {\"Dummy1\", \"DummyVal1\"},\n        {\"Dummy2\", \"DummyVal2\"}\n      });\n      return true;\n    }\n  );\n});\n```\n\n### Send file content\n\n```cpp\nsvr.Get(\"/content\", [&](const Request &req, Response &res) {\n  res.set_file_content(\"./path/to/content.html\");\n});\n\nsvr.Get(\"/content\", [&](const Request &req, Response &res) {\n  res.set_file_content(\"./path/to/content\", \"text/html\");\n});\n```\n\n### 'Expect: 100-continue' handler\n\nBy default, the server sends a `100 Continue` response for an `Expect: 100-continue` header.\n\n```cpp\n// Send a '417 Expectation Failed' response.\nsvr.set_expect_100_continue_handler([](const Request &req, Response &res) {\n  return StatusCode::ExpectationFailed_417;\n});\n```\n\n```cpp\n// Send a final status without reading the message body.\nsvr.set_expect_100_continue_handler([](const Request &req, Response &res) {\n  return res.status = StatusCode::Unauthorized_401;\n});\n```\n\n### Keep-Alive connection\n\n```cpp\nsvr.set_keep_alive_max_count(2); // Default is 100\nsvr.set_keep_alive_timeout(10);  // Default is 5\n```\n\n### Timeout\n\n```c++\nsvr.set_read_timeout(5, 0); // 5 seconds\nsvr.set_write_timeout(5, 0); // 5 seconds\nsvr.set_idle_interval(0, 100000); // 100 milliseconds\n```\n\n### Set maximum payload length for reading a request body\n\n```c++\nsvr.set_payload_max_length(1024 * 1024 * 512); // 512MB\n```\n\n> [!NOTE]\n> When the request body content type is 'www-form-urlencoded', the actual payload length shouldn't exceed `CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH`.\n\n### Server-Sent Events\n\nPlease see [Server example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssesvr.cc) and [Client example](https://github.com/yhirose/cpp-httplib/blob/master/example/ssecli.cc).\n\n### Default thread pool support\n\n`ThreadPool` is used as the **default** task queue, with a default thread count of 8 or `std::thread::hardware_concurrency() - 1`, whichever is greater. You can change it with `CPPHTTPLIB_THREAD_POOL_COUNT`.\n\nIf you want to set the thread count at runtime, there is no convenient way... But here is how.\n\n```cpp\nsvr.new_task_queue = [] { return new ThreadPool(12); };\n```\n\nYou can also provide an optional parameter to limit the maximum number\nof pending requests, i.e. requests `accept()`ed by the listener but\nstill waiting to be serviced by worker threads.\n\n```cpp\nsvr.new_task_queue = [] { return new ThreadPool(/*num_threads=*/12, /*max_queued_requests=*/18); };\n```\n\nDefault limit is 0 (unlimited). Once the limit is reached, the listener\nwill shutdown the client connection.\n\n### Override the default thread pool with yours\n\nYou can supply your own thread pool implementation according to your need.\n\n```cpp\nclass YourThreadPoolTaskQueue : public TaskQueue {\npublic:\n  YourThreadPoolTaskQueue(size_t n) {\n    pool_.start_with_thread_count(n);\n  }\n\n  virtual bool enqueue(std::function<void()> fn) override {\n    /* Return true if the task was actually enqueued, or false\n     * if the caller must drop the corresponding connection. */\n    return pool_.enqueue(fn);\n  }\n\n  virtual void shutdown() override {\n    pool_.shutdown_gracefully();\n  }\n\nprivate:\n  YourThreadPool pool_;\n};\n\nsvr.new_task_queue = [] {\n  return new YourThreadPoolTaskQueue(12);\n};\n```\n\nClient\n------\n\n```c++\n#include <httplib.h>\n#include <iostream>\n\nint main(void)\n{\n  httplib::Client cli(\"localhost\", 1234);\n\n  if (auto res = cli.Get(\"/hi\")) {\n    if (res->status == StatusCode::OK_200) {\n      std::cout << res->body << std::endl;\n    }\n  } else {\n    auto err = res.error();\n    std::cout << \"HTTP error: \" << httplib::to_string(err) << std::endl;\n  }\n}\n```\n\n> [!TIP]\n> Constructor with scheme-host-port string is now supported!\n\n```c++\nhttplib::Client cli(\"localhost\");\nhttplib::Client cli(\"localhost:8080\");\nhttplib::Client cli(\"http://localhost\");\nhttplib::Client cli(\"http://localhost:8080\");\nhttplib::Client cli(\"https://localhost\");\nhttplib::SSLClient cli(\"localhost\");\n```\n\n### Error code\n\nHere is the list of errors from `Result::error()`.\n\n```c++\nenum Error {\n  Success = 0,\n  Unknown,\n  Connection,\n  BindIPAddress,\n  Read,\n  Write,\n  ExceedRedirectCount,\n  Canceled,\n  SSLConnection,\n  SSLLoadingCerts,\n  SSLServerVerification,\n  SSLServerHostnameVerification,\n  UnsupportedMultipartBoundaryChars,\n  Compression,\n  ConnectionTimeout,\n  ProxyConnection,\n};\n```\n\n### Client Logging\n\n#### Access Logging\n\n```cpp\ncli.set_logger([](const httplib::Request& req, const httplib::Response& res) {\n  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(\n    std::chrono::steady_clock::now() - start_time).count();\n  std::cout << \"✓ \" << req.method << \" \" << req.path\n            << \" -> \" << res.status << \" (\" << res.body.size() << \" bytes, \"\n            << duration << \"ms)\" << std::endl;\n});\n```\n\n#### Error Logging\n\n```cpp\ncli.set_error_logger([](const httplib::Error& err, const httplib::Request* req) {\n  std::cerr << \"✗ \";\n  if (req) {\n    std::cerr << req->method << \" \" << req->path << \" \";\n  }\n  std::cerr << \"failed: \" << httplib::to_string(err);\n\n  // Add specific guidance based on error type\n  switch (err) {\n    case httplib::Error::Connection:\n      std::cerr << \" (verify server is running and reachable)\";\n      break;\n    case httplib::Error::SSLConnection:\n      std::cerr << \" (check SSL certificate and TLS configuration)\";\n      break;\n    case httplib::Error::ConnectionTimeout:\n      std::cerr << \" (increase timeout or check network latency)\";\n      break;\n    case httplib::Error::Read:\n      std::cerr << \" (server may have closed connection prematurely)\";\n      break;\n    default:\n      break;\n  }\n  std::cerr << std::endl;\n});\n```\n\n### GET with HTTP headers\n\n```c++\nhttplib::Headers headers = {\n  { \"Hello\", \"World!\" }\n};\nauto res = cli.Get(\"/hi\", headers);\n```\nor\n```c++\nauto res = cli.Get(\"/hi\", {{\"Hello\", \"World!\"}});\n```\nor\n```c++\ncli.set_default_headers({\n  { \"Hello\", \"World!\" }\n});\nauto res = cli.Get(\"/hi\");\n```\n\n### POST\n\n```c++\nres = cli.Post(\"/post\", \"text\", \"text/plain\");\nres = cli.Post(\"/person\", \"name=john1&note=coder\", \"application/x-www-form-urlencoded\");\n```\n\n### POST with parameters\n\n```c++\nhttplib::Params params;\nparams.emplace(\"name\", \"john\");\nparams.emplace(\"note\", \"coder\");\n\nauto res = cli.Post(\"/post\", params);\n```\n or\n\n```c++\nhttplib::Params params{\n  { \"name\", \"john\" },\n  { \"note\", \"coder\" }\n};\n\nauto res = cli.Post(\"/post\", params);\n```\n\n### POST with Multipart Form Data\n\n```c++\nhttplib::UploadFormDataItems items = {\n  { \"text1\", \"text default\", \"\", \"\" },\n  { \"text2\", \"aωb\", \"\", \"\" },\n  { \"file1\", \"h\\ne\\n\\nl\\nl\\no\\n\", \"hello.txt\", \"text/plain\" },\n  { \"file2\", \"{\\n  \\\"world\\\", true\\n}\\n\", \"world.json\", \"application/json\" },\n  { \"file3\", \"\", \"\", \"application/octet-stream\" },\n};\n\nauto res = cli.Post(\"/multipart\", items);\n```\n\n### PUT\n\n```c++\nres = cli.Put(\"/resource/foo\", \"text\", \"text/plain\");\n```\n\n### DELETE\n\n```c++\nres = cli.Delete(\"/resource/foo\");\n```\n\n### OPTIONS\n\n```c++\nres = cli.Options(\"*\");\nres = cli.Options(\"/resource/foo\");\n```\n\n### Timeout\n\n```c++\ncli.set_connection_timeout(0, 300000); // 300 milliseconds\ncli.set_read_timeout(5, 0); // 5 seconds\ncli.set_write_timeout(5, 0); // 5 seconds\n\n// This method works the same as curl's `--max-time` option\ncli.set_max_timeout(5000); // 5 seconds\n```\n\n### Receive content with a content receiver\n\n```c++\nstd::string body;\n\nauto res = cli.Get(\"/large-data\",\n  [&](const char *data, size_t data_length) {\n    body.append(data, data_length);\n    return true;\n  });\n```\n\n```cpp\nstd::string body;\n\nauto res = cli.Get(\n  \"/stream\", Headers(),\n  [&](const Response &response) {\n    EXPECT_EQ(StatusCode::OK_200, response.status);\n    return true; // return 'false' if you want to cancel the request.\n  },\n  [&](const char *data, size_t data_length) {\n    body.append(data, data_length);\n    return true; // return 'false' if you want to cancel the request.\n  });\n```\n\n### Send content with a content provider\n\n```cpp\nstd::string body = ...;\n\nauto res = cli.Post(\n  \"/stream\", body.size(),\n  [](size_t offset, size_t length, DataSink &sink) {\n    sink.write(body.data() + offset, length);\n    return true; // return 'false' if you want to cancel the request.\n  },\n  \"text/plain\");\n```\n\n### Chunked transfer encoding\n\n```cpp\nauto res = cli.Post(\n  \"/stream\",\n  [](size_t offset, DataSink &sink) {\n    sink.os << \"chunked data 1\";\n    sink.os << \"chunked data 2\";\n    sink.os << \"chunked data 3\";\n    sink.done();\n    return true; // return 'false' if you want to cancel the request.\n  },\n  \"text/plain\");\n```\n\n### With Progress Callback\n\n```cpp\nhttplib::Client cli(url, port);\n\n// prints: 0 / 000 bytes => 50% complete\nauto res = cli.Get(\"/\", [](size_t len, size_t total) {\n  printf(\"%lld / %lld bytes => %d%% complete\\n\",\n    len, total,\n    (int)(len*100/total));\n  return true; // return 'false' if you want to cancel the request.\n}\n);\n```\n\n![progress](https://user-images.githubusercontent.com/236374/33138910-495c4ecc-cf86-11e7-8693-2fc6d09615c4.gif)\n\n### Authentication\n\n```cpp\n// Basic Authentication\ncli.set_basic_auth(\"user\", \"pass\");\n\n// Digest Authentication\ncli.set_digest_auth(\"user\", \"pass\");\n\n// Bearer Token Authentication\ncli.set_bearer_token_auth(\"token\");\n```\n\n> [!NOTE]\n> OpenSSL is required for Digest Authentication.\n\n### Proxy server support\n\n```cpp\ncli.set_proxy(\"host\", port);\n\n// Basic Authentication\ncli.set_proxy_basic_auth(\"user\", \"pass\");\n\n// Digest Authentication\ncli.set_proxy_digest_auth(\"user\", \"pass\");\n\n// Bearer Token Authentication\ncli.set_proxy_bearer_token_auth(\"pass\");\n```\n\n> [!NOTE]\n> OpenSSL is required for Digest Authentication.\n\n### Range\n\n```cpp\nhttplib::Client cli(\"httpbin.org\");\n\nauto res = cli.Get(\"/range/32\", {\n  httplib::make_range_header({{1, 10}}) // 'Range: bytes=1-10'\n});\n// res->status should be 206.\n// res->body should be \"bcdefghijk\".\n```\n\n```cpp\nhttplib::make_range_header({{1, 10}, {20, -1}})      // 'Range: bytes=1-10, 20-'\nhttplib::make_range_header({{100, 199}, {500, 599}}) // 'Range: bytes=100-199, 500-599'\nhttplib::make_range_header({{0, 0}, {-1, 1}})        // 'Range: bytes=0-0, -1'\n```\n\n### Keep-Alive connection\n\n```cpp\nhttplib::Client cli(\"localhost\", 1234);\n\ncli.Get(\"/hello\");         // with \"Connection: close\"\n\ncli.set_keep_alive(true);\ncli.Get(\"/world\");\n\ncli.set_keep_alive(false);\ncli.Get(\"/last-request\");  // with \"Connection: close\"\n```\n\n### Redirect\n\n```cpp\nhttplib::Client cli(\"yahoo.com\");\n\nauto res = cli.Get(\"/\");\nres->status; // 301\n\ncli.set_follow_location(true);\nres = cli.Get(\"/\");\nres->status; // 200\n```\n\n### Use a specific network interface\n\n> [!NOTE]\n> This feature is not available on Windows, yet.\n\n```cpp\ncli.set_interface(\"eth0\"); // Interface name, IP address or host name\n```\n\n### Automatic Path Encoding\n\nThe client automatically encodes special characters in URL paths by default:\n\n```cpp\nhttplib::Client cli(\"https://example.com\");\n\n// Automatic path encoding (default behavior)\ncli.set_path_encode(true);\nauto res = cli.Get(\"/path with spaces/file.txt\"); // Automatically encodes spaces\n\n// Disable automatic path encoding\ncli.set_path_encode(false);\nauto res = cli.Get(\"/already%20encoded/path\"); // Use pre-encoded paths\n```\n\n- `set_path_encode(bool on)` - Controls automatic encoding of special characters in URL paths\n  - `true` (default): Automatically encodes spaces, plus signs, newlines, and other special characters\n  - `false`: Sends paths as-is without encoding (useful for pre-encoded URLs)\n\n### Performance Note for Local Connections\n\n> [!WARNING]\n> On Windows systems with improperly configured IPv6 settings, using \"localhost\" as the hostname may cause significant connection delays (up to 2 seconds per request) due to DNS resolution issues. This affects both client and server operations. For better performance when connecting to local services, use \"127.0.0.1\" instead of \"localhost\".\n> \n> See: https://github.com/yhirose/cpp-httplib/issues/366#issuecomment-593004264\n\n```cpp\n// May be slower on Windows due to DNS resolution delays\nhttplib::Client cli(\"localhost\", 8080);\nhttplib::Server svr;\nsvr.listen(\"localhost\", 8080);\n\n// Faster alternative for local connections\nhttplib::Client cli(\"127.0.0.1\", 8080);\nhttplib::Server svr;\nsvr.listen(\"127.0.0.1\", 8080);\n```\n\nCompression\n-----------\n\nThe server can apply compression to the following MIME type contents:\n\n  * all text types except text/event-stream\n  * image/svg+xml\n  * application/javascript\n  * application/json\n  * application/xml\n  * application/protobuf\n  * application/xhtml+xml\n\n### Zlib Support\n\n'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`. `libz` should be linked.\n\n### Brotli Support\n\nBrotli compression is available with `CPPHTTPLIB_BROTLI_SUPPORT`. Necessary libraries should be linked.\nPlease see https://github.com/google/brotli for more detail.\n\n### Zstd Support\n\nZstd compression is available with `CPPHTTPLIB_ZSTD_SUPPORT`. Necessary libraries should be linked.\nPlease see https://github.com/facebook/zstd for more detail.\n\n### Default `Accept-Encoding` value\n\nThe default `Accept-Encoding` value contains all possible compression types. So, the following two examples are same.\n\n```c++\nres = cli.Get(\"/resource/foo\");\nres = cli.Get(\"/resource/foo\", {{\"Accept-Encoding\", \"br, gzip, deflate, zstd\"}});\n```\n\nIf we don't want a response without compression, we have to set `Accept-Encoding` to an empty string. This behavior is similar to curl.\n\n```c++\nres = cli.Get(\"/resource/foo\", {{\"Accept-Encoding\", \"\"}});\n```\n\n### Compress request body on client\n\n```c++\ncli.set_compress(true);\nres = cli.Post(\"/resource/foo\", \"...\", \"text/plain\");\n```\n\n### Compress response body on client\n\n```c++\ncli.set_decompress(false);\nres = cli.Get(\"/resource/foo\");\nres->body; // Compressed data\n\n```\n\nUnix Domain Socket Support\n--------------------------\n\nUnix Domain Socket support is available on Linux and macOS.\n\n```c++\n// Server\nhttplib::Server svr;\nsvr.set_address_family(AF_UNIX).listen(\"./my-socket.sock\", 80);\n\n// Client\nhttplib::Client cli(\"./my-socket.sock\");\ncli.set_address_family(AF_UNIX);\n```\n\n\"my-socket.sock\" can be a relative path or an absolute path. Your application must have the appropriate permissions for the path. You can also use an abstract socket address on Linux. To use an abstract socket address, prepend a null byte ('\\x00') to the path.\n\nThis library automatically sets the Host header to \"localhost\" for Unix socket connections, similar to curl's behavior:\n\n\nURI Encoding/Decoding Utilities\n-------------------------------\n\ncpp-httplib provides utility functions for URI encoding and decoding:\n\n```cpp\n#include <httplib.h>\n\nstd::string url = \"https://example.com/search?q=hello world\";\nstd::string encoded = httplib::encode_uri(url);\nstd::string decoded = httplib::decode_uri(encoded);\n\nstd::string param = \"hello world\";\nstd::string encoded_component = httplib::encode_uri_component(param);\nstd::string decoded_component = httplib::decode_uri_component(encoded_component);\n```\n\n### Functions\n\n- `encode_uri(const std::string &value)` - Encodes a full URI, preserving reserved characters like `://`, `?`, `&`, `=`\n- `decode_uri(const std::string &value)` - Decodes a URI-encoded string\n- `encode_uri_component(const std::string &value)` - Encodes a URI component (query parameter, path segment), encoding all reserved characters\n- `decode_uri_component(const std::string &value)` - Decodes a URI component\n\nUse `encode_uri()` for full URLs and `encode_uri_component()` for individual query parameters or path segments.\n\n\nSplit httplib.h into .h and .cc\n-------------------------------\n\n```console\n$ ./split.py -h\nusage: split.py [-h] [-e EXTENSION] [-o OUT]\n\nThis script splits httplib.h into .h and .cc parts.\n\noptional arguments:\n  -h, --help            show this help message and exit\n  -e EXTENSION, --extension EXTENSION\n                        extension of the implementation file (default: cc)\n  -o OUT, --out OUT     where to write the files (default: out)\n\n$ ./split.py\nWrote out/httplib.h and out/httplib.cc\n```\n\nDockerfile for Static HTTP Server\n---------------------------------\n\nDockerfile for static HTTP server is available. Port number of this HTTP server is 80, and it serves static files from `/html` directory in the container.\n\n```bash\n> docker build -t cpp-httplib-server .\n...\n\n> docker run --rm -it -p 8080:80 -v ./docker/html:/html cpp-httplib-server\nServing HTTP on 0.0.0.0 port 80 ...\n192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] \"GET / HTTP/1.1\" 200 599 \"-\" \"curl/8.7.1\"\n192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] \"GET / HTTP/1.1\" 200 599 \"-\" \"Mozilla/5.0 ...\"\n192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] \"GET /favicon.ico HTTP/1.1\" 404 152 \"-\" \"Mozilla/5.0 ...\"\n```\n\nFrom Docker Hub\n\n```bash\n> docker run --rm -it -p 8080:80 -v ./docker/html:/html yhirose4dockerhub/cpp-httplib-server\nServing HTTP on 0.0.0.0 port 80 ...\n192.168.65.1 - - [31/Aug/2024:21:33:56 +0000] \"GET / HTTP/1.1\" 200 599 \"-\" \"curl/8.7.1\"\n192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] \"GET / HTTP/1.1\" 200 599 \"-\" \"Mozilla/5.0 ...\"\n192.168.65.1 - - [31/Aug/2024:21:34:26 +0000] \"GET /favicon.ico HTTP/1.1\" 404 152 \"-\" \"Mozilla/5.0 ...\"\n```\n\nNOTE\n----\n\n### Regular Expression Stack Overflow\n\n> [!CAUTION]\n> When using complex regex patterns in route handlers, be aware that certain patterns may cause stack overflow during pattern matching. This is a known issue with `std::regex` implementations and affects the `dispatch_request()` method.\n> \n> ```cpp\n> // This pattern can cause stack overflow with large input\n> svr.Get(\".*\", handler);\n> ```\n> \n> Consider using simpler patterns or path parameters to avoid this issue:\n> \n> ```cpp\n> // Safer alternatives\n> svr.Get(\"/users/:id\", handler);           // Path parameters\n> svr.Get(R\"(/api/v\\d+/.*)\", handler);     // More specific patterns\n> ```\n\n### g++\n\ng++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).\n\n### Windows\n\nInclude `httplib.h` before `Windows.h` or include `Windows.h` by defining `WIN32_LEAN_AND_MEAN` beforehand.\n\n```cpp\n#include <httplib.h>\n#include <Windows.h>\n```\n\n```cpp\n#define WIN32_LEAN_AND_MEAN\n#include <Windows.h>\n#include <httplib.h>\n```\n\n> [!NOTE]\n> cpp-httplib officially supports only the latest Visual Studio. It might work with former versions of Visual Studio, but I can no longer verify it. Pull requests are always welcome for the older versions of Visual Studio unless they break the C++11 conformance.\n\n> [!NOTE]\n> Windows 8 or lower, Visual Studio 2015 or lower, and Cygwin and MSYS2 including MinGW are neither supported nor tested.\n\nLicense\n-------\n\nMIT license (© 2025 Yuji Hirose)\n\nSpecial Thanks To\n-----------------\n\n[These folks](https://github.com/yhirose/cpp-httplib/graphs/contributors) made great contributions to polish this library to totally another level from a simple toy!\n"
  },
  {
    "path": "third_party/cpp-httplib/cpp-httplib/httplib.h",
    "content": "//\n//  httplib.h\n//\n//  Copyright (c) 2025 Yuji Hirose. All rights reserved.\n//  MIT License\n//\n\n#ifndef CPPHTTPLIB_HTTPLIB_H\n#define CPPHTTPLIB_HTTPLIB_H\n\n#define CPPHTTPLIB_VERSION \"0.27.0\"\n#define CPPHTTPLIB_VERSION_NUM \"0x001B00\"\n\n/*\n * Configuration\n */\n\n#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND\n#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND\n#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000\n#endif\n\n#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT\n#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100\n#endif\n\n#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND\n#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300\n#endif\n\n#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND\n#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND\n#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND\n#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND\n#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND\n#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND\n#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND\n#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND\n#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND\n#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND\n#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND\n#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0\n#endif\n\n#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND\n#ifdef _WIN32\n#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000\n#else\n#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0\n#endif\n#endif\n\n#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH\n#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192\n#endif\n\n#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH\n#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192\n#endif\n\n#ifndef CPPHTTPLIB_HEADER_MAX_COUNT\n#define CPPHTTPLIB_HEADER_MAX_COUNT 100\n#endif\n\n#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT\n#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20\n#endif\n\n#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT\n#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024\n#endif\n\n#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH\n#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())\n#endif\n\n#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH\n#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192\n#endif\n\n#ifndef CPPHTTPLIB_RANGE_MAX_COUNT\n#define CPPHTTPLIB_RANGE_MAX_COUNT 1024\n#endif\n\n#ifndef CPPHTTPLIB_TCP_NODELAY\n#define CPPHTTPLIB_TCP_NODELAY false\n#endif\n\n#ifndef CPPHTTPLIB_IPV6_V6ONLY\n#define CPPHTTPLIB_IPV6_V6ONLY false\n#endif\n\n#ifndef CPPHTTPLIB_RECV_BUFSIZ\n#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)\n#endif\n\n#ifndef CPPHTTPLIB_SEND_BUFSIZ\n#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)\n#endif\n\n#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ\n#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)\n#endif\n\n#ifndef CPPHTTPLIB_THREAD_POOL_COUNT\n#define CPPHTTPLIB_THREAD_POOL_COUNT                                           \\\n  ((std::max)(8u, std::thread::hardware_concurrency() > 0                      \\\n                      ? std::thread::hardware_concurrency() - 1                \\\n                      : 0))\n#endif\n\n#ifndef CPPHTTPLIB_RECV_FLAGS\n#define CPPHTTPLIB_RECV_FLAGS 0\n#endif\n\n#ifndef CPPHTTPLIB_SEND_FLAGS\n#define CPPHTTPLIB_SEND_FLAGS 0\n#endif\n\n#ifndef CPPHTTPLIB_LISTEN_BACKLOG\n#define CPPHTTPLIB_LISTEN_BACKLOG 5\n#endif\n\n#ifndef CPPHTTPLIB_MAX_LINE_LENGTH\n#define CPPHTTPLIB_MAX_LINE_LENGTH 32768\n#endif\n\n#define CPPHTTPLIB_NO_EXCEPTIONS 1\n\n/*\n * Headers\n */\n\n#ifdef _WIN32\n#ifndef _CRT_SECURE_NO_WARNINGS\n#define _CRT_SECURE_NO_WARNINGS\n#endif //_CRT_SECURE_NO_WARNINGS\n\n#ifndef _CRT_NONSTDC_NO_DEPRECATE\n#define _CRT_NONSTDC_NO_DEPRECATE\n#endif //_CRT_NONSTDC_NO_DEPRECATE\n\n#if defined(_MSC_VER)\n#if _MSC_VER < 1900\n#error Sorry, Visual Studio versions prior to 2015 are not supported\n#endif\n\n#pragma comment(lib, \"ws2_32.lib\")\n\n#ifdef _WIN64\nusing ssize_t = __int64;\n#else\nusing ssize_t = long;\n#endif\n#endif // _MSC_VER\n\n#ifndef S_ISREG\n#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)\n#endif // S_ISREG\n\n#ifndef S_ISDIR\n#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)\n#endif // S_ISDIR\n\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif // NOMINMAX\n\n#include <io.h>\n#include <winsock2.h>\n#include <ws2tcpip.h>\n\n#if defined(__has_include)\n#if __has_include(<afunix.h>)\n// afunix.h uses types declared in winsock2.h, so has to be included after it.\n#include <afunix.h>\n#define CPPHTTPLIB_HAVE_AFUNIX_H 1\n#endif\n#endif\n\n#ifndef WSA_FLAG_NO_HANDLE_INHERIT\n#define WSA_FLAG_NO_HANDLE_INHERIT 0x80\n#endif\n\nusing nfds_t = unsigned long;\nusing socket_t = SOCKET;\nusing socklen_t = int;\n\n#else // not _WIN32\n\n#include <arpa/inet.h>\n#if !defined(_AIX) && !defined(__MVS__)\n#include <ifaddrs.h>\n#endif\n#ifdef __MVS__\n#include <strings.h>\n#ifndef NI_MAXHOST\n#define NI_MAXHOST 1025\n#endif\n#endif\n#include <net/if.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#ifdef __linux__\n#include <resolv.h>\n#endif\n#include <csignal>\n#include <netinet/tcp.h>\n#include <poll.h>\n#include <pthread.h>\n#include <sys/mman.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <unistd.h>\n\nusing socket_t = int;\n#ifndef INVALID_SOCKET\n#define INVALID_SOCKET (-1)\n#endif\n#endif //_WIN32\n\n#if defined(__APPLE__)\n#include <TargetConditionals.h>\n#endif\n\n#include <algorithm>\n#include <array>\n#include <atomic>\n#include <cassert>\n#include <cctype>\n#include <climits>\n#include <condition_variable>\n#include <cstring>\n#include <errno.h>\n#include <exception>\n#include <fcntl.h>\n#include <functional>\n#include <iomanip>\n#include <iostream>\n#include <list>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <random>\n#include <regex>\n#include <set>\n#include <sstream>\n#include <string>\n#include <sys/stat.h>\n#include <thread>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#if defined(CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO) ||                        \\\n    defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)\n#if TARGET_OS_MAC\n#include <CFNetwork/CFHost.h>\n#include <CoreFoundation/CoreFoundation.h>\n#endif\n#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO or\n       // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n#ifdef _WIN32\n#include <wincrypt.h>\n\n// these are defined in wincrypt.h and it breaks compilation if BoringSSL is\n// used\n#undef X509_NAME\n#undef X509_CERT_PAIR\n#undef X509_EXTENSIONS\n#undef PKCS7_SIGNER_INFO\n\n#ifdef _MSC_VER\n#pragma comment(lib, \"crypt32.lib\")\n#endif\n#endif // _WIN32\n\n#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)\n#if TARGET_OS_MAC\n#include <Security/Security.h>\n#endif\n#endif // CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO\n\n#include <openssl/err.h>\n#include <openssl/evp.h>\n#include <openssl/ssl.h>\n#include <openssl/x509v3.h>\n\n#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)\n#include <openssl/applink.c>\n#endif\n\n#include <iostream>\n#include <sstream>\n\n#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)\n#if OPENSSL_VERSION_NUMBER < 0x1010107f\n#error Please use OpenSSL or a current version of BoringSSL\n#endif\n#define SSL_get1_peer_certificate SSL_get_peer_certificate\n#elif OPENSSL_VERSION_NUMBER < 0x30000000L\n#error Sorry, OpenSSL versions prior to 3.0.0 are not supported\n#endif\n\n#endif // CPPHTTPLIB_OPENSSL_SUPPORT\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n#include \"third_party/zlib/zlib_crashpad.h\"\n#endif\n\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n#include <brotli/decode.h>\n#include <brotli/encode.h>\n#endif\n\n#ifdef CPPHTTPLIB_ZSTD_SUPPORT\n#include <zstd.h>\n#endif\n\n/*\n * Declaration\n */\nnamespace httplib {\n\nnamespace detail {\n\n/*\n * Backport std::make_unique from C++14.\n *\n * NOTE: This code came up with the following stackoverflow post:\n * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique\n *\n */\n\ntemplate <class T, class... Args>\ntypename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type\nmake_unique(Args &&...args) {\n  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));\n}\n\ntemplate <class T>\ntypename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type\nmake_unique(std::size_t n) {\n  typedef typename std::remove_extent<T>::type RT;\n  return std::unique_ptr<T>(new RT[n]);\n}\n\nnamespace case_ignore {\n\ninline unsigned char to_lower(int c) {\n  const static unsigned char table[256] = {\n      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,\n      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,\n      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,\n      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,\n      60,  61,  62,  63,  64,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106,\n      107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,\n      122, 91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,\n      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,\n      120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,\n      135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,\n      150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,\n      165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,\n      180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,\n      227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,\n      242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,\n      225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,\n      240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,\n      255,\n  };\n  return table[(unsigned char)(char)c];\n}\n\ninline bool equal(const std::string &a, const std::string &b) {\n  return a.size() == b.size() &&\n         std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {\n           return to_lower(ca) == to_lower(cb);\n         });\n}\n\nstruct equal_to {\n  bool operator()(const std::string &a, const std::string &b) const {\n    return equal(a, b);\n  }\n};\n\nstruct hash {\n  size_t operator()(const std::string &key) const {\n    return hash_core(key.data(), key.size(), 0);\n  }\n\n  size_t hash_core(const char *s, size_t l, size_t h) const {\n    return (l == 0) ? h\n                    : hash_core(s + 1, l - 1,\n                                // Unsets the 6 high bits of h, therefore no\n                                // overflow happens\n                                (((std::numeric_limits<size_t>::max)() >> 6) &\n                                 h * 33) ^\n                                    static_cast<unsigned char>(to_lower(*s)));\n  }\n};\n\ntemplate <typename T>\nusing unordered_set = std::unordered_set<T, detail::case_ignore::hash,\n                                         detail::case_ignore::equal_to>;\n\n} // namespace case_ignore\n\n// This is based on\n// \"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189\".\n\nstruct scope_exit {\n  explicit scope_exit(std::function<void(void)> &&f)\n      : exit_function(std::move(f)), execute_on_destruction{true} {}\n\n  scope_exit(scope_exit &&rhs) noexcept\n      : exit_function(std::move(rhs.exit_function)),\n        execute_on_destruction{rhs.execute_on_destruction} {\n    rhs.release();\n  }\n\n  ~scope_exit() {\n    if (execute_on_destruction) { this->exit_function(); }\n  }\n\n  void release() { this->execute_on_destruction = false; }\n\nprivate:\n  scope_exit(const scope_exit &) = delete;\n  void operator=(const scope_exit &) = delete;\n  scope_exit &operator=(scope_exit &&) = delete;\n\n  std::function<void(void)> exit_function;\n  bool execute_on_destruction;\n};\n\n} // namespace detail\n\nenum SSLVerifierResponse {\n  // no decision has been made, use the built-in certificate verifier\n  NoDecisionMade,\n  // connection certificate is verified and accepted\n  CertificateAccepted,\n  // connection certificate was processed but is rejected\n  CertificateRejected\n};\n\nenum StatusCode {\n  // Information responses\n  Continue_100 = 100,\n  SwitchingProtocol_101 = 101,\n  Processing_102 = 102,\n  EarlyHints_103 = 103,\n\n  // Successful responses\n  OK_200 = 200,\n  Created_201 = 201,\n  Accepted_202 = 202,\n  NonAuthoritativeInformation_203 = 203,\n  NoContent_204 = 204,\n  ResetContent_205 = 205,\n  PartialContent_206 = 206,\n  MultiStatus_207 = 207,\n  AlreadyReported_208 = 208,\n  IMUsed_226 = 226,\n\n  // Redirection messages\n  MultipleChoices_300 = 300,\n  MovedPermanently_301 = 301,\n  Found_302 = 302,\n  SeeOther_303 = 303,\n  NotModified_304 = 304,\n  UseProxy_305 = 305,\n  unused_306 = 306,\n  TemporaryRedirect_307 = 307,\n  PermanentRedirect_308 = 308,\n\n  // Client error responses\n  BadRequest_400 = 400,\n  Unauthorized_401 = 401,\n  PaymentRequired_402 = 402,\n  Forbidden_403 = 403,\n  NotFound_404 = 404,\n  MethodNotAllowed_405 = 405,\n  NotAcceptable_406 = 406,\n  ProxyAuthenticationRequired_407 = 407,\n  RequestTimeout_408 = 408,\n  Conflict_409 = 409,\n  Gone_410 = 410,\n  LengthRequired_411 = 411,\n  PreconditionFailed_412 = 412,\n  PayloadTooLarge_413 = 413,\n  UriTooLong_414 = 414,\n  UnsupportedMediaType_415 = 415,\n  RangeNotSatisfiable_416 = 416,\n  ExpectationFailed_417 = 417,\n  ImATeapot_418 = 418,\n  MisdirectedRequest_421 = 421,\n  UnprocessableContent_422 = 422,\n  Locked_423 = 423,\n  FailedDependency_424 = 424,\n  TooEarly_425 = 425,\n  UpgradeRequired_426 = 426,\n  PreconditionRequired_428 = 428,\n  TooManyRequests_429 = 429,\n  RequestHeaderFieldsTooLarge_431 = 431,\n  UnavailableForLegalReasons_451 = 451,\n\n  // Server error responses\n  InternalServerError_500 = 500,\n  NotImplemented_501 = 501,\n  BadGateway_502 = 502,\n  ServiceUnavailable_503 = 503,\n  GatewayTimeout_504 = 504,\n  HttpVersionNotSupported_505 = 505,\n  VariantAlsoNegotiates_506 = 506,\n  InsufficientStorage_507 = 507,\n  LoopDetected_508 = 508,\n  NotExtended_510 = 510,\n  NetworkAuthenticationRequired_511 = 511,\n};\n\nusing Headers =\n    std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,\n                            detail::case_ignore::equal_to>;\n\nusing Params = std::multimap<std::string, std::string>;\nusing Match = std::smatch;\n\nusing DownloadProgress = std::function<bool(size_t current, size_t total)>;\nusing UploadProgress = std::function<bool(size_t current, size_t total)>;\n\nstruct Response;\nusing ResponseHandler = std::function<bool(const Response &response)>;\n\nstruct FormData {\n  std::string name;\n  std::string content;\n  std::string filename;\n  std::string content_type;\n  Headers headers;\n};\n\nstruct FormField {\n  std::string name;\n  std::string content;\n  Headers headers;\n};\nusing FormFields = std::multimap<std::string, FormField>;\n\nusing FormFiles = std::multimap<std::string, FormData>;\n\nstruct MultipartFormData {\n  FormFields fields; // Text fields from multipart\n  FormFiles files;   // Files from multipart\n\n  // Text field access\n  std::string get_field(const std::string &key, size_t id = 0) const;\n  std::vector<std::string> get_fields(const std::string &key) const;\n  bool has_field(const std::string &key) const;\n  size_t get_field_count(const std::string &key) const;\n\n  // File access\n  FormData get_file(const std::string &key, size_t id = 0) const;\n  std::vector<FormData> get_files(const std::string &key) const;\n  bool has_file(const std::string &key) const;\n  size_t get_file_count(const std::string &key) const;\n};\n\nstruct UploadFormData {\n  std::string name;\n  std::string content;\n  std::string filename;\n  std::string content_type;\n};\nusing UploadFormDataItems = std::vector<UploadFormData>;\n\nclass DataSink {\npublic:\n  DataSink() : os(&sb_), sb_(*this) {}\n\n  DataSink(const DataSink &) = delete;\n  DataSink &operator=(const DataSink &) = delete;\n  DataSink(DataSink &&) = delete;\n  DataSink &operator=(DataSink &&) = delete;\n\n  std::function<bool(const char *data, size_t data_len)> write;\n  std::function<bool()> is_writable;\n  std::function<void()> done;\n  std::function<void(const Headers &trailer)> done_with_trailer;\n  std::ostream os;\n\nprivate:\n  class data_sink_streambuf final : public std::streambuf {\n  public:\n    explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}\n\n  protected:\n    std::streamsize xsputn(const char *s, std::streamsize n) override {\n      sink_.write(s, static_cast<size_t>(n));\n      return n;\n    }\n\n  private:\n    DataSink &sink_;\n  };\n\n  data_sink_streambuf sb_;\n};\n\nusing ContentProvider =\n    std::function<bool(size_t offset, size_t length, DataSink &sink)>;\n\nusing ContentProviderWithoutLength =\n    std::function<bool(size_t offset, DataSink &sink)>;\n\nusing ContentProviderResourceReleaser = std::function<void(bool success)>;\n\nstruct FormDataProvider {\n  std::string name;\n  ContentProviderWithoutLength provider;\n  std::string filename;\n  std::string content_type;\n};\nusing FormDataProviderItems = std::vector<FormDataProvider>;\n\nusing ContentReceiverWithProgress = std::function<bool(\n    const char *data, size_t data_length, size_t offset, size_t total_length)>;\n\nusing ContentReceiver =\n    std::function<bool(const char *data, size_t data_length)>;\n\nusing FormDataHeader = std::function<bool(const FormData &file)>;\n\nclass ContentReader {\npublic:\n  using Reader = std::function<bool(ContentReceiver receiver)>;\n  using FormDataReader =\n      std::function<bool(FormDataHeader header, ContentReceiver receiver)>;\n\n  ContentReader(Reader reader, FormDataReader multipart_reader)\n      : reader_(std::move(reader)),\n        formdata_reader_(std::move(multipart_reader)) {}\n\n  bool operator()(FormDataHeader header, ContentReceiver receiver) const {\n    return formdata_reader_(std::move(header), std::move(receiver));\n  }\n\n  bool operator()(ContentReceiver receiver) const {\n    return reader_(std::move(receiver));\n  }\n\n  Reader reader_;\n  FormDataReader formdata_reader_;\n};\n\nusing Range = std::pair<ssize_t, ssize_t>;\nusing Ranges = std::vector<Range>;\n\nstruct Request {\n  std::string method;\n  std::string path;\n  std::string matched_route;\n  Params params;\n  Headers headers;\n  Headers trailers;\n  std::string body;\n\n  std::string remote_addr;\n  int remote_port = -1;\n  std::string local_addr;\n  int local_port = -1;\n\n  // for server\n  std::string version;\n  std::string target;\n  MultipartFormData form;\n  Ranges ranges;\n  Match matches;\n  std::unordered_map<std::string, std::string> path_params;\n  std::function<bool()> is_connection_closed = []() { return true; };\n\n  // for client\n  std::vector<std::string> accept_content_types;\n  ResponseHandler response_handler;\n  ContentReceiverWithProgress content_receiver;\n  DownloadProgress download_progress;\n  UploadProgress upload_progress;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  const SSL *ssl = nullptr;\n#endif\n\n  bool has_header(const std::string &key) const;\n  std::string get_header_value(const std::string &key, const char *def = \"\",\n                               size_t id = 0) const;\n  size_t get_header_value_u64(const std::string &key, size_t def = 0,\n                              size_t id = 0) const;\n  size_t get_header_value_count(const std::string &key) const;\n  void set_header(const std::string &key, const std::string &val);\n\n  bool has_trailer(const std::string &key) const;\n  std::string get_trailer_value(const std::string &key, size_t id = 0) const;\n  size_t get_trailer_value_count(const std::string &key) const;\n\n  bool has_param(const std::string &key) const;\n  std::string get_param_value(const std::string &key, size_t id = 0) const;\n  size_t get_param_value_count(const std::string &key) const;\n\n  bool is_multipart_form_data() const;\n\n  // private members...\n  size_t redirect_count_ = CPPHTTPLIB_REDIRECT_MAX_COUNT;\n  size_t content_length_ = 0;\n  ContentProvider content_provider_;\n  bool is_chunked_content_provider_ = false;\n  size_t authorization_count_ = 0;\n  std::chrono::time_point<std::chrono::steady_clock> start_time_ =\n      (std::chrono::steady_clock::time_point::min)();\n};\n\nstruct Response {\n  std::string version;\n  int status = -1;\n  std::string reason;\n  Headers headers;\n  Headers trailers;\n  std::string body;\n  std::string location; // Redirect location\n\n  bool has_header(const std::string &key) const;\n  std::string get_header_value(const std::string &key, const char *def = \"\",\n                               size_t id = 0) const;\n  size_t get_header_value_u64(const std::string &key, size_t def = 0,\n                              size_t id = 0) const;\n  size_t get_header_value_count(const std::string &key) const;\n  void set_header(const std::string &key, const std::string &val);\n\n  bool has_trailer(const std::string &key) const;\n  std::string get_trailer_value(const std::string &key, size_t id = 0) const;\n  size_t get_trailer_value_count(const std::string &key) const;\n\n  void set_redirect(const std::string &url, int status = StatusCode::Found_302);\n  void set_content(const char *s, size_t n, const std::string &content_type);\n  void set_content(const std::string &s, const std::string &content_type);\n  void set_content(std::string &&s, const std::string &content_type);\n\n  void set_content_provider(\n      size_t length, const std::string &content_type, ContentProvider provider,\n      ContentProviderResourceReleaser resource_releaser = nullptr);\n\n  void set_content_provider(\n      const std::string &content_type, ContentProviderWithoutLength provider,\n      ContentProviderResourceReleaser resource_releaser = nullptr);\n\n  void set_chunked_content_provider(\n      const std::string &content_type, ContentProviderWithoutLength provider,\n      ContentProviderResourceReleaser resource_releaser = nullptr);\n\n  void set_file_content(const std::string &path,\n                        const std::string &content_type);\n  void set_file_content(const std::string &path);\n\n  Response() = default;\n  Response(const Response &) = default;\n  Response &operator=(const Response &) = default;\n  Response(Response &&) = default;\n  Response &operator=(Response &&) = default;\n  ~Response() {\n    if (content_provider_resource_releaser_) {\n      content_provider_resource_releaser_(content_provider_success_);\n    }\n  }\n\n  // private members...\n  size_t content_length_ = 0;\n  ContentProvider content_provider_;\n  ContentProviderResourceReleaser content_provider_resource_releaser_;\n  bool is_chunked_content_provider_ = false;\n  bool content_provider_success_ = false;\n  std::string file_content_path_;\n  std::string file_content_content_type_;\n};\n\nclass Stream {\npublic:\n  virtual ~Stream() = default;\n\n  virtual bool is_readable() const = 0;\n  virtual bool wait_readable() const = 0;\n  virtual bool wait_writable() const = 0;\n\n  virtual ssize_t read(char *ptr, size_t size) = 0;\n  virtual ssize_t write(const char *ptr, size_t size) = 0;\n  virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;\n  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;\n  virtual socket_t socket() const = 0;\n\n  virtual time_t duration() const = 0;\n\n  ssize_t write(const char *ptr);\n  ssize_t write(const std::string &s);\n};\n\nclass TaskQueue {\npublic:\n  TaskQueue() = default;\n  virtual ~TaskQueue() = default;\n\n  virtual bool enqueue(std::function<void()> fn) = 0;\n  virtual void shutdown() = 0;\n\n  virtual void on_idle() {}\n};\n\nclass ThreadPool final : public TaskQueue {\npublic:\n  explicit ThreadPool(size_t n, size_t mqr = 0)\n      : shutdown_(false), max_queued_requests_(mqr) {\n    while (n) {\n      threads_.emplace_back(worker(*this));\n      n--;\n    }\n  }\n\n  ThreadPool(const ThreadPool &) = delete;\n  ~ThreadPool() override = default;\n\n  bool enqueue(std::function<void()> fn) override {\n    {\n      std::unique_lock<std::mutex> lock(mutex_);\n      if (max_queued_requests_ > 0 && jobs_.size() >= max_queued_requests_) {\n        return false;\n      }\n      jobs_.push_back(std::move(fn));\n    }\n\n    cond_.notify_one();\n    return true;\n  }\n\n  void shutdown() override {\n    // Stop all worker threads...\n    {\n      std::unique_lock<std::mutex> lock(mutex_);\n      shutdown_ = true;\n    }\n\n    cond_.notify_all();\n\n    // Join...\n    for (auto &t : threads_) {\n      t.join();\n    }\n  }\n\nprivate:\n  struct worker {\n    explicit worker(ThreadPool &pool) : pool_(pool) {}\n\n    void operator()() {\n      for (;;) {\n        std::function<void()> fn;\n        {\n          std::unique_lock<std::mutex> lock(pool_.mutex_);\n\n          pool_.cond_.wait(\n              lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; });\n\n          if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }\n\n          fn = pool_.jobs_.front();\n          pool_.jobs_.pop_front();\n        }\n\n        assert(true == static_cast<bool>(fn));\n        fn();\n      }\n\n#if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) &&   \\\n    !defined(LIBRESSL_VERSION_NUMBER)\n      OPENSSL_thread_stop();\n#endif\n    }\n\n    ThreadPool &pool_;\n  };\n  friend struct worker;\n\n  std::vector<std::thread> threads_;\n  std::list<std::function<void()>> jobs_;\n\n  bool shutdown_;\n  size_t max_queued_requests_ = 0;\n\n  std::condition_variable cond_;\n  std::mutex mutex_;\n};\n\nusing Logger = std::function<void(const Request &, const Response &)>;\n\n// Forward declaration for Error type\nenum class Error;\nusing ErrorLogger = std::function<void(const Error &, const Request *)>;\n\nusing SocketOptions = std::function<void(socket_t sock)>;\n\nnamespace detail {\n\nbool set_socket_opt_impl(socket_t sock, int level, int optname,\n                         const void *optval, socklen_t optlen);\nbool set_socket_opt(socket_t sock, int level, int optname, int opt);\nbool set_socket_opt_time(socket_t sock, int level, int optname, time_t sec,\n                         time_t usec);\n\n} // namespace detail\n\nvoid default_socket_options(socket_t sock);\n\nconst char *status_message(int status);\n\nstd::string get_bearer_token_auth(const Request &req);\n\nnamespace detail {\n\nclass MatcherBase {\npublic:\n  MatcherBase(std::string pattern) : pattern_(pattern) {}\n  virtual ~MatcherBase() = default;\n\n  const std::string &pattern() const { return pattern_; }\n\n  // Match request path and populate its matches and\n  virtual bool match(Request &request) const = 0;\n\nprivate:\n  std::string pattern_;\n};\n\n/**\n * Captures parameters in request path and stores them in Request::path_params\n *\n * Capture name is a substring of a pattern from : to /.\n * The rest of the pattern is matched against the request path directly\n * Parameters are captured starting from the next character after\n * the end of the last matched static pattern fragment until the next /.\n *\n * Example pattern:\n * \"/path/fragments/:capture/more/fragments/:second_capture\"\n * Static fragments:\n * \"/path/fragments/\", \"more/fragments/\"\n *\n * Given the following request path:\n * \"/path/fragments/:1/more/fragments/:2\"\n * the resulting capture will be\n * {{\"capture\", \"1\"}, {\"second_capture\", \"2\"}}\n */\nclass PathParamsMatcher final : public MatcherBase {\npublic:\n  PathParamsMatcher(const std::string &pattern);\n\n  bool match(Request &request) const override;\n\nprivate:\n  // Treat segment separators as the end of path parameter capture\n  // Does not need to handle query parameters as they are parsed before path\n  // matching\n  static constexpr char separator = '/';\n\n  // Contains static path fragments to match against, excluding the '/' after\n  // path params\n  // Fragments are separated by path params\n  std::vector<std::string> static_fragments_;\n  // Stores the names of the path parameters to be used as keys in the\n  // Request::path_params map\n  std::vector<std::string> param_names_;\n};\n\n/**\n * Performs std::regex_match on request path\n * and stores the result in Request::matches\n *\n * Note that regex match is performed directly on the whole request.\n * This means that wildcard patterns may match multiple path segments with /:\n * \"/begin/(.*)/end\" will match both \"/begin/middle/end\" and \"/begin/1/2/end\".\n */\nclass RegexMatcher final : public MatcherBase {\npublic:\n  RegexMatcher(const std::string &pattern)\n      : MatcherBase(pattern), regex_(pattern) {}\n\n  bool match(Request &request) const override;\n\nprivate:\n  std::regex regex_;\n};\n\nssize_t write_headers(Stream &strm, const Headers &headers);\n\nstd::string make_host_and_port_string(const std::string &host, int port,\n                                      bool is_ssl);\n\n} // namespace detail\n\nclass Server {\npublic:\n  using Handler = std::function<void(const Request &, Response &)>;\n\n  using ExceptionHandler =\n      std::function<void(const Request &, Response &, std::exception_ptr ep)>;\n\n  enum class HandlerResponse {\n    Handled,\n    Unhandled,\n  };\n  using HandlerWithResponse =\n      std::function<HandlerResponse(const Request &, Response &)>;\n\n  using HandlerWithContentReader = std::function<void(\n      const Request &, Response &, const ContentReader &content_reader)>;\n\n  using Expect100ContinueHandler =\n      std::function<int(const Request &, Response &)>;\n\n  Server();\n\n  virtual ~Server();\n\n  virtual bool is_valid() const;\n\n  Server &Get(const std::string &pattern, Handler handler);\n  Server &Post(const std::string &pattern, Handler handler);\n  Server &Post(const std::string &pattern, HandlerWithContentReader handler);\n  Server &Put(const std::string &pattern, Handler handler);\n  Server &Put(const std::string &pattern, HandlerWithContentReader handler);\n  Server &Patch(const std::string &pattern, Handler handler);\n  Server &Patch(const std::string &pattern, HandlerWithContentReader handler);\n  Server &Delete(const std::string &pattern, Handler handler);\n  Server &Delete(const std::string &pattern, HandlerWithContentReader handler);\n  Server &Options(const std::string &pattern, Handler handler);\n\n  bool set_base_dir(const std::string &dir,\n                    const std::string &mount_point = std::string());\n  bool set_mount_point(const std::string &mount_point, const std::string &dir,\n                       Headers headers = Headers());\n  bool remove_mount_point(const std::string &mount_point);\n  Server &set_file_extension_and_mimetype_mapping(const std::string &ext,\n                                                  const std::string &mime);\n  Server &set_default_file_mimetype(const std::string &mime);\n  Server &set_file_request_handler(Handler handler);\n\n  template <class ErrorHandlerFunc>\n  Server &set_error_handler(ErrorHandlerFunc &&handler) {\n    return set_error_handler_core(\n        std::forward<ErrorHandlerFunc>(handler),\n        std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});\n  }\n\n  Server &set_exception_handler(ExceptionHandler handler);\n\n  Server &set_pre_routing_handler(HandlerWithResponse handler);\n  Server &set_post_routing_handler(Handler handler);\n\n  Server &set_pre_request_handler(HandlerWithResponse handler);\n\n  Server &set_expect_100_continue_handler(Expect100ContinueHandler handler);\n  Server &set_logger(Logger logger);\n  Server &set_pre_compression_logger(Logger logger);\n  Server &set_error_logger(ErrorLogger error_logger);\n\n  Server &set_address_family(int family);\n  Server &set_tcp_nodelay(bool on);\n  Server &set_ipv6_v6only(bool on);\n  Server &set_socket_options(SocketOptions socket_options);\n\n  Server &set_default_headers(Headers headers);\n  Server &\n  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);\n\n  Server &set_trusted_proxies(const std::vector<std::string> &proxies);\n\n  Server &set_keep_alive_max_count(size_t count);\n  Server &set_keep_alive_timeout(time_t sec);\n\n  Server &set_read_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  Server &set_read_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  Server &set_write_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  Server &set_write_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  Server &set_idle_interval(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  Server &set_idle_interval(const std::chrono::duration<Rep, Period> &duration);\n\n  Server &set_payload_max_length(size_t length);\n\n  bool bind_to_port(const std::string &host, int port, int socket_flags = 0);\n  int bind_to_any_port(const std::string &host, int socket_flags = 0);\n  bool listen_after_bind();\n\n  bool listen(const std::string &host, int port, int socket_flags = 0);\n\n  bool is_running() const;\n  void wait_until_ready() const;\n  void stop();\n  void decommission();\n\n  std::function<TaskQueue *(void)> new_task_queue;\n\nprotected:\n  bool process_request(Stream &strm, const std::string &remote_addr,\n                       int remote_port, const std::string &local_addr,\n                       int local_port, bool close_connection,\n                       bool &connection_closed,\n                       const std::function<void(Request &)> &setup_request);\n\n  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};\n\n  std::vector<std::string> trusted_proxies_;\n\n  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;\n  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;\n  time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;\n  time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;\n  time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;\n  time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;\n  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;\n  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;\n  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;\n\nprivate:\n  using Handlers =\n      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>, Handler>>;\n  using HandlersForContentReader =\n      std::vector<std::pair<std::unique_ptr<detail::MatcherBase>,\n                            HandlerWithContentReader>>;\n\n  static std::unique_ptr<detail::MatcherBase>\n  make_matcher(const std::string &pattern);\n\n  Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);\n  Server &set_error_handler_core(Handler handler, std::false_type);\n\n  socket_t create_server_socket(const std::string &host, int port,\n                                int socket_flags,\n                                SocketOptions socket_options) const;\n  int bind_internal(const std::string &host, int port, int socket_flags);\n  bool listen_internal();\n\n  bool routing(Request &req, Response &res, Stream &strm);\n  bool handle_file_request(const Request &req, Response &res);\n  bool dispatch_request(Request &req, Response &res,\n                        const Handlers &handlers) const;\n  bool dispatch_request_for_content_reader(\n      Request &req, Response &res, ContentReader content_reader,\n      const HandlersForContentReader &handlers) const;\n\n  bool parse_request_line(const char *s, Request &req) const;\n  void apply_ranges(const Request &req, Response &res,\n                    std::string &content_type, std::string &boundary) const;\n  bool write_response(Stream &strm, bool close_connection, Request &req,\n                      Response &res);\n  bool write_response_with_content(Stream &strm, bool close_connection,\n                                   const Request &req, Response &res);\n  bool write_response_core(Stream &strm, bool close_connection,\n                           const Request &req, Response &res,\n                           bool need_apply_ranges);\n  bool write_content_with_provider(Stream &strm, const Request &req,\n                                   Response &res, const std::string &boundary,\n                                   const std::string &content_type);\n  bool read_content(Stream &strm, Request &req, Response &res);\n  bool read_content_with_content_receiver(Stream &strm, Request &req,\n                                          Response &res,\n                                          ContentReceiver receiver,\n                                          FormDataHeader multipart_header,\n                                          ContentReceiver multipart_receiver);\n  bool read_content_core(Stream &strm, Request &req, Response &res,\n                         ContentReceiver receiver,\n                         FormDataHeader multipart_header,\n                         ContentReceiver multipart_receiver) const;\n\n  virtual bool process_and_close_socket(socket_t sock);\n\n  void output_log(const Request &req, const Response &res) const;\n  void output_pre_compression_log(const Request &req,\n                                  const Response &res) const;\n  void output_error_log(const Error &err, const Request *req) const;\n\n  std::atomic<bool> is_running_{false};\n  std::atomic<bool> is_decommissioned{false};\n\n  struct MountPointEntry {\n    std::string mount_point;\n    std::string base_dir;\n    Headers headers;\n  };\n  std::vector<MountPointEntry> base_dirs_;\n  std::map<std::string, std::string> file_extension_and_mimetype_map_;\n  std::string default_file_mimetype_ = \"application/octet-stream\";\n  Handler file_request_handler_;\n\n  Handlers get_handlers_;\n  Handlers post_handlers_;\n  HandlersForContentReader post_handlers_for_content_reader_;\n  Handlers put_handlers_;\n  HandlersForContentReader put_handlers_for_content_reader_;\n  Handlers patch_handlers_;\n  HandlersForContentReader patch_handlers_for_content_reader_;\n  Handlers delete_handlers_;\n  HandlersForContentReader delete_handlers_for_content_reader_;\n  Handlers options_handlers_;\n\n  HandlerWithResponse error_handler_;\n  ExceptionHandler exception_handler_;\n  HandlerWithResponse pre_routing_handler_;\n  Handler post_routing_handler_;\n  HandlerWithResponse pre_request_handler_;\n  Expect100ContinueHandler expect_100_continue_handler_;\n\n  mutable std::mutex logger_mutex_;\n  Logger logger_;\n  Logger pre_compression_logger_;\n  ErrorLogger error_logger_;\n\n  int address_family_ = AF_UNSPEC;\n  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;\n  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;\n  SocketOptions socket_options_ = default_socket_options;\n\n  Headers default_headers_;\n  std::function<ssize_t(Stream &, Headers &)> header_writer_ =\n      detail::write_headers;\n};\n\nenum class Error {\n  Success = 0,\n  Unknown,\n  Connection,\n  BindIPAddress,\n  Read,\n  Write,\n  ExceedRedirectCount,\n  Canceled,\n  SSLConnection,\n  SSLLoadingCerts,\n  SSLServerVerification,\n  SSLServerHostnameVerification,\n  UnsupportedMultipartBoundaryChars,\n  Compression,\n  ConnectionTimeout,\n  ProxyConnection,\n  ResourceExhaustion,\n  TooManyFormDataFiles,\n  ExceedMaxPayloadSize,\n  ExceedUriMaxLength,\n  ExceedMaxSocketDescriptorCount,\n  InvalidRequestLine,\n  InvalidHTTPMethod,\n  InvalidHTTPVersion,\n  InvalidHeaders,\n  MultipartParsing,\n  OpenFile,\n  Listen,\n  GetSockName,\n  UnsupportedAddressFamily,\n  HTTPParsing,\n  InvalidRangeHeader,\n\n  // For internal use only\n  SSLPeerCouldBeClosed_,\n};\n\nstd::string to_string(Error error);\n\nstd::ostream &operator<<(std::ostream &os, const Error &obj);\n\nclass Result {\npublic:\n  Result() = default;\n  Result(std::unique_ptr<Response> &&res, Error err,\n         Headers &&request_headers = Headers{})\n      : res_(std::move(res)), err_(err),\n        request_headers_(std::move(request_headers)) {}\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,\n         int ssl_error)\n      : res_(std::move(res)), err_(err),\n        request_headers_(std::move(request_headers)), ssl_error_(ssl_error) {}\n  Result(std::unique_ptr<Response> &&res, Error err, Headers &&request_headers,\n         int ssl_error, unsigned long ssl_openssl_error)\n      : res_(std::move(res)), err_(err),\n        request_headers_(std::move(request_headers)), ssl_error_(ssl_error),\n        ssl_openssl_error_(ssl_openssl_error) {}\n#endif\n  // Response\n  operator bool() const { return res_ != nullptr; }\n  bool operator==(std::nullptr_t) const { return res_ == nullptr; }\n  bool operator!=(std::nullptr_t) const { return res_ != nullptr; }\n  const Response &value() const { return *res_; }\n  Response &value() { return *res_; }\n  const Response &operator*() const { return *res_; }\n  Response &operator*() { return *res_; }\n  const Response *operator->() const { return res_.get(); }\n  Response *operator->() { return res_.get(); }\n\n  // Error\n  Error error() const { return err_; }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  // SSL Error\n  int ssl_error() const { return ssl_error_; }\n  // OpenSSL Error\n  unsigned long ssl_openssl_error() const { return ssl_openssl_error_; }\n#endif\n\n  // Request Headers\n  bool has_request_header(const std::string &key) const;\n  std::string get_request_header_value(const std::string &key,\n                                       const char *def = \"\",\n                                       size_t id = 0) const;\n  size_t get_request_header_value_u64(const std::string &key, size_t def = 0,\n                                      size_t id = 0) const;\n  size_t get_request_header_value_count(const std::string &key) const;\n\nprivate:\n  std::unique_ptr<Response> res_;\n  Error err_ = Error::Unknown;\n  Headers request_headers_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  int ssl_error_ = 0;\n  unsigned long ssl_openssl_error_ = 0;\n#endif\n};\n\nclass ClientImpl {\npublic:\n  explicit ClientImpl(const std::string &host);\n\n  explicit ClientImpl(const std::string &host, int port);\n\n  explicit ClientImpl(const std::string &host, int port,\n                      const std::string &client_cert_path,\n                      const std::string &client_key_path);\n\n  virtual ~ClientImpl();\n\n  virtual bool is_valid() const;\n\n  // clang-format off\n  Result Get(const std::string &path, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n\n  Result Head(const std::string &path);\n  Result Head(const std::string &path, const Headers &headers);\n\n  Result Post(const std::string &path);\n  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Params &params);\n  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers);\n  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const Params &params);\n  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n\n  Result Put(const std::string &path);\n  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Params &params);\n  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers);\n  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const Params &params);\n  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n\n  Result Patch(const std::string &path);\n  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Params &params);\n  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const Params &params);\n  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n\n  Result Delete(const std::string &path, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);\n\n  Result Options(const std::string &path);\n  Result Options(const std::string &path, const Headers &headers);\n  // clang-format on\n\n  bool send(Request &req, Response &res, Error &error);\n  Result send(const Request &req);\n\n  void stop();\n\n  std::string host() const;\n  int port() const;\n\n  size_t is_socket_open() const;\n  socket_t socket() const;\n\n  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);\n\n  void set_default_headers(Headers headers);\n\n  void\n  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);\n\n  void set_address_family(int family);\n  void set_tcp_nodelay(bool on);\n  void set_ipv6_v6only(bool on);\n  void set_socket_options(SocketOptions socket_options);\n\n  void set_connection_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void\n  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_read_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_write_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_max_timeout(time_t msec);\n  template <class Rep, class Period>\n  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_basic_auth(const std::string &username, const std::string &password);\n  void set_bearer_token_auth(const std::string &token);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_digest_auth(const std::string &username,\n                       const std::string &password);\n#endif\n\n  void set_keep_alive(bool on);\n  void set_follow_location(bool on);\n\n  void set_path_encode(bool on);\n\n  void set_compress(bool on);\n\n  void set_decompress(bool on);\n\n  void set_interface(const std::string &intf);\n\n  void set_proxy(const std::string &host, int port);\n  void set_proxy_basic_auth(const std::string &username,\n                            const std::string &password);\n  void set_proxy_bearer_token_auth(const std::string &token);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_proxy_digest_auth(const std::string &username,\n                             const std::string &password);\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_ca_cert_path(const std::string &ca_cert_file_path,\n                        const std::string &ca_cert_dir_path = std::string());\n  void set_ca_cert_store(X509_STORE *ca_cert_store);\n  X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void enable_server_certificate_verification(bool enabled);\n  void enable_server_hostname_verification(bool enabled);\n  void set_server_certificate_verifier(\n      std::function<SSLVerifierResponse(SSL *ssl)> verifier);\n#endif\n\n  void set_logger(Logger logger);\n  void set_error_logger(ErrorLogger error_logger);\n\nprotected:\n  struct Socket {\n    socket_t sock = INVALID_SOCKET;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    SSL *ssl = nullptr;\n#endif\n\n    bool is_open() const { return sock != INVALID_SOCKET; }\n  };\n\n  virtual bool create_and_connect_socket(Socket &socket, Error &error);\n\n  // All of:\n  //   shutdown_ssl\n  //   shutdown_socket\n  //   close_socket\n  // should ONLY be called when socket_mutex_ is locked.\n  // Also, shutdown_ssl and close_socket should also NOT be called concurrently\n  // with a DIFFERENT thread sending requests using that socket.\n  virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);\n  void shutdown_socket(Socket &socket) const;\n  void close_socket(Socket &socket);\n\n  bool process_request(Stream &strm, Request &req, Response &res,\n                       bool close_connection, Error &error);\n\n  bool write_content_with_provider(Stream &strm, const Request &req,\n                                   Error &error) const;\n\n  void copy_settings(const ClientImpl &rhs);\n\n  void output_log(const Request &req, const Response &res) const;\n  void output_error_log(const Error &err, const Request *req) const;\n\n  // Socket endpoint information\n  const std::string host_;\n  const int port_;\n  const std::string host_and_port_;\n\n  // Current open socket\n  Socket socket_;\n  mutable std::mutex socket_mutex_;\n  std::recursive_mutex request_mutex_;\n\n  // These are all protected under socket_mutex\n  size_t socket_requests_in_flight_ = 0;\n  std::thread::id socket_requests_are_from_thread_ = std::thread::id();\n  bool socket_should_be_closed_when_request_is_done_ = false;\n\n  // Hostname-IP map\n  std::map<std::string, std::string> addr_map_;\n\n  // Default headers\n  Headers default_headers_;\n\n  // Header writer\n  std::function<ssize_t(Stream &, Headers &)> header_writer_ =\n      detail::write_headers;\n\n  // Settings\n  std::string client_cert_path_;\n  std::string client_key_path_;\n\n  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;\n  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;\n  time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;\n  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;\n  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;\n  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;\n  time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;\n\n  std::string basic_auth_username_;\n  std::string basic_auth_password_;\n  std::string bearer_token_auth_token_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  std::string digest_auth_username_;\n  std::string digest_auth_password_;\n#endif\n\n  bool keep_alive_ = false;\n  bool follow_location_ = false;\n\n  bool path_encode_ = true;\n\n  int address_family_ = AF_UNSPEC;\n  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;\n  bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;\n  SocketOptions socket_options_ = nullptr;\n\n  bool compress_ = false;\n  bool decompress_ = true;\n\n  std::string interface_;\n\n  std::string proxy_host_;\n  int proxy_port_ = -1;\n\n  std::string proxy_basic_auth_username_;\n  std::string proxy_basic_auth_password_;\n  std::string proxy_bearer_token_auth_token_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  std::string proxy_digest_auth_username_;\n  std::string proxy_digest_auth_password_;\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  std::string ca_cert_file_path_;\n  std::string ca_cert_dir_path_;\n\n  X509_STORE *ca_cert_store_ = nullptr;\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  bool server_certificate_verification_ = true;\n  bool server_hostname_verification_ = true;\n  std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;\n#endif\n\n  mutable std::mutex logger_mutex_;\n  Logger logger_;\n  ErrorLogger error_logger_;\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  int last_ssl_error_ = 0;\n  unsigned long last_openssl_error_ = 0;\n#endif\n\nprivate:\n  bool send_(Request &req, Response &res, Error &error);\n  Result send_(Request &&req);\n\n  socket_t create_client_socket(Error &error) const;\n  bool read_response_line(Stream &strm, const Request &req,\n                          Response &res) const;\n  bool write_request(Stream &strm, Request &req, bool close_connection,\n                     Error &error);\n  bool redirect(Request &req, Response &res, Error &error);\n  bool create_redirect_client(const std::string &scheme,\n                              const std::string &host, int port, Request &req,\n                              Response &res, const std::string &path,\n                              const std::string &location, Error &error);\n  template <typename ClientType> void setup_redirect_client(ClientType &client);\n  bool handle_request(Stream &strm, Request &req, Response &res,\n                      bool close_connection, Error &error);\n  std::unique_ptr<Response> send_with_content_provider(\n      Request &req, const char *body, size_t content_length,\n      ContentProvider content_provider,\n      ContentProviderWithoutLength content_provider_without_length,\n      const std::string &content_type, Error &error);\n  Result send_with_content_provider(\n      const std::string &method, const std::string &path,\n      const Headers &headers, const char *body, size_t content_length,\n      ContentProvider content_provider,\n      ContentProviderWithoutLength content_provider_without_length,\n      const std::string &content_type, UploadProgress progress);\n  ContentProviderWithoutLength get_multipart_content_provider(\n      const std::string &boundary, const UploadFormDataItems &items,\n      const FormDataProviderItems &provider_items) const;\n\n  virtual bool\n  process_socket(const Socket &socket,\n                 std::chrono::time_point<std::chrono::steady_clock> start_time,\n                 std::function<bool(Stream &strm)> callback);\n  virtual bool is_ssl() const;\n};\n\nclass Client {\npublic:\n  // Universal interface\n  explicit Client(const std::string &scheme_host_port);\n\n  explicit Client(const std::string &scheme_host_port,\n                  const std::string &client_cert_path,\n                  const std::string &client_key_path);\n\n  // HTTP only interface\n  explicit Client(const std::string &host, int port);\n\n  explicit Client(const std::string &host, int port,\n                  const std::string &client_cert_path,\n                  const std::string &client_key_path);\n\n  Client(Client &&) = default;\n  Client &operator=(Client &&) = default;\n\n  ~Client();\n\n  bool is_valid() const;\n\n  // clang-format off\n  Result Get(const std::string &path, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Params &params, const Headers &headers, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Params &params, const Headers &headers, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n  Result Get(const std::string &path, const Params &params, const Headers &headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n\n  Result Head(const std::string &path);\n  Result Head(const std::string &path, const Headers &headers);\n\n  Result Post(const std::string &path);\n  Result Post(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Params &params);\n  Result Post(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers);\n  Result Post(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const Params &params);\n  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);\n  Result Post(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n\n  Result Put(const std::string &path);\n  Result Put(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Params &params);\n  Result Put(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers);\n  Result Put(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const Params &params);\n  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);\n  Result Put(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n\n  Result Patch(const std::string &path);\n  Result Patch(const std::string &path, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Params &params);\n  Result Patch(const std::string &path, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers);\n  Result Patch(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, size_t content_length, ContentProvider content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, ContentProviderWithoutLength content_provider, const std::string &content_type, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const Params &params);\n  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const std::string &boundary, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const UploadFormDataItems &items, const FormDataProviderItems &provider_items, UploadProgress progress = nullptr);\n  Result Patch(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, ContentReceiver content_receiver, DownloadProgress progress = nullptr);\n\n  Result Delete(const std::string &path, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Params &params, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Headers &headers, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Headers &headers, const char *body, size_t content_length, const std::string &content_type, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Headers &headers, const std::string &body, const std::string &content_type, DownloadProgress progress = nullptr);\n  Result Delete(const std::string &path, const Headers &headers, const Params &params, DownloadProgress progress = nullptr);\n\n  Result Options(const std::string &path);\n  Result Options(const std::string &path, const Headers &headers);\n  // clang-format on\n\n  bool send(Request &req, Response &res, Error &error);\n  Result send(const Request &req);\n\n  void stop();\n\n  std::string host() const;\n  int port() const;\n\n  size_t is_socket_open() const;\n  socket_t socket() const;\n\n  void set_hostname_addr_map(std::map<std::string, std::string> addr_map);\n\n  void set_default_headers(Headers headers);\n\n  void\n  set_header_writer(std::function<ssize_t(Stream &, Headers &)> const &writer);\n\n  void set_address_family(int family);\n  void set_tcp_nodelay(bool on);\n  void set_socket_options(SocketOptions socket_options);\n\n  void set_connection_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void\n  set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_read_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_write_timeout(time_t sec, time_t usec = 0);\n  template <class Rep, class Period>\n  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_max_timeout(time_t msec);\n  template <class Rep, class Period>\n  void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);\n\n  void set_basic_auth(const std::string &username, const std::string &password);\n  void set_bearer_token_auth(const std::string &token);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_digest_auth(const std::string &username,\n                       const std::string &password);\n#endif\n\n  void set_keep_alive(bool on);\n  void set_follow_location(bool on);\n\n  void set_path_encode(bool on);\n  void set_url_encode(bool on);\n\n  void set_compress(bool on);\n\n  void set_decompress(bool on);\n\n  void set_interface(const std::string &intf);\n\n  void set_proxy(const std::string &host, int port);\n  void set_proxy_basic_auth(const std::string &username,\n                            const std::string &password);\n  void set_proxy_bearer_token_auth(const std::string &token);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_proxy_digest_auth(const std::string &username,\n                             const std::string &password);\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void enable_server_certificate_verification(bool enabled);\n  void enable_server_hostname_verification(bool enabled);\n  void set_server_certificate_verifier(\n      std::function<SSLVerifierResponse(SSL *ssl)> verifier);\n#endif\n\n  void set_logger(Logger logger);\n  void set_error_logger(ErrorLogger error_logger);\n\n  // SSL\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  void set_ca_cert_path(const std::string &ca_cert_file_path,\n                        const std::string &ca_cert_dir_path = std::string());\n\n  void set_ca_cert_store(X509_STORE *ca_cert_store);\n  void load_ca_cert_store(const char *ca_cert, std::size_t size);\n\n  long get_openssl_verify_result() const;\n\n  SSL_CTX *ssl_context() const;\n#endif\n\nprivate:\n  std::unique_ptr<ClientImpl> cli_;\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  bool is_ssl_ = false;\n#endif\n};\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nclass SSLServer : public Server {\npublic:\n  SSLServer(const char *cert_path, const char *private_key_path,\n            const char *client_ca_cert_file_path = nullptr,\n            const char *client_ca_cert_dir_path = nullptr,\n            const char *private_key_password = nullptr);\n\n  SSLServer(X509 *cert, EVP_PKEY *private_key,\n            X509_STORE *client_ca_cert_store = nullptr);\n\n  SSLServer(\n      const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback);\n\n  ~SSLServer() override;\n\n  bool is_valid() const override;\n\n  SSL_CTX *ssl_context() const;\n\n  void update_certs(X509 *cert, EVP_PKEY *private_key,\n                    X509_STORE *client_ca_cert_store = nullptr);\n\n  int ssl_last_error() const { return last_ssl_error_; }\n\nprivate:\n  bool process_and_close_socket(socket_t sock) override;\n\n  STACK_OF(X509_NAME) * extract_ca_names_from_x509_store(X509_STORE *store);\n\n  SSL_CTX *ctx_;\n  std::mutex ctx_mutex_;\n\n  int last_ssl_error_ = 0;\n};\n\nclass SSLClient final : public ClientImpl {\npublic:\n  explicit SSLClient(const std::string &host);\n\n  explicit SSLClient(const std::string &host, int port);\n\n  explicit SSLClient(const std::string &host, int port,\n                     const std::string &client_cert_path,\n                     const std::string &client_key_path,\n                     const std::string &private_key_password = std::string());\n\n  explicit SSLClient(const std::string &host, int port, X509 *client_cert,\n                     EVP_PKEY *client_key,\n                     const std::string &private_key_password = std::string());\n\n  ~SSLClient() override;\n\n  bool is_valid() const override;\n\n  void set_ca_cert_store(X509_STORE *ca_cert_store);\n  void load_ca_cert_store(const char *ca_cert, std::size_t size);\n\n  long get_openssl_verify_result() const;\n\n  SSL_CTX *ssl_context() const;\n\nprivate:\n  bool create_and_connect_socket(Socket &socket, Error &error) override;\n  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;\n  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);\n\n  bool\n  process_socket(const Socket &socket,\n                 std::chrono::time_point<std::chrono::steady_clock> start_time,\n                 std::function<bool(Stream &strm)> callback) override;\n  bool is_ssl() const override;\n\n  bool connect_with_proxy(\n      Socket &sock,\n      std::chrono::time_point<std::chrono::steady_clock> start_time,\n      Response &res, bool &success, Error &error);\n  bool initialize_ssl(Socket &socket, Error &error);\n\n  bool load_certs();\n\n  bool verify_host(X509 *server_cert) const;\n  bool verify_host_with_subject_alt_name(X509 *server_cert) const;\n  bool verify_host_with_common_name(X509 *server_cert) const;\n  bool check_host_name(const char *pattern, size_t pattern_len) const;\n\n  SSL_CTX *ctx_;\n  std::mutex ctx_mutex_;\n  std::once_flag initialize_cert_;\n\n  std::vector<std::string> host_components_;\n\n  long verify_result_ = 0;\n\n  friend class ClientImpl;\n};\n#endif\n\n/*\n * Implementation of template methods.\n */\n\nnamespace detail {\n\ntemplate <typename T, typename U>\ninline void duration_to_sec_and_usec(const T &duration, U callback) {\n  auto sec = std::chrono::duration_cast<std::chrono::seconds>(duration).count();\n  auto usec = std::chrono::duration_cast<std::chrono::microseconds>(\n                  duration - std::chrono::seconds(sec))\n                  .count();\n  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));\n}\n\ntemplate <size_t N> inline constexpr size_t str_len(const char (&)[N]) {\n  return N - 1;\n}\n\ninline bool is_numeric(const std::string &str) {\n  return !str.empty() &&\n         std::all_of(str.cbegin(), str.cend(),\n                     [](unsigned char c) { return std::isdigit(c); });\n}\n\ninline size_t get_header_value_u64(const Headers &headers,\n                                   const std::string &key, size_t def,\n                                   size_t id, bool &is_invalid_value) {\n  is_invalid_value = false;\n  auto rng = headers.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) {\n    if (is_numeric(it->second)) {\n      return std::strtoull(it->second.data(), nullptr, 10);\n    } else {\n      is_invalid_value = true;\n    }\n  }\n  return def;\n}\n\ninline size_t get_header_value_u64(const Headers &headers,\n                                   const std::string &key, size_t def,\n                                   size_t id) {\n  auto dummy = false;\n  return get_header_value_u64(headers, key, def, id, dummy);\n}\n\n} // namespace detail\n\ninline size_t Request::get_header_value_u64(const std::string &key, size_t def,\n                                            size_t id) const {\n  return detail::get_header_value_u64(headers, key, def, id);\n}\n\ninline size_t Response::get_header_value_u64(const std::string &key, size_t def,\n                                             size_t id) const {\n  return detail::get_header_value_u64(headers, key, def, id);\n}\n\nnamespace detail {\n\ninline bool set_socket_opt_impl(socket_t sock, int level, int optname,\n                                const void *optval, socklen_t optlen) {\n  return setsockopt(sock, level, optname,\n#ifdef _WIN32\n                    reinterpret_cast<const char *>(optval),\n#else\n                    optval,\n#endif\n                    optlen) == 0;\n}\n\ninline bool set_socket_opt(socket_t sock, int level, int optname, int optval) {\n  return set_socket_opt_impl(sock, level, optname, &optval, sizeof(optval));\n}\n\ninline bool set_socket_opt_time(socket_t sock, int level, int optname,\n                                time_t sec, time_t usec) {\n#ifdef _WIN32\n  auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);\n#else\n  timeval timeout;\n  timeout.tv_sec = static_cast<long>(sec);\n  timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);\n#endif\n  return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));\n}\n\n} // namespace detail\n\ninline void default_socket_options(socket_t sock) {\n  detail::set_socket_opt(sock, SOL_SOCKET,\n#ifdef SO_REUSEPORT\n                         SO_REUSEPORT,\n#else\n                         SO_REUSEADDR,\n#endif\n                         1);\n}\n\ninline const char *status_message(int status) {\n  switch (status) {\n  case StatusCode::Continue_100: return \"Continue\";\n  case StatusCode::SwitchingProtocol_101: return \"Switching Protocol\";\n  case StatusCode::Processing_102: return \"Processing\";\n  case StatusCode::EarlyHints_103: return \"Early Hints\";\n  case StatusCode::OK_200: return \"OK\";\n  case StatusCode::Created_201: return \"Created\";\n  case StatusCode::Accepted_202: return \"Accepted\";\n  case StatusCode::NonAuthoritativeInformation_203:\n    return \"Non-Authoritative Information\";\n  case StatusCode::NoContent_204: return \"No Content\";\n  case StatusCode::ResetContent_205: return \"Reset Content\";\n  case StatusCode::PartialContent_206: return \"Partial Content\";\n  case StatusCode::MultiStatus_207: return \"Multi-Status\";\n  case StatusCode::AlreadyReported_208: return \"Already Reported\";\n  case StatusCode::IMUsed_226: return \"IM Used\";\n  case StatusCode::MultipleChoices_300: return \"Multiple Choices\";\n  case StatusCode::MovedPermanently_301: return \"Moved Permanently\";\n  case StatusCode::Found_302: return \"Found\";\n  case StatusCode::SeeOther_303: return \"See Other\";\n  case StatusCode::NotModified_304: return \"Not Modified\";\n  case StatusCode::UseProxy_305: return \"Use Proxy\";\n  case StatusCode::unused_306: return \"unused\";\n  case StatusCode::TemporaryRedirect_307: return \"Temporary Redirect\";\n  case StatusCode::PermanentRedirect_308: return \"Permanent Redirect\";\n  case StatusCode::BadRequest_400: return \"Bad Request\";\n  case StatusCode::Unauthorized_401: return \"Unauthorized\";\n  case StatusCode::PaymentRequired_402: return \"Payment Required\";\n  case StatusCode::Forbidden_403: return \"Forbidden\";\n  case StatusCode::NotFound_404: return \"Not Found\";\n  case StatusCode::MethodNotAllowed_405: return \"Method Not Allowed\";\n  case StatusCode::NotAcceptable_406: return \"Not Acceptable\";\n  case StatusCode::ProxyAuthenticationRequired_407:\n    return \"Proxy Authentication Required\";\n  case StatusCode::RequestTimeout_408: return \"Request Timeout\";\n  case StatusCode::Conflict_409: return \"Conflict\";\n  case StatusCode::Gone_410: return \"Gone\";\n  case StatusCode::LengthRequired_411: return \"Length Required\";\n  case StatusCode::PreconditionFailed_412: return \"Precondition Failed\";\n  case StatusCode::PayloadTooLarge_413: return \"Payload Too Large\";\n  case StatusCode::UriTooLong_414: return \"URI Too Long\";\n  case StatusCode::UnsupportedMediaType_415: return \"Unsupported Media Type\";\n  case StatusCode::RangeNotSatisfiable_416: return \"Range Not Satisfiable\";\n  case StatusCode::ExpectationFailed_417: return \"Expectation Failed\";\n  case StatusCode::ImATeapot_418: return \"I'm a teapot\";\n  case StatusCode::MisdirectedRequest_421: return \"Misdirected Request\";\n  case StatusCode::UnprocessableContent_422: return \"Unprocessable Content\";\n  case StatusCode::Locked_423: return \"Locked\";\n  case StatusCode::FailedDependency_424: return \"Failed Dependency\";\n  case StatusCode::TooEarly_425: return \"Too Early\";\n  case StatusCode::UpgradeRequired_426: return \"Upgrade Required\";\n  case StatusCode::PreconditionRequired_428: return \"Precondition Required\";\n  case StatusCode::TooManyRequests_429: return \"Too Many Requests\";\n  case StatusCode::RequestHeaderFieldsTooLarge_431:\n    return \"Request Header Fields Too Large\";\n  case StatusCode::UnavailableForLegalReasons_451:\n    return \"Unavailable For Legal Reasons\";\n  case StatusCode::NotImplemented_501: return \"Not Implemented\";\n  case StatusCode::BadGateway_502: return \"Bad Gateway\";\n  case StatusCode::ServiceUnavailable_503: return \"Service Unavailable\";\n  case StatusCode::GatewayTimeout_504: return \"Gateway Timeout\";\n  case StatusCode::HttpVersionNotSupported_505:\n    return \"HTTP Version Not Supported\";\n  case StatusCode::VariantAlsoNegotiates_506: return \"Variant Also Negotiates\";\n  case StatusCode::InsufficientStorage_507: return \"Insufficient Storage\";\n  case StatusCode::LoopDetected_508: return \"Loop Detected\";\n  case StatusCode::NotExtended_510: return \"Not Extended\";\n  case StatusCode::NetworkAuthenticationRequired_511:\n    return \"Network Authentication Required\";\n\n  default:\n  case StatusCode::InternalServerError_500: return \"Internal Server Error\";\n  }\n}\n\ninline std::string get_bearer_token_auth(const Request &req) {\n  if (req.has_header(\"Authorization\")) {\n    constexpr auto bearer_header_prefix_len = detail::str_len(\"Bearer \");\n    return req.get_header_value(\"Authorization\")\n        .substr(bearer_header_prefix_len);\n  }\n  return \"\";\n}\n\ntemplate <class Rep, class Period>\ninline Server &\nServer::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });\n  return *this;\n}\n\ntemplate <class Rep, class Period>\ninline Server &\nServer::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });\n  return *this;\n}\n\ntemplate <class Rep, class Period>\ninline Server &\nServer::set_idle_interval(const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_idle_interval(sec, usec); });\n  return *this;\n}\n\ninline std::string to_string(const Error error) {\n  switch (error) {\n  case Error::Success: return \"Success (no error)\";\n  case Error::Unknown: return \"Unknown\";\n  case Error::Connection: return \"Could not establish connection\";\n  case Error::BindIPAddress: return \"Failed to bind IP address\";\n  case Error::Read: return \"Failed to read connection\";\n  case Error::Write: return \"Failed to write connection\";\n  case Error::ExceedRedirectCount: return \"Maximum redirect count exceeded\";\n  case Error::Canceled: return \"Connection handling canceled\";\n  case Error::SSLConnection: return \"SSL connection failed\";\n  case Error::SSLLoadingCerts: return \"SSL certificate loading failed\";\n  case Error::SSLServerVerification: return \"SSL server verification failed\";\n  case Error::SSLServerHostnameVerification:\n    return \"SSL server hostname verification failed\";\n  case Error::UnsupportedMultipartBoundaryChars:\n    return \"Unsupported HTTP multipart boundary characters\";\n  case Error::Compression: return \"Compression failed\";\n  case Error::ConnectionTimeout: return \"Connection timed out\";\n  case Error::ProxyConnection: return \"Proxy connection failed\";\n  case Error::ResourceExhaustion: return \"Resource exhaustion\";\n  case Error::TooManyFormDataFiles: return \"Too many form data files\";\n  case Error::ExceedMaxPayloadSize: return \"Exceeded maximum payload size\";\n  case Error::ExceedUriMaxLength: return \"Exceeded maximum URI length\";\n  case Error::ExceedMaxSocketDescriptorCount:\n    return \"Exceeded maximum socket descriptor count\";\n  case Error::InvalidRequestLine: return \"Invalid request line\";\n  case Error::InvalidHTTPMethod: return \"Invalid HTTP method\";\n  case Error::InvalidHTTPVersion: return \"Invalid HTTP version\";\n  case Error::InvalidHeaders: return \"Invalid headers\";\n  case Error::MultipartParsing: return \"Multipart parsing failed\";\n  case Error::OpenFile: return \"Failed to open file\";\n  case Error::Listen: return \"Failed to listen on socket\";\n  case Error::GetSockName: return \"Failed to get socket name\";\n  case Error::UnsupportedAddressFamily: return \"Unsupported address family\";\n  case Error::HTTPParsing: return \"HTTP parsing failed\";\n  case Error::InvalidRangeHeader: return \"Invalid Range header\";\n  default: break;\n  }\n\n  return \"Invalid\";\n}\n\ninline std::ostream &operator<<(std::ostream &os, const Error &obj) {\n  os << to_string(obj);\n  os << \" (\" << static_cast<std::underlying_type<Error>::type>(obj) << ')';\n  return os;\n}\n\ninline size_t Result::get_request_header_value_u64(const std::string &key,\n                                                   size_t def,\n                                                   size_t id) const {\n  return detail::get_header_value_u64(request_headers_, key, def, id);\n}\n\ntemplate <class Rep, class Period>\ninline void ClientImpl::set_connection_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(duration, [&](time_t sec, time_t usec) {\n    set_connection_timeout(sec, usec);\n  });\n}\n\ntemplate <class Rep, class Period>\ninline void ClientImpl::set_read_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_read_timeout(sec, usec); });\n}\n\ntemplate <class Rep, class Period>\ninline void ClientImpl::set_write_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  detail::duration_to_sec_and_usec(\n      duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });\n}\n\ntemplate <class Rep, class Period>\ninline void ClientImpl::set_max_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  auto msec =\n      std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();\n  set_max_timeout(msec);\n}\n\ntemplate <class Rep, class Period>\ninline void Client::set_connection_timeout(\n    const std::chrono::duration<Rep, Period> &duration) {\n  cli_->set_connection_timeout(duration);\n}\n\ntemplate <class Rep, class Period>\ninline void\nClient::set_read_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  cli_->set_read_timeout(duration);\n}\n\ntemplate <class Rep, class Period>\ninline void\nClient::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  cli_->set_write_timeout(duration);\n}\n\ninline void Client::set_max_timeout(time_t msec) {\n  cli_->set_max_timeout(msec);\n}\n\ntemplate <class Rep, class Period>\ninline void\nClient::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {\n  cli_->set_max_timeout(duration);\n}\n\n/*\n * Forward declarations and types that will be part of the .h file if split into\n * .h + .cc.\n */\n\nstd::string hosted_at(const std::string &hostname);\n\nvoid hosted_at(const std::string &hostname, std::vector<std::string> &addrs);\n\n// JavaScript-style URL encoding/decoding functions\nstd::string encode_uri_component(const std::string &value);\nstd::string encode_uri(const std::string &value);\nstd::string decode_uri_component(const std::string &value);\nstd::string decode_uri(const std::string &value);\n\n// RFC 3986 compliant URL component encoding/decoding functions\nstd::string encode_path_component(const std::string &component);\nstd::string decode_path_component(const std::string &component);\nstd::string encode_query_component(const std::string &component,\n                                   bool space_as_plus = true);\nstd::string decode_query_component(const std::string &component,\n                                   bool plus_as_space = true);\n\nstd::string append_query_params(const std::string &path, const Params &params);\n\nstd::pair<std::string, std::string> make_range_header(const Ranges &ranges);\n\nstd::pair<std::string, std::string>\nmake_basic_authentication_header(const std::string &username,\n                                 const std::string &password,\n                                 bool is_proxy = false);\n\nnamespace detail {\n\n#if defined(_WIN32)\ninline std::wstring u8string_to_wstring(const char *s) {\n  std::wstring ws;\n  auto len = static_cast<int>(strlen(s));\n  auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);\n  if (wlen > 0) {\n    ws.resize(wlen);\n    wlen = ::MultiByteToWideChar(\n        CP_UTF8, 0, s, len,\n        const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);\n    if (wlen != static_cast<int>(ws.size())) { ws.clear(); }\n  }\n  return ws;\n}\n#endif\n\nstruct FileStat {\n  FileStat(const std::string &path);\n  bool is_file() const;\n  bool is_dir() const;\n\nprivate:\n#if defined(_WIN32)\n  struct _stat st_;\n#else\n  struct stat st_;\n#endif\n  int ret_ = -1;\n};\n\nstd::string trim_copy(const std::string &s);\n\nvoid divide(\n    const char *data, std::size_t size, char d,\n    std::function<void(const char *, std::size_t, const char *, std::size_t)>\n        fn);\n\nvoid divide(\n    const std::string &str, char d,\n    std::function<void(const char *, std::size_t, const char *, std::size_t)>\n        fn);\n\nvoid split(const char *b, const char *e, char d,\n           std::function<void(const char *, const char *)> fn);\n\nvoid split(const char *b, const char *e, char d, size_t m,\n           std::function<void(const char *, const char *)> fn);\n\nbool process_client_socket(\n    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,\n    time_t write_timeout_sec, time_t write_timeout_usec,\n    time_t max_timeout_msec,\n    std::chrono::time_point<std::chrono::steady_clock> start_time,\n    std::function<bool(Stream &)> callback);\n\nsocket_t create_client_socket(const std::string &host, const std::string &ip,\n                              int port, int address_family, bool tcp_nodelay,\n                              bool ipv6_v6only, SocketOptions socket_options,\n                              time_t connection_timeout_sec,\n                              time_t connection_timeout_usec,\n                              time_t read_timeout_sec, time_t read_timeout_usec,\n                              time_t write_timeout_sec,\n                              time_t write_timeout_usec,\n                              const std::string &intf, Error &error);\n\nconst char *get_header_value(const Headers &headers, const std::string &key,\n                             const char *def, size_t id);\n\nstd::string params_to_query_str(const Params &params);\n\nvoid parse_query_text(const char *data, std::size_t size, Params &params);\n\nvoid parse_query_text(const std::string &s, Params &params);\n\nbool parse_multipart_boundary(const std::string &content_type,\n                              std::string &boundary);\n\nbool parse_range_header(const std::string &s, Ranges &ranges);\n\nbool parse_accept_header(const std::string &s,\n                         std::vector<std::string> &content_types);\n\nint close_socket(socket_t sock);\n\nssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);\n\nssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);\n\nenum class EncodingType { None = 0, Gzip, Brotli, Zstd };\n\nEncodingType encoding_type(const Request &req, const Response &res);\n\nclass BufferStream final : public Stream {\npublic:\n  BufferStream() = default;\n  ~BufferStream() override = default;\n\n  bool is_readable() const override;\n  bool wait_readable() const override;\n  bool wait_writable() const override;\n  ssize_t read(char *ptr, size_t size) override;\n  ssize_t write(const char *ptr, size_t size) override;\n  void get_remote_ip_and_port(std::string &ip, int &port) const override;\n  void get_local_ip_and_port(std::string &ip, int &port) const override;\n  socket_t socket() const override;\n  time_t duration() const override;\n\n  const std::string &get_buffer() const;\n\nprivate:\n  std::string buffer;\n  size_t position = 0;\n};\n\nclass compressor {\npublic:\n  virtual ~compressor() = default;\n\n  typedef std::function<bool(const char *data, size_t data_len)> Callback;\n  virtual bool compress(const char *data, size_t data_length, bool last,\n                        Callback callback) = 0;\n};\n\nclass decompressor {\npublic:\n  virtual ~decompressor() = default;\n\n  virtual bool is_valid() const = 0;\n\n  typedef std::function<bool(const char *data, size_t data_len)> Callback;\n  virtual bool decompress(const char *data, size_t data_length,\n                          Callback callback) = 0;\n};\n\nclass nocompressor final : public compressor {\npublic:\n  ~nocompressor() override = default;\n\n  bool compress(const char *data, size_t data_length, bool /*last*/,\n                Callback callback) override;\n};\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\nclass gzip_compressor final : public compressor {\npublic:\n  gzip_compressor();\n  ~gzip_compressor() override;\n\n  bool compress(const char *data, size_t data_length, bool last,\n                Callback callback) override;\n\nprivate:\n  bool is_valid_ = false;\n  z_stream strm_;\n};\n\nclass gzip_decompressor final : public decompressor {\npublic:\n  gzip_decompressor();\n  ~gzip_decompressor() override;\n\n  bool is_valid() const override;\n\n  bool decompress(const char *data, size_t data_length,\n                  Callback callback) override;\n\nprivate:\n  bool is_valid_ = false;\n  z_stream strm_;\n};\n#endif\n\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\nclass brotli_compressor final : public compressor {\npublic:\n  brotli_compressor();\n  ~brotli_compressor();\n\n  bool compress(const char *data, size_t data_length, bool last,\n                Callback callback) override;\n\nprivate:\n  BrotliEncoderState *state_ = nullptr;\n};\n\nclass brotli_decompressor final : public decompressor {\npublic:\n  brotli_decompressor();\n  ~brotli_decompressor();\n\n  bool is_valid() const override;\n\n  bool decompress(const char *data, size_t data_length,\n                  Callback callback) override;\n\nprivate:\n  BrotliDecoderResult decoder_r;\n  BrotliDecoderState *decoder_s = nullptr;\n};\n#endif\n\n#ifdef CPPHTTPLIB_ZSTD_SUPPORT\nclass zstd_compressor : public compressor {\npublic:\n  zstd_compressor();\n  ~zstd_compressor();\n\n  bool compress(const char *data, size_t data_length, bool last,\n                Callback callback) override;\n\nprivate:\n  ZSTD_CCtx *ctx_ = nullptr;\n};\n\nclass zstd_decompressor : public decompressor {\npublic:\n  zstd_decompressor();\n  ~zstd_decompressor();\n\n  bool is_valid() const override;\n\n  bool decompress(const char *data, size_t data_length,\n                  Callback callback) override;\n\nprivate:\n  ZSTD_DCtx *ctx_ = nullptr;\n};\n#endif\n\n// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`\n// to store data. The call can set memory on stack for performance.\nclass stream_line_reader {\npublic:\n  stream_line_reader(Stream &strm, char *fixed_buffer,\n                     size_t fixed_buffer_size);\n  const char *ptr() const;\n  size_t size() const;\n  bool end_with_crlf() const;\n  bool getline();\n\nprivate:\n  void append(char c);\n\n  Stream &strm_;\n  char *fixed_buffer_;\n  const size_t fixed_buffer_size_;\n  size_t fixed_buffer_used_size_ = 0;\n  std::string growable_buffer_;\n};\n\nclass mmap {\npublic:\n  mmap(const char *path);\n  ~mmap();\n\n  bool open(const char *path);\n  void close();\n\n  bool is_open() const;\n  size_t size() const;\n  const char *data() const;\n\nprivate:\n#if defined(_WIN32)\n  HANDLE hFile_ = NULL;\n  HANDLE hMapping_ = NULL;\n#else\n  int fd_ = -1;\n#endif\n  size_t size_ = 0;\n  void *addr_ = nullptr;\n  bool is_open_empty_file = false;\n};\n\n// NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5\nnamespace fields {\n\ninline bool is_token_char(char c) {\n  return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||\n         c == '&' || c == '\\'' || c == '*' || c == '+' || c == '-' ||\n         c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';\n}\n\ninline bool is_token(const std::string &s) {\n  if (s.empty()) { return false; }\n  for (auto c : s) {\n    if (!is_token_char(c)) { return false; }\n  }\n  return true;\n}\n\ninline bool is_field_name(const std::string &s) { return is_token(s); }\n\ninline bool is_vchar(char c) { return c >= 33 && c <= 126; }\n\ninline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }\n\ninline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }\n\ninline bool is_field_content(const std::string &s) {\n  if (s.empty()) { return true; }\n\n  if (s.size() == 1) {\n    return is_field_vchar(s[0]);\n  } else if (s.size() == 2) {\n    return is_field_vchar(s[0]) && is_field_vchar(s[1]);\n  } else {\n    size_t i = 0;\n\n    if (!is_field_vchar(s[i])) { return false; }\n    i++;\n\n    while (i < s.size() - 1) {\n      auto c = s[i++];\n      if (c == ' ' || c == '\\t' || is_field_vchar(c)) {\n      } else {\n        return false;\n      }\n    }\n\n    return is_field_vchar(s[i]);\n  }\n}\n\ninline bool is_field_value(const std::string &s) { return is_field_content(s); }\n\n} // namespace fields\n\n} // namespace detail\n\n// ----------------------------------------------------------------------------\n\n/*\n * Implementation that will be part of the .cc file if split into .h + .cc.\n */\n\nnamespace detail {\n\ninline bool is_hex(char c, int &v) {\n  if (0x20 <= c && isdigit(c)) {\n    v = c - '0';\n    return true;\n  } else if ('A' <= c && c <= 'F') {\n    v = c - 'A' + 10;\n    return true;\n  } else if ('a' <= c && c <= 'f') {\n    v = c - 'a' + 10;\n    return true;\n  }\n  return false;\n}\n\ninline bool from_hex_to_i(const std::string &s, size_t i, size_t cnt,\n                          int &val) {\n  if (i >= s.size()) { return false; }\n\n  val = 0;\n  for (; cnt; i++, cnt--) {\n    if (!s[i]) { return false; }\n    auto v = 0;\n    if (is_hex(s[i], v)) {\n      val = val * 16 + v;\n    } else {\n      return false;\n    }\n  }\n  return true;\n}\n\ninline std::string from_i_to_hex(size_t n) {\n  static const auto charset = \"0123456789abcdef\";\n  std::string ret;\n  do {\n    ret = charset[n & 15] + ret;\n    n >>= 4;\n  } while (n > 0);\n  return ret;\n}\n\ninline size_t to_utf8(int code, char *buff) {\n  if (code < 0x0080) {\n    buff[0] = static_cast<char>(code & 0x7F);\n    return 1;\n  } else if (code < 0x0800) {\n    buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));\n    buff[1] = static_cast<char>(0x80 | (code & 0x3F));\n    return 2;\n  } else if (code < 0xD800) {\n    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));\n    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n    buff[2] = static_cast<char>(0x80 | (code & 0x3F));\n    return 3;\n  } else if (code < 0xE000) { // D800 - DFFF is invalid...\n    return 0;\n  } else if (code < 0x10000) {\n    buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));\n    buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n    buff[2] = static_cast<char>(0x80 | (code & 0x3F));\n    return 3;\n  } else if (code < 0x110000) {\n    buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));\n    buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));\n    buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));\n    buff[3] = static_cast<char>(0x80 | (code & 0x3F));\n    return 4;\n  }\n\n  // NOTREACHED\n  return 0;\n}\n\n// NOTE: This code came up with the following stackoverflow post:\n// https://stackoverflow.com/questions/180947/base64-decode-snippet-in-c\ninline std::string base64_encode(const std::string &in) {\n  static const auto lookup =\n      \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n  std::string out;\n  out.reserve(in.size());\n\n  auto val = 0;\n  auto valb = -6;\n\n  for (auto c : in) {\n    val = (val << 8) + static_cast<uint8_t>(c);\n    valb += 8;\n    while (valb >= 0) {\n      out.push_back(lookup[(val >> valb) & 0x3F]);\n      valb -= 6;\n    }\n  }\n\n  if (valb > -6) { out.push_back(lookup[((val << 8) >> (valb + 8)) & 0x3F]); }\n\n  while (out.size() % 4) {\n    out.push_back('=');\n  }\n\n  return out;\n}\n\ninline bool is_valid_path(const std::string &path) {\n  size_t level = 0;\n  size_t i = 0;\n\n  // Skip slash\n  while (i < path.size() && path[i] == '/') {\n    i++;\n  }\n\n  while (i < path.size()) {\n    // Read component\n    auto beg = i;\n    while (i < path.size() && path[i] != '/') {\n      if (path[i] == '\\0') {\n        return false;\n      } else if (path[i] == '\\\\') {\n        return false;\n      }\n      i++;\n    }\n\n    auto len = i - beg;\n    assert(len > 0);\n\n    if (!path.compare(beg, len, \".\")) {\n      ;\n    } else if (!path.compare(beg, len, \"..\")) {\n      if (level == 0) { return false; }\n      level--;\n    } else {\n      level++;\n    }\n\n    // Skip slash\n    while (i < path.size() && path[i] == '/') {\n      i++;\n    }\n  }\n\n  return true;\n}\n\ninline FileStat::FileStat(const std::string &path) {\n#if defined(_WIN32)\n  auto wpath = u8string_to_wstring(path.c_str());\n  ret_ = _wstat(wpath.c_str(), &st_);\n#else\n  ret_ = stat(path.c_str(), &st_);\n#endif\n}\ninline bool FileStat::is_file() const {\n  return ret_ >= 0 && S_ISREG(st_.st_mode);\n}\ninline bool FileStat::is_dir() const {\n  return ret_ >= 0 && S_ISDIR(st_.st_mode);\n}\n\ninline std::string encode_path(const std::string &s) {\n  std::string result;\n  result.reserve(s.size());\n\n  for (size_t i = 0; s[i]; i++) {\n    switch (s[i]) {\n    case ' ': result += \"%20\"; break;\n    case '+': result += \"%2B\"; break;\n    case '\\r': result += \"%0D\"; break;\n    case '\\n': result += \"%0A\"; break;\n    case '\\'': result += \"%27\"; break;\n    case ',': result += \"%2C\"; break;\n    // case ':': result += \"%3A\"; break; // ok? probably...\n    case ';': result += \"%3B\"; break;\n    default:\n      auto c = static_cast<uint8_t>(s[i]);\n      if (c >= 0x80) {\n        result += '%';\n        char hex[4];\n        auto len = snprintf(hex, sizeof(hex) - 1, \"%02X\", c);\n        assert(len == 2);\n        result.append(hex, static_cast<size_t>(len));\n      } else {\n        result += s[i];\n      }\n      break;\n    }\n  }\n\n  return result;\n}\n\ninline std::string file_extension(const std::string &path) {\n  std::smatch m;\n  thread_local auto re = std::regex(\"\\\\.([a-zA-Z0-9]+)$\");\n  if (std::regex_search(path, m, re)) { return m[1].str(); }\n  return std::string();\n}\n\ninline bool is_space_or_tab(char c) { return c == ' ' || c == '\\t'; }\n\ninline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,\n                                      size_t right) {\n  while (b + left < e && is_space_or_tab(b[left])) {\n    left++;\n  }\n  while (right > 0 && is_space_or_tab(b[right - 1])) {\n    right--;\n  }\n  return std::make_pair(left, right);\n}\n\ninline std::string trim_copy(const std::string &s) {\n  auto r = trim(s.data(), s.data() + s.size(), 0, s.size());\n  return s.substr(r.first, r.second - r.first);\n}\n\ninline std::string trim_double_quotes_copy(const std::string &s) {\n  if (s.length() >= 2 && s.front() == '\"' && s.back() == '\"') {\n    return s.substr(1, s.size() - 2);\n  }\n  return s;\n}\n\ninline void\ndivide(const char *data, std::size_t size, char d,\n       std::function<void(const char *, std::size_t, const char *, std::size_t)>\n           fn) {\n  const auto it = std::find(data, data + size, d);\n  const auto found = static_cast<std::size_t>(it != data + size);\n  const auto lhs_data = data;\n  const auto lhs_size = static_cast<std::size_t>(it - data);\n  const auto rhs_data = it + found;\n  const auto rhs_size = size - lhs_size - found;\n\n  fn(lhs_data, lhs_size, rhs_data, rhs_size);\n}\n\ninline void\ndivide(const std::string &str, char d,\n       std::function<void(const char *, std::size_t, const char *, std::size_t)>\n           fn) {\n  divide(str.data(), str.size(), d, std::move(fn));\n}\n\ninline void split(const char *b, const char *e, char d,\n                  std::function<void(const char *, const char *)> fn) {\n  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));\n}\n\ninline void split(const char *b, const char *e, char d, size_t m,\n                  std::function<void(const char *, const char *)> fn) {\n  size_t i = 0;\n  size_t beg = 0;\n  size_t count = 1;\n\n  while (e ? (b + i < e) : (b[i] != '\\0')) {\n    if (b[i] == d && count < m) {\n      auto r = trim(b, e, beg, i);\n      if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }\n      beg = i + 1;\n      count++;\n    }\n    i++;\n  }\n\n  if (i) {\n    auto r = trim(b, e, beg, i);\n    if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }\n  }\n}\n\ninline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,\n                                              size_t fixed_buffer_size)\n    : strm_(strm), fixed_buffer_(fixed_buffer),\n      fixed_buffer_size_(fixed_buffer_size) {}\n\ninline const char *stream_line_reader::ptr() const {\n  if (growable_buffer_.empty()) {\n    return fixed_buffer_;\n  } else {\n    return growable_buffer_.data();\n  }\n}\n\ninline size_t stream_line_reader::size() const {\n  if (growable_buffer_.empty()) {\n    return fixed_buffer_used_size_;\n  } else {\n    return growable_buffer_.size();\n  }\n}\n\ninline bool stream_line_reader::end_with_crlf() const {\n  auto end = ptr() + size();\n  return size() >= 2 && end[-2] == '\\r' && end[-1] == '\\n';\n}\n\ninline bool stream_line_reader::getline() {\n  fixed_buffer_used_size_ = 0;\n  growable_buffer_.clear();\n\n#ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR\n  char prev_byte = 0;\n#endif\n\n  for (size_t i = 0;; i++) {\n    if (size() >= CPPHTTPLIB_MAX_LINE_LENGTH) {\n      // Treat exceptionally long lines as an error to\n      // prevent infinite loops/memory exhaustion\n      return false;\n    }\n    char byte;\n    auto n = strm_.read(&byte, 1);\n\n    if (n < 0) {\n      return false;\n    } else if (n == 0) {\n      if (i == 0) {\n        return false;\n      } else {\n        break;\n      }\n    }\n\n    append(byte);\n\n#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR\n    if (byte == '\\n') { break; }\n#else\n    if (prev_byte == '\\r' && byte == '\\n') { break; }\n    prev_byte = byte;\n#endif\n  }\n\n  return true;\n}\n\ninline void stream_line_reader::append(char c) {\n  if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) {\n    fixed_buffer_[fixed_buffer_used_size_++] = c;\n    fixed_buffer_[fixed_buffer_used_size_] = '\\0';\n  } else {\n    if (growable_buffer_.empty()) {\n      assert(fixed_buffer_[fixed_buffer_used_size_] == '\\0');\n      growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);\n    }\n    growable_buffer_ += c;\n  }\n}\n\ninline mmap::mmap(const char *path) { open(path); }\n\ninline mmap::~mmap() { close(); }\n\ninline bool mmap::open(const char *path) {\n  close();\n\n#if defined(_WIN32)\n  auto wpath = u8string_to_wstring(path);\n  if (wpath.empty()) { return false; }\n\n#if _WIN32_WINNT >= _WIN32_WINNT_WIN8\n  hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,\n                         OPEN_EXISTING, NULL);\n#else\n  hFile_ = ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,\n                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\n#endif\n\n  if (hFile_ == INVALID_HANDLE_VALUE) { return false; }\n\n  LARGE_INTEGER size{};\n  if (!::GetFileSizeEx(hFile_, &size)) { return false; }\n  // If the following line doesn't compile due to QuadPart, update Windows SDK.\n  // See:\n  // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721\n  if (static_cast<ULONGLONG>(size.QuadPart) >\n      (std::numeric_limits<decltype(size_)>::max)()) {\n    // `size_t` might be 32-bits, on 32-bits Windows.\n    return false;\n  }\n  size_ = static_cast<size_t>(size.QuadPart);\n\n#if _WIN32_WINNT >= _WIN32_WINNT_WIN8\n  hMapping_ =\n      ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);\n#else\n  hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);\n#endif\n\n  // Special treatment for an empty file...\n  if (hMapping_ == NULL && size_ == 0) {\n    close();\n    is_open_empty_file = true;\n    return true;\n  }\n\n  if (hMapping_ == NULL) {\n    close();\n    return false;\n  }\n\n#if _WIN32_WINNT >= _WIN32_WINNT_WIN8\n  addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);\n#else\n  addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);\n#endif\n\n  if (addr_ == nullptr) {\n    close();\n    return false;\n  }\n#else\n  fd_ = ::open(path, O_RDONLY);\n  if (fd_ == -1) { return false; }\n\n  struct stat sb;\n  if (fstat(fd_, &sb) == -1) {\n    close();\n    return false;\n  }\n  size_ = static_cast<size_t>(sb.st_size);\n\n  addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);\n\n  // Special treatment for an empty file...\n  if (addr_ == MAP_FAILED && size_ == 0) {\n    close();\n    is_open_empty_file = true;\n    return false;\n  }\n#endif\n\n  return true;\n}\n\ninline bool mmap::is_open() const {\n  return is_open_empty_file ? true : addr_ != nullptr;\n}\n\ninline size_t mmap::size() const { return size_; }\n\ninline const char *mmap::data() const {\n  return is_open_empty_file ? \"\" : static_cast<const char *>(addr_);\n}\n\ninline void mmap::close() {\n#if defined(_WIN32)\n  if (addr_) {\n    ::UnmapViewOfFile(addr_);\n    addr_ = nullptr;\n  }\n\n  if (hMapping_) {\n    ::CloseHandle(hMapping_);\n    hMapping_ = NULL;\n  }\n\n  if (hFile_ != INVALID_HANDLE_VALUE) {\n    ::CloseHandle(hFile_);\n    hFile_ = INVALID_HANDLE_VALUE;\n  }\n\n  is_open_empty_file = false;\n#else\n  if (addr_ != nullptr) {\n    munmap(addr_, size_);\n    addr_ = nullptr;\n  }\n\n  if (fd_ != -1) {\n    ::close(fd_);\n    fd_ = -1;\n  }\n#endif\n  size_ = 0;\n}\ninline int close_socket(socket_t sock) {\n#ifdef _WIN32\n  return closesocket(sock);\n#else\n  return close(sock);\n#endif\n}\n\ntemplate <typename T> inline ssize_t handle_EINTR(T fn) {\n  ssize_t res = 0;\n  while (true) {\n    res = fn();\n    if (res < 0 && errno == EINTR) {\n      std::this_thread::sleep_for(std::chrono::microseconds{1});\n      continue;\n    }\n    break;\n  }\n  return res;\n}\n\ninline ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags) {\n  return handle_EINTR([&]() {\n    return recv(sock,\n#ifdef _WIN32\n                static_cast<char *>(ptr), static_cast<int>(size),\n#else\n                ptr, size,\n#endif\n                flags);\n  });\n}\n\ninline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,\n                           int flags) {\n  return handle_EINTR([&]() {\n    return send(sock,\n#ifdef _WIN32\n                static_cast<const char *>(ptr), static_cast<int>(size),\n#else\n                ptr, size,\n#endif\n                flags);\n  });\n}\n\ninline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {\n#ifdef _WIN32\n  return ::WSAPoll(fds, nfds, timeout);\n#else\n  return ::poll(fds, nfds, timeout);\n#endif\n}\n\ntemplate <bool Read>\ninline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {\n#ifdef __APPLE__\n  if (sock >= FD_SETSIZE) { return -1; }\n\n  fd_set fds, *rfds, *wfds;\n  FD_ZERO(&fds);\n  FD_SET(sock, &fds);\n  rfds = (Read ? &fds : nullptr);\n  wfds = (Read ? nullptr : &fds);\n\n  timeval tv;\n  tv.tv_sec = static_cast<long>(sec);\n  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);\n\n  return handle_EINTR([&]() {\n    return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);\n  });\n#else\n  struct pollfd pfd;\n  pfd.fd = sock;\n  pfd.events = (Read ? POLLIN : POLLOUT);\n\n  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);\n\n  return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });\n#endif\n}\n\ninline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {\n  return select_impl<true>(sock, sec, usec);\n}\n\ninline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {\n  return select_impl<false>(sock, sec, usec);\n}\n\ninline Error wait_until_socket_is_ready(socket_t sock, time_t sec,\n                                        time_t usec) {\n#ifdef __APPLE__\n  if (sock >= FD_SETSIZE) { return Error::Connection; }\n\n  fd_set fdsr, fdsw;\n  FD_ZERO(&fdsr);\n  FD_ZERO(&fdsw);\n  FD_SET(sock, &fdsr);\n  FD_SET(sock, &fdsw);\n\n  timeval tv;\n  tv.tv_sec = static_cast<long>(sec);\n  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);\n\n  auto ret = handle_EINTR([&]() {\n    return select(static_cast<int>(sock + 1), &fdsr, &fdsw, nullptr, &tv);\n  });\n\n  if (ret == 0) { return Error::ConnectionTimeout; }\n\n  if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {\n    auto error = 0;\n    socklen_t len = sizeof(error);\n    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,\n                          reinterpret_cast<char *>(&error), &len);\n    auto successful = res >= 0 && !error;\n    return successful ? Error::Success : Error::Connection;\n  }\n\n  return Error::Connection;\n#else\n  struct pollfd pfd_read;\n  pfd_read.fd = sock;\n  pfd_read.events = POLLIN | POLLOUT;\n\n  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);\n\n  auto poll_res =\n      handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });\n\n  if (poll_res == 0) { return Error::ConnectionTimeout; }\n\n  if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {\n    auto error = 0;\n    socklen_t len = sizeof(error);\n    auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,\n                          reinterpret_cast<char *>(&error), &len);\n    auto successful = res >= 0 && !error;\n    return successful ? Error::Success : Error::Connection;\n  }\n\n  return Error::Connection;\n#endif\n}\n\ninline bool is_socket_alive(socket_t sock) {\n  const auto val = detail::select_read(sock, 0, 0);\n  if (val == 0) {\n    return true;\n  } else if (val < 0 && errno == EBADF) {\n    return false;\n  }\n  char buf[1];\n  return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;\n}\n\nclass SocketStream final : public Stream {\npublic:\n  SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,\n               time_t write_timeout_sec, time_t write_timeout_usec,\n               time_t max_timeout_msec = 0,\n               std::chrono::time_point<std::chrono::steady_clock> start_time =\n                   (std::chrono::steady_clock::time_point::min)());\n  ~SocketStream() override;\n\n  bool is_readable() const override;\n  bool wait_readable() const override;\n  bool wait_writable() const override;\n  ssize_t read(char *ptr, size_t size) override;\n  ssize_t write(const char *ptr, size_t size) override;\n  void get_remote_ip_and_port(std::string &ip, int &port) const override;\n  void get_local_ip_and_port(std::string &ip, int &port) const override;\n  socket_t socket() const override;\n  time_t duration() const override;\n\nprivate:\n  socket_t sock_;\n  time_t read_timeout_sec_;\n  time_t read_timeout_usec_;\n  time_t write_timeout_sec_;\n  time_t write_timeout_usec_;\n  time_t max_timeout_msec_;\n  const std::chrono::time_point<std::chrono::steady_clock> start_time_;\n\n  std::vector<char> read_buff_;\n  size_t read_buff_off_ = 0;\n  size_t read_buff_content_size_ = 0;\n\n  static const size_t read_buff_size_ = 1024l * 4;\n};\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nclass SSLSocketStream final : public Stream {\npublic:\n  SSLSocketStream(\n      socket_t sock, SSL *ssl, time_t read_timeout_sec,\n      time_t read_timeout_usec, time_t write_timeout_sec,\n      time_t write_timeout_usec, time_t max_timeout_msec = 0,\n      std::chrono::time_point<std::chrono::steady_clock> start_time =\n          (std::chrono::steady_clock::time_point::min)());\n  ~SSLSocketStream() override;\n\n  bool is_readable() const override;\n  bool wait_readable() const override;\n  bool wait_writable() const override;\n  ssize_t read(char *ptr, size_t size) override;\n  ssize_t write(const char *ptr, size_t size) override;\n  void get_remote_ip_and_port(std::string &ip, int &port) const override;\n  void get_local_ip_and_port(std::string &ip, int &port) const override;\n  socket_t socket() const override;\n  time_t duration() const override;\n\nprivate:\n  socket_t sock_;\n  SSL *ssl_;\n  time_t read_timeout_sec_;\n  time_t read_timeout_usec_;\n  time_t write_timeout_sec_;\n  time_t write_timeout_usec_;\n  time_t max_timeout_msec_;\n  const std::chrono::time_point<std::chrono::steady_clock> start_time_;\n};\n#endif\n\ninline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,\n                       time_t keep_alive_timeout_sec) {\n  using namespace std::chrono;\n\n  const auto interval_usec =\n      CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;\n\n  // Avoid expensive `steady_clock::now()` call for the first time\n  if (select_read(sock, 0, interval_usec) > 0) { return true; }\n\n  const auto start = steady_clock::now() - microseconds{interval_usec};\n  const auto timeout = seconds{keep_alive_timeout_sec};\n\n  while (true) {\n    if (svr_sock == INVALID_SOCKET) {\n      break; // Server socket is closed\n    }\n\n    auto val = select_read(sock, 0, interval_usec);\n    if (val < 0) {\n      break; // Ssocket error\n    } else if (val == 0) {\n      if (steady_clock::now() - start > timeout) {\n        break; // Timeout\n      }\n    } else {\n      return true; // Ready for read\n    }\n  }\n\n  return false;\n}\n\ntemplate <typename T>\ninline bool\nprocess_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,\n                           size_t keep_alive_max_count,\n                           time_t keep_alive_timeout_sec, T callback) {\n  assert(keep_alive_max_count > 0);\n  auto ret = false;\n  auto count = keep_alive_max_count;\n  while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {\n    auto close_connection = count == 1;\n    auto connection_closed = false;\n    ret = callback(close_connection, connection_closed);\n    if (!ret || connection_closed) { break; }\n    count--;\n  }\n  return ret;\n}\n\ntemplate <typename T>\ninline bool\nprocess_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,\n                      size_t keep_alive_max_count,\n                      time_t keep_alive_timeout_sec, time_t read_timeout_sec,\n                      time_t read_timeout_usec, time_t write_timeout_sec,\n                      time_t write_timeout_usec, T callback) {\n  return process_server_socket_core(\n      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,\n      [&](bool close_connection, bool &connection_closed) {\n        SocketStream strm(sock, read_timeout_sec, read_timeout_usec,\n                          write_timeout_sec, write_timeout_usec);\n        return callback(strm, close_connection, connection_closed);\n      });\n}\n\ninline bool process_client_socket(\n    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,\n    time_t write_timeout_sec, time_t write_timeout_usec,\n    time_t max_timeout_msec,\n    std::chrono::time_point<std::chrono::steady_clock> start_time,\n    std::function<bool(Stream &)> callback) {\n  SocketStream strm(sock, read_timeout_sec, read_timeout_usec,\n                    write_timeout_sec, write_timeout_usec, max_timeout_msec,\n                    start_time);\n  return callback(strm);\n}\n\ninline int shutdown_socket(socket_t sock) {\n#ifdef _WIN32\n  return shutdown(sock, SD_BOTH);\n#else\n  return shutdown(sock, SHUT_RDWR);\n#endif\n}\n\ninline std::string escape_abstract_namespace_unix_domain(const std::string &s) {\n  if (s.size() > 1 && s[0] == '\\0') {\n    auto ret = s;\n    ret[0] = '@';\n    return ret;\n  }\n  return s;\n}\n\ninline std::string\nunescape_abstract_namespace_unix_domain(const std::string &s) {\n  if (s.size() > 1 && s[0] == '@') {\n    auto ret = s;\n    ret[0] = '\\0';\n    return ret;\n  }\n  return s;\n}\n\ninline int getaddrinfo_with_timeout(const char *node, const char *service,\n                                    const struct addrinfo *hints,\n                                    struct addrinfo **res, time_t timeout_sec) {\n#ifdef CPPHTTPLIB_USE_NON_BLOCKING_GETADDRINFO\n  if (timeout_sec <= 0) {\n    // No timeout specified, use standard getaddrinfo\n    return getaddrinfo(node, service, hints, res);\n  }\n\n#ifdef _WIN32\n  // Windows-specific implementation using GetAddrInfoEx with overlapped I/O\n  OVERLAPPED overlapped = {0};\n  HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);\n  if (!event) { return EAI_FAIL; }\n\n  overlapped.hEvent = event;\n\n  PADDRINFOEXW result_addrinfo = nullptr;\n  HANDLE cancel_handle = nullptr;\n\n  ADDRINFOEXW hints_ex = {0};\n  if (hints) {\n    hints_ex.ai_flags = hints->ai_flags;\n    hints_ex.ai_family = hints->ai_family;\n    hints_ex.ai_socktype = hints->ai_socktype;\n    hints_ex.ai_protocol = hints->ai_protocol;\n  }\n\n  auto wnode = u8string_to_wstring(node);\n  auto wservice = u8string_to_wstring(service);\n\n  auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,\n                              hints ? &hints_ex : nullptr, &result_addrinfo,\n                              nullptr, &overlapped, nullptr, &cancel_handle);\n\n  if (ret == WSA_IO_PENDING) {\n    auto wait_result =\n        ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));\n    if (wait_result == WAIT_TIMEOUT) {\n      if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }\n      ::CloseHandle(event);\n      return EAI_AGAIN;\n    }\n\n    DWORD bytes_returned;\n    if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,\n                               &bytes_returned, FALSE)) {\n      ::CloseHandle(event);\n      return ::WSAGetLastError();\n    }\n  }\n\n  ::CloseHandle(event);\n\n  if (ret == NO_ERROR || ret == WSA_IO_PENDING) {\n    *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);\n    return 0;\n  }\n\n  return ret;\n#elif TARGET_OS_MAC\n  // macOS implementation using CFHost API for asynchronous DNS resolution\n  CFStringRef hostname_ref = CFStringCreateWithCString(\n      kCFAllocatorDefault, node, kCFStringEncodingUTF8);\n  if (!hostname_ref) { return EAI_MEMORY; }\n\n  CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);\n  CFRelease(hostname_ref);\n  if (!host_ref) { return EAI_MEMORY; }\n\n  // Set up context for callback\n  struct CFHostContext {\n    bool completed = false;\n    bool success = false;\n    CFArrayRef addresses = nullptr;\n    std::mutex mutex;\n    std::condition_variable cv;\n  } context;\n\n  CFHostClientContext client_context;\n  memset(&client_context, 0, sizeof(client_context));\n  client_context.info = &context;\n\n  // Set callback\n  auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,\n                     const CFStreamError *error, void *info) {\n    auto ctx = static_cast<CFHostContext *>(info);\n    std::lock_guard<std::mutex> lock(ctx->mutex);\n\n    if (error && error->error != 0) {\n      ctx->success = false;\n    } else {\n      Boolean hasBeenResolved;\n      ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);\n      if (ctx->addresses && hasBeenResolved) {\n        CFRetain(ctx->addresses);\n        ctx->success = true;\n      } else {\n        ctx->success = false;\n      }\n    }\n    ctx->completed = true;\n    ctx->cv.notify_one();\n  };\n\n  if (!CFHostSetClient(host_ref, callback, &client_context)) {\n    CFRelease(host_ref);\n    return EAI_SYSTEM;\n  }\n\n  // Schedule on run loop\n  CFRunLoopRef run_loop = CFRunLoopGetCurrent();\n  CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);\n\n  // Start resolution\n  CFStreamError stream_error;\n  if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {\n    CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);\n    CFRelease(host_ref);\n    return EAI_FAIL;\n  }\n\n  // Wait for completion with timeout\n  auto timeout_time =\n      std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);\n  bool timed_out = false;\n\n  {\n    std::unique_lock<std::mutex> lock(context.mutex);\n\n    while (!context.completed) {\n      auto now = std::chrono::steady_clock::now();\n      if (now >= timeout_time) {\n        timed_out = true;\n        break;\n      }\n\n      // Run the runloop for a short time\n      lock.unlock();\n      CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);\n      lock.lock();\n    }\n  }\n\n  // Clean up\n  CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);\n  CFHostSetClient(host_ref, nullptr, nullptr);\n\n  if (timed_out || !context.completed) {\n    CFHostCancelInfoResolution(host_ref, kCFHostAddresses);\n    CFRelease(host_ref);\n    return EAI_AGAIN;\n  }\n\n  if (!context.success || !context.addresses) {\n    CFRelease(host_ref);\n    return EAI_NODATA;\n  }\n\n  // Convert CFArray to addrinfo\n  CFIndex count = CFArrayGetCount(context.addresses);\n  if (count == 0) {\n    CFRelease(context.addresses);\n    CFRelease(host_ref);\n    return EAI_NODATA;\n  }\n\n  struct addrinfo *result_addrinfo = nullptr;\n  struct addrinfo **current = &result_addrinfo;\n\n  for (CFIndex i = 0; i < count; i++) {\n    CFDataRef addr_data =\n        static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));\n    if (!addr_data) continue;\n\n    const struct sockaddr *sockaddr_ptr =\n        reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));\n    socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));\n\n    // Allocate addrinfo structure\n    *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));\n    if (!*current) {\n      freeaddrinfo(result_addrinfo);\n      CFRelease(context.addresses);\n      CFRelease(host_ref);\n      return EAI_MEMORY;\n    }\n\n    memset(*current, 0, sizeof(struct addrinfo));\n\n    // Set up addrinfo fields\n    (*current)->ai_family = sockaddr_ptr->sa_family;\n    (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;\n    (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;\n    (*current)->ai_addrlen = sockaddr_len;\n\n    // Copy sockaddr\n    (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));\n    if (!(*current)->ai_addr) {\n      freeaddrinfo(result_addrinfo);\n      CFRelease(context.addresses);\n      CFRelease(host_ref);\n      return EAI_MEMORY;\n    }\n    memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);\n\n    // Set port if service is specified\n    if (service && strlen(service) > 0) {\n      int port = atoi(service);\n      if (port > 0) {\n        if (sockaddr_ptr->sa_family == AF_INET) {\n          reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)\n              ->sin_port = htons(static_cast<uint16_t>(port));\n        } else if (sockaddr_ptr->sa_family == AF_INET6) {\n          reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)\n              ->sin6_port = htons(static_cast<uint16_t>(port));\n        }\n      }\n    }\n\n    current = &((*current)->ai_next);\n  }\n\n  CFRelease(context.addresses);\n  CFRelease(host_ref);\n\n  *res = result_addrinfo;\n  return 0;\n#elif defined(_GNU_SOURCE) && defined(__GLIBC__) &&                            \\\n    (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))\n  // Linux implementation using getaddrinfo_a for asynchronous DNS resolution\n  struct gaicb request;\n  struct gaicb *requests[1] = {&request};\n  struct sigevent sevp;\n  struct timespec timeout;\n\n  // Initialize the request structure\n  memset(&request, 0, sizeof(request));\n  request.ar_name = node;\n  request.ar_service = service;\n  request.ar_request = hints;\n\n  // Set up timeout\n  timeout.tv_sec = timeout_sec;\n  timeout.tv_nsec = 0;\n\n  // Initialize sigevent structure (not used, but required)\n  memset(&sevp, 0, sizeof(sevp));\n  sevp.sigev_notify = SIGEV_NONE;\n\n  // Start asynchronous resolution\n  int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);\n  if (start_result != 0) { return start_result; }\n\n  // Wait for completion with timeout\n  int wait_result =\n      gai_suspend((const struct gaicb *const *)requests, 1, &timeout);\n\n  if (wait_result == 0 || wait_result == EAI_ALLDONE) {\n    // Completed successfully, get the result\n    int gai_result = gai_error(&request);\n    if (gai_result == 0) {\n      *res = request.ar_result;\n      return 0;\n    } else {\n      // Clean up on error\n      if (request.ar_result) { freeaddrinfo(request.ar_result); }\n      return gai_result;\n    }\n  } else if (wait_result == EAI_AGAIN) {\n    // Timeout occurred, cancel the request\n    gai_cancel(&request);\n    return EAI_AGAIN;\n  } else {\n    // Other error occurred\n    gai_cancel(&request);\n    return wait_result;\n  }\n#else\n  // Fallback implementation using thread-based timeout for other Unix systems\n\n  struct GetAddrInfoState {\n    std::mutex mutex;\n    std::condition_variable result_cv;\n    bool completed = false;\n    int result = EAI_SYSTEM;\n    std::string node = node;\n    std::string service = service;\n    struct addrinfo hints = hints;\n    struct addrinfo *info = nullptr;\n  };\n\n  // Allocate on the heap, so the resolver thread can keep using the data.\n  auto state = std::make_shared<GetAddrInfoState>();\n\n  std::thread resolve_thread([=]() {\n    auto thread_result = getaddrinfo(\n        state->node.c_str(), state->service.c_str(), hints, &state->info);\n\n    std::lock_guard<std::mutex> lock(state->mutex);\n    state->result = thread_result;\n    state->completed = true;\n    state->result_cv.notify_one();\n  });\n\n  // Wait for completion or timeout\n  std::unique_lock<std::mutex> lock(state->mutex);\n  auto finished =\n      state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),\n                                [&] { return state->completed; });\n\n  if (finished) {\n    // Operation completed within timeout\n    resolve_thread.join();\n    *res = state->info;\n    return state->result;\n  } else {\n    // Timeout occurred\n    resolve_thread.detach(); // Let the thread finish in background\n    return EAI_AGAIN;        // Return timeout error\n  }\n#endif\n#else\n  (void)(timeout_sec); // Unused parameter for non-blocking getaddrinfo\n  return getaddrinfo(node, service, hints, res);\n#endif\n}\n\ntemplate <typename BindOrConnect>\nsocket_t create_socket(const std::string &host, const std::string &ip, int port,\n                       int address_family, int socket_flags, bool tcp_nodelay,\n                       bool ipv6_v6only, SocketOptions socket_options,\n                       BindOrConnect bind_or_connect, time_t timeout_sec = 0) {\n  // Get address info\n  const char *node = nullptr;\n  struct addrinfo hints;\n  struct addrinfo *result;\n\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_protocol = IPPROTO_IP;\n\n  if (!ip.empty()) {\n    node = ip.c_str();\n    // Ask getaddrinfo to convert IP in c-string to address\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_flags = AI_NUMERICHOST;\n  } else {\n    if (!host.empty()) { node = host.c_str(); }\n    hints.ai_family = address_family;\n    hints.ai_flags = socket_flags;\n  }\n\n#if !defined(_WIN32) || defined(CPPHTTPLIB_HAVE_AFUNIX_H)\n  if (hints.ai_family == AF_UNIX) {\n    const auto addrlen = host.length();\n    if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }\n\n#ifdef SOCK_CLOEXEC\n    auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,\n                       hints.ai_protocol);\n#else\n    auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);\n#endif\n\n    if (sock != INVALID_SOCKET) {\n      sockaddr_un addr{};\n      addr.sun_family = AF_UNIX;\n\n      auto unescaped_host = unescape_abstract_namespace_unix_domain(host);\n      std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);\n\n      hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);\n      hints.ai_addrlen = static_cast<socklen_t>(\n          sizeof(addr) - sizeof(addr.sun_path) + addrlen);\n\n#ifndef SOCK_CLOEXEC\n#ifndef _WIN32\n      fcntl(sock, F_SETFD, FD_CLOEXEC);\n#endif\n#endif\n\n      if (socket_options) { socket_options(sock); }\n\n#ifdef _WIN32\n      // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so\n      // remove the option.\n      detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);\n#endif\n\n      bool dummy;\n      if (!bind_or_connect(sock, hints, dummy)) {\n        close_socket(sock);\n        sock = INVALID_SOCKET;\n      }\n    }\n    return sock;\n  }\n#endif\n\n  auto service = std::to_string(port);\n\n  if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,\n                               timeout_sec)) {\n#if defined __linux__ && !defined __ANDROID__\n    res_init();\n#endif\n    return INVALID_SOCKET;\n  }\n  auto se = detail::scope_exit([&] { freeaddrinfo(result); });\n\n  for (auto rp = result; rp; rp = rp->ai_next) {\n    // Create a socket\n#ifdef _WIN32\n    auto sock =\n        WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, nullptr, 0,\n                   WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);\n    /**\n     * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1\n     * and above the socket creation fails on older Windows Systems.\n     *\n     * Let's try to create a socket the old way in this case.\n     *\n     * Reference:\n     * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa\n     *\n     * WSA_FLAG_NO_HANDLE_INHERIT:\n     * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with\n     * SP1, and later\n     *\n     */\n    if (sock == INVALID_SOCKET) {\n      sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n    }\n#else\n\n#ifdef SOCK_CLOEXEC\n    auto sock =\n        socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);\n#else\n    auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);\n#endif\n\n#endif\n    if (sock == INVALID_SOCKET) { continue; }\n\n#if !defined _WIN32 && !defined SOCK_CLOEXEC\n    if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {\n      close_socket(sock);\n      continue;\n    }\n#endif\n\n    if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }\n\n    if (rp->ai_family == AF_INET6) {\n      set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);\n    }\n\n    if (socket_options) { socket_options(sock); }\n\n    // bind or connect\n    auto quit = false;\n    if (bind_or_connect(sock, *rp, quit)) { return sock; }\n\n    close_socket(sock);\n\n    if (quit) { break; }\n  }\n\n  return INVALID_SOCKET;\n}\n\ninline void set_nonblocking(socket_t sock, bool nonblocking) {\n#ifdef _WIN32\n  auto flags = nonblocking ? 1UL : 0UL;\n  ioctlsocket(sock, FIONBIO, &flags);\n#else\n  auto flags = fcntl(sock, F_GETFL, 0);\n  fcntl(sock, F_SETFL,\n        nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK)));\n#endif\n}\n\ninline bool is_connection_error() {\n#ifdef _WIN32\n  return WSAGetLastError() != WSAEWOULDBLOCK;\n#else\n  return errno != EINPROGRESS;\n#endif\n}\n\ninline bool bind_ip_address(socket_t sock, const std::string &host) {\n  struct addrinfo hints;\n  struct addrinfo *result;\n\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_protocol = 0;\n\n  if (getaddrinfo_with_timeout(host.c_str(), \"0\", &hints, &result, 0)) {\n    return false;\n  }\n\n  auto se = detail::scope_exit([&] { freeaddrinfo(result); });\n\n  auto ret = false;\n  for (auto rp = result; rp; rp = rp->ai_next) {\n    const auto &ai = *rp;\n    if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {\n      ret = true;\n      break;\n    }\n  }\n\n  return ret;\n}\n\n#if !defined _WIN32 && !defined ANDROID && !defined _AIX && !defined __MVS__\n#define USE_IF2IP\n#endif\n\n#ifdef USE_IF2IP\ninline std::string if2ip(int address_family, const std::string &ifn) {\n  struct ifaddrs *ifap;\n  getifaddrs(&ifap);\n  auto se = detail::scope_exit([&] { freeifaddrs(ifap); });\n\n  std::string addr_candidate;\n  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {\n    if (ifa->ifa_addr && ifn == ifa->ifa_name &&\n        (AF_UNSPEC == address_family ||\n         ifa->ifa_addr->sa_family == address_family)) {\n      if (ifa->ifa_addr->sa_family == AF_INET) {\n        auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);\n        char buf[INET_ADDRSTRLEN];\n        if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {\n          return std::string(buf, INET_ADDRSTRLEN);\n        }\n      } else if (ifa->ifa_addr->sa_family == AF_INET6) {\n        auto sa = reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr);\n        if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) {\n          char buf[INET6_ADDRSTRLEN] = {};\n          if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) {\n            // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL\n            auto s6_addr_head = sa->sin6_addr.s6_addr[0];\n            if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {\n              addr_candidate = std::string(buf, INET6_ADDRSTRLEN);\n            } else {\n              return std::string(buf, INET6_ADDRSTRLEN);\n            }\n          }\n        }\n      }\n    }\n  }\n  return addr_candidate;\n}\n#endif\n\ninline socket_t create_client_socket(\n    const std::string &host, const std::string &ip, int port,\n    int address_family, bool tcp_nodelay, bool ipv6_v6only,\n    SocketOptions socket_options, time_t connection_timeout_sec,\n    time_t connection_timeout_usec, time_t read_timeout_sec,\n    time_t read_timeout_usec, time_t write_timeout_sec,\n    time_t write_timeout_usec, const std::string &intf, Error &error) {\n  auto sock = create_socket(\n      host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,\n      std::move(socket_options),\n      [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {\n        if (!intf.empty()) {\n#ifdef USE_IF2IP\n          auto ip_from_if = if2ip(address_family, intf);\n          if (ip_from_if.empty()) { ip_from_if = intf; }\n          if (!bind_ip_address(sock2, ip_from_if)) {\n            error = Error::BindIPAddress;\n            return false;\n          }\n#endif\n        }\n\n        set_nonblocking(sock2, true);\n\n        auto ret =\n            ::connect(sock2, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));\n\n        if (ret < 0) {\n          if (is_connection_error()) {\n            error = Error::Connection;\n            return false;\n          }\n          error = wait_until_socket_is_ready(sock2, connection_timeout_sec,\n                                             connection_timeout_usec);\n          if (error != Error::Success) {\n            if (error == Error::ConnectionTimeout) { quit = true; }\n            return false;\n          }\n        }\n\n        set_nonblocking(sock2, false);\n        set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,\n                            read_timeout_usec);\n        set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,\n                            write_timeout_usec);\n\n        error = Error::Success;\n        return true;\n      },\n      connection_timeout_sec); // Pass DNS timeout\n\n  if (sock != INVALID_SOCKET) {\n    error = Error::Success;\n  } else {\n    if (error == Error::Success) { error = Error::Connection; }\n  }\n\n  return sock;\n}\n\ninline bool get_ip_and_port(const struct sockaddr_storage &addr,\n                            socklen_t addr_len, std::string &ip, int &port) {\n  if (addr.ss_family == AF_INET) {\n    port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);\n  } else if (addr.ss_family == AF_INET6) {\n    port =\n        ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);\n  } else {\n    return false;\n  }\n\n  std::array<char, NI_MAXHOST> ipstr{};\n  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,\n                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,\n                  0, NI_NUMERICHOST)) {\n    return false;\n  }\n\n  ip = ipstr.data();\n  return true;\n}\n\ninline void get_local_ip_and_port(socket_t sock, std::string &ip, int &port) {\n  struct sockaddr_storage addr;\n  socklen_t addr_len = sizeof(addr);\n  if (!getsockname(sock, reinterpret_cast<struct sockaddr *>(&addr),\n                   &addr_len)) {\n    get_ip_and_port(addr, addr_len, ip, port);\n  }\n}\n\ninline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {\n  struct sockaddr_storage addr;\n  socklen_t addr_len = sizeof(addr);\n\n  if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),\n                   &addr_len)) {\n#ifndef _WIN32\n    if (addr.ss_family == AF_UNIX) {\n#if defined(__linux__)\n      struct ucred ucred;\n      socklen_t len = sizeof(ucred);\n      if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0) {\n        port = ucred.pid;\n      }\n#elif defined(SOL_LOCAL) && defined(SO_PEERPID)\n      pid_t pid;\n      socklen_t len = sizeof(pid);\n      if (getsockopt(sock, SOL_LOCAL, SO_PEERPID, &pid, &len) == 0) {\n        port = pid;\n      }\n#endif\n      return;\n    }\n#endif\n    get_ip_and_port(addr, addr_len, ip, port);\n  }\n}\n\ninline constexpr unsigned int str2tag_core(const char *s, size_t l,\n                                           unsigned int h) {\n  return (l == 0)\n             ? h\n             : str2tag_core(\n                   s + 1, l - 1,\n                   // Unsets the 6 high bits of h, therefore no overflow happens\n                   (((std::numeric_limits<unsigned int>::max)() >> 6) &\n                    h * 33) ^\n                       static_cast<unsigned char>(*s));\n}\n\ninline unsigned int str2tag(const std::string &s) {\n  return str2tag_core(s.data(), s.size(), 0);\n}\n\nnamespace udl {\n\ninline constexpr unsigned int operator\"\"_t(const char *s, size_t l) {\n  return str2tag_core(s, l, 0);\n}\n\n} // namespace udl\n\ninline std::string\nfind_content_type(const std::string &path,\n                  const std::map<std::string, std::string> &user_data,\n                  const std::string &default_content_type) {\n  auto ext = file_extension(path);\n\n  auto it = user_data.find(ext);\n  if (it != user_data.end()) { return it->second; }\n\n  using udl::operator\"\"_t;\n\n  switch (str2tag(ext)) {\n  default: return default_content_type;\n\n  case \"css\"_t: return \"text/css\";\n  case \"csv\"_t: return \"text/csv\";\n  case \"htm\"_t:\n  case \"html\"_t: return \"text/html\";\n  case \"js\"_t:\n  case \"mjs\"_t: return \"text/javascript\";\n  case \"txt\"_t: return \"text/plain\";\n  case \"vtt\"_t: return \"text/vtt\";\n\n  case \"apng\"_t: return \"image/apng\";\n  case \"avif\"_t: return \"image/avif\";\n  case \"bmp\"_t: return \"image/bmp\";\n  case \"gif\"_t: return \"image/gif\";\n  case \"png\"_t: return \"image/png\";\n  case \"svg\"_t: return \"image/svg+xml\";\n  case \"webp\"_t: return \"image/webp\";\n  case \"ico\"_t: return \"image/x-icon\";\n  case \"tif\"_t: return \"image/tiff\";\n  case \"tiff\"_t: return \"image/tiff\";\n  case \"jpg\"_t:\n  case \"jpeg\"_t: return \"image/jpeg\";\n\n  case \"mp4\"_t: return \"video/mp4\";\n  case \"mpeg\"_t: return \"video/mpeg\";\n  case \"webm\"_t: return \"video/webm\";\n\n  case \"mp3\"_t: return \"audio/mp3\";\n  case \"mpga\"_t: return \"audio/mpeg\";\n  case \"weba\"_t: return \"audio/webm\";\n  case \"wav\"_t: return \"audio/wave\";\n\n  case \"otf\"_t: return \"font/otf\";\n  case \"ttf\"_t: return \"font/ttf\";\n  case \"woff\"_t: return \"font/woff\";\n  case \"woff2\"_t: return \"font/woff2\";\n\n  case \"7z\"_t: return \"application/x-7z-compressed\";\n  case \"atom\"_t: return \"application/atom+xml\";\n  case \"pdf\"_t: return \"application/pdf\";\n  case \"json\"_t: return \"application/json\";\n  case \"rss\"_t: return \"application/rss+xml\";\n  case \"tar\"_t: return \"application/x-tar\";\n  case \"xht\"_t:\n  case \"xhtml\"_t: return \"application/xhtml+xml\";\n  case \"xslt\"_t: return \"application/xslt+xml\";\n  case \"xml\"_t: return \"application/xml\";\n  case \"gz\"_t: return \"application/gzip\";\n  case \"zip\"_t: return \"application/zip\";\n  case \"wasm\"_t: return \"application/wasm\";\n  }\n}\n\ninline bool can_compress_content_type(const std::string &content_type) {\n  using udl::operator\"\"_t;\n\n  auto tag = str2tag(content_type);\n\n  switch (tag) {\n  case \"image/svg+xml\"_t:\n  case \"application/javascript\"_t:\n  case \"application/json\"_t:\n  case \"application/xml\"_t:\n  case \"application/protobuf\"_t:\n  case \"application/xhtml+xml\"_t: return true;\n\n  case \"text/event-stream\"_t: return false;\n\n  default: return !content_type.rfind(\"text/\", 0);\n  }\n}\n\ninline EncodingType encoding_type(const Request &req, const Response &res) {\n  auto ret =\n      detail::can_compress_content_type(res.get_header_value(\"Content-Type\"));\n  if (!ret) { return EncodingType::None; }\n\n  const auto &s = req.get_header_value(\"Accept-Encoding\");\n  (void)(s);\n\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n  // TODO: 'Accept-Encoding' has br, not br;q=0\n  ret = s.find(\"br\") != std::string::npos;\n  if (ret) { return EncodingType::Brotli; }\n#endif\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n  // TODO: 'Accept-Encoding' has gzip, not gzip;q=0\n  ret = s.find(\"gzip\") != std::string::npos;\n  if (ret) { return EncodingType::Gzip; }\n#endif\n\n#ifdef CPPHTTPLIB_ZSTD_SUPPORT\n  // TODO: 'Accept-Encoding' has zstd, not zstd;q=0\n  ret = s.find(\"zstd\") != std::string::npos;\n  if (ret) { return EncodingType::Zstd; }\n#endif\n\n  return EncodingType::None;\n}\n\ninline bool nocompressor::compress(const char *data, size_t data_length,\n                                   bool /*last*/, Callback callback) {\n  if (!data_length) { return true; }\n  return callback(data, data_length);\n}\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\ninline gzip_compressor::gzip_compressor() {\n  std::memset(&strm_, 0, sizeof(strm_));\n  strm_.zalloc = Z_NULL;\n  strm_.zfree = Z_NULL;\n  strm_.opaque = Z_NULL;\n\n  is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,\n                           Z_DEFAULT_STRATEGY) == Z_OK;\n}\n\ninline gzip_compressor::~gzip_compressor() { deflateEnd(&strm_); }\n\ninline bool gzip_compressor::compress(const char *data, size_t data_length,\n                                      bool last, Callback callback) {\n  assert(is_valid_);\n\n  do {\n    constexpr size_t max_avail_in =\n        (std::numeric_limits<decltype(strm_.avail_in)>::max)();\n\n    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(\n        (std::min)(data_length, max_avail_in));\n    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));\n\n    data_length -= strm_.avail_in;\n    data += strm_.avail_in;\n\n    auto flush = (last && data_length == 0) ? Z_FINISH : Z_NO_FLUSH;\n    auto ret = Z_OK;\n\n    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n    do {\n      strm_.avail_out = static_cast<uInt>(buff.size());\n      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());\n\n      ret = deflate(&strm_, flush);\n      if (ret == Z_STREAM_ERROR) { return false; }\n\n      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {\n        return false;\n      }\n    } while (strm_.avail_out == 0);\n\n    assert((flush == Z_FINISH && ret == Z_STREAM_END) ||\n           (flush == Z_NO_FLUSH && ret == Z_OK));\n    assert(strm_.avail_in == 0);\n  } while (data_length > 0);\n\n  return true;\n}\n\ninline gzip_decompressor::gzip_decompressor() {\n  std::memset(&strm_, 0, sizeof(strm_));\n  strm_.zalloc = Z_NULL;\n  strm_.zfree = Z_NULL;\n  strm_.opaque = Z_NULL;\n\n  // 15 is the value of wbits, which should be at the maximum possible value\n  // to ensure that any gzip stream can be decoded. The offset of 32 specifies\n  // that the stream type should be automatically detected either gzip or\n  // deflate.\n  is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;\n}\n\ninline gzip_decompressor::~gzip_decompressor() { inflateEnd(&strm_); }\n\ninline bool gzip_decompressor::is_valid() const { return is_valid_; }\n\ninline bool gzip_decompressor::decompress(const char *data, size_t data_length,\n                                          Callback callback) {\n  assert(is_valid_);\n\n  auto ret = Z_OK;\n\n  do {\n    constexpr size_t max_avail_in =\n        (std::numeric_limits<decltype(strm_.avail_in)>::max)();\n\n    strm_.avail_in = static_cast<decltype(strm_.avail_in)>(\n        (std::min)(data_length, max_avail_in));\n    strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));\n\n    data_length -= strm_.avail_in;\n    data += strm_.avail_in;\n\n    std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n    while (strm_.avail_in > 0 && ret == Z_OK) {\n      strm_.avail_out = static_cast<uInt>(buff.size());\n      strm_.next_out = reinterpret_cast<Bytef *>(buff.data());\n\n      ret = inflate(&strm_, Z_NO_FLUSH);\n\n      assert(ret != Z_STREAM_ERROR);\n      switch (ret) {\n      case Z_NEED_DICT:\n      case Z_DATA_ERROR:\n      case Z_MEM_ERROR: inflateEnd(&strm_); return false;\n      }\n\n      if (!callback(buff.data(), buff.size() - strm_.avail_out)) {\n        return false;\n      }\n    }\n\n    if (ret != Z_OK && ret != Z_STREAM_END) { return false; }\n\n  } while (data_length > 0);\n\n  return true;\n}\n#endif\n\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\ninline brotli_compressor::brotli_compressor() {\n  state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);\n}\n\ninline brotli_compressor::~brotli_compressor() {\n  BrotliEncoderDestroyInstance(state_);\n}\n\ninline bool brotli_compressor::compress(const char *data, size_t data_length,\n                                        bool last, Callback callback) {\n  std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n\n  auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;\n  auto available_in = data_length;\n  auto next_in = reinterpret_cast<const uint8_t *>(data);\n\n  for (;;) {\n    if (last) {\n      if (BrotliEncoderIsFinished(state_)) { break; }\n    } else {\n      if (!available_in) { break; }\n    }\n\n    auto available_out = buff.size();\n    auto next_out = buff.data();\n\n    if (!BrotliEncoderCompressStream(state_, operation, &available_in, &next_in,\n                                     &available_out, &next_out, nullptr)) {\n      return false;\n    }\n\n    auto output_bytes = buff.size() - available_out;\n    if (output_bytes) {\n      callback(reinterpret_cast<const char *>(buff.data()), output_bytes);\n    }\n  }\n\n  return true;\n}\n\ninline brotli_decompressor::brotli_decompressor() {\n  decoder_s = BrotliDecoderCreateInstance(0, 0, 0);\n  decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT\n                        : BROTLI_DECODER_RESULT_ERROR;\n}\n\ninline brotli_decompressor::~brotli_decompressor() {\n  if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }\n}\n\ninline bool brotli_decompressor::is_valid() const { return decoder_s; }\n\ninline bool brotli_decompressor::decompress(const char *data,\n                                            size_t data_length,\n                                            Callback callback) {\n  if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||\n      decoder_r == BROTLI_DECODER_RESULT_ERROR) {\n    return 0;\n  }\n\n  auto next_in = reinterpret_cast<const uint8_t *>(data);\n  size_t avail_in = data_length;\n  size_t total_out;\n\n  decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;\n\n  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n  while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {\n    char *next_out = buff.data();\n    size_t avail_out = buff.size();\n\n    decoder_r = BrotliDecoderDecompressStream(\n        decoder_s, &avail_in, &next_in, &avail_out,\n        reinterpret_cast<uint8_t **>(&next_out), &total_out);\n\n    if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }\n\n    if (!callback(buff.data(), buff.size() - avail_out)) { return false; }\n  }\n\n  return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||\n         decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;\n}\n#endif\n\n#ifdef CPPHTTPLIB_ZSTD_SUPPORT\ninline zstd_compressor::zstd_compressor() {\n  ctx_ = ZSTD_createCCtx();\n  ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);\n}\n\ninline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }\n\ninline bool zstd_compressor::compress(const char *data, size_t data_length,\n                                      bool last, Callback callback) {\n  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n\n  ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;\n  ZSTD_inBuffer input = {data, data_length, 0};\n\n  bool finished;\n  do {\n    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};\n    size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);\n\n    if (ZSTD_isError(remaining)) { return false; }\n\n    if (!callback(buff.data(), output.pos)) { return false; }\n\n    finished = last ? (remaining == 0) : (input.pos == input.size);\n\n  } while (!finished);\n\n  return true;\n}\n\ninline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }\n\ninline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }\n\ninline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }\n\ninline bool zstd_decompressor::decompress(const char *data, size_t data_length,\n                                          Callback callback) {\n  std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};\n  ZSTD_inBuffer input = {data, data_length, 0};\n\n  while (input.pos < input.size) {\n    ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};\n    size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);\n\n    if (ZSTD_isError(remaining)) { return false; }\n\n    if (!callback(buff.data(), output.pos)) { return false; }\n  }\n\n  return true;\n}\n#endif\n\ninline bool is_prohibited_header_name(const std::string &name) {\n  using udl::operator\"\"_t;\n\n  switch (str2tag(name)) {\n  case \"REMOTE_ADDR\"_t:\n  case \"REMOTE_PORT\"_t:\n  case \"LOCAL_ADDR\"_t:\n  case \"LOCAL_PORT\"_t: return true;\n  default: return false;\n  }\n}\n\ninline bool has_header(const Headers &headers, const std::string &key) {\n  if (is_prohibited_header_name(key)) { return false; }\n  return headers.find(key) != headers.end();\n}\n\ninline const char *get_header_value(const Headers &headers,\n                                    const std::string &key, const char *def,\n                                    size_t id) {\n  if (is_prohibited_header_name(key)) {\n#ifndef CPPHTTPLIB_NO_EXCEPTIONS\n    std::string msg = \"Prohibited header name '\" + key + \"' is specified.\";\n    throw std::invalid_argument(msg);\n#else\n    return \"\";\n#endif\n  }\n\n  auto rng = headers.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) { return it->second.c_str(); }\n  return def;\n}\n\ntemplate <typename T>\ninline bool parse_header(const char *beg, const char *end, T fn) {\n  // Skip trailing spaces and tabs.\n  while (beg < end && is_space_or_tab(end[-1])) {\n    end--;\n  }\n\n  auto p = beg;\n  while (p < end && *p != ':') {\n    p++;\n  }\n\n  auto name = std::string(beg, p);\n  if (!detail::fields::is_field_name(name)) { return false; }\n\n  if (p == end) { return false; }\n\n  auto key_end = p;\n\n  if (*p++ != ':') { return false; }\n\n  while (p < end && is_space_or_tab(*p)) {\n    p++;\n  }\n\n  if (p <= end) {\n    auto key_len = key_end - beg;\n    if (!key_len) { return false; }\n\n    auto key = std::string(beg, key_end);\n    auto val = std::string(p, end);\n\n    if (!detail::fields::is_field_value(val)) { return false; }\n\n    if (case_ignore::equal(key, \"Location\") ||\n        case_ignore::equal(key, \"Referer\")) {\n      fn(key, val);\n    } else {\n      fn(key, decode_path_component(val));\n    }\n\n    return true;\n  }\n\n  return false;\n}\n\ninline bool read_headers(Stream &strm, Headers &headers) {\n  const auto bufsiz = 2048;\n  char buf[bufsiz];\n  stream_line_reader line_reader(strm, buf, bufsiz);\n\n  size_t header_count = 0;\n\n  for (;;) {\n    if (!line_reader.getline()) { return false; }\n\n    // Check if the line ends with CRLF.\n    auto line_terminator_len = 2;\n    if (line_reader.end_with_crlf()) {\n      // Blank line indicates end of headers.\n      if (line_reader.size() == 2) { break; }\n    } else {\n#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR\n      // Blank line indicates end of headers.\n      if (line_reader.size() == 1) { break; }\n      line_terminator_len = 1;\n#else\n      continue; // Skip invalid line.\n#endif\n    }\n\n    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }\n\n    // Check header count limit\n    if (header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) { return false; }\n\n    // Exclude line terminator\n    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;\n\n    if (!parse_header(line_reader.ptr(), end,\n                      [&](const std::string &key, const std::string &val) {\n                        headers.emplace(key, val);\n                      })) {\n      return false;\n    }\n\n    header_count++;\n  }\n\n  return true;\n}\n\ninline bool read_content_with_length(Stream &strm, size_t len,\n                                     DownloadProgress progress,\n                                     ContentReceiverWithProgress out) {\n  char buf[CPPHTTPLIB_RECV_BUFSIZ];\n\n  size_t r = 0;\n  while (r < len) {\n    auto read_len = static_cast<size_t>(len - r);\n    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));\n    if (n <= 0) { return false; }\n\n    if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }\n    r += static_cast<size_t>(n);\n\n    if (progress) {\n      if (!progress(r, len)) { return false; }\n    }\n  }\n\n  return true;\n}\n\ninline void skip_content_with_length(Stream &strm, size_t len) {\n  char buf[CPPHTTPLIB_RECV_BUFSIZ];\n  size_t r = 0;\n  while (r < len) {\n    auto read_len = static_cast<size_t>(len - r);\n    auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));\n    if (n <= 0) { return; }\n    r += static_cast<size_t>(n);\n  }\n}\n\nenum class ReadContentResult {\n  Success,         // Successfully read the content\n  PayloadTooLarge, // The content exceeds the specified payload limit\n  Error            // An error occurred while reading the content\n};\n\ninline ReadContentResult\nread_content_without_length(Stream &strm, size_t payload_max_length,\n                            ContentReceiverWithProgress out) {\n  char buf[CPPHTTPLIB_RECV_BUFSIZ];\n  size_t r = 0;\n  for (;;) {\n    auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);\n    if (n == 0) { return ReadContentResult::Success; }\n    if (n < 0) { return ReadContentResult::Error; }\n\n    // Check if adding this data would exceed the payload limit\n    if (r > payload_max_length ||\n        payload_max_length - r < static_cast<size_t>(n)) {\n      return ReadContentResult::PayloadTooLarge;\n    }\n\n    if (!out(buf, static_cast<size_t>(n), r, 0)) {\n      return ReadContentResult::Error;\n    }\n    r += static_cast<size_t>(n);\n  }\n\n  return ReadContentResult::Success;\n}\n\ntemplate <typename T>\ninline ReadContentResult read_content_chunked(Stream &strm, T &x,\n                                              size_t payload_max_length,\n                                              ContentReceiverWithProgress out) {\n  const auto bufsiz = 16;\n  char buf[bufsiz];\n\n  stream_line_reader line_reader(strm, buf, bufsiz);\n\n  if (!line_reader.getline()) { return ReadContentResult::Error; }\n\n  unsigned long chunk_len;\n  size_t total_len = 0;\n  while (true) {\n    char *end_ptr;\n\n    chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);\n\n    if (end_ptr == line_reader.ptr()) { return ReadContentResult::Error; }\n    if (chunk_len == ULONG_MAX) { return ReadContentResult::Error; }\n\n    if (chunk_len == 0) { break; }\n\n    // Check if adding this chunk would exceed the payload limit\n    if (total_len > payload_max_length ||\n        payload_max_length - total_len < chunk_len) {\n      return ReadContentResult::PayloadTooLarge;\n    }\n\n    total_len += chunk_len;\n\n    if (!read_content_with_length(strm, chunk_len, nullptr, out)) {\n      return ReadContentResult::Error;\n    }\n\n    if (!line_reader.getline()) { return ReadContentResult::Error; }\n\n    if (strcmp(line_reader.ptr(), \"\\r\\n\") != 0) {\n      return ReadContentResult::Error;\n    }\n\n    if (!line_reader.getline()) { return ReadContentResult::Error; }\n  }\n\n  assert(chunk_len == 0);\n\n  // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions \"The chunked\n  // transfer coding is complete when a chunk with a chunk-size of zero is\n  // received, possibly followed by a trailer section, and finally terminated by\n  // an empty line\". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1\n  //\n  // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section\n  // does't care for the existence of the final CRLF. In other words, it seems\n  // to be ok whether the final CRLF exists or not in the chunked data.\n  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3\n  //\n  // According to the reference code in RFC 9112, cpp-httplib now allows\n  // chunked transfer coding data without the final CRLF.\n  if (!line_reader.getline()) { return ReadContentResult::Success; }\n\n  // RFC 7230 Section 4.1.2 - Headers prohibited in trailers\n  thread_local case_ignore::unordered_set<std::string> prohibited_trailers = {\n      // Message framing\n      \"transfer-encoding\", \"content-length\",\n\n      // Routing\n      \"host\",\n\n      // Authentication\n      \"authorization\", \"www-authenticate\", \"proxy-authenticate\",\n      \"proxy-authorization\", \"cookie\", \"set-cookie\",\n\n      // Request modifiers\n      \"cache-control\", \"expect\", \"max-forwards\", \"pragma\", \"range\", \"te\",\n\n      // Response control\n      \"age\", \"expires\", \"date\", \"location\", \"retry-after\", \"vary\", \"warning\",\n\n      // Payload processing\n      \"content-encoding\", \"content-type\", \"content-range\", \"trailer\"};\n\n  // Parse declared trailer headers once for performance\n  case_ignore::unordered_set<std::string> declared_trailers;\n  if (has_header(x.headers, \"Trailer\")) {\n    auto trailer_header = get_header_value(x.headers, \"Trailer\", \"\", 0);\n    auto len = std::strlen(trailer_header);\n\n    split(trailer_header, trailer_header + len, ',',\n          [&](const char *b, const char *e) {\n            std::string key(b, e);\n            if (prohibited_trailers.find(key) == prohibited_trailers.end()) {\n              declared_trailers.insert(key);\n            }\n          });\n  }\n\n  size_t trailer_header_count = 0;\n  while (strcmp(line_reader.ptr(), \"\\r\\n\") != 0) {\n    if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) {\n      return ReadContentResult::Error;\n    }\n\n    // Check trailer header count limit\n    if (trailer_header_count >= CPPHTTPLIB_HEADER_MAX_COUNT) {\n      return ReadContentResult::Error;\n    }\n\n    // Exclude line terminator\n    constexpr auto line_terminator_len = 2;\n    auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;\n\n    parse_header(line_reader.ptr(), end,\n                 [&](const std::string &key, const std::string &val) {\n                   if (declared_trailers.find(key) != declared_trailers.end()) {\n                     x.trailers.emplace(key, val);\n                     trailer_header_count++;\n                   }\n                 });\n\n    if (!line_reader.getline()) { return ReadContentResult::Error; }\n  }\n\n  return ReadContentResult::Success;\n}\n\ninline bool is_chunked_transfer_encoding(const Headers &headers) {\n  return case_ignore::equal(\n      get_header_value(headers, \"Transfer-Encoding\", \"\", 0), \"chunked\");\n}\n\ntemplate <typename T, typename U>\nbool prepare_content_receiver(T &x, int &status,\n                              ContentReceiverWithProgress receiver,\n                              bool decompress, U callback) {\n  if (decompress) {\n    std::string encoding = x.get_header_value(\"Content-Encoding\");\n    std::unique_ptr<decompressor> decompressor;\n\n    if (encoding == \"gzip\" || encoding == \"deflate\") {\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n      decompressor = detail::make_unique<gzip_decompressor>();\n#else\n      status = StatusCode::UnsupportedMediaType_415;\n      return false;\n#endif\n    } else if (encoding.find(\"br\") != std::string::npos) {\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n      decompressor = detail::make_unique<brotli_decompressor>();\n#else\n      status = StatusCode::UnsupportedMediaType_415;\n      return false;\n#endif\n    } else if (encoding == \"zstd\") {\n#ifdef CPPHTTPLIB_ZSTD_SUPPORT\n      decompressor = detail::make_unique<zstd_decompressor>();\n#else\n      status = StatusCode::UnsupportedMediaType_415;\n      return false;\n#endif\n    }\n\n    if (decompressor) {\n      if (decompressor->is_valid()) {\n        ContentReceiverWithProgress out = [&](const char *buf, size_t n,\n                                              size_t off, size_t len) {\n          return decompressor->decompress(buf, n,\n                                          [&](const char *buf2, size_t n2) {\n                                            return receiver(buf2, n2, off, len);\n                                          });\n        };\n        return callback(std::move(out));\n      } else {\n        status = StatusCode::InternalServerError_500;\n        return false;\n      }\n    }\n  }\n\n  ContentReceiverWithProgress out = [&](const char *buf, size_t n, size_t off,\n                                        size_t len) {\n    return receiver(buf, n, off, len);\n  };\n  return callback(std::move(out));\n}\n\ntemplate <typename T>\nbool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,\n                  DownloadProgress progress,\n                  ContentReceiverWithProgress receiver, bool decompress) {\n  return prepare_content_receiver(\n      x, status, std::move(receiver), decompress,\n      [&](const ContentReceiverWithProgress &out) {\n        auto ret = true;\n        auto exceed_payload_max_length = false;\n\n        if (is_chunked_transfer_encoding(x.headers)) {\n          auto result = read_content_chunked(strm, x, payload_max_length, out);\n          if (result == ReadContentResult::Success) {\n            ret = true;\n          } else if (result == ReadContentResult::PayloadTooLarge) {\n            exceed_payload_max_length = true;\n            ret = false;\n          } else {\n            ret = false;\n          }\n        } else if (!has_header(x.headers, \"Content-Length\")) {\n          auto result =\n              read_content_without_length(strm, payload_max_length, out);\n          if (result == ReadContentResult::Success) {\n            ret = true;\n          } else if (result == ReadContentResult::PayloadTooLarge) {\n            exceed_payload_max_length = true;\n            ret = false;\n          } else {\n            ret = false;\n          }\n        } else {\n          auto is_invalid_value = false;\n          auto len = get_header_value_u64(x.headers, \"Content-Length\",\n                                          (std::numeric_limits<size_t>::max)(),\n                                          0, is_invalid_value);\n\n          if (is_invalid_value) {\n            ret = false;\n          } else if (len > payload_max_length) {\n            exceed_payload_max_length = true;\n            skip_content_with_length(strm, len);\n            ret = false;\n          } else if (len > 0) {\n            ret = read_content_with_length(strm, len, std::move(progress), out);\n          }\n        }\n\n        if (!ret) {\n          status = exceed_payload_max_length ? StatusCode::PayloadTooLarge_413\n                                             : StatusCode::BadRequest_400;\n        }\n        return ret;\n      });\n}\n\ninline ssize_t write_request_line(Stream &strm, const std::string &method,\n                                  const std::string &path) {\n  std::string s = method;\n  s += \" \";\n  s += path;\n  s += \" HTTP/1.1\\r\\n\";\n  return strm.write(s.data(), s.size());\n}\n\ninline ssize_t write_response_line(Stream &strm, int status) {\n  std::string s = \"HTTP/1.1 \";\n  s += std::to_string(status);\n  s += \" \";\n  s += httplib::status_message(status);\n  s += \"\\r\\n\";\n  return strm.write(s.data(), s.size());\n}\n\ninline ssize_t write_headers(Stream &strm, const Headers &headers) {\n  ssize_t write_len = 0;\n  for (const auto &x : headers) {\n    std::string s;\n    s = x.first;\n    s += \": \";\n    s += x.second;\n    s += \"\\r\\n\";\n\n    auto len = strm.write(s.data(), s.size());\n    if (len < 0) { return len; }\n    write_len += len;\n  }\n  auto len = strm.write(\"\\r\\n\");\n  if (len < 0) { return len; }\n  write_len += len;\n  return write_len;\n}\n\ninline bool write_data(Stream &strm, const char *d, size_t l) {\n  size_t offset = 0;\n  while (offset < l) {\n    auto length = strm.write(d + offset, l - offset);\n    if (length < 0) { return false; }\n    offset += static_cast<size_t>(length);\n  }\n  return true;\n}\n\ntemplate <typename T>\ninline bool write_content_with_progress(Stream &strm,\n                                        const ContentProvider &content_provider,\n                                        size_t offset, size_t length,\n                                        T is_shutting_down,\n                                        const UploadProgress &upload_progress,\n                                        Error &error) {\n  size_t end_offset = offset + length;\n  size_t start_offset = offset;\n  auto ok = true;\n  DataSink data_sink;\n\n  data_sink.write = [&](const char *d, size_t l) -> bool {\n    if (ok) {\n      if (write_data(strm, d, l)) {\n        offset += l;\n\n        if (upload_progress && length > 0) {\n          size_t current_written = offset - start_offset;\n          if (!upload_progress(current_written, length)) {\n            ok = false;\n            return false;\n          }\n        }\n      } else {\n        ok = false;\n      }\n    }\n    return ok;\n  };\n\n  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };\n\n  while (offset < end_offset && !is_shutting_down()) {\n    if (!strm.wait_writable()) {\n      error = Error::Write;\n      return false;\n    } else if (!content_provider(offset, end_offset - offset, data_sink)) {\n      error = Error::Canceled;\n      return false;\n    } else if (!ok) {\n      error = Error::Write;\n      return false;\n    }\n  }\n\n  error = Error::Success;\n  return true;\n}\n\ntemplate <typename T>\ninline bool write_content(Stream &strm, const ContentProvider &content_provider,\n                          size_t offset, size_t length, T is_shutting_down,\n                          Error &error) {\n  return write_content_with_progress<T>(strm, content_provider, offset, length,\n                                        is_shutting_down, nullptr, error);\n}\n\ntemplate <typename T>\ninline bool write_content(Stream &strm, const ContentProvider &content_provider,\n                          size_t offset, size_t length,\n                          const T &is_shutting_down) {\n  auto error = Error::Success;\n  return write_content(strm, content_provider, offset, length, is_shutting_down,\n                       error);\n}\n\ntemplate <typename T>\ninline bool\nwrite_content_without_length(Stream &strm,\n                             const ContentProvider &content_provider,\n                             const T &is_shutting_down) {\n  size_t offset = 0;\n  auto data_available = true;\n  auto ok = true;\n  DataSink data_sink;\n\n  data_sink.write = [&](const char *d, size_t l) -> bool {\n    if (ok) {\n      offset += l;\n      if (!write_data(strm, d, l)) { ok = false; }\n    }\n    return ok;\n  };\n\n  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };\n\n  data_sink.done = [&](void) { data_available = false; };\n\n  while (data_available && !is_shutting_down()) {\n    if (!strm.wait_writable()) {\n      return false;\n    } else if (!content_provider(offset, 0, data_sink)) {\n      return false;\n    } else if (!ok) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename T, typename U>\ninline bool\nwrite_content_chunked(Stream &strm, const ContentProvider &content_provider,\n                      const T &is_shutting_down, U &compressor, Error &error) {\n  size_t offset = 0;\n  auto data_available = true;\n  auto ok = true;\n  DataSink data_sink;\n\n  data_sink.write = [&](const char *d, size_t l) -> bool {\n    if (ok) {\n      data_available = l > 0;\n      offset += l;\n\n      std::string payload;\n      if (compressor.compress(d, l, false,\n                              [&](const char *data, size_t data_len) {\n                                payload.append(data, data_len);\n                                return true;\n                              })) {\n        if (!payload.empty()) {\n          // Emit chunked response header and footer for each chunk\n          auto chunk =\n              from_i_to_hex(payload.size()) + \"\\r\\n\" + payload + \"\\r\\n\";\n          if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }\n        }\n      } else {\n        ok = false;\n      }\n    }\n    return ok;\n  };\n\n  data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };\n\n  auto done_with_trailer = [&](const Headers *trailer) {\n    if (!ok) { return; }\n\n    data_available = false;\n\n    std::string payload;\n    if (!compressor.compress(nullptr, 0, true,\n                             [&](const char *data, size_t data_len) {\n                               payload.append(data, data_len);\n                               return true;\n                             })) {\n      ok = false;\n      return;\n    }\n\n    if (!payload.empty()) {\n      // Emit chunked response header and footer for each chunk\n      auto chunk = from_i_to_hex(payload.size()) + \"\\r\\n\" + payload + \"\\r\\n\";\n      if (!write_data(strm, chunk.data(), chunk.size())) {\n        ok = false;\n        return;\n      }\n    }\n\n    constexpr const char done_marker[] = \"0\\r\\n\";\n    if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }\n\n    // Trailer\n    if (trailer) {\n      for (const auto &kv : *trailer) {\n        std::string field_line = kv.first + \": \" + kv.second + \"\\r\\n\";\n        if (!write_data(strm, field_line.data(), field_line.size())) {\n          ok = false;\n        }\n      }\n    }\n\n    constexpr const char crlf[] = \"\\r\\n\";\n    if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }\n  };\n\n  data_sink.done = [&](void) { done_with_trailer(nullptr); };\n\n  data_sink.done_with_trailer = [&](const Headers &trailer) {\n    done_with_trailer(&trailer);\n  };\n\n  while (data_available && !is_shutting_down()) {\n    if (!strm.wait_writable()) {\n      error = Error::Write;\n      return false;\n    } else if (!content_provider(offset, 0, data_sink)) {\n      error = Error::Canceled;\n      return false;\n    } else if (!ok) {\n      error = Error::Write;\n      return false;\n    }\n  }\n\n  error = Error::Success;\n  return true;\n}\n\ntemplate <typename T, typename U>\ninline bool write_content_chunked(Stream &strm,\n                                  const ContentProvider &content_provider,\n                                  const T &is_shutting_down, U &compressor) {\n  auto error = Error::Success;\n  return write_content_chunked(strm, content_provider, is_shutting_down,\n                               compressor, error);\n}\n\ntemplate <typename T>\ninline bool redirect(T &cli, Request &req, Response &res,\n                     const std::string &path, const std::string &location,\n                     Error &error) {\n  Request new_req = req;\n  new_req.path = path;\n  new_req.redirect_count_ -= 1;\n\n  if (res.status == StatusCode::SeeOther_303 &&\n      (req.method != \"GET\" && req.method != \"HEAD\")) {\n    new_req.method = \"GET\";\n    new_req.body.clear();\n    new_req.headers.clear();\n  }\n\n  Response new_res;\n\n  auto ret = cli.send(new_req, new_res, error);\n  if (ret) {\n    req = new_req;\n    res = new_res;\n\n    if (res.location.empty()) { res.location = location; }\n  }\n  return ret;\n}\n\ninline std::string params_to_query_str(const Params &params) {\n  std::string query;\n\n  for (auto it = params.begin(); it != params.end(); ++it) {\n    if (it != params.begin()) { query += \"&\"; }\n    query += encode_query_component(it->first);\n    query += \"=\";\n    query += encode_query_component(it->second);\n  }\n  return query;\n}\n\ninline void parse_query_text(const char *data, std::size_t size,\n                             Params &params) {\n  std::set<std::string> cache;\n  split(data, data + size, '&', [&](const char *b, const char *e) {\n    std::string kv(b, e);\n    if (cache.find(kv) != cache.end()) { return; }\n    cache.insert(std::move(kv));\n\n    std::string key;\n    std::string val;\n    divide(b, static_cast<std::size_t>(e - b), '=',\n           [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,\n               std::size_t rhs_size) {\n             key.assign(lhs_data, lhs_size);\n             val.assign(rhs_data, rhs_size);\n           });\n\n    if (!key.empty()) {\n      params.emplace(decode_query_component(key), decode_query_component(val));\n    }\n  });\n}\n\ninline void parse_query_text(const std::string &s, Params &params) {\n  parse_query_text(s.data(), s.size(), params);\n}\n\ninline bool parse_multipart_boundary(const std::string &content_type,\n                                     std::string &boundary) {\n  auto boundary_keyword = \"boundary=\";\n  auto pos = content_type.find(boundary_keyword);\n  if (pos == std::string::npos) { return false; }\n  auto end = content_type.find(';', pos);\n  auto beg = pos + strlen(boundary_keyword);\n  boundary = trim_double_quotes_copy(content_type.substr(beg, end - beg));\n  return !boundary.empty();\n}\n\ninline void parse_disposition_params(const std::string &s, Params &params) {\n  std::set<std::string> cache;\n  split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {\n    std::string kv(b, e);\n    if (cache.find(kv) != cache.end()) { return; }\n    cache.insert(kv);\n\n    std::string key;\n    std::string val;\n    split(b, e, '=', [&](const char *b2, const char *e2) {\n      if (key.empty()) {\n        key.assign(b2, e2);\n      } else {\n        val.assign(b2, e2);\n      }\n    });\n\n    if (!key.empty()) {\n      params.emplace(trim_double_quotes_copy((key)),\n                     trim_double_quotes_copy((val)));\n    }\n  });\n}\n\n#ifdef CPPHTTPLIB_NO_EXCEPTIONS\ninline bool parse_range_header(const std::string &s, Ranges &ranges) {\n#else\ninline bool parse_range_header(const std::string &s, Ranges &ranges) try {\n#endif\n  auto is_valid = [](const std::string &str) {\n    return std::all_of(str.cbegin(), str.cend(),\n                       [](unsigned char c) { return std::isdigit(c); });\n  };\n\n  if (s.size() > 7 && s.compare(0, 6, \"bytes=\") == 0) {\n    const auto pos = static_cast<size_t>(6);\n    const auto len = static_cast<size_t>(s.size() - 6);\n    auto all_valid_ranges = true;\n    split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {\n      if (!all_valid_ranges) { return; }\n\n      const auto it = std::find(b, e, '-');\n      if (it == e) {\n        all_valid_ranges = false;\n        return;\n      }\n\n      const auto lhs = std::string(b, it);\n      const auto rhs = std::string(it + 1, e);\n      if (!is_valid(lhs) || !is_valid(rhs)) {\n        all_valid_ranges = false;\n        return;\n      }\n\n      const auto first =\n          static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));\n      const auto last =\n          static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));\n      if ((first == -1 && last == -1) ||\n          (first != -1 && last != -1 && first > last)) {\n        all_valid_ranges = false;\n        return;\n      }\n\n      ranges.emplace_back(first, last);\n    });\n    return all_valid_ranges && !ranges.empty();\n  }\n  return false;\n#ifdef CPPHTTPLIB_NO_EXCEPTIONS\n}\n#else\n} catch (...) { return false; }\n#endif\n\ninline bool parse_accept_header(const std::string &s,\n                                std::vector<std::string> &content_types) {\n  content_types.clear();\n\n  // Empty string is considered valid (no preference)\n  if (s.empty()) { return true; }\n\n  // Check for invalid patterns: leading/trailing commas or consecutive commas\n  if (s.front() == ',' || s.back() == ',' ||\n      s.find(\",,\") != std::string::npos) {\n    return false;\n  }\n\n  struct AcceptEntry {\n    std::string media_type;\n    double quality;\n    int order; // Original order in header\n  };\n\n  std::vector<AcceptEntry> entries;\n  int order = 0;\n  bool has_invalid_entry = false;\n\n  // Split by comma and parse each entry\n  split(s.data(), s.data() + s.size(), ',', [&](const char *b, const char *e) {\n    std::string entry(b, e);\n    entry = trim_copy(entry);\n\n    if (entry.empty()) {\n      has_invalid_entry = true;\n      return;\n    }\n\n    AcceptEntry accept_entry;\n    accept_entry.quality = 1.0; // Default quality\n    accept_entry.order = order++;\n\n    // Find q= parameter\n    auto q_pos = entry.find(\";q=\");\n    if (q_pos == std::string::npos) { q_pos = entry.find(\"; q=\"); }\n\n    if (q_pos != std::string::npos) {\n      // Extract media type (before q parameter)\n      accept_entry.media_type = trim_copy(entry.substr(0, q_pos));\n\n      // Extract quality value\n      auto q_start = entry.find('=', q_pos) + 1;\n      auto q_end = entry.find(';', q_start);\n      if (q_end == std::string::npos) { q_end = entry.length(); }\n\n      std::string quality_str =\n          trim_copy(entry.substr(q_start, q_end - q_start));\n      if (quality_str.empty()) {\n        has_invalid_entry = true;\n        return;\n      }\n\n#ifdef CPPHTTPLIB_NO_EXCEPTIONS\n      {\n        std::istringstream iss(quality_str);\n        iss >> accept_entry.quality;\n\n        // Check if conversion was successful and entire string was consumed\n        if (iss.fail() || !iss.eof()) {\n          has_invalid_entry = true;\n          return;\n        }\n      }\n#else\n      try {\n        accept_entry.quality = std::stod(quality_str);\n      } catch (...) {\n        has_invalid_entry = true;\n        return;\n      }\n#endif\n      // Check if quality is in valid range [0.0, 1.0]\n      if (accept_entry.quality < 0.0 || accept_entry.quality > 1.0) {\n        has_invalid_entry = true;\n        return;\n      }\n    } else {\n      // No quality parameter, use entire entry as media type\n      accept_entry.media_type = entry;\n    }\n\n    // Remove additional parameters from media type\n    auto param_pos = accept_entry.media_type.find(';');\n    if (param_pos != std::string::npos) {\n      accept_entry.media_type =\n          trim_copy(accept_entry.media_type.substr(0, param_pos));\n    }\n\n    // Basic validation of media type format\n    if (accept_entry.media_type.empty()) {\n      has_invalid_entry = true;\n      return;\n    }\n\n    // Check for basic media type format (should contain '/' or be '*')\n    if (accept_entry.media_type != \"*\" &&\n        accept_entry.media_type.find('/') == std::string::npos) {\n      has_invalid_entry = true;\n      return;\n    }\n\n    entries.push_back(accept_entry);\n  });\n\n  // Return false if any invalid entry was found\n  if (has_invalid_entry) { return false; }\n\n  // Sort by quality (descending), then by original order (ascending)\n  std::sort(entries.begin(), entries.end(),\n            [](const AcceptEntry &a, const AcceptEntry &b) {\n              if (a.quality != b.quality) {\n                return a.quality > b.quality; // Higher quality first\n              }\n              return a.order < b.order; // Earlier order first for same quality\n            });\n\n  // Extract sorted media types\n  content_types.reserve(entries.size());\n  for (const auto &entry : entries) {\n    content_types.push_back(entry.media_type);\n  }\n\n  return true;\n}\n\nclass FormDataParser {\npublic:\n  FormDataParser() = default;\n\n  void set_boundary(std::string &&boundary) {\n    boundary_ = boundary;\n    dash_boundary_crlf_ = dash_ + boundary_ + crlf_;\n    crlf_dash_boundary_ = crlf_ + dash_ + boundary_;\n  }\n\n  bool is_valid() const { return is_valid_; }\n\n  bool parse(const char *buf, size_t n, const FormDataHeader &header_callback,\n             const ContentReceiver &content_callback) {\n\n    buf_append(buf, n);\n\n    while (buf_size() > 0) {\n      switch (state_) {\n      case 0: { // Initial boundary\n        auto pos = buf_find(dash_boundary_crlf_);\n        if (pos == buf_size()) { return true; }\n        buf_erase(pos + dash_boundary_crlf_.size());\n        state_ = 1;\n        break;\n      }\n      case 1: { // New entry\n        clear_file_info();\n        state_ = 2;\n        break;\n      }\n      case 2: { // Headers\n        auto pos = buf_find(crlf_);\n        if (pos > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }\n        while (pos < buf_size()) {\n          // Empty line\n          if (pos == 0) {\n            if (!header_callback(file_)) {\n              is_valid_ = false;\n              return false;\n            }\n            buf_erase(crlf_.size());\n            state_ = 3;\n            break;\n          }\n\n          const auto header = buf_head(pos);\n\n          if (!parse_header(header.data(), header.data() + header.size(),\n                            [&](const std::string &, const std::string &) {})) {\n            is_valid_ = false;\n            return false;\n          }\n\n          // Parse and emplace space trimmed headers into a map\n          if (!parse_header(\n                  header.data(), header.data() + header.size(),\n                  [&](const std::string &key, const std::string &val) {\n                    file_.headers.emplace(key, val);\n                  })) {\n            is_valid_ = false;\n            return false;\n          }\n\n          constexpr const char header_content_type[] = \"Content-Type:\";\n\n          if (start_with_case_ignore(header, header_content_type)) {\n            file_.content_type =\n                trim_copy(header.substr(str_len(header_content_type)));\n          } else {\n            thread_local const std::regex re_content_disposition(\n                R\"~(^Content-Disposition:\\s*form-data;\\s*(.*)$)~\",\n                std::regex_constants::icase);\n\n            std::smatch m;\n            if (std::regex_match(header, m, re_content_disposition)) {\n              Params params;\n              parse_disposition_params(m[1], params);\n\n              auto it = params.find(\"name\");\n              if (it != params.end()) {\n                file_.name = it->second;\n              } else {\n                is_valid_ = false;\n                return false;\n              }\n\n              it = params.find(\"filename\");\n              if (it != params.end()) { file_.filename = it->second; }\n\n              it = params.find(\"filename*\");\n              if (it != params.end()) {\n                // Only allow UTF-8 encoding...\n                thread_local const std::regex re_rfc5987_encoding(\n                    R\"~(^UTF-8''(.+?)$)~\", std::regex_constants::icase);\n\n                std::smatch m2;\n                if (std::regex_match(it->second, m2, re_rfc5987_encoding)) {\n                  file_.filename = decode_path_component(m2[1]); // override...\n                } else {\n                  is_valid_ = false;\n                  return false;\n                }\n              }\n            }\n          }\n          buf_erase(pos + crlf_.size());\n          pos = buf_find(crlf_);\n        }\n        if (state_ != 3) { return true; }\n        break;\n      }\n      case 3: { // Body\n        if (crlf_dash_boundary_.size() > buf_size()) { return true; }\n        auto pos = buf_find(crlf_dash_boundary_);\n        if (pos < buf_size()) {\n          if (!content_callback(buf_data(), pos)) {\n            is_valid_ = false;\n            return false;\n          }\n          buf_erase(pos + crlf_dash_boundary_.size());\n          state_ = 4;\n        } else {\n          auto len = buf_size() - crlf_dash_boundary_.size();\n          if (len > 0) {\n            if (!content_callback(buf_data(), len)) {\n              is_valid_ = false;\n              return false;\n            }\n            buf_erase(len);\n          }\n          return true;\n        }\n        break;\n      }\n      case 4: { // Boundary\n        if (crlf_.size() > buf_size()) { return true; }\n        if (buf_start_with(crlf_)) {\n          buf_erase(crlf_.size());\n          state_ = 1;\n        } else {\n          if (dash_.size() > buf_size()) { return true; }\n          if (buf_start_with(dash_)) {\n            buf_erase(dash_.size());\n            is_valid_ = true;\n            buf_erase(buf_size()); // Remove epilogue\n          } else {\n            return true;\n          }\n        }\n        break;\n      }\n      }\n    }\n\n    return true;\n  }\n\nprivate:\n  void clear_file_info() {\n    file_.name.clear();\n    file_.filename.clear();\n    file_.content_type.clear();\n    file_.headers.clear();\n  }\n\n  bool start_with_case_ignore(const std::string &a, const char *b) const {\n    const auto b_len = strlen(b);\n    if (a.size() < b_len) { return false; }\n    for (size_t i = 0; i < b_len; i++) {\n      if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  const std::string dash_ = \"--\";\n  const std::string crlf_ = \"\\r\\n\";\n  std::string boundary_;\n  std::string dash_boundary_crlf_;\n  std::string crlf_dash_boundary_;\n\n  size_t state_ = 0;\n  bool is_valid_ = false;\n  FormData file_;\n\n  // Buffer\n  bool start_with(const std::string &a, size_t spos, size_t epos,\n                  const std::string &b) const {\n    if (epos - spos < b.size()) { return false; }\n    for (size_t i = 0; i < b.size(); i++) {\n      if (a[i + spos] != b[i]) { return false; }\n    }\n    return true;\n  }\n\n  size_t buf_size() const { return buf_epos_ - buf_spos_; }\n\n  const char *buf_data() const { return &buf_[buf_spos_]; }\n\n  std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }\n\n  bool buf_start_with(const std::string &s) const {\n    return start_with(buf_, buf_spos_, buf_epos_, s);\n  }\n\n  size_t buf_find(const std::string &s) const {\n    auto c = s.front();\n\n    size_t off = buf_spos_;\n    while (off < buf_epos_) {\n      auto pos = off;\n      while (true) {\n        if (pos == buf_epos_) { return buf_size(); }\n        if (buf_[pos] == c) { break; }\n        pos++;\n      }\n\n      auto remaining_size = buf_epos_ - pos;\n      if (s.size() > remaining_size) { return buf_size(); }\n\n      if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }\n\n      off = pos + 1;\n    }\n\n    return buf_size();\n  }\n\n  void buf_append(const char *data, size_t n) {\n    auto remaining_size = buf_size();\n    if (remaining_size > 0 && buf_spos_ > 0) {\n      for (size_t i = 0; i < remaining_size; i++) {\n        buf_[i] = buf_[buf_spos_ + i];\n      }\n    }\n    buf_spos_ = 0;\n    buf_epos_ = remaining_size;\n\n    if (remaining_size + n > buf_.size()) { buf_.resize(remaining_size + n); }\n\n    for (size_t i = 0; i < n; i++) {\n      buf_[buf_epos_ + i] = data[i];\n    }\n    buf_epos_ += n;\n  }\n\n  void buf_erase(size_t size) { buf_spos_ += size; }\n\n  std::string buf_;\n  size_t buf_spos_ = 0;\n  size_t buf_epos_ = 0;\n};\n\ninline std::string random_string(size_t length) {\n  constexpr const char data[] =\n      \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n  thread_local auto engine([]() {\n    // std::random_device might actually be deterministic on some\n    // platforms, but due to lack of support in the c++ standard library,\n    // doing better requires either some ugly hacks or breaking portability.\n    std::random_device seed_gen;\n    // Request 128 bits of entropy for initialization\n    std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};\n    return std::mt19937(seed_sequence);\n  }());\n\n  std::string result;\n  for (size_t i = 0; i < length; i++) {\n    result += data[engine() % (sizeof(data) - 1)];\n  }\n  return result;\n}\n\ninline std::string make_multipart_data_boundary() {\n  return \"--cpp-httplib-multipart-data-\" + detail::random_string(16);\n}\n\ninline bool is_multipart_boundary_chars_valid(const std::string &boundary) {\n  auto valid = true;\n  for (size_t i = 0; i < boundary.size(); i++) {\n    auto c = boundary[i];\n    if (!std::isalnum(c) && c != '-' && c != '_') {\n      valid = false;\n      break;\n    }\n  }\n  return valid;\n}\n\ntemplate <typename T>\ninline std::string\nserialize_multipart_formdata_item_begin(const T &item,\n                                        const std::string &boundary) {\n  std::string body = \"--\" + boundary + \"\\r\\n\";\n  body += \"Content-Disposition: form-data; name=\\\"\" + item.name + \"\\\"\";\n  if (!item.filename.empty()) {\n    body += \"; filename=\\\"\" + item.filename + \"\\\"\";\n  }\n  body += \"\\r\\n\";\n  if (!item.content_type.empty()) {\n    body += \"Content-Type: \" + item.content_type + \"\\r\\n\";\n  }\n  body += \"\\r\\n\";\n\n  return body;\n}\n\ninline std::string serialize_multipart_formdata_item_end() { return \"\\r\\n\"; }\n\ninline std::string\nserialize_multipart_formdata_finish(const std::string &boundary) {\n  return \"--\" + boundary + \"--\\r\\n\";\n}\n\ninline std::string\nserialize_multipart_formdata_get_content_type(const std::string &boundary) {\n  return \"multipart/form-data; boundary=\" + boundary;\n}\n\ninline std::string\nserialize_multipart_formdata(const UploadFormDataItems &items,\n                             const std::string &boundary, bool finish = true) {\n  std::string body;\n\n  for (const auto &item : items) {\n    body += serialize_multipart_formdata_item_begin(item, boundary);\n    body += item.content + serialize_multipart_formdata_item_end();\n  }\n\n  if (finish) { body += serialize_multipart_formdata_finish(boundary); }\n\n  return body;\n}\n\ninline void coalesce_ranges(Ranges &ranges, size_t content_length) {\n  if (ranges.size() <= 1) return;\n\n  // Sort ranges by start position\n  std::sort(ranges.begin(), ranges.end(),\n            [](const Range &a, const Range &b) { return a.first < b.first; });\n\n  Ranges coalesced;\n  coalesced.reserve(ranges.size());\n\n  for (auto &r : ranges) {\n    auto first_pos = r.first;\n    auto last_pos = r.second;\n\n    // Handle special cases like in range_error\n    if (first_pos == -1 && last_pos == -1) {\n      first_pos = 0;\n      last_pos = static_cast<ssize_t>(content_length);\n    }\n\n    if (first_pos == -1) {\n      first_pos = static_cast<ssize_t>(content_length) - last_pos;\n      last_pos = static_cast<ssize_t>(content_length) - 1;\n    }\n\n    if (last_pos == -1 || last_pos >= static_cast<ssize_t>(content_length)) {\n      last_pos = static_cast<ssize_t>(content_length) - 1;\n    }\n\n    // Skip invalid ranges\n    if (!(0 <= first_pos && first_pos <= last_pos &&\n          last_pos < static_cast<ssize_t>(content_length))) {\n      continue;\n    }\n\n    // Coalesce with previous range if overlapping or adjacent (but not\n    // identical)\n    if (!coalesced.empty()) {\n      auto &prev = coalesced.back();\n      // Check if current range overlaps or is adjacent to previous range\n      // but don't coalesce identical ranges (allow duplicates)\n      if (first_pos <= prev.second + 1 &&\n          !(first_pos == prev.first && last_pos == prev.second)) {\n        // Extend the previous range\n        prev.second = (std::max)(prev.second, last_pos);\n        continue;\n      }\n    }\n\n    // Add new range\n    coalesced.emplace_back(first_pos, last_pos);\n  }\n\n  ranges = std::move(coalesced);\n}\n\ninline bool range_error(Request &req, Response &res) {\n  if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {\n    ssize_t content_len = static_cast<ssize_t>(\n        res.content_length_ ? res.content_length_ : res.body.size());\n\n    std::vector<std::pair<ssize_t, ssize_t>> processed_ranges;\n    size_t overwrapping_count = 0;\n\n    // NOTE: The following Range check is based on '14.2. Range' in RFC 9110\n    // 'HTTP Semantics' to avoid potential denial-of-service attacks.\n    // https://www.rfc-editor.org/rfc/rfc9110#section-14.2\n\n    // Too many ranges\n    if (req.ranges.size() > CPPHTTPLIB_RANGE_MAX_COUNT) { return true; }\n\n    for (auto &r : req.ranges) {\n      auto &first_pos = r.first;\n      auto &last_pos = r.second;\n\n      if (first_pos == -1 && last_pos == -1) {\n        first_pos = 0;\n        last_pos = content_len;\n      }\n\n      if (first_pos == -1) {\n        first_pos = content_len - last_pos;\n        last_pos = content_len - 1;\n      }\n\n      // NOTE: RFC-9110 '14.1.2. Byte Ranges':\n      // A client can limit the number of bytes requested without knowing the\n      // size of the selected representation. If the last-pos value is absent,\n      // or if the value is greater than or equal to the current length of the\n      // representation data, the byte range is interpreted as the remainder of\n      // the representation (i.e., the server replaces the value of last-pos\n      // with a value that is one less than the current length of the selected\n      // representation).\n      // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6\n      if (last_pos == -1 || last_pos >= content_len) {\n        last_pos = content_len - 1;\n      }\n\n      // Range must be within content length\n      if (!(0 <= first_pos && first_pos <= last_pos &&\n            last_pos <= content_len - 1)) {\n        return true;\n      }\n\n      // Request must not have more than two overlapping ranges\n      for (const auto &processed_range : processed_ranges) {\n        if (!(last_pos < processed_range.first ||\n              first_pos > processed_range.second)) {\n          overwrapping_count++;\n          if (overwrapping_count > 2) { return true; }\n          break; // Only count once per range\n        }\n      }\n\n      processed_ranges.emplace_back(first_pos, last_pos);\n    }\n\n    // After validation, coalesce overlapping ranges as per RFC 9110\n    coalesce_ranges(req.ranges, static_cast<size_t>(content_len));\n  }\n\n  return false;\n}\n\ninline std::pair<size_t, size_t>\nget_range_offset_and_length(Range r, size_t content_length) {\n  assert(r.first != -1 && r.second != -1);\n  assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));\n  assert(r.first <= r.second &&\n         r.second < static_cast<ssize_t>(content_length));\n  (void)(content_length);\n  return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);\n}\n\ninline std::string make_content_range_header_field(\n    const std::pair<size_t, size_t> &offset_and_length, size_t content_length) {\n  auto st = offset_and_length.first;\n  auto ed = st + offset_and_length.second - 1;\n\n  std::string field = \"bytes \";\n  field += std::to_string(st);\n  field += \"-\";\n  field += std::to_string(ed);\n  field += \"/\";\n  field += std::to_string(content_length);\n  return field;\n}\n\ntemplate <typename SToken, typename CToken, typename Content>\nbool process_multipart_ranges_data(const Request &req,\n                                   const std::string &boundary,\n                                   const std::string &content_type,\n                                   size_t content_length, SToken stoken,\n                                   CToken ctoken, Content content) {\n  for (size_t i = 0; i < req.ranges.size(); i++) {\n    ctoken(\"--\");\n    stoken(boundary);\n    ctoken(\"\\r\\n\");\n    if (!content_type.empty()) {\n      ctoken(\"Content-Type: \");\n      stoken(content_type);\n      ctoken(\"\\r\\n\");\n    }\n\n    auto offset_and_length =\n        get_range_offset_and_length(req.ranges[i], content_length);\n\n    ctoken(\"Content-Range: \");\n    stoken(make_content_range_header_field(offset_and_length, content_length));\n    ctoken(\"\\r\\n\");\n    ctoken(\"\\r\\n\");\n\n    if (!content(offset_and_length.first, offset_and_length.second)) {\n      return false;\n    }\n    ctoken(\"\\r\\n\");\n  }\n\n  ctoken(\"--\");\n  stoken(boundary);\n  ctoken(\"--\");\n\n  return true;\n}\n\ninline void make_multipart_ranges_data(const Request &req, Response &res,\n                                       const std::string &boundary,\n                                       const std::string &content_type,\n                                       size_t content_length,\n                                       std::string &data) {\n  process_multipart_ranges_data(\n      req, boundary, content_type, content_length,\n      [&](const std::string &token) { data += token; },\n      [&](const std::string &token) { data += token; },\n      [&](size_t offset, size_t length) {\n        assert(offset + length <= content_length);\n        data += res.body.substr(offset, length);\n        return true;\n      });\n}\n\ninline size_t get_multipart_ranges_data_length(const Request &req,\n                                               const std::string &boundary,\n                                               const std::string &content_type,\n                                               size_t content_length) {\n  size_t data_length = 0;\n\n  process_multipart_ranges_data(\n      req, boundary, content_type, content_length,\n      [&](const std::string &token) { data_length += token.size(); },\n      [&](const std::string &token) { data_length += token.size(); },\n      [&](size_t /*offset*/, size_t length) {\n        data_length += length;\n        return true;\n      });\n\n  return data_length;\n}\n\ntemplate <typename T>\ninline bool\nwrite_multipart_ranges_data(Stream &strm, const Request &req, Response &res,\n                            const std::string &boundary,\n                            const std::string &content_type,\n                            size_t content_length, const T &is_shutting_down) {\n  return process_multipart_ranges_data(\n      req, boundary, content_type, content_length,\n      [&](const std::string &token) { strm.write(token); },\n      [&](const std::string &token) { strm.write(token); },\n      [&](size_t offset, size_t length) {\n        return write_content(strm, res.content_provider_, offset, length,\n                             is_shutting_down);\n      });\n}\n\ninline bool expect_content(const Request &req) {\n  if (req.method == \"POST\" || req.method == \"PUT\" || req.method == \"PATCH\" ||\n      req.method == \"DELETE\") {\n    return true;\n  }\n  if (req.has_header(\"Content-Length\") &&\n      req.get_header_value_u64(\"Content-Length\") > 0) {\n    return true;\n  }\n  if (is_chunked_transfer_encoding(req.headers)) { return true; }\n  return false;\n}\n\ninline bool has_crlf(const std::string &s) {\n  auto p = s.c_str();\n  while (*p) {\n    if (*p == '\\r' || *p == '\\n') { return true; }\n    p++;\n  }\n  return false;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline std::string message_digest(const std::string &s, const EVP_MD *algo) {\n  auto context = std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)>(\n      EVP_MD_CTX_new(), EVP_MD_CTX_free);\n\n  unsigned int hash_length = 0;\n  unsigned char hash[EVP_MAX_MD_SIZE];\n\n  EVP_DigestInit_ex(context.get(), algo, nullptr);\n  EVP_DigestUpdate(context.get(), s.c_str(), s.size());\n  EVP_DigestFinal_ex(context.get(), hash, &hash_length);\n\n  std::stringstream ss;\n  for (auto i = 0u; i < hash_length; ++i) {\n    ss << std::hex << std::setw(2) << std::setfill('0')\n       << static_cast<unsigned int>(hash[i]);\n  }\n\n  return ss.str();\n}\n\ninline std::string MD5(const std::string &s) {\n  return message_digest(s, EVP_md5());\n}\n\ninline std::string SHA_256(const std::string &s) {\n  return message_digest(s, EVP_sha256());\n}\n\ninline std::string SHA_512(const std::string &s) {\n  return message_digest(s, EVP_sha512());\n}\n\ninline std::pair<std::string, std::string> make_digest_authentication_header(\n    const Request &req, const std::map<std::string, std::string> &auth,\n    size_t cnonce_count, const std::string &cnonce, const std::string &username,\n    const std::string &password, bool is_proxy = false) {\n  std::string nc;\n  {\n    std::stringstream ss;\n    ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;\n    nc = ss.str();\n  }\n\n  std::string qop;\n  if (auth.find(\"qop\") != auth.end()) {\n    qop = auth.at(\"qop\");\n    if (qop.find(\"auth-int\") != std::string::npos) {\n      qop = \"auth-int\";\n    } else if (qop.find(\"auth\") != std::string::npos) {\n      qop = \"auth\";\n    } else {\n      qop.clear();\n    }\n  }\n\n  std::string algo = \"MD5\";\n  if (auth.find(\"algorithm\") != auth.end()) { algo = auth.at(\"algorithm\"); }\n\n  std::string response;\n  {\n    auto H = algo == \"SHA-256\"   ? detail::SHA_256\n             : algo == \"SHA-512\" ? detail::SHA_512\n                                 : detail::MD5;\n\n    auto A1 = username + \":\" + auth.at(\"realm\") + \":\" + password;\n\n    auto A2 = req.method + \":\" + req.path;\n    if (qop == \"auth-int\") { A2 += \":\" + H(req.body); }\n\n    if (qop.empty()) {\n      response = H(H(A1) + \":\" + auth.at(\"nonce\") + \":\" + H(A2));\n    } else {\n      response = H(H(A1) + \":\" + auth.at(\"nonce\") + \":\" + nc + \":\" + cnonce +\n                   \":\" + qop + \":\" + H(A2));\n    }\n  }\n\n  auto opaque = (auth.find(\"opaque\") != auth.end()) ? auth.at(\"opaque\") : \"\";\n\n  auto field = \"Digest username=\\\"\" + username + \"\\\", realm=\\\"\" +\n               auth.at(\"realm\") + \"\\\", nonce=\\\"\" + auth.at(\"nonce\") +\n               \"\\\", uri=\\\"\" + req.path + \"\\\", algorithm=\" + algo +\n               (qop.empty() ? \", response=\\\"\"\n                            : \", qop=\" + qop + \", nc=\" + nc + \", cnonce=\\\"\" +\n                                  cnonce + \"\\\", response=\\\"\") +\n               response + \"\\\"\" +\n               (opaque.empty() ? \"\" : \", opaque=\\\"\" + opaque + \"\\\"\");\n\n  auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n  return std::make_pair(key, field);\n}\n\ninline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {\n  detail::set_nonblocking(sock, true);\n  auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });\n\n  char buf[1];\n  return !SSL_peek(ssl, buf, 1) &&\n         SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;\n}\n\n#ifdef _WIN32\n// NOTE: This code came up with the following stackoverflow post:\n// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store\ninline bool load_system_certs_on_windows(X509_STORE *store) {\n  auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L\"ROOT\");\n  if (!hStore) { return false; }\n\n  auto result = false;\n  PCCERT_CONTEXT pContext = NULL;\n  while ((pContext = CertEnumCertificatesInStore(hStore, pContext)) !=\n         nullptr) {\n    auto encoded_cert =\n        static_cast<const unsigned char *>(pContext->pbCertEncoded);\n\n    auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);\n    if (x509) {\n      X509_STORE_add_cert(store, x509);\n      X509_free(x509);\n      result = true;\n    }\n  }\n\n  CertFreeCertificateContext(pContext);\n  CertCloseStore(hStore, 0);\n\n  return result;\n}\n#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC\ntemplate <typename T>\nusing CFObjectPtr =\n    std::unique_ptr<typename std::remove_pointer<T>::type, void (*)(CFTypeRef)>;\n\ninline void cf_object_ptr_deleter(CFTypeRef obj) {\n  if (obj) { CFRelease(obj); }\n}\n\ninline bool retrieve_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {\n  CFStringRef keys[] = {kSecClass, kSecMatchLimit, kSecReturnRef};\n  CFTypeRef values[] = {kSecClassCertificate, kSecMatchLimitAll,\n                        kCFBooleanTrue};\n\n  CFObjectPtr<CFDictionaryRef> query(\n      CFDictionaryCreate(nullptr, reinterpret_cast<const void **>(keys), values,\n                         sizeof(keys) / sizeof(keys[0]),\n                         &kCFTypeDictionaryKeyCallBacks,\n                         &kCFTypeDictionaryValueCallBacks),\n      cf_object_ptr_deleter);\n\n  if (!query) { return false; }\n\n  CFTypeRef security_items = nullptr;\n  if (SecItemCopyMatching(query.get(), &security_items) != errSecSuccess ||\n      CFArrayGetTypeID() != CFGetTypeID(security_items)) {\n    return false;\n  }\n\n  certs.reset(reinterpret_cast<CFArrayRef>(security_items));\n  return true;\n}\n\ninline bool retrieve_root_certs_from_keychain(CFObjectPtr<CFArrayRef> &certs) {\n  CFArrayRef root_security_items = nullptr;\n  if (SecTrustCopyAnchorCertificates(&root_security_items) != errSecSuccess) {\n    return false;\n  }\n\n  certs.reset(root_security_items);\n  return true;\n}\n\ninline bool add_certs_to_x509_store(CFArrayRef certs, X509_STORE *store) {\n  auto result = false;\n  for (auto i = 0; i < CFArrayGetCount(certs); ++i) {\n    const auto cert = reinterpret_cast<const __SecCertificate *>(\n        CFArrayGetValueAtIndex(certs, i));\n\n    if (SecCertificateGetTypeID() != CFGetTypeID(cert)) { continue; }\n\n    CFDataRef cert_data = nullptr;\n    if (SecItemExport(cert, kSecFormatX509Cert, 0, nullptr, &cert_data) !=\n        errSecSuccess) {\n      continue;\n    }\n\n    CFObjectPtr<CFDataRef> cert_data_ptr(cert_data, cf_object_ptr_deleter);\n\n    auto encoded_cert = static_cast<const unsigned char *>(\n        CFDataGetBytePtr(cert_data_ptr.get()));\n\n    auto x509 =\n        d2i_X509(NULL, &encoded_cert, CFDataGetLength(cert_data_ptr.get()));\n\n    if (x509) {\n      X509_STORE_add_cert(store, x509);\n      X509_free(x509);\n      result = true;\n    }\n  }\n\n  return result;\n}\n\ninline bool load_system_certs_on_macos(X509_STORE *store) {\n  auto result = false;\n  CFObjectPtr<CFArrayRef> certs(nullptr, cf_object_ptr_deleter);\n  if (retrieve_certs_from_keychain(certs) && certs) {\n    result = add_certs_to_x509_store(certs.get(), store);\n  }\n\n  if (retrieve_root_certs_from_keychain(certs) && certs) {\n    result = add_certs_to_x509_store(certs.get(), store) || result;\n  }\n\n  return result;\n}\n#endif // _WIN32\n#endif // CPPHTTPLIB_OPENSSL_SUPPORT\n\n#ifdef _WIN32\nclass WSInit {\npublic:\n  WSInit() {\n    WSADATA wsaData;\n    if (WSAStartup(0x0002, &wsaData) == 0) is_valid_ = true;\n  }\n\n  ~WSInit() {\n    if (is_valid_) WSACleanup();\n  }\n\n  bool is_valid_ = false;\n};\n\nstatic WSInit wsinit_;\n#endif\n\ninline bool parse_www_authenticate(const Response &res,\n                                   std::map<std::string, std::string> &auth,\n                                   bool is_proxy) {\n  auto auth_key = is_proxy ? \"Proxy-Authenticate\" : \"WWW-Authenticate\";\n  if (res.has_header(auth_key)) {\n    thread_local auto re =\n        std::regex(R\"~((?:(?:,\\s*)?(.+?)=(?:\"(.*?)\"|([^,]*))))~\");\n    auto s = res.get_header_value(auth_key);\n    auto pos = s.find(' ');\n    if (pos != std::string::npos) {\n      auto type = s.substr(0, pos);\n      if (type == \"Basic\") {\n        return false;\n      } else if (type == \"Digest\") {\n        s = s.substr(pos + 1);\n        auto beg = std::sregex_iterator(s.begin(), s.end(), re);\n        for (auto i = beg; i != std::sregex_iterator(); ++i) {\n          const auto &m = *i;\n          auto key = s.substr(static_cast<size_t>(m.position(1)),\n                              static_cast<size_t>(m.length(1)));\n          auto val = m.length(2) > 0\n                         ? s.substr(static_cast<size_t>(m.position(2)),\n                                    static_cast<size_t>(m.length(2)))\n                         : s.substr(static_cast<size_t>(m.position(3)),\n                                    static_cast<size_t>(m.length(3)));\n          auth[key] = val;\n        }\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\nclass ContentProviderAdapter {\npublic:\n  explicit ContentProviderAdapter(\n      ContentProviderWithoutLength &&content_provider)\n      : content_provider_(content_provider) {}\n\n  bool operator()(size_t offset, size_t, DataSink &sink) {\n    return content_provider_(offset, sink);\n  }\n\nprivate:\n  ContentProviderWithoutLength content_provider_;\n};\n\n} // namespace detail\n\ninline std::string hosted_at(const std::string &hostname) {\n  std::vector<std::string> addrs;\n  hosted_at(hostname, addrs);\n  if (addrs.empty()) { return std::string(); }\n  return addrs[0];\n}\n\ninline void hosted_at(const std::string &hostname,\n                      std::vector<std::string> &addrs) {\n  struct addrinfo hints;\n  struct addrinfo *result;\n\n  memset(&hints, 0, sizeof(struct addrinfo));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_protocol = 0;\n\n  if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,\n                                       &result, 0)) {\n#if defined __linux__ && !defined __ANDROID__\n    res_init();\n#endif\n    return;\n  }\n  auto se = detail::scope_exit([&] { freeaddrinfo(result); });\n\n  for (auto rp = result; rp; rp = rp->ai_next) {\n    const auto &addr =\n        *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);\n    std::string ip;\n    auto dummy = -1;\n    if (detail::get_ip_and_port(addr, sizeof(struct sockaddr_storage), ip,\n                                dummy)) {\n      addrs.push_back(ip);\n    }\n  }\n}\n\ninline std::string encode_uri_component(const std::string &value) {\n  std::ostringstream escaped;\n  escaped.fill('0');\n  escaped << std::hex;\n\n  for (auto c : value) {\n    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||\n        c == '.' || c == '!' || c == '~' || c == '*' || c == '\\'' || c == '(' ||\n        c == ')') {\n      escaped << c;\n    } else {\n      escaped << std::uppercase;\n      escaped << '%' << std::setw(2)\n              << static_cast<int>(static_cast<unsigned char>(c));\n      escaped << std::nouppercase;\n    }\n  }\n\n  return escaped.str();\n}\n\ninline std::string encode_uri(const std::string &value) {\n  std::ostringstream escaped;\n  escaped.fill('0');\n  escaped << std::hex;\n\n  for (auto c : value) {\n    if (std::isalnum(static_cast<uint8_t>(c)) || c == '-' || c == '_' ||\n        c == '.' || c == '!' || c == '~' || c == '*' || c == '\\'' || c == '(' ||\n        c == ')' || c == ';' || c == '/' || c == '?' || c == ':' || c == '@' ||\n        c == '&' || c == '=' || c == '+' || c == '$' || c == ',' || c == '#') {\n      escaped << c;\n    } else {\n      escaped << std::uppercase;\n      escaped << '%' << std::setw(2)\n              << static_cast<int>(static_cast<unsigned char>(c));\n      escaped << std::nouppercase;\n    }\n  }\n\n  return escaped.str();\n}\n\ninline std::string decode_uri_component(const std::string &value) {\n  std::string result;\n\n  for (size_t i = 0; i < value.size(); i++) {\n    if (value[i] == '%' && i + 2 < value.size()) {\n      auto val = 0;\n      if (detail::from_hex_to_i(value, i + 1, 2, val)) {\n        result += static_cast<char>(val);\n        i += 2;\n      } else {\n        result += value[i];\n      }\n    } else {\n      result += value[i];\n    }\n  }\n\n  return result;\n}\n\ninline std::string decode_uri(const std::string &value) {\n  std::string result;\n\n  for (size_t i = 0; i < value.size(); i++) {\n    if (value[i] == '%' && i + 2 < value.size()) {\n      auto val = 0;\n      if (detail::from_hex_to_i(value, i + 1, 2, val)) {\n        result += static_cast<char>(val);\n        i += 2;\n      } else {\n        result += value[i];\n      }\n    } else {\n      result += value[i];\n    }\n  }\n\n  return result;\n}\n\ninline std::string encode_path_component(const std::string &component) {\n  std::string result;\n  result.reserve(component.size() * 3);\n\n  for (size_t i = 0; i < component.size(); i++) {\n    auto c = static_cast<unsigned char>(component[i]);\n\n    // Unreserved characters per RFC 3986: ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"\n    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {\n      result += static_cast<char>(c);\n    }\n    // Path-safe sub-delimiters: \"!\" / \"$\" / \"&\" / \"'\" / \"(\" / \")\" / \"*\" / \"+\" /\n    // \",\" / \";\" / \"=\"\n    else if (c == '!' || c == '$' || c == '&' || c == '\\'' || c == '(' ||\n             c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||\n             c == '=') {\n      result += static_cast<char>(c);\n    }\n    // Colon is allowed in path segments except first segment\n    else if (c == ':') {\n      result += static_cast<char>(c);\n    }\n    // @ is allowed in path\n    else if (c == '@') {\n      result += static_cast<char>(c);\n    } else {\n      result += '%';\n      char hex[3];\n      snprintf(hex, sizeof(hex), \"%02X\", c);\n      result.append(hex, 2);\n    }\n  }\n  return result;\n}\n\ninline std::string decode_path_component(const std::string &component) {\n  std::string result;\n  result.reserve(component.size());\n\n  for (size_t i = 0; i < component.size(); i++) {\n    if (component[i] == '%' && i + 1 < component.size()) {\n      if (component[i + 1] == 'u') {\n        // Unicode %uXXXX encoding\n        auto val = 0;\n        if (detail::from_hex_to_i(component, i + 2, 4, val)) {\n          // 4 digits Unicode codes\n          char buff[4];\n          size_t len = detail::to_utf8(val, buff);\n          if (len > 0) { result.append(buff, len); }\n          i += 5; // 'u0000'\n        } else {\n          result += component[i];\n        }\n      } else {\n        // Standard %XX encoding\n        auto val = 0;\n        if (detail::from_hex_to_i(component, i + 1, 2, val)) {\n          // 2 digits hex codes\n          result += static_cast<char>(val);\n          i += 2; // 'XX'\n        } else {\n          result += component[i];\n        }\n      }\n    } else {\n      result += component[i];\n    }\n  }\n  return result;\n}\n\ninline std::string encode_query_component(const std::string &component,\n                                          bool space_as_plus) {\n  std::string result;\n  result.reserve(component.size() * 3);\n\n  for (size_t i = 0; i < component.size(); i++) {\n    auto c = static_cast<unsigned char>(component[i]);\n\n    // Unreserved characters per RFC 3986\n    if (std::isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') {\n      result += static_cast<char>(c);\n    }\n    // Space handling\n    else if (c == ' ') {\n      if (space_as_plus) {\n        result += '+';\n      } else {\n        result += \"%20\";\n      }\n    }\n    // Plus sign handling\n    else if (c == '+') {\n      if (space_as_plus) {\n        result += \"%2B\";\n      } else {\n        result += static_cast<char>(c);\n      }\n    }\n    // Query-safe sub-delimiters (excluding & and = which are query delimiters)\n    else if (c == '!' || c == '$' || c == '\\'' || c == '(' || c == ')' ||\n             c == '*' || c == ',' || c == ';') {\n      result += static_cast<char>(c);\n    }\n    // Colon and @ are allowed in query\n    else if (c == ':' || c == '@') {\n      result += static_cast<char>(c);\n    }\n    // Forward slash is allowed in query values\n    else if (c == '/') {\n      result += static_cast<char>(c);\n    }\n    // Question mark is allowed in query values (after first ?)\n    else if (c == '?') {\n      result += static_cast<char>(c);\n    } else {\n      result += '%';\n      char hex[3];\n      snprintf(hex, sizeof(hex), \"%02X\", c);\n      result.append(hex, 2);\n    }\n  }\n  return result;\n}\n\ninline std::string decode_query_component(const std::string &component,\n                                          bool plus_as_space) {\n  std::string result;\n  result.reserve(component.size());\n\n  for (size_t i = 0; i < component.size(); i++) {\n    if (component[i] == '%' && i + 2 < component.size()) {\n      std::string hex = component.substr(i + 1, 2);\n      char *end;\n      unsigned long value = std::strtoul(hex.c_str(), &end, 16);\n      if (end == hex.c_str() + 2) {\n        result += static_cast<char>(value);\n        i += 2;\n      } else {\n        result += component[i];\n      }\n    } else if (component[i] == '+' && plus_as_space) {\n      result += ' '; // + becomes space in form-urlencoded\n    } else {\n      result += component[i];\n    }\n  }\n  return result;\n}\n\ninline std::string append_query_params(const std::string &path,\n                                       const Params &params) {\n  std::string path_with_query = path;\n  thread_local const std::regex re(\"[^?]+\\\\?.*\");\n  auto delm = std::regex_match(path, re) ? '&' : '?';\n  path_with_query += delm + detail::params_to_query_str(params);\n  return path_with_query;\n}\n\n// Header utilities\ninline std::pair<std::string, std::string>\nmake_range_header(const Ranges &ranges) {\n  std::string field = \"bytes=\";\n  auto i = 0;\n  for (const auto &r : ranges) {\n    if (i != 0) { field += \", \"; }\n    if (r.first != -1) { field += std::to_string(r.first); }\n    field += '-';\n    if (r.second != -1) { field += std::to_string(r.second); }\n    i++;\n  }\n  return std::make_pair(\"Range\", std::move(field));\n}\n\ninline std::pair<std::string, std::string>\nmake_basic_authentication_header(const std::string &username,\n                                 const std::string &password, bool is_proxy) {\n  auto field = \"Basic \" + detail::base64_encode(username + \":\" + password);\n  auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n  return std::make_pair(key, std::move(field));\n}\n\ninline std::pair<std::string, std::string>\nmake_bearer_token_authentication_header(const std::string &token,\n                                        bool is_proxy = false) {\n  auto field = \"Bearer \" + token;\n  auto key = is_proxy ? \"Proxy-Authorization\" : \"Authorization\";\n  return std::make_pair(key, std::move(field));\n}\n\n// Request implementation\ninline bool Request::has_header(const std::string &key) const {\n  return detail::has_header(headers, key);\n}\n\ninline std::string Request::get_header_value(const std::string &key,\n                                             const char *def, size_t id) const {\n  return detail::get_header_value(headers, key, def, id);\n}\n\ninline size_t Request::get_header_value_count(const std::string &key) const {\n  auto r = headers.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline void Request::set_header(const std::string &key,\n                                const std::string &val) {\n  if (detail::fields::is_field_name(key) &&\n      detail::fields::is_field_value(val)) {\n    headers.emplace(key, val);\n  }\n}\n\ninline bool Request::has_trailer(const std::string &key) const {\n  return trailers.find(key) != trailers.end();\n}\n\ninline std::string Request::get_trailer_value(const std::string &key,\n                                              size_t id) const {\n  auto rng = trailers.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) { return it->second; }\n  return std::string();\n}\n\ninline size_t Request::get_trailer_value_count(const std::string &key) const {\n  auto r = trailers.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline bool Request::has_param(const std::string &key) const {\n  return params.find(key) != params.end();\n}\n\ninline std::string Request::get_param_value(const std::string &key,\n                                            size_t id) const {\n  auto rng = params.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) { return it->second; }\n  return std::string();\n}\n\ninline size_t Request::get_param_value_count(const std::string &key) const {\n  auto r = params.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline bool Request::is_multipart_form_data() const {\n  const auto &content_type = get_header_value(\"Content-Type\");\n  return !content_type.rfind(\"multipart/form-data\", 0);\n}\n\n// Multipart FormData implementation\ninline std::string MultipartFormData::get_field(const std::string &key,\n                                                size_t id) const {\n  auto rng = fields.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) { return it->second.content; }\n  return std::string();\n}\n\ninline std::vector<std::string>\nMultipartFormData::get_fields(const std::string &key) const {\n  std::vector<std::string> values;\n  auto rng = fields.equal_range(key);\n  for (auto it = rng.first; it != rng.second; it++) {\n    values.push_back(it->second.content);\n  }\n  return values;\n}\n\ninline bool MultipartFormData::has_field(const std::string &key) const {\n  return fields.find(key) != fields.end();\n}\n\ninline size_t MultipartFormData::get_field_count(const std::string &key) const {\n  auto r = fields.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline FormData MultipartFormData::get_file(const std::string &key,\n                                            size_t id) const {\n  auto rng = files.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) { return it->second; }\n  return FormData();\n}\n\ninline std::vector<FormData>\nMultipartFormData::get_files(const std::string &key) const {\n  std::vector<FormData> values;\n  auto rng = files.equal_range(key);\n  for (auto it = rng.first; it != rng.second; it++) {\n    values.push_back(it->second);\n  }\n  return values;\n}\n\ninline bool MultipartFormData::has_file(const std::string &key) const {\n  return files.find(key) != files.end();\n}\n\ninline size_t MultipartFormData::get_file_count(const std::string &key) const {\n  auto r = files.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\n// Response implementation\ninline bool Response::has_header(const std::string &key) const {\n  return headers.find(key) != headers.end();\n}\n\ninline std::string Response::get_header_value(const std::string &key,\n                                              const char *def,\n                                              size_t id) const {\n  return detail::get_header_value(headers, key, def, id);\n}\n\ninline size_t Response::get_header_value_count(const std::string &key) const {\n  auto r = headers.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline void Response::set_header(const std::string &key,\n                                 const std::string &val) {\n  if (detail::fields::is_field_name(key) &&\n      detail::fields::is_field_value(val)) {\n    headers.emplace(key, val);\n  }\n}\ninline bool Response::has_trailer(const std::string &key) const {\n  return trailers.find(key) != trailers.end();\n}\n\ninline std::string Response::get_trailer_value(const std::string &key,\n                                               size_t id) const {\n  auto rng = trailers.equal_range(key);\n  auto it = rng.first;\n  std::advance(it, static_cast<ssize_t>(id));\n  if (it != rng.second) { return it->second; }\n  return std::string();\n}\n\ninline size_t Response::get_trailer_value_count(const std::string &key) const {\n  auto r = trailers.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\ninline void Response::set_redirect(const std::string &url, int stat) {\n  if (detail::fields::is_field_value(url)) {\n    set_header(\"Location\", url);\n    if (300 <= stat && stat < 400) {\n      this->status = stat;\n    } else {\n      this->status = StatusCode::Found_302;\n    }\n  }\n}\n\ninline void Response::set_content(const char *s, size_t n,\n                                  const std::string &content_type) {\n  body.assign(s, n);\n\n  auto rng = headers.equal_range(\"Content-Type\");\n  headers.erase(rng.first, rng.second);\n  set_header(\"Content-Type\", content_type);\n}\n\ninline void Response::set_content(const std::string &s,\n                                  const std::string &content_type) {\n  set_content(s.data(), s.size(), content_type);\n}\n\ninline void Response::set_content(std::string &&s,\n                                  const std::string &content_type) {\n  body = std::move(s);\n\n  auto rng = headers.equal_range(\"Content-Type\");\n  headers.erase(rng.first, rng.second);\n  set_header(\"Content-Type\", content_type);\n}\n\ninline void Response::set_content_provider(\n    size_t in_length, const std::string &content_type, ContentProvider provider,\n    ContentProviderResourceReleaser resource_releaser) {\n  set_header(\"Content-Type\", content_type);\n  content_length_ = in_length;\n  if (in_length > 0) { content_provider_ = std::move(provider); }\n  content_provider_resource_releaser_ = std::move(resource_releaser);\n  is_chunked_content_provider_ = false;\n}\n\ninline void Response::set_content_provider(\n    const std::string &content_type, ContentProviderWithoutLength provider,\n    ContentProviderResourceReleaser resource_releaser) {\n  set_header(\"Content-Type\", content_type);\n  content_length_ = 0;\n  content_provider_ = detail::ContentProviderAdapter(std::move(provider));\n  content_provider_resource_releaser_ = std::move(resource_releaser);\n  is_chunked_content_provider_ = false;\n}\n\ninline void Response::set_chunked_content_provider(\n    const std::string &content_type, ContentProviderWithoutLength provider,\n    ContentProviderResourceReleaser resource_releaser) {\n  set_header(\"Content-Type\", content_type);\n  content_length_ = 0;\n  content_provider_ = detail::ContentProviderAdapter(std::move(provider));\n  content_provider_resource_releaser_ = std::move(resource_releaser);\n  is_chunked_content_provider_ = true;\n}\n\ninline void Response::set_file_content(const std::string &path,\n                                       const std::string &content_type) {\n  file_content_path_ = path;\n  file_content_content_type_ = content_type;\n}\n\ninline void Response::set_file_content(const std::string &path) {\n  file_content_path_ = path;\n}\n\n// Result implementation\ninline bool Result::has_request_header(const std::string &key) const {\n  return request_headers_.find(key) != request_headers_.end();\n}\n\ninline std::string Result::get_request_header_value(const std::string &key,\n                                                    const char *def,\n                                                    size_t id) const {\n  return detail::get_header_value(request_headers_, key, def, id);\n}\n\ninline size_t\nResult::get_request_header_value_count(const std::string &key) const {\n  auto r = request_headers_.equal_range(key);\n  return static_cast<size_t>(std::distance(r.first, r.second));\n}\n\n// Stream implementation\ninline ssize_t Stream::write(const char *ptr) {\n  return write(ptr, strlen(ptr));\n}\n\ninline ssize_t Stream::write(const std::string &s) {\n  return write(s.data(), s.size());\n}\n\nnamespace detail {\n\ninline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,\n                                time_t timeout_sec, time_t timeout_usec,\n                                time_t &actual_timeout_sec,\n                                time_t &actual_timeout_usec) {\n  auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);\n\n  auto actual_timeout_msec =\n      (std::min)(max_timeout_msec - duration_msec, timeout_msec);\n\n  if (actual_timeout_msec < 0) { actual_timeout_msec = 0; }\n\n  actual_timeout_sec = actual_timeout_msec / 1000;\n  actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;\n}\n\n// Socket stream implementation\ninline SocketStream::SocketStream(\n    socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,\n    time_t write_timeout_sec, time_t write_timeout_usec,\n    time_t max_timeout_msec,\n    std::chrono::time_point<std::chrono::steady_clock> start_time)\n    : sock_(sock), read_timeout_sec_(read_timeout_sec),\n      read_timeout_usec_(read_timeout_usec),\n      write_timeout_sec_(write_timeout_sec),\n      write_timeout_usec_(write_timeout_usec),\n      max_timeout_msec_(max_timeout_msec), start_time_(start_time),\n      read_buff_(read_buff_size_, 0) {}\n\ninline SocketStream::~SocketStream() = default;\n\ninline bool SocketStream::is_readable() const {\n  return read_buff_off_ < read_buff_content_size_;\n}\n\ninline bool SocketStream::wait_readable() const {\n  if (max_timeout_msec_ <= 0) {\n    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;\n  }\n\n  time_t read_timeout_sec;\n  time_t read_timeout_usec;\n  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,\n                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);\n\n  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;\n}\n\ninline bool SocketStream::wait_writable() const {\n  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&\n         is_socket_alive(sock_);\n}\n\ninline ssize_t SocketStream::read(char *ptr, size_t size) {\n#ifdef _WIN32\n  size =\n      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));\n#else\n  size = (std::min)(size,\n                    static_cast<size_t>((std::numeric_limits<ssize_t>::max)()));\n#endif\n\n  if (read_buff_off_ < read_buff_content_size_) {\n    auto remaining_size = read_buff_content_size_ - read_buff_off_;\n    if (size <= remaining_size) {\n      memcpy(ptr, read_buff_.data() + read_buff_off_, size);\n      read_buff_off_ += size;\n      return static_cast<ssize_t>(size);\n    } else {\n      memcpy(ptr, read_buff_.data() + read_buff_off_, remaining_size);\n      read_buff_off_ += remaining_size;\n      return static_cast<ssize_t>(remaining_size);\n    }\n  }\n\n  if (!wait_readable()) { return -1; }\n\n  read_buff_off_ = 0;\n  read_buff_content_size_ = 0;\n\n  if (size < read_buff_size_) {\n    auto n = read_socket(sock_, read_buff_.data(), read_buff_size_,\n                         CPPHTTPLIB_RECV_FLAGS);\n    if (n <= 0) {\n      return n;\n    } else if (n <= static_cast<ssize_t>(size)) {\n      memcpy(ptr, read_buff_.data(), static_cast<size_t>(n));\n      return n;\n    } else {\n      memcpy(ptr, read_buff_.data(), size);\n      read_buff_off_ = size;\n      read_buff_content_size_ = static_cast<size_t>(n);\n      return static_cast<ssize_t>(size);\n    }\n  } else {\n    return read_socket(sock_, ptr, size, CPPHTTPLIB_RECV_FLAGS);\n  }\n}\n\ninline ssize_t SocketStream::write(const char *ptr, size_t size) {\n  if (!wait_writable()) { return -1; }\n\n#if defined(_WIN32) && !defined(_WIN64)\n  size =\n      (std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));\n#endif\n\n  return send_socket(sock_, ptr, size, CPPHTTPLIB_SEND_FLAGS);\n}\n\ninline void SocketStream::get_remote_ip_and_port(std::string &ip,\n                                                 int &port) const {\n  return detail::get_remote_ip_and_port(sock_, ip, port);\n}\n\ninline void SocketStream::get_local_ip_and_port(std::string &ip,\n                                                int &port) const {\n  return detail::get_local_ip_and_port(sock_, ip, port);\n}\n\ninline socket_t SocketStream::socket() const { return sock_; }\n\ninline time_t SocketStream::duration() const {\n  return std::chrono::duration_cast<std::chrono::milliseconds>(\n             std::chrono::steady_clock::now() - start_time_)\n      .count();\n}\n\n// Buffer stream implementation\ninline bool BufferStream::is_readable() const { return true; }\n\ninline bool BufferStream::wait_readable() const { return true; }\n\ninline bool BufferStream::wait_writable() const { return true; }\n\ninline ssize_t BufferStream::read(char *ptr, size_t size) {\n#if defined(_MSC_VER) && _MSC_VER < 1910\n  auto len_read = buffer._Copy_s(ptr, size, size, position);\n#else\n  auto len_read = buffer.copy(ptr, size, position);\n#endif\n  position += static_cast<size_t>(len_read);\n  return static_cast<ssize_t>(len_read);\n}\n\ninline ssize_t BufferStream::write(const char *ptr, size_t size) {\n  buffer.append(ptr, size);\n  return static_cast<ssize_t>(size);\n}\n\ninline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,\n                                                 int & /*port*/) const {}\n\ninline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,\n                                                int & /*port*/) const {}\n\ninline socket_t BufferStream::socket() const { return 0; }\n\ninline time_t BufferStream::duration() const { return 0; }\n\ninline const std::string &BufferStream::get_buffer() const { return buffer; }\n\ninline PathParamsMatcher::PathParamsMatcher(const std::string &pattern)\n    : MatcherBase(pattern) {\n  constexpr const char marker[] = \"/:\";\n\n  // One past the last ending position of a path param substring\n  std::size_t last_param_end = 0;\n\n#ifndef CPPHTTPLIB_NO_EXCEPTIONS\n  // Needed to ensure that parameter names are unique during matcher\n  // construction\n  // If exceptions are disabled, only last duplicate path\n  // parameter will be set\n  std::unordered_set<std::string> param_name_set;\n#endif\n\n  while (true) {\n    const auto marker_pos = pattern.find(\n        marker, last_param_end == 0 ? last_param_end : last_param_end - 1);\n    if (marker_pos == std::string::npos) { break; }\n\n    static_fragments_.push_back(\n        pattern.substr(last_param_end, marker_pos - last_param_end + 1));\n\n    const auto param_name_start = marker_pos + str_len(marker);\n\n    auto sep_pos = pattern.find(separator, param_name_start);\n    if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }\n\n    auto param_name =\n        pattern.substr(param_name_start, sep_pos - param_name_start);\n\n#ifndef CPPHTTPLIB_NO_EXCEPTIONS\n    if (param_name_set.find(param_name) != param_name_set.cend()) {\n      std::string msg = \"Encountered path parameter '\" + param_name +\n                        \"' multiple times in route pattern '\" + pattern + \"'.\";\n      throw std::invalid_argument(msg);\n    }\n#endif\n\n    param_names_.push_back(std::move(param_name));\n\n    last_param_end = sep_pos + 1;\n  }\n\n  if (last_param_end < pattern.length()) {\n    static_fragments_.push_back(pattern.substr(last_param_end));\n  }\n}\n\ninline bool PathParamsMatcher::match(Request &request) const {\n  request.matches = std::smatch();\n  request.path_params.clear();\n  request.path_params.reserve(param_names_.size());\n\n  // One past the position at which the path matched the pattern last time\n  std::size_t starting_pos = 0;\n  for (size_t i = 0; i < static_fragments_.size(); ++i) {\n    const auto &fragment = static_fragments_[i];\n\n    if (starting_pos + fragment.length() > request.path.length()) {\n      return false;\n    }\n\n    // Avoid unnecessary allocation by using strncmp instead of substr +\n    // comparison\n    if (std::strncmp(request.path.c_str() + starting_pos, fragment.c_str(),\n                     fragment.length()) != 0) {\n      return false;\n    }\n\n    starting_pos += fragment.length();\n\n    // Should only happen when we have a static fragment after a param\n    // Example: '/users/:id/subscriptions'\n    // The 'subscriptions' fragment here does not have a corresponding param\n    if (i >= param_names_.size()) { continue; }\n\n    auto sep_pos = request.path.find(separator, starting_pos);\n    if (sep_pos == std::string::npos) { sep_pos = request.path.length(); }\n\n    const auto &param_name = param_names_[i];\n\n    request.path_params.emplace(\n        param_name, request.path.substr(starting_pos, sep_pos - starting_pos));\n\n    // Mark everything up to '/' as matched\n    starting_pos = sep_pos + 1;\n  }\n  // Returns false if the path is longer than the pattern\n  return starting_pos >= request.path.length();\n}\n\ninline bool RegexMatcher::match(Request &request) const {\n  request.path_params.clear();\n  return std::regex_match(request.path, request.matches, regex_);\n}\n\ninline std::string make_host_and_port_string(const std::string &host, int port,\n                                             bool is_ssl) {\n  std::string result;\n\n  // Enclose IPv6 address in brackets (but not if already enclosed)\n  if (host.find(':') == std::string::npos ||\n      (!host.empty() && host[0] == '[')) {\n    // IPv4, hostname, or already bracketed IPv6\n    result = host;\n  } else {\n    // IPv6 address without brackets\n    result = \"[\" + host + \"]\";\n  }\n\n  // Append port if not default\n  if ((!is_ssl && port == 80) || (is_ssl && port == 443)) {\n    ; // do nothing\n  } else {\n    result += \":\" + std::to_string(port);\n  }\n\n  return result;\n}\n\n} // namespace detail\n\n// HTTP server implementation\ninline Server::Server()\n    : new_task_queue(\n          [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }) {\n#ifndef _WIN32\n  signal(SIGPIPE, SIG_IGN);\n#endif\n}\n\ninline Server::~Server() = default;\n\ninline std::unique_ptr<detail::MatcherBase>\nServer::make_matcher(const std::string &pattern) {\n  if (pattern.find(\"/:\") != std::string::npos) {\n    return detail::make_unique<detail::PathParamsMatcher>(pattern);\n  } else {\n    return detail::make_unique<detail::RegexMatcher>(pattern);\n  }\n}\n\ninline Server &Server::Get(const std::string &pattern, Handler handler) {\n  get_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Post(const std::string &pattern, Handler handler) {\n  post_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Post(const std::string &pattern,\n                            HandlerWithContentReader handler) {\n  post_handlers_for_content_reader_.emplace_back(make_matcher(pattern),\n                                                 std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Put(const std::string &pattern, Handler handler) {\n  put_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Put(const std::string &pattern,\n                           HandlerWithContentReader handler) {\n  put_handlers_for_content_reader_.emplace_back(make_matcher(pattern),\n                                                std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Patch(const std::string &pattern, Handler handler) {\n  patch_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Patch(const std::string &pattern,\n                             HandlerWithContentReader handler) {\n  patch_handlers_for_content_reader_.emplace_back(make_matcher(pattern),\n                                                  std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Delete(const std::string &pattern, Handler handler) {\n  delete_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Delete(const std::string &pattern,\n                              HandlerWithContentReader handler) {\n  delete_handlers_for_content_reader_.emplace_back(make_matcher(pattern),\n                                                   std::move(handler));\n  return *this;\n}\n\ninline Server &Server::Options(const std::string &pattern, Handler handler) {\n  options_handlers_.emplace_back(make_matcher(pattern), std::move(handler));\n  return *this;\n}\n\ninline bool Server::set_base_dir(const std::string &dir,\n                                 const std::string &mount_point) {\n  return set_mount_point(mount_point, dir);\n}\n\ninline bool Server::set_mount_point(const std::string &mount_point,\n                                    const std::string &dir, Headers headers) {\n  detail::FileStat stat(dir);\n  if (stat.is_dir()) {\n    std::string mnt = !mount_point.empty() ? mount_point : \"/\";\n    if (!mnt.empty() && mnt[0] == '/') {\n      base_dirs_.push_back({mnt, dir, std::move(headers)});\n      return true;\n    }\n  }\n  return false;\n}\n\ninline bool Server::remove_mount_point(const std::string &mount_point) {\n  for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {\n    if (it->mount_point == mount_point) {\n      base_dirs_.erase(it);\n      return true;\n    }\n  }\n  return false;\n}\n\ninline Server &\nServer::set_file_extension_and_mimetype_mapping(const std::string &ext,\n                                                const std::string &mime) {\n  file_extension_and_mimetype_map_[ext] = mime;\n  return *this;\n}\n\ninline Server &Server::set_default_file_mimetype(const std::string &mime) {\n  default_file_mimetype_ = mime;\n  return *this;\n}\n\ninline Server &Server::set_file_request_handler(Handler handler) {\n  file_request_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_error_handler_core(HandlerWithResponse handler,\n                                              std::true_type) {\n  error_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_error_handler_core(Handler handler,\n                                              std::false_type) {\n  error_handler_ = [handler](const Request &req, Response &res) {\n    handler(req, res);\n    return HandlerResponse::Handled;\n  };\n  return *this;\n}\n\ninline Server &Server::set_exception_handler(ExceptionHandler handler) {\n  exception_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_pre_routing_handler(HandlerWithResponse handler) {\n  pre_routing_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_post_routing_handler(Handler handler) {\n  post_routing_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_pre_request_handler(HandlerWithResponse handler) {\n  pre_request_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_logger(Logger logger) {\n  logger_ = std::move(logger);\n  return *this;\n}\n\ninline Server &Server::set_error_logger(ErrorLogger error_logger) {\n  error_logger_ = std::move(error_logger);\n  return *this;\n}\n\ninline Server &Server::set_pre_compression_logger(Logger logger) {\n  pre_compression_logger_ = std::move(logger);\n  return *this;\n}\n\ninline Server &\nServer::set_expect_100_continue_handler(Expect100ContinueHandler handler) {\n  expect_100_continue_handler_ = std::move(handler);\n  return *this;\n}\n\ninline Server &Server::set_address_family(int family) {\n  address_family_ = family;\n  return *this;\n}\n\ninline Server &Server::set_tcp_nodelay(bool on) {\n  tcp_nodelay_ = on;\n  return *this;\n}\n\ninline Server &Server::set_ipv6_v6only(bool on) {\n  ipv6_v6only_ = on;\n  return *this;\n}\n\ninline Server &Server::set_socket_options(SocketOptions socket_options) {\n  socket_options_ = std::move(socket_options);\n  return *this;\n}\n\ninline Server &Server::set_default_headers(Headers headers) {\n  default_headers_ = std::move(headers);\n  return *this;\n}\n\ninline Server &Server::set_header_writer(\n    std::function<ssize_t(Stream &, Headers &)> const &writer) {\n  header_writer_ = writer;\n  return *this;\n}\n\ninline Server &\nServer::set_trusted_proxies(const std::vector<std::string> &proxies) {\n  trusted_proxies_ = proxies;\n  return *this;\n}\n\ninline Server &Server::set_keep_alive_max_count(size_t count) {\n  keep_alive_max_count_ = count;\n  return *this;\n}\n\ninline Server &Server::set_keep_alive_timeout(time_t sec) {\n  keep_alive_timeout_sec_ = sec;\n  return *this;\n}\n\ninline Server &Server::set_read_timeout(time_t sec, time_t usec) {\n  read_timeout_sec_ = sec;\n  read_timeout_usec_ = usec;\n  return *this;\n}\n\ninline Server &Server::set_write_timeout(time_t sec, time_t usec) {\n  write_timeout_sec_ = sec;\n  write_timeout_usec_ = usec;\n  return *this;\n}\n\ninline Server &Server::set_idle_interval(time_t sec, time_t usec) {\n  idle_interval_sec_ = sec;\n  idle_interval_usec_ = usec;\n  return *this;\n}\n\ninline Server &Server::set_payload_max_length(size_t length) {\n  payload_max_length_ = length;\n  return *this;\n}\n\ninline bool Server::bind_to_port(const std::string &host, int port,\n                                 int socket_flags) {\n  auto ret = bind_internal(host, port, socket_flags);\n  if (ret == -1) { is_decommissioned = true; }\n  return ret >= 0;\n}\ninline int Server::bind_to_any_port(const std::string &host, int socket_flags) {\n  auto ret = bind_internal(host, 0, socket_flags);\n  if (ret == -1) { is_decommissioned = true; }\n  return ret;\n}\n\ninline bool Server::listen_after_bind() { return listen_internal(); }\n\ninline bool Server::listen(const std::string &host, int port,\n                           int socket_flags) {\n  return bind_to_port(host, port, socket_flags) && listen_internal();\n}\n\ninline bool Server::is_running() const { return is_running_; }\n\ninline void Server::wait_until_ready() const {\n  while (!is_running_ && !is_decommissioned) {\n    std::this_thread::sleep_for(std::chrono::milliseconds{1});\n  }\n}\n\ninline void Server::stop() {\n  if (is_running_) {\n    assert(svr_sock_ != INVALID_SOCKET);\n    std::atomic<socket_t> sock(svr_sock_.exchange(INVALID_SOCKET));\n    detail::shutdown_socket(sock);\n    detail::close_socket(sock);\n  }\n  is_decommissioned = false;\n}\n\ninline void Server::decommission() { is_decommissioned = true; }\n\ninline bool Server::parse_request_line(const char *s, Request &req) const {\n  auto len = strlen(s);\n  if (len < 2 || s[len - 2] != '\\r' || s[len - 1] != '\\n') { return false; }\n  len -= 2;\n\n  {\n    size_t count = 0;\n\n    detail::split(s, s + len, ' ', [&](const char *b, const char *e) {\n      switch (count) {\n      case 0: req.method = std::string(b, e); break;\n      case 1: req.target = std::string(b, e); break;\n      case 2: req.version = std::string(b, e); break;\n      default: break;\n      }\n      count++;\n    });\n\n    if (count != 3) { return false; }\n  }\n\n  thread_local const std::set<std::string> methods{\n      \"GET\",     \"HEAD\",    \"POST\",  \"PUT\",   \"DELETE\",\n      \"CONNECT\", \"OPTIONS\", \"TRACE\", \"PATCH\", \"PRI\"};\n\n  if (methods.find(req.method) == methods.end()) {\n    output_error_log(Error::InvalidHTTPMethod, &req);\n    return false;\n  }\n\n  if (req.version != \"HTTP/1.1\" && req.version != \"HTTP/1.0\") {\n    output_error_log(Error::InvalidHTTPVersion, &req);\n    return false;\n  }\n\n  {\n    // Skip URL fragment\n    for (size_t i = 0; i < req.target.size(); i++) {\n      if (req.target[i] == '#') {\n        req.target.erase(i);\n        break;\n      }\n    }\n\n    detail::divide(req.target, '?',\n                   [&](const char *lhs_data, std::size_t lhs_size,\n                       const char *rhs_data, std::size_t rhs_size) {\n                     req.path =\n                         decode_path_component(std::string(lhs_data, lhs_size));\n                     detail::parse_query_text(rhs_data, rhs_size, req.params);\n                   });\n  }\n\n  return true;\n}\n\ninline bool Server::write_response(Stream &strm, bool close_connection,\n                                   Request &req, Response &res) {\n  // NOTE: `req.ranges` should be empty, otherwise it will be applied\n  // incorrectly to the error content.\n  req.ranges.clear();\n  return write_response_core(strm, close_connection, req, res, false);\n}\n\ninline bool Server::write_response_with_content(Stream &strm,\n                                                bool close_connection,\n                                                const Request &req,\n                                                Response &res) {\n  return write_response_core(strm, close_connection, req, res, true);\n}\n\ninline bool Server::write_response_core(Stream &strm, bool close_connection,\n                                        const Request &req, Response &res,\n                                        bool need_apply_ranges) {\n  assert(res.status != -1);\n\n  if (400 <= res.status && error_handler_ &&\n      error_handler_(req, res) == HandlerResponse::Handled) {\n    need_apply_ranges = true;\n  }\n\n  std::string content_type;\n  std::string boundary;\n  if (need_apply_ranges) { apply_ranges(req, res, content_type, boundary); }\n\n  // Prepare additional headers\n  if (close_connection || req.get_header_value(\"Connection\") == \"close\") {\n    res.set_header(\"Connection\", \"close\");\n  } else {\n    std::string s = \"timeout=\";\n    s += std::to_string(keep_alive_timeout_sec_);\n    s += \", max=\";\n    s += std::to_string(keep_alive_max_count_);\n    res.set_header(\"Keep-Alive\", s);\n  }\n\n  if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&\n      !res.has_header(\"Content-Type\")) {\n    res.set_header(\"Content-Type\", \"text/plain\");\n  }\n\n  if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&\n      !res.has_header(\"Content-Length\")) {\n    res.set_header(\"Content-Length\", \"0\");\n  }\n\n  if (req.method == \"HEAD\" && !res.has_header(\"Accept-Ranges\")) {\n    res.set_header(\"Accept-Ranges\", \"bytes\");\n  }\n\n  if (post_routing_handler_) { post_routing_handler_(req, res); }\n\n  // Response line and headers\n  {\n    detail::BufferStream bstrm;\n    if (!detail::write_response_line(bstrm, res.status)) { return false; }\n    if (!header_writer_(bstrm, res.headers)) { return false; }\n\n    // Flush buffer\n    auto &data = bstrm.get_buffer();\n    detail::write_data(strm, data.data(), data.size());\n  }\n\n  // Body\n  auto ret = true;\n  if (req.method != \"HEAD\") {\n    if (!res.body.empty()) {\n      if (!detail::write_data(strm, res.body.data(), res.body.size())) {\n        ret = false;\n      }\n    } else if (res.content_provider_) {\n      if (write_content_with_provider(strm, req, res, boundary, content_type)) {\n        res.content_provider_success_ = true;\n      } else {\n        ret = false;\n      }\n    }\n  }\n\n  // Log\n  output_log(req, res);\n\n  return ret;\n}\n\ninline bool\nServer::write_content_with_provider(Stream &strm, const Request &req,\n                                    Response &res, const std::string &boundary,\n                                    const std::string &content_type) {\n  auto is_shutting_down = [this]() {\n    return this->svr_sock_ == INVALID_SOCKET;\n  };\n\n  if (res.content_length_ > 0) {\n    if (req.ranges.empty()) {\n      return detail::write_content(strm, res.content_provider_, 0,\n                                   res.content_length_, is_shutting_down);\n    } else if (req.ranges.size() == 1) {\n      auto offset_and_length = detail::get_range_offset_and_length(\n          req.ranges[0], res.content_length_);\n\n      return detail::write_content(strm, res.content_provider_,\n                                   offset_and_length.first,\n                                   offset_and_length.second, is_shutting_down);\n    } else {\n      return detail::write_multipart_ranges_data(\n          strm, req, res, boundary, content_type, res.content_length_,\n          is_shutting_down);\n    }\n  } else {\n    if (res.is_chunked_content_provider_) {\n      auto type = detail::encoding_type(req, res);\n\n      std::unique_ptr<detail::compressor> compressor;\n      if (type == detail::EncodingType::Gzip) {\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n        compressor = detail::make_unique<detail::gzip_compressor>();\n#endif\n      } else if (type == detail::EncodingType::Brotli) {\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n        compressor = detail::make_unique<detail::brotli_compressor>();\n#endif\n      } else if (type == detail::EncodingType::Zstd) {\n#ifdef CPPHTTPLIB_ZSTD_SUPPORT\n        compressor = detail::make_unique<detail::zstd_compressor>();\n#endif\n      } else {\n        compressor = detail::make_unique<detail::nocompressor>();\n      }\n      assert(compressor != nullptr);\n\n      return detail::write_content_chunked(strm, res.content_provider_,\n                                           is_shutting_down, *compressor);\n    } else {\n      return detail::write_content_without_length(strm, res.content_provider_,\n                                                  is_shutting_down);\n    }\n  }\n}\n\ninline bool Server::read_content(Stream &strm, Request &req, Response &res) {\n  FormFields::iterator cur_field;\n  FormFiles::iterator cur_file;\n  auto is_text_field = false;\n  size_t count = 0;\n  if (read_content_core(\n          strm, req, res,\n          // Regular\n          [&](const char *buf, size_t n) {\n            if (req.body.size() + n > req.body.max_size()) { return false; }\n            req.body.append(buf, n);\n            return true;\n          },\n          // Multipart FormData\n          [&](const FormData &file) {\n            if (count++ == CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT) {\n              output_error_log(Error::TooManyFormDataFiles, &req);\n              return false;\n            }\n\n            if (file.filename.empty()) {\n              cur_field = req.form.fields.emplace(\n                  file.name, FormField{file.name, file.content, file.headers});\n              is_text_field = true;\n            } else {\n              cur_file = req.form.files.emplace(file.name, file);\n              is_text_field = false;\n            }\n            return true;\n          },\n          [&](const char *buf, size_t n) {\n            if (is_text_field) {\n              auto &content = cur_field->second.content;\n              if (content.size() + n > content.max_size()) { return false; }\n              content.append(buf, n);\n            } else {\n              auto &content = cur_file->second.content;\n              if (content.size() + n > content.max_size()) { return false; }\n              content.append(buf, n);\n            }\n            return true;\n          })) {\n    const auto &content_type = req.get_header_value(\"Content-Type\");\n    if (!content_type.find(\"application/x-www-form-urlencoded\")) {\n      if (req.body.size() > CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH) {\n        res.status = StatusCode::PayloadTooLarge_413; // NOTE: should be 414?\n        output_error_log(Error::ExceedMaxPayloadSize, &req);\n        return false;\n      }\n      detail::parse_query_text(req.body, req.params);\n    }\n    return true;\n  }\n  return false;\n}\n\ninline bool Server::read_content_with_content_receiver(\n    Stream &strm, Request &req, Response &res, ContentReceiver receiver,\n    FormDataHeader multipart_header, ContentReceiver multipart_receiver) {\n  return read_content_core(strm, req, res, std::move(receiver),\n                           std::move(multipart_header),\n                           std::move(multipart_receiver));\n}\n\ninline bool Server::read_content_core(\n    Stream &strm, Request &req, Response &res, ContentReceiver receiver,\n    FormDataHeader multipart_header, ContentReceiver multipart_receiver) const {\n  detail::FormDataParser multipart_form_data_parser;\n  ContentReceiverWithProgress out;\n\n  if (req.is_multipart_form_data()) {\n    const auto &content_type = req.get_header_value(\"Content-Type\");\n    std::string boundary;\n    if (!detail::parse_multipart_boundary(content_type, boundary)) {\n      res.status = StatusCode::BadRequest_400;\n      output_error_log(Error::MultipartParsing, &req);\n      return false;\n    }\n\n    multipart_form_data_parser.set_boundary(std::move(boundary));\n    out = [&](const char *buf, size_t n, size_t /*off*/, size_t /*len*/) {\n      return multipart_form_data_parser.parse(buf, n, multipart_header,\n                                              multipart_receiver);\n    };\n  } else {\n    out = [receiver](const char *buf, size_t n, size_t /*off*/,\n                     size_t /*len*/) { return receiver(buf, n); };\n  }\n\n  if (req.method == \"DELETE\" && !req.has_header(\"Content-Length\")) {\n    return true;\n  }\n\n  if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,\n                            out, true)) {\n    return false;\n  }\n\n  if (req.is_multipart_form_data()) {\n    if (!multipart_form_data_parser.is_valid()) {\n      res.status = StatusCode::BadRequest_400;\n      output_error_log(Error::MultipartParsing, &req);\n      return false;\n    }\n  }\n\n  return true;\n}\n\ninline bool Server::handle_file_request(const Request &req, Response &res) {\n  for (const auto &entry : base_dirs_) {\n    // Prefix match\n    if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {\n      std::string sub_path = \"/\" + req.path.substr(entry.mount_point.size());\n      if (detail::is_valid_path(sub_path)) {\n        auto path = entry.base_dir + sub_path;\n        if (path.back() == '/') { path += \"index.html\"; }\n\n        detail::FileStat stat(path);\n\n        if (stat.is_dir()) {\n          res.set_redirect(sub_path + \"/\", StatusCode::MovedPermanently_301);\n          return true;\n        }\n\n        if (stat.is_file()) {\n          for (const auto &kv : entry.headers) {\n            res.set_header(kv.first, kv.second);\n          }\n\n          auto mm = std::make_shared<detail::mmap>(path.c_str());\n          if (!mm->is_open()) {\n            output_error_log(Error::OpenFile, &req);\n            return false;\n          }\n\n          res.set_content_provider(\n              mm->size(),\n              detail::find_content_type(path, file_extension_and_mimetype_map_,\n                                        default_file_mimetype_),\n              [mm](size_t offset, size_t length, DataSink &sink) -> bool {\n                sink.write(mm->data() + offset, length);\n                return true;\n              });\n\n          if (req.method != \"HEAD\" && file_request_handler_) {\n            file_request_handler_(req, res);\n          }\n\n          return true;\n        } else {\n          output_error_log(Error::OpenFile, &req);\n        }\n      }\n    }\n  }\n  return false;\n}\n\ninline socket_t\nServer::create_server_socket(const std::string &host, int port,\n                             int socket_flags,\n                             SocketOptions socket_options) const {\n  return detail::create_socket(\n      host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,\n      ipv6_v6only_, std::move(socket_options),\n      [&](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {\n        if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {\n          output_error_log(Error::BindIPAddress, nullptr);\n          return false;\n        }\n        if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {\n          output_error_log(Error::Listen, nullptr);\n          return false;\n        }\n        return true;\n      });\n}\n\ninline int Server::bind_internal(const std::string &host, int port,\n                                 int socket_flags) {\n  if (is_decommissioned) { return -1; }\n\n  if (!is_valid()) { return -1; }\n\n  svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);\n  if (svr_sock_ == INVALID_SOCKET) { return -1; }\n\n  if (port == 0) {\n    struct sockaddr_storage addr;\n    socklen_t addr_len = sizeof(addr);\n    if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),\n                    &addr_len) == -1) {\n      output_error_log(Error::GetSockName, nullptr);\n      return -1;\n    }\n    if (addr.ss_family == AF_INET) {\n      return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);\n    } else if (addr.ss_family == AF_INET6) {\n      return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);\n    } else {\n      output_error_log(Error::UnsupportedAddressFamily, nullptr);\n      return -1;\n    }\n  } else {\n    return port;\n  }\n}\n\ninline bool Server::listen_internal() {\n  if (is_decommissioned) { return false; }\n\n  auto ret = true;\n  is_running_ = true;\n  auto se = detail::scope_exit([&]() { is_running_ = false; });\n\n  {\n    std::unique_ptr<TaskQueue> task_queue(new_task_queue());\n\n    while (svr_sock_ != INVALID_SOCKET) {\n#ifndef _WIN32\n      if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {\n#endif\n        auto val = detail::select_read(svr_sock_, idle_interval_sec_,\n                                       idle_interval_usec_);\n        if (val == 0) { // Timeout\n          task_queue->on_idle();\n          continue;\n        }\n#ifndef _WIN32\n      }\n#endif\n\n#if defined _WIN32\n      // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,\n      // OVERLAPPED\n      socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);\n#elif defined SOCK_CLOEXEC\n      socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);\n#else\n      socket_t sock = accept(svr_sock_, nullptr, nullptr);\n#endif\n\n      if (sock == INVALID_SOCKET) {\n        if (errno == EMFILE) {\n          // The per-process limit of open file descriptors has been reached.\n          // Try to accept new connections after a short sleep.\n          std::this_thread::sleep_for(std::chrono::microseconds{1});\n          continue;\n        } else if (errno == EINTR || errno == EAGAIN) {\n          continue;\n        }\n        if (svr_sock_ != INVALID_SOCKET) {\n          detail::close_socket(svr_sock_);\n          ret = false;\n          output_error_log(Error::Connection, nullptr);\n        } else {\n          ; // The server socket was closed by user.\n        }\n        break;\n      }\n\n      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,\n                                  read_timeout_sec_, read_timeout_usec_);\n      detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,\n                                  write_timeout_sec_, write_timeout_usec_);\n\n      if (!task_queue->enqueue(\n              [this, sock]() { process_and_close_socket(sock); })) {\n        output_error_log(Error::ResourceExhaustion, nullptr);\n        detail::shutdown_socket(sock);\n        detail::close_socket(sock);\n      }\n    }\n\n    task_queue->shutdown();\n  }\n\n  is_decommissioned = !ret;\n  return ret;\n}\n\ninline bool Server::routing(Request &req, Response &res, Stream &strm) {\n  if (pre_routing_handler_ &&\n      pre_routing_handler_(req, res) == HandlerResponse::Handled) {\n    return true;\n  }\n\n  // File handler\n  if ((req.method == \"GET\" || req.method == \"HEAD\") &&\n      handle_file_request(req, res)) {\n    return true;\n  }\n\n  if (detail::expect_content(req)) {\n    // Content reader handler\n    {\n      ContentReader reader(\n          [&](ContentReceiver receiver) {\n            auto result = read_content_with_content_receiver(\n                strm, req, res, std::move(receiver), nullptr, nullptr);\n            if (!result) { output_error_log(Error::Read, &req); }\n            return result;\n          },\n          [&](FormDataHeader header, ContentReceiver receiver) {\n            auto result = read_content_with_content_receiver(\n                strm, req, res, nullptr, std::move(header),\n                std::move(receiver));\n            if (!result) { output_error_log(Error::Read, &req); }\n            return result;\n          });\n\n      if (req.method == \"POST\") {\n        if (dispatch_request_for_content_reader(\n                req, res, std::move(reader),\n                post_handlers_for_content_reader_)) {\n          return true;\n        }\n      } else if (req.method == \"PUT\") {\n        if (dispatch_request_for_content_reader(\n                req, res, std::move(reader),\n                put_handlers_for_content_reader_)) {\n          return true;\n        }\n      } else if (req.method == \"PATCH\") {\n        if (dispatch_request_for_content_reader(\n                req, res, std::move(reader),\n                patch_handlers_for_content_reader_)) {\n          return true;\n        }\n      } else if (req.method == \"DELETE\") {\n        if (dispatch_request_for_content_reader(\n                req, res, std::move(reader),\n                delete_handlers_for_content_reader_)) {\n          return true;\n        }\n      }\n    }\n\n    // Read content into `req.body`\n    if (!read_content(strm, req, res)) {\n      output_error_log(Error::Read, &req);\n      return false;\n    }\n  }\n\n  // Regular handler\n  if (req.method == \"GET\" || req.method == \"HEAD\") {\n    return dispatch_request(req, res, get_handlers_);\n  } else if (req.method == \"POST\") {\n    return dispatch_request(req, res, post_handlers_);\n  } else if (req.method == \"PUT\") {\n    return dispatch_request(req, res, put_handlers_);\n  } else if (req.method == \"DELETE\") {\n    return dispatch_request(req, res, delete_handlers_);\n  } else if (req.method == \"OPTIONS\") {\n    return dispatch_request(req, res, options_handlers_);\n  } else if (req.method == \"PATCH\") {\n    return dispatch_request(req, res, patch_handlers_);\n  }\n\n  res.status = StatusCode::BadRequest_400;\n  return false;\n}\n\ninline bool Server::dispatch_request(Request &req, Response &res,\n                                     const Handlers &handlers) const {\n  for (const auto &x : handlers) {\n    const auto &matcher = x.first;\n    const auto &handler = x.second;\n\n    if (matcher->match(req)) {\n      req.matched_route = matcher->pattern();\n      if (!pre_request_handler_ ||\n          pre_request_handler_(req, res) != HandlerResponse::Handled) {\n        handler(req, res);\n      }\n      return true;\n    }\n  }\n  return false;\n}\n\ninline void Server::apply_ranges(const Request &req, Response &res,\n                                 std::string &content_type,\n                                 std::string &boundary) const {\n  if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {\n    auto it = res.headers.find(\"Content-Type\");\n    if (it != res.headers.end()) {\n      content_type = it->second;\n      res.headers.erase(it);\n    }\n\n    boundary = detail::make_multipart_data_boundary();\n\n    res.set_header(\"Content-Type\",\n                   \"multipart/byteranges; boundary=\" + boundary);\n  }\n\n  auto type = detail::encoding_type(req, res);\n\n  if (res.body.empty()) {\n    if (res.content_length_ > 0) {\n      size_t length = 0;\n      if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {\n        length = res.content_length_;\n      } else if (req.ranges.size() == 1) {\n        auto offset_and_length = detail::get_range_offset_and_length(\n            req.ranges[0], res.content_length_);\n\n        length = offset_and_length.second;\n\n        auto content_range = detail::make_content_range_header_field(\n            offset_and_length, res.content_length_);\n        res.set_header(\"Content-Range\", content_range);\n      } else {\n        length = detail::get_multipart_ranges_data_length(\n            req, boundary, content_type, res.content_length_);\n      }\n      res.set_header(\"Content-Length\", std::to_string(length));\n    } else {\n      if (res.content_provider_) {\n        if (res.is_chunked_content_provider_) {\n          res.set_header(\"Transfer-Encoding\", \"chunked\");\n          if (type == detail::EncodingType::Gzip) {\n            res.set_header(\"Content-Encoding\", \"gzip\");\n          } else if (type == detail::EncodingType::Brotli) {\n            res.set_header(\"Content-Encoding\", \"br\");\n          } else if (type == detail::EncodingType::Zstd) {\n            res.set_header(\"Content-Encoding\", \"zstd\");\n          }\n        }\n      }\n    }\n  } else {\n    if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {\n      ;\n    } else if (req.ranges.size() == 1) {\n      auto offset_and_length =\n          detail::get_range_offset_and_length(req.ranges[0], res.body.size());\n      auto offset = offset_and_length.first;\n      auto length = offset_and_length.second;\n\n      auto content_range = detail::make_content_range_header_field(\n          offset_and_length, res.body.size());\n      res.set_header(\"Content-Range\", content_range);\n\n      assert(offset + length <= res.body.size());\n      res.body = res.body.substr(offset, length);\n    } else {\n      std::string data;\n      detail::make_multipart_ranges_data(req, res, boundary, content_type,\n                                         res.body.size(), data);\n      res.body.swap(data);\n    }\n\n    if (type != detail::EncodingType::None) {\n      output_pre_compression_log(req, res);\n\n      std::unique_ptr<detail::compressor> compressor;\n      std::string content_encoding;\n\n      if (type == detail::EncodingType::Gzip) {\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n        compressor = detail::make_unique<detail::gzip_compressor>();\n        content_encoding = \"gzip\";\n#endif\n      } else if (type == detail::EncodingType::Brotli) {\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n        compressor = detail::make_unique<detail::brotli_compressor>();\n        content_encoding = \"br\";\n#endif\n      } else if (type == detail::EncodingType::Zstd) {\n#ifdef CPPHTTPLIB_ZSTD_SUPPORT\n        compressor = detail::make_unique<detail::zstd_compressor>();\n        content_encoding = \"zstd\";\n#endif\n      }\n\n      if (compressor) {\n        std::string compressed;\n        if (compressor->compress(res.body.data(), res.body.size(), true,\n                                 [&](const char *data, size_t data_len) {\n                                   compressed.append(data, data_len);\n                                   return true;\n                                 })) {\n          res.body.swap(compressed);\n          res.set_header(\"Content-Encoding\", content_encoding);\n        }\n      }\n    }\n\n    auto length = std::to_string(res.body.size());\n    res.set_header(\"Content-Length\", length);\n  }\n}\n\ninline bool Server::dispatch_request_for_content_reader(\n    Request &req, Response &res, ContentReader content_reader,\n    const HandlersForContentReader &handlers) const {\n  for (const auto &x : handlers) {\n    const auto &matcher = x.first;\n    const auto &handler = x.second;\n\n    if (matcher->match(req)) {\n      req.matched_route = matcher->pattern();\n      if (!pre_request_handler_ ||\n          pre_request_handler_(req, res) != HandlerResponse::Handled) {\n        handler(req, res, content_reader);\n      }\n      return true;\n    }\n  }\n  return false;\n}\n\ninline std::string\nget_client_ip(const std::string &x_forwarded_for,\n              const std::vector<std::string> &trusted_proxies) {\n  // X-Forwarded-For is a comma-separated list per RFC 7239\n  std::vector<std::string> ip_list;\n  detail::split(x_forwarded_for.data(),\n                x_forwarded_for.data() + x_forwarded_for.size(), ',',\n                [&](const char *b, const char *e) {\n                  auto r = detail::trim(b, e, 0, static_cast<size_t>(e - b));\n                  ip_list.emplace_back(std::string(b + r.first, b + r.second));\n                });\n\n  for (size_t i = 0; i < ip_list.size(); ++i) {\n    auto ip = ip_list[i];\n\n    auto is_trusted_proxy =\n        std::any_of(trusted_proxies.begin(), trusted_proxies.end(),\n                    [&](const std::string &proxy) { return ip == proxy; });\n\n    if (is_trusted_proxy) {\n      if (i == 0) {\n        // If the trusted proxy is the first IP, there's no preceding client IP\n        return ip;\n      } else {\n        // Return the IP immediately before the trusted proxy\n        return ip_list[i - 1];\n      }\n    }\n  }\n\n  // If no trusted proxy is found, return the first IP in the list\n  return ip_list.front();\n}\n\ninline bool\nServer::process_request(Stream &strm, const std::string &remote_addr,\n                        int remote_port, const std::string &local_addr,\n                        int local_port, bool close_connection,\n                        bool &connection_closed,\n                        const std::function<void(Request &)> &setup_request) {\n  std::array<char, 2048> buf{};\n\n  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());\n\n  // Connection has been closed on client\n  if (!line_reader.getline()) { return false; }\n\n  Request req;\n  req.start_time_ = std::chrono::steady_clock::now();\n\n  Response res;\n  res.version = \"HTTP/1.1\";\n  res.headers = default_headers_;\n\n#ifdef __APPLE__\n  // Socket file descriptor exceeded FD_SETSIZE...\n  if (strm.socket() >= FD_SETSIZE) {\n    Headers dummy;\n    detail::read_headers(strm, dummy);\n    res.status = StatusCode::InternalServerError_500;\n    output_error_log(Error::ExceedMaxSocketDescriptorCount, &req);\n    return write_response(strm, close_connection, req, res);\n  }\n#endif\n\n  // Request line and headers\n  if (!parse_request_line(line_reader.ptr(), req)) {\n    res.status = StatusCode::BadRequest_400;\n    output_error_log(Error::InvalidRequestLine, &req);\n    return write_response(strm, close_connection, req, res);\n  }\n\n  // Request headers\n  if (!detail::read_headers(strm, req.headers)) {\n    res.status = StatusCode::BadRequest_400;\n    output_error_log(Error::InvalidHeaders, &req);\n    return write_response(strm, close_connection, req, res);\n  }\n\n  // Check if the request URI doesn't exceed the limit\n  if (req.target.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {\n    Headers dummy;\n    detail::read_headers(strm, dummy);\n    res.status = StatusCode::UriTooLong_414;\n    output_error_log(Error::ExceedUriMaxLength, &req);\n    return write_response(strm, close_connection, req, res);\n  }\n\n  if (req.get_header_value(\"Connection\") == \"close\") {\n    connection_closed = true;\n  }\n\n  if (req.version == \"HTTP/1.0\" &&\n      req.get_header_value(\"Connection\") != \"Keep-Alive\") {\n    connection_closed = true;\n  }\n\n  if (!trusted_proxies_.empty() && req.has_header(\"X-Forwarded-For\")) {\n    auto x_forwarded_for = req.get_header_value(\"X-Forwarded-For\");\n    req.remote_addr = get_client_ip(x_forwarded_for, trusted_proxies_);\n  } else {\n    req.remote_addr = remote_addr;\n  }\n  req.remote_port = remote_port;\n\n  req.local_addr = local_addr;\n  req.local_port = local_port;\n\n  if (req.has_header(\"Accept\")) {\n    const auto &accept_header = req.get_header_value(\"Accept\");\n    if (!detail::parse_accept_header(accept_header, req.accept_content_types)) {\n      res.status = StatusCode::BadRequest_400;\n      output_error_log(Error::HTTPParsing, &req);\n      return write_response(strm, close_connection, req, res);\n    }\n  }\n\n  if (req.has_header(\"Range\")) {\n    const auto &range_header_value = req.get_header_value(\"Range\");\n    if (!detail::parse_range_header(range_header_value, req.ranges)) {\n      res.status = StatusCode::RangeNotSatisfiable_416;\n      output_error_log(Error::InvalidRangeHeader, &req);\n      return write_response(strm, close_connection, req, res);\n    }\n  }\n\n  if (setup_request) { setup_request(req); }\n\n  if (req.get_header_value(\"Expect\") == \"100-continue\") {\n    int status = StatusCode::Continue_100;\n    if (expect_100_continue_handler_) {\n      status = expect_100_continue_handler_(req, res);\n    }\n    switch (status) {\n    case StatusCode::Continue_100:\n    case StatusCode::ExpectationFailed_417:\n      detail::write_response_line(strm, status);\n      strm.write(\"\\r\\n\");\n      break;\n    default:\n      connection_closed = true;\n      return write_response(strm, true, req, res);\n    }\n  }\n\n  // Setup `is_connection_closed` method\n  auto sock = strm.socket();\n  req.is_connection_closed = [sock]() {\n    return !detail::is_socket_alive(sock);\n  };\n\n  // Routing\n  auto routed = false;\n#ifdef CPPHTTPLIB_NO_EXCEPTIONS\n  routed = routing(req, res, strm);\n#else\n  try {\n    routed = routing(req, res, strm);\n  } catch (std::exception &e) {\n    if (exception_handler_) {\n      auto ep = std::current_exception();\n      exception_handler_(req, res, ep);\n      routed = true;\n    } else {\n      res.status = StatusCode::InternalServerError_500;\n      std::string val;\n      auto s = e.what();\n      for (size_t i = 0; s[i]; i++) {\n        switch (s[i]) {\n        case '\\r': val += \"\\\\r\"; break;\n        case '\\n': val += \"\\\\n\"; break;\n        default: val += s[i]; break;\n        }\n      }\n      res.set_header(\"EXCEPTION_WHAT\", val);\n    }\n  } catch (...) {\n    if (exception_handler_) {\n      auto ep = std::current_exception();\n      exception_handler_(req, res, ep);\n      routed = true;\n    } else {\n      res.status = StatusCode::InternalServerError_500;\n      res.set_header(\"EXCEPTION_WHAT\", \"UNKNOWN\");\n    }\n  }\n#endif\n  if (routed) {\n    if (res.status == -1) {\n      res.status = req.ranges.empty() ? StatusCode::OK_200\n                                      : StatusCode::PartialContent_206;\n    }\n\n    // Serve file content by using a content provider\n    if (!res.file_content_path_.empty()) {\n      const auto &path = res.file_content_path_;\n      auto mm = std::make_shared<detail::mmap>(path.c_str());\n      if (!mm->is_open()) {\n        res.body.clear();\n        res.content_length_ = 0;\n        res.content_provider_ = nullptr;\n        res.status = StatusCode::NotFound_404;\n        output_error_log(Error::OpenFile, &req);\n        return write_response(strm, close_connection, req, res);\n      }\n\n      auto content_type = res.file_content_content_type_;\n      if (content_type.empty()) {\n        content_type = detail::find_content_type(\n            path, file_extension_and_mimetype_map_, default_file_mimetype_);\n      }\n\n      res.set_content_provider(\n          mm->size(), content_type,\n          [mm](size_t offset, size_t length, DataSink &sink) -> bool {\n            sink.write(mm->data() + offset, length);\n            return true;\n          });\n    }\n\n    if (detail::range_error(req, res)) {\n      res.body.clear();\n      res.content_length_ = 0;\n      res.content_provider_ = nullptr;\n      res.status = StatusCode::RangeNotSatisfiable_416;\n      return write_response(strm, close_connection, req, res);\n    }\n\n    return write_response_with_content(strm, close_connection, req, res);\n  } else {\n    if (res.status == -1) { res.status = StatusCode::NotFound_404; }\n\n    return write_response(strm, close_connection, req, res);\n  }\n}\n\ninline bool Server::is_valid() const { return true; }\n\ninline bool Server::process_and_close_socket(socket_t sock) {\n  std::string remote_addr;\n  int remote_port = 0;\n  detail::get_remote_ip_and_port(sock, remote_addr, remote_port);\n\n  std::string local_addr;\n  int local_port = 0;\n  detail::get_local_ip_and_port(sock, local_addr, local_port);\n\n  auto ret = detail::process_server_socket(\n      svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,\n      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,\n      write_timeout_usec_,\n      [&](Stream &strm, bool close_connection, bool &connection_closed) {\n        return process_request(strm, remote_addr, remote_port, local_addr,\n                               local_port, close_connection, connection_closed,\n                               nullptr);\n      });\n\n  detail::shutdown_socket(sock);\n  detail::close_socket(sock);\n  return ret;\n}\n\ninline void Server::output_log(const Request &req, const Response &res) const {\n  if (logger_) {\n    std::lock_guard<std::mutex> guard(logger_mutex_);\n    logger_(req, res);\n  }\n}\n\ninline void Server::output_pre_compression_log(const Request &req,\n                                               const Response &res) const {\n  if (pre_compression_logger_) {\n    std::lock_guard<std::mutex> guard(logger_mutex_);\n    pre_compression_logger_(req, res);\n  }\n}\n\ninline void Server::output_error_log(const Error &err,\n                                     const Request *req) const {\n  if (error_logger_) {\n    std::lock_guard<std::mutex> guard(logger_mutex_);\n    error_logger_(err, req);\n  }\n}\n\n// HTTP client implementation\ninline ClientImpl::ClientImpl(const std::string &host)\n    : ClientImpl(host, 80, std::string(), std::string()) {}\n\ninline ClientImpl::ClientImpl(const std::string &host, int port)\n    : ClientImpl(host, port, std::string(), std::string()) {}\n\ninline ClientImpl::ClientImpl(const std::string &host, int port,\n                              const std::string &client_cert_path,\n                              const std::string &client_key_path)\n    : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),\n      host_and_port_(detail::make_host_and_port_string(host_, port, is_ssl())),\n      client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}\n\ninline ClientImpl::~ClientImpl() {\n  // Wait until all the requests in flight are handled.\n  size_t retry_count = 10;\n  while (retry_count-- > 0) {\n    {\n      std::lock_guard<std::mutex> guard(socket_mutex_);\n      if (socket_requests_in_flight_ == 0) { break; }\n    }\n    std::this_thread::sleep_for(std::chrono::milliseconds{1});\n  }\n\n  std::lock_guard<std::mutex> guard(socket_mutex_);\n  shutdown_socket(socket_);\n  close_socket(socket_);\n}\n\ninline bool ClientImpl::is_valid() const { return true; }\n\ninline void ClientImpl::copy_settings(const ClientImpl &rhs) {\n  client_cert_path_ = rhs.client_cert_path_;\n  client_key_path_ = rhs.client_key_path_;\n  connection_timeout_sec_ = rhs.connection_timeout_sec_;\n  read_timeout_sec_ = rhs.read_timeout_sec_;\n  read_timeout_usec_ = rhs.read_timeout_usec_;\n  write_timeout_sec_ = rhs.write_timeout_sec_;\n  write_timeout_usec_ = rhs.write_timeout_usec_;\n  max_timeout_msec_ = rhs.max_timeout_msec_;\n  basic_auth_username_ = rhs.basic_auth_username_;\n  basic_auth_password_ = rhs.basic_auth_password_;\n  bearer_token_auth_token_ = rhs.bearer_token_auth_token_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  digest_auth_username_ = rhs.digest_auth_username_;\n  digest_auth_password_ = rhs.digest_auth_password_;\n#endif\n  keep_alive_ = rhs.keep_alive_;\n  follow_location_ = rhs.follow_location_;\n  path_encode_ = rhs.path_encode_;\n  address_family_ = rhs.address_family_;\n  tcp_nodelay_ = rhs.tcp_nodelay_;\n  ipv6_v6only_ = rhs.ipv6_v6only_;\n  socket_options_ = rhs.socket_options_;\n  compress_ = rhs.compress_;\n  decompress_ = rhs.decompress_;\n  interface_ = rhs.interface_;\n  proxy_host_ = rhs.proxy_host_;\n  proxy_port_ = rhs.proxy_port_;\n  proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;\n  proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;\n  proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;\n  proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;\n#endif\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  ca_cert_file_path_ = rhs.ca_cert_file_path_;\n  ca_cert_dir_path_ = rhs.ca_cert_dir_path_;\n  ca_cert_store_ = rhs.ca_cert_store_;\n#endif\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  server_certificate_verification_ = rhs.server_certificate_verification_;\n  server_hostname_verification_ = rhs.server_hostname_verification_;\n  server_certificate_verifier_ = rhs.server_certificate_verifier_;\n#endif\n  logger_ = rhs.logger_;\n  error_logger_ = rhs.error_logger_;\n}\n\ninline socket_t ClientImpl::create_client_socket(Error &error) const {\n  if (!proxy_host_.empty() && proxy_port_ != -1) {\n    return detail::create_client_socket(\n        proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,\n        ipv6_v6only_, socket_options_, connection_timeout_sec_,\n        connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,\n        write_timeout_sec_, write_timeout_usec_, interface_, error);\n  }\n\n  // Check is custom IP specified for host_\n  std::string ip;\n  auto it = addr_map_.find(host_);\n  if (it != addr_map_.end()) { ip = it->second; }\n\n  return detail::create_client_socket(\n      host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,\n      socket_options_, connection_timeout_sec_, connection_timeout_usec_,\n      read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,\n      write_timeout_usec_, interface_, error);\n}\n\ninline bool ClientImpl::create_and_connect_socket(Socket &socket,\n                                                  Error &error) {\n  auto sock = create_client_socket(error);\n  if (sock == INVALID_SOCKET) { return false; }\n  socket.sock = sock;\n  return true;\n}\n\ninline void ClientImpl::shutdown_ssl(Socket & /*socket*/,\n                                     bool /*shutdown_gracefully*/) {\n  // If there are any requests in flight from threads other than us, then it's\n  // a thread-unsafe race because individual ssl* objects are not thread-safe.\n  assert(socket_requests_in_flight_ == 0 ||\n         socket_requests_are_from_thread_ == std::this_thread::get_id());\n}\n\ninline void ClientImpl::shutdown_socket(Socket &socket) const {\n  if (socket.sock == INVALID_SOCKET) { return; }\n  detail::shutdown_socket(socket.sock);\n}\n\ninline void ClientImpl::close_socket(Socket &socket) {\n  // If there are requests in flight in another thread, usually closing\n  // the socket will be fine and they will simply receive an error when\n  // using the closed socket, but it is still a bug since rarely the OS\n  // may reassign the socket id to be used for a new socket, and then\n  // suddenly they will be operating on a live socket that is different\n  // than the one they intended!\n  assert(socket_requests_in_flight_ == 0 ||\n         socket_requests_are_from_thread_ == std::this_thread::get_id());\n\n  // It is also a bug if this happens while SSL is still active\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  assert(socket.ssl == nullptr);\n#endif\n  if (socket.sock == INVALID_SOCKET) { return; }\n  detail::close_socket(socket.sock);\n  socket.sock = INVALID_SOCKET;\n}\n\ninline bool ClientImpl::read_response_line(Stream &strm, const Request &req,\n                                           Response &res) const {\n  std::array<char, 2048> buf{};\n\n  detail::stream_line_reader line_reader(strm, buf.data(), buf.size());\n\n  if (!line_reader.getline()) { return false; }\n\n#ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR\n  thread_local const std::regex re(\"(HTTP/1\\\\.[01]) (\\\\d{3})(?: (.*?))?\\r?\\n\");\n#else\n  thread_local const std::regex re(\"(HTTP/1\\\\.[01]) (\\\\d{3})(?: (.*?))?\\r\\n\");\n#endif\n\n  std::cmatch m;\n  if (!std::regex_match(line_reader.ptr(), m, re)) {\n    return req.method == \"CONNECT\";\n  }\n  res.version = std::string(m[1]);\n  res.status = std::stoi(std::string(m[2]));\n  res.reason = std::string(m[3]);\n\n  // Ignore '100 Continue'\n  while (res.status == StatusCode::Continue_100) {\n    if (!line_reader.getline()) { return false; } // CRLF\n    if (!line_reader.getline()) { return false; } // next response line\n\n    if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }\n    res.version = std::string(m[1]);\n    res.status = std::stoi(std::string(m[2]));\n    res.reason = std::string(m[3]);\n  }\n\n  return true;\n}\n\ninline bool ClientImpl::send(Request &req, Response &res, Error &error) {\n  std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);\n  auto ret = send_(req, res, error);\n  if (error == Error::SSLPeerCouldBeClosed_) {\n    assert(!ret);\n    ret = send_(req, res, error);\n  }\n  return ret;\n}\n\ninline bool ClientImpl::send_(Request &req, Response &res, Error &error) {\n  {\n    std::lock_guard<std::mutex> guard(socket_mutex_);\n\n    // Set this to false immediately - if it ever gets set to true by the end\n    // of the request, we know another thread instructed us to close the\n    // socket.\n    socket_should_be_closed_when_request_is_done_ = false;\n\n    auto is_alive = false;\n    if (socket_.is_open()) {\n      is_alive = detail::is_socket_alive(socket_.sock);\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n      if (is_alive && is_ssl()) {\n        if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {\n          is_alive = false;\n        }\n      }\n#endif\n\n      if (!is_alive) {\n        // Attempt to avoid sigpipe by shutting down non-gracefully if it\n        // seems like the other side has already closed the connection Also,\n        // there cannot be any requests in flight from other threads since we\n        // locked request_mutex_, so safe to close everything immediately\n        const bool shutdown_gracefully = false;\n        shutdown_ssl(socket_, shutdown_gracefully);\n        shutdown_socket(socket_);\n        close_socket(socket_);\n      }\n    }\n\n    if (!is_alive) {\n      if (!create_and_connect_socket(socket_, error)) {\n        output_error_log(error, &req);\n        return false;\n      }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n      // TODO: refactoring\n      if (is_ssl()) {\n        auto &scli = static_cast<SSLClient &>(*this);\n        if (!proxy_host_.empty() && proxy_port_ != -1) {\n          auto success = false;\n          if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,\n                                       error)) {\n            if (!success) { output_error_log(error, &req); }\n            return success;\n          }\n        }\n\n        if (!scli.initialize_ssl(socket_, error)) {\n          output_error_log(error, &req);\n          return false;\n        }\n      }\n#endif\n    }\n\n    // Mark the current socket as being in use so that it cannot be closed by\n    // anyone else while this request is ongoing, even though we will be\n    // releasing the mutex.\n    if (socket_requests_in_flight_ > 1) {\n      assert(socket_requests_are_from_thread_ == std::this_thread::get_id());\n    }\n    socket_requests_in_flight_ += 1;\n    socket_requests_are_from_thread_ = std::this_thread::get_id();\n  }\n\n  for (const auto &header : default_headers_) {\n    if (req.headers.find(header.first) == req.headers.end()) {\n      req.headers.insert(header);\n    }\n  }\n\n  auto ret = false;\n  auto close_connection = !keep_alive_;\n\n  auto se = detail::scope_exit([&]() {\n    // Briefly lock mutex in order to mark that a request is no longer ongoing\n    std::lock_guard<std::mutex> guard(socket_mutex_);\n    socket_requests_in_flight_ -= 1;\n    if (socket_requests_in_flight_ <= 0) {\n      assert(socket_requests_in_flight_ == 0);\n      socket_requests_are_from_thread_ = std::thread::id();\n    }\n\n    if (socket_should_be_closed_when_request_is_done_ || close_connection ||\n        !ret) {\n      shutdown_ssl(socket_, true);\n      shutdown_socket(socket_);\n      close_socket(socket_);\n    }\n  });\n\n  ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {\n    return handle_request(strm, req, res, close_connection, error);\n  });\n\n  if (!ret) {\n    if (error == Error::Success) {\n      error = Error::Unknown;\n      output_error_log(error, &req);\n    }\n  }\n\n  return ret;\n}\n\ninline Result ClientImpl::send(const Request &req) {\n  auto req2 = req;\n  return send_(std::move(req2));\n}\n\ninline Result ClientImpl::send_(Request &&req) {\n  auto res = detail::make_unique<Response>();\n  auto error = Error::Success;\n  auto ret = send(req, *res, error);\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers),\n                last_ssl_error_, last_openssl_error_};\n#else\n  return Result{ret ? std::move(res) : nullptr, error, std::move(req.headers)};\n#endif\n}\n\ninline bool ClientImpl::handle_request(Stream &strm, Request &req,\n                                       Response &res, bool close_connection,\n                                       Error &error) {\n  if (req.path.empty()) {\n    error = Error::Connection;\n    output_error_log(error, &req);\n    return false;\n  }\n\n  auto req_save = req;\n\n  bool ret;\n\n  if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {\n    auto req2 = req;\n    req2.path = \"http://\" + host_and_port_ + req.path;\n    ret = process_request(strm, req2, res, close_connection, error);\n    req = req2;\n    req.path = req_save.path;\n  } else {\n    ret = process_request(strm, req, res, close_connection, error);\n  }\n\n  if (!ret) { return false; }\n\n  if (res.get_header_value(\"Connection\") == \"close\" ||\n      (res.version == \"HTTP/1.0\" && res.reason != \"Connection established\")) {\n    // TODO this requires a not-entirely-obvious chain of calls to be correct\n    // for this to be safe.\n\n    // This is safe to call because handle_request is only called by send_\n    // which locks the request mutex during the process. It would be a bug\n    // to call it from a different thread since it's a thread-safety issue\n    // to do these things to the socket if another thread is using the socket.\n    std::lock_guard<std::mutex> guard(socket_mutex_);\n    shutdown_ssl(socket_, true);\n    shutdown_socket(socket_);\n    close_socket(socket_);\n  }\n\n  if (300 < res.status && res.status < 400 && follow_location_) {\n    req = req_save;\n    ret = redirect(req, res, error);\n  }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  if ((res.status == StatusCode::Unauthorized_401 ||\n       res.status == StatusCode::ProxyAuthenticationRequired_407) &&\n      req.authorization_count_ < 5) {\n    auto is_proxy = res.status == StatusCode::ProxyAuthenticationRequired_407;\n    const auto &username =\n        is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;\n    const auto &password =\n        is_proxy ? proxy_digest_auth_password_ : digest_auth_password_;\n\n    if (!username.empty() && !password.empty()) {\n      std::map<std::string, std::string> auth;\n      if (detail::parse_www_authenticate(res, auth, is_proxy)) {\n        Request new_req = req;\n        new_req.authorization_count_ += 1;\n        new_req.headers.erase(is_proxy ? \"Proxy-Authorization\"\n                                       : \"Authorization\");\n        new_req.headers.insert(detail::make_digest_authentication_header(\n            req, auth, new_req.authorization_count_, detail::random_string(10),\n            username, password, is_proxy));\n\n        Response new_res;\n\n        ret = send(new_req, new_res, error);\n        if (ret) { res = new_res; }\n      }\n    }\n  }\n#endif\n\n  return ret;\n}\n\ninline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {\n  if (req.redirect_count_ == 0) {\n    error = Error::ExceedRedirectCount;\n    output_error_log(error, &req);\n    return false;\n  }\n\n  auto location = res.get_header_value(\"location\");\n  if (location.empty()) { return false; }\n\n  thread_local const std::regex re(\n      R\"((?:(https?):)?(?://(?:\\[([a-fA-F\\d:]+)\\]|([^:/?#]+))(?::(\\d+))?)?([^?#]*)(\\?[^#]*)?(?:#.*)?)\");\n\n  std::smatch m;\n  if (!std::regex_match(location, m, re)) { return false; }\n\n  auto scheme = is_ssl() ? \"https\" : \"http\";\n\n  auto next_scheme = m[1].str();\n  auto next_host = m[2].str();\n  if (next_host.empty()) { next_host = m[3].str(); }\n  auto port_str = m[4].str();\n  auto next_path = m[5].str();\n  auto next_query = m[6].str();\n\n  auto next_port = port_;\n  if (!port_str.empty()) {\n    next_port = std::stoi(port_str);\n  } else if (!next_scheme.empty()) {\n    next_port = next_scheme == \"https\" ? 443 : 80;\n  }\n\n  if (next_scheme.empty()) { next_scheme = scheme; }\n  if (next_host.empty()) { next_host = host_; }\n  if (next_path.empty()) { next_path = \"/\"; }\n\n  auto path = decode_query_component(next_path, true) + next_query;\n\n  // Same host redirect - use current client\n  if (next_scheme == scheme && next_host == host_ && next_port == port_) {\n    return detail::redirect(*this, req, res, path, location, error);\n  }\n\n  // Cross-host/scheme redirect - create new client with robust setup\n  return create_redirect_client(next_scheme, next_host, next_port, req, res,\n                                path, location, error);\n}\n\n// New method for robust redirect client creation\ninline bool ClientImpl::create_redirect_client(\n    const std::string &scheme, const std::string &host, int port, Request &req,\n    Response &res, const std::string &path, const std::string &location,\n    Error &error) {\n  // Determine if we need SSL\n  auto need_ssl = (scheme == \"https\");\n\n  // Clean up request headers that are host/client specific\n  // Remove headers that should not be carried over to new host\n  auto headers_to_remove =\n      std::vector<std::string>{\"Host\", \"Proxy-Authorization\", \"Authorization\"};\n\n  for (const auto &header_name : headers_to_remove) {\n    auto it = req.headers.find(header_name);\n    while (it != req.headers.end()) {\n      it = req.headers.erase(it);\n      it = req.headers.find(header_name);\n    }\n  }\n\n  // Create appropriate client type and handle redirect\n  if (need_ssl) {\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    // Create SSL client for HTTPS redirect\n    SSLClient redirect_client(host, port);\n\n    // Setup basic client configuration first\n    setup_redirect_client(redirect_client);\n\n    // SSL-specific configuration for proxy environments\n    if (!proxy_host_.empty() && proxy_port_ != -1) {\n      // Critical: Disable SSL verification for proxy environments\n      redirect_client.enable_server_certificate_verification(false);\n      redirect_client.enable_server_hostname_verification(false);\n    } else {\n      // For direct SSL connections, copy SSL verification settings\n      redirect_client.enable_server_certificate_verification(\n          server_certificate_verification_);\n      redirect_client.enable_server_hostname_verification(\n          server_hostname_verification_);\n    }\n\n    // Handle CA certificate store and paths if available\n    if (ca_cert_store_ && X509_STORE_up_ref(ca_cert_store_)) {\n      redirect_client.set_ca_cert_store(ca_cert_store_);\n    }\n    if (!ca_cert_file_path_.empty()) {\n      redirect_client.set_ca_cert_path(ca_cert_file_path_, ca_cert_dir_path_);\n    }\n\n    // Client certificates are set through constructor for SSLClient\n    // NOTE: SSLClient constructor already takes client_cert_path and\n    // client_key_path so we need to create it properly if client certs are\n    // needed\n\n    // Execute the redirect\n    return detail::redirect(redirect_client, req, res, path, location, error);\n#else\n    // SSL not supported - set appropriate error\n    error = Error::SSLConnection;\n    output_error_log(error, &req);\n    return false;\n#endif\n  } else {\n    // HTTP redirect\n    ClientImpl redirect_client(host, port);\n\n    // Setup client with robust configuration\n    setup_redirect_client(redirect_client);\n\n    // Execute the redirect\n    return detail::redirect(redirect_client, req, res, path, location, error);\n  }\n}\n\n// New method for robust client setup (based on basic_manual_redirect.cpp\n// logic)\ntemplate <typename ClientType>\ninline void ClientImpl::setup_redirect_client(ClientType &client) {\n  // Copy basic settings first\n  client.set_connection_timeout(connection_timeout_sec_);\n  client.set_read_timeout(read_timeout_sec_, read_timeout_usec_);\n  client.set_write_timeout(write_timeout_sec_, write_timeout_usec_);\n  client.set_keep_alive(keep_alive_);\n  client.set_follow_location(\n      true); // Enable redirects to handle multi-step redirects\n  client.set_path_encode(path_encode_);\n  client.set_compress(compress_);\n  client.set_decompress(decompress_);\n\n  // Copy authentication settings BEFORE proxy setup\n  if (!basic_auth_username_.empty()) {\n    client.set_basic_auth(basic_auth_username_, basic_auth_password_);\n  }\n  if (!bearer_token_auth_token_.empty()) {\n    client.set_bearer_token_auth(bearer_token_auth_token_);\n  }\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  if (!digest_auth_username_.empty()) {\n    client.set_digest_auth(digest_auth_username_, digest_auth_password_);\n  }\n#endif\n\n  // Setup proxy configuration (CRITICAL ORDER - proxy must be set\n  // before proxy auth)\n  if (!proxy_host_.empty() && proxy_port_ != -1) {\n    // First set proxy host and port\n    client.set_proxy(proxy_host_, proxy_port_);\n\n    // Then set proxy authentication (order matters!)\n    if (!proxy_basic_auth_username_.empty()) {\n      client.set_proxy_basic_auth(proxy_basic_auth_username_,\n                                  proxy_basic_auth_password_);\n    }\n    if (!proxy_bearer_token_auth_token_.empty()) {\n      client.set_proxy_bearer_token_auth(proxy_bearer_token_auth_token_);\n    }\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    if (!proxy_digest_auth_username_.empty()) {\n      client.set_proxy_digest_auth(proxy_digest_auth_username_,\n                                   proxy_digest_auth_password_);\n    }\n#endif\n  }\n\n  // Copy network and socket settings\n  client.set_address_family(address_family_);\n  client.set_tcp_nodelay(tcp_nodelay_);\n  client.set_ipv6_v6only(ipv6_v6only_);\n  if (socket_options_) { client.set_socket_options(socket_options_); }\n  if (!interface_.empty()) { client.set_interface(interface_); }\n\n  // Copy logging and headers\n  if (logger_) { client.set_logger(logger_); }\n  if (error_logger_) { client.set_error_logger(error_logger_); }\n\n  // NOTE: DO NOT copy default_headers_ as they may contain stale Host headers\n  // Each new client should generate its own headers based on its target host\n}\n\ninline bool ClientImpl::write_content_with_provider(Stream &strm,\n                                                    const Request &req,\n                                                    Error &error) const {\n  auto is_shutting_down = []() { return false; };\n\n  if (req.is_chunked_content_provider_) {\n    // TODO: Brotli support\n    std::unique_ptr<detail::compressor> compressor;\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n    if (compress_) {\n      compressor = detail::make_unique<detail::gzip_compressor>();\n    } else\n#endif\n    {\n      compressor = detail::make_unique<detail::nocompressor>();\n    }\n\n    return detail::write_content_chunked(strm, req.content_provider_,\n                                         is_shutting_down, *compressor, error);\n  } else {\n    return detail::write_content_with_progress(\n        strm, req.content_provider_, 0, req.content_length_, is_shutting_down,\n        req.upload_progress, error);\n  }\n}\n\ninline bool ClientImpl::write_request(Stream &strm, Request &req,\n                                      bool close_connection, Error &error) {\n  // Prepare additional headers\n  if (close_connection) {\n    if (!req.has_header(\"Connection\")) {\n      req.set_header(\"Connection\", \"close\");\n    }\n  }\n\n  if (!req.has_header(\"Host\")) {\n    // For Unix socket connections, use \"localhost\" as Host header (similar to\n    // curl behavior)\n    if (address_family_ == AF_UNIX) {\n      req.set_header(\"Host\", \"localhost\");\n    } else {\n      req.set_header(\"Host\", host_and_port_);\n    }\n  }\n\n  if (!req.has_header(\"Accept\")) { req.set_header(\"Accept\", \"*/*\"); }\n\n  if (!req.content_receiver) {\n    if (!req.has_header(\"Accept-Encoding\")) {\n      std::string accept_encoding;\n#ifdef CPPHTTPLIB_BROTLI_SUPPORT\n      accept_encoding = \"br\";\n#endif\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n      if (!accept_encoding.empty()) { accept_encoding += \", \"; }\n      accept_encoding += \"gzip, deflate\";\n#endif\n#ifdef CPPHTTPLIB_ZSTD_SUPPORT\n      if (!accept_encoding.empty()) { accept_encoding += \", \"; }\n      accept_encoding += \"zstd\";\n#endif\n      req.set_header(\"Accept-Encoding\", accept_encoding);\n    }\n\n#ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT\n    if (!req.has_header(\"User-Agent\")) {\n      auto agent = std::string(\"cpp-httplib/\") + CPPHTTPLIB_VERSION;\n      req.set_header(\"User-Agent\", agent);\n    }\n#endif\n  };\n\n  if (req.body.empty()) {\n    if (req.content_provider_) {\n      if (!req.is_chunked_content_provider_) {\n        if (!req.has_header(\"Content-Length\")) {\n          auto length = std::to_string(req.content_length_);\n          req.set_header(\"Content-Length\", length);\n        }\n      }\n    } else {\n      if (req.method == \"POST\" || req.method == \"PUT\" ||\n          req.method == \"PATCH\") {\n        req.set_header(\"Content-Length\", \"0\");\n      }\n    }\n  } else {\n    if (!req.has_header(\"Content-Type\")) {\n      req.set_header(\"Content-Type\", \"text/plain\");\n    }\n\n    if (!req.has_header(\"Content-Length\")) {\n      auto length = std::to_string(req.body.size());\n      req.set_header(\"Content-Length\", length);\n    }\n  }\n\n  if (!basic_auth_password_.empty() || !basic_auth_username_.empty()) {\n    if (!req.has_header(\"Authorization\")) {\n      req.headers.insert(make_basic_authentication_header(\n          basic_auth_username_, basic_auth_password_, false));\n    }\n  }\n\n  if (!proxy_basic_auth_username_.empty() &&\n      !proxy_basic_auth_password_.empty()) {\n    if (!req.has_header(\"Proxy-Authorization\")) {\n      req.headers.insert(make_basic_authentication_header(\n          proxy_basic_auth_username_, proxy_basic_auth_password_, true));\n    }\n  }\n\n  if (!bearer_token_auth_token_.empty()) {\n    if (!req.has_header(\"Authorization\")) {\n      req.headers.insert(make_bearer_token_authentication_header(\n          bearer_token_auth_token_, false));\n    }\n  }\n\n  if (!proxy_bearer_token_auth_token_.empty()) {\n    if (!req.has_header(\"Proxy-Authorization\")) {\n      req.headers.insert(make_bearer_token_authentication_header(\n          proxy_bearer_token_auth_token_, true));\n    }\n  }\n\n  // Request line and headers\n  {\n    detail::BufferStream bstrm;\n\n    // Extract path and query from req.path\n    std::string path_part, query_part;\n    auto query_pos = req.path.find('?');\n    if (query_pos != std::string::npos) {\n      path_part = req.path.substr(0, query_pos);\n      query_part = req.path.substr(query_pos + 1);\n    } else {\n      path_part = req.path;\n      query_part = \"\";\n    }\n\n    // Encode path and query\n    auto path_with_query =\n        path_encode_ ? detail::encode_path(path_part) : path_part;\n\n    detail::parse_query_text(query_part, req.params);\n    if (!req.params.empty()) {\n      path_with_query = append_query_params(path_with_query, req.params);\n    }\n\n    // Write request line and headers\n    detail::write_request_line(bstrm, req.method, path_with_query);\n    header_writer_(bstrm, req.headers);\n\n    // Flush buffer\n    auto &data = bstrm.get_buffer();\n    if (!detail::write_data(strm, data.data(), data.size())) {\n      error = Error::Write;\n      output_error_log(error, &req);\n      return false;\n    }\n  }\n\n  // Body\n  if (req.body.empty()) {\n    return write_content_with_provider(strm, req, error);\n  }\n\n  if (req.upload_progress) {\n    auto body_size = req.body.size();\n    size_t written = 0;\n    auto data = req.body.data();\n\n    while (written < body_size) {\n      size_t to_write = (std::min)(CPPHTTPLIB_SEND_BUFSIZ, body_size - written);\n      if (!detail::write_data(strm, data + written, to_write)) {\n        error = Error::Write;\n        output_error_log(error, &req);\n        return false;\n      }\n      written += to_write;\n\n      if (!req.upload_progress(written, body_size)) {\n        error = Error::Canceled;\n        output_error_log(error, &req);\n        return false;\n      }\n    }\n  } else {\n    if (!detail::write_data(strm, req.body.data(), req.body.size())) {\n      error = Error::Write;\n      output_error_log(error, &req);\n      return false;\n    }\n  }\n\n  return true;\n}\n\ninline std::unique_ptr<Response> ClientImpl::send_with_content_provider(\n    Request &req, const char *body, size_t content_length,\n    ContentProvider content_provider,\n    ContentProviderWithoutLength content_provider_without_length,\n    const std::string &content_type, Error &error) {\n  if (!content_type.empty()) { req.set_header(\"Content-Type\", content_type); }\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n  if (compress_) { req.set_header(\"Content-Encoding\", \"gzip\"); }\n#endif\n\n#ifdef CPPHTTPLIB_ZLIB_SUPPORT\n  if (compress_ && !content_provider_without_length) {\n    // TODO: Brotli support\n    detail::gzip_compressor compressor;\n\n    if (content_provider) {\n      auto ok = true;\n      size_t offset = 0;\n      DataSink data_sink;\n\n      data_sink.write = [&](const char *data, size_t data_len) -> bool {\n        if (ok) {\n          auto last = offset + data_len == content_length;\n\n          auto ret = compressor.compress(\n              data, data_len, last,\n              [&](const char *compressed_data, size_t compressed_data_len) {\n                req.body.append(compressed_data, compressed_data_len);\n                return true;\n              });\n\n          if (ret) {\n            offset += data_len;\n          } else {\n            ok = false;\n          }\n        }\n        return ok;\n      };\n\n      while (ok && offset < content_length) {\n        if (!content_provider(offset, content_length - offset, data_sink)) {\n          error = Error::Canceled;\n          output_error_log(error, &req);\n          return nullptr;\n        }\n      }\n    } else {\n      if (!compressor.compress(body, content_length, true,\n                               [&](const char *data, size_t data_len) {\n                                 req.body.append(data, data_len);\n                                 return true;\n                               })) {\n        error = Error::Compression;\n        output_error_log(error, &req);\n        return nullptr;\n      }\n    }\n  } else\n#endif\n  {\n    if (content_provider) {\n      req.content_length_ = content_length;\n      req.content_provider_ = std::move(content_provider);\n      req.is_chunked_content_provider_ = false;\n    } else if (content_provider_without_length) {\n      req.content_length_ = 0;\n      req.content_provider_ = detail::ContentProviderAdapter(\n          std::move(content_provider_without_length));\n      req.is_chunked_content_provider_ = true;\n      req.set_header(\"Transfer-Encoding\", \"chunked\");\n    } else {\n      req.body.assign(body, content_length);\n    }\n  }\n\n  auto res = detail::make_unique<Response>();\n  return send(req, *res, error) ? std::move(res) : nullptr;\n}\n\ninline Result ClientImpl::send_with_content_provider(\n    const std::string &method, const std::string &path, const Headers &headers,\n    const char *body, size_t content_length, ContentProvider content_provider,\n    ContentProviderWithoutLength content_provider_without_length,\n    const std::string &content_type, UploadProgress progress) {\n  Request req;\n  req.method = method;\n  req.headers = headers;\n  req.path = path;\n  req.upload_progress = std::move(progress);\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  auto error = Error::Success;\n\n  auto res = send_with_content_provider(\n      req, body, content_length, std::move(content_provider),\n      std::move(content_provider_without_length), content_type, error);\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  return Result{std::move(res), error, std::move(req.headers), last_ssl_error_,\n                last_openssl_error_};\n#else\n  return Result{std::move(res), error, std::move(req.headers)};\n#endif\n}\n\ninline void ClientImpl::output_log(const Request &req,\n                                   const Response &res) const {\n  if (logger_) {\n    std::lock_guard<std::mutex> guard(logger_mutex_);\n    logger_(req, res);\n  }\n}\n\ninline void ClientImpl::output_error_log(const Error &err,\n                                         const Request *req) const {\n  if (error_logger_) {\n    std::lock_guard<std::mutex> guard(logger_mutex_);\n    error_logger_(err, req);\n  }\n}\n\ninline bool ClientImpl::process_request(Stream &strm, Request &req,\n                                        Response &res, bool close_connection,\n                                        Error &error) {\n  // Send request\n  if (!write_request(strm, req, close_connection, error)) { return false; }\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n  if (is_ssl()) {\n    auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;\n    if (!is_proxy_enabled) {\n      if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {\n        error = Error::SSLPeerCouldBeClosed_;\n        output_error_log(error, &req);\n        return false;\n      }\n    }\n  }\n#endif\n\n  // Receive response and headers\n  if (!read_response_line(strm, req, res) ||\n      !detail::read_headers(strm, res.headers)) {\n    error = Error::Read;\n    output_error_log(error, &req);\n    return false;\n  }\n\n  // Body\n  if ((res.status != StatusCode::NoContent_204) && req.method != \"HEAD\" &&\n      req.method != \"CONNECT\") {\n    auto redirect = 300 < res.status && res.status < 400 &&\n                    res.status != StatusCode::NotModified_304 &&\n                    follow_location_;\n\n    if (req.response_handler && !redirect) {\n      if (!req.response_handler(res)) {\n        error = Error::Canceled;\n        output_error_log(error, &req);\n        return false;\n      }\n    }\n\n    auto out =\n        req.content_receiver\n            ? static_cast<ContentReceiverWithProgress>(\n                  [&](const char *buf, size_t n, size_t off, size_t len) {\n                    if (redirect) { return true; }\n                    auto ret = req.content_receiver(buf, n, off, len);\n                    if (!ret) {\n                      error = Error::Canceled;\n                      output_error_log(error, &req);\n                    }\n                    return ret;\n                  })\n            : static_cast<ContentReceiverWithProgress>(\n                  [&](const char *buf, size_t n, size_t /*off*/,\n                      size_t /*len*/) {\n                    assert(res.body.size() + n <= res.body.max_size());\n                    res.body.append(buf, n);\n                    return true;\n                  });\n\n    auto progress = [&](size_t current, size_t total) {\n      if (!req.download_progress || redirect) { return true; }\n      auto ret = req.download_progress(current, total);\n      if (!ret) {\n        error = Error::Canceled;\n        output_error_log(error, &req);\n      }\n      return ret;\n    };\n\n    if (res.has_header(\"Content-Length\")) {\n      if (!req.content_receiver) {\n        auto len = res.get_header_value_u64(\"Content-Length\");\n        if (len > res.body.max_size()) {\n          error = Error::Read;\n          output_error_log(error, &req);\n          return false;\n        }\n        res.body.reserve(static_cast<size_t>(len));\n      }\n    }\n\n    if (res.status != StatusCode::NotModified_304) {\n      int dummy_status;\n      if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),\n                                dummy_status, std::move(progress),\n                                std::move(out), decompress_)) {\n        if (error != Error::Canceled) { error = Error::Read; }\n        output_error_log(error, &req);\n        return false;\n      }\n    }\n  }\n\n  // Log\n  output_log(req, res);\n\n  return true;\n}\n\ninline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(\n    const std::string &boundary, const UploadFormDataItems &items,\n    const FormDataProviderItems &provider_items) const {\n  size_t cur_item = 0;\n  size_t cur_start = 0;\n  // cur_item and cur_start are copied to within the std::function and\n  // maintain state between successive calls\n  return [&, cur_item, cur_start](size_t offset,\n                                  DataSink &sink) mutable -> bool {\n    if (!offset && !items.empty()) {\n      sink.os << detail::serialize_multipart_formdata(items, boundary, false);\n      return true;\n    } else if (cur_item < provider_items.size()) {\n      if (!cur_start) {\n        const auto &begin = detail::serialize_multipart_formdata_item_begin(\n            provider_items[cur_item], boundary);\n        offset += begin.size();\n        cur_start = offset;\n        sink.os << begin;\n      }\n\n      DataSink cur_sink;\n      auto has_data = true;\n      cur_sink.write = sink.write;\n      cur_sink.done = [&]() { has_data = false; };\n\n      if (!provider_items[cur_item].provider(offset - cur_start, cur_sink)) {\n        return false;\n      }\n\n      if (!has_data) {\n        sink.os << detail::serialize_multipart_formdata_item_end();\n        cur_item++;\n        cur_start = 0;\n      }\n      return true;\n    } else {\n      sink.os << detail::serialize_multipart_formdata_finish(boundary);\n      sink.done();\n      return true;\n    }\n  };\n}\n\ninline bool ClientImpl::process_socket(\n    const Socket &socket,\n    std::chrono::time_point<std::chrono::steady_clock> start_time,\n    std::function<bool(Stream &strm)> callback) {\n  return detail::process_client_socket(\n      socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,\n      write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));\n}\n\ninline bool ClientImpl::is_ssl() const { return false; }\n\ninline Result ClientImpl::Get(const std::string &path,\n                              DownloadProgress progress) {\n  return Get(path, Headers(), std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Params &params,\n                              const Headers &headers,\n                              DownloadProgress progress) {\n  if (params.empty()) { return Get(path, headers); }\n\n  std::string path_with_query = append_query_params(path, params);\n  return Get(path_with_query, headers, std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers,\n                              DownloadProgress progress) {\n  Request req;\n  req.method = \"GET\";\n  req.path = path;\n  req.headers = headers;\n  req.download_progress = std::move(progress);\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Get(const std::string &path,\n                              ContentReceiver content_receiver,\n                              DownloadProgress progress) {\n  return Get(path, Headers(), nullptr, std::move(content_receiver),\n             std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers,\n                              ContentReceiver content_receiver,\n                              DownloadProgress progress) {\n  return Get(path, headers, nullptr, std::move(content_receiver),\n             std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path,\n                              ResponseHandler response_handler,\n                              ContentReceiver content_receiver,\n                              DownloadProgress progress) {\n  return Get(path, Headers(), std::move(response_handler),\n             std::move(content_receiver), std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Headers &headers,\n                              ResponseHandler response_handler,\n                              ContentReceiver content_receiver,\n                              DownloadProgress progress) {\n  Request req;\n  req.method = \"GET\";\n  req.path = path;\n  req.headers = headers;\n  req.response_handler = std::move(response_handler);\n  req.content_receiver =\n      [content_receiver](const char *data, size_t data_length,\n                         size_t /*offset*/, size_t /*total_length*/) {\n        return content_receiver(data, data_length);\n      };\n  req.download_progress = std::move(progress);\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Params &params,\n                              const Headers &headers,\n                              ContentReceiver content_receiver,\n                              DownloadProgress progress) {\n  return Get(path, params, headers, nullptr, std::move(content_receiver),\n             std::move(progress));\n}\n\ninline Result ClientImpl::Get(const std::string &path, const Params &params,\n                              const Headers &headers,\n                              ResponseHandler response_handler,\n                              ContentReceiver content_receiver,\n                              DownloadProgress progress) {\n  if (params.empty()) {\n    return Get(path, headers, std::move(response_handler),\n               std::move(content_receiver), std::move(progress));\n  }\n\n  std::string path_with_query = append_query_params(path, params);\n  return Get(path_with_query, headers, std::move(response_handler),\n             std::move(content_receiver), std::move(progress));\n}\n\ninline Result ClientImpl::Head(const std::string &path) {\n  return Head(path, Headers());\n}\n\ninline Result ClientImpl::Head(const std::string &path,\n                               const Headers &headers) {\n  Request req;\n  req.method = \"HEAD\";\n  req.headers = headers;\n  req.path = path;\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Post(const std::string &path) {\n  return Post(path, std::string(), std::string());\n}\n\ninline Result ClientImpl::Post(const std::string &path,\n                               const Headers &headers) {\n  return Post(path, headers, nullptr, 0, std::string());\n}\n\ninline Result ClientImpl::Post(const std::string &path, const char *body,\n                               size_t content_length,\n                               const std::string &content_type,\n                               UploadProgress progress) {\n  return Post(path, Headers(), body, content_length, content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const std::string &body,\n                               const std::string &content_type,\n                               UploadProgress progress) {\n  return Post(path, Headers(), body, content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Params &params) {\n  return Post(path, Headers(), params);\n}\n\ninline Result ClientImpl::Post(const std::string &path, size_t content_length,\n                               ContentProvider content_provider,\n                               const std::string &content_type,\n                               UploadProgress progress) {\n  return Post(path, Headers(), content_length, std::move(content_provider),\n              content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path,\n                               ContentProviderWithoutLength content_provider,\n                               const std::string &content_type,\n                               UploadProgress progress) {\n  return Post(path, Headers(), std::move(content_provider), content_type,\n              progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const Params &params) {\n  auto query = detail::params_to_query_str(params);\n  return Post(path, headers, query, \"application/x-www-form-urlencoded\");\n}\n\ninline Result ClientImpl::Post(const std::string &path,\n                               const UploadFormDataItems &items,\n                               UploadProgress progress) {\n  return Post(path, Headers(), items, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const UploadFormDataItems &items,\n                               UploadProgress progress) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Post(path, headers, body, content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const UploadFormDataItems &items,\n                               const std::string &boundary,\n                               UploadProgress progress) {\n  if (!detail::is_multipart_boundary_chars_valid(boundary)) {\n    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};\n  }\n\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Post(path, headers, body, content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const char *body, size_t content_length,\n                               const std::string &content_type,\n                               UploadProgress progress) {\n  return send_with_content_provider(\"POST\", path, headers, body, content_length,\n                                    nullptr, nullptr, content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const std::string &body,\n                               const std::string &content_type,\n                               UploadProgress progress) {\n  return send_with_content_provider(\"POST\", path, headers, body.data(),\n                                    body.size(), nullptr, nullptr, content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               size_t content_length,\n                               ContentProvider content_provider,\n                               const std::string &content_type,\n                               UploadProgress progress) {\n  return send_with_content_provider(\"POST\", path, headers, nullptr,\n                                    content_length, std::move(content_provider),\n                                    nullptr, content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               ContentProviderWithoutLength content_provider,\n                               const std::string &content_type,\n                               UploadProgress progress) {\n  return send_with_content_provider(\"POST\", path, headers, nullptr, 0, nullptr,\n                                    std::move(content_provider), content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const UploadFormDataItems &items,\n                               const FormDataProviderItems &provider_items,\n                               UploadProgress progress) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  return send_with_content_provider(\n      \"POST\", path, headers, nullptr, 0, nullptr,\n      get_multipart_content_provider(boundary, items, provider_items),\n      content_type, progress);\n}\n\ninline Result ClientImpl::Post(const std::string &path, const Headers &headers,\n                               const std::string &body,\n                               const std::string &content_type,\n                               ContentReceiver content_receiver,\n                               DownloadProgress progress) {\n  Request req;\n  req.method = \"POST\";\n  req.path = path;\n  req.headers = headers;\n  req.body = body;\n  req.content_receiver =\n      [content_receiver](const char *data, size_t data_length,\n                         size_t /*offset*/, size_t /*total_length*/) {\n        return content_receiver(data, data_length);\n      };\n  req.download_progress = std::move(progress);\n\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  if (!content_type.empty()) { req.set_header(\"Content-Type\", content_type); }\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Put(const std::string &path) {\n  return Put(path, std::string(), std::string());\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers) {\n  return Put(path, headers, nullptr, 0, std::string());\n}\n\ninline Result ClientImpl::Put(const std::string &path, const char *body,\n                              size_t content_length,\n                              const std::string &content_type,\n                              UploadProgress progress) {\n  return Put(path, Headers(), body, content_length, content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const std::string &body,\n                              const std::string &content_type,\n                              UploadProgress progress) {\n  return Put(path, Headers(), body, content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Params &params) {\n  return Put(path, Headers(), params);\n}\n\ninline Result ClientImpl::Put(const std::string &path, size_t content_length,\n                              ContentProvider content_provider,\n                              const std::string &content_type,\n                              UploadProgress progress) {\n  return Put(path, Headers(), content_length, std::move(content_provider),\n             content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path,\n                              ContentProviderWithoutLength content_provider,\n                              const std::string &content_type,\n                              UploadProgress progress) {\n  return Put(path, Headers(), std::move(content_provider), content_type,\n             progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const Params &params) {\n  auto query = detail::params_to_query_str(params);\n  return Put(path, headers, query, \"application/x-www-form-urlencoded\");\n}\n\ninline Result ClientImpl::Put(const std::string &path,\n                              const UploadFormDataItems &items,\n                              UploadProgress progress) {\n  return Put(path, Headers(), items, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const UploadFormDataItems &items,\n                              UploadProgress progress) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Put(path, headers, body, content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const UploadFormDataItems &items,\n                              const std::string &boundary,\n                              UploadProgress progress) {\n  if (!detail::is_multipart_boundary_chars_valid(boundary)) {\n    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};\n  }\n\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Put(path, headers, body, content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const char *body, size_t content_length,\n                              const std::string &content_type,\n                              UploadProgress progress) {\n  return send_with_content_provider(\"PUT\", path, headers, body, content_length,\n                                    nullptr, nullptr, content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const std::string &body,\n                              const std::string &content_type,\n                              UploadProgress progress) {\n  return send_with_content_provider(\"PUT\", path, headers, body.data(),\n                                    body.size(), nullptr, nullptr, content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              size_t content_length,\n                              ContentProvider content_provider,\n                              const std::string &content_type,\n                              UploadProgress progress) {\n  return send_with_content_provider(\"PUT\", path, headers, nullptr,\n                                    content_length, std::move(content_provider),\n                                    nullptr, content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              ContentProviderWithoutLength content_provider,\n                              const std::string &content_type,\n                              UploadProgress progress) {\n  return send_with_content_provider(\"PUT\", path, headers, nullptr, 0, nullptr,\n                                    std::move(content_provider), content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const UploadFormDataItems &items,\n                              const FormDataProviderItems &provider_items,\n                              UploadProgress progress) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  return send_with_content_provider(\n      \"PUT\", path, headers, nullptr, 0, nullptr,\n      get_multipart_content_provider(boundary, items, provider_items),\n      content_type, progress);\n}\n\ninline Result ClientImpl::Put(const std::string &path, const Headers &headers,\n                              const std::string &body,\n                              const std::string &content_type,\n                              ContentReceiver content_receiver,\n                              DownloadProgress progress) {\n  Request req;\n  req.method = \"PUT\";\n  req.path = path;\n  req.headers = headers;\n  req.body = body;\n  req.content_receiver =\n      [content_receiver](const char *data, size_t data_length,\n                         size_t /*offset*/, size_t /*total_length*/) {\n        return content_receiver(data, data_length);\n      };\n  req.download_progress = std::move(progress);\n\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  if (!content_type.empty()) { req.set_header(\"Content-Type\", content_type); }\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Patch(const std::string &path) {\n  return Patch(path, std::string(), std::string());\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                UploadProgress progress) {\n  return Patch(path, headers, nullptr, 0, std::string(), progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const char *body,\n                                size_t content_length,\n                                const std::string &content_type,\n                                UploadProgress progress) {\n  return Patch(path, Headers(), body, content_length, content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path,\n                                const std::string &body,\n                                const std::string &content_type,\n                                UploadProgress progress) {\n  return Patch(path, Headers(), body, content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Params &params) {\n  return Patch(path, Headers(), params);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, size_t content_length,\n                                ContentProvider content_provider,\n                                const std::string &content_type,\n                                UploadProgress progress) {\n  return Patch(path, Headers(), content_length, std::move(content_provider),\n               content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path,\n                                ContentProviderWithoutLength content_provider,\n                                const std::string &content_type,\n                                UploadProgress progress) {\n  return Patch(path, Headers(), std::move(content_provider), content_type,\n               progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const Params &params) {\n  auto query = detail::params_to_query_str(params);\n  return Patch(path, headers, query, \"application/x-www-form-urlencoded\");\n}\n\ninline Result ClientImpl::Patch(const std::string &path,\n                                const UploadFormDataItems &items,\n                                UploadProgress progress) {\n  return Patch(path, Headers(), items, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const UploadFormDataItems &items,\n                                UploadProgress progress) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Patch(path, headers, body, content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const UploadFormDataItems &items,\n                                const std::string &boundary,\n                                UploadProgress progress) {\n  if (!detail::is_multipart_boundary_chars_valid(boundary)) {\n    return Result{nullptr, Error::UnsupportedMultipartBoundaryChars};\n  }\n\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  const auto &body = detail::serialize_multipart_formdata(items, boundary);\n  return Patch(path, headers, body, content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const char *body, size_t content_length,\n                                const std::string &content_type,\n                                UploadProgress progress) {\n  return send_with_content_provider(\"PATCH\", path, headers, body,\n                                    content_length, nullptr, nullptr,\n                                    content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const std::string &body,\n                                const std::string &content_type,\n                                UploadProgress progress) {\n  return send_with_content_provider(\"PATCH\", path, headers, body.data(),\n                                    body.size(), nullptr, nullptr, content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                size_t content_length,\n                                ContentProvider content_provider,\n                                const std::string &content_type,\n                                UploadProgress progress) {\n  return send_with_content_provider(\"PATCH\", path, headers, nullptr,\n                                    content_length, std::move(content_provider),\n                                    nullptr, content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                ContentProviderWithoutLength content_provider,\n                                const std::string &content_type,\n                                UploadProgress progress) {\n  return send_with_content_provider(\"PATCH\", path, headers, nullptr, 0, nullptr,\n                                    std::move(content_provider), content_type,\n                                    progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const UploadFormDataItems &items,\n                                const FormDataProviderItems &provider_items,\n                                UploadProgress progress) {\n  const auto &boundary = detail::make_multipart_data_boundary();\n  const auto &content_type =\n      detail::serialize_multipart_formdata_get_content_type(boundary);\n  return send_with_content_provider(\n      \"PATCH\", path, headers, nullptr, 0, nullptr,\n      get_multipart_content_provider(boundary, items, provider_items),\n      content_type, progress);\n}\n\ninline Result ClientImpl::Patch(const std::string &path, const Headers &headers,\n                                const std::string &body,\n                                const std::string &content_type,\n                                ContentReceiver content_receiver,\n                                DownloadProgress progress) {\n  Request req;\n  req.method = \"PATCH\";\n  req.path = path;\n  req.headers = headers;\n  req.body = body;\n  req.content_receiver =\n      [content_receiver](const char *data, size_t data_length,\n                         size_t /*offset*/, size_t /*total_length*/) {\n        return content_receiver(data, data_length);\n      };\n  req.download_progress = std::move(progress);\n\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  if (!content_type.empty()) { req.set_header(\"Content-Type\", content_type); }\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 DownloadProgress progress) {\n  return Delete(path, Headers(), std::string(), std::string(), progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers,\n                                 DownloadProgress progress) {\n  return Delete(path, headers, std::string(), std::string(), progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path, const char *body,\n                                 size_t content_length,\n                                 const std::string &content_type,\n                                 DownloadProgress progress) {\n  return Delete(path, Headers(), body, content_length, content_type, progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const std::string &body,\n                                 const std::string &content_type,\n                                 DownloadProgress progress) {\n  return Delete(path, Headers(), body.data(), body.size(), content_type,\n                progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers,\n                                 const std::string &body,\n                                 const std::string &content_type,\n                                 DownloadProgress progress) {\n  return Delete(path, headers, body.data(), body.size(), content_type,\n                progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path, const Params &params,\n                                 DownloadProgress progress) {\n  return Delete(path, Headers(), params, progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers, const Params &params,\n                                 DownloadProgress progress) {\n  auto query = detail::params_to_query_str(params);\n  return Delete(path, headers, query, \"application/x-www-form-urlencoded\",\n                progress);\n}\n\ninline Result ClientImpl::Delete(const std::string &path,\n                                 const Headers &headers, const char *body,\n                                 size_t content_length,\n                                 const std::string &content_type,\n                                 DownloadProgress progress) {\n  Request req;\n  req.method = \"DELETE\";\n  req.headers = headers;\n  req.path = path;\n  req.download_progress = std::move(progress);\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  if (!content_type.empty()) { req.set_header(\"Content-Type\", content_type); }\n  req.body.assign(body, content_length);\n\n  return send_(std::move(req));\n}\n\ninline Result ClientImpl::Options(const std::string &path) {\n  return Options(path, Headers());\n}\n\ninline Result ClientImpl::Options(const std::string &path,\n                                  const Headers &headers) {\n  Request req;\n  req.method = \"OPTIONS\";\n  req.headers = headers;\n  req.path = path;\n  if (max_timeout_msec_ > 0) {\n    req.start_time_ = std::chrono::steady_clock::now();\n  }\n\n  return send_(std::move(req));\n}\n\ninline void ClientImpl::stop() {\n  std::lock_guard<std::mutex> guard(socket_mutex_);\n\n  // If there is anything ongoing right now, the ONLY thread-safe thing we can\n  // do is to shutdown_socket, so that threads using this socket suddenly\n  // discover they can't read/write any more and error out. Everything else\n  // (closing the socket, shutting ssl down) is unsafe because these actions\n  // are not thread-safe.\n  if (socket_requests_in_flight_ > 0) {\n    shutdown_socket(socket_);\n\n    // Aside from that, we set a flag for the socket to be closed when we're\n    // done.\n    socket_should_be_closed_when_request_is_done_ = true;\n    return;\n  }\n\n  // Otherwise, still holding the mutex, we can shut everything down ourselves\n  shutdown_ssl(socket_, true);\n  shutdown_socket(socket_);\n  close_socket(socket_);\n}\n\ninline std::string ClientImpl::host() const { return host_; }\n\ninline int ClientImpl::port() const { return port_; }\n\ninline size_t ClientImpl::is_socket_open() const {\n  std::lock_guard<std::mutex> guard(socket_mutex_);\n  return socket_.is_open();\n}\n\ninline socket_t ClientImpl::socket() const { return socket_.sock; }\n\ninline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {\n  connection_timeout_sec_ = sec;\n  connection_timeout_usec_ = usec;\n}\n\ninline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {\n  read_timeout_sec_ = sec;\n  read_timeout_usec_ = usec;\n}\n\ninline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {\n  write_timeout_sec_ = sec;\n  write_timeout_usec_ = usec;\n}\n\ninline void ClientImpl::set_max_timeout(time_t msec) {\n  max_timeout_msec_ = msec;\n}\n\ninline void ClientImpl::set_basic_auth(const std::string &username,\n                                       const std::string &password) {\n  basic_auth_username_ = username;\n  basic_auth_password_ = password;\n}\n\ninline void ClientImpl::set_bearer_token_auth(const std::string &token) {\n  bearer_token_auth_token_ = token;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void ClientImpl::set_digest_auth(const std::string &username,\n                                        const std::string &password) {\n  digest_auth_username_ = username;\n  digest_auth_password_ = password;\n}\n#endif\n\ninline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }\n\ninline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }\n\ninline void ClientImpl::set_path_encode(bool on) { path_encode_ = on; }\n\ninline void\nClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {\n  addr_map_ = std::move(addr_map);\n}\n\ninline void ClientImpl::set_default_headers(Headers headers) {\n  default_headers_ = std::move(headers);\n}\n\ninline void ClientImpl::set_header_writer(\n    std::function<ssize_t(Stream &, Headers &)> const &writer) {\n  header_writer_ = writer;\n}\n\ninline void ClientImpl::set_address_family(int family) {\n  address_family_ = family;\n}\n\ninline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }\n\ninline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }\n\ninline void ClientImpl::set_socket_options(SocketOptions socket_options) {\n  socket_options_ = std::move(socket_options);\n}\n\ninline void ClientImpl::set_compress(bool on) { compress_ = on; }\n\ninline void ClientImpl::set_decompress(bool on) { decompress_ = on; }\n\ninline void ClientImpl::set_interface(const std::string &intf) {\n  interface_ = intf;\n}\n\ninline void ClientImpl::set_proxy(const std::string &host, int port) {\n  proxy_host_ = host;\n  proxy_port_ = port;\n}\n\ninline void ClientImpl::set_proxy_basic_auth(const std::string &username,\n                                             const std::string &password) {\n  proxy_basic_auth_username_ = username;\n  proxy_basic_auth_password_ = password;\n}\n\ninline void ClientImpl::set_proxy_bearer_token_auth(const std::string &token) {\n  proxy_bearer_token_auth_token_ = token;\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void ClientImpl::set_proxy_digest_auth(const std::string &username,\n                                              const std::string &password) {\n  proxy_digest_auth_username_ = username;\n  proxy_digest_auth_password_ = password;\n}\n\ninline void ClientImpl::set_ca_cert_path(const std::string &ca_cert_file_path,\n                                         const std::string &ca_cert_dir_path) {\n  ca_cert_file_path_ = ca_cert_file_path;\n  ca_cert_dir_path_ = ca_cert_dir_path;\n}\n\ninline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {\n  if (ca_cert_store && ca_cert_store != ca_cert_store_) {\n    ca_cert_store_ = ca_cert_store;\n  }\n}\n\ninline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,\n                                                    std::size_t size) const {\n  auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));\n  auto se = detail::scope_exit([&] { BIO_free_all(mem); });\n  if (!mem) { return nullptr; }\n\n  auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);\n  if (!inf) { return nullptr; }\n\n  auto cts = X509_STORE_new();\n  if (cts) {\n    for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {\n      auto itmp = sk_X509_INFO_value(inf, i);\n      if (!itmp) { continue; }\n\n      if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }\n      if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }\n    }\n  }\n\n  sk_X509_INFO_pop_free(inf, X509_INFO_free);\n  return cts;\n}\n\ninline void ClientImpl::enable_server_certificate_verification(bool enabled) {\n  server_certificate_verification_ = enabled;\n}\n\ninline void ClientImpl::enable_server_hostname_verification(bool enabled) {\n  server_hostname_verification_ = enabled;\n}\n\ninline void ClientImpl::set_server_certificate_verifier(\n    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {\n  server_certificate_verifier_ = verifier;\n}\n#endif\n\ninline void ClientImpl::set_logger(Logger logger) {\n  logger_ = std::move(logger);\n}\n\ninline void ClientImpl::set_error_logger(ErrorLogger error_logger) {\n  error_logger_ = std::move(error_logger);\n}\n\n/*\n * SSL Implementation\n */\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\nnamespace detail {\n\ninline bool is_ip_address(const std::string &host) {\n  struct in_addr addr4;\n  struct in6_addr addr6;\n  return inet_pton(AF_INET, host.c_str(), &addr4) == 1 ||\n         inet_pton(AF_INET6, host.c_str(), &addr6) == 1;\n}\n\ntemplate <typename U, typename V>\ninline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,\n                    U SSL_connect_or_accept, V setup) {\n  SSL *ssl = nullptr;\n  {\n    std::lock_guard<std::mutex> guard(ctx_mutex);\n    ssl = SSL_new(ctx);\n  }\n\n  if (ssl) {\n    set_nonblocking(sock, true);\n    auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);\n    BIO_set_nbio(bio, 1);\n    SSL_set_bio(ssl, bio, bio);\n\n    if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {\n      SSL_shutdown(ssl);\n      {\n        std::lock_guard<std::mutex> guard(ctx_mutex);\n        SSL_free(ssl);\n      }\n      set_nonblocking(sock, false);\n      return nullptr;\n    }\n    BIO_set_nbio(bio, 0);\n    set_nonblocking(sock, false);\n  }\n\n  return ssl;\n}\n\ninline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,\n                       bool shutdown_gracefully) {\n  // sometimes we may want to skip this to try to avoid SIGPIPE if we know\n  // the remote has closed the network connection\n  // Note that it is not always possible to avoid SIGPIPE, this is merely a\n  // best-efforts.\n  if (shutdown_gracefully) {\n    (void)(sock);\n    // SSL_shutdown() returns 0 on first call (indicating close_notify alert\n    // sent) and 1 on subsequent call (indicating close_notify alert received)\n    if (SSL_shutdown(ssl) == 0) {\n      // Expected to return 1, but even if it doesn't, we free ssl\n      SSL_shutdown(ssl);\n    }\n  }\n\n  std::lock_guard<std::mutex> guard(ctx_mutex);\n  SSL_free(ssl);\n}\n\ntemplate <typename U>\nbool ssl_connect_or_accept_nonblocking(socket_t sock, SSL *ssl,\n                                       U ssl_connect_or_accept,\n                                       time_t timeout_sec, time_t timeout_usec,\n                                       int *ssl_error) {\n  auto res = 0;\n  while ((res = ssl_connect_or_accept(ssl)) != 1) {\n    auto err = SSL_get_error(ssl, res);\n    switch (err) {\n    case SSL_ERROR_WANT_READ:\n      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }\n      break;\n    case SSL_ERROR_WANT_WRITE:\n      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }\n      break;\n    default: break;\n    }\n    if (ssl_error) { *ssl_error = err; }\n    return false;\n  }\n  return true;\n}\n\ntemplate <typename T>\ninline bool process_server_socket_ssl(\n    const std::atomic<socket_t> &svr_sock, SSL *ssl, socket_t sock,\n    size_t keep_alive_max_count, time_t keep_alive_timeout_sec,\n    time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,\n    time_t write_timeout_usec, T callback) {\n  return process_server_socket_core(\n      svr_sock, sock, keep_alive_max_count, keep_alive_timeout_sec,\n      [&](bool close_connection, bool &connection_closed) {\n        SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,\n                             write_timeout_sec, write_timeout_usec);\n        return callback(strm, close_connection, connection_closed);\n      });\n}\n\ntemplate <typename T>\ninline bool process_client_socket_ssl(\n    SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,\n    time_t write_timeout_sec, time_t write_timeout_usec,\n    time_t max_timeout_msec,\n    std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {\n  SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,\n                       write_timeout_sec, write_timeout_usec, max_timeout_msec,\n                       start_time);\n  return callback(strm);\n}\n\n// SSL socket stream implementation\ninline SSLSocketStream::SSLSocketStream(\n    socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,\n    time_t write_timeout_sec, time_t write_timeout_usec,\n    time_t max_timeout_msec,\n    std::chrono::time_point<std::chrono::steady_clock> start_time)\n    : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),\n      read_timeout_usec_(read_timeout_usec),\n      write_timeout_sec_(write_timeout_sec),\n      write_timeout_usec_(write_timeout_usec),\n      max_timeout_msec_(max_timeout_msec), start_time_(start_time) {\n  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);\n}\n\ninline SSLSocketStream::~SSLSocketStream() = default;\n\ninline bool SSLSocketStream::is_readable() const {\n  return SSL_pending(ssl_) > 0;\n}\n\ninline bool SSLSocketStream::wait_readable() const {\n  if (max_timeout_msec_ <= 0) {\n    return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;\n  }\n\n  time_t read_timeout_sec;\n  time_t read_timeout_usec;\n  calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,\n                      read_timeout_usec_, read_timeout_sec, read_timeout_usec);\n\n  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;\n}\n\ninline bool SSLSocketStream::wait_writable() const {\n  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&\n         is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);\n}\n\ninline ssize_t SSLSocketStream::read(char *ptr, size_t size) {\n  if (SSL_pending(ssl_) > 0) {\n    return SSL_read(ssl_, ptr, static_cast<int>(size));\n  } else if (wait_readable()) {\n    auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));\n    if (ret < 0) {\n      auto err = SSL_get_error(ssl_, ret);\n      auto n = 1000;\n#ifdef _WIN32\n      while (--n >= 0 && (err == SSL_ERROR_WANT_READ ||\n                          (err == SSL_ERROR_SYSCALL &&\n                           WSAGetLastError() == WSAETIMEDOUT))) {\n#else\n      while (--n >= 0 && err == SSL_ERROR_WANT_READ) {\n#endif\n        if (SSL_pending(ssl_) > 0) {\n          return SSL_read(ssl_, ptr, static_cast<int>(size));\n        } else if (wait_readable()) {\n          std::this_thread::sleep_for(std::chrono::microseconds{10});\n          ret = SSL_read(ssl_, ptr, static_cast<int>(size));\n          if (ret >= 0) { return ret; }\n          err = SSL_get_error(ssl_, ret);\n        } else {\n          break;\n        }\n      }\n      assert(ret < 0);\n    }\n    return ret;\n  } else {\n    return -1;\n  }\n}\n\ninline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {\n  if (wait_writable()) {\n    auto handle_size = static_cast<int>(\n        std::min<size_t>(size, (std::numeric_limits<int>::max)()));\n\n    auto ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));\n    if (ret < 0) {\n      auto err = SSL_get_error(ssl_, ret);\n      auto n = 1000;\n#ifdef _WIN32\n      while (--n >= 0 && (err == SSL_ERROR_WANT_WRITE ||\n                          (err == SSL_ERROR_SYSCALL &&\n                           WSAGetLastError() == WSAETIMEDOUT))) {\n#else\n      while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {\n#endif\n        if (wait_writable()) {\n          std::this_thread::sleep_for(std::chrono::microseconds{10});\n          ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));\n          if (ret >= 0) { return ret; }\n          err = SSL_get_error(ssl_, ret);\n        } else {\n          break;\n        }\n      }\n      assert(ret < 0);\n    }\n    return ret;\n  }\n  return -1;\n}\n\ninline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,\n                                                    int &port) const {\n  detail::get_remote_ip_and_port(sock_, ip, port);\n}\n\ninline void SSLSocketStream::get_local_ip_and_port(std::string &ip,\n                                                   int &port) const {\n  detail::get_local_ip_and_port(sock_, ip, port);\n}\n\ninline socket_t SSLSocketStream::socket() const { return sock_; }\n\ninline time_t SSLSocketStream::duration() const {\n  return std::chrono::duration_cast<std::chrono::milliseconds>(\n             std::chrono::steady_clock::now() - start_time_)\n      .count();\n}\n\n} // namespace detail\n\n// SSL HTTP server implementation\ninline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,\n                            const char *client_ca_cert_file_path,\n                            const char *client_ca_cert_dir_path,\n                            const char *private_key_password) {\n  ctx_ = SSL_CTX_new(TLS_server_method());\n\n  if (ctx_) {\n    SSL_CTX_set_options(ctx_,\n                        SSL_OP_NO_COMPRESSION |\n                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);\n\n    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);\n\n    if (private_key_password != nullptr && (private_key_password[0] != '\\0')) {\n      SSL_CTX_set_default_passwd_cb_userdata(\n          ctx_,\n          reinterpret_cast<void *>(const_cast<char *>(private_key_password)));\n    }\n\n    if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||\n        SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=\n            1 ||\n        SSL_CTX_check_private_key(ctx_) != 1) {\n      last_ssl_error_ = static_cast<int>(ERR_get_error());\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {\n      SSL_CTX_load_verify_locations(ctx_, client_ca_cert_file_path,\n                                    client_ca_cert_dir_path);\n\n      // Set client CA list to be sent to clients during TLS handshake\n      if (client_ca_cert_file_path) {\n        auto ca_list = SSL_load_client_CA_file(client_ca_cert_file_path);\n        if (ca_list != nullptr) {\n          SSL_CTX_set_client_CA_list(ctx_, ca_list);\n        } else {\n          // Failed to load client CA list, but we continue since\n          // SSL_CTX_load_verify_locations already succeeded and\n          // certificate verification will still work\n          last_ssl_error_ = static_cast<int>(ERR_get_error());\n        }\n      }\n\n      SSL_CTX_set_verify(\n          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);\n    }\n  }\n}\n\ninline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,\n                            X509_STORE *client_ca_cert_store) {\n  ctx_ = SSL_CTX_new(TLS_server_method());\n\n  if (ctx_) {\n    SSL_CTX_set_options(ctx_,\n                        SSL_OP_NO_COMPRESSION |\n                            SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);\n\n    SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);\n\n    if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||\n        SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    } else if (client_ca_cert_store) {\n      SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);\n\n      // Extract CA names from the store and set them as the client CA list\n      auto ca_list = extract_ca_names_from_x509_store(client_ca_cert_store);\n      if (ca_list) {\n        SSL_CTX_set_client_CA_list(ctx_, ca_list);\n      } else {\n        // Failed to extract CA names, record the error\n        last_ssl_error_ = static_cast<int>(ERR_get_error());\n      }\n\n      SSL_CTX_set_verify(\n          ctx_, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);\n    }\n  }\n}\n\ninline SSLServer::SSLServer(\n    const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {\n  ctx_ = SSL_CTX_new(TLS_method());\n  if (ctx_) {\n    if (!setup_ssl_ctx_callback(*ctx_)) {\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    }\n  }\n}\n\ninline SSLServer::~SSLServer() {\n  if (ctx_) { SSL_CTX_free(ctx_); }\n}\n\ninline bool SSLServer::is_valid() const { return ctx_; }\n\ninline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }\n\ninline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,\n                                    X509_STORE *client_ca_cert_store) {\n\n  std::lock_guard<std::mutex> guard(ctx_mutex_);\n\n  SSL_CTX_use_certificate(ctx_, cert);\n  SSL_CTX_use_PrivateKey(ctx_, private_key);\n\n  if (client_ca_cert_store != nullptr) {\n    SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);\n  }\n}\n\ninline bool SSLServer::process_and_close_socket(socket_t sock) {\n  auto ssl = detail::ssl_new(\n      sock, ctx_, ctx_mutex_,\n      [&](SSL *ssl2) {\n        return detail::ssl_connect_or_accept_nonblocking(\n            sock, ssl2, SSL_accept, read_timeout_sec_, read_timeout_usec_,\n            &last_ssl_error_);\n      },\n      [](SSL * /*ssl2*/) { return true; });\n\n  auto ret = false;\n  if (ssl) {\n    std::string remote_addr;\n    int remote_port = 0;\n    detail::get_remote_ip_and_port(sock, remote_addr, remote_port);\n\n    std::string local_addr;\n    int local_port = 0;\n    detail::get_local_ip_and_port(sock, local_addr, local_port);\n\n    ret = detail::process_server_socket_ssl(\n        svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,\n        read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,\n        write_timeout_usec_,\n        [&](Stream &strm, bool close_connection, bool &connection_closed) {\n          return process_request(strm, remote_addr, remote_port, local_addr,\n                                 local_port, close_connection,\n                                 connection_closed,\n                                 [&](Request &req) { req.ssl = ssl; });\n        });\n\n    // Shutdown gracefully if the result seemed successful, non-gracefully if\n    // the connection appeared to be closed.\n    const bool shutdown_gracefully = ret;\n    detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);\n  }\n\n  detail::shutdown_socket(sock);\n  detail::close_socket(sock);\n  return ret;\n}\n\ninline STACK_OF(X509_NAME) * SSLServer::extract_ca_names_from_x509_store(\n                                 X509_STORE *store) {\n  if (!store) { return nullptr; }\n\n  auto ca_list = sk_X509_NAME_new_null();\n  if (!ca_list) { return nullptr; }\n\n  // Get all objects from the store\n  auto objs = X509_STORE_get0_objects(store);\n  if (!objs) {\n    sk_X509_NAME_free(ca_list);\n    return nullptr;\n  }\n\n  // Iterate through objects and extract certificate subject names\n  auto count = sk_X509_OBJECT_num(objs);\n  for (decltype(count) i = 0; i < count; i++) {\n    auto obj = sk_X509_OBJECT_value(objs, i);\n    if (X509_OBJECT_get_type(obj) == X509_LU_X509) {\n      auto cert = X509_OBJECT_get0_X509(obj);\n      if (cert) {\n        auto subject = X509_get_subject_name(cert);\n        if (subject) {\n          auto name_dup = X509_NAME_dup(subject);\n          if (name_dup) { sk_X509_NAME_push(ca_list, name_dup); }\n        }\n      }\n    }\n  }\n\n  // If no names were extracted, free the list and return nullptr\n  if (sk_X509_NAME_num(ca_list) == 0) {\n    sk_X509_NAME_free(ca_list);\n    return nullptr;\n  }\n\n  return ca_list;\n}\n\n// SSL HTTP client implementation\ninline SSLClient::SSLClient(const std::string &host)\n    : SSLClient(host, 443, std::string(), std::string()) {}\n\ninline SSLClient::SSLClient(const std::string &host, int port)\n    : SSLClient(host, port, std::string(), std::string()) {}\n\ninline SSLClient::SSLClient(const std::string &host, int port,\n                            const std::string &client_cert_path,\n                            const std::string &client_key_path,\n                            const std::string &private_key_password)\n    : ClientImpl(host, port, client_cert_path, client_key_path) {\n  ctx_ = SSL_CTX_new(TLS_client_method());\n\n  SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);\n\n  detail::split(&host_[0], &host_[host_.size()], '.',\n                [&](const char *b, const char *e) {\n                  host_components_.emplace_back(b, e);\n                });\n\n  if (!client_cert_path.empty() && !client_key_path.empty()) {\n    if (!private_key_password.empty()) {\n      SSL_CTX_set_default_passwd_cb_userdata(\n          ctx_, reinterpret_cast<void *>(\n                    const_cast<char *>(private_key_password.c_str())));\n    }\n\n    if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(),\n                                     SSL_FILETYPE_PEM) != 1 ||\n        SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(),\n                                    SSL_FILETYPE_PEM) != 1) {\n      last_openssl_error_ = ERR_get_error();\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    }\n  }\n}\n\ninline SSLClient::SSLClient(const std::string &host, int port,\n                            X509 *client_cert, EVP_PKEY *client_key,\n                            const std::string &private_key_password)\n    : ClientImpl(host, port) {\n  ctx_ = SSL_CTX_new(TLS_client_method());\n\n  detail::split(&host_[0], &host_[host_.size()], '.',\n                [&](const char *b, const char *e) {\n                  host_components_.emplace_back(b, e);\n                });\n\n  if (client_cert != nullptr && client_key != nullptr) {\n    if (!private_key_password.empty()) {\n      SSL_CTX_set_default_passwd_cb_userdata(\n          ctx_, reinterpret_cast<void *>(\n                    const_cast<char *>(private_key_password.c_str())));\n    }\n\n    if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||\n        SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {\n      last_openssl_error_ = ERR_get_error();\n      SSL_CTX_free(ctx_);\n      ctx_ = nullptr;\n    }\n  }\n}\n\ninline SSLClient::~SSLClient() {\n  if (ctx_) { SSL_CTX_free(ctx_); }\n  // Make sure to shut down SSL since shutdown_ssl will resolve to the\n  // base function rather than the derived function once we get to the\n  // base class destructor, and won't free the SSL (causing a leak).\n  shutdown_ssl_impl(socket_, true);\n}\n\ninline bool SSLClient::is_valid() const { return ctx_; }\n\ninline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {\n  if (ca_cert_store) {\n    if (ctx_) {\n      if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {\n        // Free memory allocated for old cert and use new store\n        // `ca_cert_store`\n        SSL_CTX_set_cert_store(ctx_, ca_cert_store);\n        ca_cert_store_ = ca_cert_store;\n      }\n    } else {\n      X509_STORE_free(ca_cert_store);\n    }\n  }\n}\n\ninline void SSLClient::load_ca_cert_store(const char *ca_cert,\n                                          std::size_t size) {\n  set_ca_cert_store(ClientImpl::create_ca_cert_store(ca_cert, size));\n}\n\ninline long SSLClient::get_openssl_verify_result() const {\n  return verify_result_;\n}\n\ninline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }\n\ninline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {\n  if (!is_valid()) {\n    error = Error::SSLConnection;\n    return false;\n  }\n  return ClientImpl::create_and_connect_socket(socket, error);\n}\n\n// Assumes that socket_mutex_ is locked and that there are no requests in\n// flight\ninline bool SSLClient::connect_with_proxy(\n    Socket &socket,\n    std::chrono::time_point<std::chrono::steady_clock> start_time,\n    Response &res, bool &success, Error &error) {\n  success = true;\n  Response proxy_res;\n  if (!detail::process_client_socket(\n          socket.sock, read_timeout_sec_, read_timeout_usec_,\n          write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,\n          start_time, [&](Stream &strm) {\n            Request req2;\n            req2.method = \"CONNECT\";\n            req2.path = host_and_port_;\n            if (max_timeout_msec_ > 0) {\n              req2.start_time_ = std::chrono::steady_clock::now();\n            }\n            return process_request(strm, req2, proxy_res, false, error);\n          })) {\n    // Thread-safe to close everything because we are assuming there are no\n    // requests in flight\n    shutdown_ssl(socket, true);\n    shutdown_socket(socket);\n    close_socket(socket);\n    success = false;\n    return false;\n  }\n\n  if (proxy_res.status == StatusCode::ProxyAuthenticationRequired_407) {\n    if (!proxy_digest_auth_username_.empty() &&\n        !proxy_digest_auth_password_.empty()) {\n      std::map<std::string, std::string> auth;\n      if (detail::parse_www_authenticate(proxy_res, auth, true)) {\n        // Close the current socket and create a new one for the authenticated\n        // request\n        shutdown_ssl(socket, true);\n        shutdown_socket(socket);\n        close_socket(socket);\n\n        // Create a new socket for the authenticated CONNECT request\n        if (!create_and_connect_socket(socket, error)) {\n          success = false;\n          output_error_log(error, nullptr);\n          return false;\n        }\n\n        proxy_res = Response();\n        if (!detail::process_client_socket(\n                socket.sock, read_timeout_sec_, read_timeout_usec_,\n                write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,\n                start_time, [&](Stream &strm) {\n                  Request req3;\n                  req3.method = \"CONNECT\";\n                  req3.path = host_and_port_;\n                  req3.headers.insert(detail::make_digest_authentication_header(\n                      req3, auth, 1, detail::random_string(10),\n                      proxy_digest_auth_username_, proxy_digest_auth_password_,\n                      true));\n                  if (max_timeout_msec_ > 0) {\n                    req3.start_time_ = std::chrono::steady_clock::now();\n                  }\n                  return process_request(strm, req3, proxy_res, false, error);\n                })) {\n          // Thread-safe to close everything because we are assuming there are\n          // no requests in flight\n          shutdown_ssl(socket, true);\n          shutdown_socket(socket);\n          close_socket(socket);\n          success = false;\n          return false;\n        }\n      }\n    }\n  }\n\n  // If status code is not 200, proxy request is failed.\n  // Set error to ProxyConnection and return proxy response\n  // as the response of the request\n  if (proxy_res.status != StatusCode::OK_200) {\n    error = Error::ProxyConnection;\n    output_error_log(error, nullptr);\n    res = std::move(proxy_res);\n    // Thread-safe to close everything because we are assuming there are\n    // no requests in flight\n    shutdown_ssl(socket, true);\n    shutdown_socket(socket);\n    close_socket(socket);\n    return false;\n  }\n\n  return true;\n}\n\ninline bool SSLClient::load_certs() {\n  auto ret = true;\n\n  std::call_once(initialize_cert_, [&]() {\n    std::lock_guard<std::mutex> guard(ctx_mutex_);\n    if (!ca_cert_file_path_.empty()) {\n      if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),\n                                         nullptr)) {\n        last_openssl_error_ = ERR_get_error();\n        ret = false;\n      }\n    } else if (!ca_cert_dir_path_.empty()) {\n      if (!SSL_CTX_load_verify_locations(ctx_, nullptr,\n                                         ca_cert_dir_path_.c_str())) {\n        last_openssl_error_ = ERR_get_error();\n        ret = false;\n      }\n    } else {\n      auto loaded = false;\n#ifdef _WIN32\n      loaded =\n          detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));\n#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && TARGET_OS_MAC\n      loaded = detail::load_system_certs_on_macos(SSL_CTX_get_cert_store(ctx_));\n#endif // _WIN32\n      if (!loaded) { SSL_CTX_set_default_verify_paths(ctx_); }\n    }\n  });\n\n  return ret;\n}\n\ninline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {\n  auto ssl = detail::ssl_new(\n      socket.sock, ctx_, ctx_mutex_,\n      [&](SSL *ssl2) {\n        if (server_certificate_verification_) {\n          if (!load_certs()) {\n            error = Error::SSLLoadingCerts;\n            output_error_log(error, nullptr);\n            return false;\n          }\n          SSL_set_verify(ssl2, SSL_VERIFY_NONE, nullptr);\n        }\n\n        if (!detail::ssl_connect_or_accept_nonblocking(\n                socket.sock, ssl2, SSL_connect, connection_timeout_sec_,\n                connection_timeout_usec_, &last_ssl_error_)) {\n          error = Error::SSLConnection;\n          output_error_log(error, nullptr);\n          return false;\n        }\n\n        if (server_certificate_verification_) {\n          auto verification_status = SSLVerifierResponse::NoDecisionMade;\n\n          if (server_certificate_verifier_) {\n            verification_status = server_certificate_verifier_(ssl2);\n          }\n\n          if (verification_status == SSLVerifierResponse::CertificateRejected) {\n            last_openssl_error_ = ERR_get_error();\n            error = Error::SSLServerVerification;\n            output_error_log(error, nullptr);\n            return false;\n          }\n\n          if (verification_status == SSLVerifierResponse::NoDecisionMade) {\n            verify_result_ = SSL_get_verify_result(ssl2);\n\n            if (verify_result_ != X509_V_OK) {\n              last_openssl_error_ = static_cast<unsigned long>(verify_result_);\n              error = Error::SSLServerVerification;\n              output_error_log(error, nullptr);\n              return false;\n            }\n\n            auto server_cert = SSL_get1_peer_certificate(ssl2);\n            auto se = detail::scope_exit([&] { X509_free(server_cert); });\n\n            if (server_cert == nullptr) {\n              last_openssl_error_ = ERR_get_error();\n              error = Error::SSLServerVerification;\n              output_error_log(error, nullptr);\n              return false;\n            }\n\n            if (server_hostname_verification_) {\n              if (!verify_host(server_cert)) {\n                last_openssl_error_ = X509_V_ERR_HOSTNAME_MISMATCH;\n                error = Error::SSLServerHostnameVerification;\n                output_error_log(error, nullptr);\n                return false;\n              }\n            }\n          }\n        }\n\n        return true;\n      },\n      [&](SSL *ssl2) {\n        // Set SNI only if host is not IP address\n        if (!detail::is_ip_address(host_)) {\n#if defined(OPENSSL_IS_BORINGSSL)\n          SSL_set_tlsext_host_name(ssl2, host_.c_str());\n#else\n          // NOTE: Direct call instead of using the OpenSSL macro to suppress\n          // -Wold-style-cast warning\n          SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME,\n                   TLSEXT_NAMETYPE_host_name,\n                   static_cast<void *>(const_cast<char *>(host_.c_str())));\n#endif\n        }\n        return true;\n      });\n\n  if (ssl) {\n    socket.ssl = ssl;\n    return true;\n  }\n\n  if (ctx_ == nullptr) {\n    error = Error::SSLConnection;\n    last_openssl_error_ = ERR_get_error();\n  }\n\n  shutdown_socket(socket);\n  close_socket(socket);\n  return false;\n}\n\ninline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {\n  shutdown_ssl_impl(socket, shutdown_gracefully);\n}\n\ninline void SSLClient::shutdown_ssl_impl(Socket &socket,\n                                         bool shutdown_gracefully) {\n  if (socket.sock == INVALID_SOCKET) {\n    assert(socket.ssl == nullptr);\n    return;\n  }\n  if (socket.ssl) {\n    detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,\n                       shutdown_gracefully);\n    socket.ssl = nullptr;\n  }\n  assert(socket.ssl == nullptr);\n}\n\ninline bool SSLClient::process_socket(\n    const Socket &socket,\n    std::chrono::time_point<std::chrono::steady_clock> start_time,\n    std::function<bool(Stream &strm)> callback) {\n  assert(socket.ssl);\n  return detail::process_client_socket_ssl(\n      socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,\n      write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,\n      std::move(callback));\n}\n\ninline bool SSLClient::is_ssl() const { return true; }\n\ninline bool SSLClient::verify_host(X509 *server_cert) const {\n  /* Quote from RFC2818 section 3.1 \"Server Identity\"\n\n     If a subjectAltName extension of type dNSName is present, that MUST\n     be used as the identity. Otherwise, the (most specific) Common Name\n     field in the Subject field of the certificate MUST be used. Although\n     the use of the Common Name is existing practice, it is deprecated and\n     Certification Authorities are encouraged to use the dNSName instead.\n\n     Matching is performed using the matching rules specified by\n     [RFC2459].  If more than one identity of a given type is present in\n     the certificate (e.g., more than one dNSName name, a match in any one\n     of the set is considered acceptable.) Names may contain the wildcard\n     character * which is considered to match any single domain name\n     component or component fragment. E.g., *.a.com matches foo.a.com but\n     not bar.foo.a.com. f*.com matches foo.com but not bar.com.\n\n     In some cases, the URI is specified as an IP address rather than a\n     hostname. In this case, the iPAddress subjectAltName must be present\n     in the certificate and must exactly match the IP in the URI.\n\n  */\n  return verify_host_with_subject_alt_name(server_cert) ||\n         verify_host_with_common_name(server_cert);\n}\n\ninline bool\nSSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {\n  auto ret = false;\n\n  auto type = GEN_DNS;\n\n  struct in6_addr addr6 = {};\n  struct in_addr addr = {};\n  size_t addr_len = 0;\n\n#ifndef __MINGW32__\n  if (inet_pton(AF_INET6, host_.c_str(), &addr6)) {\n    type = GEN_IPADD;\n    addr_len = sizeof(struct in6_addr);\n  } else if (inet_pton(AF_INET, host_.c_str(), &addr)) {\n    type = GEN_IPADD;\n    addr_len = sizeof(struct in_addr);\n  }\n#endif\n\n  auto alt_names = static_cast<const struct stack_st_GENERAL_NAME *>(\n      X509_get_ext_d2i(server_cert, NID_subject_alt_name, nullptr, nullptr));\n\n  if (alt_names) {\n    auto dsn_matched = false;\n    auto ip_matched = false;\n\n    auto count = sk_GENERAL_NAME_num(alt_names);\n\n    for (decltype(count) i = 0; i < count && !dsn_matched; i++) {\n      auto val = sk_GENERAL_NAME_value(alt_names, i);\n      if (!val || val->type != type) { continue; }\n\n      auto name =\n          reinterpret_cast<const char *>(ASN1_STRING_get0_data(val->d.ia5));\n      if (name == nullptr) { continue; }\n\n      auto name_len = static_cast<size_t>(ASN1_STRING_length(val->d.ia5));\n\n      switch (type) {\n      case GEN_DNS: dsn_matched = check_host_name(name, name_len); break;\n\n      case GEN_IPADD:\n        if (!memcmp(&addr6, name, addr_len) || !memcmp(&addr, name, addr_len)) {\n          ip_matched = true;\n        }\n        break;\n      }\n    }\n\n    if (dsn_matched || ip_matched) { ret = true; }\n  }\n\n  GENERAL_NAMES_free(const_cast<STACK_OF(GENERAL_NAME) *>(\n      reinterpret_cast<const STACK_OF(GENERAL_NAME) *>(alt_names)));\n  return ret;\n}\n\ninline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {\n  const auto subject_name = X509_get_subject_name(server_cert);\n\n  if (subject_name != nullptr) {\n    char name[BUFSIZ];\n    auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,\n                                              name, sizeof(name));\n\n    if (name_len != -1) {\n      return check_host_name(name, static_cast<size_t>(name_len));\n    }\n  }\n\n  return false;\n}\n\ninline bool SSLClient::check_host_name(const char *pattern,\n                                       size_t pattern_len) const {\n  if (host_.size() == pattern_len && host_ == pattern) { return true; }\n\n  // Wildcard match\n  // https://bugs.launchpad.net/ubuntu/+source/firefox-3.0/+bug/376484\n  std::vector<std::string> pattern_components;\n  detail::split(&pattern[0], &pattern[pattern_len], '.',\n                [&](const char *b, const char *e) {\n                  pattern_components.emplace_back(b, e);\n                });\n\n  if (host_components_.size() != pattern_components.size()) { return false; }\n\n  auto itr = pattern_components.begin();\n  for (const auto &h : host_components_) {\n    auto &p = *itr;\n    if (p != h && p != \"*\") {\n      auto partial_match = (p.size() > 0 && p[p.size() - 1] == '*' &&\n                            !p.compare(0, p.size() - 1, h));\n      if (!partial_match) { return false; }\n    }\n    ++itr;\n  }\n\n  return true;\n}\n#endif\n\n// Universal client implementation\ninline Client::Client(const std::string &scheme_host_port)\n    : Client(scheme_host_port, std::string(), std::string()) {}\n\ninline Client::Client(const std::string &scheme_host_port,\n                      const std::string &client_cert_path,\n                      const std::string &client_key_path) {\n  const static std::regex re(\n      R\"((?:([a-z]+):\\/\\/)?(?:\\[([a-fA-F\\d:]+)\\]|([^:/?#]+))(?::(\\d+))?)\");\n\n  std::smatch m;\n  if (std::regex_match(scheme_host_port, m, re)) {\n    auto scheme = m[1].str();\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n    if (!scheme.empty() && (scheme != \"http\" && scheme != \"https\")) {\n#else\n    if (!scheme.empty() && scheme != \"http\") {\n#endif\n#ifndef CPPHTTPLIB_NO_EXCEPTIONS\n      std::string msg = \"'\" + scheme + \"' scheme is not supported.\";\n      throw std::invalid_argument(msg);\n#endif\n      return;\n    }\n\n    auto is_ssl = scheme == \"https\";\n\n    auto host = m[2].str();\n    if (host.empty()) { host = m[3].str(); }\n\n    auto port_str = m[4].str();\n    auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);\n\n    if (is_ssl) {\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\n      cli_ = detail::make_unique<SSLClient>(host, port, client_cert_path,\n                                            client_key_path);\n      is_ssl_ = is_ssl;\n#endif\n    } else {\n      cli_ = detail::make_unique<ClientImpl>(host, port, client_cert_path,\n                                             client_key_path);\n    }\n  } else {\n    // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)\n    // if port param below changes.\n    cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,\n                                           client_cert_path, client_key_path);\n  }\n} // namespace detail\n\ninline Client::Client(const std::string &host, int port)\n    : cli_(detail::make_unique<ClientImpl>(host, port)) {}\n\ninline Client::Client(const std::string &host, int port,\n                      const std::string &client_cert_path,\n                      const std::string &client_key_path)\n    : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,\n                                           client_key_path)) {}\n\ninline Client::~Client() = default;\n\ninline bool Client::is_valid() const {\n  return cli_ != nullptr && cli_->is_valid();\n}\n\ninline Result Client::Get(const std::string &path, DownloadProgress progress) {\n  return cli_->Get(path, std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Headers &headers,\n                          DownloadProgress progress) {\n  return cli_->Get(path, headers, std::move(progress));\n}\ninline Result Client::Get(const std::string &path,\n                          ContentReceiver content_receiver,\n                          DownloadProgress progress) {\n  return cli_->Get(path, std::move(content_receiver), std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Headers &headers,\n                          ContentReceiver content_receiver,\n                          DownloadProgress progress) {\n  return cli_->Get(path, headers, std::move(content_receiver),\n                   std::move(progress));\n}\ninline Result Client::Get(const std::string &path,\n                          ResponseHandler response_handler,\n                          ContentReceiver content_receiver,\n                          DownloadProgress progress) {\n  return cli_->Get(path, std::move(response_handler),\n                   std::move(content_receiver), std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Headers &headers,\n                          ResponseHandler response_handler,\n                          ContentReceiver content_receiver,\n                          DownloadProgress progress) {\n  return cli_->Get(path, headers, std::move(response_handler),\n                   std::move(content_receiver), std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Params &params,\n                          const Headers &headers, DownloadProgress progress) {\n  return cli_->Get(path, params, headers, std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Params &params,\n                          const Headers &headers,\n                          ContentReceiver content_receiver,\n                          DownloadProgress progress) {\n  return cli_->Get(path, params, headers, std::move(content_receiver),\n                   std::move(progress));\n}\ninline Result Client::Get(const std::string &path, const Params &params,\n                          const Headers &headers,\n                          ResponseHandler response_handler,\n                          ContentReceiver content_receiver,\n                          DownloadProgress progress) {\n  return cli_->Get(path, params, headers, std::move(response_handler),\n                   std::move(content_receiver), std::move(progress));\n}\n\ninline Result Client::Head(const std::string &path) { return cli_->Head(path); }\ninline Result Client::Head(const std::string &path, const Headers &headers) {\n  return cli_->Head(path, headers);\n}\n\ninline Result Client::Post(const std::string &path) { return cli_->Post(path); }\ninline Result Client::Post(const std::string &path, const Headers &headers) {\n  return cli_->Post(path, headers);\n}\ninline Result Client::Post(const std::string &path, const char *body,\n                           size_t content_length,\n                           const std::string &content_type,\n                           UploadProgress progress) {\n  return cli_->Post(path, body, content_length, content_type, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const char *body, size_t content_length,\n                           const std::string &content_type,\n                           UploadProgress progress) {\n  return cli_->Post(path, headers, body, content_length, content_type,\n                    progress);\n}\ninline Result Client::Post(const std::string &path, const std::string &body,\n                           const std::string &content_type,\n                           UploadProgress progress) {\n  return cli_->Post(path, body, content_type, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const std::string &body,\n                           const std::string &content_type,\n                           UploadProgress progress) {\n  return cli_->Post(path, headers, body, content_type, progress);\n}\ninline Result Client::Post(const std::string &path, size_t content_length,\n                           ContentProvider content_provider,\n                           const std::string &content_type,\n                           UploadProgress progress) {\n  return cli_->Post(path, content_length, std::move(content_provider),\n                    content_type, progress);\n}\ninline Result Client::Post(const std::string &path,\n                           ContentProviderWithoutLength content_provider,\n                           const std::string &content_type,\n                           UploadProgress progress) {\n  return cli_->Post(path, std::move(content_provider), content_type, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           size_t content_length,\n                           ContentProvider content_provider,\n                           const std::string &content_type,\n                           UploadProgress progress) {\n  return cli_->Post(path, headers, content_length, std::move(content_provider),\n                    content_type, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           ContentProviderWithoutLength content_provider,\n                           const std::string &content_type,\n                           UploadProgress progress) {\n  return cli_->Post(path, headers, std::move(content_provider), content_type,\n                    progress);\n}\ninline Result Client::Post(const std::string &path, const Params &params) {\n  return cli_->Post(path, params);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const Params &params) {\n  return cli_->Post(path, headers, params);\n}\ninline Result Client::Post(const std::string &path,\n                           const UploadFormDataItems &items,\n                           UploadProgress progress) {\n  return cli_->Post(path, items, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const UploadFormDataItems &items,\n                           UploadProgress progress) {\n  return cli_->Post(path, headers, items, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const UploadFormDataItems &items,\n                           const std::string &boundary,\n                           UploadProgress progress) {\n  return cli_->Post(path, headers, items, boundary, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const UploadFormDataItems &items,\n                           const FormDataProviderItems &provider_items,\n                           UploadProgress progress) {\n  return cli_->Post(path, headers, items, provider_items, progress);\n}\ninline Result Client::Post(const std::string &path, const Headers &headers,\n                           const std::string &body,\n                           const std::string &content_type,\n                           ContentReceiver content_receiver,\n                           DownloadProgress progress) {\n  return cli_->Post(path, headers, body, content_type, content_receiver,\n                    progress);\n}\n\ninline Result Client::Put(const std::string &path) { return cli_->Put(path); }\ninline Result Client::Put(const std::string &path, const Headers &headers) {\n  return cli_->Put(path, headers);\n}\ninline Result Client::Put(const std::string &path, const char *body,\n                          size_t content_length,\n                          const std::string &content_type,\n                          UploadProgress progress) {\n  return cli_->Put(path, body, content_length, content_type, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const char *body, size_t content_length,\n                          const std::string &content_type,\n                          UploadProgress progress) {\n  return cli_->Put(path, headers, body, content_length, content_type, progress);\n}\ninline Result Client::Put(const std::string &path, const std::string &body,\n                          const std::string &content_type,\n                          UploadProgress progress) {\n  return cli_->Put(path, body, content_type, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const std::string &body,\n                          const std::string &content_type,\n                          UploadProgress progress) {\n  return cli_->Put(path, headers, body, content_type, progress);\n}\ninline Result Client::Put(const std::string &path, size_t content_length,\n                          ContentProvider content_provider,\n                          const std::string &content_type,\n                          UploadProgress progress) {\n  return cli_->Put(path, content_length, std::move(content_provider),\n                   content_type, progress);\n}\ninline Result Client::Put(const std::string &path,\n                          ContentProviderWithoutLength content_provider,\n                          const std::string &content_type,\n                          UploadProgress progress) {\n  return cli_->Put(path, std::move(content_provider), content_type, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          size_t content_length,\n                          ContentProvider content_provider,\n                          const std::string &content_type,\n                          UploadProgress progress) {\n  return cli_->Put(path, headers, content_length, std::move(content_provider),\n                   content_type, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          ContentProviderWithoutLength content_provider,\n                          const std::string &content_type,\n                          UploadProgress progress) {\n  return cli_->Put(path, headers, std::move(content_provider), content_type,\n                   progress);\n}\ninline Result Client::Put(const std::string &path, const Params &params) {\n  return cli_->Put(path, params);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const Params &params) {\n  return cli_->Put(path, headers, params);\n}\ninline Result Client::Put(const std::string &path,\n                          const UploadFormDataItems &items,\n                          UploadProgress progress) {\n  return cli_->Put(path, items, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const UploadFormDataItems &items,\n                          UploadProgress progress) {\n  return cli_->Put(path, headers, items, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const UploadFormDataItems &items,\n                          const std::string &boundary,\n                          UploadProgress progress) {\n  return cli_->Put(path, headers, items, boundary, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const UploadFormDataItems &items,\n                          const FormDataProviderItems &provider_items,\n                          UploadProgress progress) {\n  return cli_->Put(path, headers, items, provider_items, progress);\n}\ninline Result Client::Put(const std::string &path, const Headers &headers,\n                          const std::string &body,\n                          const std::string &content_type,\n                          ContentReceiver content_receiver,\n                          DownloadProgress progress) {\n  return cli_->Put(path, headers, body, content_type, content_receiver,\n                   progress);\n}\n\ninline Result Client::Patch(const std::string &path) {\n  return cli_->Patch(path);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers) {\n  return cli_->Patch(path, headers);\n}\ninline Result Client::Patch(const std::string &path, const char *body,\n                            size_t content_length,\n                            const std::string &content_type,\n                            UploadProgress progress) {\n  return cli_->Patch(path, body, content_length, content_type, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const char *body, size_t content_length,\n                            const std::string &content_type,\n                            UploadProgress progress) {\n  return cli_->Patch(path, headers, body, content_length, content_type,\n                     progress);\n}\ninline Result Client::Patch(const std::string &path, const std::string &body,\n                            const std::string &content_type,\n                            UploadProgress progress) {\n  return cli_->Patch(path, body, content_type, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const std::string &body,\n                            const std::string &content_type,\n                            UploadProgress progress) {\n  return cli_->Patch(path, headers, body, content_type, progress);\n}\ninline Result Client::Patch(const std::string &path, size_t content_length,\n                            ContentProvider content_provider,\n                            const std::string &content_type,\n                            UploadProgress progress) {\n  return cli_->Patch(path, content_length, std::move(content_provider),\n                     content_type, progress);\n}\ninline Result Client::Patch(const std::string &path,\n                            ContentProviderWithoutLength content_provider,\n                            const std::string &content_type,\n                            UploadProgress progress) {\n  return cli_->Patch(path, std::move(content_provider), content_type, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            size_t content_length,\n                            ContentProvider content_provider,\n                            const std::string &content_type,\n                            UploadProgress progress) {\n  return cli_->Patch(path, headers, content_length, std::move(content_provider),\n                     content_type, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            ContentProviderWithoutLength content_provider,\n                            const std::string &content_type,\n                            UploadProgress progress) {\n  return cli_->Patch(path, headers, std::move(content_provider), content_type,\n                     progress);\n}\ninline Result Client::Patch(const std::string &path, const Params &params) {\n  return cli_->Patch(path, params);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const Params &params) {\n  return cli_->Patch(path, headers, params);\n}\ninline Result Client::Patch(const std::string &path,\n                            const UploadFormDataItems &items,\n                            UploadProgress progress) {\n  return cli_->Patch(path, items, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const UploadFormDataItems &items,\n                            UploadProgress progress) {\n  return cli_->Patch(path, headers, items, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const UploadFormDataItems &items,\n                            const std::string &boundary,\n                            UploadProgress progress) {\n  return cli_->Patch(path, headers, items, boundary, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const UploadFormDataItems &items,\n                            const FormDataProviderItems &provider_items,\n                            UploadProgress progress) {\n  return cli_->Patch(path, headers, items, provider_items, progress);\n}\ninline Result Client::Patch(const std::string &path, const Headers &headers,\n                            const std::string &body,\n                            const std::string &content_type,\n                            ContentReceiver content_receiver,\n                            DownloadProgress progress) {\n  return cli_->Patch(path, headers, body, content_type, content_receiver,\n                     progress);\n}\n\ninline Result Client::Delete(const std::string &path,\n                             DownloadProgress progress) {\n  return cli_->Delete(path, progress);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers,\n                             DownloadProgress progress) {\n  return cli_->Delete(path, headers, progress);\n}\ninline Result Client::Delete(const std::string &path, const char *body,\n                             size_t content_length,\n                             const std::string &content_type,\n                             DownloadProgress progress) {\n  return cli_->Delete(path, body, content_length, content_type, progress);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers,\n                             const char *body, size_t content_length,\n                             const std::string &content_type,\n                             DownloadProgress progress) {\n  return cli_->Delete(path, headers, body, content_length, content_type,\n                      progress);\n}\ninline Result Client::Delete(const std::string &path, const std::string &body,\n                             const std::string &content_type,\n                             DownloadProgress progress) {\n  return cli_->Delete(path, body, content_type, progress);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers,\n                             const std::string &body,\n                             const std::string &content_type,\n                             DownloadProgress progress) {\n  return cli_->Delete(path, headers, body, content_type, progress);\n}\ninline Result Client::Delete(const std::string &path, const Params &params,\n                             DownloadProgress progress) {\n  return cli_->Delete(path, params, progress);\n}\ninline Result Client::Delete(const std::string &path, const Headers &headers,\n                             const Params &params, DownloadProgress progress) {\n  return cli_->Delete(path, headers, params, progress);\n}\n\ninline Result Client::Options(const std::string &path) {\n  return cli_->Options(path);\n}\ninline Result Client::Options(const std::string &path, const Headers &headers) {\n  return cli_->Options(path, headers);\n}\n\ninline bool Client::send(Request &req, Response &res, Error &error) {\n  return cli_->send(req, res, error);\n}\n\ninline Result Client::send(const Request &req) { return cli_->send(req); }\n\ninline void Client::stop() { cli_->stop(); }\n\ninline std::string Client::host() const { return cli_->host(); }\n\ninline int Client::port() const { return cli_->port(); }\n\ninline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }\n\ninline socket_t Client::socket() const { return cli_->socket(); }\n\ninline void\nClient::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {\n  cli_->set_hostname_addr_map(std::move(addr_map));\n}\n\ninline void Client::set_default_headers(Headers headers) {\n  cli_->set_default_headers(std::move(headers));\n}\n\ninline void Client::set_header_writer(\n    std::function<ssize_t(Stream &, Headers &)> const &writer) {\n  cli_->set_header_writer(writer);\n}\n\ninline void Client::set_address_family(int family) {\n  cli_->set_address_family(family);\n}\n\ninline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }\n\ninline void Client::set_socket_options(SocketOptions socket_options) {\n  cli_->set_socket_options(std::move(socket_options));\n}\n\ninline void Client::set_connection_timeout(time_t sec, time_t usec) {\n  cli_->set_connection_timeout(sec, usec);\n}\n\ninline void Client::set_read_timeout(time_t sec, time_t usec) {\n  cli_->set_read_timeout(sec, usec);\n}\n\ninline void Client::set_write_timeout(time_t sec, time_t usec) {\n  cli_->set_write_timeout(sec, usec);\n}\n\ninline void Client::set_basic_auth(const std::string &username,\n                                   const std::string &password) {\n  cli_->set_basic_auth(username, password);\n}\ninline void Client::set_bearer_token_auth(const std::string &token) {\n  cli_->set_bearer_token_auth(token);\n}\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void Client::set_digest_auth(const std::string &username,\n                                    const std::string &password) {\n  cli_->set_digest_auth(username, password);\n}\n#endif\n\ninline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }\ninline void Client::set_follow_location(bool on) {\n  cli_->set_follow_location(on);\n}\n\ninline void Client::set_path_encode(bool on) { cli_->set_path_encode(on); }\n\n[[deprecated(\"Use set_path_encode instead\")]]\ninline void Client::set_url_encode(bool on) {\n  cli_->set_path_encode(on);\n}\n\ninline void Client::set_compress(bool on) { cli_->set_compress(on); }\n\ninline void Client::set_decompress(bool on) { cli_->set_decompress(on); }\n\ninline void Client::set_interface(const std::string &intf) {\n  cli_->set_interface(intf);\n}\n\ninline void Client::set_proxy(const std::string &host, int port) {\n  cli_->set_proxy(host, port);\n}\ninline void Client::set_proxy_basic_auth(const std::string &username,\n                                         const std::string &password) {\n  cli_->set_proxy_basic_auth(username, password);\n}\ninline void Client::set_proxy_bearer_token_auth(const std::string &token) {\n  cli_->set_proxy_bearer_token_auth(token);\n}\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void Client::set_proxy_digest_auth(const std::string &username,\n                                          const std::string &password) {\n  cli_->set_proxy_digest_auth(username, password);\n}\n#endif\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void Client::enable_server_certificate_verification(bool enabled) {\n  cli_->enable_server_certificate_verification(enabled);\n}\n\ninline void Client::enable_server_hostname_verification(bool enabled) {\n  cli_->enable_server_hostname_verification(enabled);\n}\n\ninline void Client::set_server_certificate_verifier(\n    std::function<SSLVerifierResponse(SSL *ssl)> verifier) {\n  cli_->set_server_certificate_verifier(verifier);\n}\n#endif\n\ninline void Client::set_logger(Logger logger) {\n  cli_->set_logger(std::move(logger));\n}\n\ninline void Client::set_error_logger(ErrorLogger error_logger) {\n  cli_->set_error_logger(std::move(error_logger));\n}\n\n#ifdef CPPHTTPLIB_OPENSSL_SUPPORT\ninline void Client::set_ca_cert_path(const std::string &ca_cert_file_path,\n                                     const std::string &ca_cert_dir_path) {\n  cli_->set_ca_cert_path(ca_cert_file_path, ca_cert_dir_path);\n}\n\ninline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {\n  if (is_ssl_) {\n    static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);\n  } else {\n    cli_->set_ca_cert_store(ca_cert_store);\n  }\n}\n\ninline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {\n  set_ca_cert_store(cli_->create_ca_cert_store(ca_cert, size));\n}\n\ninline long Client::get_openssl_verify_result() const {\n  if (is_ssl_) {\n    return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();\n  }\n  return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???\n}\n\ninline SSL_CTX *Client::ssl_context() const {\n  if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }\n  return nullptr;\n}\n#endif\n\n// ----------------------------------------------------------------------------\n\n} // namespace httplib\n\n#endif // CPPHTTPLIB_HTTPLIB_H\n"
  },
  {
    "path": "third_party/edo/BUILD.gn",
    "content": "# Copyright 2025 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../build/crashpad_buildconfig.gni\")\n\nif (crashpad_is_in_chromium) {\n  group(\"edo\") {\n    testonly = true\n    public_deps = [ \"//ios/third_party/edo\" ]\n  }\n} else {\n  config(\"config\") {\n    include_dirs = [ \"../../third_party/edo/edo\" ]\n    cflags = [ \"-Wno-newline-eof\" ]\n    if (xcode_version_int >= 1630) {\n      cflags += [ \"-Wno-cast-function-type-mismatch\" ]\n    }\n  }\n\n  source_set(\"edo\") {\n    testonly = true\n\n    sources = [\n      \"edo/Channel/Sources/EDOBlockingQueue.h\",\n      \"edo/Channel/Sources/EDOBlockingQueue.m\",\n      \"edo/Channel/Sources/EDOChannel.h\",\n      \"edo/Channel/Sources/EDOChannelErrors.h\",\n      \"edo/Channel/Sources/EDOChannelErrors.m\",\n      \"edo/Channel/Sources/EDOChannelForwarder.h\",\n      \"edo/Channel/Sources/EDOChannelForwarder.m\",\n      \"edo/Channel/Sources/EDOChannelMultiplexer.h\",\n      \"edo/Channel/Sources/EDOChannelMultiplexer.m\",\n      \"edo/Channel/Sources/EDOChannelPool.h\",\n      \"edo/Channel/Sources/EDOChannelPool.m\",\n      \"edo/Channel/Sources/EDOChannelUtil.h\",\n      \"edo/Channel/Sources/EDOChannelUtil.m\",\n      \"edo/Channel/Sources/EDOHostPort.h\",\n      \"edo/Channel/Sources/EDOHostPort.m\",\n      \"edo/Channel/Sources/EDOListenSocket.h\",\n      \"edo/Channel/Sources/EDOListenSocket.m\",\n      \"edo/Channel/Sources/EDOSocket.h\",\n      \"edo/Channel/Sources/EDOSocket.m\",\n      \"edo/Channel/Sources/EDOSocketChannel.h\",\n      \"edo/Channel/Sources/EDOSocketChannel.m\",\n      \"edo/Channel/Sources/EDOSocketPort.h\",\n      \"edo/Channel/Sources/EDOSocketPort.m\",\n      \"edo/Device/Sources/EDODeviceChannel.h\",\n      \"edo/Device/Sources/EDODeviceChannel.m\",\n      \"edo/Device/Sources/EDODeviceConnector.h\",\n      \"edo/Device/Sources/EDODeviceConnector.m\",\n      \"edo/Device/Sources/EDODeviceDetector.h\",\n      \"edo/Device/Sources/EDODeviceDetector.m\",\n      \"edo/Device/Sources/EDOUSBMuxUtil.h\",\n      \"edo/Device/Sources/EDOUSBMuxUtil.m\",\n      \"edo/DeviceForwarder/Sources/EDODeviceForwardersManager.h\",\n      \"edo/DeviceForwarder/Sources/EDODeviceForwardersManager.m\",\n      \"edo/Measure/Sources/EDONumericMeasure.h\",\n      \"edo/Measure/Sources/EDONumericMeasure.m\",\n      \"edo/Service/Sources/EDOBlockObject.h\",\n      \"edo/Service/Sources/EDOBlockObject.m\",\n      \"edo/Service/Sources/EDOClassMessage.h\",\n      \"edo/Service/Sources/EDOClassMessage.m\",\n      \"edo/Service/Sources/EDOClientService+Private.h\",\n      \"edo/Service/Sources/EDOClientService.h\",\n      \"edo/Service/Sources/EDOClientService.m\",\n      \"edo/Service/Sources/EDOClientServiceStatsCollector.h\",\n      \"edo/Service/Sources/EDOClientServiceStatsCollector.m\",\n      \"edo/Service/Sources/EDODeallocationTracker.h\",\n      \"edo/Service/Sources/EDODeallocationTracker.m\",\n      \"edo/Service/Sources/EDOExecutor.h\",\n      \"edo/Service/Sources/EDOExecutor.m\",\n      \"edo/Service/Sources/EDOExecutorMessage.h\",\n      \"edo/Service/Sources/EDOExecutorMessage.m\",\n      \"edo/Service/Sources/EDOHostNamingService+Private.h\",\n      \"edo/Service/Sources/EDOHostNamingService.h\",\n      \"edo/Service/Sources/EDOHostNamingService.m\",\n      \"edo/Service/Sources/EDOHostService+Handlers.h\",\n      \"edo/Service/Sources/EDOHostService+Handlers.m\",\n      \"edo/Service/Sources/EDOHostService+Private.h\",\n      \"edo/Service/Sources/EDOHostService.h\",\n      \"edo/Service/Sources/EDOHostService.m\",\n      \"edo/Service/Sources/EDOInvocationMessage.h\",\n      \"edo/Service/Sources/EDOInvocationMessage.m\",\n      \"edo/Service/Sources/EDOMessage.h\",\n      \"edo/Service/Sources/EDOMessage.m\",\n      \"edo/Service/Sources/EDOMethodSignatureMessage.h\",\n      \"edo/Service/Sources/EDOMethodSignatureMessage.m\",\n      \"edo/Service/Sources/EDOObject+EDOParameter.m\",\n      \"edo/Service/Sources/EDOObject+Invocation.m\",\n      \"edo/Service/Sources/EDOObject+Private.h\",\n      \"edo/Service/Sources/EDOObject.h\",\n      \"edo/Service/Sources/EDOObject.m\",\n      \"edo/Service/Sources/EDOObjectAliveMessage.h\",\n      \"edo/Service/Sources/EDOObjectAliveMessage.m\",\n      \"edo/Service/Sources/EDOObjectMessage.h\",\n      \"edo/Service/Sources/EDOObjectMessage.m\",\n      \"edo/Service/Sources/EDOObjectReleaseMessage.h\",\n      \"edo/Service/Sources/EDOObjectReleaseMessage.m\",\n      \"edo/Service/Sources/EDOParameter.h\",\n      \"edo/Service/Sources/EDOParameter.m\",\n      \"edo/Service/Sources/EDOProtocolObject.h\",\n      \"edo/Service/Sources/EDOProtocolObject.m\",\n      \"edo/Service/Sources/EDORemoteException.h\",\n      \"edo/Service/Sources/EDORemoteException.m\",\n      \"edo/Service/Sources/EDORemoteVariable.h\",\n      \"edo/Service/Sources/EDORemoteVariable.m\",\n      \"edo/Service/Sources/EDORuntimeUtils.h\",\n      \"edo/Service/Sources/EDORuntimeUtils.m\",\n      \"edo/Service/Sources/EDOServiceError.h\",\n      \"edo/Service/Sources/EDOServiceError.m\",\n      \"edo/Service/Sources/EDOServiceException.h\",\n      \"edo/Service/Sources/EDOServiceException.m\",\n      \"edo/Service/Sources/EDOServicePort.h\",\n      \"edo/Service/Sources/EDOServicePort.m\",\n      \"edo/Service/Sources/EDOServiceRequest.h\",\n      \"edo/Service/Sources/EDOServiceRequest.m\",\n      \"edo/Service/Sources/EDOTimingFunctions.h\",\n      \"edo/Service/Sources/EDOTimingFunctions.m\",\n      \"edo/Service/Sources/EDOValueObject+EDOParameter.m\",\n      \"edo/Service/Sources/EDOValueObject.h\",\n      \"edo/Service/Sources/EDOValueObject.m\",\n      \"edo/Service/Sources/EDOValueType.m\",\n      \"edo/Service/Sources/EDOWeakObject.h\",\n      \"edo/Service/Sources/EDOWeakObject.m\",\n      \"edo/Service/Sources/NSBlock+EDOInvocation.m\",\n      \"edo/Service/Sources/NSKeyedArchiver+EDOAdditions.h\",\n      \"edo/Service/Sources/NSKeyedArchiver+EDOAdditions.m\",\n      \"edo/Service/Sources/NSKeyedUnarchiver+EDOAdditions.h\",\n      \"edo/Service/Sources/NSKeyedUnarchiver+EDOAdditions.m\",\n      \"edo/Service/Sources/NSObject+EDOBlockedType.h\",\n      \"edo/Service/Sources/NSObject+EDOBlockedType.m\",\n      \"edo/Service/Sources/NSObject+EDOParameter.h\",\n      \"edo/Service/Sources/NSObject+EDOParameter.m\",\n      \"edo/Service/Sources/NSObject+EDOValue.h\",\n      \"edo/Service/Sources/NSObject+EDOValue.m\",\n      \"edo/Service/Sources/NSObject+EDOValueObject.h\",\n      \"edo/Service/Sources/NSObject+EDOValueObject.m\",\n      \"edo/Service/Sources/NSObject+EDOWeakObject.h\",\n      \"edo/Service/Sources/NSObject+EDOWeakObject.m\",\n      \"edo/Service/Sources/NSProxy+EDOParameter.h\",\n      \"edo/Service/Sources/NSProxy+EDOParameter.m\",\n    ]\n\n    public_configs = [ \":config\" ]\n    deps = [ \"../../build:apple_enable_arc\" ]\n  }\n}\n"
  },
  {
    "path": "third_party/edo/README.crashpad",
    "content": "Name: eDistantObject\nShort Name: EDO\nURL: https://github.com/google/eDistantObject\nRevision: See DEPS\nLicense: Apache 2.0\nLicense File: edo/LICENSE\nSecurity Critical: no\nShipped: no\n\nDescription:\niOS remote method invocations (distant object) over Inter-process communication layer.\n"
  },
  {
    "path": "third_party/fuchsia/BUILD.gn",
    "content": "# Copyright 2018 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../build/crashpad_buildconfig.gni\")\n\nif (crashpad_is_in_fuchsia) {\n  group(\"fuchsia\") {\n    public_deps = [\n      \"//sdk/lib/fdio\",\n      \"//zircon/system/ulib/zx\",\n    ]\n  }\n} else if (crashpad_is_in_chromium) {\n  group(\"fuchsia\") {\n    public_deps = [\n      \"//third_party/fuchsia-sdk/sdk/pkg/fdio\",\n      \"//third_party/fuchsia-sdk/sdk/pkg/zx\",\n    ]\n  }\n} else {\n  group(\"fuchsia\") {\n    public_deps = [\n      \"//third_party/fuchsia/sdk/$host_os-amd64/pkg/fdio\",\n      \"//third_party/fuchsia/sdk/$host_os-amd64/pkg/zx\",\n    ]\n  }\n}\n"
  },
  {
    "path": "third_party/fuchsia/README.crashpad",
    "content": "This directory is a placeholder for Fuchsia tools that will be downloaded by\nCIPD (https://github.com/luci/luci-go/tree/master/cipd). The CIPD update happens\nas part of gclient runhooks.\n"
  },
  {
    "path": "third_party/fuchsia/runner.py",
    "content": "#!/usr/bin/env python\n\n# Copyright 2018 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport sys\n\nos.execv(sys.argv[1], sys.argv[1:])\n"
  },
  {
    "path": "third_party/getopt/BUILD.gn",
    "content": "# Copyright 2014 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nstatic_library(\"getopt\") {\n  sources = [\n    \"getopt.cc\",\n    \"getopt.h\",\n  ]\n}\n"
  },
  {
    "path": "third_party/getopt/LICENSE",
    "content": "Copyright (C) 1997 Gregory Pietsch\n\n[These files] are hereby placed in the public domain without restrictions. Just\ngive the author credit, don't claim you wrote it or prevent anyone else from\nusing it.\n"
  },
  {
    "path": "third_party/getopt/README.crashpad",
    "content": "Name: Gregory Pietsch getopt\nShort Name: getopt\nURL: https://sourceware.org/ml/newlib/2005/msg00758.html\nLicense: Public domain\nLicense File: LICENSE\nSecurity Critical: no\nShipped: yes\n\nDescription:\nA public domain implementation of getopt.\n\nLocal Modifications:\n - Minor compilation fixes applied for Windows.\n - NO_ARG, REQUIRED_ARG, and OPTIONAL_ARG were renamed to the more traditional\n   no_argument, required_argument, and optional_argument for source\n   compatibility with BSD and glibc getopt_long().\n - Add copy of copyright (Public domain) to the top of both files for Chromium's\n   checklicenses step.\n - Compiled as .cc, and wrapped in namespace crashpad.\n - memcmp() -> strncmp() in getopt.cc to make ASan happier about some string\n   manipulation.\n"
  },
  {
    "path": "third_party/getopt/getopt.cc",
    "content": "/*\nCopyright (C) 1997 Gregory Pietsch\n\n[These files] are hereby placed in the public domain without restrictions. Just\ngive the author credit, don't claim you wrote it or prevent anyone else from\nusing it.\n*/\n\n/****************************************************************************\n\ngetopt.c - Read command line options\n\nAUTHOR: Gregory Pietsch\nCREATED Fri Jan 10 21:13:05 1997\n\nDESCRIPTION:\n\nThe getopt() function parses the command line arguments.  Its arguments argc\nand argv are the argument count and array as passed to the main() function\non program invocation.  The argument optstring is a list of available option\ncharacters.  If such a character is followed by a colon (`:'), the option\ntakes an argument, which is placed in optarg.  If such a character is\nfollowed by two colons, the option takes an optional argument, which is\nplaced in optarg.  If the option does not take an argument, optarg is NULL.\n\nThe external variable optind is the index of the next array element of argv\nto be processed; it communicates from one call to the next which element to\nprocess.\n\nThe getopt_long() function works like getopt() except that it also accepts\nlong options started by two dashes `--'.  If these take values, it is either\nin the form\n\n--arg=value\n\n or\n\n--arg value\n\nIt takes the additional arguments longopts which is a pointer to the first\nelement of an array of type GETOPT_LONG_OPTION_T.  The last element of the\narray has to be filled with NULL for the name field.\n\nThe longind pointer points to the index of the current long option relative\nto longopts if it is non-NULL.\n\nThe getopt() function returns the option character if the option was found\nsuccessfully, `:' if there was a missing parameter for one of the options,\n`?' for an unknown option character, and EOF for the end of the option list.\n\nThe getopt_long() function's return value is described in the header file.\n\nThe function getopt_long_only() is identical to getopt_long(), except that a\nplus sign `+' can introduce long options as well as `--'.\n\nThe following describes how to deal with options that follow non-option\nargv-elements.\n\nIf the caller did not specify anything, the default is REQUIRE_ORDER if the\nenvironment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.\n\nREQUIRE_ORDER means don't recognize them as options; stop option processing\nwhen the first non-option is seen.  This is what Unix does.  This mode of\noperation is selected by either setting the environment variable\nPOSIXLY_CORRECT, or using `+' as the first character of the optstring\nparameter.\n\nPERMUTE is the default.  We permute the contents of ARGV as we scan, so that\neventually all the non-options are at the end.  This allows options to be\ngiven in any order, even with programs that were not written to expect this.\n\nRETURN_IN_ORDER is an option available to programs that were written to\nexpect options and other argv-elements in any order and that care about the\nordering of the two.  We describe each non-option argv-element as if it were\nthe argument of an option with character code 1.  Using `-' as the first\ncharacter of the optstring parameter selects this mode of operation.\n\nThe special argument `--' forces an end of option-scanning regardless of the\nvalue of ordering.  In the case of RETURN_IN_ORDER, only `--' can cause\ngetopt() and friends to return EOF with optind != argc.\n\nCOPYRIGHT NOTICE AND DISCLAIMER:\n\nCopyright (C) 1997 Gregory Pietsch\n\nThis file and the accompanying getopt.h header file are hereby placed in the\npublic domain without restrictions.  Just give the author credit, don't\nclaim you wrote it or prevent anyone else from using it.\n\nGregory Pietsch's current e-mail address:\ngpietsch@comcast.net\n****************************************************************************/\n\n/* include files */\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#ifndef GETOPT_H\n#include \"getopt.h\"\n#endif\n\nnamespace crashpad {\n\n/* macros */\n\n/* types */\ntypedef enum GETOPT_ORDERING_T\n{\n  PERMUTE,\n  RETURN_IN_ORDER,\n  REQUIRE_ORDER\n} GETOPT_ORDERING_T;\n\n/* globally-defined variables */\nchar *optarg = NULL;\nint optind = 0;\nint opterr = 1;\nint optopt = '?';\n\n/* functions */\n\n/* reverse_argv_elements:  reverses num elements starting at argv */\nstatic void\nreverse_argv_elements (char **argv, int num)\n{\n  int i;\n  char *tmp;\n\n  for (i = 0; i < (num >> 1); i++)\n    {\n      tmp = argv[i];\n      argv[i] = argv[num - i - 1];\n      argv[num - i - 1] = tmp;\n    }\n}\n\n/* permute: swap two blocks of argv-elements given their lengths */\nstatic void\npermute (char **argv, int len1, int len2)\n{\n  reverse_argv_elements (argv, len1);\n  reverse_argv_elements (argv, len1 + len2);\n  reverse_argv_elements (argv, len2);\n}\n\n/* is_option: is this argv-element an option or the end of the option list? */\nstatic int\nis_option (char *argv_element, int only)\n{\n  return ((argv_element == NULL)\n          || (argv_element[0] == '-') || (only && argv_element[0] == '+'));\n}\n\n/* getopt_internal:  the function that does all the dirty work */\nstatic int\ngetopt_internal (int argc, char **argv, char *shortopts,\n                 GETOPT_LONG_OPTION_T * longopts, int *longind, int only)\n{\n  GETOPT_ORDERING_T ordering = PERMUTE;\n  static size_t optwhere = 0;\n  size_t permute_from = 0;\n  int num_nonopts = 0;\n  int optindex = 0;\n  size_t match_chars = 0;\n  char *possible_arg = NULL;\n  int longopt_match = -1;\n  int has_arg = -1;\n  char *cp = NULL;\n  int arg_next = 0;\n\n  /* first, deal with silly parameters and easy stuff */\n  if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))\n    return (optopt = '?');\n  if (optind >= argc || argv[optind] == NULL)\n    return EOF;\n  if (strcmp (argv[optind], \"--\") == 0)\n    {\n      optind++;\n      return EOF;\n    }\n  /* if this is our first time through */\n  if (optind == 0) {\n    optind = 1;\n    optwhere = 1;\n  }\n\n  /* define ordering */\n  if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))\n    {\n      ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;\n      shortopts++;\n    }\n  else\n    ordering = (getenv (\"POSIXLY_CORRECT\") != NULL) ? REQUIRE_ORDER : PERMUTE;\n\n  /*\n   * based on ordering, find our next option, if we're at the beginning of\n   * one\n   */\n  if (optwhere == 1)\n    {\n      switch (ordering)\n        {\n        case PERMUTE:\n          permute_from = optind;\n          num_nonopts = 0;\n          while (!is_option (argv[optind], only))\n            {\n              optind++;\n              num_nonopts++;\n            }\n          if (argv[optind] == NULL)\n            {\n              /* no more options */\n              optind = (int)permute_from;\n              return EOF;\n            }\n          else if (strcmp (argv[optind], \"--\") == 0)\n            {\n              /* no more options, but have to get `--' out of the way */\n              permute (argv + permute_from, num_nonopts, 1);\n              optind = (int)(permute_from + 1);\n              return EOF;\n            }\n          break;\n        case RETURN_IN_ORDER:\n          if (!is_option (argv[optind], only))\n            {\n              optarg = argv[optind++];\n              return (optopt = 1);\n            }\n          break;\n        case REQUIRE_ORDER:\n          if (!is_option (argv[optind], only))\n            return EOF;\n          break;\n        }\n    }\n  /* we've got an option, so parse it */\n\n  /* first, is it a long option? */\n  if (longopts != NULL\n      && (strncmp (argv[optind], \"--\", 2) == 0\n          || (only && argv[optind][0] == '+')) && optwhere == 1)\n    {\n      /* handle long options */\n      if (strncmp (argv[optind], \"--\", 2) == 0)\n        optwhere = 2;\n      longopt_match = -1;\n      possible_arg = strchr (argv[optind] + optwhere, '=');\n      if (possible_arg == NULL)\n        {\n          /* no =, so next argv might be arg */\n          match_chars = strlen (argv[optind]);\n          possible_arg = argv[optind] + match_chars;\n          match_chars = match_chars - optwhere;\n        }\n      else\n        match_chars = (possible_arg - argv[optind]) - optwhere;\n      for (optindex = 0; longopts[optindex].name != NULL; optindex++)\n        {\n          if (strncmp (argv[optind] + optwhere,\n                       longopts[optindex].name, match_chars) == 0)\n            {\n              /* do we have an exact match? */\n              if (match_chars == strlen (longopts[optindex].name))\n                {\n                  longopt_match = optindex;\n                  break;\n                }\n              /* do any characters match? */\n              else\n                {\n                  if (longopt_match < 0)\n                    longopt_match = optindex;\n                  else\n                    {\n                      /* we have ambiguous options */\n                      if (opterr)\n                        fprintf (stderr, \"%s: option `%s' is ambiguous \"\n                                 \"(could be `--%s' or `--%s')\\n\",\n                                 argv[0],\n                                 argv[optind],\n                                 longopts[longopt_match].name,\n                                 longopts[optindex].name);\n                      return (optopt = '?');\n                    }\n                }\n            }\n        }\n      if (longopt_match >= 0)\n        has_arg = longopts[longopt_match].has_arg;\n    }\n  /* if we didn't find a long option, is it a short option? */\n  if (longopt_match < 0 && shortopts != NULL)\n    {\n      cp = strchr (shortopts, argv[optind][optwhere]);\n      if (cp == NULL)\n        {\n          /* couldn't find option in shortopts */\n          if (opterr)\n            fprintf (stderr,\n                     \"%s: invalid option -- `-%c'\\n\",\n                     argv[0], argv[optind][optwhere]);\n          optwhere++;\n          if (argv[optind][optwhere] == '\\0')\n            {\n              optind++;\n              optwhere = 1;\n            }\n          return (optopt = '?');\n        }\n        has_arg = ((cp[1] == ':') ? ((cp[2] == ':') ? optional_argument\n                                                    : required_argument)\n                                  : no_argument);\n        possible_arg = argv[optind] + optwhere + 1;\n        optopt = *cp;\n    }\n  /* get argument and reset optwhere */\n  arg_next = 0;\n  switch (has_arg)\n    {\n    case optional_argument:\n      if (*possible_arg == '=')\n        possible_arg++;\n      if (*possible_arg != '\\0')\n        {\n          optarg = possible_arg;\n          optwhere = 1;\n        }\n      else\n        optarg = NULL;\n      break;\n    case required_argument:\n      if (*possible_arg == '=')\n        possible_arg++;\n      if (*possible_arg != '\\0')\n        {\n          optarg = possible_arg;\n          optwhere = 1;\n        }\n      else if (optind + 1 >= argc)\n        {\n          if (opterr)\n            {\n              fprintf (stderr, \"%s: argument required for option `\", argv[0]);\n              if (longopt_match >= 0)\n                fprintf (stderr, \"--%s'\\n\", longopts[longopt_match].name);\n              else\n                fprintf (stderr, \"-%c'\\n\", *cp);\n            }\n          optind++;\n          return (optopt = ':');\n        }\n      else\n        {\n          optarg = argv[optind + 1];\n          arg_next = 1;\n          optwhere = 1;\n        }\n      break;\n    case no_argument:\n      if (longopt_match < 0)\n        {\n          optwhere++;\n          if (argv[optind][optwhere] == '\\0')\n            optwhere = 1;\n        }\n      else\n        optwhere = 1;\n      optarg = NULL;\n      break;\n    }\n\n  /* do we have to permute or otherwise modify optind? */\n  if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)\n    {\n      permute (argv + permute_from, num_nonopts, 1 + arg_next);\n      optind = (int)(permute_from + 1 + arg_next);\n    }\n  else if (optwhere == 1)\n    optind = optind + 1 + arg_next;\n\n  /* finally return */\n  if (longopt_match >= 0)\n    {\n      if (longind != NULL)\n        *longind = longopt_match;\n      if (longopts[longopt_match].flag != NULL)\n        {\n          *(longopts[longopt_match].flag) = longopts[longopt_match].val;\n          return 0;\n        }\n      else\n        return longopts[longopt_match].val;\n    }\n  else\n    return optopt;\n}\n\nint\ngetopt (int argc, char **argv, char *optstring)\n{\n  return getopt_internal (argc, argv, optstring, NULL, NULL, 0);\n}\n\nint\ngetopt_long (int argc, char **argv, const char *shortopts,\n             const GETOPT_LONG_OPTION_T * longopts, int *longind)\n{\n  return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0);\n}\n\nint\ngetopt_long_only (int argc, char **argv, const char *shortopts,\n                  const GETOPT_LONG_OPTION_T * longopts, int *longind)\n{\n  return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1);\n}\n\n}  // namespace crashpad\n\n/* end of file GETOPT.C */\n"
  },
  {
    "path": "third_party/getopt/getopt.h",
    "content": "/*\nCopyright (C) 1997 Gregory Pietsch\n\n[These files] are hereby placed in the public domain without restrictions. Just\ngive the author credit, don't claim you wrote it or prevent anyone else from\nusing it.\n*/\n\n#ifndef GETOPT_H\n#define GETOPT_H\n\n/* include files needed by this include file */\n\n/* macros defined by this include file */\n#define no_argument       0\n#define required_argument 1\n#define optional_argument 2\n\n/* types defined by this include file */\n\nnamespace crashpad {\n\n/* GETOPT_LONG_OPTION_T: The type of long option */\ntypedef struct GETOPT_LONG_OPTION_T\n{\n  const char *name;             /* the name of the long option */\n  int has_arg;                  /* one of the above macros */\n  int *flag;                    /* determines if getopt_long() returns a\n                                 * value for a long option; if it is\n                                 * non-NULL, 0 is returned as a function\n                                 * value and the value of val is stored in\n                                 * the area pointed to by flag.  Otherwise,\n                                 * val is returned. */\n  int val;                      /* determines the value to return if flag is\n                                 * NULL. */\n} GETOPT_LONG_OPTION_T;\n\ntypedef GETOPT_LONG_OPTION_T option;\n\n/* externally-defined variables */\nextern char *optarg;\nextern int optind;\nextern int opterr;\nextern int optopt;\n\n/* function prototypes */\nint getopt(int argc, char** argv, char* optstring);\nint getopt_long(int argc,\n                char** argv,\n                const char* shortopts,\n                const GETOPT_LONG_OPTION_T* longopts,\n                int* longind);\nint getopt_long_only(int argc,\n                     char** argv,\n                     const char* shortopts,\n                     const GETOPT_LONG_OPTION_T* longopts,\n                     int* longind);\n\n}  // namespace crashpad\n\n#endif /* GETOPT_H */\n\n/* END OF FILE getopt.h */\n"
  },
  {
    "path": "third_party/googletest/BUILD.gn",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../build/crashpad_buildconfig.gni\")\nimport(\"../../build/test.gni\")\n\nif (crashpad_is_in_chromium) {\n  group(\"googletest\") {\n    testonly = true\n    public_deps = [ \"//testing/gtest\" ]\n  }\n  group(\"googlemock\") {\n    testonly = true\n    public_deps = [ \"//testing/gmock\" ]\n  }\n} else if (crashpad_is_in_dart || crashpad_is_in_fuchsia) {\n  group(\"googletest\") {\n    testonly = true\n    public_deps = [ \"//third_party/googletest:gtest\" ]\n  }\n  group(\"googlemock\") {\n    testonly = true\n    public_deps = [ \"//third_party/googletest:gmock\" ]\n  }\n} else if (crashpad_is_standalone || crashpad_is_external) {\n  if (crashpad_is_standalone) {\n    googletest_dir = \"googletest\"\n    mini_chromium_dir = \"//third_party/mini_chromium/mini_chromium\"\n  } else if (crashpad_is_external) {\n    googletest_dir = \"../../../../googletest\"\n    mini_chromium_dir = \"//../../mini_chromium/mini_chromium\"\n  }\n\n  config(\"googletest_private_config\") {\n    visibility = [ \":*\" ]\n    include_dirs = [ \"$googletest_dir/googletest\" ]\n    defines = [ \"GUNIT_NO_GOOGLE3=1\" ]\n  }\n\n  config(\"googletest_public_config\") {\n    include_dirs = [ \"$googletest_dir/googletest/include\" ]\n  }\n\n  static_library(\"googletest\") {\n    testonly = true\n    sources = [\n      \"$googletest_dir/googletest/include/gtest/gtest-assertion-result.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest-death-test.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest-matchers.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest-message.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest-param-test.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest-printers.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest-spi.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest-test-part.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest-typed-test.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest_pred_impl.h\",\n      \"$googletest_dir/googletest/include/gtest/gtest_prod.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/custom/gtest-port.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/custom/gtest-printers.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/custom/gtest.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/gtest-death-test-internal.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/gtest-filepath.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/gtest-internal.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/gtest-param-util.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/gtest-port-arch.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/gtest-port.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/gtest-string.h\",\n      \"$googletest_dir/googletest/include/gtest/internal/gtest-type-util.h\",\n      \"$googletest_dir/googletest/src/gtest-all.cc\",\n      \"$googletest_dir/googletest/src/gtest-assertion-result.cc\",\n      \"$googletest_dir/googletest/src/gtest-death-test.cc\",\n      \"$googletest_dir/googletest/src/gtest-filepath.cc\",\n      \"$googletest_dir/googletest/src/gtest-internal-inl.h\",\n      \"$googletest_dir/googletest/src/gtest-matchers.cc\",\n      \"$googletest_dir/googletest/src/gtest-port.cc\",\n      \"$googletest_dir/googletest/src/gtest-printers.cc\",\n      \"$googletest_dir/googletest/src/gtest-test-part.cc\",\n      \"$googletest_dir/googletest/src/gtest-typed-test.cc\",\n      \"$googletest_dir/googletest/src/gtest.cc\",\n    ]\n    sources -= [ \"$googletest_dir/googletest/src/gtest-all.cc\" ]\n    public_configs = [ \":googletest_public_config\" ]\n    configs -= [ \"$mini_chromium_dir/build/config:Wexit_time_destructors\" ]\n    configs += [ \":googletest_private_config\" ]\n    if (crashpad_is_fuchsia) {\n      deps = [ \"../fuchsia\" ]\n    }\n  }\n\n  static_library(\"googletest_main\") {\n    # Tests outside of this file should use ../../test:googletest_main instead.\n    visibility = [ \":*\" ]\n\n    testonly = true\n    sources = [ \"$googletest_dir/googletest/src/gtest_main.cc\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_all_test\") {\n    sources = [\n      \"$googletest_dir/googletest/test/googletest-death-test-test.cc\",\n      \"$googletest_dir/googletest/test/googletest-filepath-test.cc\",\n      \"$googletest_dir/googletest/test/googletest-message-test.cc\",\n      \"$googletest_dir/googletest/test/googletest-options-test.cc\",\n      \"$googletest_dir/googletest/test/googletest-port-test.cc\",\n      \"$googletest_dir/googletest/test/googletest-test-part-test.cc\",\n      \"$googletest_dir/googletest/test/gtest-typed-test2_test.cc\",\n      \"$googletest_dir/googletest/test/gtest-typed-test_test.cc\",\n      \"$googletest_dir/googletest/test/gtest-typed-test_test.h\",\n      \"$googletest_dir/googletest/test/gtest_main_unittest.cc\",\n      \"$googletest_dir/googletest/test/gtest_pred_impl_unittest.cc\",\n      \"$googletest_dir/googletest/test/gtest_prod_test.cc\",\n      \"$googletest_dir/googletest/test/gtest_skip_test.cc\",\n      \"$googletest_dir/googletest/test/gtest_unittest.cc\",\n      \"$googletest_dir/googletest/test/production.cc\",\n      \"$googletest_dir/googletest/test/production.h\",\n    ]\n\n    if (!crashpad_is_win) {\n      # TODO: Fix error C2015: too many characters in constant. As this error\n      # cannot be suppressed, removing the test on Windows. See\n      # https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2855854/2\n      # for details.\n      sources +=\n          [ \"$googletest_dir/googletest/test/googletest-printers-test.cc\" ]\n    }\n    configs -= [ \"$mini_chromium_dir/build/config:Wexit_time_destructors\" ]\n    configs += [ \":googletest_private_config\" ]\n    deps = [\n      \":googletest\",\n      \":googletest_main\",\n    ]\n\n    if (crashpad_is_win) {\n      cflags = [ \"/wd4702\" ]  # unreachable code\n    }\n  }\n\n  test(\"gtest_environment_test\") {\n    sources = [ \"$googletest_dir/googletest/test/gtest_environment_test.cc\" ]\n    configs += [ \":googletest_private_config\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_listener_test\") {\n    sources = [ \"$googletest_dir/googletest/test/googletest-listener-test.cc\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_macro_stack_footprint_test\") {\n    sources = [ \"$googletest_dir/googletest/test/gtest_test_macro_stack_footprint_test.cc\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_no_test\") {\n    sources = [ \"$googletest_dir/googletest/test/gtest_no_test_unittest.cc\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_param_test\") {\n    sources = [\n      \"$googletest_dir/googletest/test/googletest-param-test-test.cc\",\n      \"$googletest_dir/googletest/test/googletest-param-test-test.h\",\n      \"$googletest_dir/googletest/test/googletest-param-test2-test.cc\",\n    ]\n    configs -= [ \"$mini_chromium_dir/build/config:Wexit_time_destructors\" ]\n    configs += [ \":googletest_private_config\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_premature_exit_test\") {\n    sources = [ \"$googletest_dir/googletest/test/gtest_premature_exit_test.cc\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_repeat_test\") {\n    sources = [ \"$googletest_dir/googletest/test/gtest_repeat_test.cc\" ]\n    configs += [ \":googletest_private_config\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_skip_in_environment_setup_test\") {\n    sources = [\n      \"$googletest_dir/googletest/test/gtest_skip_in_environment_setup_test.cc\",\n    ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_sole_header_test\") {\n    sources = [ \"$googletest_dir/googletest/test/gtest_sole_header_test.cc\" ]\n    deps = [\n      \":googletest\",\n      \":googletest_main\",\n    ]\n  }\n\n  test(\"gtest_stress_test\") {\n    sources = [ \"$googletest_dir/googletest/test/gtest_stress_test.cc\" ]\n    configs += [ \":googletest_private_config\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  test(\"gtest_unittest_api_test\") {\n    sources = [ \"$googletest_dir/googletest/test/gtest-unittest-api_test.cc\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  group(\"googletest_all_tests\") {\n    testonly = true\n    deps = [\n      \":gtest_all_test\",\n      \":gtest_environment_test\",\n      \":gtest_listener_test\",\n      \":gtest_macro_stack_footprint_test\",\n      \":gtest_no_test\",\n      \":gtest_param_test\",\n      \":gtest_premature_exit_test\",\n      \":gtest_repeat_test\",\n      \":gtest_skip_in_environment_setup_test\",\n      \":gtest_sole_header_test\",\n      \":gtest_stress_test\",\n      \":gtest_unittest_api_test\",\n    ]\n  }\n\n  config(\"googlemock_private_config\") {\n    visibility = [ \":*\" ]\n    include_dirs = [ \"$googletest_dir/googlemock\" ]\n  }\n\n  config(\"googlemock_public_config\") {\n    include_dirs = [ \"$googletest_dir/googlemock/include\" ]\n  }\n\n  static_library(\"googlemock\") {\n    testonly = true\n    sources = [\n      \"$googletest_dir/googlemock/include/gmock/gmock-actions.h\",\n      \"$googletest_dir/googlemock/include/gmock/gmock-cardinalities.h\",\n      \"$googletest_dir/googlemock/include/gmock/gmock-function-mocker.h\",\n      \"$googletest_dir/googlemock/include/gmock/gmock-matchers.h\",\n      \"$googletest_dir/googlemock/include/gmock/gmock-more-actions.h\",\n      \"$googletest_dir/googlemock/include/gmock/gmock-more-matchers.h\",\n      \"$googletest_dir/googlemock/include/gmock/gmock-nice-strict.h\",\n      \"$googletest_dir/googlemock/include/gmock/gmock-spec-builders.h\",\n      \"$googletest_dir/googlemock/include/gmock/gmock.h\",\n      \"$googletest_dir/googlemock/include/gmock/internal/custom/gmock-generated-actions.h\",\n      \"$googletest_dir/googlemock/include/gmock/internal/custom/gmock-matchers.h\",\n      \"$googletest_dir/googlemock/include/gmock/internal/custom/gmock-port.h\",\n      \"$googletest_dir/googlemock/include/gmock/internal/gmock-internal-utils.h\",\n      \"$googletest_dir/googlemock/include/gmock/internal/gmock-port.h\",\n      \"$googletest_dir/googlemock/include/gmock/internal/gmock-pp.h\",\n      \"$googletest_dir/googlemock/src/gmock-all.cc\",\n      \"$googletest_dir/googlemock/src/gmock-cardinalities.cc\",\n      \"$googletest_dir/googlemock/src/gmock-internal-utils.cc\",\n      \"$googletest_dir/googlemock/src/gmock-matchers.cc\",\n      \"$googletest_dir/googlemock/src/gmock-spec-builders.cc\",\n      \"$googletest_dir/googlemock/src/gmock.cc\",\n    ]\n    sources -= [ \"$googletest_dir/googlemock/src/gmock-all.cc\" ]\n    public_configs = [ \":googlemock_public_config\" ]\n    configs -= [ \"$mini_chromium_dir/build/config:Wexit_time_destructors\" ]\n    configs += [ \":googlemock_private_config\" ]\n    deps = [ \":googletest\" ]\n  }\n\n  static_library(\"googlemock_main\") {\n    # Tests outside of this file should use ../../test:googlemock_main instead.\n    visibility = [ \":*\" ]\n    testonly = true\n    sources = [ \"$googletest_dir/googlemock/src/gmock_main.cc\" ]\n    deps = [\n      \":googlemock\",\n      \":googletest\",\n    ]\n  }\n\n  test(\"gmock_all_test\") {\n    sources = [\n      \"$googletest_dir/googlemock/test/gmock-actions_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-cardinalities_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-function-mocker_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-internal-utils_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-matchers-arithmetic_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-matchers-comparisons_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-matchers-containers_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-matchers-misc_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-matchers_test.h\",\n      \"$googletest_dir/googlemock/test/gmock-more-actions_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-nice-strict_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-port_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-pp-string_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-pp_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock-spec-builders_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock_test.cc\",\n    ]\n    configs += [\n      \":googlemock_private_config\",\n      \":googletest_private_config\",\n    ]\n    deps = [\n      \":googlemock\",\n      \":googlemock_main\",\n      \":googletest\",\n    ]\n\n    if (crashpad_is_clang) {\n      cflags_cc = [\n        # googletest/googlemock/test/gmock-function-mocker_test.cc does not\n        # always use the override modifier with MOCK_METHOD.\n        \"-Wno-inconsistent-missing-override\",\n\n        # For googlemock/test/gmock-matchers-misc_test.cc comparison of\n        # integers of different signs.\n        \"-Wno-sign-compare\",\n      ]\n    }\n\n    if (crashpad_is_win) {\n      cflags = [\n        # TODO: Correct SDK in vc\\tools\\msvc\\14.14.26428\\include\\functional\n        \"/wd4789\",  # VAR of size N bytes will be overrun\n\n        # For googlemock/test/gmock-matchers-misc_test.cc comparison of\n        # integers of different signs.\n        \"/wd4018\",\n      ]\n    }\n  }\n\n  test(\"gmock_link_test\") {\n    sources = [\n      \"$googletest_dir/googlemock/test/gmock_link2_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock_link_test.cc\",\n      \"$googletest_dir/googlemock/test/gmock_link_test.h\",\n    ]\n    configs += [ \":googlemock_private_config\" ]\n    deps = [\n      \":googlemock\",\n      \":googlemock_main\",\n      \":googletest\",\n    ]\n  }\n\n  test(\"gmock_stress_test\") {\n    sources = [ \"$googletest_dir/googlemock/test/gmock_stress_test.cc\" ]\n    configs -= [ \"$mini_chromium_dir/build/config:Wexit_time_destructors\" ]\n    configs += [ \":googlemock_private_config\" ]\n    deps = [\n      \":googlemock\",\n      \":googletest\",\n    ]\n  }\n\n  group(\"googlemock_all_tests\") {\n    testonly = true\n    deps = [\n      \":gmock_all_test\",\n      \":gmock_link_test\",\n      \":gmock_stress_test\",\n    ]\n  }\n}\n"
  },
  {
    "path": "third_party/googletest/README.crashpad",
    "content": "Name: Google Test\nShort Name: googletest\nURL: https://github.com/google/googletest/\nRevision: See DEPS\nLicense: BSD 3-clause\nLicense File: googletest/googletest/LICENSE\nSecurity Critical: no\nShipped: no\n\nDescription:\nGoogle Test (Google C++ Testing Framework) is Google’s framework for writing C++\ntests on a variety of platforms. It includes Google Mock, an extension for\nwriting and using C++ mock classes.\n\nLocal Modifications:\nNone\n"
  },
  {
    "path": "third_party/linux/README.md",
    "content": "This directory is a placeholder for Fuchsia tools that will be downloaded by\nCIPD (https://github.com/luci/luci-go/tree/master/cipd). The CIPD update happens\nas part of gclient runhooks.\n"
  },
  {
    "path": "third_party/lss/BUILD.gn",
    "content": "# Copyright 2019 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../build/crashpad_buildconfig.gni\")\n\nconfig(\"lss_config\") {\n  if (crashpad_is_in_chromium) {\n    defines = [ \"CRASHPAD_LSS_SOURCE_EXTERNAL\" ]\n  } else if (crashpad_is_in_fuchsia) {\n    defines = [ \"CRASHPAD_LSS_SOURCE_FUCHSIA\" ]\n  } else {\n    defines = [ \"CRASHPAD_LSS_SOURCE_EMBEDDED\" ]\n  }\n}\n\nsource_set(\"lss\") {\n  public_configs = [ \":lss_config\" ]\n\n  sources = [ \"lss.h\" ]\n}\n"
  },
  {
    "path": "third_party/lss/README.crashpad",
    "content": "Name: linux-syscall-support\nShort Name: lss\nURL: https://chromium.googlesource.com/linux-syscall-support/\nRevision: See DEPS\nLicense: BSD 3-clause\nLicense File: lss/linux_syscall_support.h\nSecurity Critical: yes\nShipped: yes\n\nDescription:\nEvery so often, projects need to directly embed Linux system calls instead of\ncalling the implementations in the system runtime library. This project\nprovides a header file that can be included into your application whenever you\nneed to make direct system calls.\n\nLocal Modifications:\nNone.\n"
  },
  {
    "path": "third_party/lss/lss.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_THIRD_PARTY_LSS_LSS_H_\n#define CRASHPAD_THIRD_PARTY_LSS_LSS_H_\n\n#if defined(CRASHPAD_LSS_SOURCE_EXTERNAL)\n#include \"third_party/lss/linux_syscall_support.h\"\n#elif defined(CRASHPAD_LSS_SOURCE_EMBEDDED)\n#include \"third_party/lss/lss/linux_syscall_support.h\"\n#elif defined(CRASHPAD_LSS_SOURCE_FUCHSIA)\n#include \"../../../../../third_party/linux-syscall-support/src/linux_syscall_support.h\"\n#else\n#error Unknown lss source\n#endif\n\n#endif  // CRASHPAD_THIRD_PARTY_LSS_LSS_H_\n"
  },
  {
    "path": "third_party/mini_chromium/BUILD.gn",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../build/crashpad_buildconfig.gni\")\n\ngroup(\"base\") {\n  if (crashpad_is_in_chromium) {\n    public_deps = [ \"//base\" ]\n  } else if (crashpad_is_standalone) {\n    public_deps = [ \"mini_chromium/base\" ]\n  } else if (crashpad_is_in_fuchsia) {\n    public_deps = [ mini_chromium_import_root + \"/base\" ]\n  } else if (crashpad_is_external) {\n    public_deps = [ \"../../../../mini_chromium/mini_chromium/base\" ]\n  } else if (crashpad_is_in_dart) {\n    public_deps = [ \"//third_party/mini_chromium/mini_chromium/base\" ]\n  }\n}\n\ngroup(\"base_test_support\") {\n  testonly = true\n\n  if (crashpad_is_in_chromium) {\n    public_deps = [ \"//base/test:test_support\" ]\n  }\n}\n\ngroup(\"build\") {\n  if (crashpad_is_in_chromium) {\n    # Chromium has no build target.\n  } else if (crashpad_is_standalone) {\n    public_deps = [ \"mini_chromium/build\" ]\n  } else if (crashpad_is_in_fuchsia) {\n    public_deps = [ mini_chromium_import_root + \"/build\" ]\n  } else if (crashpad_is_external) {\n    public_deps = [ \"../../../../mini_chromium/mini_chromium/build\" ]\n  } else if (crashpad_is_in_dart) {\n    public_deps = [ \"//third_party/mini_chromium/mini_chromium/build\" ]\n  }\n}\n"
  },
  {
    "path": "third_party/mini_chromium/README.crashpad",
    "content": "Name: mini_chromium\nShort Name: mini_chromium\nURL: https://chromium.googlesource.com/chromium/mini_chromium/\nRevision: See DEPS\nLicense: BSD 3-clause\nLicense File: mini_chromium/LICENSE\nSecurity Critical: yes\nShipped in Chromium: no\n\nDescription:\nmini_chromium is a small collection of useful low-level (“base”) routines from\nthe Chromium open-source project at https://www.chromium.org/Home. Chromium is\nlarge, sprawling, full of dependencies, and a web browser. mini_chromium is\nsmall, self-contained, and a library. mini_chromium is especially useful as a\ndependency of other code that wishes to use Chromium’s base routines.\n\nLocal Modifications:\nNone\n"
  },
  {
    "path": "third_party/ninja/README.crashpad",
    "content": "Name: ninja\nShort Name: ninja\nURL: https://github.com/ninja-build/ninja\nRevision: See the CIPD version in DEPS\nLicense: Apache License 2.0\nLicense File: https://github.com/ninja-build/ninja/blob/master/COPYING\nSecurity Critical: no\nShipped: no\n\nDescription:\nNinja is a small build system with a focus on speed, and is used to build\nthis project.\n\nThe CIPD packages are built using the 3pp pipeline.\nhttps://chromium.googlesource.com/infra/infra/+/refs/heads/main/3pp/ninja/\n\nLocal Modifications:\nNone\n"
  },
  {
    "path": "third_party/ninja/ninja",
    "content": "#!/bin/sh\n\n# Copyright 2022 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -eu\n\nOS=\"$(uname -s)\"\nTHIS_DIR=\"$(dirname \"${0}\")\"\n\nprint_help() {\ncat <<EOF >&2\nNo ninja binary is available for this system.\nTry building your own binary by doing:\n  cd ~\n  git clone https://github.com/ninja-build/ninja.git\n  cd ninja && ./configure.py --bootstrap\nThen add ~/ninja/ to your PATH.\nEOF\n}\n\ncase \"${OS}\" in\n  Linux)\n    exec \"${THIS_DIR}/linux/ninja\" \"$@\";;\n  Darwin)\n    ARCH=\"$(uname -m)\"\n    case \"${ARCH}\" in\n      x86_64)\n        exec \"${THIS_DIR}/mac-amd64/ninja\" \"$@\";;\n      arm64)\n        exec \"${THIS_DIR}/mac-arm64/ninja\" \"$@\";;\n      *)\n        echo \"Unsupported architecture ${ARCH}\" >&2\n        print_help\n        exit 1;;\n    esac\n    ;;\n  *)\n    echo \"Unsupported OS ${OS}\" >&2\n    print_help\n    exit 1;;\nesac\n"
  },
  {
    "path": "third_party/xnu/APPLE_LICENSE",
    "content": "APPLE PUBLIC SOURCE LICENSE\nVersion 2.0 - August 6, 2003\n\nPlease read this License carefully before downloading this software.\nBy downloading or using this software, you are agreeing to be bound by\nthe terms of this License. If you do not or cannot agree to the terms\nof this License, please do not download or use the software.\n\n1. General; Definitions. This License applies to any program or other\nwork which Apple Computer, Inc. (\"Apple\") makes publicly available and\nwhich contains a notice placed by Apple identifying such program or\nwork as \"Original Code\" and stating that it is subject to the terms of\nthis Apple Public Source License version 2.0 (\"License\"). As used in\nthis License:\n\n1.1 \"Applicable Patent Rights\" mean: (a) in the case where Apple is\nthe grantor of rights, (i) claims of patents that are now or hereafter\nacquired, owned by or assigned to Apple and (ii) that cover subject\nmatter contained in the Original Code, but only to the extent\nnecessary to use, reproduce and/or distribute the Original Code\nwithout infringement; and (b) in the case where You are the grantor of\nrights, (i) claims of patents that are now or hereafter acquired,\nowned by or assigned to You and (ii) that cover subject matter in Your\nModifications, taken alone or in combination with Original Code.\n\n1.2 \"Contributor\" means any person or entity that creates or\ncontributes to the creation of Modifications.\n\n1.3 \"Covered Code\" means the Original Code, Modifications, the\ncombination of Original Code and any Modifications, and/or any\nrespective portions thereof.\n\n1.4 \"Externally Deploy\" means: (a) to sublicense, distribute or\notherwise make Covered Code available, directly or indirectly, to\nanyone other than You; and/or (b) to use Covered Code, alone or as\npart of a Larger Work, in any way to provide a service, including but\nnot limited to delivery of content, through electronic communication\nwith a client other than You.\n\n1.5 \"Larger Work\" means a work which combines Covered Code or portions\nthereof with code not governed by the terms of this License.\n\n1.6 \"Modifications\" mean any addition to, deletion from, and/or change\nto, the substance and/or structure of the Original Code, any previous\nModifications, the combination of Original Code and any previous\nModifications, and/or any respective portions thereof. When code is\nreleased as a series of files, a Modification is: (a) any addition to\nor deletion from the contents of a file containing Covered Code;\nand/or (b) any new file or other representation of computer program\nstatements that contains any part of Covered Code.\n\n1.7 \"Original Code\" means (a) the Source Code of a program or other\nwork as originally made available by Apple under this License,\nincluding the Source Code of any updates or upgrades to such programs\nor works made available by Apple under this License, and that has been\nexpressly identified by Apple as such in the header file(s) of such\nwork; and (b) the object code compiled from such Source Code and\noriginally made available by Apple under this License.\n\n1.8 \"Source Code\" means the human readable form of a program or other\nwork that is suitable for making modifications to it, including all\nmodules it contains, plus any associated interface definition files,\nscripts used to control compilation and installation of an executable\n(object code).\n\n1.9 \"You\" or \"Your\" means an individual or a legal entity exercising\nrights under this License. For legal entities, \"You\" or \"Your\"\nincludes any entity which controls, is controlled by, or is under\ncommon control with, You, where \"control\" means (a) the power, direct\nor indirect, to cause the direction or management of such entity,\nwhether by contract or otherwise, or (b) ownership of fifty percent\n(50%) or more of the outstanding shares or beneficial ownership of\nsuch entity.\n\n2. Permitted Uses; Conditions & Restrictions. Subject to the terms\nand conditions of this License, Apple hereby grants You, effective on\nthe date You accept this License and download the Original Code, a\nworld-wide, royalty-free, non-exclusive license, to the extent of\nApple's Applicable Patent Rights and copyrights covering the Original\nCode, to do the following:\n\n2.1 Unmodified Code. You may use, reproduce, display, perform,\ninternally distribute within Your organization, and Externally Deploy\nverbatim, unmodified copies of the Original Code, for commercial or\nnon-commercial purposes, provided that in each instance:\n\n(a) You must retain and reproduce in all copies of Original Code the\ncopyright and other proprietary notices and disclaimers of Apple as\nthey appear in the Original Code, and keep intact all notices in the\nOriginal Code that refer to this License; and\n\n(b) You must include a copy of this License with every copy of Source\nCode of Covered Code and documentation You distribute or Externally\nDeploy, and You may not offer or impose any terms on such Source Code\nthat alter or restrict this License or the recipients' rights\nhereunder, except as permitted under Section 6.\n\n2.2 Modified Code. You may modify Covered Code and use, reproduce,\ndisplay, perform, internally distribute within Your organization, and\nExternally Deploy Your Modifications and Covered Code, for commercial\nor non-commercial purposes, provided that in each instance You also\nmeet all of these conditions:\n\n(a) You must satisfy all the conditions of Section 2.1 with respect to\nthe Source Code of the Covered Code;\n\n(b) You must duplicate, to the extent it does not already exist, the\nnotice in Exhibit A in each file of the Source Code of all Your\nModifications, and cause the modified files to carry prominent notices\nstating that You changed the files and the date of any change; and\n\n(c) If You Externally Deploy Your Modifications, You must make\nSource Code of all Your Externally Deployed Modifications either\navailable to those to whom You have Externally Deployed Your\nModifications, or publicly available. Source Code of Your Externally\nDeployed Modifications must be released under the terms set forth in\nthis License, including the license grants set forth in Section 3\nbelow, for as long as you Externally Deploy the Covered Code or twelve\n(12) months from the date of initial External Deployment, whichever is\nlonger. You should preferably distribute the Source Code of Your\nExternally Deployed Modifications electronically (e.g. download from a\nweb site).\n\n2.3 Distribution of Executable Versions. In addition, if You\nExternally Deploy Covered Code (Original Code and/or Modifications) in\nobject code, executable form only, You must include a prominent\nnotice, in the code itself as well as in related documentation,\nstating that Source Code of the Covered Code is available under the\nterms of this License with information on how and where to obtain such\nSource Code.\n\n2.4 Third Party Rights. You expressly acknowledge and agree that\nalthough Apple and each Contributor grants the licenses to their\nrespective portions of the Covered Code set forth herein, no\nassurances are provided by Apple or any Contributor that the Covered\nCode does not infringe the patent or other intellectual property\nrights of any other entity. Apple and each Contributor disclaim any\nliability to You for claims brought by any other entity based on\ninfringement of intellectual property rights or otherwise. As a\ncondition to exercising the rights and licenses granted hereunder, You\nhereby assume sole responsibility to secure any other intellectual\nproperty rights needed, if any. For example, if a third party patent\nlicense is required to allow You to distribute the Covered Code, it is\nYour responsibility to acquire that license before distributing the\nCovered Code.\n\n3. Your Grants. In consideration of, and as a condition to, the\nlicenses granted to You under this License, You hereby grant to any\nperson or entity receiving or distributing Covered Code under this\nLicense a non-exclusive, royalty-free, perpetual, irrevocable license,\nunder Your Applicable Patent Rights and other intellectual property\nrights (other than patent) owned or controlled by You, to use,\nreproduce, display, perform, modify, sublicense, distribute and\nExternally Deploy Your Modifications of the same scope and extent as\nApple's licenses under Sections 2.1 and 2.2 above.\n\n4. Larger Works. You may create a Larger Work by combining Covered\nCode with other code not governed by the terms of this License and\ndistribute the Larger Work as a single product. In each such instance,\nYou must make sure the requirements of this License are fulfilled for\nthe Covered Code or any portion thereof.\n\n5. Limitations on Patent License. Except as expressly stated in\nSection 2, no other patent rights, express or implied, are granted by\nApple herein. Modifications and/or Larger Works may require additional\npatent licenses from Apple which Apple may grant in its sole\ndiscretion.\n\n6. Additional Terms. You may choose to offer, and to charge a fee for,\nwarranty, support, indemnity or liability obligations and/or other\nrights consistent with the scope of the license granted herein\n(\"Additional Terms\") to one or more recipients of Covered Code.\nHowever, You may do so only on Your own behalf and as Your sole\nresponsibility, and not on behalf of Apple or any Contributor. You\nmust obtain the recipient's agreement that any such Additional Terms\nare offered by You alone, and You hereby agree to indemnify, defend\nand hold Apple and every Contributor harmless for any liability\nincurred by or claims asserted against Apple or such Contributor by\nreason of any such Additional Terms.\n\n7. Versions of the License. Apple may publish revised and/or new\nversions of this License from time to time. Each version will be given\na distinguishing version number. Once Original Code has been published\nunder a particular version of this License, You may continue to use it\nunder the terms of that version. You may also choose to use such\nOriginal Code under the terms of any subsequent version of this\nLicense published by Apple. No one other than Apple has the right to\nmodify the terms applicable to Covered Code created under this\nLicense.\n\n8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in\npart pre-release, untested, or not fully tested works. The Covered\nCode may contain errors that could cause failures or loss of data, and\nmay be incomplete or contain inaccuracies. You expressly acknowledge\nand agree that use of the Covered Code, or any portion thereof, is at\nYour sole and entire risk. THE COVERED CODE IS PROVIDED \"AS IS\" AND\nWITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND\nAPPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS \"APPLE\" FOR THE\nPURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM\nALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT\nNOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF\nMERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR\nPURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD\nPARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST\nINTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE\nFUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,\nTHAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR\nERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO\nORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE\nAUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.\nYou acknowledge that the Covered Code is not intended for use in the\noperation of nuclear facilities, aircraft navigation, communication\nsystems, or air traffic control machines in which case the failure of\nthe Covered Code could lead to death, personal injury, or severe\nphysical or environmental damage.\n\n9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO\nEVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,\nSPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING\nTO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR\nANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,\nTORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF\nAPPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY\nREMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF\nINCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY\nTO YOU. In no event shall Apple's total liability to You for all\ndamages (other than as may be required by applicable law) under this\nLicense exceed the amount of fifty dollars ($50.00).\n\n10. Trademarks. This License does not grant any rights to use the\ntrademarks or trade names \"Apple\", \"Apple Computer\", \"Mac\", \"Mac OS\",\n\"QuickTime\", \"QuickTime Streaming Server\" or any other trademarks,\nservice marks, logos or trade names belonging to Apple (collectively\n\"Apple Marks\") or to any trademark, service mark, logo or trade name\nbelonging to any Contributor. You agree not to use any Apple Marks in\nor as part of the name of products derived from the Original Code or\nto endorse or promote products derived from the Original Code other\nthan as expressly permitted by and in strict compliance at all times\nwith Apple's third party trademark usage guidelines which are posted\nat http://www.apple.com/legal/guidelinesfor3rdparties.html.\n\n11. Ownership. Subject to the licenses granted under this License,\neach Contributor retains all rights, title and interest in and to any\nModifications made by such Contributor. Apple retains all rights,\ntitle and interest in and to the Original Code and any Modifications\nmade by or on behalf of Apple (\"Apple Modifications\"), and such Apple\nModifications will not be automatically subject to this License. Apple\nmay, at its sole discretion, choose to license such Apple\nModifications under this License, or on different terms from those\ncontained in this License or may choose not to license them at all.\n\n12. Termination.\n\n12.1 Termination. This License and the rights granted hereunder will\nterminate:\n\n(a) automatically without notice from Apple if You fail to comply with\nany term(s) of this License and fail to cure such breach within 30\ndays of becoming aware of such breach;\n\n(b) immediately in the event of the circumstances described in Section\n13.5(b); or\n\n(c) automatically without notice from Apple if You, at any time during\nthe term of this License, commence an action for patent infringement\nagainst Apple; provided that Apple did not first commence\nan action for patent infringement against You in that instance.\n\n12.2 Effect of Termination. Upon termination, You agree to immediately\nstop any further use, reproduction, modification, sublicensing and\ndistribution of the Covered Code. All sublicenses to the Covered Code\nwhich have been properly granted prior to termination shall survive\nany termination of this License. Provisions which, by their nature,\nshould remain in effect beyond the termination of this License shall\nsurvive, including but not limited to Sections 3, 5, 8, 9, 10, 11,\n12.2 and 13. No party will be liable to any other for compensation,\nindemnity or damages of any sort solely as a result of terminating\nthis License in accordance with its terms, and termination of this\nLicense will be without prejudice to any other right or remedy of\nany party.\n\n13. Miscellaneous.\n\n13.1 Government End Users. The Covered Code is a \"commercial item\" as\ndefined in FAR 2.101. Government software and technical data rights in\nthe Covered Code include only those rights customarily provided to the\npublic as defined in this License. This customary commercial license\nin technical data and software is provided in accordance with FAR\n12.211 (Technical Data) and 12.212 (Computer Software) and, for\nDepartment of Defense purchases, DFAR 252.227-7015 (Technical Data --\nCommercial Items) and 227.7202-3 (Rights in Commercial Computer\nSoftware or Computer Software Documentation). Accordingly, all U.S.\nGovernment End Users acquire Covered Code with only those rights set\nforth herein.\n\n13.2 Relationship of Parties. This License will not be construed as\ncreating an agency, partnership, joint venture or any other form of\nlegal association between or among You, Apple or any Contributor, and\nYou will not represent to the contrary, whether expressly, by\nimplication, appearance or otherwise.\n\n13.3 Independent Development. Nothing in this License will impair\nApple's right to acquire, license, develop, have others develop for\nit, market and/or distribute technology or products that perform the\nsame or similar functions as, or otherwise compete with,\nModifications, Larger Works, technology or products that You may\ndevelop, produce, market or distribute.\n\n13.4 Waiver; Construction. Failure by Apple or any Contributor to\nenforce any provision of this License will not be deemed a waiver of\nfuture enforcement of that or any other provision. Any law or\nregulation which provides that the language of a contract shall be\nconstrued against the drafter will not apply to this License.\n\n13.5 Severability. (a) If for any reason a court of competent\njurisdiction finds any provision of this License, or portion thereof,\nto be unenforceable, that provision of the License will be enforced to\nthe maximum extent permissible so as to effect the economic benefits\nand intent of the parties, and the remainder of this License will\ncontinue in full force and effect. (b) Notwithstanding the foregoing,\nif applicable law prohibits or restricts You from fully and/or\nspecifically complying with Sections 2 and/or 3 or prevents the\nenforceability of either of those Sections, this License will\nimmediately terminate and You must immediately discontinue any use of\nthe Covered Code and destroy all copies of it that are in your\npossession or control.\n\n13.6 Dispute Resolution. Any litigation or other dispute resolution\nbetween You and Apple relating to this License shall take place in the\nNorthern District of California, and You and Apple hereby consent to\nthe personal jurisdiction of, and venue in, the state and federal\ncourts within that District with respect to this License. The\napplication of the United Nations Convention on Contracts for the\nInternational Sale of Goods is expressly excluded.\n\n13.7 Entire Agreement; Governing Law. This License constitutes the\nentire agreement between the parties with respect to the subject\nmatter hereof. This License shall be governed by the laws of the\nUnited States and the State of California, except that body of\nCalifornia law concerning conflicts of law.\n\nWhere You are located in the province of Quebec, Canada, the following\nclause applies: The parties hereby confirm that they have requested\nthat this License and all related documents be drafted in English. Les\nparties ont exige que le present contrat et tous les documents\nconnexes soient rediges en anglais.\n\nEXHIBIT A.\n\n\"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights\nReserved.\n\nThis file contains Original Code and/or Modifications of Original Code\nas defined in and that are subject to the Apple Public Source License\nVersion 2.0 (the 'License'). You may not use this file except in\ncompliance with the License. Please obtain a copy of the License at\nhttp://www.opensource.apple.com/apsl/ and read it before using this\nfile.\n\nThe Original Code and all software distributed under the License are\ndistributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER\nEXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,\nINCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.\nPlease see the License for the specific language governing rights and\nlimitations under the License.\"\n"
  },
  {
    "path": "third_party/xnu/BUILD.gn",
    "content": "# Copyright 2019 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nsource_set(\"xnu\") {\n  sources = [ \"EXTERNAL_HEADERS/mach-o/loader.h\" ]\n}\n"
  },
  {
    "path": "third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h",
    "content": "/*\n * Copyright (c) 1999-2019 Apple Inc.  All Rights Reserved.\n *\n * @APPLE_LICENSE_HEADER_START@\n * \n * This file contains Original Code and/or Modifications of Original Code\n * as defined in and that are subject to the Apple Public Source License\n * Version 2.0 (the 'License'). You may not use this file except in\n * compliance with the License. Please obtain a copy of the License at\n * http://www.opensource.apple.com/apsl/ and read it before using this\n * file.\n * \n * The Original Code and all software distributed under the License are\n * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER\n * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,\n * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.\n * Please see the License for the specific language governing rights and\n * limitations under the License.\n * \n * @APPLE_LICENSE_HEADER_END@\n */\n#ifndef _MACHO_LOADER_H_\n#define _MACHO_LOADER_H_\n\n/*\n * This file describes the format of mach object files.\n */\n#include <stdint.h>\n\n/*\n * <mach/machine.h> is needed here for the cpu_type_t and cpu_subtype_t types\n * and contains the constants for the possible values of these types.\n */\n#include <mach/machine.h>\n\n/*\n * <mach/vm_prot.h> is needed here for the vm_prot_t type and contains the \n * constants that are or'ed together for the possible values of this type.\n */\n#include <mach/vm_prot.h>\n\n/*\n * <machine/thread_status.h> is expected to define the flavors of the thread\n * states and the structures of those flavors for each machine.\n#include <mach/machine/thread_status.h>\n#include <architecture/byte_order.h>\n */\n\n/*\n * The 32-bit mach header appears at the very beginning of the object file for\n * 32-bit architectures.\n */\nstruct mach_header {\n\tuint32_t\tmagic;\t\t/* mach magic number identifier */\n\tcpu_type_t\tcputype;\t/* cpu specifier */\n\tcpu_subtype_t\tcpusubtype;\t/* machine specifier */\n\tuint32_t\tfiletype;\t/* type of file */\n\tuint32_t\tncmds;\t\t/* number of load commands */\n\tuint32_t\tsizeofcmds;\t/* the size of all the load commands */\n\tuint32_t\tflags;\t\t/* flags */\n};\n\n/* Constant for the magic field of the mach_header (32-bit architectures) */\n#define\tMH_MAGIC\t0xfeedface\t/* the mach magic number */\n#define MH_CIGAM\t0xcefaedfe\t/* NXSwapInt(MH_MAGIC) */\n\n/*\n * The 64-bit mach header appears at the very beginning of object files for\n * 64-bit architectures.\n */\nstruct mach_header_64 {\n\tuint32_t\tmagic;\t\t/* mach magic number identifier */\n\tcpu_type_t\tcputype;\t/* cpu specifier */\n\tcpu_subtype_t\tcpusubtype;\t/* machine specifier */\n\tuint32_t\tfiletype;\t/* type of file */\n\tuint32_t\tncmds;\t\t/* number of load commands */\n\tuint32_t\tsizeofcmds;\t/* the size of all the load commands */\n\tuint32_t\tflags;\t\t/* flags */\n\tuint32_t\treserved;\t/* reserved */\n};\n\n/* Constant for the magic field of the mach_header_64 (64-bit architectures) */\n#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */\n#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */\n\n/*\n * The layout of the file depends on the filetype.  For all but the MH_OBJECT\n * file type the segments are padded out and aligned on a segment alignment\n * boundary for efficient demand pageing.  The MH_EXECUTE, MH_FVMLIB, MH_DYLIB,\n * MH_DYLINKER and MH_BUNDLE file types also have the headers included as part\n * of their first segment.\n * \n * The file type MH_OBJECT is a compact format intended as output of the\n * assembler and input (and possibly output) of the link editor (the .o\n * format).  All sections are in one unnamed segment with no segment padding. \n * This format is used as an executable format when the file is so small the\n * segment padding greatly increases its size.\n *\n * The file type MH_PRELOAD is an executable format intended for things that\n * are not executed under the kernel (proms, stand alones, kernels, etc).  The\n * format can be executed under the kernel but may demand paged it and not\n * preload it before execution.\n *\n * A core file is in MH_CORE format and can be any in an arbritray legal\n * Mach-O file.\n *\n * Constants for the filetype field of the mach_header\n */\n#define\tMH_OBJECT\t0x1\t\t/* relocatable object file */\n#define\tMH_EXECUTE\t0x2\t\t/* demand paged executable file */\n#define\tMH_FVMLIB\t0x3\t\t/* fixed VM shared library file */\n#define\tMH_CORE\t\t0x4\t\t/* core file */\n#define\tMH_PRELOAD\t0x5\t\t/* preloaded executable file */\n#define\tMH_DYLIB\t0x6\t\t/* dynamically bound shared library */\n#define\tMH_DYLINKER\t0x7\t\t/* dynamic link editor */\n#define\tMH_BUNDLE\t0x8\t\t/* dynamically bound bundle file */\n#define\tMH_DYLIB_STUB\t0x9\t\t/* shared library stub for static */\n\t\t\t\t\t/*  linking only, no section contents */\n#define\tMH_DSYM\t\t0xa\t\t/* companion file with only debug */\n\t\t\t\t\t/*  sections */\n#define\tMH_KEXT_BUNDLE\t0xb\t\t/* x86_64 kexts */\n#define\tMH_FILESET\t0xc\t\t/* set of mach-o's */\n\n/* Constants for the flags field of the mach_header */\n#define\tMH_NOUNDEFS\t0x1\t\t/* the object file has no undefined\n\t\t\t\t\t   references */\n#define\tMH_INCRLINK\t0x2\t\t/* the object file is the output of an\n\t\t\t\t\t   incremental link against a base file\n\t\t\t\t\t   and can't be link edited again */\n#define MH_DYLDLINK\t0x4\t\t/* the object file is input for the\n\t\t\t\t\t   dynamic linker and can't be staticly\n\t\t\t\t\t   link edited again */\n#define MH_BINDATLOAD\t0x8\t\t/* the object file's undefined\n\t\t\t\t\t   references are bound by the dynamic\n\t\t\t\t\t   linker when loaded. */\n#define MH_PREBOUND\t0x10\t\t/* the file has its dynamic undefined\n\t\t\t\t\t   references prebound. */\n#define MH_SPLIT_SEGS\t0x20\t\t/* the file has its read-only and\n\t\t\t\t\t   read-write segments split */\n#define MH_LAZY_INIT\t0x40\t\t/* the shared library init routine is\n\t\t\t\t\t   to be run lazily via catching memory\n\t\t\t\t\t   faults to its writeable segments\n\t\t\t\t\t   (obsolete) */\n#define MH_TWOLEVEL\t0x80\t\t/* the image is using two-level name\n\t\t\t\t\t   space bindings */\n#define MH_FORCE_FLAT\t0x100\t\t/* the executable is forcing all images\n\t\t\t\t\t   to use flat name space bindings */\n#define MH_NOMULTIDEFS\t0x200\t\t/* this umbrella guarantees no multiple\n\t\t\t\t\t   defintions of symbols in its\n\t\t\t\t\t   sub-images so the two-level namespace\n\t\t\t\t\t   hints can always be used. */\n#define MH_NOFIXPREBINDING 0x400\t/* do not have dyld notify the\n\t\t\t\t\t   prebinding agent about this\n\t\t\t\t\t   executable */\n#define MH_PREBINDABLE  0x800           /* the binary is not prebound but can\n\t\t\t\t\t   have its prebinding redone. only used\n                                           when MH_PREBOUND is not set. */\n#define MH_ALLMODSBOUND 0x1000\t\t/* indicates that this binary binds to\n                                           all two-level namespace modules of\n\t\t\t\t\t   its dependent libraries. only used\n\t\t\t\t\t   when MH_PREBINDABLE and MH_TWOLEVEL\n\t\t\t\t\t   are both set. */ \n#define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000/* safe to divide up the sections into\n\t\t\t\t\t    sub-sections via symbols for dead\n\t\t\t\t\t    code stripping */\n#define MH_CANONICAL    0x4000\t\t/* the binary has been canonicalized\n\t\t\t\t\t   via the unprebind operation */\n#define MH_WEAK_DEFINES\t0x8000\t\t/* the final linked image contains\n\t\t\t\t\t   external weak symbols */\n#define MH_BINDS_TO_WEAK 0x10000\t/* the final linked image uses\n\t\t\t\t\t   weak symbols */\n\n#define MH_ALLOW_STACK_EXECUTION 0x20000/* When this bit is set, all stacks \n\t\t\t\t\t   in the task will be given stack\n\t\t\t\t\t   execution privilege.  Only used in\n\t\t\t\t\t   MH_EXECUTE filetypes. */\n#define MH_ROOT_SAFE 0x40000           /* When this bit is set, the binary \n\t\t\t\t\t  declares it is safe for use in\n\t\t\t\t\t  processes with uid zero */\n                                         \n#define MH_SETUID_SAFE 0x80000         /* When this bit is set, the binary \n\t\t\t\t\t  declares it is safe for use in\n\t\t\t\t\t  processes when issetugid() is true */\n\n#define MH_NO_REEXPORTED_DYLIBS 0x100000 /* When this bit is set on a dylib, \n\t\t\t\t\t  the static linker does not need to\n\t\t\t\t\t  examine dependent dylibs to see\n\t\t\t\t\t  if any are re-exported */\n#define\tMH_PIE 0x200000\t\t\t/* When this bit is set, the OS will\n\t\t\t\t\t   load the main executable at a\n\t\t\t\t\t   random address.  Only used in\n\t\t\t\t\t   MH_EXECUTE filetypes. */\n#define\tMH_DEAD_STRIPPABLE_DYLIB 0x400000 /* Only for use on dylibs.  When\n\t\t\t\t\t     linking against a dylib that\n\t\t\t\t\t     has this bit set, the static linker\n\t\t\t\t\t     will automatically not create a\n\t\t\t\t\t     LC_LOAD_DYLIB load command to the\n\t\t\t\t\t     dylib if no symbols are being\n\t\t\t\t\t     referenced from the dylib. */\n#define MH_HAS_TLV_DESCRIPTORS 0x800000 /* Contains a section of type \n\t\t\t\t\t    S_THREAD_LOCAL_VARIABLES */\n\n#define MH_NO_HEAP_EXECUTION 0x1000000\t/* When this bit is set, the OS will\n\t\t\t\t\t   run the main executable with\n\t\t\t\t\t   a non-executable heap even on\n\t\t\t\t\t   platforms (e.g. i386) that don't\n\t\t\t\t\t   require it. Only used in MH_EXECUTE\n\t\t\t\t\t   filetypes. */\n\n#define MH_APP_EXTENSION_SAFE 0x02000000 /* The code was linked for use in an\n\t\t\t\t\t    application extension. */\n\n#define\tMH_NLIST_OUTOFSYNC_WITH_DYLDINFO 0x04000000 /* The external symbols\n\t\t\t\t\t   listed in the nlist symbol table do\n\t\t\t\t\t   not include all the symbols listed in\n\t\t\t\t\t   the dyld info. */\n\n#define\tMH_SIM_SUPPORT 0x08000000\t/* Allow LC_MIN_VERSION_MACOS and\n\t\t\t\t\t   LC_BUILD_VERSION load commands with\n\t\t\t\t\t   the platforms macOS, iOSMac,\n\t\t\t\t\t   iOSSimulator, tvOSSimulator and\n\t\t\t\t\t   watchOSSimulator. */\n\n#define MH_DYLIB_IN_CACHE 0x80000000\t/* Only for use on dylibs. When this bit\n\t\t\t\t\t   is set, the dylib is part of the dyld\n\t\t\t\t\t   shared cache, rather than loose in\n\t\t\t\t\t   the filesystem. */\n\n/*\n * The load commands directly follow the mach_header.  The total size of all\n * of the commands is given by the sizeofcmds field in the mach_header.  All\n * load commands must have as their first two fields cmd and cmdsize.  The cmd\n * field is filled in with a constant for that command type.  Each command type\n * has a structure specifically for it.  The cmdsize field is the size in bytes\n * of the particular load command structure plus anything that follows it that\n * is a part of the load command (i.e. section structures, strings, etc.).  To\n * advance to the next load command the cmdsize can be added to the offset or\n * pointer of the current load command.  The cmdsize for 32-bit architectures\n * MUST be a multiple of 4 bytes and for 64-bit architectures MUST be a multiple\n * of 8 bytes (these are forever the maximum alignment of any load commands).\n * The padded bytes must be zero.  All tables in the object file must also\n * follow these rules so the file can be memory mapped.  Otherwise the pointers\n * to these tables will not work well or at all on some machines.  With all\n * padding zeroed like objects will compare byte for byte.\n */\nstruct load_command {\n\tuint32_t cmd;\t\t/* type of load command */\n\tuint32_t cmdsize;\t/* total size of command in bytes */\n};\n\n/*\n * After MacOS X 10.1 when a new load command is added that is required to be\n * understood by the dynamic linker for the image to execute properly the\n * LC_REQ_DYLD bit will be or'ed into the load command constant.  If the dynamic\n * linker sees such a load command it it does not understand will issue a\n * \"unknown load command required for execution\" error and refuse to use the\n * image.  Other load commands without this bit that are not understood will\n * simply be ignored.\n */\n#define LC_REQ_DYLD 0x80000000\n\n/* Constants for the cmd field of all load commands, the type */\n#define\tLC_SEGMENT\t0x1\t/* segment of this file to be mapped */\n#define\tLC_SYMTAB\t0x2\t/* link-edit stab symbol table info */\n#define\tLC_SYMSEG\t0x3\t/* link-edit gdb symbol table info (obsolete) */\n#define\tLC_THREAD\t0x4\t/* thread */\n#define\tLC_UNIXTHREAD\t0x5\t/* unix thread (includes a stack) */\n#define\tLC_LOADFVMLIB\t0x6\t/* load a specified fixed VM shared library */\n#define\tLC_IDFVMLIB\t0x7\t/* fixed VM shared library identification */\n#define\tLC_IDENT\t0x8\t/* object identification info (obsolete) */\n#define LC_FVMFILE\t0x9\t/* fixed VM file inclusion (internal use) */\n#define LC_PREPAGE      0xa     /* prepage command (internal use) */\n#define\tLC_DYSYMTAB\t0xb\t/* dynamic link-edit symbol table info */\n#define\tLC_LOAD_DYLIB\t0xc\t/* load a dynamically linked shared library */\n#define\tLC_ID_DYLIB\t0xd\t/* dynamically linked shared lib ident */\n#define LC_LOAD_DYLINKER 0xe\t/* load a dynamic linker */\n#define LC_ID_DYLINKER\t0xf\t/* dynamic linker identification */\n#define\tLC_PREBOUND_DYLIB 0x10\t/* modules prebound for a dynamically */\n\t\t\t\t/*  linked shared library */\n#define\tLC_ROUTINES\t0x11\t/* image routines */\n#define\tLC_SUB_FRAMEWORK 0x12\t/* sub framework */\n#define\tLC_SUB_UMBRELLA 0x13\t/* sub umbrella */\n#define\tLC_SUB_CLIENT\t0x14\t/* sub client */\n#define\tLC_SUB_LIBRARY  0x15\t/* sub library */\n#define\tLC_TWOLEVEL_HINTS 0x16\t/* two-level namespace lookup hints */\n#define\tLC_PREBIND_CKSUM  0x17\t/* prebind checksum */\n\n/*\n * load a dynamically linked shared library that is allowed to be missing\n * (all symbols are weak imported).\n */\n#define\tLC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)\n\n#define\tLC_SEGMENT_64\t0x19\t/* 64-bit segment of this file to be\n\t\t\t\t   mapped */\n#define\tLC_ROUTINES_64\t0x1a\t/* 64-bit image routines */\n#define LC_UUID\t\t0x1b\t/* the uuid */\n#define LC_RPATH       (0x1c | LC_REQ_DYLD)    /* runpath additions */\n#define LC_CODE_SIGNATURE 0x1d\t/* local of code signature */\n#define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */\n#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */\n#define\tLC_LAZY_LOAD_DYLIB 0x20\t/* delay load of dylib until first use */\n#define\tLC_ENCRYPTION_INFO 0x21\t/* encrypted segment information */\n#define\tLC_DYLD_INFO \t0x22\t/* compressed dyld information */\n#define\tLC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD)\t/* compressed dyld information only */\n#define\tLC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */\n#define LC_VERSION_MIN_MACOSX 0x24   /* build for MacOSX min OS version */\n#define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */\n#define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */\n#define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat\n\t\t\t\t    like environment variable */\n#define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */\n#define LC_DATA_IN_CODE 0x29 /* table of non-instructions in __text */\n#define LC_SOURCE_VERSION 0x2A /* source version used to build binary */\n#define LC_DYLIB_CODE_SIGN_DRS 0x2B /* Code signing DRs copied from linked dylibs */\n#define\tLC_ENCRYPTION_INFO_64 0x2C /* 64-bit encrypted segment information */\n#define LC_LINKER_OPTION 0x2D /* linker options in MH_OBJECT files */\n#define LC_LINKER_OPTIMIZATION_HINT 0x2E /* optimization hints in MH_OBJECT files */\n#define LC_VERSION_MIN_TVOS 0x2F /* build for AppleTV min OS version */\n#define LC_VERSION_MIN_WATCHOS 0x30 /* build for Watch min OS version */\n#define LC_NOTE 0x31 /* arbitrary data included within a Mach-O file */\n#define LC_BUILD_VERSION 0x32 /* build for platform min OS version */\n#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) /* used with linkedit_data_command, payload is trie */\n#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) /* used with linkedit_data_command */\n#define LC_FILESET_ENTRY      (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */\n\n/*\n * A variable length string in a load command is represented by an lc_str\n * union.  The strings are stored just after the load command structure and\n * the offset is from the start of the load command structure.  The size\n * of the string is reflected in the cmdsize field of the load command.\n * Once again any padded bytes to bring the cmdsize field to a multiple\n * of 4 bytes must be zero.\n */\nunion lc_str {\n\tuint32_t\toffset;\t/* offset to the string */\n#ifndef __LP64__\n\tchar\t\t*ptr;\t/* pointer to the string */\n#endif \n};\n\n/*\n * The segment load command indicates that a part of this file is to be\n * mapped into the task's address space.  The size of this segment in memory,\n * vmsize, maybe equal to or larger than the amount to map from this file,\n * filesize.  The file is mapped starting at fileoff to the beginning of\n * the segment in memory, vmaddr.  The rest of the memory of the segment,\n * if any, is allocated zero fill on demand.  The segment's maximum virtual\n * memory protection and initial virtual memory protection are specified\n * by the maxprot and initprot fields.  If the segment has sections then the\n * section structures directly follow the segment command and their size is\n * reflected in cmdsize.\n */\nstruct segment_command { /* for 32-bit architectures */\n\tuint32_t\tcmd;\t\t/* LC_SEGMENT */\n\tuint32_t\tcmdsize;\t/* includes sizeof section structs */\n\tchar\t\tsegname[16];\t/* segment name */\n\tuint32_t\tvmaddr;\t\t/* memory address of this segment */\n\tuint32_t\tvmsize;\t\t/* memory size of this segment */\n\tuint32_t\tfileoff;\t/* file offset of this segment */\n\tuint32_t\tfilesize;\t/* amount to map from the file */\n\tvm_prot_t\tmaxprot;\t/* maximum VM protection */\n\tvm_prot_t\tinitprot;\t/* initial VM protection */\n\tuint32_t\tnsects;\t\t/* number of sections in segment */\n\tuint32_t\tflags;\t\t/* flags */\n};\n\n/*\n * The 64-bit segment load command indicates that a part of this file is to be\n * mapped into a 64-bit task's address space.  If the 64-bit segment has\n * sections then section_64 structures directly follow the 64-bit segment\n * command and their size is reflected in cmdsize.\n */\nstruct segment_command_64 { /* for 64-bit architectures */\n\tuint32_t\tcmd;\t\t/* LC_SEGMENT_64 */\n\tuint32_t\tcmdsize;\t/* includes sizeof section_64 structs */\n\tchar\t\tsegname[16];\t/* segment name */\n\tuint64_t\tvmaddr;\t\t/* memory address of this segment */\n\tuint64_t\tvmsize;\t\t/* memory size of this segment */\n\tuint64_t\tfileoff;\t/* file offset of this segment */\n\tuint64_t\tfilesize;\t/* amount to map from the file */\n\tvm_prot_t\tmaxprot;\t/* maximum VM protection */\n\tvm_prot_t\tinitprot;\t/* initial VM protection */\n\tuint32_t\tnsects;\t\t/* number of sections in segment */\n\tuint32_t\tflags;\t\t/* flags */\n};\n\n/* Constants for the flags field of the segment_command */\n#define\tSG_HIGHVM\t0x1\t/* the file contents for this segment is for\n\t\t\t\t   the high part of the VM space, the low part\n\t\t\t\t   is zero filled (for stacks in core files) */\n#define\tSG_FVMLIB\t0x2\t/* this segment is the VM that is allocated by\n\t\t\t\t   a fixed VM library, for overlap checking in\n\t\t\t\t   the link editor */\n#define\tSG_NORELOC\t0x4\t/* this segment has nothing that was relocated\n\t\t\t\t   in it and nothing relocated to it, that is\n\t\t\t\t   it maybe safely replaced without relocation*/\n#define SG_PROTECTED_VERSION_1\t0x8 /* This segment is protected.  If the\n\t\t\t\t       segment starts at file offset 0, the\n\t\t\t\t       first page of the segment is not\n\t\t\t\t       protected.  All other pages of the\n\t\t\t\t       segment are protected. */\n#define SG_READ_ONLY    0x10 /* This segment is made read-only after fixups */\n\n\n\n/*\n * A segment is made up of zero or more sections.  Non-MH_OBJECT files have\n * all of their segments with the proper sections in each, and padded to the\n * specified segment alignment when produced by the link editor.  The first\n * segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header\n * and load commands of the object file before its first section.  The zero\n * fill sections are always last in their segment (in all formats).  This\n * allows the zeroed segment padding to be mapped into memory where zero fill\n * sections might be. The gigabyte zero fill sections, those with the section\n * type S_GB_ZEROFILL, can only be in a segment with sections of this type.\n * These segments are then placed after all other segments.\n *\n * The MH_OBJECT format has all of its sections in one segment for\n * compactness.  There is no padding to a specified segment boundary and the\n * mach_header and load commands are not part of the segment.\n *\n * Sections with the same section name, sectname, going into the same segment,\n * segname, are combined by the link editor.  The resulting section is aligned\n * to the maximum alignment of the combined sections and is the new section's\n * alignment.  The combined sections are aligned to their original alignment in\n * the combined section.  Any padded bytes to get the specified alignment are\n * zeroed.\n *\n * The format of the relocation entries referenced by the reloff and nreloc\n * fields of the section structure for mach object files is described in the\n * header file <reloc.h>.\n */\nstruct section { /* for 32-bit architectures */\n\tchar\t\tsectname[16];\t/* name of this section */\n\tchar\t\tsegname[16];\t/* segment this section goes in */\n\tuint32_t\taddr;\t\t/* memory address of this section */\n\tuint32_t\tsize;\t\t/* size in bytes of this section */\n\tuint32_t\toffset;\t\t/* file offset of this section */\n\tuint32_t\talign;\t\t/* section alignment (power of 2) */\n\tuint32_t\treloff;\t\t/* file offset of relocation entries */\n\tuint32_t\tnreloc;\t\t/* number of relocation entries */\n\tuint32_t\tflags;\t\t/* flags (section type and attributes)*/\n\tuint32_t\treserved1;\t/* reserved (for offset or index) */\n\tuint32_t\treserved2;\t/* reserved (for count or sizeof) */\n};\n\nstruct section_64 { /* for 64-bit architectures */\n\tchar\t\tsectname[16];\t/* name of this section */\n\tchar\t\tsegname[16];\t/* segment this section goes in */\n\tuint64_t\taddr;\t\t/* memory address of this section */\n\tuint64_t\tsize;\t\t/* size in bytes of this section */\n\tuint32_t\toffset;\t\t/* file offset of this section */\n\tuint32_t\talign;\t\t/* section alignment (power of 2) */\n\tuint32_t\treloff;\t\t/* file offset of relocation entries */\n\tuint32_t\tnreloc;\t\t/* number of relocation entries */\n\tuint32_t\tflags;\t\t/* flags (section type and attributes)*/\n\tuint32_t\treserved1;\t/* reserved (for offset or index) */\n\tuint32_t\treserved2;\t/* reserved (for count or sizeof) */\n\tuint32_t\treserved3;\t/* reserved */\n};\n\n/*\n * The flags field of a section structure is separated into two parts a section\n * type and section attributes.  The section types are mutually exclusive (it\n * can only have one type) but the section attributes are not (it may have more\n * than one attribute).\n */\n#define SECTION_TYPE\t\t 0x000000ff\t/* 256 section types */\n#define SECTION_ATTRIBUTES\t 0xffffff00\t/*  24 section attributes */\n\n/* Constants for the type of a section */\n#define\tS_REGULAR\t\t0x0\t/* regular section */\n#define\tS_ZEROFILL\t\t0x1\t/* zero fill on demand section */\n#define\tS_CSTRING_LITERALS\t0x2\t/* section with only literal C strings*/\n#define\tS_4BYTE_LITERALS\t0x3\t/* section with only 4 byte literals */\n#define\tS_8BYTE_LITERALS\t0x4\t/* section with only 8 byte literals */\n#define\tS_LITERAL_POINTERS\t0x5\t/* section with only pointers to */\n\t\t\t\t\t/*  literals */\n/*\n * For the two types of symbol pointers sections and the symbol stubs section\n * they have indirect symbol table entries.  For each of the entries in the\n * section the indirect symbol table entries, in corresponding order in the\n * indirect symbol table, start at the index stored in the reserved1 field\n * of the section structure.  Since the indirect symbol table entries\n * correspond to the entries in the section the number of indirect symbol table\n * entries is inferred from the size of the section divided by the size of the\n * entries in the section.  For symbol pointers sections the size of the entries\n * in the section is 4 bytes and for symbol stubs sections the byte size of the\n * stubs is stored in the reserved2 field of the section structure.\n */\n#define\tS_NON_LAZY_SYMBOL_POINTERS\t0x6\t/* section with only non-lazy\n\t\t\t\t\t\t   symbol pointers */\n#define\tS_LAZY_SYMBOL_POINTERS\t\t0x7\t/* section with only lazy symbol\n\t\t\t\t\t\t   pointers */\n#define\tS_SYMBOL_STUBS\t\t\t0x8\t/* section with only symbol\n\t\t\t\t\t\t   stubs, byte size of stub in\n\t\t\t\t\t\t   the reserved2 field */\n#define\tS_MOD_INIT_FUNC_POINTERS\t0x9\t/* section with only function\n\t\t\t\t\t\t   pointers for initialization*/\n#define\tS_MOD_TERM_FUNC_POINTERS\t0xa\t/* section with only function\n\t\t\t\t\t\t   pointers for termination */\n#define\tS_COALESCED\t\t\t0xb\t/* section contains symbols that\n\t\t\t\t\t\t   are to be coalesced */\n#define\tS_GB_ZEROFILL\t\t\t0xc\t/* zero fill on demand section\n\t\t\t\t\t\t   (that can be larger than 4\n\t\t\t\t\t\t   gigabytes) */\n#define\tS_INTERPOSING\t\t\t0xd\t/* section with only pairs of\n\t\t\t\t\t\t   function pointers for\n\t\t\t\t\t\t   interposing */\n#define\tS_16BYTE_LITERALS\t\t0xe\t/* section with only 16 byte\n\t\t\t\t\t\t   literals */\n#define\tS_DTRACE_DOF\t\t\t0xf\t/* section contains \n\t\t\t\t\t\t   DTrace Object Format */\n#define\tS_LAZY_DYLIB_SYMBOL_POINTERS\t0x10\t/* section with only lazy\n\t\t\t\t\t\t   symbol pointers to lazy\n\t\t\t\t\t\t   loaded dylibs */\n/*\n * Section types to support thread local variables\n */\n#define S_THREAD_LOCAL_REGULAR                   0x11  /* template of initial \n\t\t\t\t\t\t\t  values for TLVs */\n#define S_THREAD_LOCAL_ZEROFILL                  0x12  /* template of initial \n\t\t\t\t\t\t\t  values for TLVs */\n#define S_THREAD_LOCAL_VARIABLES                 0x13  /* TLV descriptors */\n#define S_THREAD_LOCAL_VARIABLE_POINTERS         0x14  /* pointers to TLV \n                                                          descriptors */\n#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS    0x15  /* functions to call\n\t\t\t\t\t\t\t  to initialize TLV\n\t\t\t\t\t\t\t  values */\n#define S_INIT_FUNC_OFFSETS                      0x16  /* 32-bit offsets to\n\t\t\t\t\t\t\t  initializers */\n\n/*\n * Constants for the section attributes part of the flags field of a section\n * structure.\n */\n#define SECTION_ATTRIBUTES_USR\t 0xff000000\t/* User setable attributes */\n#define S_ATTR_PURE_INSTRUCTIONS 0x80000000\t/* section contains only true\n\t\t\t\t\t\t   machine instructions */\n#define S_ATTR_NO_TOC \t\t 0x40000000\t/* section contains coalesced\n\t\t\t\t\t\t   symbols that are not to be\n\t\t\t\t\t\t   in a ranlib table of\n\t\t\t\t\t\t   contents */\n#define S_ATTR_STRIP_STATIC_SYMS 0x20000000\t/* ok to strip static symbols\n\t\t\t\t\t\t   in this section in files\n\t\t\t\t\t\t   with the MH_DYLDLINK flag */\n#define S_ATTR_NO_DEAD_STRIP\t 0x10000000\t/* no dead stripping */\n#define S_ATTR_LIVE_SUPPORT\t 0x08000000\t/* blocks are live if they\n\t\t\t\t\t\t   reference live blocks */\n#define S_ATTR_SELF_MODIFYING_CODE 0x04000000\t/* Used with i386 code stubs\n\t\t\t\t\t\t   written on by dyld */\n/*\n * If a segment contains any sections marked with S_ATTR_DEBUG then all\n * sections in that segment must have this attribute.  No section other than\n * a section marked with this attribute may reference the contents of this\n * section.  A section with this attribute may contain no symbols and must have\n * a section type S_REGULAR.  The static linker will not copy section contents\n * from sections with this attribute into its output file.  These sections\n * generally contain DWARF debugging info.\n */ \n#define\tS_ATTR_DEBUG\t\t 0x02000000\t/* a debug section */\n#define SECTION_ATTRIBUTES_SYS\t 0x00ffff00\t/* system setable attributes */\n#define S_ATTR_SOME_INSTRUCTIONS 0x00000400\t/* section contains some\n\t\t\t\t\t\t   machine instructions */\n#define S_ATTR_EXT_RELOC\t 0x00000200\t/* section has external\n\t\t\t\t\t\t   relocation entries */\n#define S_ATTR_LOC_RELOC\t 0x00000100\t/* section has local\n\t\t\t\t\t\t   relocation entries */\n\n\n/*\n * The names of segments and sections in them are mostly meaningless to the\n * link-editor.  But there are few things to support traditional UNIX\n * executables that require the link-editor and assembler to use some names\n * agreed upon by convention.\n *\n * The initial protection of the \"__TEXT\" segment has write protection turned\n * off (not writeable).\n *\n * The link-editor will allocate common symbols at the end of the \"__common\"\n * section in the \"__DATA\" segment.  It will create the section and segment\n * if needed.\n */\n\n/* The currently known segment names and the section names in those segments */\n\n#define\tSEG_PAGEZERO\t\"__PAGEZERO\"\t/* the pagezero segment which has no */\n\t\t\t\t\t/* protections and catches NULL */\n\t\t\t\t\t/* references for MH_EXECUTE files */\n\n\n#define\tSEG_TEXT\t\"__TEXT\"\t/* the tradition UNIX text segment */\n#define\tSECT_TEXT\t\"__text\"\t/* the real text part of the text */\n\t\t\t\t\t/* section no headers, and no padding */\n#define SECT_FVMLIB_INIT0 \"__fvmlib_init0\"\t/* the fvmlib initialization */\n\t\t\t\t\t\t/*  section */\n#define SECT_FVMLIB_INIT1 \"__fvmlib_init1\"\t/* the section following the */\n\t\t\t\t\t        /*  fvmlib initialization */\n\t\t\t\t\t\t/*  section */\n\n#define\tSEG_DATA\t\"__DATA\"\t/* the tradition UNIX data segment */\n#define\tSECT_DATA\t\"__data\"\t/* the real initialized data section */\n\t\t\t\t\t/* no padding, no bss overlap */\n#define\tSECT_BSS\t\"__bss\"\t\t/* the real uninitialized data section*/\n\t\t\t\t\t/* no padding */\n#define SECT_COMMON\t\"__common\"\t/* the section common symbols are */\n\t\t\t\t\t/* allocated in by the link editor */\n\n#define\tSEG_OBJC\t\"__OBJC\"\t/* objective-C runtime segment */\n#define SECT_OBJC_SYMBOLS \"__symbol_table\"\t/* symbol table */\n#define SECT_OBJC_MODULES \"__module_info\"\t/* module information */\n#define SECT_OBJC_STRINGS \"__selector_strs\"\t/* string table */\n#define SECT_OBJC_REFS \"__selector_refs\"\t/* string table */\n\n#define\tSEG_ICON\t \"__ICON\"\t/* the icon segment */\n#define\tSECT_ICON_HEADER \"__header\"\t/* the icon headers */\n#define\tSECT_ICON_TIFF   \"__tiff\"\t/* the icons in tiff format */\n\n#define\tSEG_LINKEDIT\t\"__LINKEDIT\"\t/* the segment containing all structs */\n\t\t\t\t\t/* created and maintained by the link */\n\t\t\t\t\t/* editor.  Created with -seglinkedit */\n\t\t\t\t\t/* option to ld(1) for MH_EXECUTE and */\n\t\t\t\t\t/* FVMLIB file types only */\n\n#define SEG_LINKINFO\t\"__LINKINFO\"\t/* the segment overlapping with linkedit */\n\t\t\t\t\t/* containing linking information */\n\n#define SEG_UNIXSTACK\t\"__UNIXSTACK\"\t/* the unix stack segment */\n\n#define SEG_IMPORT\t\"__IMPORT\"\t/* the segment for the self (dyld) */\n\t\t\t\t\t/* modifing code stubs that has read, */\n\t\t\t\t\t/* write and execute permissions */\n\n/*\n * Fixed virtual memory shared libraries are identified by two things.  The\n * target pathname (the name of the library as found for execution), and the\n * minor version number.  The address of where the headers are loaded is in\n * header_addr. (THIS IS OBSOLETE and no longer supported).\n */\nstruct fvmlib {\n\tunion lc_str\tname;\t\t/* library's target pathname */\n\tuint32_t\tminor_version;\t/* library's minor version number */\n\tuint32_t\theader_addr;\t/* library's header address */\n};\n\n/*\n * A fixed virtual shared library (filetype == MH_FVMLIB in the mach header)\n * contains a fvmlib_command (cmd == LC_IDFVMLIB) to identify the library.\n * An object that uses a fixed virtual shared library also contains a\n * fvmlib_command (cmd == LC_LOADFVMLIB) for each library it uses.\n * (THIS IS OBSOLETE and no longer supported).\n */\nstruct fvmlib_command {\n\tuint32_t\tcmd;\t\t/* LC_IDFVMLIB or LC_LOADFVMLIB */\n\tuint32_t\tcmdsize;\t/* includes pathname string */\n\tstruct fvmlib\tfvmlib;\t\t/* the library identification */\n};\n\n/*\n * Dynamicly linked shared libraries are identified by two things.  The\n * pathname (the name of the library as found for execution), and the\n * compatibility version number.  The pathname must match and the compatibility\n * number in the user of the library must be greater than or equal to the\n * library being used.  The time stamp is used to record the time a library was\n * built and copied into user so it can be use to determined if the library used\n * at runtime is exactly the same as used to built the program.\n */\nstruct dylib {\n    union lc_str  name;\t\t\t/* library's path name */\n    uint32_t timestamp;\t\t\t/* library's build time stamp */\n    uint32_t current_version;\t\t/* library's current version number */\n    uint32_t compatibility_version;\t/* library's compatibility vers number*/\n};\n\n/*\n * A dynamically linked shared library (filetype == MH_DYLIB in the mach header)\n * contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library.\n * An object that uses a dynamically linked shared library also contains a\n * dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or\n * LC_REEXPORT_DYLIB) for each library it uses.\n */\nstruct dylib_command {\n\tuint32_t\tcmd;\t\t/* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,\n\t\t\t\t\t   LC_REEXPORT_DYLIB */\n\tuint32_t\tcmdsize;\t/* includes pathname string */\n\tstruct dylib\tdylib;\t\t/* the library identification */\n};\n\n/*\n * A dynamically linked shared library may be a subframework of an umbrella\n * framework.  If so it will be linked with \"-umbrella umbrella_name\" where\n * Where \"umbrella_name\" is the name of the umbrella framework. A subframework\n * can only be linked against by its umbrella framework or other subframeworks\n * that are part of the same umbrella framework.  Otherwise the static link\n * editor produces an error and states to link against the umbrella framework.\n * The name of the umbrella framework for subframeworks is recorded in the\n * following structure.\n */\nstruct sub_framework_command {\n\tuint32_t\tcmd;\t\t/* LC_SUB_FRAMEWORK */\n\tuint32_t\tcmdsize;\t/* includes umbrella string */\n\tunion lc_str \tumbrella;\t/* the umbrella framework name */\n};\n\n/*\n * For dynamically linked shared libraries that are subframework of an umbrella\n * framework they can allow clients other than the umbrella framework or other\n * subframeworks in the same umbrella framework.  To do this the subframework\n * is built with \"-allowable_client client_name\" and an LC_SUB_CLIENT load\n * command is created for each -allowable_client flag.  The client_name is\n * usually a framework name.  It can also be a name used for bundles clients\n * where the bundle is built with \"-client_name client_name\".\n */\nstruct sub_client_command {\n\tuint32_t\tcmd;\t\t/* LC_SUB_CLIENT */\n\tuint32_t\tcmdsize;\t/* includes client string */\n\tunion lc_str \tclient;\t\t/* the client name */\n};\n\n/*\n * A dynamically linked shared library may be a sub_umbrella of an umbrella\n * framework.  If so it will be linked with \"-sub_umbrella umbrella_name\" where\n * Where \"umbrella_name\" is the name of the sub_umbrella framework.  When\n * staticly linking when -twolevel_namespace is in effect a twolevel namespace \n * umbrella framework will only cause its subframeworks and those frameworks\n * listed as sub_umbrella frameworks to be implicited linked in.  Any other\n * dependent dynamic libraries will not be linked it when -twolevel_namespace\n * is in effect.  The primary library recorded by the static linker when\n * resolving a symbol in these libraries will be the umbrella framework.\n * Zero or more sub_umbrella frameworks may be use by an umbrella framework.\n * The name of a sub_umbrella framework is recorded in the following structure.\n */\nstruct sub_umbrella_command {\n\tuint32_t\tcmd;\t\t/* LC_SUB_UMBRELLA */\n\tuint32_t\tcmdsize;\t/* includes sub_umbrella string */\n\tunion lc_str \tsub_umbrella;\t/* the sub_umbrella framework name */\n};\n\n/*\n * A dynamically linked shared library may be a sub_library of another shared\n * library.  If so it will be linked with \"-sub_library library_name\" where\n * Where \"library_name\" is the name of the sub_library shared library.  When\n * staticly linking when -twolevel_namespace is in effect a twolevel namespace \n * shared library will only cause its subframeworks and those frameworks\n * listed as sub_umbrella frameworks and libraries listed as sub_libraries to\n * be implicited linked in.  Any other dependent dynamic libraries will not be\n * linked it when -twolevel_namespace is in effect.  The primary library\n * recorded by the static linker when resolving a symbol in these libraries\n * will be the umbrella framework (or dynamic library). Zero or more sub_library\n * shared libraries may be use by an umbrella framework or (or dynamic library).\n * The name of a sub_library framework is recorded in the following structure.\n * For example /usr/lib/libobjc_profile.A.dylib would be recorded as \"libobjc\".\n */\nstruct sub_library_command {\n\tuint32_t\tcmd;\t\t/* LC_SUB_LIBRARY */\n\tuint32_t\tcmdsize;\t/* includes sub_library string */\n\tunion lc_str \tsub_library;\t/* the sub_library name */\n};\n\n/*\n * A program (filetype == MH_EXECUTE) that is\n * prebound to its dynamic libraries has one of these for each library that\n * the static linker used in prebinding.  It contains a bit vector for the\n * modules in the library.  The bits indicate which modules are bound (1) and\n * which are not (0) from the library.  The bit for module 0 is the low bit\n * of the first byte.  So the bit for the Nth module is:\n * (linked_modules[N/8] >> N%8) & 1\n */\nstruct prebound_dylib_command {\n\tuint32_t\tcmd;\t\t/* LC_PREBOUND_DYLIB */\n\tuint32_t\tcmdsize;\t/* includes strings */\n\tunion lc_str\tname;\t\t/* library's path name */\n\tuint32_t\tnmodules;\t/* number of modules in library */\n\tunion lc_str\tlinked_modules;\t/* bit vector of linked modules */\n};\n\n/*\n * A program that uses a dynamic linker contains a dylinker_command to identify\n * the name of the dynamic linker (LC_LOAD_DYLINKER).  And a dynamic linker\n * contains a dylinker_command to identify the dynamic linker (LC_ID_DYLINKER).\n * A file can have at most one of these.\n * This struct is also used for the LC_DYLD_ENVIRONMENT load command and\n * contains string for dyld to treat like environment variable.\n */\nstruct dylinker_command {\n\tuint32_t\tcmd;\t\t/* LC_ID_DYLINKER, LC_LOAD_DYLINKER or\n\t\t\t\t\t   LC_DYLD_ENVIRONMENT */\n\tuint32_t\tcmdsize;\t/* includes pathname string */\n\tunion lc_str    name;\t\t/* dynamic linker's path name */\n};\n\n/*\n * Thread commands contain machine-specific data structures suitable for\n * use in the thread state primitives.  The machine specific data structures\n * follow the struct thread_command as follows.\n * Each flavor of machine specific data structure is preceded by an uint32_t\n * constant for the flavor of that data structure, an uint32_t that is the\n * count of uint32_t's of the size of the state data structure and then\n * the state data structure follows.  This triple may be repeated for many\n * flavors.  The constants for the flavors, counts and state data structure\n * definitions are expected to be in the header file <machine/thread_status.h>.\n * These machine specific data structures sizes must be multiples of\n * 4 bytes.  The cmdsize reflects the total size of the thread_command\n * and all of the sizes of the constants for the flavors, counts and state\n * data structures.\n *\n * For executable objects that are unix processes there will be one\n * thread_command (cmd == LC_UNIXTHREAD) created for it by the link-editor.\n * This is the same as a LC_THREAD, except that a stack is automatically\n * created (based on the shell's limit for the stack size).  Command arguments\n * and environment variables are copied onto that stack.\n */\nstruct thread_command {\n\tuint32_t\tcmd;\t\t/* LC_THREAD or  LC_UNIXTHREAD */\n\tuint32_t\tcmdsize;\t/* total size of this command */\n\t/* uint32_t flavor\t\t   flavor of thread state */\n\t/* uint32_t count\t\t   count of uint32_t's in thread state */\n\t/* struct XXX_thread_state state   thread state for this flavor */\n\t/* ... */\n};\n\n/*\n * The routines command contains the address of the dynamic shared library \n * initialization routine and an index into the module table for the module\n * that defines the routine.  Before any modules are used from the library the\n * dynamic linker fully binds the module that defines the initialization routine\n * and then calls it.  This gets called before any module initialization\n * routines (used for C++ static constructors) in the library.\n */\nstruct routines_command { /* for 32-bit architectures */\n\tuint32_t\tcmd;\t\t/* LC_ROUTINES */\n\tuint32_t\tcmdsize;\t/* total size of this command */\n\tuint32_t\tinit_address;\t/* address of initialization routine */\n\tuint32_t\tinit_module;\t/* index into the module table that */\n\t\t\t\t        /*  the init routine is defined in */\n\tuint32_t\treserved1;\n\tuint32_t\treserved2;\n\tuint32_t\treserved3;\n\tuint32_t\treserved4;\n\tuint32_t\treserved5;\n\tuint32_t\treserved6;\n};\n\n/*\n * The 64-bit routines command.  Same use as above.\n */\nstruct routines_command_64 { /* for 64-bit architectures */\n\tuint32_t\tcmd;\t\t/* LC_ROUTINES_64 */\n\tuint32_t\tcmdsize;\t/* total size of this command */\n\tuint64_t\tinit_address;\t/* address of initialization routine */\n\tuint64_t\tinit_module;\t/* index into the module table that */\n\t\t\t\t\t/*  the init routine is defined in */\n\tuint64_t\treserved1;\n\tuint64_t\treserved2;\n\tuint64_t\treserved3;\n\tuint64_t\treserved4;\n\tuint64_t\treserved5;\n\tuint64_t\treserved6;\n};\n\n/*\n * The symtab_command contains the offsets and sizes of the link-edit 4.3BSD\n * \"stab\" style symbol table information as described in the header files\n * <nlist.h> and <stab.h>.\n */\nstruct symtab_command {\n\tuint32_t\tcmd;\t\t/* LC_SYMTAB */\n\tuint32_t\tcmdsize;\t/* sizeof(struct symtab_command) */\n\tuint32_t\tsymoff;\t\t/* symbol table offset */\n\tuint32_t\tnsyms;\t\t/* number of symbol table entries */\n\tuint32_t\tstroff;\t\t/* string table offset */\n\tuint32_t\tstrsize;\t/* string table size in bytes */\n};\n\n/*\n * This is the second set of the symbolic information which is used to support\n * the data structures for the dynamically link editor.\n *\n * The original set of symbolic information in the symtab_command which contains\n * the symbol and string tables must also be present when this load command is\n * present.  When this load command is present the symbol table is organized\n * into three groups of symbols:\n *\tlocal symbols (static and debugging symbols) - grouped by module\n *\tdefined external symbols - grouped by module (sorted by name if not lib)\n *\tundefined external symbols (sorted by name if MH_BINDATLOAD is not set,\n *\t     \t\t\t    and in order the were seen by the static\n *\t\t\t\t    linker if MH_BINDATLOAD is set)\n * In this load command there are offsets and counts to each of the three groups\n * of symbols.\n *\n * This load command contains a the offsets and sizes of the following new\n * symbolic information tables:\n *\ttable of contents\n *\tmodule table\n *\treference symbol table\n *\tindirect symbol table\n * The first three tables above (the table of contents, module table and\n * reference symbol table) are only present if the file is a dynamically linked\n * shared library.  For executable and object modules, which are files\n * containing only one module, the information that would be in these three\n * tables is determined as follows:\n * \ttable of contents - the defined external symbols are sorted by name\n *\tmodule table - the file contains only one module so everything in the\n *\t\t       file is part of the module.\n *\treference symbol table - is the defined and undefined external symbols\n *\n * For dynamically linked shared library files this load command also contains\n * offsets and sizes to the pool of relocation entries for all sections\n * separated into two groups:\n *\texternal relocation entries\n *\tlocal relocation entries\n * For executable and object modules the relocation entries continue to hang\n * off the section structures.\n */\nstruct dysymtab_command {\n    uint32_t cmd;\t/* LC_DYSYMTAB */\n    uint32_t cmdsize;\t/* sizeof(struct dysymtab_command) */\n\n    /*\n     * The symbols indicated by symoff and nsyms of the LC_SYMTAB load command\n     * are grouped into the following three groups:\n     *    local symbols (further grouped by the module they are from)\n     *    defined external symbols (further grouped by the module they are from)\n     *    undefined symbols\n     *\n     * The local symbols are used only for debugging.  The dynamic binding\n     * process may have to use them to indicate to the debugger the local\n     * symbols for a module that is being bound.\n     *\n     * The last two groups are used by the dynamic binding process to do the\n     * binding (indirectly through the module table and the reference symbol\n     * table when this is a dynamically linked shared library file).\n     */\n    uint32_t ilocalsym;\t/* index to local symbols */\n    uint32_t nlocalsym;\t/* number of local symbols */\n\n    uint32_t iextdefsym;/* index to externally defined symbols */\n    uint32_t nextdefsym;/* number of externally defined symbols */\n\n    uint32_t iundefsym;\t/* index to undefined symbols */\n    uint32_t nundefsym;\t/* number of undefined symbols */\n\n    /*\n     * For the for the dynamic binding process to find which module a symbol\n     * is defined in the table of contents is used (analogous to the ranlib\n     * structure in an archive) which maps defined external symbols to modules\n     * they are defined in.  This exists only in a dynamically linked shared\n     * library file.  For executable and object modules the defined external\n     * symbols are sorted by name and is use as the table of contents.\n     */\n    uint32_t tocoff;\t/* file offset to table of contents */\n    uint32_t ntoc;\t/* number of entries in table of contents */\n\n    /*\n     * To support dynamic binding of \"modules\" (whole object files) the symbol\n     * table must reflect the modules that the file was created from.  This is\n     * done by having a module table that has indexes and counts into the merged\n     * tables for each module.  The module structure that these two entries\n     * refer to is described below.  This exists only in a dynamically linked\n     * shared library file.  For executable and object modules the file only\n     * contains one module so everything in the file belongs to the module.\n     */\n    uint32_t modtaboff;\t/* file offset to module table */\n    uint32_t nmodtab;\t/* number of module table entries */\n\n    /*\n     * To support dynamic module binding the module structure for each module\n     * indicates the external references (defined and undefined) each module\n     * makes.  For each module there is an offset and a count into the\n     * reference symbol table for the symbols that the module references.\n     * This exists only in a dynamically linked shared library file.  For\n     * executable and object modules the defined external symbols and the\n     * undefined external symbols indicates the external references.\n     */\n    uint32_t extrefsymoff;\t/* offset to referenced symbol table */\n    uint32_t nextrefsyms;\t/* number of referenced symbol table entries */\n\n    /*\n     * The sections that contain \"symbol pointers\" and \"routine stubs\" have\n     * indexes and (implied counts based on the size of the section and fixed\n     * size of the entry) into the \"indirect symbol\" table for each pointer\n     * and stub.  For every section of these two types the index into the\n     * indirect symbol table is stored in the section header in the field\n     * reserved1.  An indirect symbol table entry is simply a 32bit index into\n     * the symbol table to the symbol that the pointer or stub is referring to.\n     * The indirect symbol table is ordered to match the entries in the section.\n     */\n    uint32_t indirectsymoff; /* file offset to the indirect symbol table */\n    uint32_t nindirectsyms;  /* number of indirect symbol table entries */\n\n    /*\n     * To support relocating an individual module in a library file quickly the\n     * external relocation entries for each module in the library need to be\n     * accessed efficiently.  Since the relocation entries can't be accessed\n     * through the section headers for a library file they are separated into\n     * groups of local and external entries further grouped by module.  In this\n     * case the presents of this load command who's extreloff, nextrel,\n     * locreloff and nlocrel fields are non-zero indicates that the relocation\n     * entries of non-merged sections are not referenced through the section\n     * structures (and the reloff and nreloc fields in the section headers are\n     * set to zero).\n     *\n     * Since the relocation entries are not accessed through the section headers\n     * this requires the r_address field to be something other than a section\n     * offset to identify the item to be relocated.  In this case r_address is\n     * set to the offset from the vmaddr of the first LC_SEGMENT command.\n     * For MH_SPLIT_SEGS images r_address is set to the the offset from the\n     * vmaddr of the first read-write LC_SEGMENT command.\n     *\n     * The relocation entries are grouped by module and the module table\n     * entries have indexes and counts into them for the group of external\n     * relocation entries for that the module.\n     *\n     * For sections that are merged across modules there must not be any\n     * remaining external relocation entries for them (for merged sections\n     * remaining relocation entries must be local).\n     */\n    uint32_t extreloff;\t/* offset to external relocation entries */\n    uint32_t nextrel;\t/* number of external relocation entries */\n\n    /*\n     * All the local relocation entries are grouped together (they are not\n     * grouped by their module since they are only used if the object is moved\n     * from it staticly link edited address).\n     */\n    uint32_t locreloff;\t/* offset to local relocation entries */\n    uint32_t nlocrel;\t/* number of local relocation entries */\n\n};\t\n\n/*\n * An indirect symbol table entry is simply a 32bit index into the symbol table \n * to the symbol that the pointer or stub is refering to.  Unless it is for a\n * non-lazy symbol pointer section for a defined symbol which strip(1) as \n * removed.  In which case it has the value INDIRECT_SYMBOL_LOCAL.  If the\n * symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that.\n */\n#define INDIRECT_SYMBOL_LOCAL\t0x80000000\n#define INDIRECT_SYMBOL_ABS\t0x40000000\n\n\n/* a table of contents entry */\nstruct dylib_table_of_contents {\n    uint32_t symbol_index;\t/* the defined external symbol\n\t\t\t\t   (index into the symbol table) */\n    uint32_t module_index;\t/* index into the module table this symbol\n\t\t\t\t   is defined in */\n};\t\n\n/* a module table entry */\nstruct dylib_module {\n    uint32_t module_name;\t/* the module name (index into string table) */\n\n    uint32_t iextdefsym;\t/* index into externally defined symbols */\n    uint32_t nextdefsym;\t/* number of externally defined symbols */\n    uint32_t irefsym;\t\t/* index into reference symbol table */\n    uint32_t nrefsym;\t\t/* number of reference symbol table entries */\n    uint32_t ilocalsym;\t\t/* index into symbols for local symbols */\n    uint32_t nlocalsym;\t\t/* number of local symbols */\n\n    uint32_t iextrel;\t\t/* index into external relocation entries */\n    uint32_t nextrel;\t\t/* number of external relocation entries */\n\n    uint32_t iinit_iterm;\t/* low 16 bits are the index into the init\n\t\t\t\t   section, high 16 bits are the index into\n\t\t\t           the term section */\n    uint32_t ninit_nterm;\t/* low 16 bits are the number of init section\n\t\t\t\t   entries, high 16 bits are the number of\n\t\t\t\t   term section entries */\n\n    uint32_t\t\t\t/* for this module address of the start of */\n\tobjc_module_info_addr;  /*  the (__OBJC,__module_info) section */\n    uint32_t\t\t\t/* for this module size of */\n\tobjc_module_info_size;\t/*  the (__OBJC,__module_info) section */\n};\t\n\n/* a 64-bit module table entry */\nstruct dylib_module_64 {\n    uint32_t module_name;\t/* the module name (index into string table) */\n\n    uint32_t iextdefsym;\t/* index into externally defined symbols */\n    uint32_t nextdefsym;\t/* number of externally defined symbols */\n    uint32_t irefsym;\t\t/* index into reference symbol table */\n    uint32_t nrefsym;\t\t/* number of reference symbol table entries */\n    uint32_t ilocalsym;\t\t/* index into symbols for local symbols */\n    uint32_t nlocalsym;\t\t/* number of local symbols */\n\n    uint32_t iextrel;\t\t/* index into external relocation entries */\n    uint32_t nextrel;\t\t/* number of external relocation entries */\n\n    uint32_t iinit_iterm;\t/* low 16 bits are the index into the init\n\t\t\t\t   section, high 16 bits are the index into\n\t\t\t\t   the term section */\n    uint32_t ninit_nterm;      /* low 16 bits are the number of init section\n\t\t\t\t  entries, high 16 bits are the number of\n\t\t\t\t  term section entries */\n\n    uint32_t\t\t\t/* for this module size of */\n        objc_module_info_size;\t/*  the (__OBJC,__module_info) section */\n    uint64_t\t\t\t/* for this module address of the start of */\n        objc_module_info_addr;\t/*  the (__OBJC,__module_info) section */\n};\n\n/* \n * The entries in the reference symbol table are used when loading the module\n * (both by the static and dynamic link editors) and if the module is unloaded\n * or replaced.  Therefore all external symbols (defined and undefined) are\n * listed in the module's reference table.  The flags describe the type of\n * reference that is being made.  The constants for the flags are defined in\n * <mach-o/nlist.h> as they are also used for symbol table entries.\n */\nstruct dylib_reference {\n    uint32_t isym:24,\t\t/* index into the symbol table */\n    \t\t  flags:8;\t/* flags to indicate the type of reference */\n};\n\n/*\n * The twolevel_hints_command contains the offset and number of hints in the\n * two-level namespace lookup hints table.\n */\nstruct twolevel_hints_command {\n    uint32_t cmd;\t/* LC_TWOLEVEL_HINTS */\n    uint32_t cmdsize;\t/* sizeof(struct twolevel_hints_command) */\n    uint32_t offset;\t/* offset to the hint table */\n    uint32_t nhints;\t/* number of hints in the hint table */\n};\n\n/*\n * The entries in the two-level namespace lookup hints table are twolevel_hint\n * structs.  These provide hints to the dynamic link editor where to start\n * looking for an undefined symbol in a two-level namespace image.  The\n * isub_image field is an index into the sub-images (sub-frameworks and\n * sub-umbrellas list) that made up the two-level image that the undefined\n * symbol was found in when it was built by the static link editor.  If\n * isub-image is 0 the the symbol is expected to be defined in library and not\n * in the sub-images.  If isub-image is non-zero it is an index into the array\n * of sub-images for the umbrella with the first index in the sub-images being\n * 1. The array of sub-images is the ordered list of sub-images of the umbrella\n * that would be searched for a symbol that has the umbrella recorded as its\n * primary library.  The table of contents index is an index into the\n * library's table of contents.  This is used as the starting point of the\n * binary search or a directed linear search.\n */\nstruct twolevel_hint {\n    uint32_t \n\tisub_image:8,\t/* index into the sub images */\n\titoc:24;\t/* index into the table of contents */\n};\n\n/*\n * The prebind_cksum_command contains the value of the original check sum for\n * prebound files or zero.  When a prebound file is first created or modified\n * for other than updating its prebinding information the value of the check sum\n * is set to zero.  When the file has it prebinding re-done and if the value of\n * the check sum is zero the original check sum is calculated and stored in\n * cksum field of this load command in the output file.  If when the prebinding\n * is re-done and the cksum field is non-zero it is left unchanged from the\n * input file.\n */\nstruct prebind_cksum_command {\n    uint32_t cmd;\t/* LC_PREBIND_CKSUM */\n    uint32_t cmdsize;\t/* sizeof(struct prebind_cksum_command) */\n    uint32_t cksum;\t/* the check sum or zero */\n};\n\n/*\n * The uuid load command contains a single 128-bit unique random number that\n * identifies an object produced by the static link editor.\n */\nstruct uuid_command {\n    uint32_t\tcmd;\t\t/* LC_UUID */\n    uint32_t\tcmdsize;\t/* sizeof(struct uuid_command) */\n    uint8_t\tuuid[16];\t/* the 128-bit uuid */\n};\n\n/*\n * The rpath_command contains a path which at runtime should be added to\n * the current run path used to find @rpath prefixed dylibs.\n */\nstruct rpath_command {\n    uint32_t\t cmd;\t\t/* LC_RPATH */\n    uint32_t\t cmdsize;\t/* includes string */\n    union lc_str path;\t\t/* path to add to run path */\n};\n\n/*\n * The linkedit_data_command contains the offsets and sizes of a blob\n * of data in the __LINKEDIT segment.  \n */\nstruct linkedit_data_command {\n    uint32_t\tcmd;\t\t/* LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO,\n                                   LC_FUNCTION_STARTS, LC_DATA_IN_CODE,\n\t\t\t\t   LC_DYLIB_CODE_SIGN_DRS,\n\t\t\t\t   LC_LINKER_OPTIMIZATION_HINT,\n\t\t\t\t   LC_DYLD_EXPORTS_TRIE, or\n\t\t\t\t   LC_DYLD_CHAINED_FIXUPS. */\n    uint32_t\tcmdsize;\t/* sizeof(struct linkedit_data_command) */\n    uint32_t\tdataoff;\t/* file offset of data in __LINKEDIT segment */\n    uint32_t\tdatasize;\t/* file size of data in __LINKEDIT segment  */\n};\n\nstruct fileset_entry_command {\n    uint32_t        cmd;        /* LC_FILESET_ENTRY */\n    uint32_t        cmdsize;    /* includes id string */\n    uint64_t        vmaddr;     /* memory address of the dylib */\n    uint64_t        fileoff;    /* file offset of the dylib */\n    union lc_str    entry_id;   /* contained entry id */\n    uint32_t        reserved;   /* entry_id is 32-bits long, so this is the reserved padding */\n};\n\n/*\n * The encryption_info_command contains the file offset and size of an\n * of an encrypted segment.\n */\nstruct encryption_info_command {\n   uint32_t\tcmd;\t\t/* LC_ENCRYPTION_INFO */\n   uint32_t\tcmdsize;\t/* sizeof(struct encryption_info_command) */\n   uint32_t\tcryptoff;\t/* file offset of encrypted range */\n   uint32_t\tcryptsize;\t/* file size of encrypted range */\n   uint32_t\tcryptid;\t/* which enryption system,\n\t\t\t\t   0 means not-encrypted yet */\n};\n\n/*\n * The encryption_info_command_64 contains the file offset and size of an\n * of an encrypted segment (for use in x86_64 targets).\n */\nstruct encryption_info_command_64 {\n   uint32_t\tcmd;\t\t/* LC_ENCRYPTION_INFO_64 */\n   uint32_t\tcmdsize;\t/* sizeof(struct encryption_info_command_64) */\n   uint32_t\tcryptoff;\t/* file offset of encrypted range */\n   uint32_t\tcryptsize;\t/* file size of encrypted range */\n   uint32_t\tcryptid;\t/* which enryption system,\n\t\t\t\t   0 means not-encrypted yet */\n   uint32_t\tpad;\t\t/* padding to make this struct's size a multiple\n\t\t\t\t   of 8 bytes */\n};\n\n/*\n * The version_min_command contains the min OS version on which this \n * binary was built to run.\n */\nstruct version_min_command {\n    uint32_t\tcmd;\t\t/* LC_VERSION_MIN_MACOSX or\n\t\t\t\t   LC_VERSION_MIN_IPHONEOS or\n\t\t\t\t   LC_VERSION_MIN_WATCHOS or\n\t\t\t\t   LC_VERSION_MIN_TVOS */\n    uint32_t\tcmdsize;\t/* sizeof(struct min_version_command) */\n    uint32_t\tversion;\t/* X.Y.Z is encoded in nibbles xxxx.yy.zz */\n    uint32_t\tsdk;\t\t/* X.Y.Z is encoded in nibbles xxxx.yy.zz */\n};\n\n/*\n * The build_version_command contains the min OS version on which this\n * binary was built to run for its platform.  The list of known platforms and\n * tool values following it.\n */\nstruct build_version_command {\n    uint32_t\tcmd;\t\t/* LC_BUILD_VERSION */\n    uint32_t\tcmdsize;\t/* sizeof(struct build_version_command) plus */\n                                /* ntools * sizeof(struct build_tool_version) */\n    uint32_t\tplatform;\t/* platform */\n    uint32_t\tminos;\t\t/* X.Y.Z is encoded in nibbles xxxx.yy.zz */\n    uint32_t\tsdk;\t\t/* X.Y.Z is encoded in nibbles xxxx.yy.zz */\n    uint32_t\tntools;\t\t/* number of tool entries following this */\n};\n\nstruct build_tool_version {\n    uint32_t\ttool;\t\t/* enum for the tool */\n    uint32_t\tversion;\t/* version number of the tool */\n};\n\n/* Known values for the platform field above. */\n#define PLATFORM_MACOS 1\n#define PLATFORM_IOS 2\n#define PLATFORM_TVOS 3\n#define PLATFORM_WATCHOS 4\n#define PLATFORM_BRIDGEOS 5\n#define PLATFORM_MACCATALYST 6\n#define PLATFORM_IOSSIMULATOR 7\n#define PLATFORM_TVOSSIMULATOR 8\n#define PLATFORM_WATCHOSSIMULATOR 9\n#define PLATFORM_DRIVERKIT 10\n#define PLATFORM_MAX PLATFORM_DRIVERKIT\n/* Addition of simulated platfrom also needs to update proc_is_simulated() */ \n\n/* Known values for the tool field above. */\n#define TOOL_CLANG 1\n#define TOOL_SWIFT 2\n#define TOOL_LD\t3\n\n/*\n * The dyld_info_command contains the file offsets and sizes of \n * the new compressed form of the information dyld needs to \n * load the image.  This information is used by dyld on Mac OS X\n * 10.6 and later.  All information pointed to by this command\n * is encoded using byte streams, so no endian swapping is needed\n * to interpret it. \n */\nstruct dyld_info_command {\n   uint32_t   cmd;\t\t/* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */\n   uint32_t   cmdsize;\t\t/* sizeof(struct dyld_info_command) */\n\n    /*\n     * Dyld rebases an image whenever dyld loads it at an address different\n     * from its preferred address.  The rebase information is a stream\n     * of byte sized opcodes whose symbolic names start with REBASE_OPCODE_.\n     * Conceptually the rebase information is a table of tuples:\n     *    <seg-index, seg-offset, type>\n     * The opcodes are a compressed way to encode the table by only\n     * encoding when a column changes.  In addition simple patterns\n     * like \"every n'th offset for m times\" can be encoded in a few\n     * bytes.\n     */\n    uint32_t   rebase_off;\t/* file offset to rebase info  */\n    uint32_t   rebase_size;\t/* size of rebase info   */\n    \n    /*\n     * Dyld binds an image during the loading process, if the image\n     * requires any pointers to be initialized to symbols in other images.  \n     * The bind information is a stream of byte sized \n     * opcodes whose symbolic names start with BIND_OPCODE_.\n     * Conceptually the bind information is a table of tuples:\n     *    <seg-index, seg-offset, type, symbol-library-ordinal, symbol-name, addend>\n     * The opcodes are a compressed way to encode the table by only\n     * encoding when a column changes.  In addition simple patterns\n     * like for runs of pointers initialzed to the same value can be \n     * encoded in a few bytes.\n     */\n    uint32_t   bind_off;\t/* file offset to binding info   */\n    uint32_t   bind_size;\t/* size of binding info  */\n        \n    /*\n     * Some C++ programs require dyld to unique symbols so that all\n     * images in the process use the same copy of some code/data.\n     * This step is done after binding. The content of the weak_bind\n     * info is an opcode stream like the bind_info.  But it is sorted\n     * alphabetically by symbol name.  This enable dyld to walk \n     * all images with weak binding information in order and look\n     * for collisions.  If there are no collisions, dyld does\n     * no updating.  That means that some fixups are also encoded\n     * in the bind_info.  For instance, all calls to \"operator new\"\n     * are first bound to libstdc++.dylib using the information\n     * in bind_info.  Then if some image overrides operator new\n     * that is detected when the weak_bind information is processed\n     * and the call to operator new is then rebound.\n     */\n    uint32_t   weak_bind_off;\t/* file offset to weak binding info   */\n    uint32_t   weak_bind_size;  /* size of weak binding info  */\n    \n    /*\n     * Some uses of external symbols do not need to be bound immediately.\n     * Instead they can be lazily bound on first use.  The lazy_bind\n     * are contains a stream of BIND opcodes to bind all lazy symbols.\n     * Normal use is that dyld ignores the lazy_bind section when\n     * loading an image.  Instead the static linker arranged for the\n     * lazy pointer to initially point to a helper function which \n     * pushes the offset into the lazy_bind area for the symbol\n     * needing to be bound, then jumps to dyld which simply adds\n     * the offset to lazy_bind_off to get the information on what \n     * to bind.  \n     */\n    uint32_t   lazy_bind_off;\t/* file offset to lazy binding info */\n    uint32_t   lazy_bind_size;  /* size of lazy binding infs */\n    \n    /*\n     * The symbols exported by a dylib are encoded in a trie.  This\n     * is a compact representation that factors out common prefixes.\n     * It also reduces LINKEDIT pages in RAM because it encodes all  \n     * information (name, address, flags) in one small, contiguous range.\n     * The export area is a stream of nodes.  The first node sequentially\n     * is the start node for the trie.  \n     *\n     * Nodes for a symbol start with a uleb128 that is the length of\n     * the exported symbol information for the string so far.\n     * If there is no exported symbol, the node starts with a zero byte. \n     * If there is exported info, it follows the length.  \n\t *\n\t * First is a uleb128 containing flags. Normally, it is followed by\n     * a uleb128 encoded offset which is location of the content named\n     * by the symbol from the mach_header for the image.  If the flags\n     * is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is\n     * a uleb128 encoded library ordinal, then a zero terminated\n     * UTF8 string.  If the string is zero length, then the symbol\n     * is re-export from the specified dylib with the same name.\n\t * If the flags is EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER, then following\n\t * the flags is two uleb128s: the stub offset and the resolver offset.\n\t * The stub is used by non-lazy pointers.  The resolver is used\n\t * by lazy pointers and must be called to get the actual address to use.\n     *\n     * After the optional exported symbol information is a byte of\n     * how many edges (0-255) that this node has leaving it, \n     * followed by each edge.\n     * Each edge is a zero terminated UTF8 of the addition chars\n     * in the symbol, followed by a uleb128 offset for the node that\n     * edge points to.\n     *  \n     */\n    uint32_t   export_off;\t/* file offset to lazy binding info */\n    uint32_t   export_size;\t/* size of lazy binding infs */\n};\n\n/*\n * The following are used to encode rebasing information\n */\n#define REBASE_TYPE_POINTER\t\t\t\t\t1\n#define REBASE_TYPE_TEXT_ABSOLUTE32\t\t\t\t2\n#define REBASE_TYPE_TEXT_PCREL32\t\t\t\t3\n\n#define REBASE_OPCODE_MASK\t\t\t\t\t0xF0\n#define REBASE_IMMEDIATE_MASK\t\t\t\t\t0x0F\n#define REBASE_OPCODE_DONE\t\t\t\t\t0x00\n#define REBASE_OPCODE_SET_TYPE_IMM\t\t\t\t0x10\n#define REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB\t\t0x20\n#define REBASE_OPCODE_ADD_ADDR_ULEB\t\t\t\t0x30\n#define REBASE_OPCODE_ADD_ADDR_IMM_SCALED\t\t\t0x40\n#define REBASE_OPCODE_DO_REBASE_IMM_TIMES\t\t\t0x50\n#define REBASE_OPCODE_DO_REBASE_ULEB_TIMES\t\t\t0x60\n#define REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB\t\t\t0x70\n#define REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB\t0x80\n\n\n/*\n * The following are used to encode binding information\n */\n#define BIND_TYPE_POINTER\t\t\t\t\t1\n#define BIND_TYPE_TEXT_ABSOLUTE32\t\t\t\t2\n#define BIND_TYPE_TEXT_PCREL32\t\t\t\t\t3\n\n#define BIND_SPECIAL_DYLIB_SELF\t\t\t\t\t 0\n#define BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE\t\t\t-1\n#define BIND_SPECIAL_DYLIB_FLAT_LOOKUP\t\t\t\t-2\n#define BIND_SPECIAL_DYLIB_WEAK_LOOKUP\t\t\t\t-3\n\n#define BIND_SYMBOL_FLAGS_WEAK_IMPORT\t\t\t\t0x1\n#define BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION\t\t\t0x8\n\n#define BIND_OPCODE_MASK\t\t\t\t\t0xF0\n#define BIND_IMMEDIATE_MASK\t\t\t\t\t0x0F\n#define BIND_OPCODE_DONE\t\t\t\t\t0x00\n#define BIND_OPCODE_SET_DYLIB_ORDINAL_IMM\t\t\t0x10\n#define BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB\t\t\t0x20\n#define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM\t\t\t0x30\n#define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM\t\t0x40\n#define BIND_OPCODE_SET_TYPE_IMM\t\t\t\t0x50\n#define BIND_OPCODE_SET_ADDEND_SLEB\t\t\t\t0x60\n#define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB\t\t\t0x70\n#define BIND_OPCODE_ADD_ADDR_ULEB\t\t\t\t0x80\n#define BIND_OPCODE_DO_BIND\t\t\t\t\t0x90\n#define BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB\t\t\t0xA0\n#define BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED\t\t\t0xB0\n#define BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB\t\t0xC0\n#define\tBIND_OPCODE_THREADED\t\t\t\t\t0xD0\n#define\tBIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB 0x00\n#define\tBIND_SUBOPCODE_THREADED_APPLY\t\t\t\t 0x01\n\n\n/*\n * The following are used on the flags byte of a terminal node\n * in the export information.\n */\n#define EXPORT_SYMBOL_FLAGS_KIND_MASK\t\t\t\t0x03\n#define EXPORT_SYMBOL_FLAGS_KIND_REGULAR\t\t\t0x00\n#define EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL\t\t\t0x01\n#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE\t\t\t0x02\n#define EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION\t\t\t0x04\n#define EXPORT_SYMBOL_FLAGS_REEXPORT\t\t\t\t0x08\n#define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER\t\t\t0x10\n\n/*\n * The linker_option_command contains linker options embedded in object files.\n */\nstruct linker_option_command {\n    uint32_t  cmd;\t/* LC_LINKER_OPTION only used in MH_OBJECT filetypes */\n    uint32_t  cmdsize;\n    uint32_t  count;\t/* number of strings */\n    /* concatenation of zero terminated UTF8 strings.\n       Zero filled at end to align */\n};\n\n/*\n * The symseg_command contains the offset and size of the GNU style\n * symbol table information as described in the header file <symseg.h>.\n * The symbol roots of the symbol segments must also be aligned properly\n * in the file.  So the requirement of keeping the offsets aligned to a\n * multiple of a 4 bytes translates to the length field of the symbol\n * roots also being a multiple of a long.  Also the padding must again be\n * zeroed. (THIS IS OBSOLETE and no longer supported).\n */\nstruct symseg_command {\n\tuint32_t\tcmd;\t\t/* LC_SYMSEG */\n\tuint32_t\tcmdsize;\t/* sizeof(struct symseg_command) */\n\tuint32_t\toffset;\t\t/* symbol segment offset */\n\tuint32_t\tsize;\t\t/* symbol segment size in bytes */\n};\n\n/*\n * The ident_command contains a free format string table following the\n * ident_command structure.  The strings are null terminated and the size of\n * the command is padded out with zero bytes to a multiple of 4 bytes/\n * (THIS IS OBSOLETE and no longer supported).\n */\nstruct ident_command {\n\tuint32_t cmd;\t\t/* LC_IDENT */\n\tuint32_t cmdsize;\t/* strings that follow this command */\n};\n\n/*\n * The fvmfile_command contains a reference to a file to be loaded at the\n * specified virtual address.  (Presently, this command is reserved for\n * internal use.  The kernel ignores this command when loading a program into\n * memory).\n */\nstruct fvmfile_command {\n\tuint32_t cmd;\t\t\t/* LC_FVMFILE */\n\tuint32_t cmdsize;\t\t/* includes pathname string */\n\tunion lc_str\tname;\t\t/* files pathname */\n\tuint32_t\theader_addr;\t/* files virtual address */\n};\n\n\n/*\n * The entry_point_command is a replacement for thread_command.\n * It is used for main executables to specify the location (file offset)\n * of main().  If -stack_size was used at link time, the stacksize\n * field will contain the stack size need for the main thread.\n */\nstruct entry_point_command {\n    uint32_t  cmd;\t/* LC_MAIN only used in MH_EXECUTE filetypes */\n    uint32_t  cmdsize;\t/* 24 */\n    uint64_t  entryoff;\t/* file (__TEXT) offset of main() */\n    uint64_t  stacksize;/* if not zero, initial stack size */\n};\n\n\n/*\n * The source_version_command is an optional load command containing\n * the version of the sources used to build the binary.\n */\nstruct source_version_command {\n    uint32_t  cmd;\t/* LC_SOURCE_VERSION */\n    uint32_t  cmdsize;\t/* 16 */\n    uint64_t  version;\t/* A.B.C.D.E packed as a24.b10.c10.d10.e10 */\n};\n\n\n/*\n * The LC_DATA_IN_CODE load commands uses a linkedit_data_command \n * to point to an array of data_in_code_entry entries. Each entry\n * describes a range of data in a code section.\n */\nstruct data_in_code_entry {\n    uint32_t\toffset;  /* from mach_header to start of data range*/\n    uint16_t\tlength;  /* number of bytes in data range */\n    uint16_t\tkind;    /* a DICE_KIND_* value  */\n};\n#define DICE_KIND_DATA              0x0001\n#define DICE_KIND_JUMP_TABLE8       0x0002\n#define DICE_KIND_JUMP_TABLE16      0x0003\n#define DICE_KIND_JUMP_TABLE32      0x0004\n#define DICE_KIND_ABS_JUMP_TABLE32  0x0005\n\n\n\n/*\n * Sections of type S_THREAD_LOCAL_VARIABLES contain an array \n * of tlv_descriptor structures.\n */\nstruct tlv_descriptor\n{\n\tvoid*\t\t(*thunk)(struct tlv_descriptor*);\n\tunsigned long\tkey;\n\tunsigned long\toffset;\n};\n\n/*\n * LC_NOTE commands describe a region of arbitrary data included in a Mach-O\n * file.  Its initial use is to record extra data in MH_CORE files.\n */\nstruct note_command {\n    uint32_t\tcmd;\t\t/* LC_NOTE */\n    uint32_t\tcmdsize;\t/* sizeof(struct note_command) */\n    char\tdata_owner[16];\t/* owner name for this LC_NOTE */\n    uint64_t\toffset;\t\t/* file offset of this data */\n    uint64_t\tsize;\t\t/* length of data region */\n};\n\n#endif /* _MACHO_LOADER_H_ */\n"
  },
  {
    "path": "third_party/xnu/README.crashpad",
    "content": "Name: XNU\nShort Name: xnu\nURL: https://github.com/apple-oss-distributions/xnu/\nVersion: 12377.1.9 (from macOS 26.0)\nRevision: f6217f891ac0bb64f3d375211650a4c1ff8ca1ea\nLicense: APSL 2.0\nLicense File: APPLE_LICENSE\nSecurity Critical: no\nShipped: yes\n\nDescription:\nXNU is the operating system kernel used on macOS and other Apple systems.\n\nLocal Modifications:\n - EXTERNAL_HEADERS/mach-o/loader.h is present. Its #includes of\n   <mach/machine/thread_status.h> and <architecture/byte_order.h> have been\n   commented out as unnecessary. Note that its #includes of <mach/machine.h> and\n   <mach/vm_prot.h> have been retained but these headers have not been provided.\n   External headers must be made available to provide the cpu_type_t,\n   cpu_subtype_t, and vm_prot_t types.\n - osfmk/mach/exc.defs and osfmk/mach/mach_exc.defs are present, to fill in\n   for <mach/exc.defs> and <mach/mach_exc.defs> on iOS, where they are missing.\n   The .defs files they depend on, <mach/mach_types.defs>,\n   <mach/machine/machine_types.defs>, and <mach/std_types.defs> are also\n   included.\n - Anything not listed above is omitted.\n"
  },
  {
    "path": "third_party/xnu/osfmk/mach/exc.defs",
    "content": "/*\n * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.\n *\n * @APPLE_OSREFERENCE_LICENSE_HEADER_START@\n * \n * This file contains Original Code and/or Modifications of Original Code\n * as defined in and that are subject to the Apple Public Source License\n * Version 2.0 (the 'License'). You may not use this file except in\n * compliance with the License. The rights granted to you under the License\n * may not be used to create, or enable the creation or redistribution of,\n * unlawful or unlicensed copies of an Apple operating system, or to\n * circumvent, violate, or enable the circumvention or violation of, any\n * terms of an Apple operating system software license agreement.\n * \n * Please obtain a copy of the License at\n * http://www.opensource.apple.com/apsl/ and read it before using this file.\n * \n * The Original Code and all software distributed under the License are\n * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER\n * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,\n * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.\n * Please see the License for the specific language governing rights and\n * limitations under the License.\n * \n * @APPLE_OSREFERENCE_LICENSE_HEADER_END@\n */\n/*\n * @OSF_COPYRIGHT@\n */\n/* \n * Mach Operating System\n * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University\n * All Rights Reserved.\n * \n * Permission to use, copy, modify and distribute this software and its\n * documentation is hereby granted, provided that both the copyright\n * notice and this permission notice appear in all copies of the\n * software, derivative works or modified versions, and any portions\n * thereof, and that both notices appear in supporting documentation.\n * \n * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS \"AS IS\"\n * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR\n * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.\n * \n * Carnegie Mellon requests users of this software to return to\n * \n *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU\n *  School of Computer Science\n *  Carnegie Mellon University\n *  Pittsburgh PA 15213-3890\n * \n * any improvements or extensions that they make and grant Carnegie Mellon\n * the rights to redistribute these changes.\n */\n/*\n */\n/*\n * Abstract:\n *\tMiG definitions file for Mach exception interface.\n */\n\nsubsystem\n#if\tKERNEL_SERVER || KOBJECT_SERVER\n\tKernelServer\n#endif\t/* KERNEL_SERVER || KOBJECT_SERVER */\n#if\tKERNEL_USER\n\t  KernelUser\n#endif\n\t\t     exc 2401;\n\n#include <mach/std_types.defs>\n#include <mach/mach_types.defs>\n\nServerPrefix catch_;\n\ntype exception_data_t\t\t= array[*:2] of integer_t;\ntype exception_type_t\t\t= int;\n\nroutine\t\texception_raise(\n\t\t\texception_port\t: mach_port_t;\n\t\t\tthread\t\t: mach_port_t;\n\t\t\ttask\t\t: mach_port_t;\n\t\t\texception\t: exception_type_t;\n\t\t\tcode\t\t: exception_data_t\n#if EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n\nroutine\t\texception_raise_state(\n\t\t\texception_port\t: mach_port_t;\n\t\t\texception\t: exception_type_t;\n\t\t\tcode\t\t: exception_data_t, const;\n\t\t  inout flavor\t\t: int;\n\t\t\told_state\t: thread_state_t, const;\n\t\t    out new_state\t: thread_state_t\n#if EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n\nroutine\t\texception_raise_state_identity(\n\t\t\texception_port  : mach_port_t;\n\t\t\tthread          : mach_port_t;\n\t\t\ttask            : mach_port_t;\n\t\t\texception       : exception_type_t;\n\t\t\tcode            : exception_data_t;\n\t\t  inout flavor          : int;\n\t\t\told_state       : thread_state_t;\n\t\t    out new_state       : thread_state_t\n#if EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n\n/* vim: set ft=c : */\n"
  },
  {
    "path": "third_party/xnu/osfmk/mach/mach_exc.defs",
    "content": "/*\n * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.\n *\n * @APPLE_OSREFERENCE_LICENSE_HEADER_START@\n * \n * This file contains Original Code and/or Modifications of Original Code\n * as defined in and that are subject to the Apple Public Source License\n * Version 2.0 (the 'License'). You may not use this file except in\n * compliance with the License. The rights granted to you under the License\n * may not be used to create, or enable the creation or redistribution of,\n * unlawful or unlicensed copies of an Apple operating system, or to\n * circumvent, violate, or enable the circumvention or violation of, any\n * terms of an Apple operating system software license agreement.\n * \n * Please obtain a copy of the License at\n * http://www.opensource.apple.com/apsl/ and read it before using this file.\n * \n * The Original Code and all software distributed under the License are\n * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER\n * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,\n * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.\n * Please see the License for the specific language governing rights and\n * limitations under the License.\n * \n * @APPLE_OSREFERENCE_LICENSE_HEADER_END@\n */\n/*\n * @OSF_COPYRIGHT@\n */\n/* \n * Mach Operating System\n * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University\n * All Rights Reserved.\n * \n * Permission to use, copy, modify and distribute this software and its\n * documentation is hereby granted, provided that both the copyright\n * notice and this permission notice appear in all copies of the\n * software, derivative works or modified versions, and any portions\n * thereof, and that both notices appear in supporting documentation.\n * \n * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS \"AS IS\"\n * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR\n * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.\n * \n * Carnegie Mellon requests users of this software to return to\n * \n *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU\n *  School of Computer Science\n *  Carnegie Mellon University\n *  Pittsburgh PA 15213-3890\n * \n * any improvements or extensions that they make and grant Carnegie Mellon\n * the rights to redistribute these changes.\n */\n/*\n */\n/*\n * Abstract:\n *\tMiG definitions file for Mach exception interface.\n */\n\nsubsystem\n#if\tKERNEL_SERVER || KOBJECT_SERVER\n\tKernelServer\n#endif\t/* KERNEL_SERVER || KOBJECT_SERVER */\n#if\tKERNEL_USER\n\t  KernelUser\n#endif\n\t\t     mach_exc 2405;\n\n#include <mach/std_types.defs>\n#include <mach/mach_types.defs>\n\nServerPrefix catch_;\n\ntype mach_exception_data_t\t= array[*:2] of int64_t;\ntype exception_type_t\t\t= int;\n\nroutine\t\tmach_exception_raise(\n\t\t\texception_port\t: mach_port_t;\n\t\t\tthread\t\t: mach_port_t;\n\t\t\ttask\t\t: mach_port_t;\n\t\t\texception\t: exception_type_t;\n\t\t\tcode\t\t: mach_exception_data_t\n#if MACH_EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if MACH_EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n\nroutine\t\tmach_exception_raise_state(\n\t\t\texception_port\t: mach_port_t;\n\t\t\texception\t: exception_type_t;\n\t\t\tcode\t\t: mach_exception_data_t, const;\n\t\t  inout flavor\t\t: int;\n\t\t\told_state\t: thread_state_t, const;\n\t\t    out new_state\t: thread_state_t\n#if MACH_EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if MACH_EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n\nroutine\t\tmach_exception_raise_state_identity(\n\t\t\texception_port  : mach_port_t;\n\t\t\tthread          : mach_port_t;\n\t\t\ttask            : mach_port_t;\n\t\t\texception       : exception_type_t;\n\t\t\tcode            : mach_exception_data_t;\n\t\t  inout flavor          : int;\n\t\t\told_state       : thread_state_t;\n\t\t    out new_state       : thread_state_t\n#if MACH_EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if MACH_EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n\n#if MACH_EXC_SERVER_TASKIDTOKEN || ((KERNEL_USER || KERNEL_SERVER) && !SANDBOX_COMPILER)\nroutine\t\tmach_exception_raise_identity_protected(\n\t\t\texception_port\t: mach_port_t;\n\t\t\tthread_id\t: uint64_t;\n\t\t\ttask_id_token_t\t: mach_port_t;\n\t\t\texception\t: exception_type_t;\n\t\t\tcode\t\t: mach_exception_data_t\n#if MACH_EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if MACH_EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n#else\n\tskip;\n#endif /* MACH_EXC_SERVER_TASKIDTOKEN */\n\n#if MACH_EXC_SERVER_BACKTRACE || ((KERNEL_USER || KERNEL_SERVER) && !SANDBOX_COMPILER)\nroutine\t\tmach_exception_raise_backtrace(\n\t\t\texception_port\t: mach_port_t;\n\t\t\tkcdata_object_t\t: mach_port_t;\n\t\t\texception\t: exception_type_t;\n\t\t\tcode\t\t: mach_exception_data_t\n#if MACH_EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if MACH_EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n#else\n\tskip;\n#endif /* MACH_EXC_SERVER_BACKTRACE */\n\n/*\n * Same as identity_protected above, but also handles an in/out thread_state_t. \n */\n#if MACH_EXC_SERVER_TASKIDTOKEN_STATE || ((KERNEL_USER || KERNEL_SERVER) && !SANDBOX_COMPILER)\nroutine\t\tmach_exception_raise_state_identity_protected(\n\t\t\texception_port\t: mach_port_t;\n\t\t\tthread_id\t: uint64_t;\n\t\t\ttask_id_token_t\t: mach_port_t;\n\t\t\texception\t: exception_type_t;\n\t\t\tcode\t\t: mach_exception_data_t;\n\t\t\tinout flavor    : int;\n\t\t\told_state       : thread_state_t;\n\t\t    out new_state   : thread_state_t\n#if MACH_EXC_SERVER_SECTOKEN\n ;\n ServerSecToken stoken\t: security_token_t\n#endif\n#if MACH_EXC_SERVER_AUDITTOKEN\n ;\n ServerAuditToken atoken: audit_token_t\n#endif\n\t\t\t);\n#else  /* MACH_EXC_SERVER_TASKIDTOKEN_STATE  || ((KERNEL_USER || KERNEL_SERVER) && !SANDBOX_COMPILER) */\n\tskip;\n#endif /* MACH_EXC_SERVER_TASKIDTOKEN_STATE  || ((KERNEL_USER || KERNEL_SERVER) && !SANDBOX_COMPILER) */\n\n\n/* vim: set ft=c : */\n"
  },
  {
    "path": "third_party/xnu/osfmk/mach/mach_types.defs",
    "content": "/*\n * Copyright (c) 2000-2016 Apple Inc. All rights reserved.\n *\n * @APPLE_OSREFERENCE_LICENSE_HEADER_START@\n * \n * This file contains Original Code and/or Modifications of Original Code\n * as defined in and that are subject to the Apple Public Source License\n * Version 2.0 (the 'License'). You may not use this file except in\n * compliance with the License. The rights granted to you under the License\n * may not be used to create, or enable the creation or redistribution of,\n * unlawful or unlicensed copies of an Apple operating system, or to\n * circumvent, violate, or enable the circumvention or violation of, any\n * terms of an Apple operating system software license agreement.\n * \n * Please obtain a copy of the License at\n * http://www.opensource.apple.com/apsl/ and read it before using this file.\n * \n * The Original Code and all software distributed under the License are\n * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER\n * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,\n * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.\n * Please see the License for the specific language governing rights and\n * limitations under the License.\n * \n * @APPLE_OSREFERENCE_LICENSE_HEADER_END@\n */\n/*\n * @OSF_COPYRIGHT@\n */\n/* \n * Mach Operating System\n * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University\n * All Rights Reserved.\n * \n * Permission to use, copy, modify and distribute this software and its\n * documentation is hereby granted, provided that both the copyright\n * notice and this permission notice appear in all copies of the\n * software, derivative works or modified versions, and any portions\n * thereof, and that both notices appear in supporting documentation.\n * \n * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS \"AS IS\"\n * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR\n * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.\n * \n * Carnegie Mellon requests users of this software to return to\n * \n *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU\n *  School of Computer Science\n *  Carnegie Mellon University\n *  Pittsburgh PA 15213-3890\n * \n * any improvements or extensions that they make and grant Carnegie Mellon\n * the rights to redistribute these changes.\n */\n/*\n * NOTICE: This file was modified by McAfee Research in 2004 to introduce\n * support for mandatory and extensible security protections.  This notice\n * is included in support of clause 2.2 (b) of the Apple Public License,\n * Version 2.0.\n */\n/*\n */\n/*\n *\tMach kernel interface type declarations\n */\n\n#ifndef\t_MACH_MACH_TYPES_DEFS_\n#define _MACH_MACH_TYPES_DEFS_\n\n\n#include <mach/std_types.defs>\n\ntype memory_object_offset_t \t= uint64_t VM_UNSAFE_TYPE(memory_object_offset_ut);\ntype memory_object_size_t \t= uint64_t VM_UNSAFE_TYPE(memory_object_size_ut);\ntype memory_object_cluster_size_t = uint32_t;\ntype memory_object_fault_info_t = array[16] of integer_t;\n\n#ifdef KERNEL_PRIVATE\n\n/* Universal Page Lists - restricted to (in-kernel) pagers for now */\ntype upl_size_t\t\t\t= uint32_t;\ntype upl_offset_t\t\t\t= uint32_t;\ntype upl_page_info_t\t\t= struct[2] of integer_t;\ntype upl_page_info_array_t\t= array[*:256] of upl_page_info_t;\n\ntype upl_t = mach_port_t\n\t\tintran: upl_t convert_port_to_upl(mach_port_t)\n\t\touttran: mach_port_t convert_upl_to_port(upl_t)\n\t\tdestructor: upl_deallocate(upl_t)\n\t\t;\n\n#endif /* KERNEL_PRIVATE */\n\ntype mach_port_status_t = struct[10] of integer_t;\t/* obsolete */\ntype mach_port_info_ext_t = struct[17] of integer_t;\n\n\t\t/* mach_port_info_t: can hold either a\n\t\t * mach_port_status_t (9 ints) or a\n\t\t * mach_port_limits_t (1 int) or a \n\t\t * mach_port_info_ext_t (17 ints). If new flavors of\n\t\t * mach_port_{get,set}_attributes are added, the size of\n\t\t * this array may have to be increased. (See mach/port.h)\n\t\t */\ntype mach_port_flavor_t \t= int;\ntype mach_port_info_t\t\t= array[*:17] of integer_t;\n\n     \t\t/*\n\t\t * mach_msg_max_trailer_t: can hold\n\t\t * mach_msg_trailer_type_t (1 int)\n\t\t * mach_msg_trailer_size_t (1 int)\n\t\t * mach_port_seqno_t (1 int)\n\t\t * security_token_t (2 ints)\n\t\t * audit_token_t (8 ints)\n\t\t * mach_port_context_t (2 ints)\n\t\t * msgh_ad (1 int)\n\t\t * msg_labels_t (1 int)\n\t\t */\ntype mach_msg_trailer_type_t = int;\ntype mach_msg_trailer_info_t\t= array[*:68] of char;\n\ntype mach_task_flavor_t = int;\n\n/* Task control, read, inspect, name port. In descending capability. */\ntype task_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: task_t convert_port_to_task_mig(mach_port_t)\n\t\touttran: mach_port_t convert_task_to_port_kernel(task_t)\n\t\tdestructor: task_deallocate_mig(task_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype task_read_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: task_read_t convert_port_to_task_read_mig(mach_port_t)\n\t\touttran: mach_port_t convert_task_read_to_port_kernel(task_read_t)\n\t\tdestructor: task_read_deallocate_mig(task_read_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype task_inspect_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: task_inspect_t convert_port_to_task_inspect_mig(mach_port_t)\n\t\touttran: mach_port_t convert_task_inspect_to_port(task_inspect_t)\n\t\tdestructor: task_inspect_deallocate_mig(task_inspect_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype task_name_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: task_name_t convert_port_to_task_name_mig(mach_port_t)\n\t\touttran: mach_port_t convert_task_name_to_port(task_name_t)\n\t\tdestructor: task_name_deallocate_mig(task_name_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype task_policy_set_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: task_policy_set_t convert_port_to_task_policy_set_mig(mach_port_t)\n\t\tdestructor: task_policy_set_deallocate_mig(task_policy_set_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype task_policy_get_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: task_policy_get_t convert_port_to_task_policy_get_mig(mach_port_t)\n\t\tdestructor: task_policy_get_deallocate_mig(task_policy_get_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype task_id_token_t = mach_port_t\n#if KERNEL_SERVER\n\t\tintran: task_id_token_t convert_port_to_task_id_token(mach_port_t)\n\t\touttran: mach_port_t convert_task_id_token_to_port(task_id_token_t)\n\t\tdestructor: task_id_token_release(task_id_token_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\n/* Thread control, read, inspect port. In descending capability. */\ntype thread_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: thread_t convert_port_to_thread(mach_port_t)\n\t\touttran: mach_port_t convert_thread_to_port(thread_t)\n\t\tdestructor: thread_deallocate(thread_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype thread_read_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: thread_read_t convert_port_to_thread_read(mach_port_t)\n\t\touttran: mach_port_t convert_thread_read_to_port(thread_read_t)\n\t\tdestructor: thread_read_deallocate(thread_read_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype thread_inspect_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: thread_inspect_t convert_port_to_thread_inspect(mach_port_t)\n\t\touttran: mach_port_t convert_thread_inspect_to_port(thread_inspect_t)\n\t\tdestructor: thread_inspect_deallocate(thread_inspect_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype thread_act_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: thread_act_t convert_port_to_thread(mach_port_t)\n\t\touttran: mach_port_t convert_thread_to_port(thread_act_t)\n\t\tdestructor: thread_deallocate(thread_act_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype thread_act_consume_ref_t = mach_port_move_send_t\n\t\tcusertype: thread_act_t\n#if\tKERNEL_SERVER\n\t\tintran: thread_act_t convert_port_to_thread(mach_port_t)\n\t\tdestructor: thread_deallocate(thread_act_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\n\t\t/* thread_state_t: This inline array can hold\n\t\t * a machine-dependent amount of data, defined in\n\t\t * mach/machine/???? (currently THREAD_STATE_MAX,\n\t\t * in mach/thread_state.h)\n\t\t */\n#include <mach/machine/thread_state.h>\ntype thread_state_flavor_t\t= int;\ntype thread_state_t\t\t= array[*:THREAD_STATE_MAX] of natural_t;\n\ntype task_array_t = ^array[] of task_t;\ntype thread_array_t = ^array[] of thread_t;\ntype thread_act_array_t = ^array[] of thread_act_t;\ntype act_params_t = array[6] of int;\n\ntype vm_map_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: vm_map_t convert_port_to_map(mach_port_t)\n\t\tdestructor: vm_map_deallocate(vm_map_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype vm_map_inspect_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: vm_map_inspect_t convert_port_to_map_inspect(mach_port_t)\n\t\tdestructor: vm_map_inspect_deallocate(vm_map_inspect_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype vm_map_read_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: vm_map_read_t convert_port_to_map_read(mach_port_t)\n\t\tdestructor: vm_map_read_deallocate(vm_map_read_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype vm_task_entry_t = mach_port_t\n\t\tcusertype: vm_map_t\n#if\tKERNEL_SERVER\n\t\tintran: vm_map_t convert_port_entry_to_map(mach_port_t)\n\t\tdestructor: vm_map_deallocate(vm_map_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype ipc_space_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: ipc_space_t convert_port_to_space(mach_port_t)\n\t\tdestructor: space_deallocate(ipc_space_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype ipc_space_read_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: ipc_space_read_t convert_port_to_space_read(mach_port_t)\n\t\tdestructor: space_read_deallocate(ipc_space_read_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype ipc_space_inspect_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: ipc_space_inspect_t convert_port_to_space_inspect(mach_port_t)\n\t\tdestructor: space_inspect_deallocate(ipc_space_inspect_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype arcade_register_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: arcade_register_t convert_port_to_arcade_register(mach_port_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype kcdata_object_t = mach_port_t\n#if KERNEL_SERVER\n\t\tintran: kcdata_object_t convert_port_to_kcdata_object(mach_port_t)\n\t\touttran: mach_port_t convert_kcdata_object_to_port(kcdata_object_t)\n\t\tdestructor: kcdata_object_release(kcdata_object_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype vm_prot_t = int VM_UNSAFE_TYPE(vm_prot_ut);\ntype vm_inherit_t = int VM_UNSAFE_TYPE(vm_inherit_ut);\ntype vm_purgable_t = int;\ntype xxx_vm_statistics_data_t = struct[13] of integer_t;\ntype vm_behavior_t = int VM_UNSAFE_TYPE(vm_behavior_ut);\ntype vm_statistics_data_t = struct[15] of integer_t;\ntype vm_machine_attribute_t = int;\ntype vm_machine_attribute_val_t = int;\ntype vm_sync_t = int;\n\n\t\t/* thread_info_t: this inline array can hold any of:\n\t\t * thread_basic_info_t (10 ints)\n\t\t * policy_timeshare_info_t (5 ints)\n\t\t * policy_fifo_info_t (4 ints)\n\t\t * policy_rr_info_t (5 ints)\n\t\t * thread_extended_info (12 ints + 64 chars)\n\t\t * if other thread_info flavors are added, this\n\t\t * definition may need to be changed. (See\n\t\t * mach/thread_info.h and mach/policy.h) */\ntype thread_flavor_t\t\t= int;\ntype thread_info_t\t\t= array[*:32] of integer_t;\n\ntype thread_policy_flavor_t\t= natural_t;\ntype thread_policy_t\t\t= array[*:16] of integer_t;\n\n\t\t/* task_info_t: this inline array can hold any of:\n\t\t * task_basic_info_32_t (8 ints)\n\t\t * task_basic_info_64_t (10 ints)\n\t\t * task_events_info_t (8 ints)\n\t\t * task_thread_times_info_t (4 ints)\n\t\t * policy_timeshare_info_t (5 ints)\n\t\t * policy_fifo_info_t (4 ints)\n\t\t * policy_rr_info_t (5 ints)\n\t\t * task security token (2 ints)\n\t\t * task audit token (8 ints)\n\t\t * dyld info (2 64-bit ints and 1 int)\n\t\t * task_extmod_info_t (8 64-bit ints)\n\t\t * task_basic_info_64_2_t\n\t\t * mach_task_basic_info_t (12 ints)\n\t\t * task_power_info_t (18 ints)\n\t\t * task_vm_info_t (93 ints)\n\t\t * If other task_info flavors are added, this\n\t\t * definition may need to be changed. (See\n\t\t * mach/task_info.h and mach/policy.h)\n\t\t *\n\t\t * Add at least 1 extra element to allow task_info(TASK_VM_INFO)\n\t\t * to detect callers that may pass \"count\" as the number of\n\t\t * bytes instead of number of integer_t, for example.\n\t\t * The MIG user stub truncates that number to the maximum\n\t\t * number of elements in \"task_info_t\" which currently\n\t\t * happens to be TASK_VM_INFO_COUNT, making it impossible\n\t\t * for the kernel to detect the misuse of \"count\" and\n\t\t * possibly causing an overflow of the user's buffer.\n\t\t */\ntype task_flavor_t\t\t= int;\ntype task_info_t\t\t= array[*:93+1] of integer_t;\n\ntype task_purgable_info_t\t= struct[68] of integer_t;\n\ntype task_policy_flavor_t\t= natural_t;\ntype task_policy_t\t\t= array[*:16] of integer_t;\n\ntype task_inspect_flavor_t = natural_t;\ntype task_inspect_info_t = array[*:4] of integer_t;\n\ntype task_exc_guard_behavior_t = uint32_t;\ntype task_corpse_forking_behavior_t = uint32_t;\n\ntype mem_entry_name_port_t = mach_port_t\n#if     KERNEL_SERVER\n\t\tintran: mem_entry_name_port_t null_conversion(mach_port_t)\n\t\touttran: mach_port_t null_conversion(mem_entry_name_port_t)\n#endif  /* KERNEL_SERVER */\n\t\t;\n\ntype mem_entry_name_port_move_send_t = mach_port_move_send_t\n\tcusertype: mem_entry_name_port_t\n#if     KERNEL_SERVER\n\t\tintran: mem_entry_name_port_t null_conversion(mach_port_t)\n\t\touttran: mach_port_t null_conversion(mem_entry_name_port_t)\n#endif  /* KERNEL_SERVER */\n\t\t;\n\ntype memory_object_default_t = mach_port_t\n#if\tKERNEL_PRIVATE\n\t\tintran: memory_object_default_t null_conversion(mach_port_t)\n\t\touttran: mach_port_t null_conversion(memory_object_default_t)\n#endif\t/* KERNEL_PRIVATE */\n\t\t;\n \ntype memory_object_t = mach_port_t /* obsolete */\n#if\tKERNEL_PRIVATE\n\t\tintran: memory_object_t convert_port_to_memory_object(mach_port_t)\n\t\touttran: mach_port_t convert_memory_object_to_port(memory_object_t)\n#endif\t/* KERNEL_PRIVATE */\n\t\t;\n\ntype memory_object_control_t = mach_port_t; /* obsolete */\n\ntype memory_object_name_t = mach_port_t\n\t\tctype: mach_port_t\n\t\t;\n\n\ntype memory_object_copy_strategy_t = int;\ntype memory_object_return_t = int;\n\ntype machine_info_data_t = struct[5] of integer_t;\ntype machine_slot_data_t = struct[8] of integer_t;\n\ntype host_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: host_t convert_port_to_host(mach_port_t)\n\t\touttran: mach_port_t convert_host_to_port(host_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype host_priv_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: host_priv_t convert_port_to_host_priv(mach_port_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype host_security_t = mach_port_t; /* obsolete */\n\n\t\t/* \n\t\t * host_info_t: variable-sized inline array that can contain:\n\t\t * \n\t\t * \thost_basic_info_old_t (5 ints)\n\t\t * \thost_basic_info_t (12 ints)\n\t\t * \thost_sched_info_t (2 ints)\n\t\t * \tkernel_resource_sizes_t (5 ints)\n\t\t * \thost_load_info_t (6 ints)\n\t\t * \tvm_statistics32_t (15 ints)\n\t\t * \thost_purgable_info_t (68 ints)\n\t\t *\thost_expired_task_info uses a task_power_info (18 ints)\n\t\t * \n\t\t * If other host_info flavors are added, this definition may\n\t\t * need to be changed. (See mach/{host_info,vm_statistics}.h)\n\t\t */\ntype host_flavor_t\t\t= int;\ntype host_info_t \t\t= array[*:68] of integer_t;\n\t\t/* \n\t\t * host_info64_t: variable-sized inline array that can contain:\n\t\t * \n\t\t *\tvm_statistics_t (6 ints and 9 longs)\n\t\t *\tvm_extmod_statistics_t (6 64-bit ints)\n\t\t */\ntype host_info64_t\t\t= array[*:256] of integer_t;\n\ntype processor_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: processor_t convert_port_to_processor(mach_port_t)\n\t\touttran: mach_port_t convert_processor_to_port(processor_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype processor_array_t = ^array[] of processor_t;\n\n\t\t/*\n\t\t * processor_info_t: variable-sized inline array that can\n\t\t * contain:\n\t\t *\n\t\t * - processor_basic_info_t:    (5 ints)\n\t\t * - processor_cpu_load_info_t: (4 ints)\n\t\t * - processor_machine_info_t:  (12 ints)\n\t\t * - processor_cpu_stat_t:      (10 ints)\n\t\t * - processor_cpu_stat64_t:    (20 ints)\n\t\t *\n\t\t * If other processor_info flavors are added, this definition\n\t\t * may need to be changed.\n\t\t *\n\t\t * See mach/processor_info.h and mach/arm/processor_info.h.\n\t\t */\n\ntype processor_flavor_t     = int;\ntype processor_info_t       = array[*:20] of integer_t;\ntype processor_info_array_t = ^array[] of integer_t;\n\ntype processor_set_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: processor_set_t convert_port_to_pset(mach_port_t)\n\t\touttran: mach_port_t convert_pset_to_port(processor_set_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype processor_set_array_t = ^array[] of processor_set_t;\n\ntype processor_set_name_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: processor_set_name_t convert_port_to_pset_name(mach_port_t)\n\t\touttran: mach_port_t convert_pset_name_to_port(processor_set_name_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype processor_set_name_array_t = ^array[] of processor_set_name_t;\n\n\t\t/* processor_set_info_t: variable-size inline array\n\t\t * that can hold:\n\t\t * processor_set_basic_info (5 ints)\n\t\t * processor_set_load_info (4 ints)\n\t\t * policy_timeshare_base_t (1 int)\n\t\t * policy_fifo_base_t (1 int)\n\t\t * policy_rr_base_t (1 int)\n\t\t * policy_timeshare_base_t (1 int)\n\t\t * policy_fifo_base_t (1 int)\n\t\t * policy_rr_base_t (1 int)\n\t\t * policy_t (1 int)\n\t\t * If other flavors are added, this definition may\n\t\t * need to be changed. (see mach/processor.h) */\ntype processor_set_flavor_t\t= int;\ntype processor_set_info_t\t= array[*:5] of integer_t;\t\n\ntype bootstrap_t = mach_port_t;\n\ntype kernel_version_t           = c_string[*:512];\ntype kernel_boot_info_t         = c_string[*:4096];\n\ntype time_value_t = struct[2] of integer_t;\n\ntype mach_port_qos_t = struct[2] of integer_t;\n\ntype mach_port_options_t = struct[3] of uint64_t;\ntype mach_port_options_ptr_t = ^ mach_port_options_t;\n\ntype mach_service_port_info_data_t = struct[256] of char;\n\ntype emulation_vector_t\t\t= ^array[] of vm_offset_t;\n\ntype inline_existence_map_t\t= array[*:512] of char;\n\ntype policy_t\t\t\t= int;\n\t\t/* policy_info_t: variable-size inline array. Can hold:\n\t\t * policy_timeshare_info_t (5 ints)\n\t\t * policy_fifo_info_t (4 ints)\n\t\t * policy_rr_info_t (5 ints) */\ntype policy_base_t\t\t= array[*:5] of integer_t;\ntype policy_info_t\t\t= array[*:2] of integer_t;\ntype policy_limit_t\t\t= array[*:1] of integer_t;\n\ntype ledger_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: ledger_t convert_port_to_ledger(mach_port_t)\n\t\touttran: mach_port_t convert_ledger_to_port(ledger_t)\n#endif\t/* KERNEL_SERVER */\n                ;\n\ntype ledger_array_t\t \t= ^array[] of ledger_t;\ntype ledger_item_t\t\t= integer_t;\n     \t\t\t\t  /* DEPRECATED */\n\ntype ledger_amount_t\t\t= int64_t;\n\ntype security_token_t\t\t= struct[2] of uint32_t;\ntype audit_token_t\t\t= struct[8] of uint32_t;\n\ntype msg_labels_t = mach_port_t;\n\n\t\t/* memory_object_info_t: variable-size inline array:\n\t\t * memory_object_attr_info_t (5 ints)\n\t\t * XXX actually it's 6 ints temporarily (object_ready!)\n\t\t * memory_object_behave_info_t (4 ints)\n\t\t * memory_object_perf_info_t (2 ints)\n\t\t * old_memory_object_attr_info_t (3 ints)\n\t\t * If other flavors are added, this definition may\n\t\t * need to be changed. (see mach/memory_object.h) */\ntype memory_object_flavor_t\t= int;\ntype memory_object_info_t\t= array[*:6] of int;\n\n\t\t/* vm_region_info_t: variable-size inline array that can hold:\n\t\t * vm_region_basic_info_t (8 ints)\n\t\t * If other flavors are added, this definition may\n\t\t * need to be changed. (see mach/vm_region.h) */\ntype vm_region_flavor_t\t\t= int;\ntype vm_region_info_t\t\t= array[*:10] of int;\ntype vm_region_recurse_info_t\t= array[*:19] of int;\n\ntype vm_page_info_flavor_t\t= int;\ntype vm_page_info_t\t\t= array[*:32] of int;\n\ntype mach_vm_read_entry_t = array[512] of mach_vm_offset_t;\ntype vm_read_entry_t = array[512] of vm_offset_t;\n#ifdef VM32_SUPPORT\ntype vm32_read_entry_t = array[512] of vm32_offset_t;\n#endif\n\ntype mach_vm_range_flavor_t = uint32_t;\ntype mach_vm_range_recipes_raw_t = array[*:1024] of uint8_t;\n\ntype exception_mask_t\t\t= int;\ntype exception_behavior_t\t= int;\n\ntype    exception_handler_t = mach_port_t;\n\ntype    exception_handler_info_t = struct[2] of natural_t;\n\ntype\texception_handler_array_t\t=\n\t\t\tarray[*:32] of exception_handler_t;\n\ntype    exception_handler_info_array_t =\n\t\t\tarray[*:32] of exception_handler_info_t;\n\ntype\texception_behavior_array_t\t=\n\t\t\tarray[*:32] of exception_behavior_t;\n\ntype\texception_flavor_array_t\t=\n\t\t\tarray[*:32] of thread_state_flavor_t;\n\ntype\texception_mask_array_t\t=\n\t\t\tarray[*:32] of exception_mask_t;\n\ntype semaphore_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: semaphore_t convert_port_to_semaphore(mach_port_t)\n\t\touttran: mach_port_t convert_semaphore_to_port(semaphore_t)\n\t\tdestructor: semaphore_dereference(semaphore_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype semaphore_consume_ref_t = mach_port_move_send_t\n\t\tcusertype: semaphore_t\n#if\tKERNEL_SERVER\n\t\tintran: semaphore_t convert_port_to_semaphore(mach_port_t)\n\t\touttran: mach_port_t convert_semaphore_to_port(semaphore_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\n#ifndef _MACH_MACH_EVENTLINK_TYPE_DEFS\n#define _MACH_MACH_EVENTLINK_TYPE_DEFS\n\ntype eventlink_t = mach_port_t\n\t\tctype: mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: ipc_eventlink_t convert_port_to_eventlink(mach_port_t)\n\t\tdestructor: ipc_eventlink_deallocate(ipc_eventlink_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype eventlink_consume_ref_t = mach_port_move_send_t\n\t\tctype: mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: ipc_eventlink_t convert_port_to_eventlink(mach_port_t)\n\t\tdestructor: ipc_eventlink_deallocate(ipc_eventlink_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype eventlink_port_pair_t = array[2] of mach_port_t;\ntype mach_eventlink_create_option_t = uint32_t;\ntype mach_eventlink_associate_option_t = uint32_t;\ntype mach_eventlink_disassociate_option_t = uint32_t;\ntype mach_eventlink_signal_wait_option_t = uint32_t;\n\n#endif /* _MACH_MACH_EVENTLINK_TYPE_DEFS */\n\n/* obsolete */\ntype lock_set_t = mach_port_t;\n\ntype task_suspension_token_t = mach_port_move_send_once_t\n#if\tKERNEL_SERVER\n\t\tintran: task_suspension_token_t convert_port_to_task_suspension_token_mig(mach_port_t)\n\t\touttran: mach_port_t convert_task_suspension_token_to_port_mig(task_suspension_token_t)\n#endif\t/* KERNEL_SERVER */\n\t\t;\n\ntype vfs_path_t = c_string[4096];\n/*\n * 8K, c.f. MAXLONGPATHLEN in sys/syslimits.h.\n * These types should NEVER be allocated on the stack.\n */\ntype nspace_path_t = c_string[8192];\ntype nspace_name_t = c_string[8192];\n\n/* public voucher types */\n\n/* Mach voucher object */\ntype mach_voucher_t = mach_port_t;\ntype mach_voucher_name_t = mach_port_name_t;\n\ntype mach_voucher_attr_manager_t = mach_port_t;\ntype mach_voucher_attr_control_t = mach_port_t;\n\n/* IPC voucher internal object */\ntype ipc_voucher_t = mach_port_t\n#if\tKERNEL_SERVER\n\t\tintran: ipc_voucher_t convert_port_to_voucher(mach_port_t)\n\t\touttran: mach_port_t convert_voucher_to_port(ipc_voucher_t)\n\t\tdestructor: ipc_voucher_release(ipc_voucher_t)\n#endif\t/* KERNEL_SERVER */\n\t        ;\n\n/* IPC voucher attribute control internal object */\ntype ipc_voucher_attr_control_t = mach_port_t; /* obsolete */\n\ntype mach_voucher_attr_key_t = uint32_t;\n\ntype mach_voucher_attr_command_t = uint32_t;\ntype mach_voucher_attr_recipe_command_t = uint32_t;\n\ntype mach_voucher_attr_content_size_t = uint32_t;\ntype mach_voucher_attr_content_t = array[*:4096] of uint8_t;\ntype mach_voucher_attr_content_array_t = array[*:5120] of uint8_t;\n\ntype mach_voucher_attr_raw_recipe_size_t = uint32_t;\ntype mach_voucher_attr_raw_recipe_t = array[*:4096] of uint8_t;\ntype mach_voucher_attr_raw_recipe_array_t = array[*:5120] of uint8_t;\n\ntype mach_voucher_selector_t = uint32_t;\n\ntype mach_voucher_attr_value_handle_t = uint64_t;\ntype mach_voucher_attr_value_handle_array_t = array[*:4] of mach_voucher_attr_value_handle_t;\ntype mach_voucher_attr_value_reference_t = uint32_t;\n\n/* kernel module loader */\ntype kmod_t = int;\ntype kmod_control_flavor_t = int;\n\ntype kmod_args_t = ^array[] of MACH_MSG_TYPE_BYTE\n\tctype: kmod_args_t;\n\ntype io_main_t = mach_port_t;\ntype UNDServerRef = mach_port_t;\n\n/* These must be kept in sync with definitions in osfmk/mach/dyld_kernel.h */\ntype dyld_kernel_image_info_t = struct[40] of MACH_MSG_TYPE_BYTE;\ntype dyld_kernel_image_info_array_t = ^array[] of dyld_kernel_image_info_t;\ntype dyld_kernel_process_info_t = struct[64] of MACH_MSG_TYPE_BYTE;\n\ntype mach_vm_offset_list_t = array[*:512] of mach_vm_offset_t;\n\n#if KERNEL_SERVER\n#ifdef\tMACH_KERNEL_PRIVATE\nsimport <ipc/ipc_voucher.h>;\t/* for voucher conversions */\nsimport <kern/ipc_kobject.h>;\t/* for null conversion */\nsimport <kern/ipc_tt.h>;\t    /* for task/thread conversion */\nsimport <kern/ipc_host.h>;\t    /* for host/processor/pset conversions */\nsimport <kern/ledger.h>;\t    /* for ledger conversions */\nsimport <kern/processor.h>;\t    /* for processor conversions */\nsimport <kern/sync_sema.h>;\t    /* for semaphore conversions */\nsimport <ipc/ipc_eventlink.h>;  /* for eventlink conversions */\nsimport <vm/memory_object.h>;\t/* for memory object type conversions */\nsimport <vm/vm_map.h>;\t\t    /* for vm_map conversions */\n#if CONFIG_ARCADE\nsimport <kern/arcade.h>;        /* for arcade_register conversions */\n#endif\n#endif\t/* MACH_KERNEL_PRIVATE */\n\nsimport <kern/ipc_mig.h>;\t    /* pick up kernel-specific MIG things */\n\nsimport <kern/task_ident.h>;    /* for task_id_token conversions */\nsimport <kern/kern_cdata.h>;    /* for kcdata_object conversions */\n#endif /* KERNEL_SERVER */\n\nimport <mach/mig.h>;\nimport <mach/mach_types.h>;\n\n#endif\t/* _MACH_MACH_TYPES_DEFS_ */\n\n/* vim: set ft=c : */\n"
  },
  {
    "path": "third_party/xnu/osfmk/mach/machine/machine_types.defs",
    "content": "/*\n * Copyright (c) 2000-2007 Apple Computer, Inc. All rights reserved.\n *\n * @APPLE_OSREFERENCE_LICENSE_HEADER_START@\n * \n * This file contains Original Code and/or Modifications of Original Code\n * as defined in and that are subject to the Apple Public Source License\n * Version 2.0 (the 'License'). You may not use this file except in\n * compliance with the License. The rights granted to you under the License\n * may not be used to create, or enable the creation or redistribution of,\n * unlawful or unlicensed copies of an Apple operating system, or to\n * circumvent, violate, or enable the circumvention or violation of, any\n * terms of an Apple operating system software license agreement.\n * \n * Please obtain a copy of the License at\n * http://www.opensource.apple.com/apsl/ and read it before using this file.\n * \n * The Original Code and all software distributed under the License are\n * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER\n * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,\n * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.\n * Please see the License for the specific language governing rights and\n * limitations under the License.\n * \n * @APPLE_OSREFERENCE_LICENSE_HEADER_END@\n */\n/*\n * @OSF_COPYRIGHT@\n */\n\n/*\n *\tHeader file for basic, machine-dependent data types.  arm+i386 version.\n */\n \n#ifndef _MACH_MACHINE_MACHNINE_TYPES_DEFS\n#define _MACH_MACHINE_MACHNINE_TYPES_DEFS\n\ntype short = int16_t;\ntype int = int32_t;\ntype unsigned = uint32_t;\n\ntype float = MACH_MSG_TYPE_REAL_32;\ntype double = MACH_MSG_TYPE_REAL_64;\n\n#if VM_KERNEL_SERVER\n#define VM_ADD_CTYPE(type) ctype: type\n#define VM_TYPE_SAFE_UNSAFE(_safe_type, _unsafe_type) ctype: _unsafe_type\n#else /* VM_KERNEL_SERVER */\n#define VM_ADD_CTYPE(type)\n#define VM_TYPE_SAFE_UNSAFE(_safe_type, _unsafe_type) ctype: _safe_type\n#endif /* VM_KERNEL_SERVER */\n\n#define VM_UNSAFE_TYPE(_unsafe_type) VM_ADD_CTYPE(_unsafe_type)\n\n/* from ISO/IEC 988:1999 spec */\n/* 7.18.1.4 Integer types capable of hgolding object pointers */\n/*\n * The [u]intptr_t types for the native\n * integer type, e.g. 32 or 64 or.. whatever\n * register size the machine has.  They are\n * used for entities that might be either\n * [unsigned] integers or pointers, and for\n * type-casting between the two.\n *\n * For instance, the IPC system represents\n * a port in user space as an integer and\n * in kernel space as a pointer.\n */\n#if defined(__LP64__)\ntype uintptr_t = uint64_t;\ntype intptr_t = int64_t;\n#else\ntype uintptr_t = uint32_t;\ntype intptr_t = int32_t;\n#endif\n\n/*\n * These are the legacy Mach types that are\n * the [rough] equivalents of the standards above.\n * They were defined in terms of int, not\n * long int, so they remain separate.\n */\n#if defined(__LP64__)\ntype register_t = int64_t;\n#else\ntype register_t = int32_t;\n#endif\ntype integer_t = int32_t;\ntype natural_t = uint32_t;\n\n/*\n * These are the VM types that scale with the address\n * space size of a given process.\n */\n\n#if defined(__LP64__)\ntype vm_address_t = uint64_t VM_UNSAFE_TYPE(vm_address_ut);\ntype vm_offset_t = uint64_t VM_UNSAFE_TYPE(vm_offset_ut);\ntype vm_size_t = uint64_t VM_UNSAFE_TYPE(vm_size_ut);\n#else\ntype vm_address_t = natural_t VM_UNSAFE_TYPE(vm_address_ut);\ntype vm_offset_t = natural_t VM_UNSAFE_TYPE(vm_offset_ut);\ntype vm_size_t = natural_t VM_UNSAFE_TYPE(vm_size_ut);\n#endif\n\n/* This is a bit of a hack for arm.  We implement the backend with a wide type, but present a native-sized type to callers */\ntype mach_port_context_t = uint64_t;\n\n/*\n * The mach_vm_xxx_t types are sized to hold the\n * maximum pointer, offset, etc... supported on the\n * platform.\n */\ntype mach_vm_address_t = uint64_t VM_UNSAFE_TYPE(mach_vm_address_ut);\ntype mach_vm_offset_t = uint64_t VM_UNSAFE_TYPE(mach_vm_offset_ut);\ntype mach_vm_size_t = uint64_t VM_UNSAFE_TYPE(mach_vm_size_ut);\n\n/*\n * These are types used internal to Mach to implement the\n * legacy 32-bit VM APIs published by the kernel.\n */\n#define\tVM32_SUPPORT\t1\n\ntype vm32_address_t = uint32_t VM_UNSAFE_TYPE(vm32_address_ut);\ntype vm32_offset_t = uint32_t VM_UNSAFE_TYPE(vm32_offset_ut);\ntype vm32_size_t = uint32_t VM_UNSAFE_TYPE(vm32_size_ut);\n\n#endif /* _MACH_MACHINE_MACHNINE_TYPES_DEFS */\n\n/* vim: set ft=c : */\n"
  },
  {
    "path": "third_party/xnu/osfmk/mach/std_types.defs",
    "content": "/*\n * Copyright (c) 2002,2000 Apple Computer, Inc. All rights reserved.\n *\n * @APPLE_OSREFERENCE_LICENSE_HEADER_START@\n * \n * This file contains Original Code and/or Modifications of Original Code\n * as defined in and that are subject to the Apple Public Source License\n * Version 2.0 (the 'License'). You may not use this file except in\n * compliance with the License. The rights granted to you under the License\n * may not be used to create, or enable the creation or redistribution of,\n * unlawful or unlicensed copies of an Apple operating system, or to\n * circumvent, violate, or enable the circumvention or violation of, any\n * terms of an Apple operating system software license agreement.\n * \n * Please obtain a copy of the License at\n * http://www.opensource.apple.com/apsl/ and read it before using this file.\n * \n * The Original Code and all software distributed under the License are\n * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER\n * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,\n * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.\n * Please see the License for the specific language governing rights and\n * limitations under the License.\n * \n * @APPLE_OSREFERENCE_LICENSE_HEADER_END@\n */\n/*\n * @OSF_COPYRIGHT@\n */\n/* \n * Mach Operating System\n * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University\n * All Rights Reserved.\n * \n * Permission to use, copy, modify and distribute this software and its\n * documentation is hereby granted, provided that both the copyright\n * notice and this permission notice appear in all copies of the\n * software, derivative works or modified versions, and any portions\n * thereof, and that both notices appear in supporting documentation.\n * \n * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS \"AS IS\"\n * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR\n * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.\n * \n * Carnegie Mellon requests users of this software to return to\n * \n *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU\n *  School of Computer Science\n *  Carnegie Mellon University\n *  Pittsburgh PA 15213-3890\n * \n * any improvements or extensions that they make and grant Carnegie Mellon\n * the rights to redistribute these changes.\n */\n/*\n */\n/*\n *\tMach kernel standard interface type declarations\n */\n\n#ifndef\t_MACH_STD_TYPES_DEFS_\n#define _MACH_STD_TYPES_DEFS_\n\nimport <Availability.h>;\n\n/* from ISO/IEC 988:1999 spec */\n/* 7.18.1.1 Exact-width integer types */\n\ntype int8_t = MACH_MSG_TYPE_INTEGER_8;\ntype uint8_t = MACH_MSG_TYPE_INTEGER_8;\ntype int16_t = MACH_MSG_TYPE_INTEGER_16;\ntype uint16_t = MACH_MSG_TYPE_INTEGER_16;\ntype int32_t = MACH_MSG_TYPE_INTEGER_32;\ntype uint32_t = MACH_MSG_TYPE_INTEGER_32;\ntype int64_t = MACH_MSG_TYPE_INTEGER_64;\ntype uint64_t = MACH_MSG_TYPE_INTEGER_64;\n\n/*\n * Legacy fixed-length Mach types which should\n * be replaced with the Standard types from above.\n */\ntype int32 = int32_t;\ntype unsigned32 = uint32_t;\ntype int64 = int64_t;\ntype unsigned64 = uint64_t;\n\n/*\n * Other fixed length Mach types.\n */\ntype char = MACH_MSG_TYPE_CHAR;\ntype boolean_t = MACH_MSG_TYPE_BOOLEAN;\n\n#include <mach/machine/machine_types.defs>\n\ntype kern_return_t = int;\n\ntype pointer_t = ^array[] of MACH_MSG_TYPE_BYTE\n\tVM_TYPE_SAFE_UNSAFE(vm_offset_t, pointer_ut);\n\ntype mach_port_t = MACH_MSG_TYPE_COPY_SEND;\ntype mach_port_array_t = array[] of mach_port_t;\n\ntype mach_port_name_t = MACH_MSG_TYPE_PORT_NAME;\ntype mach_port_name_array_t = array[] of mach_port_name_t;\n\ntype mach_port_right_t = natural_t;\n\ntype mach_port_type_t = natural_t;\ntype mach_port_type_array_t = array[] of mach_port_type_t;\n\ntype mach_port_urefs_t = natural_t;\ntype mach_port_delta_t = integer_t;\ntype mach_port_seqno_t = natural_t;\ntype mach_port_mscount_t = unsigned;\ntype mach_port_msgcount_t = unsigned;\ntype mach_port_rights_t = unsigned;\ntype mach_msg_id_t = integer_t;\ntype mach_msg_size_t = natural_t;\ntype mach_msg_type_name_t = unsigned;\ntype mach_msg_options_t = integer_t;\n\ntype mach_port_move_receive_t =\t\tMACH_MSG_TYPE_MOVE_RECEIVE\n\tctype: mach_port_t;\ntype mach_port_copy_send_t =\t\tMACH_MSG_TYPE_COPY_SEND\n\tctype: mach_port_t;\ntype mach_port_make_send_t =\t\tMACH_MSG_TYPE_MAKE_SEND\n\tctype: mach_port_t;\ntype mach_port_move_send_t =\t\tMACH_MSG_TYPE_MOVE_SEND\n\tctype: mach_port_t;\ntype mach_port_make_send_once_t =\tMACH_MSG_TYPE_MAKE_SEND_ONCE\n\tctype: mach_port_t;\ntype mach_port_move_send_once_t =\tMACH_MSG_TYPE_MOVE_SEND_ONCE\n\tctype: mach_port_t;\n\ntype mach_port_receive_t =\t\tMACH_MSG_TYPE_PORT_RECEIVE\n\tctype: mach_port_t;\ntype mach_port_send_t =\t\t\tMACH_MSG_TYPE_PORT_SEND\n\tctype: mach_port_t;\ntype mach_port_send_once_t =\t\tMACH_MSG_TYPE_PORT_SEND_ONCE\n\tctype: mach_port_t;\n\ntype mach_port_poly_t = polymorphic\n\tctype: mach_port_t;\n\nimport <mach/std_types.h>;\nimport <mach/mig.h>;\n\n#endif\t/* _MACH_STD_TYPES_DEFS_ */\n\n/* vim: set ft=c : */\n"
  },
  {
    "path": "third_party/zlib/BUILD.gn",
    "content": "# Copyright 2017 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../build/crashpad_buildconfig.gni\")\n\nif (crashpad_is_in_chromium || crashpad_is_in_fuchsia || crashpad_is_in_dart) {\n  zlib_source = \"external\"\n} else if (!crashpad_is_win && !crashpad_is_fuchsia) {\n  zlib_source = \"system\"\n} else if (crashpad_is_standalone) {\n  zlib_source = \"embedded\"\n} else if (crashpad_is_external) {\n  zlib_source = \"external_with_embedded_build\"\n}\n\nconfig(\"zlib_config\") {\n  if (zlib_source == \"external\") {\n    defines = [ \"CRASHPAD_ZLIB_SOURCE_EXTERNAL\" ]\n  } else if (zlib_source == \"system\") {\n    defines = [ \"CRASHPAD_ZLIB_SOURCE_SYSTEM\" ]\n  } else if (zlib_source == \"embedded\") {\n    defines = [ \"CRASHPAD_ZLIB_SOURCE_EMBEDDED\" ]\n    include_dirs = [ \"zlib\" ]\n  } else if (zlib_source == \"external_with_embedded_build\") {\n    defines = [ \"CRASHPAD_ZLIB_SOURCE_EXTERNAL_WITH_EMBEDDED_BUILD\" ]\n    include_dirs = [ \"../../../../zlib/src\" ]\n  }\n}\n\nconfig(\"Wno-sign-compare\") {\n  cflags = [ \"-Wno-sign-compare\" ]\n}\n\nif (zlib_source == \"external\") {\n  group(\"zlib\") {\n    public_configs = [ \":zlib_config\" ]\n    public_deps = [ \"//third_party/zlib\" ]\n  }\n} else if (zlib_source == \"system\") {\n  source_set(\"zlib\") {\n    public_configs = [ \":zlib_config\" ]\n    libs = [ \"z\" ]\n  }\n} else if (zlib_source == \"embedded\" ||\n           zlib_source == \"external_with_embedded_build\") {\n  static_library(\"zlib\") {\n    if (zlib_source == \"embedded\") {\n      zlib_dir = \"zlib\"\n    } else if (zlib_source == \"external_with_embedded_build\") {\n      zlib_dir = \"../../../../zlib/src\"\n    }\n    sources = [\n      \"$zlib_dir/adler32.c\",\n      \"$zlib_dir/compress.c\",\n      \"$zlib_dir/crc32.c\",\n      \"$zlib_dir/crc32.h\",\n      \"$zlib_dir/deflate.c\",\n      \"$zlib_dir/deflate.h\",\n      \"$zlib_dir/gzclose.c\",\n      \"$zlib_dir/gzguts.h\",\n      \"$zlib_dir/gzlib.c\",\n      \"$zlib_dir/gzread.c\",\n      \"$zlib_dir/gzwrite.c\",\n      \"$zlib_dir/infback.c\",\n      \"$zlib_dir/inffast.c\",\n      \"$zlib_dir/inffast.h\",\n      \"$zlib_dir/inffixed.h\",\n      \"$zlib_dir/inflate.c\",\n      \"$zlib_dir/inflate.h\",\n      \"$zlib_dir/inftrees.c\",\n      \"$zlib_dir/inftrees.h\",\n      \"$zlib_dir/trees.c\",\n      \"$zlib_dir/trees.h\",\n      \"$zlib_dir/uncompr.c\",\n      \"$zlib_dir/zconf.h\",\n      \"$zlib_dir/zlib.h\",\n      \"$zlib_dir/zutil.c\",\n      \"$zlib_dir/zutil.h\",\n      \"zlib_crashpad.h\",\n    ]\n\n    cflags = []\n    defines = [ \"HAVE_STDARG_H\" ]\n    public_configs = [ \":zlib_config\" ]\n\n    if (crashpad_is_win) {\n      cflags += [\n        \"/wd4131\",  # uses old-style declarator\n        \"/wd4244\",  # conversion from 't1' to 't2', possible loss of data\n        \"/wd4245\",  # conversion from 't1' to 't2', signed/unsigned mismatch\n        \"/wd4267\",  # conversion from 'size_t' to 't', possible loss of data\n        \"/wd4324\",  # structure was padded due to alignment specifier\n        \"/wd4702\",  # unreachable code\n      ]\n    } else {\n      defines += [\n        \"HAVE_HIDDEN\",\n        \"HAVE_UNISTD_H\",\n      ]\n    }\n\n    if (crashpad_is_fuchsia) {\n      # Fuchsia build's default warnings include -Wsign-compare (indirectly)\n      configs += [ \":Wno-sign-compare\" ]\n    }\n\n    if (crashpad_is_standalone) {\n      configs -= [ \"//third_party/mini_chromium/mini_chromium/build/config:Wimplicit_fallthrough\" ]\n    } else if (crashpad_is_external) {\n      configs -= [ \"//../../mini_chromium/mini_chromium/build/config:Wimplicit_fallthrough\" ]\n    }\n\n    if (zlib_source == \"embedded\") {\n      sources += [ \"$zlib_dir/chromeconf.h\" ]\n\n      if (current_cpu == \"x86\" || current_cpu == \"x64\") {\n        sources += [\n          \"$zlib_dir/crc_folding.c\",\n        ]\n        if (!crashpad_is_win || crashpad_is_clang) {\n          cflags += [\n            \"-msse4.2\",\n            \"-mpclmul\",\n          ]\n        }\n        if (crashpad_is_clang) {\n          cflags += [ \"-Wno-incompatible-pointer-types\" ]\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "third_party/zlib/README.crashpad",
    "content": "Name: zlib\nShort Name: zlib\nURL: https://zlib.net/\nRevision: See zlib/README.chromium\nLicense: zlib\nLicense File: zlib/LICENSE\nSecurity Critical: yes\nShipped: yes\n\nDescription:\n“A massively spiffy yet delicately unobtrusive compression library.”\n\nzlib is a free, general-purpose, legally unencumbered lossless data-compression\nlibrary. zlib implements the “deflate” compression algorithm described by RFC\n1951, which combines the LZ77 (Lempel-Ziv) algorithm with Huffman coding. zlib\nalso implements the zlib (RFC 1950) and gzip (RFC 1952) wrapper formats.\n\nLocal Modifications:\nSee zlib/README.chromium.\n"
  },
  {
    "path": "third_party/zlib/zlib_crashpad.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_THIRD_PARTY_ZLIB_ZLIB_CRASHPAD_H_\n#define CRASHPAD_THIRD_PARTY_ZLIB_ZLIB_CRASHPAD_H_\n\n// #include this file instead of the system version of <zlib.h> or equivalent\n// available at any other location in the source tree. It will #include the\n// proper <zlib.h> depending on how the build has been configured.\n\n#if defined(CRASHPAD_ZLIB_SOURCE_SYSTEM) ||   \\\n    defined(CRASHPAD_ZLIB_SOURCE_EXTERNAL) || \\\n    defined(CRASHPAD_ZLIB_SOURCE_EXTERNAL_WITH_EMBEDDED_BUILD)\n#include <zlib.h>\n#elif defined(CRASHPAD_ZLIB_SOURCE_EMBEDDED)\n#include \"third_party/zlib/zlib/zlib.h\"\n#else\n#error Unknown zlib source\n#endif\n\n#endif  // CRASHPAD_THIRD_PARTY_ZLIB_ZLIB_CRASHPAD_H_\n"
  },
  {
    "path": "tools/BUILD.gn",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../build/crashpad_buildconfig.gni\")\n\nsource_set(\"tool_support\") {\n  sources = [\n    \"tool_support.cc\",\n    \"tool_support.h\",\n  ]\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  deps = [ \"$mini_chromium_source_parent:base\" ]\n}\n\ncrashpad_executable(\"dump_minidump_annotations\") {\n  sources = [ \"dump_minidump_annotations.cc\" ]\n\n  deps = [\n    \":tool_support\",\n    \"../client\",\n    \"../snapshot\",\n    \"../util\",\n  ]\n\n  if (crashpad_is_win) {\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n  }\n}\n\nif (!crashpad_is_ios && !crashpad_is_fuchsia) {\n  crashpad_executable(\"crashpad_database_util\") {\n    sources = [ \"crashpad_database_util.cc\" ]\n\n    deps = [\n      \":tool_support\",\n      \"$mini_chromium_source_parent:base\",\n      \"../build:default_exe_manifest_win\",\n      \"../client\",\n      \"../compat\",\n      \"../util\",\n    ]\n  }\n\n  crashpad_executable(\"crashpad_http_upload\") {\n    sources = [ \"crashpad_http_upload.cc\" ]\n\n    deps = [\n      \":tool_support\",\n      \"$mini_chromium_source_parent:base\",\n      \"../build:default_exe_manifest_win\",\n      \"../compat\",\n      \"../util\",\n      \"../util:net\",\n    ]\n  }\n}\n\ncrashpad_executable(\"base94_encoder\") {\n  sources = [ \"base94_encoder.cc\" ]\n  deps = [\n    \":tool_support\",\n    \"$mini_chromium_source_parent:base\",\n    \"../build:default_exe_manifest_win\",\n    \"../util\",\n  ]\n}\n\nif (!crashpad_is_fuchsia && !crashpad_is_ios) {\n  crashpad_executable(\"generate_dump\") {\n    sources = [ \"generate_dump.cc\" ]\n\n    deps = [\n      \":tool_support\",\n      \"$mini_chromium_source_parent:base\",\n      \"../build:default_exe_manifest_win\",\n      \"../compat\",\n      \"../minidump\",\n      \"../snapshot\",\n      \"../util\",\n    ]\n\n    if (crashpad_is_mac) {\n      # This would be better as a config so that it could be shared with\n      # exception_port_tool, but configs can’t alter “inputs”.\n      # https://crbug.com/781858.\n      inputs = [ \"mac/sectaskaccess_info.plist\" ]\n      ldflags = [\n        \"-sectcreate\",\n        \"__TEXT\",\n        \"__info_plist\",\n        rebase_path(inputs[0], root_build_dir),\n      ]\n    }\n\n    if (crashpad_is_win) {\n      cflags =\n          [ \"/wd4201\" ]  # nonstandard extension used : nameless struct/union\n    }\n  }\n}\n\nif (crashpad_is_mac || crashpad_is_fuchsia) {\n  crashpad_executable(\"run_with_crashpad\") {\n    sources = [ \"run_with_crashpad.cc\" ]\n\n    deps = [\n      \":tool_support\",\n      \"$mini_chromium_source_parent:base\",\n      \"../client\",\n      \"../compat\",\n      \"../util\",\n    ]\n  }\n}\n\nif (crashpad_is_mac) {\n  crashpad_executable(\"catch_exception_tool\") {\n    sources = [ \"mac/catch_exception_tool.cc\" ]\n\n    deps = [\n      \":tool_support\",\n      \"$mini_chromium_source_parent:base\",\n      \"../compat\",\n      \"../util\",\n    ]\n  }\n\n  crashpad_executable(\"exception_port_tool\") {\n    sources = [ \"mac/exception_port_tool.cc\" ]\n\n    # This would be better as a config so that it could be shared with\n    # generate_dump, but configs can’t alter “inputs”. https://crbug.com/781858.\n    inputs = [ \"mac/sectaskaccess_info.plist\" ]\n    ldflags = [\n      \"-sectcreate\",\n      \"__TEXT\",\n      \"__info_plist\",\n      rebase_path(inputs[0], root_build_dir),\n    ]\n\n    deps = [\n      \":tool_support\",\n      \"$mini_chromium_source_parent:base\",\n      \"../compat\",\n      \"../util\",\n    ]\n  }\n\n  crashpad_executable(\"on_demand_service_tool\") {\n    sources = [ \"mac/on_demand_service_tool.mm\" ]\n\n    frameworks = [\n      \"CoreFoundation.framework\",\n      \"Foundation.framework\",\n    ]\n\n    deps = [\n      \":tool_support\",\n      \"$mini_chromium_source_parent:base\",\n      \"../build:apple_enable_arc\",\n      \"../compat\",\n      \"../util\",\n    ]\n  }\n}\n"
  },
  {
    "path": "tools/base94_encoder.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <getopt.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"tools/tool_support.h\"\n#include \"util/stream/file_encoder.h\"\n\nnamespace crashpad {\nnamespace {\n\nvoid Usage(const base::FilePath& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %\" PRFilePath \" [options] <input-file> <output-file>\\n\"\n\"Encode/Decode the given file\\n\"\n\"\\n\"\n\"  -e, --encode   compress and encode the input file to a base94 encoded\"\n                  \" file\\n\"\n\"  -d, --decode   decode and decompress a base94 encoded file\\n\"\n\"      --help     display this help and exit\\n\"\n\"      --version  output version information and exit\\n\",\n          me.value().c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nint Base94EncoderMain(int argc, char* argv[]) {\n  const base::FilePath argv0(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));\n  const base::FilePath me(argv0.BaseName());\n\n  enum OptionFlags {\n    // “Short” (single-character) options.\n    kOptionEncode = 'e',\n    kOptionDecode = 'd',\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  struct Options {\n    bool encoding;\n    base::FilePath input_file;\n    base::FilePath output_file;\n  } options = {};\n\n  static constexpr option long_options[] = {\n      {\"encode\", no_argument, nullptr, kOptionEncode},\n      {\"decode\", no_argument, nullptr, kOptionDecode},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  bool encoding_valid = false;\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"de\", long_options, nullptr)) != -1) {\n    switch (opt) {\n      case kOptionEncode:\n        options.encoding = true;\n        encoding_valid = true;\n        break;\n      case kOptionDecode:\n        options.encoding = false;\n        encoding_valid = true;\n        break;\n      case kOptionHelp:\n        Usage(me);\n        return EXIT_SUCCESS;\n      case kOptionVersion:\n        ToolSupport::Version(me);\n        return EXIT_SUCCESS;\n      default:\n        ToolSupport::UsageHint(me, nullptr);\n        return EXIT_FAILURE;\n    }\n  }\n\n  if (!encoding_valid) {\n    ToolSupport::UsageHint(me, \"Either -e or -d required\");\n    return EXIT_FAILURE;\n  }\n\n  argc -= optind;\n  argv += optind;\n  if (argc != 2) {\n    ToolSupport::UsageHint(me, \"Both input-file and output-file required\");\n    return EXIT_FAILURE;\n  }\n\n  options.input_file = base::FilePath(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));\n  options.output_file = base::FilePath(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[1]));\n\n  FileEncoder encoder(options.encoding ? crashpad::FileEncoder::Mode::kEncode\n                                       : crashpad::FileEncoder::Mode::kDecode,\n                      options.input_file,\n                      options.output_file);\n  return encoder.Process() ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n\n}  // namespace\n}  // namespace crashpad\n\n#if BUILDFLAG(IS_POSIX)\nint main(int argc, char* argv[]) {\n  return crashpad::Base94EncoderMain(argc, argv);\n}\n#elif BUILDFLAG(IS_WIN)\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::ToolSupport::Wmain(argc, argv, crashpad::Base94EncoderMain);\n}\n#endif  // BUILDFLAG(IS_POSIX)\n"
  },
  {
    "path": "tools/base94_encoder.md",
    "content": "<!--\nCopyright 2020 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# base94_encoder(1)\n\n## Name\n\nbase94_encoder—Encode/Decode the given file\n\n## Synopsis\n\n**base94_encoder** [_OPTION…_] input-file output-file\n\n## Description\n\nEncodes a file for printing safely by compressing and base94 encoding it.\n\nThe base94_encoder can decode the input file by base94 decoding and\nuncompressing it.\n\n## Options\n\n * **-e**, **--encode**\n\n   Compress and encode the input file to a base94 encoded file.\n\n * **-d**, **--decode**\n\n   Decode and decompress a base94 encoded file.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Examples\n\nEncode file a to b:\n\n```\n$ base94_encoder --encode a b\n```\n\nDecode file b to a\n\n```\n$ base94_encoder --decode b a\n```\n\n## Exit Status\n\n * **0**\n\n   Success.\n\n * **1**\n\n   Failure, with a message printed to the standard error stream.\n\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2020 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "tools/crashpad_database_util.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <errno.h>\n#include <getopt.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n#include <sys/types.h>\n#include <time.h>\n\n#include <iterator>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"base/check_op.h\"\n#include \"base/files/file_path.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"client/crash_report_database.h\"\n#include \"client/settings.h\"\n#include \"tools/tool_support.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n\nnamespace crashpad {\nnamespace {\n\nvoid Usage(const base::FilePath& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %\" PRFilePath \" [OPTION]... PID\\n\"\n\"Operate on Crashpad crash report databases.\\n\"\n\"\\n\"\n\"      --create                    allow database at PATH to be created\\n\"\n\"  -d, --database=PATH             operate on the crash report database at PATH\\n\"\n\"      --show-client-id            show the client ID\\n\"\n\"      --show-uploads-enabled      show whether uploads are enabled\\n\"\n\"      --show-last-upload-attempt-time\\n\"\n\"                                  show the last-upload-attempt time\\n\"\n\"      --show-pending-reports      show reports eligible for upload\\n\"\n\"      --show-completed-reports    show reports not eligible for upload\\n\"\n\"      --show-all-report-info      with --show-*-reports, show more information\\n\"\n\"      --show-report=UUID          show report stored under UUID\\n\"\n\"      --set-uploads-enabled=BOOL  enable or disable uploads\\n\"\n\"      --set-last-upload-attempt-time=TIME\\n\"\n\"                                  set the last-upload-attempt time to TIME\\n\"\n\"      --new-report=PATH           submit a new report at PATH, or - for stdin\\n\"\n\"      --utc                       show and set UTC times instead of local\\n\"\n\"      --help                      display this help and exit\\n\"\n\"      --version                   output version information and exit\\n\",\n          me.value().c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nstruct Options {\n  std::vector<UUID> show_reports;\n  std::vector<base::FilePath> new_report_paths;\n  const char* database;\n  const char* set_last_upload_attempt_time_string;\n  time_t set_last_upload_attempt_time;\n  bool create;\n  bool show_client_id;\n  bool show_uploads_enabled;\n  bool show_last_upload_attempt_time;\n  bool show_pending_reports;\n  bool show_completed_reports;\n  bool show_all_report_info;\n  bool set_uploads_enabled;\n  bool has_set_uploads_enabled;\n  bool utc;\n};\n\n// Converts |string| to |boolean|, returning true if a conversion could be\n// performed, and false without setting |boolean| if no conversion could be\n// performed. Various string representations of a boolean are recognized\n// case-insensitively.\nbool StringToBool(const char* string, bool* boolean) {\n  static constexpr const char* kFalseWords[] = {\n      \"0\",\n      \"false\",\n      \"no\",\n      \"off\",\n      \"disabled\",\n      \"clear\",\n  };\n  static constexpr const char* kTrueWords[] = {\n      \"1\",\n      \"true\",\n      \"yes\",\n      \"on\",\n      \"enabled\",\n      \"set\",\n  };\n\n  for (size_t index = 0; index < std::size(kFalseWords); ++index) {\n    if (strcasecmp(string, kFalseWords[index]) == 0) {\n      *boolean = false;\n      return true;\n    }\n  }\n\n  for (size_t index = 0; index < std::size(kTrueWords); ++index) {\n    if (strcasecmp(string, kTrueWords[index]) == 0) {\n      *boolean = true;\n      return true;\n    }\n  }\n\n  return false;\n}\n\n// Converts |boolean| to a string, either \"true\" or \"false\".\nstd::string BoolToString(bool boolean) {\n  return std::string(boolean ? \"true\" : \"false\");\n}\n\n// Converts |string| to |out_time|, returning true if a conversion could be\n// performed, and false without setting |boolean| if no conversion could be\n// performed. Various time formats are recognized, including several string\n// representations and a numeric time_t representation. The special |string|\n// \"never\" is recognized as converted to a |out_time| value of 0; \"now\" is\n// converted to the current time. |utc|, when true, causes |string| to be\n// interpreted as a UTC time rather than a local time when the time zone is\n// ambiguous.\nbool StringToTime(const char* string, time_t* out_time, bool utc) {\n  if (strcasecmp(string, \"never\") == 0) {\n    *out_time = 0;\n    return true;\n  }\n\n  if (strcasecmp(string, \"now\") == 0) {\n    errno = 0;\n    PCHECK(time(out_time) != -1 || errno == 0);\n    return true;\n  }\n\n  const char* end = string + strlen(string);\n\n  static constexpr const char* kFormats[] = {\n      \"%Y-%m-%d %H:%M:%S %Z\",\n      \"%Y-%m-%d %H:%M:%S\",\n      \"%+\",\n  };\n\n  for (size_t index = 0; index < std::size(kFormats); ++index) {\n    tm time_tm;\n    const char* strptime_result = strptime(string, kFormats[index], &time_tm);\n    if (strptime_result == end) {\n      time_t test_out_time;\n      if (utc) {\n        test_out_time = timegm(&time_tm);\n      } else {\n        test_out_time = mktime(&time_tm);\n      }\n\n      // mktime() is supposed to set errno in the event of an error, but support\n      // for this is spotty, so there’s no way to distinguish between a true\n      // time_t of -1 (1969-12-31 23:59:59 UTC) and an error. Assume error.\n      //\n      // See 10.11.5 Libc-1082.50.1/stdtime/FreeBSD/localtime.c and\n      // glibc-2.24/time/mktime.c, which don’t set errno or save and restore\n      // errno. Post-Android 7.1.0 Bionic is even more hopeless, setting errno\n      // whenever the time conversion returns -1, even for valid input. See\n      // libc/tzcode/localtime.c mktime(). Windows seems to get it right: see\n      // 10.0.14393 SDK Source/ucrt/time/mktime.cpp.\n      if (test_out_time != -1) {\n        *out_time = test_out_time;\n        return true;\n      }\n    }\n  }\n\n  int64_t int64_result;\n  if (StringToNumber(string, &int64_result) &&\n      base::IsValueInRangeForNumericType<time_t>(int64_result)) {\n    *out_time = int64_result;\n    return true;\n  }\n\n  return false;\n}\n\n// Converts |out_time| to a string, and returns it. |utc| determines whether the\n// converted time will reference local time or UTC. If |out_time| is 0, the\n// string \"never\" will be returned as a special case.\nstd::string TimeToString(time_t out_time, bool utc) {\n  if (out_time == 0) {\n    return std::string(\"never\");\n  }\n\n  tm time_tm;\n  if (utc) {\n    PCHECK(gmtime_r(&out_time, &time_tm));\n  } else {\n    PCHECK(localtime_r(&out_time, &time_tm));\n  }\n\n  char string[64];\n  CHECK_NE(\n      strftime(string, std::size(string), \"%Y-%m-%d %H:%M:%S %Z\", &time_tm),\n      0u);\n\n  return std::string(string);\n}\n\n// Shows information about a single |report|. |space_count| is the number of\n// spaces to print before each line that is printed. |utc| determines whether\n// times should be shown in UTC or the local time zone.\nvoid ShowReport(const CrashReportDatabase::Report& report,\n                size_t space_count,\n                bool utc) {\n  std::string spaces(space_count, ' ');\n\n  printf(\"%sPath: %\" PRFilePath \"\\n\",\n         spaces.c_str(),\n         report.file_path.value().c_str());\n  if (!report.id.empty()) {\n    printf(\"%sRemote ID: %s\\n\", spaces.c_str(), report.id.c_str());\n  }\n  printf(\"%sCreation time: %s\\n\",\n         spaces.c_str(),\n         TimeToString(report.creation_time, utc).c_str());\n  printf(\"%sUploaded: %s\\n\",\n         spaces.c_str(),\n         BoolToString(report.uploaded).c_str());\n  printf(\"%sLast upload attempt time: %s\\n\",\n         spaces.c_str(),\n         TimeToString(report.last_upload_attempt_time, utc).c_str());\n  printf(\"%sUpload attempts: %d\\n\", spaces.c_str(), report.upload_attempts);\n}\n\n// Shows information about a vector of |reports|. |space_count| is the number of\n// spaces to print before each line that is printed. |options| will be consulted\n// to determine whether to show expanded information\n// (options.show_all_report_info) and what time zone to use when showing\n// expanded information (options.utc).\nvoid ShowReports(const std::vector<CrashReportDatabase::Report>& reports,\n                 size_t space_count,\n                 const Options& options) {\n  std::string spaces(space_count, ' ');\n  const char* colon = options.show_all_report_info ? \":\" : \"\";\n\n  for (const CrashReportDatabase::Report& report : reports) {\n    printf(\"%s%s%s\\n\", spaces.c_str(), report.uuid.ToString().c_str(), colon);\n    if (options.show_all_report_info) {\n      ShowReport(report, space_count + 2, options.utc);\n    }\n  }\n}\n\nint DatabaseUtilMain(int argc, char* argv[]) {\n  const base::FilePath argv0(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));\n  const base::FilePath me(argv0.BaseName());\n\n  enum OptionFlags {\n    // “Short” (single-character) options.\n    kOptionDatabase = 'd',\n\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n    kOptionCreate,\n    kOptionShowClientID,\n    kOptionShowUploadsEnabled,\n    kOptionShowLastUploadAttemptTime,\n    kOptionShowPendingReports,\n    kOptionShowCompletedReports,\n    kOptionShowAllReportInfo,\n    kOptionShowReport,\n    kOptionSetUploadsEnabled,\n    kOptionSetLastUploadAttemptTime,\n    kOptionNewReport,\n    kOptionUTC,\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  static constexpr option long_options[] = {\n      {\"create\", no_argument, nullptr, kOptionCreate},\n      {\"database\", required_argument, nullptr, kOptionDatabase},\n      {\"show-client-id\", no_argument, nullptr, kOptionShowClientID},\n      {\"show-uploads-enabled\", no_argument, nullptr, kOptionShowUploadsEnabled},\n      {\"show-last-upload-attempt-time\",\n       no_argument,\n       nullptr,\n       kOptionShowLastUploadAttemptTime},\n      {\"show-pending-reports\", no_argument, nullptr, kOptionShowPendingReports},\n      {\"show-completed-reports\",\n       no_argument,\n       nullptr,\n       kOptionShowCompletedReports},\n      {\"show-all-report-info\", no_argument, nullptr, kOptionShowAllReportInfo},\n      {\"show-report\", required_argument, nullptr, kOptionShowReport},\n      {\"set-uploads-enabled\",\n       required_argument,\n       nullptr,\n       kOptionSetUploadsEnabled},\n      {\"set-last-upload-attempt-time\",\n       required_argument,\n       nullptr,\n       kOptionSetLastUploadAttemptTime},\n      {\"new-report\", required_argument, nullptr, kOptionNewReport},\n      {\"utc\", no_argument, nullptr, kOptionUTC},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  Options options = {};\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"d:\", long_options, nullptr)) != -1) {\n    switch (opt) {\n      case kOptionCreate: {\n        options.create = true;\n        break;\n      }\n      case kOptionDatabase: {\n        options.database = optarg;\n        break;\n      }\n      case kOptionShowClientID: {\n        options.show_client_id = true;\n        break;\n      }\n      case kOptionShowUploadsEnabled: {\n        options.show_uploads_enabled = true;\n        break;\n      }\n      case kOptionShowLastUploadAttemptTime: {\n        options.show_last_upload_attempt_time = true;\n        break;\n      }\n      case kOptionShowPendingReports: {\n        options.show_pending_reports = true;\n        break;\n      }\n      case kOptionShowCompletedReports: {\n        options.show_completed_reports = true;\n        break;\n      }\n      case kOptionShowAllReportInfo: {\n        options.show_all_report_info = true;\n        break;\n      }\n      case kOptionShowReport: {\n        UUID uuid;\n        if (!uuid.InitializeFromString(optarg)) {\n          ToolSupport::UsageHint(me, \"--show-report requires a UUID\");\n          return EXIT_FAILURE;\n        }\n        options.show_reports.push_back(uuid);\n        break;\n      }\n      case kOptionSetUploadsEnabled: {\n        if (!StringToBool(optarg, &options.set_uploads_enabled)) {\n          ToolSupport::UsageHint(me, \"--set-uploads-enabled requires a BOOL\");\n          return EXIT_FAILURE;\n        }\n        options.has_set_uploads_enabled = true;\n        break;\n      }\n      case kOptionSetLastUploadAttemptTime: {\n        options.set_last_upload_attempt_time_string = optarg;\n        break;\n      }\n      case kOptionNewReport: {\n        options.new_report_paths.push_back(base::FilePath(\n            ToolSupport::CommandLineArgumentToFilePathStringType(optarg)));\n        break;\n      }\n      case kOptionUTC: {\n        options.utc = true;\n        break;\n      }\n      case kOptionHelp: {\n        Usage(me);\n        return EXIT_SUCCESS;\n      }\n      case kOptionVersion: {\n        ToolSupport::Version(me);\n        return EXIT_SUCCESS;\n      }\n      default: {\n        ToolSupport::UsageHint(me, nullptr);\n        return EXIT_FAILURE;\n      }\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n  if (!options.database) {\n    ToolSupport::UsageHint(me, \"--database is required\");\n    return EXIT_FAILURE;\n  }\n\n  // This conversion couldn’t happen in the option-processing loop above because\n  // it depends on options.utc, which may have been set after\n  // options.set_last_upload_attempt_time_string.\n  if (options.set_last_upload_attempt_time_string) {\n    if (!StringToTime(options.set_last_upload_attempt_time_string,\n                      &options.set_last_upload_attempt_time,\n                      options.utc)) {\n      ToolSupport::UsageHint(me,\n                             \"--set-last-upload-attempt-time requires a TIME\");\n      return EXIT_FAILURE;\n    }\n  }\n\n  // --new-report is treated as a show operation because it produces output.\n  const size_t show_operations = options.show_client_id +\n                                 options.show_uploads_enabled +\n                                 options.show_last_upload_attempt_time +\n                                 options.show_pending_reports +\n                                 options.show_completed_reports +\n                                 options.show_reports.size() +\n                                 options.new_report_paths.size();\n  const size_t set_operations =\n      options.has_set_uploads_enabled +\n      (options.set_last_upload_attempt_time_string != nullptr);\n\n  if ((options.create ? 1 : 0) + show_operations + set_operations == 0) {\n    ToolSupport::UsageHint(me, \"nothing to do\");\n    return EXIT_FAILURE;\n  }\n\n  std::unique_ptr<CrashReportDatabase> database;\n  base::FilePath database_path = base::FilePath(\n      ToolSupport::CommandLineArgumentToFilePathStringType(options.database));\n  if (options.create) {\n    database = CrashReportDatabase::Initialize(database_path);\n  } else {\n    database = CrashReportDatabase::InitializeWithoutCreating(database_path);\n  }\n  if (!database) {\n    return EXIT_FAILURE;\n  }\n\n  Settings* settings = database->GetSettings();\n\n  // Handle the “show” options before the “set” options so that when they’re\n  // specified together, the “show” option reflects the initial state.\n\n  if (options.show_client_id) {\n    UUID client_id;\n    if (!settings->GetClientID(&client_id)) {\n      return EXIT_FAILURE;\n    }\n\n    const char* prefix = (show_operations > 1) ? \"Client ID: \" : \"\";\n\n    printf(\"%s%s\\n\", prefix, client_id.ToString().c_str());\n  }\n\n  if (options.show_uploads_enabled) {\n    bool uploads_enabled;\n    if (!settings->GetUploadsEnabled(&uploads_enabled)) {\n      return EXIT_FAILURE;\n    }\n\n    const char* prefix = (show_operations > 1) ? \"Uploads enabled: \" : \"\";\n\n    printf(\"%s%s\\n\", prefix, BoolToString(uploads_enabled).c_str());\n  }\n\n  if (options.show_last_upload_attempt_time) {\n    time_t last_upload_attempt_time;\n    if (!settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) {\n      return EXIT_FAILURE;\n    }\n\n    const char* prefix =\n        (show_operations > 1) ? \"Last upload attempt time: \" : \"\";\n\n    printf(\"%s%s (%ld)\\n\",\n           prefix,\n           TimeToString(last_upload_attempt_time, options.utc).c_str(),\n           static_cast<long>(last_upload_attempt_time));\n  }\n\n  if (options.show_pending_reports) {\n    std::vector<CrashReportDatabase::Report> pending_reports;\n    if (database->GetPendingReports(&pending_reports) !=\n        CrashReportDatabase::kNoError) {\n      return EXIT_FAILURE;\n    }\n\n    if (show_operations > 1) {\n      printf(\"Pending reports:\\n\");\n    }\n\n    ShowReports(pending_reports, show_operations > 1 ? 2 : 0, options);\n  }\n\n  if (options.show_completed_reports) {\n    std::vector<CrashReportDatabase::Report> completed_reports;\n    if (database->GetCompletedReports(&completed_reports) !=\n        CrashReportDatabase::kNoError) {\n      return EXIT_FAILURE;\n    }\n\n    if (show_operations > 1) {\n      printf(\"Completed reports:\\n\");\n    }\n\n    ShowReports(completed_reports, show_operations > 1 ? 2 : 0, options);\n  }\n\n  for (const UUID& uuid : options.show_reports) {\n    CrashReportDatabase::Report report;\n    CrashReportDatabase::OperationStatus status =\n        database->LookUpCrashReport(uuid, &report);\n    if (status == CrashReportDatabase::kNoError) {\n      if (show_operations > 1) {\n        printf(\"Report %s:\\n\", uuid.ToString().c_str());\n      }\n      ShowReport(report, show_operations > 1 ? 2 : 0, options.utc);\n    } else if (status == CrashReportDatabase::kReportNotFound) {\n      // If only asked to do one thing, a failure to find the single requested\n      // report should result in a failure exit status.\n      if (show_operations + set_operations == 1) {\n        fprintf(\n            stderr, \"%\" PRFilePath \": Report not found\\n\", me.value().c_str());\n        return EXIT_FAILURE;\n      }\n      printf(\"Report %s not found\\n\", uuid.ToString().c_str());\n    } else {\n      return EXIT_FAILURE;\n    }\n  }\n\n  if (options.has_set_uploads_enabled &&\n      !settings->SetUploadsEnabled(options.set_uploads_enabled)) {\n    return EXIT_FAILURE;\n  }\n\n  if (options.set_last_upload_attempt_time_string &&\n      !settings->SetLastUploadAttemptTime(\n          options.set_last_upload_attempt_time)) {\n    return EXIT_FAILURE;\n  }\n\n  bool used_stdin = false;\n  for (const base::FilePath& new_report_path : options.new_report_paths) {\n    std::unique_ptr<FileReaderInterface> file_reader;\n\n    if (new_report_path.value() == FILE_PATH_LITERAL(\"-\")) {\n      if (used_stdin) {\n        fprintf(stderr,\n                \"%\" PRFilePath\n                \": Only one --new-report may be read from standard input\\n\",\n                me.value().c_str());\n        return EXIT_FAILURE;\n      }\n      used_stdin = true;\n      file_reader.reset(new WeakFileHandleFileReader(\n          StdioFileHandle(StdioStream::kStandardInput)));\n    } else {\n      std::unique_ptr<FileReader> file_path_reader(new FileReader());\n      if (!file_path_reader->Open(new_report_path)) {\n        return EXIT_FAILURE;\n      }\n\n      file_reader = std::move(file_path_reader);\n    }\n\n    std::unique_ptr<CrashReportDatabase::NewReport> new_report;\n    CrashReportDatabase::OperationStatus status =\n        database->PrepareNewCrashReport(&new_report);\n    if (status != CrashReportDatabase::kNoError) {\n      return EXIT_FAILURE;\n    }\n\n    char buf[4096];\n    FileOperationResult read_result;\n    do {\n      read_result = file_reader->Read(buf, sizeof(buf));\n      if (read_result < 0) {\n        return EXIT_FAILURE;\n      }\n      if (read_result > 0 && !new_report->Writer()->Write(buf, read_result)) {\n        return EXIT_FAILURE;\n      }\n    } while (read_result > 0);\n\n    UUID uuid;\n    status = database->FinishedWritingCrashReport(std::move(new_report), &uuid);\n    if (status != CrashReportDatabase::kNoError) {\n      return EXIT_FAILURE;\n    }\n\n    const char* prefix = (show_operations > 1) ? \"New report ID: \" : \"\";\n    printf(\"%s%s\\n\", prefix, uuid.ToString().c_str());\n  }\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace crashpad\n\n#if BUILDFLAG(IS_POSIX)\nint main(int argc, char* argv[]) {\n  return crashpad::DatabaseUtilMain(argc, argv);\n}\n#elif BUILDFLAG(IS_WIN)\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::ToolSupport::Wmain(argc, argv, crashpad::DatabaseUtilMain);\n}\n#endif  // BUILDFLAG(IS_POSIX)\n"
  },
  {
    "path": "tools/crashpad_database_util.md",
    "content": "<!--\nCopyright 2015 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# crashpad_database_util(1)\n\n## Name\n\ncrashpad_database_util—Operate on Crashpad crash report databases\n\n## Synopsis\n\n**crashpad_database_util** [_OPTION…_]\n\n## Description\n\nOperates on Crashpad crash report databases. The database’s settings can be\nqueried and modified, and information about crash reports stored in the database\ncan be displayed.\n\nWhen this program is requested to both show and set information in a single\ninvocation, all “show” operations will be completed prior to beginning any “set”\noperation.\n\nPrograms that use the Crashpad client library directly will not normally use\nthis tool, but may use the database through the programmatic interfaces in the\nclient library. This tool exists to allow developers to manipulate a Crashpad\ndatabase.\n\n## Options\n\n * **--create**\n\n   Creates the database identified by **--database** if it does not exist,\n   provided that the parent directory of _PATH_ exists.\n\n * **-d**, **--database**=_PATH_\n\n   Use _PATH_ as the path to the Crashpad crash report database. This option is\n   required. The database must already exist unless **--create** is also\n   specified.\n\n * **--show-client-id**\n\n   Show the client ID stored in the database’s settings. The client ID is\n   formatted as a UUID. The client ID is set when the database is created.\n   [crashpad_handler(8)](../handler/crashpad_handler.md) retrieves the client ID\n   and stores it in crash reports as they are written.\n\n * **--show-uploads-enabled**\n\n   Show the status of the uploads-enabled bit stored in the database’s settings.\n   [crashpad_handler(8)](../handler/crashpad_handler.md) does not upload reports\n   when this bit is false. This bit is false when a database is created, and is\n   under an application’s control via the Crashpad client library interface.\n\n   See also **--set-uploads-enabled**.\n\n * **--show-last-upload-attempt-time**\n\n   Show the last-upload-attempt time stored in the database’s settings. This\n   value is `0`, meaning “never,” when the database is created.\n   [crashpad_handler(8)](../handler/crashpad_handler.md) consults this value\n   before attempting an upload to implement its rate-limiting behavior. The\n   database updates this value whenever an upload is attempted.\n\n   See also **--set-last-upload-attempt-time**.\n\n * **--show-pending-reports**\n\n   Show reports eligible for upload.\n\n * **--show-completed-reports**\n\n   Show reports not eligible for upload. A report is moved from the “pending”\n   state to the “completed” state by\n   [crashpad_handler(8)](../handler/crashpad_handler.md). This may happen when a\n   report is successfully uploaded, when a report is not uploaded because\n   uploads are disabled, or when a report upload attempt fails and will not be\n   retried.\n\n * **--show-all-report-info**\n\n   With **--show-pending-reports** or **--show-completed-reports**, show all\n   metadata for each report displayed. Without this option, only report IDs will\n   be shown.\n\n * **--show-report**=_UUID_\n\n   Show a report from the database looked up by its identifier, _UUID_, which\n   must be formatted in string representation per RFC 4122 §3. All metadata for\n   each report found via a **--show-report** option will be shown. If _UUID_ is\n   not found, the string `\"not found\"` will be printed. If this program is only\n   requested to show a single report and it is not found, it will treat this as\n   a failure for the purposes of determining its exit status. This option may\n   appear multiple times.\n\n * **--set-report-uploads-enabled**=_BOOL_\n\n   Enable or disable report upload in the database’s settings. _BOOL_ is a\n   string representation of a boolean value, such as `\"0\"` or `\"true\"`.\n\n   See also **--show-uploads-enabled**.\n\n * **--set-last-upload-attempt-time**=_TIME_\n\n   Set the last-upload-attempt time in the database’s settings. _TIME_ is a\n   string representation of a time, which may be in _yyyy-mm-dd hh:mm:ss_\n   format, a numeric `time_t` value, or the special strings `\"never\"` or\n   `\"now\"`.\n\n   See also **--show-last-upload-attempt-time**.\n\n * **--new-report**=_PATH_\n\n   Submit a new report located at _PATH_ to the database. If _PATH_ is `\"-\"`,\n   the new report will be read from standard input. The new report will be in\n   the “pending” state. The UUID assigned to the new report will be printed.\n   This option may appear multiple times.\n\n * **--utc**\n\n   When showing times, do so in UTC as opposed to the local time zone. When\n   setting times, interpret ambiguous time strings in UTC as opposed to the\n   local time zone.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Examples\n\nShows all crash reports in a crash report database that are in the “completed”\nstate.\n\n```\n$ crashpad_database_util --database /tmp/crashpad_database \\\n      --show-completed-reports\n23f9512b-63e1-4ead-9dcd-e2e21fbccc68\n4bfca440-039f-4bc6-bbd4-6933cef5efd4\n56caeff8-b61a-43b2-832d-9e796e6e4a50\n```\n\nDisables report upload in a crash report database’s settings, and then verifies\nthat the change was made.\n\n```\n$ crashpad_database_util --database /tmp/crashpad_database \\\n      --set-uploads-enabled false\n$ crashpad_database_util --database /tmp/crashpad_database \\\n      --show-uploads-enabled\nfalse\n```\n\n## Exit Status\n\n * **0**\n\n   Success.\n\n * **1**\n\n   Failure, with a message printed to the standard error stream.\n\n## See Also\n\n[crashpad_handler(8)](../handler/crashpad_handler.md)\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2015 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "tools/crashpad_http_upload.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <getopt.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"tools/tool_support.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/net/http_body.h\"\n#include \"util/net/http_multipart_builder.h\"\n#include \"util/net/http_transport.h\"\n#include \"util/string/split_string.h\"\n\nnamespace crashpad {\nnamespace {\n\nvoid Usage(const base::FilePath& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %\" PRFilePath \" [OPTION]...\\n\"\n\"Send an HTTP POST request.\\n\"\n\"  -f, --file=KEY=PATH     upload the file at PATH for the HTTP KEY parameter\\n\"\n\"      --no-upload-gzip    don't use gzip compression when uploading\\n\"\n\"  -o, --output=FILE       write the response body to FILE instead of stdout\\n\"\n\"  -s, --string=KEY=VALUE  set the HTTP KEY parameter to VALUE\\n\"\n\"  -u, --url=URL           send the request to URL\\n\"\n\"      --help              display this help and exit\\n\"\n\"      --version           output version information and exit\\n\",\n          me.value().c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nint HTTPUploadMain(int argc, char* argv[]) {\n  const base::FilePath argv0(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));\n  const base::FilePath me(argv0.BaseName());\n\n  enum OptionFlags {\n    // “Short” (single-character) options.\n    kOptionFile = 'f',\n    kOptionOutput = 'o',\n    kOptionString = 's',\n    kOptionURL = 'u',\n\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n    kOptionNoUploadGzip,\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  struct {\n    std::string url;\n    const char* output;\n    bool upload_gzip;\n  } options = {};\n  options.upload_gzip = true;\n\n  static constexpr option long_options[] = {\n      {\"file\", required_argument, nullptr, kOptionFile},\n      {\"no-upload-gzip\", no_argument, nullptr, kOptionNoUploadGzip},\n      {\"output\", required_argument, nullptr, kOptionOutput},\n      {\"string\", required_argument, nullptr, kOptionString},\n      {\"url\", required_argument, nullptr, kOptionURL},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  std::vector<std::unique_ptr<FileReader>> readers;\n  HTTPMultipartBuilder http_multipart_builder;\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"f:o:s:u:\", long_options, nullptr)) !=\n         -1) {\n    switch (opt) {\n      case kOptionFile: {\n        std::string key;\n        std::string path;\n        if (!SplitStringFirst(optarg, '=', &key, &path)) {\n          ToolSupport::UsageHint(me, \"--file requires KEY=STRING\");\n          return EXIT_FAILURE;\n        }\n        base::FilePath file_path(\n            ToolSupport::CommandLineArgumentToFilePathStringType(path));\n        std::string file_name(\n            ToolSupport::FilePathToCommandLineArgument(file_path.BaseName()));\n\n        readers.push_back(std::make_unique<FileReader>());\n        FileReader* upload_file_reader = readers.back().get();\n        if (!upload_file_reader->Open(file_path)) {\n          return EXIT_FAILURE;\n        }\n        http_multipart_builder.SetFileAttachment(\n            key, file_name, upload_file_reader, \"application/octet-stream\");\n        break;\n      }\n      case kOptionNoUploadGzip: {\n        options.upload_gzip = false;\n        break;\n      }\n      case kOptionOutput: {\n        options.output = optarg;\n        break;\n      }\n      case kOptionString: {\n        std::string key;\n        std::string value;\n        if (!SplitStringFirst(optarg, '=', &key, &value)) {\n          ToolSupport::UsageHint(me, \"--string requires KEY=VALUE\");\n          return EXIT_FAILURE;\n        }\n        http_multipart_builder.SetFormData(key, value);\n        break;\n      }\n      case kOptionURL:\n        options.url = optarg;\n        break;\n      case kOptionHelp:\n        Usage(me);\n        return EXIT_SUCCESS;\n      case kOptionVersion:\n        ToolSupport::Version(me);\n        return EXIT_SUCCESS;\n      default:\n        ToolSupport::UsageHint(me, nullptr);\n        return EXIT_FAILURE;\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n  if (options.url.empty()) {\n    ToolSupport::UsageHint(me, \"--url is required\");\n    return EXIT_FAILURE;\n  }\n\n  if (argc) {\n    ToolSupport::UsageHint(me, nullptr);\n    return EXIT_FAILURE;\n  }\n\n  std::unique_ptr<FileWriterInterface> file_writer;\n  if (options.output) {\n    FileWriter* file_writer_impl = new FileWriter();\n    file_writer.reset(file_writer_impl);\n    base::FilePath output_path(\n        ToolSupport::CommandLineArgumentToFilePathStringType(options.output));\n    if (!file_writer_impl->Open(output_path,\n                                FileWriteMode::kTruncateOrCreate,\n                                FilePermissions::kWorldReadable)) {\n      return EXIT_FAILURE;\n    }\n  } else {\n    file_writer.reset(new WeakFileHandleFileWriter(\n        StdioFileHandle(StdioStream::kStandardOutput)));\n  }\n\n  http_multipart_builder.SetGzipEnabled(options.upload_gzip);\n\n  std::unique_ptr<HTTPTransport> http_transport(HTTPTransport::Create());\n  http_transport->SetURL(options.url);\n\n  HTTPHeaders content_headers;\n  http_multipart_builder.PopulateContentHeaders(&content_headers);\n  for (const auto& content_header : content_headers) {\n    http_transport->SetHeader(content_header.first, content_header.second);\n  }\n\n  http_transport->SetBodyStream(http_multipart_builder.GetBodyStream());\n\n  std::string response_body;\n  if (!http_transport->ExecuteSynchronously(&response_body)) {\n    return EXIT_FAILURE;\n  }\n\n  if (!response_body.empty() &&\n      !file_writer->Write(&response_body[0], response_body.size())) {\n    return EXIT_FAILURE;\n  }\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace crashpad\n\n#if BUILDFLAG(IS_POSIX)\nint main(int argc, char* argv[]) {\n  return crashpad::HTTPUploadMain(argc, argv);\n}\n#elif BUILDFLAG(IS_WIN)\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::ToolSupport::Wmain(argc, argv, crashpad::HTTPUploadMain);\n}\n#endif  // BUILDFLAG(IS_POSIX)\n"
  },
  {
    "path": "tools/crashpad_http_upload.md",
    "content": "<!--\nCopyright 2017 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# crashpad_http_upload(1)\n\n## Name\n\ncrashpad_http_upload—Send an HTTP POST request\n\n## Synopsis\n\n**crashpad_http_uplaod** [_OPTION…_]\n\n## Description\n\nPerforms an HTTP or HTTPS POST, building a `multipart/form-data` request from\nkey-value pairs and files in the manner of an HTML `<form>` with a POST action.\nProvides the response.\n\nPrograms that use the Crashpad client library directly will not normally use\nthis tool. This tool is provided for debugging and testing as it isolates\nCrashpad’s networking implementation normally used to upload crash reports to\na crash report collection server, making it available for more general use.\n\n## Options\n\n * **-f**, **--file**=_KEY_=_PATH_\n\n   Include _PATH_ in the request as a file upload, in the manner of an HTML\n   `<input type=\"file\">` element. _KEY_ is used as the field name.\n\n * **--no-upload-gzip**\n\n   Do not use `gzip` compression. Normally, the entire request body is\n   compressed into a `gzip` stream and transmitted with `Content-Encoding:\n   gzip`. This option disables compression, and is intended for use with servers\n   that don’t accept uploads compressed in this way.\n\n * **-o**, **--output**=_FILE_\n\n   The response body will be written to _FILE_ instead of standard output.\n\n * **-s**, **--string**=_KEY_=_VALUE_\n\n   Include _KEY_ and _VALUE_ in the request as an ordinary form field, in the\n   manner of an HTML `<input type=\"text\">` element. _KEY_ is used as the field\n   name, and _VALUE_ is used as its value.\n\n * **-u**, **--url**=_URL_\n\n   Send the request to _URL_. This option is required.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Examples\n\nUploads a file to an HTTP server running on `localhost`.\n\n```\n$ crashpad_http-upload --url http://localhost/upload_test \\\n      --string=when=now --file=what=1040.pdf\nThanks for the upload!\n```\n\nThis example corresponds to the HTML form:\n\n```\n<form action=\"http://localhost/upload_test\" method=\"post\">\n  <input type=\"text\" name=\"when\" value=\"now\" />\n  <input type=\"file\" name=\"what\" />\n  <input type=\"submit\" />\n</form>\n```\n\n## Exit Status\n\n * **0**\n\n   Success.\n\n * **1**\n\n   Failure, with a message printed to the standard error stream. HTTP error\n   statuses such as 404 (Not Found) are included in the definition of failure.\n\n## See Also\n\n[crashpad_handler(8)](../handler/crashpad_handler.md)\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2017 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "tools/dump_minidump_annotations.cc",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <getopt.h>\n\n#include \"base/files/file_path.h\"\n#include \"client/annotation.h\"\n#include \"util/file/file_reader.h\"\n#include \"snapshot/minidump/process_snapshot_minidump.h\"\n#include \"tools/tool_support.h\"\n\nnamespace crashpad {\nnamespace {\n\nvoid Usage(const base::FilePath& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %\" PRFilePath \" [OPTION]... PATH\\n\"\n\"Dump annotations from minidumps.\\n\"\n\"\\n\"\n\"      --help                      display this help and exit\\n\"\n\"      --version                   output version information and exit\\n\",\n          me.value().c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nstruct Options {\n  const char* minidump;\n};\n\nint DumpMinidumpAnnotationsMain(int argc, char* argv[]) {\n  const base::FilePath argv0(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));\n  const base::FilePath me(argv0.BaseName());\n\n  enum OptionFlags {\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n    kOptionMinidump,\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  static constexpr option long_options[] = {\n      {\"minidump\", required_argument, nullptr, kOptionMinidump},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  Options options = {};\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"\", long_options, nullptr)) != -1) {\n    switch (opt) {\n      case kOptionMinidump: {\n        options.minidump = optarg;\n        break;\n      }\n      case kOptionHelp: {\n        Usage(me);\n        return EXIT_SUCCESS;\n      }\n      case kOptionVersion: {\n        ToolSupport::Version(me);\n        return EXIT_SUCCESS;\n      }\n      default: {\n        ToolSupport::UsageHint(me, nullptr);\n        return EXIT_FAILURE;\n      }\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n  if (!options.minidump) {\n    ToolSupport::UsageHint(me, \"--minidump is required\");\n    return EXIT_FAILURE;\n  }\n\n  FileReader reader;\n  if (!reader.Open(base::FilePath(\n      ToolSupport::CommandLineArgumentToFilePathStringType(\n          options.minidump)))) {\n    return EXIT_FAILURE;\n  }\n\n  ProcessSnapshotMinidump snapshot;\n  if (!snapshot.Initialize(&reader)) {\n    return EXIT_FAILURE;\n  }\n\n  printf(\"Snapshot Annotations\\n\");\n  for (const auto& kv : snapshot.AnnotationsSimpleMap()) {\n    printf(\"  snapshot_annotations[\\\"%s\\\"] = %s\\n\",\n           kv.first.c_str(), kv.second.c_str());\n  }\n\n  for (const ModuleSnapshot* module : snapshot.Modules()) {\n    printf(\"Module: %s\\n\", module->Name().c_str());\n    printf(\"  Simple Annotations\\n\");\n    for (const auto& kv : module->AnnotationsSimpleMap()) {\n      printf(\"    simple_annotations[\\\"%s\\\"] = %s\\n\",\n             kv.first.c_str(), kv.second.c_str());\n    }\n\n    printf(\"  Vectored Annotations\\n\");\n    int index = 0;\n    for (const std::string& annotation : module->AnnotationsVector()) {\n      printf(\"    vectored_annotations[%d] = %s\\n\", index, annotation.c_str());\n      index++;\n    }\n\n    printf(\"  Annotation Objects\\n\");\n    for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {\n      printf(\"    annotation_objects[\\\"%s\\\"] = \", annotation.name.c_str());\n      if (annotation.type != static_cast<uint16_t>(Annotation::Type::kString)) {\n\n        printf(\"<non-string value, not printing>\\n\");\n        continue;\n      }\n\n      std::string value(reinterpret_cast<const char*>(annotation.value.data()),\n                        annotation.value.size());\n\n      printf(\"%s\\n\", value.c_str());\n    }\n  }\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint main(int argc, char* argv[]) {\n  return crashpad::DumpMinidumpAnnotationsMain(argc, argv);\n}\n"
  },
  {
    "path": "tools/generate_dump.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <getopt.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <string>\n\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"minidump/minidump_file_writer.h\"\n#include \"tools/tool_support.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/process/process_id.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include <unistd.h>\n\n#include \"util/posix/drop_privileges.h\"\n#endif\n\n#if BUILDFLAG(IS_APPLE)\n#include <mach/mach.h>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"snapshot/mac/process_snapshot_mac.h\"\n#include \"util/mach/scoped_task_suspend.h\"\n#include \"util/mach/task_for_pid.h\"\n#elif BUILDFLAG(IS_WIN)\n#include \"base/strings/utf_string_conversions.h\"\n#include \"snapshot/win/process_snapshot_win.h\"\n#include \"util/win/scoped_process_suspend.h\"\n#include \"util/win/xp_compat.h\"\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include \"snapshot/linux/process_snapshot_linux.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#endif  // BUILDFLAG(IS_APPLE)\n\nnamespace crashpad {\nnamespace {\n\nvoid Usage(const base::FilePath& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %\" PRFilePath \" [OPTION]... PID\\n\"\n\"Generate a minidump file containing a snapshot of a running process.\\n\"\n\"\\n\"\n\"  -r, --no-suspend   don't suspend the target process during dump generation\\n\"\n\"  -o, --output=FILE  write the minidump to FILE instead of minidump.PID\\n\"\n\"      --help         display this help and exit\\n\"\n\"      --version      output version information and exit\\n\",\n          me.value().c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nint GenerateDumpMain(int argc, char* argv[]) {\n  const base::FilePath argv0(\n      ToolSupport::CommandLineArgumentToFilePathStringType(argv[0]));\n  const base::FilePath me(argv0.BaseName());\n\n  enum OptionFlags {\n    // “Short” (single-character) options.\n    kOptionOutput = 'o',\n    kOptionNoSuspend = 'r',\n\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  struct {\n    std::string dump_path;\n    ProcessID pid;\n    bool suspend;\n  } options = {};\n  options.suspend = true;\n\n  static constexpr option long_options[] = {\n      {\"no-suspend\", no_argument, nullptr, kOptionNoSuspend},\n      {\"output\", required_argument, nullptr, kOptionOutput},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"o:r\", long_options, nullptr)) != -1) {\n    switch (opt) {\n      case kOptionOutput:\n        options.dump_path = optarg;\n        break;\n      case kOptionNoSuspend:\n        options.suspend = false;\n        break;\n      case kOptionHelp:\n        Usage(me);\n        return EXIT_SUCCESS;\n      case kOptionVersion:\n        ToolSupport::Version(me);\n        return EXIT_SUCCESS;\n      default:\n        ToolSupport::UsageHint(me, nullptr);\n        return EXIT_FAILURE;\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n  if (argc != 1) {\n    ToolSupport::UsageHint(me, \"PID is required\");\n    return EXIT_FAILURE;\n  }\n\n  if (!StringToNumber(argv[0], &options.pid) || options.pid <= 0) {\n    fprintf(stderr,\n            \"%\" PRFilePath \": invalid PID: %s\\n\",\n            me.value().c_str(),\n            argv[0]);\n    return EXIT_FAILURE;\n  }\n\n#if BUILDFLAG(IS_APPLE)\n  task_t task = TaskForPID(options.pid);\n  if (task == TASK_NULL) {\n    return EXIT_FAILURE;\n  }\n  base::apple::ScopedMachSendRight task_owner(task);\n\n  // This tool may have been installed as a setuid binary so that TaskForPID()\n  // could succeed. Drop any privileges now that they’re no longer necessary.\n  DropPrivileges();\n\n  if (options.pid == getpid()) {\n    if (options.suspend) {\n      LOG(ERROR) << \"cannot suspend myself\";\n      return EXIT_FAILURE;\n    }\n    LOG(WARNING) << \"operating on myself\";\n  }\n#elif BUILDFLAG(IS_WIN)\n  ScopedKernelHANDLE process(\n      OpenProcess(kXPProcessAllAccess, false, options.pid));\n  if (!process.is_valid()) {\n    PLOG(ERROR) << \"could not open process \" << options.pid;\n    return EXIT_FAILURE;\n  }\n#endif  // BUILDFLAG(IS_APPLE)\n\n  if (options.dump_path.empty()) {\n    options.dump_path = base::StringPrintf(\"minidump.%\" PRI_PROCESS_ID,\n                                           options.pid);\n  }\n\n  {\n#if BUILDFLAG(IS_APPLE)\n    std::unique_ptr<ScopedTaskSuspend> suspend;\n    if (options.suspend) {\n      suspend.reset(new ScopedTaskSuspend(task));\n    }\n#elif BUILDFLAG(IS_WIN)\n    std::unique_ptr<ScopedProcessSuspend> suspend;\n    if (options.suspend) {\n      suspend.reset(new ScopedProcessSuspend(process.get()));\n    }\n#endif  // BUILDFLAG(IS_APPLE)\n\n#if BUILDFLAG(IS_APPLE)\n    ProcessSnapshotMac process_snapshot;\n    if (!process_snapshot.Initialize(task)) {\n      return EXIT_FAILURE;\n    }\n#elif BUILDFLAG(IS_WIN)\n    ProcessSnapshotWin process_snapshot;\n    if (!process_snapshot.Initialize(process.get(),\n                                     options.suspend\n                                         ? ProcessSuspensionState::kSuspended\n                                         : ProcessSuspensionState::kRunning,\n                                     0,\n                                     0)) {\n      return EXIT_FAILURE;\n    }\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n    // TODO(jperaza): https://crashpad.chromium.org/bug/30.\n    DirectPtraceConnection task;\n    if (!task.Initialize(options.pid)) {\n      return EXIT_FAILURE;\n    }\n    ProcessSnapshotLinux process_snapshot;\n    if (!process_snapshot.Initialize(&task)) {\n      return EXIT_FAILURE;\n    }\n#endif  // BUILDFLAG(IS_APPLE)\n\n    FileWriter file_writer;\n    base::FilePath dump_path(\n        ToolSupport::CommandLineArgumentToFilePathStringType(\n            options.dump_path));\n    if (!file_writer.Open(dump_path,\n                          FileWriteMode::kTruncateOrCreate,\n                          FilePermissions::kWorldReadable)) {\n      return EXIT_FAILURE;\n    }\n\n    MinidumpFileWriter minidump;\n    minidump.InitializeFromSnapshot(&process_snapshot);\n    if (!minidump.WriteEverything(&file_writer)) {\n      file_writer.Close();\n      if (unlink(options.dump_path.c_str()) != 0) {\n        PLOG(ERROR) << \"unlink\";\n      }\n      return EXIT_FAILURE;\n    }\n  }\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace crashpad\n\n#if BUILDFLAG(IS_POSIX)\nint main(int argc, char* argv[]) {\n  return crashpad::GenerateDumpMain(argc, argv);\n}\n#elif BUILDFLAG(IS_WIN)\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::ToolSupport::Wmain(argc, argv, crashpad::GenerateDumpMain);\n}\n#endif  // BUILDFLAG(IS_POSIX)\n"
  },
  {
    "path": "tools/generate_dump.md",
    "content": "<!--\nCopyright 2014 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# generate_dump(1)\n\n## Name\n\ngenerate_dump—Generate a minidump file containing a snapshot of a running\nprocess\n\n## Synopsis\n\n**generate_dump** [_OPTION…_] _PID_\n\n## Description\n\nGenerates a minidump file containing a snapshot of a running process whose\nprocess identifier is _PID_. By default, the target process will be suspended\nwhile the minidump is generated, and the minidump file will be written to\n`minidump.PID`. After the minidump file is generated, the target process resumes\nrunning.\n\nThe minidump file will contain information about the process, its threads, its\nmodules, and the system. It will not contain any exception information because\nit will be generated from a live running process, not as a result of an\nexception occurring.\n\nOn macOS, this program uses `task_for_pid()` to access the process’ task port.\nThis operation may be restricted to use by the superuser, executables signed by\nan authority trusted by the system, and processes otherwise permitted by\ntaskgated(8). Consequently, this program must normally either be signed or be\ninvoked by root. It is possible to install this program as a setuid root\nexecutable to overcome this limitation, although it will remain impossible to\ngenerate dumps for processes protected by [System Integrity Protection\n(SIP)](https://support.apple.com/HT204899), including those whose “restrict”\ncodesign(1) option is respected.\n\nThis program is similar to the gcore(1) program available on some operating\nsystems.\n\n## Options\n\n * **-r**, **--no-suspend**\n\n   The target process will continue running while the minidump file is\n   generated. Normally, the target process is suspended during this operation,\n   which guarantees that the minidump file will contain an atomic snapshot of\n   the process.\n\n   This option may be useful when attempting to generate a minidump from a\n   process that dump generation has an interprocess dependency on, such as a\n   system server like launchd(8) or opendirectoryd(8) on macOS. Deadlock could\n   occur if any portion of the dump generation operation blocks while waiting\n   for a response from one of these servers while they are suspended.\n\n * **-o**, **--output**=_FILE_\n\n   The minidump will be written to _FILE_ instead of `minidump.PID`.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Examples\n\nGenerate a minidump file in `/tmp/minidump` containing a snapshot of the process\nwith PID 1234.\n\n```\n$ generate_dump --output=/tmp/minidump 1234\n```\n\n## Exit Status\n\n * **0**\n\n   Success.\n\n * **1**\n\n   Failure, with a message printed to the standard error stream.\n\n## See Also\n\n[catch_exception_tool(1)](mac/catch_exception_tool.md)\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2014 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "tools/mac/catch_exception_tool.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <fcntl.h>\n#include <getopt.h>\n#include <libgen.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <string>\n#include <vector>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/files/scoped_file.h\"\n#include \"base/logging.h\"\n#include \"tools/tool_support.h\"\n#include \"util/mach/bootstrap.h\"\n#include \"util/mach/exc_server_variants.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/exception_types.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n#include \"util/mach/symbolic_constants_mach.h\"\n#include \"util/posix/symbolic_constants_posix.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n\nnamespace crashpad {\nnamespace {\n\nstruct Options {\n  std::string file_path;\n  std::string mach_service;\n  FILE* file;\n  int timeout_secs;\n  bool has_timeout;\n  MachMessageServer::Persistent persistent;\n};\n\nclass ExceptionServer final : public UniversalMachExcServer::Interface {\n public:\n  ExceptionServer(const Options& options,\n                  const std::string& me,\n                  int* exceptions_handled)\n      : UniversalMachExcServer::Interface(),\n        options_(options),\n        me_(me),\n        exceptions_handled_(exceptions_handled) {}\n\n  // UniversalMachExcServer::Interface:\n  virtual kern_return_t CatchMachException(\n      exception_behavior_t behavior,\n      exception_handler_t exception_port,\n      thread_t thread,\n      task_t task,\n      exception_type_t exception,\n      const mach_exception_data_type_t* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer,\n      bool* destroy_complex_request) override {\n    *destroy_complex_request = true;\n    ++*exceptions_handled_;\n\n    fprintf(options_.file,\n            \"%s: behavior %s\",\n            me_.c_str(),\n            ExceptionBehaviorToString(\n                behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());\n\n    kern_return_t kr;\n    if (ExceptionBehaviorHasIdentity(behavior)) {\n      // It’s not possible to call pid_for_task() once EXC_CORPSE_NOTIFY has\n      // been generated. It is possible to obtain the process ID by mapping the\n      // corpse kcdata area from the task’s address space at code[0] (size\n      // code[1]) and locating TASK_CRASHINFO_PID within that area. This area\n      // also includes TASK_CRASHINFO_CRASHED_THREADID which could be used\n      // instead of thread_info() below, and TASK_CRASHINFO_EXCEPTION_CODES\n      // which could be used to recover the exception codes passed to the\n      // EXC_CRASH handler. None of this is currently done because corpses are a\n      // new 10.11-only feature. See 10.11 <corpses/task_corpse.h> and\n      // <kern/kern_cdata.h>.\n      if (exception != EXC_CORPSE_NOTIFY) {\n        pid_t pid;\n        kr = pid_for_task(task, &pid);\n        if (kr != KERN_SUCCESS) {\n          fprintf(options_.file, \"\\n\");\n          fflush(options_.file);\n          MACH_LOG(ERROR, kr) << \"pid_for_task\";\n          return KERN_FAILURE;\n        }\n        fprintf(options_.file, \", pid %d\", pid);\n      }\n\n      thread_identifier_info identifier_info;\n      mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;\n      kr = thread_info(thread,\n                       THREAD_IDENTIFIER_INFO,\n                       reinterpret_cast<thread_info_t>(&identifier_info),\n                       &count);\n      if (kr != KERN_SUCCESS) {\n        fprintf(options_.file, \"\\n\");\n        fflush(options_.file);\n        MACH_LOG(ERROR, kr) << \"thread_info\";\n        return KERN_FAILURE;\n      }\n      fprintf(options_.file, \", thread %lld\", identifier_info.thread_id);\n    }\n\n    fprintf(\n        options_.file,\n        \", exception %s, codes[%d]\",\n        ExceptionToString(exception, kUseFullName | kUnknownIsNumeric).c_str(),\n        code_count);\n\n    for (size_t index = 0; index < code_count; ++index) {\n      fprintf(options_.file,\n              \"%s %#llx\",\n              index != 0 ? \",\" : \"\",\n              code[index]);\n    }\n\n    if (exception == EXC_CRASH) {\n      mach_exception_code_t original_code_0;\n      int signal;\n      exception_type_t original_exception =\n          ExcCrashRecoverOriginalException(code[0], &original_code_0, &signal);\n      fprintf(options_.file,\n              \", original exception %s, original code[0] %lld, signal %s\",\n              ExceptionToString(original_exception,\n                                kUseFullName | kUnknownIsNumeric).c_str(),\n              original_code_0,\n              SignalToString(signal, kUseFullName | kUnknownIsNumeric).c_str());\n    }\n\n    if (ExceptionBehaviorHasState(behavior)) {\n      std::string flavor_string =\n          ThreadStateFlavorToString(*flavor, kUseFullName | kUnknownIsNumeric);\n      fprintf(options_.file,\n              \", flavor %s, old_state_count %d\",\n              flavor_string.c_str(),\n              old_state_count);\n    }\n\n    fprintf(options_.file, \"\\n\");\n    fflush(options_.file);\n\n    if (exception != EXC_CRASH && exception != kMachExceptionSimulated) {\n      // Find another handler.\n      return KERN_FAILURE;\n    }\n\n    ExcServerCopyState(\n        behavior, old_state, old_state_count, new_state, new_state_count);\n\n    return ExcServerSuccessfulReturnValue(exception, behavior, false);\n  }\n\n private:\n  const Options& options_;\n  const std::string& me_;\n  int* exceptions_handled_;\n};\n\nvoid Usage(const std::string& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %s -m SERVICE [OPTION]...\\n\"\n\"Catch Mach exceptions and display information about them.\\n\"\n\"\\n\"\n\"  -f, --file=FILE             append information to FILE instead of stdout\\n\"\n\"  -m, --mach-service=SERVICE  register SERVICE with the bootstrap server\\n\"\n\"  -p, --persistent            continue processing exceptions after the first\\n\"\n\"  -t, --timeout=TIMEOUT       run for a maximum of TIMEOUT seconds\\n\"\n\"      --help                  display this help and exit\\n\"\n\"      --version               output version information and exit\\n\",\n          me.c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nint CatchExceptionToolMain(int argc, char* argv[]) {\n  const std::string me(basename(argv[0]));\n\n  enum OptionFlags {\n    // “Short” (single-character) options.\n    kOptionFile = 'f',\n    kOptionMachService = 'm',\n    kOptionPersistent = 'p',\n    kOptionTimeout = 't',\n\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  Options options = {};\n\n  static constexpr option long_options[] = {\n      {\"file\", required_argument, nullptr, kOptionFile},\n      {\"mach-service\", required_argument, nullptr, kOptionMachService},\n      {\"persistent\", no_argument, nullptr, kOptionPersistent},\n      {\"timeout\", required_argument, nullptr, kOptionTimeout},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"f:m:pt:\", long_options, nullptr)) !=\n         -1) {\n    switch (opt) {\n      case kOptionFile:\n        options.file_path = optarg;\n        break;\n      case kOptionMachService:\n        options.mach_service = optarg;\n        break;\n      case kOptionPersistent:\n        options.persistent = MachMessageServer::kPersistent;\n        break;\n      case kOptionTimeout:\n        if (!StringToNumber(optarg, &options.timeout_secs) ||\n            options.timeout_secs < 0) {\n          ToolSupport::UsageHint(me, \"-t requires a zero or positive TIMEOUT\");\n          return EXIT_FAILURE;\n        }\n        options.has_timeout = true;\n        break;\n      case kOptionHelp:\n        Usage(me);\n        return EXIT_SUCCESS;\n      case kOptionVersion:\n        ToolSupport::Version(me);\n        return EXIT_SUCCESS;\n      default:\n        ToolSupport::UsageHint(me, nullptr);\n        return EXIT_FAILURE;\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n  if (options.mach_service.empty()) {\n    ToolSupport::UsageHint(me, \"-m is required\");\n    return EXIT_FAILURE;\n  }\n\n  base::apple::ScopedMachReceiveRight service_port(\n      BootstrapCheckIn(options.mach_service));\n  if (service_port == kMachPortNull) {\n    return EXIT_FAILURE;\n  }\n\n  base::ScopedFILE file_owner;\n  if (options.file_path.empty()) {\n    options.file = stdout;\n  } else {\n    file_owner.reset(fopen(options.file_path.c_str(), \"a\"));\n    if (!file_owner.get()) {\n      PLOG(ERROR) << \"fopen \" << options.file_path;\n      return EXIT_FAILURE;\n    }\n    options.file = file_owner.get();\n    if (fcntl(fileno(options.file), F_SETFD, FD_CLOEXEC) == -1) {\n      PLOG(ERROR) << \"fcntl \" << options.file_path;\n      return EXIT_FAILURE;\n    }\n  }\n\n  int exceptions_handled = 0;\n  ExceptionServer exception_server(options, me, &exceptions_handled);\n  UniversalMachExcServer universal_mach_exc_server(&exception_server);\n\n  // Assume that if persistent mode has been requested, it’s desirable to ignore\n  // large messages and keep running.\n  MachMessageServer::ReceiveLarge receive_large =\n      (options.persistent == MachMessageServer::kPersistent)\n          ? MachMessageServer::kReceiveLargeIgnore\n          : MachMessageServer::kReceiveLargeError;\n\n  mach_msg_timeout_t timeout_ms;\n  if (!options.has_timeout) {\n    timeout_ms = kMachMessageTimeoutWaitIndefinitely;\n  } else if (options.timeout_secs == 0) {\n    timeout_ms = kMachMessageTimeoutNonblocking;\n  } else {\n    timeout_ms = options.timeout_secs * 1000;\n  }\n\n  mach_msg_return_t mr = MachMessageServer::Run(&universal_mach_exc_server,\n                                                service_port.get(),\n                                                MACH_MSG_OPTION_NONE,\n                                                options.persistent,\n                                                receive_large,\n                                                timeout_ms);\n  if (mr == MACH_RCV_TIMED_OUT && options.has_timeout && options.persistent &&\n      exceptions_handled) {\n    // This is not an error: when exiting on timeout during persistent\n    // processing and at least one exception was handled, it’s considered a\n    // success.\n  } else if (mr != MACH_MSG_SUCCESS) {\n    MACH_LOG(ERROR, mr) << \"MachMessageServer::Run\";\n    return EXIT_FAILURE;\n  }\n\n  return EXIT_SUCCESS;\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint main(int argc, char* argv[]) {\n  return crashpad::CatchExceptionToolMain(argc, argv);\n}\n"
  },
  {
    "path": "tools/mac/catch_exception_tool.md",
    "content": "<!--\nCopyright 2014 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# catch_exception_tool(1)\n\n## Name\n\ncatch_exception_tool—Catch Mach exceptions and display information about them\n\n## Synopsis\n\n**catch_exception_tool** **-m** _SERVICE_ [_OPTION…_]\n\n## Description\n\nRuns a Mach exception server registered with the bootstrap server under the name\n_SERVICE_. The exception server is capable of receiving exceptions for\n“behavior” values of `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, and\n`EXCEPTION_STATE_IDENTITY`, with or without `MACH_EXCEPTION_CODES` set.\n\n## Options\n\n * **-f**, **--file**=_FILE_\n\n   Information about the exception will be appended to _FILE_ instead of the\n   standard output stream.\n\n * **-m**, **--mach-service**=_SERVICE_\n\n   Check in with the bootstrap server under the name _SERVICE_. This service\n   name may already be reserved with the bootstrap server in cases where this\n   tool is started by launchd(8) as a result of a message being sent to a\n   service declared in a job’s `MachServices` dictionary (see launchd.plist(5)).\n   The service name may also be completely unknown to the system.\n\n * **-p**, **--persistent**\n\n   Continue processing exceptions after the first one. The default mode is\n   one-shot, where this tool exits after processing the first exception.\n\n * **-t**, **--timeout**=_TIMEOUT_\n\n   Run for a maximum of _TIMEOUT_ seconds. Specify `0` to request non-blocking\n   operation, in which the tool exits immediately if no exception is received.\n   In **--persistent** mode, _TIMEOUT_ applies to the overall duration that this\n   tool will run, not to the processing of individual exceptions. When\n   **--timeout** is not specified, this tool will block indefinitely while\n   waiting for an exception.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Examples\n\nRun a one-shot blocking exception server registered with the bootstrap server\nunder the name `svc`:\n\n```\n$ catch_exception_tool --mach-service=svc --file=out &\n[1] 1233\n$ exception_port_tool --set-handler=handler=bootstrap:svc crasher\nIllegal instruction: 4\n[1]+  Done    catch_exception_tool --mach-service=svc --file=out\n$ cat out\ncatch_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\n```\n\nRun an on-demand exception server started by launchd(5) available via the\nbootstrap server under the name `svc`:\n\n```\n$ `on_demand_service_tool --load --label=catch_exception \\\n      --mach-service=svc \\\n      $(which catch_exception_tool) --mach-service=svc \\\n      --file=/tmp/out --persistent --timeout=0\n$ exception_port_tool --set-handler=handler=bootstrap:svc crasher\nIllegal instruction: 4\n$ on_demand_service_tool --unload --label=catch_exception\n$ cat /tmp/out\ncatch_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\n```\n\n## Exit Status\n\n * **0**\n\n   Success. In **--persistent** mode with a **--timeout** set, it is considered\n   successful if at least one exception was caught when the timer expires.\n\n * **1**\n\n   Failure, with a message printed to the standard error stream.\n\n## See Also\n\n[exception_port_tool(1)](exception_port_tool.md),\n[on_demand_service_tool(1)](on_demand_service_tool.md)\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2014 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "tools/mac/exception_port_tool.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <errno.h>\n#include <getopt.h>\n#include <libgen.h>\n#include <mach/mach.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <string>\n#include <vector>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/apple/scoped_mach_port.h\"\n#include \"tools/tool_support.h\"\n#include \"util/mach/bootstrap.h\"\n#include \"util/mach/exception_ports.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/symbolic_constants_mach.h\"\n#include \"util/mach/task_for_pid.h\"\n#include \"util/posix/drop_privileges.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n\nnamespace crashpad {\nnamespace {\n\n//! \\brief Manages a pool of Mach send rights, deallocating all send rights upon\n//!     destruction.\n//!\n//! This class effectively implements what a vector of\n//! base::apple::ScopedMachSendRight objects would be.\n//!\n//! The various “show” operations performed by this program display Mach ports\n//! by their names as they are known in this task. For this to be useful, rights\n//! to the same ports must have consistent names across successive calls. This\n//! cannot be guaranteed if the rights are deallocated as soon as they are used,\n//! because if that deallocation causes the task to lose its last right to a\n//! port, subsequently regaining a right to the same port would cause it to be\n//! known by a new name in this task.\n//!\n//! Instead of immediately deallocating send rights that are used for display,\n//! they can be added to this pool. The pool collects send rights, ensuring that\n//! they remain alive in this task, and that subsequent calls that obtain the\n//! same rights cause them to be known by the same name. All rights are\n//! deallocated upon destruction.\nclass MachSendRightPool {\n public:\n  MachSendRightPool()\n      : send_rights_() {\n  }\n\n  MachSendRightPool(const MachSendRightPool&) = delete;\n  MachSendRightPool& operator=(const MachSendRightPool&) = delete;\n\n  ~MachSendRightPool() {\n    for (mach_port_t send_right : send_rights_) {\n      kern_return_t kr = mach_port_deallocate(mach_task_self(), send_right);\n      MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << \"mach_port_deallocate\";\n    }\n  }\n\n  //! \\brief Adds a send right to the pool.\n  //!\n  //! \\param[in] send_right The send right to be added. The pool object takes\n  //!     its own reference to the send right, which remains valid until the\n  //!     pool object is destroyed. The caller remains responsible for its\n  //!     reference to the send right.\n  //!\n  //! It is possible and in fact likely that one pool will wind up owning the\n  //! same send right multiple times. This is acceptable, because send rights\n  //! are reference-counted.\n  void AddSendRight(mach_port_t send_right) {\n    kern_return_t kr = mach_port_mod_refs(mach_task_self(),\n                                          send_right,\n                                          MACH_PORT_RIGHT_SEND,\n                                          1);\n    MACH_CHECK(kr == KERN_SUCCESS, kr) << \"mach_port_mod_refs\";\n\n    send_rights_.push_back(send_right);\n  }\n\n private:\n  std::vector<mach_port_t> send_rights_;\n};\n\nstruct ExceptionHandlerDescription {\n  ExceptionPorts::TargetType target_type;\n  exception_mask_t mask;\n  exception_behavior_t behavior;\n  thread_state_flavor_t flavor;\n  std::string handler;\n};\n\nconstexpr char kHandlerNull[] = \"NULL\";\nconstexpr char kHandlerBootstrapColon[] = \"bootstrap:\";\n\n// Populates |description| based on a textual representation in\n// |handler_string_ro|, returning true on success and false on failure (parse\n// error). The --help string describes the format of |handler_string_ro|.\n// Briefly, it is a comma-separated string that allows the members of\n// |description| to be specified as \"field=value\". Values for \"target\" can be\n// \"host\", \"task\", or \"thread\"; values for \"handler\" are of the form\n// \"bootstrap:service_name\" where service_name will be looked up with the\n// bootstrap server; and values for the other fields are interpreted by\n// SymbolicConstantsMach.\nbool ParseHandlerString(const char* handler_string_ro,\n                        ExceptionHandlerDescription* description) {\n  static constexpr char kTargetEquals[] = \"target=\";\n  static constexpr char kMaskEquals[] = \"mask=\";\n  static constexpr char kBehaviorEquals[] = \"behavior=\";\n  static constexpr char kFlavorEquals[] = \"flavor=\";\n  static constexpr char kHandlerEquals[] = \"handler=\";\n\n  std::string handler_string(handler_string_ro);\n  char* handler_string_c = &handler_string[0];\n\n  char* token;\n  while ((token = strsep(&handler_string_c, \",\")) != nullptr) {\n    if (strncmp(token, kTargetEquals, strlen(kTargetEquals)) == 0) {\n      const char* value = token + strlen(kTargetEquals);\n      if (strcmp(value, \"host\") == 0) {\n        description->target_type = ExceptionPorts::kTargetTypeHost;\n      } else if (strcmp(value, \"task\") == 0) {\n        description->target_type = ExceptionPorts::kTargetTypeTask;\n      } else if (strcmp(value, \"thread\") == 0) {\n        description->target_type = ExceptionPorts::kTargetTypeThread;\n      } else {\n        return false;\n      }\n    } else if (strncmp(token, kMaskEquals, strlen(kMaskEquals)) == 0) {\n      const char* value = token + strlen(kMaskEquals);\n      if (!StringToExceptionMask(\n              value,\n              kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr,\n              &description->mask)) {\n        return false;\n      }\n    } else if (strncmp(token, kBehaviorEquals, strlen(kBehaviorEquals)) == 0) {\n      const char* value = token + strlen(kBehaviorEquals);\n      if (!StringToExceptionBehavior(\n              value,\n              kAllowFullName | kAllowShortName | kAllowNumber,\n              &description->behavior)) {\n        return false;\n      }\n    } else if (strncmp(token, kFlavorEquals, strlen(kFlavorEquals)) == 0) {\n      const char* value = token + strlen(kFlavorEquals);\n      if (!StringToThreadStateFlavor(\n              value,\n              kAllowFullName | kAllowShortName | kAllowNumber,\n              &description->flavor)) {\n        return false;\n      }\n    } else if (strncmp(token, kHandlerEquals, strlen(kHandlerEquals)) == 0) {\n      const char* value = token + strlen(kHandlerEquals);\n      if (strcmp(value, kHandlerNull) != 0 &&\n          strncmp(value,\n                  kHandlerBootstrapColon,\n                  strlen(kHandlerBootstrapColon)) != 0) {\n        return false;\n      }\n      description->handler = std::string(value);\n    } else {\n      return false;\n    }\n  }\n\n  return true;\n}\n\n// ShowExceptionPorts() shows handlers as numeric mach_port_t values, which are\n// opaque and meaningless on their own. ShowBootstrapService() can be used to\n// look up a service with the bootstrap server by name and show its mach_port_t\n// value, which can then be associated with handlers shown by\n// ShowExceptionPorts(). Any send rights obtained by this function are added to\n// |mach_send_right_pool|.\nvoid ShowBootstrapService(const std::string& service_name,\n                          MachSendRightPool* mach_send_right_pool) {\n  base::apple::ScopedMachSendRight service_port(BootstrapLookUp(service_name));\n  if (service_port == kMachPortNull) {\n    return;\n  }\n\n  mach_send_right_pool->AddSendRight(service_port.get());\n\n  printf(\"service %s %#x\\n\", service_name.c_str(), service_port.get());\n}\n\n// Prints information about all exception ports known for |exception_ports|. If\n// |numeric| is true, all information is printed in numeric form, otherwise, it\n// will be converted to symbolic constants where possible by\n// SymbolicConstantsMach. If |is_new| is true, information will be presented as\n// “new exception ports”, indicating that they show the state of the exception\n// ports after SetExceptionPort() has been called. Any send rights obtained by\n// this function are added to |mach_send_right_pool|.\nvoid ShowExceptionPorts(const ExceptionPorts& exception_ports,\n                        bool numeric,\n                        bool is_new,\n                        MachSendRightPool* mach_send_right_pool) {\n  const char* target_name = exception_ports.TargetTypeName();\n\n  ExceptionPorts::ExceptionHandlerVector handlers;\n  if (!exception_ports.GetExceptionPorts(ExcMaskValid(), &handlers)) {\n    return;\n  }\n\n  const char* age_name = is_new ? \"new \" : \"\";\n\n  if (handlers.empty()) {\n    printf(\"no %s%s exception ports\\n\", age_name, target_name);\n  }\n\n  for (size_t port_index = 0; port_index < handlers.size(); ++port_index) {\n    mach_send_right_pool->AddSendRight(handlers[port_index].port);\n\n    if (numeric) {\n      printf(\n          \"%s%s exception port %zu, mask %#x, port %#x, \"\n          \"behavior %#x, flavor %u\\n\",\n          age_name,\n          target_name,\n          port_index,\n          handlers[port_index].mask,\n          handlers[port_index].port,\n          handlers[port_index].behavior,\n          handlers[port_index].flavor);\n    } else {\n      std::string mask_string = ExceptionMaskToString(\n          handlers[port_index].mask, kUseShortName | kUnknownIsEmpty | kUseOr);\n      if (mask_string.empty()) {\n        mask_string.assign(\"?\");\n      }\n\n      std::string behavior_string = ExceptionBehaviorToString(\n          handlers[port_index].behavior, kUseShortName | kUnknownIsEmpty);\n      if (behavior_string.empty()) {\n        behavior_string.assign(\"?\");\n      }\n\n      std::string flavor_string = ThreadStateFlavorToString(\n          handlers[port_index].flavor, kUseShortName | kUnknownIsEmpty);\n      if (flavor_string.empty()) {\n        flavor_string.assign(\"?\");\n      }\n\n      printf(\n          \"%s%s exception port %zu, mask %#x (%s), port %#x, \"\n          \"behavior %#x (%s), flavor %u (%s)\\n\",\n          age_name,\n          target_name,\n          port_index,\n          handlers[port_index].mask,\n          mask_string.c_str(),\n          handlers[port_index].port,\n          handlers[port_index].behavior,\n          behavior_string.c_str(),\n          handlers[port_index].flavor,\n          flavor_string.c_str());\n    }\n  }\n}\n\n// Sets the exception port for |target_port|, a send right to a thread, task, or\n// host port, to |description|, which identifies what type of port |target_port|\n// is and describes an exception port to be set. Returns true on success.\n//\n// This function may be called more than once if setting different handlers is\n// desired.\nbool SetExceptionPort(const ExceptionHandlerDescription* description,\n                      mach_port_t target_port) {\n  base::apple::ScopedMachSendRight service_port;\n  if (description->handler.compare(\n          0, strlen(kHandlerBootstrapColon), kHandlerBootstrapColon) == 0) {\n    const char* service_name =\n        description->handler.c_str() + strlen(kHandlerBootstrapColon);\n    service_port = BootstrapLookUp(service_name);\n    if (service_port == kMachPortNull) {\n      return false;\n    }\n\n    // The service port doesn’t need to be added to a MachSendRightPool because\n    // it’s not used for display at all. ScopedMachSendRight is sufficient.\n  } else if (description->handler != kHandlerNull) {\n    return false;\n  }\n\n  ExceptionPorts exception_ports(description->target_type, target_port);\n  if (!exception_ports.SetExceptionPort(description->mask,\n                                        service_port.get(),\n                                        description->behavior,\n                                        description->flavor)) {\n    return false;\n  }\n\n  return true;\n}\n\nvoid Usage(const std::string& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %s [OPTION]... [COMMAND [ARG]...]\\n\"\n\"View and change Mach exception ports, and run COMMAND if supplied.\\n\"\n\"\\n\"\n\"  -s, --set-handler=DESCRIPTION  set an exception port to DESCRIPTION, see below\\n\"\n\"      --show-bootstrap=SERVICE   look up and display a service registered with\\n\"\n\"                                 the bootstrap server\\n\"\n\"  -p, --pid=PID                  operate on PID instead of the current task\\n\"\n\"  -h, --show-host                display original host exception ports\\n\"\n\"  -t, --show-task                display original task exception ports\\n\"\n\"      --show-thread              display original thread exception ports\\n\"\n\"  -H, --show-new-host            display modified host exception ports\\n\"\n\"  -T, --show-new-task            display modified task exception ports\\n\"\n\"      --show-new-thread          display modified thread exception ports\\n\"\n\"  -n, --numeric                  display values numerically, not symbolically\\n\"\n\"      --help                     display this help and exit\\n\"\n\"      --version                  output version information and exit\\n\"\n\"\\n\"\n\"Any operations on host exception ports require superuser permissions.\\n\"\n\"\\n\"\n\"DESCRIPTION is formatted as a comma-separated sequence of tokens, where each\\n\"\n\"token consists of a key and value separated by an equals sign. Available keys:\\n\"\n\"  target    which target's exception ports to set: host, task, or thread\\n\"\n\"  mask      the mask of exception types to handle: CRASH, ALL, or others\\n\"\n\"  behavior  the specific exception handler routine to call: DEFAULT, STATE,\\n\"\n\"            or STATE_IDENTITY, possibly with MACH_EXCEPTION_CODES.\\n\"\n\"  flavor    the thread state flavor passed to the handler: architecture-specific\\n\"\n\"  handler   the exception handler: NULL or bootstrap:SERVICE, indicating that\\n\"\n\"            the handler should be looked up with the bootstrap server\\n\"\n\"The default DESCRIPTION is\\n\"\n\"  target=task,mask=CRASH,behavior=DEFAULT|MACH,flavor=NONE,handler=NULL\\n\",\n          me.c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nint ExceptionPortToolMain(int argc, char* argv[]) {\n  const std::string me(basename(argv[0]));\n\n  enum ExitCode {\n    kExitSuccess = EXIT_SUCCESS,\n\n    // To differentiate this tool’s errors from errors in the programs it execs,\n    // use a high exit code for ordinary failures instead of EXIT_FAILURE. This\n    // is the same rationale for using the distinct exit codes for exec\n    // failures.\n    kExitFailure = 125,\n\n    // Like env, use exit code 126 if the program was found but could not be\n    // invoked, and 127 if it could not be found.\n    // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html\n    kExitExecFailure = 126,\n    kExitExecENOENT = 127,\n  };\n\n  enum OptionFlags {\n    // “Short” (single-character) options.\n    kOptionSetPort = 's',\n    kOptionPid = 'p',\n    kOptionShowHost = 'h',\n    kOptionShowTask = 't',\n    kOptionShowNewHost = 'H',\n    kOptionShowNewTask = 'T',\n    kOptionNumeric = 'n',\n\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n    kOptionShowBootstrap,\n    kOptionShowThread,\n    kOptionShowNewThread,\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  struct {\n    std::vector<const char*> show_bootstrap;\n    std::vector<ExceptionHandlerDescription> set_handler;\n    pid_t pid;\n    task_t alternate_task;\n    bool show_host;\n    bool show_task;\n    bool show_thread;\n    bool show_new_host;\n    bool show_new_task;\n    bool show_new_thread;\n    bool numeric;\n  } options = {};\n\n  static constexpr option long_options[] = {\n      {\"set-handler\", required_argument, nullptr, kOptionSetPort},\n      {\"show-bootstrap\", required_argument, nullptr, kOptionShowBootstrap},\n      {\"pid\", required_argument, nullptr, kOptionPid},\n      {\"show-host\", no_argument, nullptr, kOptionShowHost},\n      {\"show-task\", no_argument, nullptr, kOptionShowTask},\n      {\"show-thread\", no_argument, nullptr, kOptionShowThread},\n      {\"show-new-host\", no_argument, nullptr, kOptionShowNewHost},\n      {\"show-new-task\", no_argument, nullptr, kOptionShowNewTask},\n      {\"show-new-thread\", no_argument, nullptr, kOptionShowNewThread},\n      {\"numeric\", no_argument, nullptr, kOptionNumeric},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"+s:p:htHTn\", long_options, nullptr)) !=\n         -1) {\n    switch (opt) {\n      case kOptionSetPort: {\n        options.set_handler.push_back({});\n        ExceptionHandlerDescription* description = &options.set_handler.back();\n        description->target_type = ExceptionPorts::kTargetTypeTask;\n        description->mask = EXC_MASK_CRASH;\n        description->behavior = EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES;\n        description->flavor = THREAD_STATE_NONE;\n        description->handler = \"NULL\";\n        if (!ParseHandlerString(optarg, description)) {\n          fprintf(stderr,\n                  \"%s: invalid exception handler: %s\\n\",\n                  me.c_str(),\n                  optarg);\n          return kExitFailure;\n        }\n        break;\n      }\n      case kOptionShowBootstrap:\n        options.show_bootstrap.push_back(optarg);\n        break;\n      case kOptionPid:\n        if (!StringToNumber(optarg, &options.pid)) {\n          fprintf(stderr, \"%s: invalid pid: %s\\n\", me.c_str(), optarg);\n          return kExitFailure;\n        }\n        break;\n      case kOptionShowHost:\n        options.show_host = true;\n        break;\n      case kOptionShowTask:\n        options.show_task = true;\n        break;\n      case kOptionShowThread:\n        options.show_thread = true;\n        break;\n      case kOptionShowNewHost:\n        options.show_new_host = true;\n        break;\n      case kOptionShowNewTask:\n        options.show_new_task = true;\n        break;\n      case kOptionShowNewThread:\n        options.show_new_thread = true;\n        break;\n      case kOptionNumeric:\n        options.numeric = true;\n        break;\n      case kOptionHelp:\n        Usage(me);\n        return kExitSuccess;\n      case kOptionVersion:\n        ToolSupport::Version(me);\n        return kExitSuccess;\n      default:\n        ToolSupport::UsageHint(me, nullptr);\n        return kExitFailure;\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n  if (options.show_bootstrap.empty() && !options.show_host &&\n      !options.show_task && !options.show_thread &&\n      options.set_handler.empty() && argc == 0) {\n    ToolSupport::UsageHint(me, \"nothing to do\");\n    return kExitFailure;\n  }\n\n  base::apple::ScopedMachSendRight alternate_task_owner;\n  if (options.pid) {\n    if (argc) {\n      ToolSupport::UsageHint(me, \"cannot combine -p with COMMAND\");\n      return kExitFailure;\n    }\n\n    options.alternate_task = TaskForPID(options.pid);\n    if (options.alternate_task == TASK_NULL) {\n      return kExitFailure;\n    }\n    alternate_task_owner.reset(options.alternate_task);\n  }\n\n  // This tool may have been installed as a setuid binary so that TaskForPID()\n  // could succeed. Drop any privileges now that they’re no longer necessary.\n  DropPrivileges();\n\n  MachSendRightPool mach_send_right_pool;\n\n  // Show bootstrap services requested.\n  for (const char* service : options.show_bootstrap) {\n    ShowBootstrapService(service, &mach_send_right_pool);\n  }\n\n  // Show the original exception ports.\n  if (options.show_host) {\n    ShowExceptionPorts(\n        ExceptionPorts(ExceptionPorts::kTargetTypeHost, HOST_NULL),\n        options.numeric,\n        false,\n        &mach_send_right_pool);\n  }\n  if (options.show_task) {\n    ShowExceptionPorts(\n        ExceptionPorts(ExceptionPorts::kTargetTypeTask, options.alternate_task),\n        options.numeric,\n        false,\n        &mach_send_right_pool);\n  }\n  if (options.show_thread) {\n    ShowExceptionPorts(\n        ExceptionPorts(ExceptionPorts::kTargetTypeThread, THREAD_NULL),\n        options.numeric,\n        false,\n        &mach_send_right_pool);\n  }\n\n  if (!options.set_handler.empty()) {\n    // Set new exception handlers.\n    for (ExceptionHandlerDescription description : options.set_handler) {\n      if (!SetExceptionPort(\n              &description,\n              description.target_type == ExceptionPorts::kTargetTypeTask\n                  ? options.alternate_task\n                  : TASK_NULL)) {\n        return kExitFailure;\n      }\n    }\n\n    // Show changed exception ports.\n    if (options.show_new_host) {\n      ShowExceptionPorts(\n          ExceptionPorts(ExceptionPorts::kTargetTypeHost, HOST_NULL),\n          options.numeric,\n          true,\n          &mach_send_right_pool);\n    }\n    if (options.show_new_task) {\n      ShowExceptionPorts(\n          ExceptionPorts(ExceptionPorts::kTargetTypeTask,\n                         options.alternate_task),\n          options.numeric,\n          true,\n          &mach_send_right_pool);\n    }\n    if (options.show_new_thread) {\n      ShowExceptionPorts(\n          ExceptionPorts(ExceptionPorts::kTargetTypeThread, THREAD_NULL),\n          options.numeric,\n          true,\n          &mach_send_right_pool);\n    }\n  }\n\n  if (argc) {\n    // Using the remaining arguments, start a new program with the new set of\n    // exception ports in effect.\n    execvp(argv[0], argv);\n    PLOG(ERROR) << \"execvp \" << argv[0];\n    return errno == ENOENT ? kExitExecENOENT : kExitExecFailure;\n  }\n\n  return kExitSuccess;\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint main(int argc, char* argv[]) {\n  return crashpad::ExceptionPortToolMain(argc, argv);\n}\n"
  },
  {
    "path": "tools/mac/exception_port_tool.md",
    "content": "<!--\nCopyright 2014 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# exception_port_tool(1)\n\n## Name\n\nexception_port_tool—Show and change Mach exception ports\n\n## Synopsis\n\n**exception_port_tool** [_OPTION…_] [_COMMAND_ [_ARG…_]]\n\n## Description\n\nShows Mach exception ports registered for a thread, task, or host target with a\n__--show-*__ option, changes Mach exception ports with **--set-handler**, shows\nchanges with a __--show-new-*__ option, and executes _COMMAND_ along with any\narguments specified (_ARG…_) with the changed exception ports in effect.\n\n## Options\n\n * **-s**, **--set-handler**=_DESCRIPTION_\n\n   Set an exception port to _DESCRIPTION_. This option may appear zero, one, or\n   more times.\n\n   _DESCRIPTION_ is formatted as a comma-separated sequence of tokens, where\n   each token consists of a key and value separated by an equals sign. These\n   keys are recognized:\n\n    * **target**=_TARGET_\n\n      _TARGET_ defines which target’s exception ports to set: **host**,\n      **task**, or **thread**. The default value of _TARGET_ is **task**.\n      Operations on **host** are restricted to the superuser.\n\n    * **mask**=_MASK_\n\n      _MASK_ defines the mask of exception types to handle, from\n      `<mach/exception_types.h>`. This can be **BAD_ACCESS**,\n      **BAD_INSTRUCTION**, **ARITHMETIC**, **EMULATION**, **SOFTWARE**,\n      **BREAKPOINT**, **SYSCALL**, **MACH_SYSCALL**, **RPC_ALERT**, **CRASH**,\n      **RESOURCE**, **GUARD**, or **CORPSE_NOTIFY**. Different exception types\n      may be combined by combining them with pipe characters (**|**). The\n      special value **ALL** includes each exception type except for **CRASH**\n      and **CORPSE_NOTIFY**. To truly specify all exception types including\n      these, use **ALL|CRASH|CORPSE_NOTIFY**. The default value of _MASK_ is\n      **CRASH**.\n\n    * **behavior**=_BEHAVIOR_\n\n      _BEHAVIOR_ defines the specific exception handler routine to be called\n      when an exception occurs. This can be **DEFAULT**, **STATE**, or\n      **STATE_IDENTITY**. **MACH** may also be specified by combining them with\n      pipe characters (**|**). The most complete set of exception information is\n      provided with **STATE_IDENTITY|MACH**. Not all exception servers implement\n      all possible behaviors. The default value of _BEHAVIOR_ is\n      **DEFAULT|MACH**.\n\n    * **flavor**=_FLAVOR_\n\n      For state-carrying values of _BEHAVIOR_ (those including **STATE** or\n      **STATE_IDENTITY**), _FLAVOR_ specifies the architecture-specific thread\n      state flavor to be provided to the exception handler. For the x86 family,\n      this can be **THREAD**, **THREAD32**, **THREAD64**, **FLOAT**,\n      **FLOAT32**, **FLOAT64**, **DEBUG**, **DEBUG32**, or **DEBUG64**. The\n      default value of _FLAVOR_ is **NONE**, which is not valid for\n      state-carrying values of _BEHAVIOR_.\n\n    * **handler**=_HANDLER_\n\n      _HANDLER_ defines the exception handler. **NULL** indicates that any\n      existing exception port should be cleared. _HANDLER_ may also take the\n      form **bootstrap**:_SERVICE_, which will look _SERVICE_ up with the\n      bootstrap server and set that service as the exception handler. The\n      default value of _HANDLER_ is **NULL**.\n\n * **--show-bootstrap**=_SERVICE_\n\n   Looks up _SERVICE_ with the bootstrap server and shows it. Normally, the\n   handler port values displayed by the other __--show-*__ options are\n   meaningless handles, but by comparing them to the port values for known\n   bootstrap services, it is possible to verify that they are set as intended.\n\n * **-p**, **--pid**=_PID_\n\n   For operations on the task target, including **--set-handler** with _TARGET_\n   set to **task**, **--show-task**, and **--show-new-task**, operates on the\n   task associated with process id _PID_ instead of the current task associated\n   with the tool. When this option is supplied, _COMMAND_ must not be specified.\n\n   This option uses `task_for_pid()` to access the process’ task port. This\n   operation may be restricted to use by the superuser, executables signed by an\n   authority trusted by the system, and processes otherwise permitted by\n   taskgated(8). Consequently, this program must normally either be signed or be\n   invoked by root to use this option. It is possible to install this program as\n   a setuid root executable to overcome this limitation. However, it is not\n   possible to use this option to operate on processes protected by [System\n   Integrity Protection (SIP)](https://support.apple.com/HT204899), including\n   those whose “restrict” codesign(1) option is respected.\n\n * **-h**, **--show-host**\n\n   Shows the original host exception ports before making any changes requested\n   by **--set-handler**. This option is restricted to the superuser.\n\n * **-t**, **--show-task**\n\n   Shows the original task exception ports before making any changes requested\n   by **--set-handler**.\n\n * **--show-thread**\n\n   Shows the original thread exception ports before making any changes requested\n   by **--set-handler**.\n\n * **-H**, **--show-new-host**\n\n   Shows the modified host exception ports after making any changes requested by\n   **--set-handler**. This option is restricted to the superuser.\n\n * **-T**, **--show-new-task**\n\n   Shows the modified task exception ports after making any changes requested by\n   **--set-handler**.\n\n * **--show-new-thread**\n\n   Shows the modified thread exception ports after making any changes requested\n   by **--set-handler**\n\n * **-n**, **--numeric**\n\n   For __--show-*__ options, all values will be displayed numerically only. The\n   default is to decode numeric values and display them symbolically as well.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Examples\n\nSets a new task-level exception handler for `EXC_CRASH`-type exceptions to the\nhandler registered with the bootstrap server as `svc`, showing the task-level\nexception ports before and after the change. The old and new exception handlers\nare verified by their service names as registered with the bootstrap server.\nWith the new task-level exception ports in effect, a program is run.\n\n```\n$ exception_port_tool --show-task --show-new-task \\\n      --show-bootstrap=com.apple.ReportCrash --show-bootstrap=svc \\\n      --set-handler=behavior=DEFAULT,handler=bootstrap:svc crash\nservice com.apple.ReportCrash 0xe03\nservice svc 0x1003\ntask exception port 0, mask 0x400 (CRASH), port 0xe03, behavior 0x80000003 (STATE_IDENTITY|MACH), flavor 7 (THREAD)\nnew task exception port 0, mask 0x400 (CRASH), port 0x1003, behavior 0x1 (DEFAULT), flavor 13 (NONE)\nIllegal instruction: 4\n```\n\nShows the task-level exception ports for the process with PID 1234. This\nrequires superuser permissions or the approval of taskgated(8), and the process\nmust not be SIP-protected.\n\n```\n# exception_port_tool --pid=1234 --show-task\ntask exception port 0, mask 0x4e (BAD_ACCESS|BAD_INSTRUCTION|ARITHMETIC|BREAKPOINT), port 0x1503, behavior 0x1 (DEFAULT), flavor 13 (NONE)\ntask exception port 1, mask 0x1c00 (CRASH|RESOURCE|GUARD), port 0x1403, behavior 0x80000003 (STATE_IDENTITY|MACH), flavor 7 (THREAD)\n```\n\n## Exit Status\n\n * **0**\n\n   Success.\n\n * **125**\n\n   Failure, with a message printed to the standard error stream.\n\n * **126**\n\n   The program specified by _COMMAND_ was found, but could not be invoked.\n\n * **127**\n\n   The program specified by _COMMAND_ could not be found.\n\n## See Also\n\n[catch_exception_tool(1)](catch_exception_tool.md),\n[on_demand_service_tool(1)](on_demand_service_tool.md)\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2014 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "tools/mac/on_demand_service_tool.md",
    "content": "<!--\nCopyright 2014 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# on_demand_service_tool(1)\n\n## Name\n\non_demand_service_tool—Load and unload on-demand Mach services registered with\nlaunchd(8)\n\n## Synopsis\n\n**on_demand_service_tool** **-L** **-l** _LABEL_ [_OPTION…_] _COMMAND_\n[_ARG…_]<br/>\n**on_demand_service_tool** **-U** **-l** _LABEL_\n\n## Description\n\nOn-demand services may be registered with launchd(8) by using the **--load**\nform. One or more service names may be registered with the bootstrap server by\nspecifying **--mach-service**. When a Mach message is sent to any of these\nservices, launchd(8) will invoke _COMMAND_ along with any arguments specified\n(_ARG…_). _COMMAND_ must be an absolute pathname.\n\nThe **--unload** form unregisters jobs registered with launchd(8).\n\n## Options\n\n * **-L**, **--load**\n\n   Registers a job with launchd(8). **--label**=_LABEL_ and _COMMAND_ are\n   required. This operation may also be referred to as “load” or “submit”.\n\n * **-U**, **--unload**\n\n   Unregisters a job with launchd(8). **--label**=_LABEL_ is required. This\n   operation may also be referred to as “unload” or “remove”.\n\n * **-l**, **--label**=_LABEL_\n\n   _LABEL_ is used as the job label to identify the job to launchd(8). _LABEL_\n   must be unique within a launchd(8) context.\n\n * **-m**, **--mach-service**=_SERVICE_\n\n   In conjunction with **--load**, registers _SERVICE_ with the bootstrap\n   server. Clients will be able to obtain a send right by looking up the\n   _SERVICE_ name with the bootstrap server. When a message is sent to such a\n   Mach port, launchd(8) will invoke _COMMAND_ along with any arguments\n   specified (_ARG…_) if it is not running. This forms the “on-demand” nature\n   referenced by this tool’s name. This option may appear zero, one, or more\n   times. _SERVICE_ must be unique within a bootstrap context.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Examples\n\nRegisters an on-demand server that will execute\n[catch_exception_tool(1)](catch_exception_tool.md) when a Mach message is sent\nto a Mach port obtained from the bootstrap server by looking up the name `svc`:\n\n```\n$ on_demand_service_tool --load --label=catch_exception \\\n      --mach-service=svc \\\n      $(which catch_exception_tool) --mach-service=svc \\\n      --file=/tmp/out --persistent --timeout=0\n```\n\nUnregisters the on-demand server installed above:\n\n```\n$ on_demand_service_tool --unload --label=catch_exception\n```\n\n## Exit Status\n\n * **0**\n\n   Success.\n\n * **1**\n\n   Failure, with a message printed to the standard error stream.\n\n## See Also\n\n[catch_exception_tool(1)](catch_exception_tool.md),\n[exception_port_tool(1)](exception_port_tool.md),\nlaunchctl(1)\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2014 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "tools/mac/on_demand_service_tool.mm",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <CoreFoundation/CoreFoundation.h>\n#import <Foundation/Foundation.h>\n#include <getopt.h>\n#include <launch.h>\n#include <libgen.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include <string>\n#include <vector>\n\n#include \"base/apple/bridging.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"tools/tool_support.h\"\n#include \"util/mac/service_management.h\"\n#include \"util/stdlib/objc.h\"\n\nnamespace crashpad {\nnamespace {\n\nvoid Usage(const std::string& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %s -L -l LABEL [OPTION]... COMMAND [ARG]...\\n\"\n\"       %s -U -l LABEL\\n\"\n\"Load and unload on-demand Mach services from launchd.\\n\"\n\"\\n\"\n\"  -L, --load                  load (submit) the job identified by --label;\\n\"\n\"                              COMMAND must be specified\\n\"\n\"  -U, --unload                unload (remove) the job identified by --label\\n\"\n\"  -l, --label=LABEL           identify the job to launchd with LABEL\\n\"\n\"  -m, --mach-service=SERVICE  register SERVICE with the bootstrap server\\n\"\n\"      --help                  display this help and exit\\n\"\n\"      --version               output version information and exit\\n\",\n          me.c_str(),\n          me.c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nint OnDemandServiceToolMain(int argc, char* argv[]) {\n  const std::string me(basename(argv[0]));\n\n  enum Operation {\n    kOperationUnknown = 0,\n    kOperationLoadJob,\n    kOperationUnloadJob,\n  };\n\n  enum OptionFlags {\n    // “Short” (single-character) options.\n    kOptionLoadJob = 'L',\n    kOptionUnloadJob = 'U',\n    kOptionJobLabel = 'l',\n    kOptionMachService = 'm',\n\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  struct {\n    Operation operation;\n    std::string job_label;\n    std::vector<std::string> mach_services;\n  } options = {};\n\n  static constexpr option long_options[] = {\n      {\"load\", no_argument, nullptr, kOptionLoadJob},\n      {\"unload\", no_argument, nullptr, kOptionUnloadJob},\n      {\"label\", required_argument, nullptr, kOptionJobLabel},\n      {\"mach-service\", required_argument, nullptr, kOptionMachService},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"+LUl:m:\", long_options, nullptr)) !=\n          -1) {\n    switch (opt) {\n      case kOptionLoadJob:\n        options.operation = kOperationLoadJob;\n        break;\n      case kOptionUnloadJob:\n        options.operation = kOperationUnloadJob;\n        break;\n      case kOptionJobLabel:\n        options.job_label = optarg;\n        break;\n      case kOptionMachService:\n        options.mach_services.push_back(optarg);\n        break;\n      case kOptionHelp:\n        Usage(me);\n        return EXIT_SUCCESS;\n      case kOptionVersion:\n        ToolSupport::Version(me);\n        return EXIT_SUCCESS;\n      default:\n        ToolSupport::UsageHint(me, nullptr);\n        return EXIT_FAILURE;\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n  if (options.job_label.empty()) {\n    ToolSupport::UsageHint(me, \"must provide -l\");\n    return EXIT_FAILURE;\n  }\n\n  switch (options.operation) {\n    case kOperationLoadJob: {\n      if (argc == 0) {\n        ToolSupport::UsageHint(me, \"must provide COMMAND with -L\");\n        return EXIT_FAILURE;\n      }\n\n      @autoreleasepool {\n        NSString* job_label = base::SysUTF8ToNSString(options.job_label);\n\n        NSMutableArray* command = [NSMutableArray arrayWithCapacity:argc];\n        for (int index = 0; index < argc; ++index) {\n          NSString* argument = base::SysUTF8ToNSString(argv[index]);\n          [command addObject:argument];\n        }\n\n        NSDictionary* job_dictionary = @{\n          @LAUNCH_JOBKEY_LABEL : job_label,\n          @LAUNCH_JOBKEY_PROGRAMARGUMENTS : command,\n        };\n\n        if (!options.mach_services.empty()) {\n          NSMutableDictionary* mach_services = [NSMutableDictionary\n              dictionaryWithCapacity:options.mach_services.size()];\n          for (std::string mach_service : options.mach_services) {\n            NSString* mach_service_ns = base::SysUTF8ToNSString(mach_service);\n            mach_services[mach_service_ns] = @YES;\n          }\n\n          NSMutableDictionary* mutable_job_dictionary =\n              [job_dictionary mutableCopy];\n          mutable_job_dictionary[@LAUNCH_JOBKEY_MACHSERVICES] = mach_services;\n          job_dictionary = mutable_job_dictionary;\n        }\n\n        CFDictionaryRef job_dictionary_cf =\n            base::apple::NSToCFPtrCast(job_dictionary);\n        if (!ServiceManagementSubmitJob(job_dictionary_cf)) {\n          fprintf(stderr, \"%s: failed to submit job\\n\", me.c_str());\n          return EXIT_FAILURE;\n        }\n      }\n\n      return EXIT_SUCCESS;\n    }\n\n    case kOperationUnloadJob: {\n      if (!ServiceManagementRemoveJob(options.job_label, true)) {\n        fprintf(stderr, \"%s: failed to remove job\\n\", me.c_str());\n        return EXIT_FAILURE;\n      }\n\n      return EXIT_SUCCESS;\n    }\n\n    default: {\n      ToolSupport::UsageHint(me, \"must provide -L or -U\");\n      return EXIT_FAILURE;\n    }\n  }\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint main(int argc, char* argv[]) {\n  return crashpad::OnDemandServiceToolMain(argc, argv);\n}\n"
  },
  {
    "path": "tools/mac/sectaskaccess_info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>SecTaskAccess</key>\n\t<array>\n\t\t<string>allowed</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "tools/run_with_crashpad.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <errno.h>\n#include <getopt.h>\n#include <libgen.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"client/crashpad_client.h\"\n#include \"tools/tool_support.h\"\n#include \"util/stdlib/map_insert.h\"\n#include \"util/string/split_string.h\"\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include <lib/fdio/spawn.h>\n#include <zircon/process.h>\n#include <zircon/syscalls.h>\n\n#include \"base/fuchsia/fuchsia_logging.h\"\n#endif\n\nnamespace crashpad {\nnamespace {\n\nvoid Usage(const std::string& me) {\n  // clang-format off\n  fprintf(stderr,\n\"Usage: %s [OPTION]... COMMAND [ARG]...\\n\"\n\"Start a Crashpad handler and have it handle crashes from COMMAND.\\n\"\n\"\\n\"\n  // clang-format on\n#if BUILDFLAG(IS_FUCHSIA)\n          // clang-format off\n\"COMMAND is run via fdio_spawn, so must be a qualified path to the subprocess to\\n\"\n\"be executed.\\n\"\n  // clang-format on\n#else\n          // clang-format off\n\"COMMAND is run via execvp() so the PATH will be searched.\\n\"\n  // clang-format on\n#endif\n          // clang-format off\n\"\\n\"\n\"  -h, --handler=HANDLER       invoke HANDLER instead of crashpad_handler\\n\"\n\"      --annotation=KEY=VALUE  passed to the handler as an --annotation argument\\n\"\n\"      --database=PATH         passed to the handler as its --database argument\\n\"\n\"      --url=URL               passed to the handler as its --url argument\\n\"\n\"  -a, --argument=ARGUMENT     invoke the handler with ARGUMENT\\n\"\n\"      --help                  display this help and exit\\n\"\n\"      --version               output version information and exit\\n\",\n          me.c_str());\n  // clang-format on\n  ToolSupport::UsageTail(me);\n}\n\nint RunWithCrashpadMain(int argc, char* argv[]) {\n  const std::string me(basename(argv[0]));\n\n  enum ExitCode {\n    kExitSuccess = EXIT_SUCCESS,\n\n    // To differentiate this tool’s errors from errors in the programs it execs,\n    // use a high exit code for ordinary failures instead of EXIT_FAILURE. This\n    // is the same rationale for using the distinct exit codes for exec\n    // failures.\n    kExitFailure = 125,\n\n    // Like env, use exit code 126 if the program was found but could not be\n    // invoked, and 127 if it could not be found.\n    // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/env.html\n    kExitExecFailure = 126,\n    kExitExecENOENT = 127,\n  };\n\n  enum OptionFlags {\n    // “Short” (single-character) options.\n    kOptionHandler = 'h',\n    kOptionArgument = 'a',\n\n    // Long options without short equivalents.\n    kOptionLastChar = 255,\n    kOptionAnnotation,\n    kOptionDatabase,\n    kOptionURL,\n\n    // Standard options.\n    kOptionHelp = -2,\n    kOptionVersion = -3,\n  };\n\n  static constexpr option long_options[] = {\n      {\"handler\", required_argument, nullptr, kOptionHandler},\n      {\"annotation\", required_argument, nullptr, kOptionAnnotation},\n      {\"database\", required_argument, nullptr, kOptionDatabase},\n      {\"url\", required_argument, nullptr, kOptionURL},\n      {\"argument\", required_argument, nullptr, kOptionArgument},\n      {\"help\", no_argument, nullptr, kOptionHelp},\n      {\"version\", no_argument, nullptr, kOptionVersion},\n      {nullptr, 0, nullptr, 0},\n  };\n\n  struct {\n    std::string handler;\n    std::map<std::string, std::string> annotations;\n    std::string database;\n    std::string url;\n    std::vector<std::string> arguments;\n  } options = {};\n  options.handler = \"crashpad_handler\";\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"+a:h:\", long_options, nullptr)) !=\n         -1) {\n    switch (opt) {\n      case kOptionHandler: {\n        options.handler = optarg;\n        break;\n      }\n      case kOptionAnnotation: {\n        std::string key;\n        std::string value;\n        if (!SplitStringFirst(optarg, '=', &key, &value)) {\n          ToolSupport::UsageHint(me, \"--annotation requires KEY=VALUE\");\n          return EXIT_FAILURE;\n        }\n        std::string old_value;\n        if (!MapInsertOrReplace(&options.annotations, key, value, &old_value)) {\n          LOG(WARNING) << \"duplicate key \" << key << \", discarding value \"\n                       << old_value;\n        }\n        break;\n      }\n      case kOptionDatabase: {\n        options.database = optarg;\n        break;\n      }\n      case kOptionURL: {\n        options.url = optarg;\n        break;\n      }\n      case kOptionArgument: {\n        options.arguments.push_back(optarg);\n        break;\n      }\n      case kOptionHelp: {\n        Usage(me);\n        return kExitSuccess;\n      }\n      case kOptionVersion: {\n        ToolSupport::Version(me);\n        return kExitSuccess;\n      }\n      default: {\n        ToolSupport::UsageHint(me, nullptr);\n        return kExitFailure;\n      }\n    }\n  }\n  argc -= optind;\n  argv += optind;\n\n  if (!argc) {\n    ToolSupport::UsageHint(me, \"COMMAND is required\");\n    return kExitFailure;\n  }\n\n  // Start the handler process and direct exceptions to it.\n  CrashpadClient crashpad_client;\n  if (!crashpad_client.StartHandler(base::FilePath(options.handler),\n                                    base::FilePath(options.database),\n                                    base::FilePath(),\n                                    options.url,\n                                    options.annotations,\n                                    options.arguments,\n                                    false,\n                                    false)) {\n    return kExitFailure;\n  }\n\n#if BUILDFLAG(IS_FUCHSIA)\n  // Fuchsia doesn't implement execvp(), launch with fdio_spawn here.\n  zx_handle_t child = ZX_HANDLE_INVALID;\n  zx_status_t status = fdio_spawn(\n      ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, argv[0], argv, &child);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"fdio_spawn failed\";\n    return status == ZX_ERR_IO ? kExitExecENOENT : kExitExecFailure;\n  }\n\n  zx_signals_t observed;\n  status = zx_object_wait_one(\n      child, ZX_TASK_TERMINATED, ZX_TIME_INFINITE, &observed);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_object_wait_one\";\n    return kExitExecFailure;\n  }\n  if (!(observed & ZX_TASK_TERMINATED)) {\n    LOG(ERROR) << \"did not observe ZX_TASK_TERMINATED\";\n    return kExitExecFailure;\n  }\n\n  zx_info_process_t proc_info;\n  status = zx_object_get_info(\n      child, ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_object_get_info\";\n    return kExitExecFailure;\n  }\n\n  return proc_info.return_code;\n#else\n  // Using the remaining arguments, start a new program with the new exception\n  // port in effect.\n  execvp(argv[0], argv);\n  PLOG(ERROR) << \"execvp \" << argv[0];\n  return errno == ENOENT ? kExitExecENOENT : kExitExecFailure;\n#endif\n}\n\n}  // namespace\n}  // namespace crashpad\n\nint main(int argc, char* argv[]) {\n  return crashpad::RunWithCrashpadMain(argc, argv);\n}\n"
  },
  {
    "path": "tools/run_with_crashpad.md",
    "content": "<!--\nCopyright 2014 The Crashpad Authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n\n# run_with_crashpad(1)\n\n## Name\n\nrun_with_crashpad—Run a program with a Crashpad exception handler\n\n## Synopsis\n\n**run_with_crashpad** [_OPTION…_] _COMMAND_ [_ARG…_]\n\n## Description\n\nStarts a Crashpad exception handler server such as\n[crashpad_handler(8)](../handler/crashpad_handler.md) and becomes its client,\nsetting an exception port referencing the handler. Then, executes _COMMAND_\nalong with any arguments specified (_ARG…_) with the new exception port in\neffect.\n\nOn macOS, the exception port is configured to receive exceptions of type\n`EXC_CRASH`, `EXC_RESOURCE`, and `EXC_GUARD` (until macOS 13). The exception\nbehavior is configured as `EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES`. The\nthread state flavor is set to `MACHINE_THREAD_STATE`.\n\nPrograms that use the Crashpad client library directly will not normally use\nthis tool. This tool exists to allow programs that are unaware of Crashpad to be\nrun with a Crashpad exception handler.\n\n## Options\n\n * **-h**, **--handler**=_HANDLER_\n\n   Invoke _HANDLER_ as the Crashpad handler program instead of the default,\n   **crashpad_handler**.\n\n * **--annotation**=_KEY=VALUE_\n\n   Passed to the Crashpad handler program as an **--annotation** argument.\n\n * **--database**=_PATH_\n\n   Passed to the Crashpad handler program as its **--database** argument.\n\n * **--url**=_URL_\n\n   Passed to the Crashpad handler program as its **--url** argument.\n\n * **-a**, **--argument**=_ARGUMENT_\n\n   Invokes the Crashpad handler program with _ARGUMENT_ as one of its arguments.\n   This option may appear zero, one, or more times. If this program has a\n   specific option such as **--database** matching the desired Crashpad handler\n   program option, the specific option should be used in preference to\n   **--argument**. Regardless of this option’s presence, the handler will always\n   be invoked with the necessary arguments to perform a handshake.\n\n * **--help**\n\n   Display help and exit.\n\n * **--version**\n\n   Output version information and exit.\n\n## Examples\n\nStarts a Crashpad exception handler server by its default name,\n**crashpad_handler**, and runs a program with this handler in effect.\n\n```\n$ run_with_crashpad --database=/tmp/crashpad_database crasher\nIllegal instruction: 4\n```\n\nStarts a Crashpad exception handler server at a nonstandard path, and runs\n[exception_port_tool(1)](mac/exception_port_tool.md) to show the task-level\nexception ports.\n\n```\n$ run_with_crashpad --handler=/tmp/crashpad_handler \\\n      --database=/tmp/crashpad_database exception_port_tool \\\n      --show-task\ntask exception port 0, mask 0x1c00 (CRASH|RESOURCE|GUARD), port 0x30b, behavior 0x80000003 (STATE_IDENTITY|MACH), flavor 7 (THREAD)\n```\n\n## Exit Status\n\n * **0**\n\n   Success.\n\n * **125**\n\n   Failure, with a message printed to the standard error stream.\n\n * **126**\n\n   The program specified by _COMMAND_ was found, but could not be invoked.\n\n * **127**\n\n   The program specified by _COMMAND_ could not be found.\n\n## See Also\n\n[crashpad_handler(8)](../handler/crashpad_handler.md),\n[exception_port_tool(1)](mac/exception_port_tool.md)\n\n## Resources\n\nCrashpad home page: https://crashpad.chromium.org/.\n\nReport bugs at https://crashpad.chromium.org/bug/new.\n\n## Copyright\n\nCopyright 2014 [The Crashpad\nAuthors](https://chromium.googlesource.com/crashpad/crashpad/+/main/AUTHORS).\n\n## License\n\nLicensed under the Apache License, Version 2.0 (the “License”);\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "tools/tool_support.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"tools/tool_support.h\"\n\n#include <stdio.h>\n\n#include <string_view>\n#include <vector>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"package.h\"\n\nnamespace crashpad {\n\n// static\nvoid ToolSupport::Version(const base::FilePath& me) {\n  fprintf(stderr,\n          \"%\" PRFilePath \" (%s) %s\\n%s\\n\",\n          me.value().c_str(),\n          PACKAGE_NAME,\n          PACKAGE_VERSION,\n          PACKAGE_COPYRIGHT);\n}\n\n// static\nvoid ToolSupport::UsageTail(const base::FilePath& me) {\n  fprintf(stderr,\n          \"\\nReport %\" PRFilePath \" bugs to\\n%s\\n%s home page: <%s>\\n\",\n          me.value().c_str(),\n          PACKAGE_BUGREPORT,\n          PACKAGE_NAME,\n          PACKAGE_URL);\n}\n\n// static\nvoid ToolSupport::UsageHint(const base::FilePath& me, const char* hint) {\n  if (hint) {\n    fprintf(stderr, \"%\" PRFilePath \": %s\\n\", me.value().c_str(), hint);\n  }\n  fprintf(stderr,\n          \"Try '%\" PRFilePath \" --help' for more information.\\n\",\n          me.value().c_str());\n}\n\n#if BUILDFLAG(IS_POSIX)\n// static\nvoid ToolSupport::Version(const std::string& me) {\n  Version(base::FilePath(me));\n}\n\n// static\nvoid ToolSupport::UsageTail(const std::string& me) {\n  UsageTail(base::FilePath(me));\n}\n\n// static\nvoid ToolSupport::UsageHint(const std::string& me, const char* hint) {\n  UsageHint(base::FilePath(me), hint);\n}\n#endif  // BUILDFLAG(IS_POSIX)\n\n#if BUILDFLAG(IS_WIN)\n\n// static\nint ToolSupport::Wmain(int argc, wchar_t* argv[], int (*entry)(int, char* [])) {\n  auto argv_as_utf8 = base::HeapArray<char*>::Uninit(argc + 1);\n  std::vector<std::string> storage;\n  storage.reserve(argc);\n  for (int i = 0; i < argc; ++i) {\n    storage.push_back(base::WideToUTF8(argv[i]));\n    argv_as_utf8[i] = &storage[i][0];\n  }\n  argv_as_utf8[argc] = nullptr;\n  return entry(argc, argv_as_utf8.data());\n}\n\n#endif  // BUILDFLAG(IS_WIN)\n\n// static\nbase::FilePath::StringType ToolSupport::CommandLineArgumentToFilePathStringType(\n    std::string_view path) {\n#if BUILDFLAG(IS_POSIX)\n  return std::string(path.data(), path.size());\n#elif BUILDFLAG(IS_WIN)\n  return base::UTF8ToWide(path);\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\n// static\nstd::string ToolSupport::FilePathToCommandLineArgument(\n    const base::FilePath& file_path) {\n#if BUILDFLAG(IS_POSIX)\n  return file_path.value();\n#elif BUILDFLAG(IS_WIN)\n  return base::WideToUTF8(file_path.value());\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "tools/tool_support.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_TOOLS_TOOL_SUPPORT_H_\n#define CRASHPAD_TOOLS_TOOL_SUPPORT_H_\n\n#include <string>\n#include <string_view>\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n//! \\brief Common functions used by command line tools.\nclass ToolSupport {\n public:\n  ToolSupport() = delete;\n  ToolSupport(const ToolSupport&) = delete;\n  ToolSupport& operator=(const ToolSupport&) = delete;\n\n  //! \\brief Handles `--version`.\n  //!\n  //! \\param[in] me The tool’s name, the basename of `argv[0]`.\n  static void Version(const base::FilePath& me);\n\n  //! \\brief Prints the footer for `--help`.\n  //!\n  //! \\param[in] me The tool’s name, the basename of `argv[0]`.\n  static void UsageTail(const base::FilePath& me);\n\n  //! \\brief Suggests using `--help` when a command line tool can’t make sense\n  //!     of its arguments.\n  //!\n  //! \\param[in] me The tool’s name, the basename of `argv[0]`.\n  //! \\param[in] hint A hint to display before the suggestion to try `--help`.\n  //!     Optional, may be `nullptr`, in which case no hint will be presented.\n  static void UsageHint(const base::FilePath& me, const char* hint);\n\n#if BUILDFLAG(IS_POSIX) || DOXYGEN\n  //! \\copydoc Version\n  static void Version(const std::string& me);\n\n  //! \\copydoc UsageTail\n  static void UsageTail(const std::string& me);\n\n  //! \\copydoc UsageHint\n  static void UsageHint(const std::string& me, const char* hint);\n#endif  // BUILDFLAG(IS_POSIX)\n\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n  //! \\brief Converts \\a argv `wchar_t` UTF-16 to UTF-8, and passes onwards to a\n  //!     UTF-8 entry point.\n  //!\n  //! \\return The return value of \\a entry.\n  static int Wmain(int argc, wchar_t* argv[], int (*entry)(int, char*[]));\n#endif  // BUILDFLAG(IS_WIN)\n\n  //! \\brief Converts a command line argument to the string type suitable for\n  //!     base::FilePath.\n  //!\n  //! On POSIX, this is a no-op. On Windows, assumes that Wmain() was used, and\n  //! the input argument was converted from UTF-16 in a `wchar_t*` to UTF-8 in a\n  //! `char*`. This undoes that transformation.\n  //!\n  //! \\sa Wmain()\n  //! \\sa FilePathToCommandLineArgument()\n  static base::FilePath::StringType CommandLineArgumentToFilePathStringType(\n      std::string_view arg);\n\n  //! \\brief Converts a base::FilePath to a command line argument.\n  //!\n  //! On POSIX, this is a no-op. On Windows, this undoes the transformation done\n  //! by CommandLineArgumentToFilePathStringType() in the same manner as\n  //! Wmain().\n  static std::string FilePathToCommandLineArgument(\n      const base::FilePath& file_path);\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_TOOLS_TOOL_SUPPORT_H_\n"
  },
  {
    "path": "util/BUILD.gn",
    "content": "# Copyright 2015 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../build/crashpad_buildconfig.gni\")\nimport(\"net/tls.gni\")\n\nif (crashpad_is_in_chromium) {\n  import(\"//build/config/sanitizers/sanitizers.gni\")\n}\n\nif (crashpad_is_apple && !crashpad_is_tvos) {\n  if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {\n    import(\"//build/config/sysroot.gni\")\n  } else {\n    import(\"$mini_chromium_import_root/build/sysroot.gni\")\n  }\n\n  action_foreach(\"mig\") {\n    script = \"mach/mig.py\"\n    inputs = [\n      \"mach/mig_fix.py\",\n      \"mach/mig_gen.py\",\n    ]\n\n    if (crashpad_is_mac) {\n      sources = [\n        \"$sysroot/usr/include/mach/exc.defs\",\n        \"$sysroot/usr/include/mach/mach_exc.defs\",\n        \"$sysroot/usr/include/mach/notify.defs\",\n        \"mach/child_port.defs\",\n      ]\n    } else if (crashpad_is_ios) {\n      sources = [\n        # The iOS SDK doesn’t have any .defs files. Get them directly from xnu.\n        \"../third_party/xnu/osfmk/mach/exc.defs\",\n        \"../third_party/xnu/osfmk/mach/mach_exc.defs\",\n      ]\n    }\n\n    outputs = [\n      \"$target_gen_dir/mach/{{source_name_part}}User.c\",\n      \"$target_gen_dir/mach/{{source_name_part}}Server.c\",\n      \"$target_gen_dir/mach/{{source_name_part}}.h\",\n      \"$target_gen_dir/mach/{{source_name_part}}Server.h\",\n    ]\n\n    args = [ \"{{source}}\" ]\n    if (crashpad_is_in_chromium) {\n      if (!use_system_xcode) {\n        import(\"//build/config/clang/clang.gni\")\n        import(\"//build/config/mac/mac_sdk.gni\")\n        clang_path =\n            rebase_path(\"$clang_base_path/bin/\", root_build_dir) + \"clang\"\n        mig_path = \"$mac_bin_path\" + \"mig\"\n        migcom_path = \"$mac_bin_path\" + \"../libexec/migcom\"\n\n        args += [\n          \"--clang-path\",\n          clang_path,\n          \"--mig-path\",\n          mig_path,\n          \"--migcom-path\",\n          migcom_path,\n        ]\n      }\n      if (crashpad_is_mac) {\n        deps = [ \"//build/config/mac:sdk_inputs\" ]\n      }\n    }\n    if (sysroot != \"\") {\n      if (crashpad_is_in_chromium) {\n        # Chromium often brings along its own SDK and wants to keep paths\n        # relative…\n        args += [\n          \"--sdk\",\n          rebase_path(sysroot, root_build_dir),\n        ]\n      } else {\n        # …but that’s not an option in the standalone Crashpad build, where the\n        # SDK is installed on the system and is absolute. (But use rebase_path\n        # just to be sure.)\n        args += [\n          \"--sdk\",\n          rebase_path(sysroot, \"\"),\n        ]\n      }\n    }\n    if (current_cpu == \"x86\") {\n      args += [\n        \"--arch\",\n        \"i386\",\n      ]\n    } else if (current_cpu == \"x64\") {\n      args += [\n        \"--arch\",\n        \"x86_64\",\n      ]\n    } else if (current_cpu == \"arm\") {\n      args += [\n        \"--arch\",\n        \"armv7\",\n      ]\n    } else if (current_cpu == \"arm64\") {\n      args += [\n        \"--arch\",\n        \"arm64\",\n      ]\n    } else if (crashpad_is_mac && current_cpu == \"mac_universal\") {\n      args += [\n        \"--arch\",\n        \"x86_64\",\n        \"--arch\",\n        \"arm64\",\n      ]\n    } else {\n      assert(false, \"Unsupported architecture\")\n    }\n    args += rebase_path(outputs, root_build_dir)\n    args += [\n      \"--\",\n      \"-I\" + rebase_path(\"..\", root_build_dir),\n      \"-I\" + rebase_path(\"../compat/mac\", root_build_dir),\n    ]\n    if (crashpad_is_ios) {\n      args += [ \"-I\" + rebase_path(\"../compat/ios\", root_build_dir) ]\n    }\n    if (crashpad_is_mac || crashpad_is_ios) {\n      # Needed for mach_exception_raise_*_protected\n      args += [\n        \"-DMACH_EXC_SERVER_TASKIDTOKEN\",\n        \"-DMACH_EXC_SERVER_TASKIDTOKEN_STATE\",\n      ]\n    }\n  }\n\n  static_library(\"mig_output\") {\n    deps = [ \":mig\" ]\n    sources = get_target_outputs(\":mig\")\n    if (crashpad_is_in_chromium) {\n      # mig output contains unreachable code, which irks -Wunreachable-code.\n      configs -= [ \"//build/config/compiler:chromium_code\" ]\n      configs += [ \"//build/config/compiler:no_chromium_code\" ]\n    }\n    public_configs = [ \"..:crashpad_config\" ]\n  }\n}\n\n# Used by crashpad_wer_handler to avoid linking all of :util.\nif (crashpad_is_win) {\n  source_set(\"util_registration_protocol\") {\n    sources = [\n      \"misc/address_types.h\",\n      \"win/address_types.h\",\n      \"win/registration_protocol_win_structs.h\",\n    ]\n    public_deps = [ \"../third_party/mini_chromium:build\" ]\n    public_configs = [ \"..:crashpad_config\" ]\n  }\n}\n\ncrashpad_static_library(\"util\") {\n  sources = [\n    \"file/delimited_file_reader.cc\",\n    \"file/delimited_file_reader.h\",\n    \"file/directory_reader.h\",\n    \"file/file_helper.cc\",\n    \"file/file_helper.h\",\n    \"file/file_io.cc\",\n    \"file/file_io.h\",\n    \"file/file_reader.cc\",\n    \"file/file_reader.h\",\n    \"file/file_seeker.cc\",\n    \"file/file_seeker.h\",\n    \"file/file_writer.cc\",\n    \"file/file_writer.h\",\n    \"file/filesystem.h\",\n    \"file/output_stream_file_writer.cc\",\n    \"file/output_stream_file_writer.h\",\n    \"file/scoped_remove_file.cc\",\n    \"file/scoped_remove_file.h\",\n    \"file/string_file.cc\",\n    \"file/string_file.h\",\n    \"misc/address_sanitizer.h\",\n    \"misc/address_types.h\",\n    \"misc/arraysize.h\",\n    \"misc/as_underlying_type.h\",\n    \"misc/capture_context.h\",\n    \"misc/clock.h\",\n    \"misc/elf_note_types.h\",\n    \"misc/from_pointer_cast.h\",\n    \"misc/implicit_cast.h\",\n    \"misc/initialization_state.h\",\n    \"misc/initialization_state_dcheck.cc\",\n    \"misc/initialization_state_dcheck.h\",\n    \"misc/lexing.cc\",\n    \"misc/lexing.h\",\n    \"misc/memory_sanitizer.h\",\n    \"misc/metrics.cc\",\n    \"misc/metrics.h\",\n    \"misc/paths.h\",\n    \"misc/pdb_structures.cc\",\n    \"misc/pdb_structures.h\",\n    \"misc/random_string.cc\",\n    \"misc/random_string.h\",\n    \"misc/range_set.cc\",\n    \"misc/range_set.h\",\n    \"misc/reinterpret_bytes.cc\",\n    \"misc/reinterpret_bytes.h\",\n    \"misc/scoped_forbid_return.cc\",\n    \"misc/scoped_forbid_return.h\",\n    \"misc/symbolic_constants_common.h\",\n    \"misc/time.cc\",\n    \"misc/time.h\",\n    \"misc/tri_state.h\",\n    \"misc/uuid.cc\",\n    \"misc/uuid.h\",\n    \"misc/zlib.cc\",\n    \"misc/zlib.h\",\n    \"numeric/checked_address_range.cc\",\n    \"numeric/checked_address_range.h\",\n    \"numeric/checked_range.h\",\n    \"numeric/checked_vm_address_range.h\",\n    \"numeric/in_range_cast.h\",\n    \"numeric/int128.h\",\n    \"numeric/safe_assignment.h\",\n    \"process/process_id.h\",\n    \"process/process_memory.cc\",\n    \"process/process_memory.h\",\n    \"process/process_memory_native.h\",\n    \"process/process_memory_range.cc\",\n    \"process/process_memory_range.h\",\n    \"stdlib/aligned_allocator.cc\",\n    \"stdlib/aligned_allocator.h\",\n    \"stdlib/map_insert.h\",\n    \"stdlib/objc.h\",\n    \"stdlib/string_number_conversion.cc\",\n    \"stdlib/string_number_conversion.h\",\n    \"stdlib/strlcpy.cc\",\n    \"stdlib/strlcpy.h\",\n    \"stdlib/strnlen.cc\",\n    \"stdlib/strnlen.h\",\n    \"stdlib/thread_safe_vector.h\",\n    \"stream/base94_output_stream.cc\",\n    \"stream/base94_output_stream.h\",\n    \"stream/file_encoder.cc\",\n    \"stream/file_encoder.h\",\n    \"stream/file_output_stream.cc\",\n    \"stream/file_output_stream.h\",\n    \"stream/log_output_stream.cc\",\n    \"stream/log_output_stream.h\",\n    \"stream/output_stream_interface.h\",\n    \"stream/zlib_output_stream.cc\",\n    \"stream/zlib_output_stream.h\",\n    \"string/split_string.cc\",\n    \"string/split_string.h\",\n    \"synchronization/scoped_spin_guard.h\",\n    \"synchronization/semaphore.h\",\n    \"thread/stoppable.h\",\n    \"thread/thread.cc\",\n    \"thread/thread.h\",\n    \"thread/thread_log_messages.cc\",\n    \"thread/thread_log_messages.h\",\n    \"thread/worker_thread.cc\",\n    \"thread/worker_thread.h\",\n  ]\n\n  defines = [ \"ZLIB_CONST\" ]\n\n  if (crashpad_is_posix || crashpad_is_fuchsia) {\n    sources += [\n      \"file/directory_reader_posix.cc\",\n      \"file/file_io_posix.cc\",\n      \"file/filesystem_posix.cc\",\n      \"misc/clock_posix.cc\",\n      \"posix/close_stdio.cc\",\n      \"posix/close_stdio.h\",\n      \"posix/scoped_dir.cc\",\n      \"posix/scoped_dir.h\",\n      \"posix/scoped_mmap.cc\",\n      \"posix/scoped_mmap.h\",\n      \"posix/signals.cc\",\n      \"posix/signals.h\",\n      \"synchronization/semaphore_posix.cc\",\n      \"thread/thread_posix.cc\",\n    ]\n\n    if (!crashpad_is_fuchsia) {\n      sources += [\n        \"posix/close_multiple.cc\",\n        \"posix/close_multiple.h\",\n        \"posix/drop_privileges.cc\",\n        \"posix/drop_privileges.h\",\n        \"posix/process_info.h\",\n\n        # These map signals to and from strings. While Fuchsia defines some of\n        # the common SIGx defines, signals are never raised on Fuchsia, so\n        # there's need to include this mapping code.\n        \"posix/symbolic_constants_posix.cc\",\n        \"posix/symbolic_constants_posix.h\",\n      ]\n\n      if (crashpad_is_android || crashpad_is_linux || crashpad_is_mac) {\n        sources += [\n          \"posix/spawn_subprocess.cc\",\n          \"posix/spawn_subprocess.h\",\n        ]\n      }\n    }\n  }\n\n  if (crashpad_is_apple) {\n    sources += [\n      \"mac/sysctl.cc\",\n      \"mac/sysctl.h\",\n      \"mac/xattr.cc\",\n      \"mac/xattr.h\",\n      \"mach/mach_extensions.cc\",\n      \"mach/mach_extensions.h\",\n      \"misc/capture_context_mac.S\",\n      \"misc/clock_mac.cc\",\n      \"misc/paths_mac.cc\",\n      \"synchronization/semaphore_mac.cc\",\n    ]\n\n    # Exclude files related to Mach exceptions when building for tvOS. Mach\n    # messaging APIs are not available to third-party applications on tvOS so\n    # crashes are handled via POSIX signals.\n    if (!crashpad_is_tvos) {\n      sources += [\n        \"mach/composite_mach_message_server.cc\",\n        \"mach/composite_mach_message_server.h\",\n        \"mach/exc_client_variants.cc\",\n        \"mach/exc_client_variants.h\",\n        \"mach/exc_server_variants.cc\",\n        \"mach/exc_server_variants.h\",\n        \"mach/exception_behaviors.cc\",\n        \"mach/exception_behaviors.h\",\n        \"mach/exception_ports.cc\",\n        \"mach/exception_ports.h\",\n        \"mach/mach_message.cc\",\n        \"mach/mach_message.h\",\n        \"mach/mach_message_server.cc\",\n        \"mach/mach_message_server.h\",\n        \"mach/symbolic_constants_mach.cc\",\n        \"mach/symbolic_constants_mach.h\",\n      ]\n    }\n  }\n\n  if (crashpad_is_mac && !crashpad_is_in_fuchsia) {\n    sources += [\n      \"mac/checked_mach_address_range.h\",\n      \"mac/launchd.h\",\n      \"mac/launchd.mm\",\n      \"mac/mac_util.cc\",\n      \"mac/mac_util.h\",\n      \"mac/service_management.cc\",\n      \"mac/service_management.h\",\n      \"mach/bootstrap.cc\",\n      \"mach/bootstrap.h\",\n      \"mach/child_port_handshake.cc\",\n      \"mach/child_port_handshake.h\",\n      \"mach/child_port_server.cc\",\n      \"mach/child_port_server.h\",\n      \"mach/child_port_types.h\",\n      \"mach/exception_types.cc\",\n      \"mach/exception_types.h\",\n      \"mach/notify_server.cc\",\n      \"mach/notify_server.h\",\n      \"mach/scoped_task_suspend.cc\",\n      \"mach/scoped_task_suspend.h\",\n      \"mach/task_for_pid.cc\",\n      \"mach/task_for_pid.h\",\n      \"posix/process_info_mac.cc\",\n      \"process/process_memory_mac.cc\",\n      \"process/process_memory_mac.h\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    sources += [\n      \"ios/ios_intermediate_dump_data.cc\",\n      \"ios/ios_intermediate_dump_data.h\",\n      \"ios/ios_intermediate_dump_format.h\",\n      \"ios/ios_intermediate_dump_interface.cc\",\n      \"ios/ios_intermediate_dump_interface.h\",\n      \"ios/ios_intermediate_dump_list.cc\",\n      \"ios/ios_intermediate_dump_list.h\",\n      \"ios/ios_intermediate_dump_map.cc\",\n      \"ios/ios_intermediate_dump_map.h\",\n      \"ios/ios_intermediate_dump_object.cc\",\n      \"ios/ios_intermediate_dump_object.h\",\n      \"ios/ios_intermediate_dump_reader.cc\",\n      \"ios/ios_intermediate_dump_reader.h\",\n      \"ios/ios_intermediate_dump_writer.cc\",\n      \"ios/ios_intermediate_dump_writer.h\",\n      \"ios/ios_system_data_collector.h\",\n      \"ios/ios_system_data_collector.mm\",\n      \"ios/raw_logging.cc\",\n      \"ios/raw_logging.h\",\n      \"ios/scoped_background_task.h\",\n      \"ios/scoped_background_task.mm\",\n      \"ios/scoped_vm_map.cc\",\n      \"ios/scoped_vm_map.h\",\n      \"ios/scoped_vm_read.cc\",\n      \"ios/scoped_vm_read.h\",\n    ]\n  }\n\n  deps = []\n\n  if (crashpad_is_android) {\n    sources += [\n      \"linux/initial_signal_dispositions.cc\",\n      \"linux/initial_signal_dispositions.h\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    sources += [\n      \"linux/address_types.h\",\n      \"linux/auxiliary_vector.cc\",\n      \"linux/auxiliary_vector.h\",\n      \"linux/checked_linux_address_range.h\",\n      \"linux/direct_ptrace_connection.cc\",\n      \"linux/direct_ptrace_connection.h\",\n      \"linux/exception_handler_client.cc\",\n      \"linux/exception_handler_client.h\",\n      \"linux/exception_handler_protocol.cc\",\n      \"linux/exception_handler_protocol.h\",\n      \"linux/exception_information.h\",\n      \"linux/memory_map.cc\",\n      \"linux/memory_map.h\",\n      \"linux/pac_helper.cc\",\n      \"linux/pac_helper.h\",\n      \"linux/proc_stat_reader.cc\",\n      \"linux/proc_stat_reader.h\",\n      \"linux/proc_task_reader.cc\",\n      \"linux/proc_task_reader.h\",\n      \"linux/ptrace_broker.cc\",\n      \"linux/ptrace_broker.h\",\n      \"linux/ptrace_client.cc\",\n      \"linux/ptrace_client.h\",\n      \"linux/ptrace_connection.h\",\n      \"linux/ptracer.cc\",\n      \"linux/ptracer.h\",\n      \"linux/scoped_pr_set_dumpable.cc\",\n      \"linux/scoped_pr_set_dumpable.h\",\n      \"linux/scoped_pr_set_ptracer.cc\",\n      \"linux/scoped_pr_set_ptracer.h\",\n      \"linux/scoped_ptrace_attach.cc\",\n      \"linux/scoped_ptrace_attach.h\",\n      \"linux/socket.cc\",\n      \"linux/socket.h\",\n      \"linux/thread_info.cc\",\n      \"linux/thread_info.h\",\n      \"linux/traits.h\",\n      \"misc/capture_context_linux.S\",\n      \"misc/paths_linux.cc\",\n      \"misc/time_linux.cc\",\n      \"posix/process_info_linux.cc\",\n      \"process/process_memory_linux.cc\",\n      \"process/process_memory_linux.h\",\n      \"process/process_memory_sanitized.cc\",\n      \"process/process_memory_sanitized.h\",\n    ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [\n      \"file/directory_reader_win.cc\",\n      \"file/file_io_win.cc\",\n      \"file/filesystem_win.cc\",\n      \"misc/clock_win.cc\",\n      \"misc/paths_win.cc\",\n      \"misc/time_win.cc\",\n      \"process/process_memory_win.cc\",\n      \"process/process_memory_win.h\",\n      \"synchronization/semaphore_win.cc\",\n      \"thread/thread_win.cc\",\n      \"win/address_types.h\",\n      \"win/checked_win_address_range.h\",\n      \"win/command_line.cc\",\n      \"win/command_line.h\",\n      \"win/context_wrappers.h\",\n      \"win/critical_section_with_debug_info.cc\",\n      \"win/critical_section_with_debug_info.h\",\n      \"win/exception_codes.h\",\n      \"win/exception_handler_server.cc\",\n      \"win/exception_handler_server.h\",\n      \"win/get_function.cc\",\n      \"win/get_function.h\",\n      \"win/get_module_information.cc\",\n      \"win/get_module_information.h\",\n      \"win/handle.cc\",\n      \"win/handle.h\",\n      \"win/initial_client_data.cc\",\n      \"win/initial_client_data.h\",\n      \"win/loader_lock.cc\",\n      \"win/loader_lock.h\",\n      \"win/module_version.cc\",\n      \"win/module_version.h\",\n      \"win/nt_internals.cc\",\n      \"win/nt_internals.h\",\n      \"win/ntstatus_logging.cc\",\n      \"win/ntstatus_logging.h\",\n      \"win/process_info.cc\",\n      \"win/process_info.h\",\n      \"win/process_structs.h\",\n      \"win/registration_protocol_win.cc\",\n      \"win/registration_protocol_win.h\",\n      \"win/registration_protocol_win_structs.h\",\n      \"win/safe_terminate_process.h\",\n      \"win/scoped_handle.cc\",\n      \"win/scoped_handle.h\",\n      \"win/scoped_local_alloc.cc\",\n      \"win/scoped_local_alloc.h\",\n      \"win/scoped_process_suspend.cc\",\n      \"win/scoped_process_suspend.h\",\n      \"win/scoped_registry_key.h\",\n      \"win/scoped_set_event.cc\",\n      \"win/scoped_set_event.h\",\n      \"win/session_end_watcher.cc\",\n      \"win/session_end_watcher.h\",\n      \"win/termination_codes.h\",\n      \"win/traits.h\",\n      \"win/xp_compat.h\",\n    ]\n\n    if (current_cpu != \"arm64\") {\n      sources += [\n        \"misc/capture_context_win.asm\",\n        \"win/safe_terminate_process.asm\",\n      ]\n    } else {\n      # When building with clang, clang-cl is used as the assembler. Since\n      # clang-cl recognizes a different assembly dialect than Microsoft’s\n      # armasm64 macro assembler, the same .asm file can’t be used for each. As\n      # a workaround, use a prebuilt .obj file when the Microsoft-dialect\n      # assembler isn’t available.\n      if (crashpad_is_clang) {\n        sources += [ \"misc/capture_context_win_arm64.obj\" ]\n      } else {\n        sources += [ \"misc/capture_context_win_arm64.asm\" ]\n      }\n    }\n  }\n\n  if (crashpad_is_fuchsia) {\n    sources += [\n      \"fuchsia/koid_utilities.cc\",\n      \"fuchsia/koid_utilities.h\",\n      \"fuchsia/scoped_task_suspend.cc\",\n      \"fuchsia/scoped_task_suspend.h\",\n      \"fuchsia/traits.h\",\n      \"misc/paths_fuchsia.cc\",\n      \"process/process_memory_fuchsia.cc\",\n      \"process/process_memory_fuchsia.h\",\n    ]\n\n    sources -= [ \"misc/capture_context.h\" ]\n  }\n\n  public_configs = [ \"..:crashpad_config\" ]\n\n  # Include generated files starting with \"util\".\n  if (crashpad_is_in_fuchsia) {\n    include_dirs =\n        [ \"$root_gen_dir/\" + rebase_path(fuchsia_crashpad_root, \"//\") ]\n  } else {\n    include_dirs = [ \"$root_gen_dir/third_party/crashpad/crashpad\" ]\n  }\n\n  public_deps = [\n    \":no_cfi_icall\",\n    \"../compat\",\n    \"../third_party/zlib\",\n  ]\n\n  deps = [ \"$mini_chromium_source_parent:base\" ]\n\n  configs = [ \"../build:flock_always_supported_defines\" ]\n\n  if (crashpad_is_apple) {\n    include_dirs += [ \"$root_gen_dir\" ]\n    deps += [ \"../build:apple_enable_arc\" ]\n\n    if (!crashpad_is_tvos) {\n      deps += [ \":mig_output\" ]\n    }\n  }\n\n  if (crashpad_is_mac && !crashpad_is_in_fuchsia) {\n    libs = [ \"bsm\" ]\n    frameworks = [\n      \"CoreFoundation.framework\",\n      \"Foundation.framework\",\n      \"IOKit.framework\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    configs += [ \"../build:crashpad_is_ios_app_extension\" ]\n  }\n\n  if (crashpad_is_win) {\n    libs = [\n      \"user32.lib\",\n\n      # TODO(jperaza): version.lib is needed for Windows 7 compatibility.\n      # mincore.lib may be linked against instead when targeting Windows 8+.\n      \"version.lib\",\n\n      \"winhttp.lib\",\n    ]\n\n    cflags = [ \"/wd4201\" ]  # nonstandard extension used: nameless struct/union.\n\n    if (current_cpu == \"x86\") {\n      asmflags = [ \"/safeseh\" ]\n    }\n  }\n\n  if (crashpad_is_fuchsia) {\n    public_deps += [ \"../third_party/fuchsia\" ]\n  }\n\n  if (crashpad_is_android || crashpad_is_linux) {\n    deps += [ \"../third_party/lss\" ]\n  }\n}\n\n# net is split into a separate target from util so that client code does\n# not have to depend on it.\ncrashpad_static_library(\"net\") {\n  sources = [\n    \"net/http_body.cc\",\n    \"net/http_body.h\",\n    \"net/http_body_gzip.cc\",\n    \"net/http_body_gzip.h\",\n    \"net/http_headers.h\",\n    \"net/http_multipart_builder.cc\",\n    \"net/http_multipart_builder.h\",\n    \"net/http_transport.cc\",\n    \"net/http_transport.h\",\n    \"net/url.cc\",\n    \"net/url.h\",\n  ]\n\n  deps = [\n    \":util\",\n    \"$mini_chromium_source_parent:base\",\n  ]\n\n  if (crashpad_is_apple) {\n    deps += [ \"../build:apple_enable_arc\" ]\n  }\n\n  if (crashpad_is_mac && !crashpad_is_in_fuchsia) {\n    sources += [ \"net/http_transport_mac.mm\" ]\n  }\n\n  if (crashpad_is_ios) {\n    sources += [ \"net/http_transport_mac.mm\" ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [ \"net/http_transport_win.cc\" ]\n  }\n\n  if (crashpad_http_transport_impl == \"socket\") {\n    sources += [ \"net/http_transport_socket.cc\" ]\n    if (crashpad_use_boringssl_for_http_transport_socket) {\n      defines = [ \"CRASHPAD_USE_BORINGSSL\" ]\n\n      if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {\n        deps += [ \"//third_party/boringssl\" ]\n      } else {\n        libs = [\n          \"crypto\",\n          \"ssl\",\n        ]\n      }\n    }\n  } else if (crashpad_http_transport_impl == \"libcurl\") {\n    sources += [ \"net/http_transport_libcurl.cc\" ]\n  }\n}\n\nif (!crashpad_is_android && !crashpad_is_ios) {\n  crashpad_executable(\"http_transport_test_server\") {\n    testonly = true\n    sources = [ \"net/http_transport_test_server.cc\" ]\n\n    deps = [\n      \":net\",\n      \":util\",\n      \"$mini_chromium_source_parent:base\",\n      \"../third_party/cpp-httplib\",\n      \"../third_party/zlib\",\n      \"../tools:tool_support\",\n    ]\n\n    # TODO(b/189353575): make these relocatable using $mini_chromium_ variables\n    if (crashpad_is_standalone) {\n      remove_configs = [ \"//third_party/mini_chromium/mini_chromium/build/config:Wexit_time_destructors\" ]\n    } else if (crashpad_is_external) {\n      remove_configs = [ \"//../../mini_chromium/mini_chromium/build/config:Wexit_time_destructors\" ]\n    }\n\n    if (crashpad_is_win) {\n      libs = [ \"ws2_32.lib\" ]\n    }\n\n    if (crashpad_use_boringssl_for_http_transport_socket) {\n      defines = [ \"CRASHPAD_USE_BORINGSSL\" ]\n\n      if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {\n        deps += [ \"//third_party/boringssl\" ]\n      } else {\n        libs = [\n          \"crypto\",\n          \"ssl\",\n        ]\n      }\n    }\n  }\n}\n\n# This exists as a separate target from util so that compat may depend on it\n# without cycles.\nsource_set(\"no_cfi_icall\") {\n  sources = [ \"misc/no_cfi_icall.h\" ]\n  public_configs = [ \"..:crashpad_config\" ]\n  public_deps = [\n    \"$mini_chromium_source_parent:base\",\n    \"$mini_chromium_source_parent:build\",\n  ]\n}\n\nsource_set(\"util_test\") {\n  testonly = true\n\n  sources = [\n    \"file/delimited_file_reader_test.cc\",\n    \"file/directory_reader_test.cc\",\n    \"file/file_io_test.cc\",\n    \"file/file_reader_test.cc\",\n    \"file/filesystem_test.cc\",\n    \"file/string_file_test.cc\",\n    \"misc/arraysize_test.cc\",\n    \"misc/capture_context_test.cc\",\n    \"misc/capture_context_test_util.h\",\n    \"misc/clock_test.cc\",\n    \"misc/from_pointer_cast_test.cc\",\n    \"misc/initialization_state_dcheck_test.cc\",\n    \"misc/initialization_state_test.cc\",\n    \"misc/no_cfi_icall_test.cc\",\n    \"misc/paths_test.cc\",\n    \"misc/random_string_test.cc\",\n    \"misc/range_set_test.cc\",\n    \"misc/reinterpret_bytes_test.cc\",\n    \"misc/scoped_forbid_return_test.cc\",\n    \"misc/time_test.cc\",\n    \"misc/uuid_test.cc\",\n    \"net/http_body_gzip_test.cc\",\n    \"net/http_body_test.cc\",\n    \"net/http_body_test_util.cc\",\n    \"net/http_body_test_util.h\",\n    \"net/http_multipart_builder_test.cc\",\n    \"net/url_test.cc\",\n    \"numeric/checked_address_range_test.cc\",\n    \"numeric/checked_range_test.cc\",\n    \"numeric/in_range_cast_test.cc\",\n    \"numeric/int128_test.cc\",\n    \"process/process_memory_range_test.cc\",\n    \"process/process_memory_test.cc\",\n    \"stdlib/aligned_allocator_test.cc\",\n    \"stdlib/map_insert_test.cc\",\n    \"stdlib/string_number_conversion_test.cc\",\n    \"stdlib/strlcpy_test.cc\",\n    \"stdlib/strnlen_test.cc\",\n    \"stdlib/thread_safe_vector_test.cc\",\n    \"stream/base94_output_stream_test.cc\",\n    \"stream/file_encoder_test.cc\",\n    \"stream/log_output_stream_test.cc\",\n    \"stream/test_output_stream.cc\",\n    \"stream/test_output_stream.h\",\n    \"stream/zlib_output_stream_test.cc\",\n    \"string/split_string_test.cc\",\n    \"synchronization/scoped_spin_guard_test.cc\",\n    \"synchronization/semaphore_test.cc\",\n    \"thread/thread_log_messages_test.cc\",\n    \"thread/thread_test.cc\",\n    \"thread/worker_thread_test.cc\",\n  ]\n\n  if (!crashpad_is_android && !crashpad_is_ios && !crashpad_is_fuchsia) {\n    # Android requires an HTTPTransport implementation.\n    sources += [ \"net/http_transport_test.cc\" ]\n  }\n\n  if (crashpad_is_posix || crashpad_is_fuchsia) {\n    if (!crashpad_is_fuchsia && !crashpad_is_ios) {\n      sources += [\n        \"posix/process_info_test.cc\",\n        \"posix/signals_test.cc\",\n        \"posix/symbolic_constants_posix_test.cc\",\n      ]\n    }\n    sources += [ \"posix/scoped_mmap_test.cc\" ]\n  }\n\n  if (crashpad_is_apple) {\n    sources += [\n      \"mac/xattr_test.cc\",\n      \"misc/capture_context_test_util_mac.cc\",\n    ]\n\n    if (!crashpad_is_tvos) {\n      sources += [\n        \"mach/composite_mach_message_server_test.cc\",\n        \"mach/exc_server_variants_test.cc\",\n        \"mach/exception_behaviors_test.cc\",\n        \"mach/mach_extensions_test.cc\",\n        \"mach/mach_message_test.cc\",\n        \"mach/symbolic_constants_mach_test.cc\",\n      ]\n    }\n  }\n\n  if (crashpad_is_mac) {\n    sources += [\n      \"mac/launchd_test.mm\",\n      \"mac/mac_util_test.mm\",\n      \"mac/service_management_test.mm\",\n      \"mac/sysctl_test.cc\",\n      \"mach/bootstrap_test.cc\",\n      \"mach/child_port_handshake_test.cc\",\n      \"mach/child_port_server_test.cc\",\n      \"mach/exc_client_variants_test.cc\",\n      \"mach/exception_ports_test.cc\",\n      \"mach/exception_types_test.cc\",\n      \"mach/mach_message_server_test.cc\",\n      \"mach/notify_server_test.cc\",\n      \"mach/scoped_task_suspend_test.cc\",\n      \"process/process_memory_mac_test.cc\",\n    ]\n  }\n\n  if (crashpad_is_ios) {\n    sources += [\n      \"ios/ios_intermediate_dump_reader_test.cc\",\n      \"ios/ios_intermediate_dump_writer_test.cc\",\n      \"ios/scoped_vm_map_test.cc\",\n      \"ios/scoped_vm_read_test.cc\",\n    ]\n\n    sources -= [\n      \"process/process_memory_range_test.cc\",\n      \"process/process_memory_test.cc\",\n    ]\n  }\n\n  if (crashpad_is_linux || crashpad_is_android) {\n    sources += [\n      \"linux/auxiliary_vector_test.cc\",\n      \"linux/memory_map_test.cc\",\n      \"linux/proc_stat_reader_test.cc\",\n      \"linux/proc_task_reader_test.cc\",\n      \"linux/ptrace_broker_test.cc\",\n      \"linux/ptracer_test.cc\",\n      \"linux/scoped_ptrace_attach_test.cc\",\n      \"linux/socket_test.cc\",\n      \"misc/capture_context_test_util_linux.cc\",\n      \"process/process_memory_sanitized_test.cc\",\n    ]\n  }\n\n  if (crashpad_is_fuchsia) {\n    sources -= [\n      \"misc/capture_context_test.cc\",\n      \"misc/capture_context_test_util.h\",\n    ]\n  }\n\n  if (crashpad_is_win) {\n    sources += [\n      \"misc/capture_context_test_util_win.cc\",\n      \"win/command_line_test.cc\",\n      \"win/critical_section_with_debug_info_test.cc\",\n      \"win/exception_handler_server_test.cc\",\n      \"win/get_function_test.cc\",\n      \"win/handle_test.cc\",\n      \"win/initial_client_data_test.cc\",\n      \"win/loader_lock_test.cc\",\n      \"win/process_info_test.cc\",\n      \"win/registration_protocol_win_test.cc\",\n      \"win/safe_terminate_process_test.cc\",\n      \"win/scoped_process_suspend_test.cc\",\n      \"win/session_end_watcher_test.cc\",\n    ]\n\n    if (crashpad_is_in_chromium && is_asan && is_component_build) {\n      # TODO(crbug.com/856174): Re-enable these once Windows ASan is fixed.\n      sources -= [ \"stdlib/string_number_conversion_test.cc\" ]\n    }\n  }\n\n  data = [\n    \"net/testdata/ascii_http_body.txt\",\n    \"net/testdata/binary_http_body.dat\",\n    \"net/testdata/crashpad_util_test_cert.pem\",\n    \"net/testdata/crashpad_util_test_key.pem\",\n  ]\n\n  deps = [\n    \":net\",\n    \":util\",\n    \"$mini_chromium_source_parent:base\",\n    \"../client\",\n    \"../compat\",\n    \"../test\",\n    \"../third_party/googletest\",\n    \"../third_party/googletest:googlemock\",\n    \"../third_party/zlib\",\n  ]\n\n  if (!crashpad_is_android && !crashpad_is_ios && !crashpad_is_fuchsia) {\n    data_deps = [ \":http_transport_test_server\" ]\n\n    if (crashpad_use_boringssl_for_http_transport_socket) {\n      defines = [ \"CRASHPAD_USE_BORINGSSL\" ]\n    }\n  }\n\n  if (crashpad_is_apple) {\n    deps += [ \"../build:apple_enable_arc\" ]\n  }\n\n  if (crashpad_is_mac) {\n    frameworks = [ \"Foundation.framework\" ]\n  }\n\n  if (crashpad_is_ios) {\n    deps += [ \":util_test_bundle_data\" ]\n  }\n\n  if (crashpad_is_android || crashpad_is_linux) {\n    deps += [ \"../third_party/lss\" ]\n  }\n\n  if (crashpad_is_win) {\n    libs = [\n      \"rpcrt4.lib\",\n      \"dbghelp.lib\",\n    ]\n    data_deps += [\n      \":crashpad_util_test_loader_lock_test\",\n      \":crashpad_util_test_process_info_test_child\",\n      \":crashpad_util_test_safe_terminate_process_test_child\",\n    ]\n  }\n}\n\nif (crashpad_is_ios) {\n  bundle_data(\"util_test_bundle_data\") {\n    testonly = true\n\n    sources = [\n      \"net/testdata/ascii_http_body.txt\",\n      \"net/testdata/binary_http_body.dat\",\n    ]\n\n    outputs = [ \"{{bundle_resources_dir}}/crashpad_test_data/\" +\n                \"{{source_root_relative_dir}}/{{source_file_part}}\" ]\n  }\n}\n\nif (crashpad_is_win) {\n  crashpad_executable(\"crashpad_util_test_process_info_test_child\") {\n    testonly = true\n    sources = [ \"win/process_info_test_child.cc\" ]\n  }\n\n  crashpad_executable(\"crashpad_util_test_safe_terminate_process_test_child\") {\n    testonly = true\n    sources = [ \"win/safe_terminate_process_test_child.cc\" ]\n  }\n\n  crashpad_loadable_module(\"crashpad_util_test_loader_lock_test\") {\n    testonly = true\n    sources = [ \"win/loader_lock_test_dll.cc\" ]\n    deps = [ \":util\" ]\n  }\n}\n"
  },
  {
    "path": "util/file/delimited_file_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/delimited_file_reader.h\"\n\n#include <sys/types.h>\n\n#include <algorithm>\n#include <iterator>\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/numerics/safe_conversions.h\"\n\nnamespace crashpad {\n\nDelimitedFileReader::DelimitedFileReader(FileReaderInterface* file_reader)\n    : file_reader_(file_reader), buf_pos_(0), buf_len_(0), eof_(false) {\n  static_assert(sizeof(buf_) <= std::numeric_limits<decltype(buf_pos_)>::max(),\n                \"buf_pos_ must cover buf_\");\n  static_assert(sizeof(buf_) <= std::numeric_limits<decltype(buf_len_)>::max(),\n                \"buf_len_ must cover buf_\");\n}\n\nDelimitedFileReader::~DelimitedFileReader() {}\n\nDelimitedFileReader::Result DelimitedFileReader::GetDelim(char delimiter,\n                                                          std::string* field) {\n  if (eof_) {\n    DCHECK_EQ(buf_pos_, buf_len_);\n\n    // Allow subsequent calls to attempt to read more data from the file. If the\n    // file is still at EOF in the future, the read will return 0 and cause\n    // kEndOfFile to be returned anyway.\n    eof_ = false;\n\n    return Result::kEndOfFile;\n  }\n\n  std::string local_field;\n  while (true) {\n    if (buf_pos_ == buf_len_) {\n      // buf_ is empty. Refill it.\n      FileOperationResult read_result = file_reader_->Read(buf_, sizeof(buf_));\n      if (read_result < 0) {\n        return Result::kError;\n      } else if (read_result == 0) {\n        if (!local_field.empty()) {\n          // The file ended with a field that wasn’t terminated by a delimiter\n          // character.\n          //\n          // This is EOF, but EOF can’t be returned because there’s a field that\n          // needs to be returned to the caller. Cache the detected EOF so it\n          // can be returned next time. This is done to support proper semantics\n          // for weird “files” like terminal input that can reach EOF and then\n          // “grow”, allowing subsequent reads past EOF to block while waiting\n          // for more data. Once EOF is detected by a read that returns 0, that\n          // EOF signal should propagate to the caller before attempting a new\n          // read. Here, it will be returned on the next call to this method\n          // without attempting to read more data.\n          eof_ = true;\n          field->swap(local_field);\n          return Result::kSuccess;\n        }\n        return Result::kEndOfFile;\n      }\n\n      DCHECK_LE(static_cast<size_t>(read_result), std::size(buf_));\n      DCHECK(\n          base::IsValueInRangeForNumericType<decltype(buf_len_)>(read_result));\n      buf_len_ = static_cast<decltype(buf_len_)>(read_result);\n      buf_pos_ = 0;\n    }\n\n    const char* const start = buf_ + buf_pos_;\n    const char* const end = buf_ + buf_len_;\n    const char* const found = std::find(start, end, delimiter);\n\n    local_field.append(start, found);\n    buf_pos_ = static_cast<decltype(buf_pos_)>(found - buf_);\n    DCHECK_LE(buf_pos_, buf_len_);\n\n    if (found != end) {\n      // A real delimiter character was found. Append it to the field being\n      // built and return it.\n      local_field.push_back(delimiter);\n      ++buf_pos_;\n      DCHECK_LE(buf_pos_, buf_len_);\n      field->swap(local_field);\n      return Result::kSuccess;\n    }\n  }\n}\n\nDelimitedFileReader::Result DelimitedFileReader::GetLine(std::string* line) {\n  return GetDelim('\\n', line);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/delimited_file_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_DELIMITED_FILE_READER_H_\n#define CRASHPAD_UTIL_FILE_DELIMITED_FILE_READER_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"util/file/file_reader.h\"\n\nnamespace crashpad {\n\n//! \\brief Reads a file one field or line at a time.\n//!\n//! The file is interpreted as a series of fields separated by delimiter\n//! characters. When the delimiter character is the newline character\n//! (<code>'\\\\n'</code>), the file is interpreted as a series of lines.\n//!\n//! It is safe to mix GetDelim() and GetLine() calls, if appropriate for the\n//! format being interpreted.\n//!\n//! This is a replacement for the standard library’s `getdelim()` and\n//! `getline()` functions, adapted to work with FileReaderInterface objects\n//! instead of `FILE*` streams.\nclass DelimitedFileReader {\n public:\n  //! \\brief The result of a GetDelim() or GetLine() call.\n  enum class Result {\n    //! \\brief An error occurred, and a message was logged.\n    kError = -1,\n\n    //! \\brief A field or line was read from the file.\n    kSuccess,\n\n    //! \\brief The end of the file was encountered.\n    kEndOfFile,\n  };\n\n  explicit DelimitedFileReader(FileReaderInterface* file_reader);\n\n  DelimitedFileReader(const DelimitedFileReader&) = delete;\n  DelimitedFileReader& operator=(const DelimitedFileReader&) = delete;\n\n  ~DelimitedFileReader();\n\n  //! \\brief Reads a single field from the file.\n  //!\n  //! \\param[in] delimiter The delimiter character that terminates the field.\n  //!     It is safe to call this method multiple times while changing the value\n  //!     of this parameter, if appropriate for the format being interpreted.\n  //! \\param[out] field The field read from the file. This parameter will\n  //!     include the field’s terminating delimiter character unless the field\n  //!     was at the end of the file and was read without such a character.\n  //!     This parameter will not be empty.\n  //!\n  //! \\return a #Result value. \\a field is only valid when Result::kSuccess is\n  //!     returned.\n  Result GetDelim(char delimiter, std::string* field);\n\n  //! \\brief Reads a single line from the file.\n  //!\n  //! \\param[out] line The line read from the file. This parameter will include\n  //!     the line terminating delimiter character unless the line was at the\n  //!     end of the file and was read without such a character. This parameter\n  //!     will not be empty.\n  //!\n  //! \\return a #Result value. \\a line is only valid when Result::kSuccess is\n  //!     returned.\n  Result GetLine(std::string* line);\n\n private:\n  char buf_[4096];\n  FileReaderInterface* file_reader_;  // weak\n  uint16_t buf_pos_;  // Index into buf_ of the start of the next field.\n  uint16_t buf_len_;  // The size of buf_ that’s been filled.\n  bool eof_;  // Caches the EOF signal when detected following a partial field.\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_DELIMITED_FILE_READER_H_\n"
  },
  {
    "path": "util/file/delimited_file_reader_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/delimited_file_reader.h\"\n\n#include <iterator>\n#include <vector>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"util/file/string_file.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(DelimitedFileReader, EmptyFile) {\n  StringFile string_file;\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, EmptyOneLineFile) {\n  StringFile string_file;\n  string_file.SetString(\"\\n\");\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, string_file.string());\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, SmallOneLineFile) {\n  StringFile string_file;\n  string_file.SetString(\"one line\\n\");\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, string_file.string());\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, SmallOneLineFileWithoutNewline) {\n  StringFile string_file;\n  string_file.SetString(\"no newline\");\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, string_file.string());\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, SmallMultiLineFile) {\n  StringFile string_file;\n  string_file.SetString(\"first\\nsecond line\\n3rd\\n\");\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, \"first\\n\");\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, \"second line\\n\");\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, \"3rd\\n\");\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, SmallMultiFieldFile) {\n  StringFile string_file;\n  string_file.SetString(\"first,second field\\ntwo lines,3rd,\");\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string field;\n  ASSERT_EQ(delimited_file_reader.GetDelim(',', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, \"first,\");\n  ASSERT_EQ(delimited_file_reader.GetDelim(',', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, \"second field\\ntwo lines,\");\n  ASSERT_EQ(delimited_file_reader.GetDelim(',', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, \"3rd,\");\n  EXPECT_EQ(delimited_file_reader.GetDelim(',', &field),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetDelim(',', &field),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, SmallMultiFieldFile_MixedDelimiters) {\n  StringFile string_file;\n  string_file.SetString(\"first,second, still 2nd\\t3rd\\nalso\\tnewline\\n55555$\");\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string field;\n  ASSERT_EQ(delimited_file_reader.GetDelim(',', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, \"first,\");\n  ASSERT_EQ(delimited_file_reader.GetDelim('\\t', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, \"second, still 2nd\\t\");\n  ASSERT_EQ(delimited_file_reader.GetLine(&field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, \"3rd\\n\");\n  ASSERT_EQ(delimited_file_reader.GetDelim('\\n', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, \"also\\tnewline\\n\");\n  ASSERT_EQ(delimited_file_reader.GetDelim('$', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, \"55555$\");\n  EXPECT_EQ(delimited_file_reader.GetDelim('?', &field),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&field),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, EmptyLineMultiLineFile) {\n  StringFile string_file;\n  string_file.SetString(\"first\\n\\n\\n4444\\n\");\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, \"first\\n\");\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, \"\\n\");\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, \"\\n\");\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, \"4444\\n\");\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, LongOneLineFile) {\n  std::string contents(50000, '!');\n  contents[1] = '?';\n  contents.push_back('\\n');\n\n  StringFile string_file;\n  string_file.SetString(contents);\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, contents);\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nvoid TestLongMultiLineFile(int base_length) {\n  std::vector<std::string> lines;\n  std::string contents;\n  for (size_t line_index = 0; line_index <= 'z' - 'a'; ++line_index) {\n    char c = 'a' + static_cast<char>(line_index);\n\n    // Mix up the lengths a little.\n    std::string line(base_length + line_index * ((line_index % 3) - 1), c);\n\n    // Mix up the data a little too.\n    ASSERT_LT(line_index, line.size());\n    line[line_index] -= ('a' - 'A');\n\n    line.push_back('\\n');\n    contents.append(line);\n    lines.push_back(line);\n  }\n\n  StringFile string_file;\n  string_file.SetString(contents);\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  for (size_t line_index = 0; line_index < lines.size(); ++line_index) {\n    SCOPED_TRACE(base::StringPrintf(\"line_index %\" PRIuS, line_index));\n    ASSERT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kSuccess);\n    EXPECT_EQ(line, lines[line_index]);\n  }\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, LongMultiLineFile) {\n  TestLongMultiLineFile(500);\n}\n\nTEST(DelimitedFileReader, ReallyLongMultiLineFile) {\n  TestLongMultiLineFile(5000);\n}\n\nTEST(DelimitedFileReader, EmbeddedNUL) {\n  static constexpr char kString[] = \"embedded\\0NUL\\n\";\n  StringFile string_file;\n  string_file.SetString(std::string(kString, std::size(kString) - 1));\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string line;\n  ASSERT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(line, string_file.string());\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetLine(&line),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, NULDelimiter) {\n  static constexpr char kString[] = \"aa\\0b\\0ccc\\0\";\n  StringFile string_file;\n  string_file.SetString(std::string(kString, std::size(kString) - 1));\n  DelimitedFileReader delimited_file_reader(&string_file);\n\n  std::string field;\n  ASSERT_EQ(delimited_file_reader.GetDelim('\\0', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, std::string(\"aa\\0\", 3));\n  ASSERT_EQ(delimited_file_reader.GetDelim('\\0', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, std::string(\"b\\0\", 2));\n  ASSERT_EQ(delimited_file_reader.GetDelim('\\0', &field),\n            DelimitedFileReader::Result::kSuccess);\n  EXPECT_EQ(field, std::string(\"ccc\\0\", 4));\n  EXPECT_EQ(delimited_file_reader.GetDelim('\\0', &field),\n            DelimitedFileReader::Result::kEndOfFile);\n\n  // The file is still at EOF.\n  EXPECT_EQ(delimited_file_reader.GetDelim('\\0', &field),\n            DelimitedFileReader::Result::kEndOfFile);\n}\n\nTEST(DelimitedFileReader, EdgeCases) {\n  static constexpr size_t kSizes[] =\n      {4094, 4095, 4096, 4097, 8190, 8191, 8192, 8193};\n  for (size_t index = 0; index < std::size(kSizes); ++index) {\n    size_t size = kSizes[index];\n    SCOPED_TRACE(\n        base::StringPrintf(\"index %\" PRIuS \", size %\" PRIuS, index, size));\n\n    std::string line_0(size, '$');\n    line_0.push_back('\\n');\n\n    StringFile string_file;\n    string_file.SetString(line_0);\n    DelimitedFileReader delimited_file_reader(&string_file);\n\n    std::string line;\n    ASSERT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kSuccess);\n    EXPECT_EQ(line, line_0);\n    EXPECT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kEndOfFile);\n\n    // The file is still at EOF.\n    EXPECT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kEndOfFile);\n\n    std::string line_1(size, '@');\n    line_1.push_back('\\n');\n\n    string_file.SetString(line_0 + line_1);\n    ASSERT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kSuccess);\n    EXPECT_EQ(line, line_0);\n    ASSERT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kSuccess);\n    EXPECT_EQ(line, line_1);\n    EXPECT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kEndOfFile);\n\n    // The file is still at EOF.\n    EXPECT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kEndOfFile);\n\n    line_1[size] = '?';\n\n    string_file.SetString(line_0 + line_1);\n    ASSERT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kSuccess);\n    EXPECT_EQ(line, line_0);\n    ASSERT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kSuccess);\n    EXPECT_EQ(line, line_1);\n    EXPECT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kEndOfFile);\n\n    // The file is still at EOF.\n    EXPECT_EQ(delimited_file_reader.GetLine(&line),\n              DelimitedFileReader::Result::kEndOfFile);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/directory_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_DIRECTORY_READER_H_\n#define CRASHPAD_UTIL_FILE_DIRECTORY_READER_H_\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include \"util/posix/scoped_dir.h\"\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n\n#include \"util/win/scoped_handle.h\"\n#endif  // BUILDFLAG(IS_POSIX)\n\nnamespace crashpad {\n\n//! \\brief Iterates over the file and directory names in a directory.\n//!\n//! The names enumerated are relative to the specified directory and do not\n//! include \".\", \"..\", or files and directories in subdirectories.\nclass DirectoryReader {\n public:\n  //! \\brief The result of a call to NextFile().\n  enum class Result {\n    //! \\brief An error occurred and a message was logged.\n    kError = -1,\n\n    //! \\brief A file was found.\n    kSuccess,\n\n    //! \\brief No more files were found.\n    kNoMoreFiles,\n  };\n\n  DirectoryReader();\n\n  DirectoryReader(const DirectoryReader&) = delete;\n  DirectoryReader& operator=(const DirectoryReader&) = delete;\n\n  ~DirectoryReader();\n\n  //! \\brief Opens the directory specified by \\a path for reading.\n  //!\n  //! \\param[in] path The path to the directory to read.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Open(const base::FilePath& path);\n\n  //! \\brief Advances the reader to the next file in the directory.\n  //!\n  //! \\param[out] filename The filename of the next file.\n  //! \\return a #Result value. \\a filename is only valid when Result::kSuccess\n  //!     is returned. If Result::kError is returned, a message will be\n  //!     logged.\n  Result NextFile(base::FilePath* filename);\n\n#if BUILDFLAG(IS_POSIX) || DOXYGEN\n  //! \\brief Returns the file descriptor associated with this reader, logging a\n  //!     message and returning -1 on error.\n  int DirectoryFD();\n#endif\n\n private:\n#if BUILDFLAG(IS_POSIX)\n  ScopedDIR dir_;\n#elif BUILDFLAG(IS_WIN)\n  WIN32_FIND_DATA find_data_;\n  ScopedSearchHANDLE handle_;\n  bool first_entry_;\n#endif  // BUILDFLAG(IS_POSIX)\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_DIRECTORY_READER_H_\n"
  },
  {
    "path": "util/file/directory_reader_posix.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/directory_reader.h\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\n#define HANDLE_EINTR_IF_EQ(x, val)                             \\\n  ({                                                           \\\n    decltype(x) eintr_wrapper_result;                          \\\n    do {                                                       \\\n      eintr_wrapper_result = (x);                              \\\n    } while (eintr_wrapper_result == (val) && errno == EINTR); \\\n    eintr_wrapper_result;                                      \\\n  })\n\nDirectoryReader::DirectoryReader() : dir_() {}\n\nDirectoryReader::~DirectoryReader() {}\n\nbool DirectoryReader::Open(const base::FilePath& path) {\n  dir_.reset(HANDLE_EINTR_IF_EQ(opendir(path.value().c_str()), nullptr));\n  if (!dir_.is_valid()) {\n    PLOG(ERROR) << \"opendir \" << path.value();\n    return false;\n  }\n  return true;\n}\n\nDirectoryReader::Result DirectoryReader::NextFile(base::FilePath* filename) {\n  DCHECK(dir_.is_valid());\n\n  errno = 0;\n  dirent* entry = HANDLE_EINTR_IF_EQ(readdir(dir_.get()), nullptr);\n  if (!entry) {\n    if (errno) {\n      PLOG(ERROR) << \"readdir \" << filename->value();\n      return Result::kError;\n    } else {\n      return Result::kNoMoreFiles;\n    }\n  }\n\n  if (strcmp(entry->d_name, \".\") == 0 || strcmp(entry->d_name, \"..\") == 0) {\n    return NextFile(filename);\n  }\n\n  *filename = base::FilePath(entry->d_name);\n  return Result::kSuccess;\n}\n\nint DirectoryReader::DirectoryFD() {\n  DCHECK(dir_.is_valid());\n  int rv = dirfd(dir_.get());\n  if (rv < 0) {\n    PLOG(ERROR) << \"dirfd\";\n  }\n  return rv;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/directory_reader_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/directory_reader.h\"\n\n#include <set>\n\n#include \"base/files/file_path.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/filesystem.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/filesystem.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(DirectoryReader, BadPaths) {\n  DirectoryReader reader;\n  EXPECT_FALSE(reader.Open(base::FilePath()));\n\n  ScopedTempDir temp_dir;\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n  EXPECT_FALSE(reader.Open(file));\n\n  EXPECT_FALSE(\n      reader.Open(temp_dir.path().Append(FILE_PATH_LITERAL(\"doesntexist\"))));\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nTEST(DirectoryReader, BadPaths_SymbolicLinks) {\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  ScopedTempDir temp_dir;\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n\n  base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n\n  DirectoryReader reader;\n  EXPECT_FALSE(reader.Open(link));\n\n  ASSERT_TRUE(LoggingRemoveFile(file));\n  EXPECT_FALSE(reader.Open(link));\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\nTEST(DirectoryReader, EmptyDirectory) {\n  ScopedTempDir temp_dir;\n  DirectoryReader reader;\n\n  ASSERT_TRUE(reader.Open(temp_dir.path()));\n  base::FilePath filename;\n  EXPECT_EQ(reader.NextFile(&filename), DirectoryReader::Result::kNoMoreFiles);\n}\n\nvoid ExpectFiles(const std::set<base::FilePath>& files,\n                 const std::set<base::FilePath>& expected) {\n  EXPECT_EQ(files.size(), expected.size());\n\n  for (const auto& filename : expected) {\n    SCOPED_TRACE(\n        base::StringPrintf(\"Filename: %\" PRFilePath, filename.value().c_str()));\n    EXPECT_NE(files.find(filename), files.end());\n  }\n}\n\nvoid TestFilesAndDirectories(bool symbolic_links) {\n  ScopedTempDir temp_dir;\n  std::set<base::FilePath> expected_files;\n\n  base::FilePath file(FILE_PATH_LITERAL(\"file\"));\n  ASSERT_TRUE(CreateFile(temp_dir.path().Append(file)));\n  EXPECT_TRUE(expected_files.insert(file).second);\n\n  base::FilePath directory(FILE_PATH_LITERAL(\"directory\"));\n  ASSERT_TRUE(LoggingCreateDirectory(temp_dir.path().Append(directory),\n                                     FilePermissions::kWorldReadable,\n                                     false));\n  EXPECT_TRUE(expected_files.insert(directory).second);\n\n  base::FilePath nested_file(FILE_PATH_LITERAL(\"nested_file\"));\n  ASSERT_TRUE(\n      CreateFile(temp_dir.path().Append(directory).Append(nested_file)));\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\n  if (symbolic_links) {\n    base::FilePath link(FILE_PATH_LITERAL(\"link\"));\n    ASSERT_TRUE(CreateSymbolicLink(temp_dir.path().Append(file),\n                                   temp_dir.path().Append(link)));\n    EXPECT_TRUE(expected_files.insert(link).second);\n\n    base::FilePath dangling(FILE_PATH_LITERAL(\"dangling\"));\n    ASSERT_TRUE(\n        CreateSymbolicLink(base::FilePath(FILE_PATH_LITERAL(\"not_a_file\")),\n                           temp_dir.path().Append(dangling)));\n    EXPECT_TRUE(expected_files.insert(dangling).second);\n  }\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\n  std::set<base::FilePath> files;\n  DirectoryReader reader;\n  ASSERT_TRUE(reader.Open(temp_dir.path()));\n  DirectoryReader::Result result;\n  base::FilePath filename;\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    EXPECT_TRUE(files.insert(filename).second);\n  }\n  EXPECT_EQ(result, DirectoryReader::Result::kNoMoreFiles);\n  EXPECT_EQ(reader.NextFile(&filename), DirectoryReader::Result::kNoMoreFiles);\n  ExpectFiles(files, expected_files);\n}\n\nTEST(DirectoryReader, FilesAndDirectories) {\n  TestFilesAndDirectories(false);\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nTEST(DirectoryReader, FilesAndDirectories_SymbolicLinks) {\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  TestFilesAndDirectories(true);\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/directory_reader_win.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/directory_reader.h\"\n\n#include <string.h>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nDirectoryReader::DirectoryReader()\n    : find_data_(), handle_(), first_entry_(false) {}\n\nDirectoryReader::~DirectoryReader() {}\n\nbool DirectoryReader::Open(const base::FilePath& path) {\n  if (path.empty()) {\n    LOG(ERROR) << \"Empty directory path\";\n    return false;\n  }\n\n  handle_.reset(\n      FindFirstFileEx(path.Append(FILE_PATH_LITERAL(\"*\")).value().c_str(),\n                      FindExInfoBasic,\n                      &find_data_,\n                      FindExSearchNameMatch,\n                      nullptr,\n                      FIND_FIRST_EX_LARGE_FETCH));\n\n  if (!handle_.is_valid()) {\n    PLOG(ERROR) << \"FindFirstFile\";\n    return false;\n  }\n\n  first_entry_ = true;\n  return true;\n}\n\nDirectoryReader::Result DirectoryReader::NextFile(base::FilePath* filename) {\n  DCHECK(handle_.is_valid());\n\n  if (!first_entry_) {\n    if (!FindNextFile(handle_.get(), &find_data_)) {\n      if (GetLastError() != ERROR_NO_MORE_FILES) {\n        PLOG(ERROR) << \"FindNextFile\";\n        return Result::kError;\n      } else {\n        return Result::kNoMoreFiles;\n      }\n    }\n  } else {\n    first_entry_ = false;\n  }\n\n  if (wcscmp(find_data_.cFileName, L\".\") == 0 ||\n      wcscmp(find_data_.cFileName, L\"..\") == 0) {\n    return NextFile(filename);\n  }\n\n  *filename = base::FilePath(find_data_.cFileName);\n  return Result::kSuccess;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_helper.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_helper.h\"\n\nnamespace crashpad {\n\nvoid CopyFileContent(FileReaderInterface* file_reader,\n                     FileWriterInterface* file_writer) {\n  char buf[4096];\n  FileOperationResult read_result;\n  do {\n    read_result = file_reader->Read(buf, sizeof(buf));\n    if (read_result < 0) {\n      break;\n    }\n    if (read_result > 0 && !file_writer->Write(buf, read_result)) {\n      break;\n    }\n  } while (read_result > 0);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_helper.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_FILE_HELPER_H_\n#define CRASHPAD_UTIL_FILE_FILE_HELPER_H_\n\n#include \"util/file/file_reader.h\"\n#include \"util/file/file_writer.h\"\n\nnamespace crashpad {\n\n//! \\brief Copy the file content from file_reader to file_writer\nvoid CopyFileContent(FileReaderInterface* file_reader,\n                     FileWriterInterface* file_writer);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_FILE_HELPER_H_\n"
  },
  {
    "path": "util/file/file_io.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_io.h\"\n\n#include <functional>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nFileOperationResult FileIORead(FileHandle file,\n                               bool can_log,\n                               void* buffer,\n                               size_t size) {\n  FileOperationResult rv = ReadFile(file, buffer, size);\n  if (rv < 0) {\n    PLOG_IF(ERROR, can_log) << internal::kNativeReadFunctionName;\n    return -1;\n  }\n  return rv;\n}\n\nclass FileIOWriteAll final : public internal::WriteAllInternal {\n public:\n  explicit FileIOWriteAll(FileHandle file) : WriteAllInternal(), file_(file) {}\n\n  FileIOWriteAll(const FileIOWriteAll&) = delete;\n  FileIOWriteAll& operator=(const FileIOWriteAll&) = delete;\n\n  ~FileIOWriteAll() {}\n\n private:\n  // WriteAllInternal:\n  FileOperationResult Write(const void* buffer, size_t size) override {\n    return internal::NativeWriteFile(file_, buffer, size);\n  }\n\n  FileHandle file_;\n};\n\nFileOperationResult ReadUntil(\n    std::function<FileOperationResult(void*, size_t)> read_function,\n    void* buffer,\n    size_t size) {\n  // Ensure bytes read fit within int32_t::max to make sure that they also fit\n  // into FileOperationResult on all platforms.\n  DCHECK_LE(size, size_t{std::numeric_limits<int32_t>::max()});\n  uintptr_t buffer_int = reinterpret_cast<uintptr_t>(buffer);\n  size_t total_bytes = 0;\n  size_t remaining = size;\n  while (remaining > 0) {\n    const FileOperationResult bytes_read =\n        read_function(reinterpret_cast<char*>(buffer_int), remaining);\n    if (bytes_read < 0) {\n      return bytes_read;\n    }\n\n    DCHECK_LE(static_cast<size_t>(bytes_read), remaining);\n\n    if (bytes_read == 0) {\n      break;\n    }\n\n    buffer_int += bytes_read;\n    remaining -= bytes_read;\n    total_bytes += bytes_read;\n  }\n  return total_bytes;\n}\n\n}  // namespace\n\nnamespace internal {\n\nbool ReadExactly(\n    std::function<FileOperationResult(bool, void*, size_t)> read_function,\n    bool can_log,\n    void* buffer,\n    size_t size) {\n  const FileOperationResult result =\n      ReadUntil(std::bind_front(read_function, can_log), buffer, size);\n  if (result < 0) {\n    return false;\n  }\n\n  if (static_cast<size_t>(result) != size) {\n    LOG_IF(ERROR, can_log) << \"ReadExactly: expected \" << size << \", observed \"\n                           << result;\n    return false;\n  }\n\n  return true;\n}\n\nbool WriteAllInternal::WriteAll(const void* buffer, size_t size) {\n  uintptr_t buffer_int = reinterpret_cast<uintptr_t>(buffer);\n\n  while (size > 0) {\n    FileOperationResult bytes_written =\n        Write(reinterpret_cast<const char*>(buffer_int), size);\n    if (bytes_written < 0) {\n      return false;\n    }\n\n    DCHECK_NE(bytes_written, 0);\n\n    buffer_int += bytes_written;\n    size -= bytes_written;\n  }\n\n  return true;\n}\n\n}  // namespace internal\n\nbool ReadFileExactly(FileHandle file, void* buffer, size_t size) {\n  return internal::ReadExactly(\n      std::bind_front(&FileIORead, file), false, buffer, size);\n}\n\nFileOperationResult ReadFileUntil(FileHandle file, void* buffer, size_t size) {\n  return ReadUntil(std::bind_front(&FileIORead, file, false), buffer, size);\n}\n\nbool LoggingReadFileExactly(FileHandle file, void* buffer, size_t size) {\n  return internal::ReadExactly(\n      std::bind_front(&FileIORead, file), true, buffer, size);\n}\n\nFileOperationResult LoggingReadFileUntil(FileHandle file,\n                                         void* buffer,\n                                         size_t size) {\n  return ReadUntil(std::bind_front(&FileIORead, file, true), buffer, size);\n}\n\nbool WriteFile(FileHandle file, const void* buffer, size_t size) {\n  FileIOWriteAll write_all(file);\n  return write_all.WriteAll(buffer, size);\n}\n\nbool LoggingWriteFile(FileHandle file, const void* buffer, size_t size) {\n  if (!WriteFile(file, buffer, size)) {\n    PLOG(ERROR) << internal::kNativeWriteFunctionName;\n    return false;\n  }\n\n  return true;\n}\n\nvoid CheckedReadFileExactly(FileHandle file, void* buffer, size_t size) {\n  CHECK(LoggingReadFileExactly(file, buffer, size));\n}\n\nvoid CheckedWriteFile(FileHandle file, const void* buffer, size_t size) {\n  CHECK(LoggingWriteFile(file, buffer, size));\n}\n\nvoid CheckedReadFileAtEOF(FileHandle file) {\n  char c;\n  FileOperationResult rv = ReadFile(file, &c, 1);\n  if (rv < 0) {\n    PCHECK(rv == 0) << internal::kNativeReadFunctionName;\n  } else {\n    CHECK_EQ(rv, 0) << internal::kNativeReadFunctionName;\n  }\n}\n\nbool LoggingReadToEOF(FileHandle file, std::string* contents) {\n  char buffer[4096];\n  FileOperationResult rv;\n  std::string local_contents;\n  while ((rv = ReadFile(file, buffer, sizeof(buffer))) > 0) {\n    DCHECK_LE(static_cast<size_t>(rv), sizeof(buffer));\n    local_contents.append(buffer, rv);\n  }\n  if (rv < 0) {\n    PLOG(ERROR) << internal::kNativeReadFunctionName;\n    return false;\n  }\n  contents->swap(local_contents);\n  return true;\n}\n\nbool LoggingReadEntireFile(const base::FilePath& path, std::string* contents) {\n  ScopedFileHandle handle(LoggingOpenFileForRead(path));\n  if (!handle.is_valid()) {\n    return false;\n  }\n\n  return LoggingReadToEOF(handle.get(), contents);\n}\n\nvoid CheckedCloseFile(FileHandle file) {\n  CHECK(LoggingCloseFile(file));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_io.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_FILE_IO_H_\n#define CRASHPAD_UTIL_FILE_FILE_IO_H_\n\n#include <sys/types.h>\n\n#include <functional>\n#include <string>\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include \"base/files/scoped_file.h\"\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#include \"util/win/scoped_handle.h\"\n#endif\n\nnamespace base {\nclass FilePath;\n}  // namespace base\n\nnamespace crashpad {\n\n#if BUILDFLAG(IS_POSIX) || DOXYGEN\n\n//! \\brief Platform-specific alias for a low-level file handle.\nusing FileHandle = int;\n\n//! \\brief Platform-specific alias for a position in an open file.\nusing FileOffset = off_t;\n\n//! \\brief Scoped wrapper of a FileHandle.\nusing ScopedFileHandle = base::ScopedFD;\n\n//! \\brief The return value of read and write calls.\nusing FileOperationResult = ssize_t;\n\n//! \\brief A value that can never be a valid FileHandle.\nconst FileHandle kInvalidFileHandle = -1;\n\n#elif BUILDFLAG(IS_WIN)\n\nusing FileHandle = HANDLE;\nusing FileOffset = LONGLONG;\nusing ScopedFileHandle = ScopedFileHANDLE;\nusing FileOperationResult = LONG_PTR;\n\nconst FileHandle kInvalidFileHandle = INVALID_HANDLE_VALUE;\n\n#endif\n\n//! \\brief Determines the mode that LoggingOpenFileForWrite() uses.\nenum class FileWriteMode {\n  //! \\brief Opens the file if it exists, or fails if it does not.\n  kReuseOrFail,\n\n  //! \\brief Opens the file if it exists, or creates a new file.\n  kReuseOrCreate,\n\n  //! \\brief Creates a new file. If the file already exists, it will be\n  //!     overwritten.\n  kTruncateOrCreate,\n\n  //! \\brief Creates a new file. If the file already exists, the open will fail.\n  kCreateOrFail,\n};\n\n//! \\brief Determines the permissions bits for files created on POSIX systems.\nenum class FilePermissions : bool {\n  //! \\brief Equivalent to `0600`.\n  kOwnerOnly,\n\n  //! \\brief Equivalent to `0644`.\n  kWorldReadable,\n};\n\n//! \\brief Determines the locking mode that LoggingLockFile() uses.\nenum class FileLocking : bool {\n  //! \\brief Equivalent to `flock()` with `LOCK_SH`.\n  kShared,\n\n  //! \\brief Equivalent to `flock()` with `LOCK_EX`.\n  kExclusive,\n};\n\n//! \\brief Determines if LoggingLockFile will block.\nenum class FileLockingBlocking : bool {\n  //! \\brief Block until the lock is acquired\n  kBlocking = false,\n\n  //! \\brief Do not block when attempting to acquire a lock.\n  kNonBlocking = true,\n};\n\n//! \\brief The return value for LoggingLockFile.\nenum class FileLockingResult : int {\n  //! \\brief The lock was acquired successfully.\n  kSuccess,\n\n  //! \\brief In non-blocking mode only, the file was already locked. Locking\n  //!     would block, so the lock was not acquired.\n  kWouldBlock,\n\n  //! \\brief The lock was not acquired.\n  kFailure,\n};\n\n//! \\brief Determines the FileHandle that StdioFileHandle() returns.\nenum class StdioStream {\n  //! \\brief Standard input, or `stdin`.\n  kStandardInput,\n\n  //! \\brief Standard output, or `stdout`.\n  kStandardOutput,\n\n  //! \\brief Standard error, or `stderr`.\n  kStandardError,\n};\n\nnamespace internal {\n\n#if BUILDFLAG(IS_POSIX) || DOXYGEN\n\n//! \\brief The name of the native read function used by ReadFile().\n//!\n//! This value may be useful for logging.\n//!\n//! \\sa kNativeWriteFunctionName\nconstexpr char kNativeReadFunctionName[] = \"read\";\n\n//! \\brief The name of the native write function used by WriteFile().\n//!\n//! This value may be useful for logging.\n//!\n//! \\sa kNativeReadFunctionName\nconstexpr char kNativeWriteFunctionName[] = \"write\";\n\n#elif BUILDFLAG(IS_WIN)\n\nconstexpr char kNativeReadFunctionName[] = \"ReadFile\";\nconstexpr char kNativeWriteFunctionName[] = \"WriteFile\";\n\n#endif\n\n//! \\brief The internal implementation of ReadFileExactly() and its wrappers.\n//!\n//! The logic is exposed so that it may be reused by FileReaderInterface, and\n//! so that it may be tested without requiring large files to be read. It is not\n//! intended to be used more generally. Use ReadFileExactly(),\n//! LoggingReadFileExactly(), CheckedReadFileExactly(), or\n//! FileReaderInterface::ReadExactly() instead.\nbool ReadExactly(\n    std::function<FileOperationResult(bool, void*, size_t)> read_function,\n    bool can_log,\n    void* buffer,\n    size_t size);\n\n//! \\brief The internal implementation of WriteFile() and its wrappers.\n//!\n//! The logic is exposed so that it may be tested without requiring large files\n//! to be written. It is not intended to be used more generally. Use\n//! WriteFile(), LoggingWriteFile(), CheckedWriteFile(), or\n//! FileWriterInterface::Write() instead.\nclass WriteAllInternal {\n public:\n  WriteAllInternal(const WriteAllInternal&) = delete;\n  WriteAllInternal& operator=(const WriteAllInternal&) = delete;\n\n  //! \\brief Calls Write(), retrying following a short write, ensuring that\n  //!     exactly \\a size bytes are written.\n  //!\n  //! \\return `true` on success. `false` if the underlying Write() fails or if\n  //!     fewer than \\a size bytes were written.\n  bool WriteAll(const void* buffer, size_t size);\n\n protected:\n  WriteAllInternal() {}\n  ~WriteAllInternal() {}\n\n private:\n  //! \\brief Wraps a write operation, such as NativeWriteFile().\n  //!\n  //! \\return The number of bytes written from \\a buffer, or `-1` on error.\n  virtual FileOperationResult Write(const void* buffer, size_t size) = 0;\n};\n\n//! \\brief Writes to a file, retrying when interrupted on POSIX.\n//!\n//! Fewer than \\a size bytes may be written to \\a file. This can happen if the\n//! underlying write operation returns before writing the entire buffer, or if\n//! the buffer is too large to write in a single operation, possibly due to a\n//! limitation of a data type used to express the number of bytes written.\n//!\n//! This function adapts native write operations for uniform use by WriteFile().\n//! This function should only be called by WriteFile(). Other code should call\n//! WriteFile() or another function that wraps WriteFile().\n//!\n//! \\param[in] file The file to write to.\n//! \\param[in] buffer A buffer containing data to be written.\n//! \\param[in] size The number of bytes from \\a buffer to write.\n//!\n//! \\return The number of bytes actually written from \\a buffer to \\a file on\n//!     success. `-1` on error, with `errno` or `GetLastError()` set\n//!     appropriately.\nFileOperationResult NativeWriteFile(FileHandle file,\n                                    const void* buffer,\n                                    size_t size);\n\n}  // namespace internal\n\n//! \\brief Reads from a file, retrying when interrupted before reading any data\n//!     on POSIX.\n//!\n//! This function reads into \\a buffer. Fewer than \\a size bytes may be read.\n//! On Windows, reading from sockets is not currently supported.\n//!\n//! \\return The number of bytes read and placed into \\a buffer, or `-1` on\n//!     error, with `errno` or `GetLastError()` set appropriately. On error, a\n//!     portion of \\a file may have been read into \\a buffer.\n//!\n//! \\sa WriteFile\n//! \\sa ReadFileExactly\n//! \\sa ReadFileUntil\n//! \\sa LoggingReadFileExactly\n//! \\sa LoggingReadFileUntil\n//! \\sa CheckedReadFileExactly\n//! \\sa CheckedReadFileAtEOF\nFileOperationResult ReadFile(FileHandle file, void* buffer, size_t size);\n\n//! \\brief Writes to a file, retrying when interrupted on POSIX or following a\n//!     short write.\n//!\n//! This function writes to \\a file, stopping only when \\a size bytes have been\n//! written.\n//!\n//! \\return `true` on success. `false` on error, with `errno` or\n//!     `GetLastError()` set appropriately. On error, a portion of \\a buffer may\n//!     have been written to \\a file.\n//!\n//! \\sa ReadFile\n//! \\sa LoggingWriteFile\n//! \\sa CheckedWriteFile\nbool WriteFile(FileHandle file, const void* buffer, size_t size);\n\n//! \\brief Wraps ReadFile(), retrying following a short read, ensuring that\n//!     exactly \\a size bytes are read. Does not log on failure.\n//!\n//! \\return `true` on success. Returns `false` if the underlying ReadFile()\n//!     fails or if fewer than \\a size bytes were read.\n//!\n//! \\sa LoggingWriteFile\n//! \\sa ReadFile\n//! \\sa ReadFileUntil\n//! \\sa LoggingReadFileExactly\n//! \\sa LoggingReadFileUntil\n//! \\sa CheckedReadFileExactly\n//! \\sa CheckedReadFileAtEOF\nbool ReadFileExactly(FileHandle file, void* buffer, size_t size);\n\n//! \\brief Wraps ReadFile(), retrying following a short read. Does not log on\n//!     failure.\n//!\n//! \\returns The number of bytes read or `-1` if the underlying ReadFile()\n//!     fails.\n//!\n//! \\sa ReadFile\n//! \\sa ReadFileExactly\n//! \\sa LoggingReadFileExactly\n//! \\sa LoggingReadFileUntil\nFileOperationResult ReadFileUntil(FileHandle file, void* buffer, size_t size);\n\n//! \\brief Wraps ReadFile(), retrying following a short read, ensuring that\n//!     exactly \\a size bytes are read. Logs an error on failure.\n//!\n//! \\return `true` on success. Returns `false` if the underlying ReadFile()\n//!     fails or if fewer than \\a size bytes were read.\n//!\n//! \\sa LoggingWriteFile\n//! \\sa ReadFile\n//! \\sa ReadFileExactly\n//! \\sa ReadFileUntil\n//! \\sa LoggingReadFileUntil\n//! \\sa CheckedReadFileExactly\n//! \\sa CheckedReadFileAtEOF\nbool LoggingReadFileExactly(FileHandle file, void* buffer, size_t size);\n\n//! \\brief Wraps ReadFile(), retrying following a short read. Logs an error on\n//!     failure.\n//!\n//! \\returns The number of bytes read or `-1` if the underlying ReadFile()\n//!     fails.\n//!\n//! \\sa ReadFileExactly\n//! \\sa ReadFileUntil\n//! \\sa LoggingReadFileExactly\n//! \\sa CheckedReadFileExactly\n//! \\sa CheckedReadFileAtEOF\nFileOperationResult LoggingReadFileUntil(FileHandle file,\n                                         void* buffer,\n                                         size_t size);\n\n//! \\brief Wraps WriteFile(), ensuring that exactly \\a size bytes are written.\n//!\n//! \\return `true` on success. If the underlying WriteFile() fails, or if fewer\n//!     than \\a size bytes were written, this function logs a message and\n//!     returns `false`.\n//!\n//! \\sa LoggingReadFileExactly\n//! \\sa WriteFile\n//! \\sa CheckedWriteFile\nbool LoggingWriteFile(FileHandle file, const void* buffer, size_t size);\n\n//! \\brief Wraps ReadFile(), ensuring that exactly \\a size bytes are read.\n//!\n//! If the underlying ReadFile() fails, or if fewer than \\a size bytes were\n//! read, this function causes execution to terminate without returning.\n//!\n//! \\sa CheckedWriteFile\n//! \\sa ReadFile\n//! \\sa LoggingReadFileExactly\n//! \\sa CheckedReadFileAtEOF\nvoid CheckedReadFileExactly(FileHandle file, void* buffer, size_t size);\n\n//! \\brief Wraps WriteFile(), ensuring that exactly \\a size bytes are written.\n//!\n//! if the underlying WriteFile() fails, or if fewer than \\a size bytes were\n//! written, this function causes execution to terminate without returning.\n//!\n//! \\sa CheckedReadFileExactly\n//! \\sa WriteFile\n//! \\sa LoggingWriteFile\nvoid CheckedWriteFile(FileHandle file, const void* buffer, size_t size);\n\n//! \\brief Wraps ReadFile(), ensuring that it indicates end-of-file.\n//!\n//! Attempts to read a single byte from \\a file, expecting no data to be read.\n//! If the underlying ReadFile() fails, or if a byte actually is read, this\n//! function causes execution to terminate without returning.\n//!\n//! \\sa CheckedReadFileExactly\n//! \\sa ReadFile\nvoid CheckedReadFileAtEOF(FileHandle file);\n\n//! \\brief Wraps ReadFile() to read from the current file position to the end of\n//!     the file into \\a contents.\n//!\n//! \\return `true` on success, or `false` with a message logged.\nbool LoggingReadToEOF(FileHandle file, std::string* contents);\n\n//! \\brief Wraps LoggingOpenFileForRead() and ReadFile() reading the entire file\n//!     into \\a contents.\n//!\n//! \\return `true` on success, or `false` with a message logged.\nbool LoggingReadEntireFile(const base::FilePath& path, std::string* contents);\n\n//! \\brief Wraps `open()` or `CreateFile()`, opening an existing file for\n//!     reading.\n//!\n//! \\return The newly opened FileHandle, or an invalid FileHandle on failure.\n//!\n//! \\sa ScopedFileHandle\n//! \\sa OpenFileForWrite\n//! \\sa OpenFileForReadAndWrite\n//! \\sa LoggingOpenFileForRead\nFileHandle OpenFileForRead(const base::FilePath& path);\n\n//! \\brief Wraps `open()` or `CreateFile()`, creating a file for output.\n//!\n//! \\a mode determines the style (truncate, reuse, etc.) that is used to open\n//! the file. On POSIX, \\a permissions determines the value that is passed as\n//! `mode` to `open()`. On Windows, the file is always opened in binary mode\n//! (that is, no CRLF translation). On Windows, the file is opened for sharing,\n//! see LoggingLockFile() and LoggingUnlockFile() to control concurrent access.\n//!\n//! \\return The newly opened FileHandle, or an invalid FileHandle on failure.\n//!\n//! \\sa ScopedFileHandle\n//! \\sa OpenFileForRead\n//! \\sa OpenFileForReadAndWrite\n//! \\sa LoggingOpenFileForWrite\nFileHandle OpenFileForWrite(const base::FilePath& path,\n                            FileWriteMode mode,\n                            FilePermissions permissions);\n\n//! \\brief Wraps `open()` or `CreateFile()`, creating a file for both input and\n//!     output.\n//!\n//! \\a mode determines the style (truncate, reuse, etc.) that is used to open\n//! the file. On POSIX, \\a permissions determines the value that is passed as\n//! `mode` to `open()`. On Windows, the file is always opened in binary mode\n//! (that is, no CRLF translation). On Windows, the file is opened for sharing,\n//! see LoggingLockFile() and LoggingUnlockFile() to control concurrent access.\n//!\n//! \\return The newly opened FileHandle, or an invalid FileHandle on failure.\n//!\n//! \\sa ScopedFileHandle\n//! \\sa OpenFileForRead\n//! \\sa OpenFileForWrite\n//! \\sa LoggingOpenFileForReadAndWrite\nFileHandle OpenFileForReadAndWrite(const base::FilePath& path,\n                                   FileWriteMode mode,\n                                   FilePermissions permissions);\n\n//! \\brief Wraps OpenFileForRead(), logging an error if the operation fails.\n//!\n//! \\return The newly opened FileHandle, or an invalid FileHandle on failure.\n//!\n//! \\sa ScopedFileHandle\n//! \\sa LoggingOpenFileForWrite\n//! \\sa LoggingOpenFileForReadAndWrite\nFileHandle LoggingOpenFileForRead(const base::FilePath& path);\n\n//! \\brief Wraps OpenFileForWrite(), logging an error if the operation fails.\n//!\n//! \\return The newly opened FileHandle, or an invalid FileHandle on failure.\n//!\n//! \\sa ScopedFileHandle\n//! \\sa LoggingOpenFileForRead\n//! \\sa LoggingOpenFileForReadAndWrite\nFileHandle LoggingOpenFileForWrite(const base::FilePath& path,\n                                   FileWriteMode mode,\n                                   FilePermissions permissions);\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n//! \\brief Opens an in-memory file for input and output.\n//!\n//! This function first attempts to open the file with `memfd_create()`. If\n//! `memfd_create()` isn't supported by the kernel, this function next attempts\n//! to open a file using `O_TMPFILE`. If `O_TMPFILE` isn't supported, this\n//! function finally falls back to creating a file with a randomized name in\n//! `/tmp` and immediately `unlink()`ing it.\n//!\n//! Unlike other file open operations, this function doesn't set `O_CLOEXEC`.\n//!\n//! \\param name A name associated with the file. This name does not indicate any\n//!     exact path and may not be used at all, depending on the strategy used to\n//!     create the file. The name should not contain any '/' characters.\n//! \\return The newly opened FileHandle, or an invalid FileHandle on failure,\n//!     with a message logged.\n//!\n//! \\sa ScopedFileHandle\n//! \\sa LoggingOpenFileForRead\n//! \\sa LoggingOpenFileForWrite\n//! \\sa LoggingOpenFileForReadAndWrite\nFileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name);\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n\n//! \\brief Wraps OpenFileForReadAndWrite(), logging an error if the operation\n//!     fails.\n//!\n//! \\return The newly opened FileHandle, or an invalid FileHandle on failure.\n//!\n//! \\sa ScopedFileHandle\n//! \\sa LoggingOpenFileForRead\n//! \\sa LoggingOpenFileForWrite\nFileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path,\n                                          FileWriteMode mode,\n                                          FilePermissions permissions);\n\n// Fuchsia does not currently support any sort of file locking. See\n// https://crashpad.chromium.org/bug/196 and\n// https://crashpad.chromium.org/bug/217.\n#if CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\n//! \\brief Locks the given \\a file using `flock()` on POSIX or `LockFileEx()` on\n//!     Windows.\n//!\n//! It is an error to attempt to lock a file in a different mode when it is\n//! already locked. This call will block until the lock is acquired unless\n//! \\a blocking is FileLockingBlocking::kNonBlocking. The entire file is\n//! locked.\n//!\n//! If \\a locking is FileLocking::kShared, \\a file must have been opened for\n//! reading, and if it's FileLocking::kExclusive, \\a file must have been opened\n//! for writing.\n//!\n//! \\param[in] file The open file handle to be locked.\n//! \\param[in] locking Controls whether the lock is a shared reader lock, or an\n//!     exclusive writer lock.\n//! \\param[in] blocking Controls whether a locked file will result in blocking\n//!     or an immediate return.\n//!\n//! \\return kSuccess if a lock is acquired. If a lock could not be acquired\n//!     because \\a blocking is FileLockingBlocking::kNonBlocking and acquiring\n//!     the lock would block, returns kWouldBlock. If a lock fails for any other\n//!     reason, returns kFailure and a message will be logged.\nFileLockingResult LoggingLockFile(FileHandle file,\n                                  FileLocking locking,\n                                  FileLockingBlocking blocking);\n\n//! \\brief Unlocks a file previously locked with LoggingLockFile().\n//!\n//! It is an error to attempt to unlock a file that was not previously locked.\n//! A previously-locked file should be unlocked before closing the file handle,\n//! otherwise on some OSs the lock may not be released immediately.\n//!\n//! \\param[in] file The open locked file handle to be unlocked.\n//!\n//! \\return `true` on success, or `false` and a message will be logged.\nbool LoggingUnlockFile(FileHandle file);\n\n#endif  // CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\n//! \\brief Wraps `lseek()` or `SetFilePointerEx()`. Logs an error if the\n//!     operation fails.\n//!\n//! Repositions the offset of the open \\a file to the specified \\a offset,\n//! relative to \\a whence. \\a whence must be one of `SEEK_SET`, `SEEK_CUR`, or\n//! `SEEK_END`, and is interpreted in the usual way.\n//!\n//! \\return The resulting offset in bytes from the beginning of the file, or\n//!     `-1` on failure.\nFileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence);\n\n//! \\brief Truncates the given \\a file to zero bytes in length.\n//!\n//! \\return `true` on success, or `false`, and a message will be logged.\nbool LoggingTruncateFile(FileHandle file);\n\n//! \\brief Wraps `close()` or `CloseHandle()`, logging an error if the operation\n//!     fails.\n//!\n//! \\return On success, `true` is returned. On failure, an error is logged and\n//!     `false` is returned.\nbool LoggingCloseFile(FileHandle file);\n\n//! \\brief Wraps `close()` or `CloseHandle()`, ensuring that it succeeds.\n//!\n//! If the underlying function fails, this function causes execution to\n//! terminate without returning.\nvoid CheckedCloseFile(FileHandle file);\n\n//! \\brief Determines the size of a file.\n//!\n//! \\param[in] file The handle to the file for which the size should be\n//!     retrieved.\n//!\n//! \\return The size of the file. If an error occurs when attempting to\n//!     determine its size, returns `-1` with an error logged.\nFileOffset LoggingFileSizeByHandle(FileHandle file);\n\n//! \\brief Returns a FileHandle corresponding to the requested standard I/O\n//!     stream.\n//!\n//! The returned FileHandle should not be closed on POSIX, where it is\n//! important to maintain valid file descriptors occupying the slots reserved\n//! for these streams. If a need to close such a stream arises on POSIX,\n//! `dup2()` should instead be used to replace the existing file descriptor with\n//! one opened to `/dev/null`. See CloseStdinAndStdout().\n//!\n//! \\param[in] stdio_stream The requested standard I/O stream.\n//!\n//! \\return A corresponding FileHandle on success. kInvalidFileHandle on error,\n//!     with a message logged.\nFileHandle StdioFileHandle(StdioStream stdio_stream);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_FILE_IO_H_\n"
  },
  {
    "path": "util/file/file_io_posix.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_io.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/file.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/random_string.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nstruct ReadTraits {\n  using BufferType = void*;\n  static FileOperationResult Operate(int fd, BufferType buffer, size_t size) {\n    return read(fd, buffer, size);\n  }\n};\n\nstruct WriteTraits {\n  using BufferType = const void*;\n  static FileOperationResult Operate(int fd, BufferType buffer, size_t size) {\n    return write(fd, buffer, size);\n  }\n};\n\ntemplate <typename Traits>\nFileOperationResult ReadOrWrite(int fd,\n                                typename Traits::BufferType buffer,\n                                size_t size) {\n  constexpr size_t kMaxReadWriteSize =\n      static_cast<size_t>(std::numeric_limits<ssize_t>::max());\n  const size_t requested_bytes = std::min(size, kMaxReadWriteSize);\n\n  FileOperationResult transacted_bytes =\n      HANDLE_EINTR(Traits::Operate(fd, buffer, requested_bytes));\n  if (transacted_bytes < 0) {\n    return -1;\n  }\n\n  DCHECK_LE(static_cast<size_t>(transacted_bytes), requested_bytes);\n  return transacted_bytes;\n}\n\nFileHandle OpenFileForOutput(int rdwr_or_wronly,\n                             const base::FilePath& path,\n                             FileWriteMode mode,\n                             FilePermissions permissions) {\n#if BUILDFLAG(IS_FUCHSIA)\n  // O_NOCTTY is invalid on Fuchsia, and O_CLOEXEC isn't necessary.\n  int flags = 0;\n#else\n  int flags = O_NOCTTY | O_CLOEXEC;\n#endif\n\n  DCHECK(rdwr_or_wronly & (O_RDWR | O_WRONLY));\n  DCHECK_EQ(rdwr_or_wronly & ~(O_RDWR | O_WRONLY), 0);\n  flags |= rdwr_or_wronly;\n\n  switch (mode) {\n    case FileWriteMode::kReuseOrFail:\n      break;\n    case FileWriteMode::kReuseOrCreate:\n      flags |= O_CREAT;\n      break;\n    case FileWriteMode::kTruncateOrCreate:\n      flags |= O_CREAT | O_TRUNC;\n      break;\n    case FileWriteMode::kCreateOrFail:\n      flags |= O_CREAT | O_EXCL;\n      break;\n  }\n\n  return HANDLE_EINTR(\n      open(path.value().c_str(),\n           flags,\n           permissions == FilePermissions::kWorldReadable ? 0644 : 0600));\n}\n}  // namespace\n\nnamespace internal {\n\nFileOperationResult NativeWriteFile(FileHandle file,\n                                    const void* buffer,\n                                    size_t size) {\n  return ReadOrWrite<WriteTraits>(file, buffer, size);\n}\n\n}  // namespace internal\n\nFileOperationResult ReadFile(FileHandle file, void* buffer, size_t size) {\n  return ReadOrWrite<ReadTraits>(file, buffer, size);\n}\n\nFileHandle OpenFileForRead(const base::FilePath& path) {\n  int flags = O_RDONLY;\n#if !BUILDFLAG(IS_FUCHSIA)\n  // O_NOCTTY is invalid on Fuchsia, and O_CLOEXEC isn't necessary.\n  flags |= O_NOCTTY | O_CLOEXEC;\n#endif\n  return HANDLE_EINTR(open(path.value().c_str(), flags));\n}\n\nFileHandle OpenFileForWrite(const base::FilePath& path,\n                            FileWriteMode mode,\n                            FilePermissions permissions) {\n  return OpenFileForOutput(O_WRONLY, path, mode, permissions);\n}\n\nFileHandle OpenFileForReadAndWrite(const base::FilePath& path,\n                                   FileWriteMode mode,\n                                   FilePermissions permissions) {\n  return OpenFileForOutput(O_RDWR, path, mode, permissions);\n}\n\nFileHandle LoggingOpenFileForRead(const base::FilePath& path) {\n  FileHandle fd = OpenFileForRead(path);\n  PLOG_IF(ERROR, fd < 0) << \"open \" << path.value();\n  return fd;\n}\n\nFileHandle LoggingOpenFileForWrite(const base::FilePath& path,\n                                   FileWriteMode mode,\n                                   FilePermissions permissions) {\n  FileHandle fd = OpenFileForWrite(path, mode, permissions);\n  PLOG_IF(ERROR, fd < 0) << \"open \" << path.value();\n  return fd;\n}\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\nFileHandle LoggingOpenMemoryFileForReadAndWrite(const base::FilePath& name) {\n  DCHECK(name.value().find('/') == std::string::npos);\n\n  int result = HANDLE_EINTR(memfd_create(name.value().c_str(), 0));\n  if (result >= 0 || errno != ENOSYS) {\n    PLOG_IF(ERROR, result < 0) << \"memfd_create\";\n    return result;\n  }\n\n  const char* tmp = getenv(\"TMPDIR\");\n  tmp = tmp ? tmp : \"/tmp\";\n\n  result = HANDLE_EINTR(open(tmp, O_RDWR | O_EXCL | O_TMPFILE, 0600));\n  if (result >= 0 ||\n      // These are the expected possible error codes indicating that O_TMPFILE\n      // doesn't have kernel or filesystem support. O_TMPFILE was added in Linux\n      // 3.11. Experimentation confirms that at least Linux 2.6.29 and Linux\n      // 3.10 set errno to EISDIR. EOPNOTSUPP is returned when the filesystem\n      // doesn't support O_TMPFILE. The man pages also mention ENOENT as an\n      // error code to check, but the language implies it would only occur when\n      // |tmp| is also an invalid directory. EINVAL is mentioned as a possible\n      // error code for any invalid values in flags, but O_TMPFILE isn't\n      // mentioned explicitly in this context and hasn't been observed in\n      // practice.\n      (errno != EISDIR && errno != EOPNOTSUPP)) {\n    PLOG_IF(ERROR, result < 0) << \"open\";\n    return result;\n  }\n\n  std::string path = base::StringPrintf(\"%s/%s.%d.%s\",\n                                        tmp,\n                                        name.value().c_str(),\n                                        getpid(),\n                                        RandomString().c_str());\n  result = HANDLE_EINTR(open(path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));\n  if (result < 0) {\n    PLOG(ERROR) << \"open\";\n    return result;\n  }\n  if (unlink(path.c_str()) != 0) {\n    PLOG(WARNING) << \"unlink\";\n  }\n  return result;\n}\n#endif\n\nFileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path,\n                                          FileWriteMode mode,\n                                          FilePermissions permissions) {\n  FileHandle fd = OpenFileForReadAndWrite(path, mode, permissions);\n  PLOG_IF(ERROR, fd < 0) << \"open \" << path.value();\n  return fd;\n}\n\n#if CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\nFileLockingResult LoggingLockFile(FileHandle file,\n                                  FileLocking locking,\n                                  FileLockingBlocking blocking) {\n  int operation = (locking == FileLocking::kShared) ? LOCK_SH : LOCK_EX;\n  if (blocking == FileLockingBlocking::kNonBlocking)\n    operation |= LOCK_NB;\n\n  int rv = HANDLE_EINTR(flock(file, operation));\n  if (rv != 0) {\n    if (errno == EWOULDBLOCK) {\n      return FileLockingResult::kWouldBlock;\n    }\n    PLOG(ERROR) << \"flock\";\n    return FileLockingResult::kFailure;\n  }\n  return FileLockingResult::kSuccess;\n}\n\nbool LoggingUnlockFile(FileHandle file) {\n  int rv = flock(file, LOCK_UN);\n  PLOG_IF(ERROR, rv != 0) << \"flock\";\n  return rv == 0;\n}\n\n#endif  // CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\nFileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence) {\n  off_t rv = lseek(file, offset, whence);\n  PLOG_IF(ERROR, rv < 0) << \"lseek\";\n  return rv;\n}\n\nbool LoggingTruncateFile(FileHandle file) {\n  if (HANDLE_EINTR(ftruncate(file, 0)) != 0) {\n    PLOG(ERROR) << \"ftruncate\";\n    return false;\n  }\n  return true;\n}\n\nbool LoggingCloseFile(FileHandle file) {\n  int rv = IGNORE_EINTR(close(file));\n  PLOG_IF(ERROR, rv != 0) << \"close\";\n  return rv == 0;\n}\n\nFileOffset LoggingFileSizeByHandle(FileHandle file) {\n  struct stat st;\n  if (fstat(file, &st) != 0) {\n    PLOG(ERROR) << \"fstat\";\n    return -1;\n  }\n  return st.st_size;\n}\n\nFileHandle StdioFileHandle(StdioStream stdio_stream) {\n  switch (stdio_stream) {\n    case StdioStream::kStandardInput:\n      return STDIN_FILENO;\n    case StdioStream::kStandardOutput:\n      return STDOUT_FILENO;\n    case StdioStream::kStandardError:\n      return STDERR_FILENO;\n  }\n\n  NOTREACHED();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_io_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_io.h\"\n\n#include <stdio.h>\n\n#include <functional>\n#include <iterator>\n#include <limits>\n#include <type_traits>\n\n#include \"base/atomicops.h\"\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/file.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing testing::_;\nusing testing::InSequence;\nusing testing::Return;\n\nclass MockReadExactly {\n public:\n  MockReadExactly() = default;\n\n  MockReadExactly(const MockReadExactly&) = delete;\n  MockReadExactly& operator=(const MockReadExactly&) = delete;\n\n  ~MockReadExactly() = default;\n\n  // Since it’s more convenient for the test to use uintptr_t than void*,\n  // ReadExactlyInt() and ReadInt() adapt the types.\n\n  bool ReadExactlyInt(bool can_log, uintptr_t data, size_t size) {\n    return internal::ReadExactly(std::bind_front(&MockReadExactly::Read, this),\n                                 can_log,\n                                 reinterpret_cast<void*>(data),\n                                 size);\n  }\n\n  MOCK_METHOD(FileOperationResult, ReadInt, (bool, uintptr_t, size_t));\n\n  FileOperationResult Read(bool can_log, void* data, size_t size) {\n    return ReadInt(can_log, reinterpret_cast<uintptr_t>(data), size);\n  }\n};\n\nTEST(FileIO, ReadExactly_Zero) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(false, _, _)).Times(0);\n  EXPECT_TRUE(read_exactly.ReadExactlyInt(false, 100, 0));\n}\n\nTEST(FileIO, ReadExactly_SingleSmallSuccess) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(false, 1000, 1)).WillOnce(Return(1));\n  EXPECT_TRUE(read_exactly.ReadExactlyInt(false, 1000, 1));\n}\n\nTEST(FileIO, ReadExactly_SingleSmallSuccessCanLog) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(true, 1000, 1)).WillOnce(Return(1));\n  EXPECT_TRUE(read_exactly.ReadExactlyInt(true, 1000, 1));\n}\n\nTEST(FileIO, ReadExactly_SingleSmallFailure) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(false, 1000, 1)).WillOnce(Return(-1));\n  EXPECT_FALSE(read_exactly.ReadExactlyInt(false, 1000, 1));\n}\n\nTEST(FileIO, ReadExactly_SingleSmallFailureCanLog) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(true, 1000, 1)).WillOnce(Return(-1));\n  EXPECT_FALSE(read_exactly.ReadExactlyInt(true, 1000, 1));\n}\n\nTEST(FileIO, ReadExactly_DoubleSmallSuccess) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x1000, 2)).WillOnce(Return(1));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x1001, 1)).WillOnce(Return(1));\n  EXPECT_TRUE(read_exactly.ReadExactlyInt(false, 0x1000, 2));\n}\n\nTEST(FileIO, ReadExactly_DoubleSmallShort) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x20000, 2)).WillOnce(Return(1));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x20001, 1)).WillOnce(Return(0));\n  EXPECT_FALSE(read_exactly.ReadExactlyInt(false, 0x20000, 2));\n}\n\nTEST(FileIO, ReadExactly_DoubleSmallShortCanLog) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(true, 0x20000, 2)).WillOnce(Return(1));\n  EXPECT_CALL(read_exactly, ReadInt(true, 0x20001, 1)).WillOnce(Return(0));\n  EXPECT_FALSE(read_exactly.ReadExactlyInt(true, 0x20000, 2));\n}\n\nTEST(FileIO, ReadExactly_Medium) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x80000000, 0x20000000))\n      .WillOnce(Return(0x10000000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x90000000, 0x10000000))\n      .WillOnce(Return(0x8000000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x98000000, 0x8000000))\n      .WillOnce(Return(0x4000000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9c000000, 0x4000000))\n      .WillOnce(Return(0x2000000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9e000000, 0x2000000))\n      .WillOnce(Return(0x1000000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9f000000, 0x1000000))\n      .WillOnce(Return(0x800000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9f800000, 0x800000))\n      .WillOnce(Return(0x400000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fc00000, 0x400000))\n      .WillOnce(Return(0x200000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fe00000, 0x200000))\n      .WillOnce(Return(0x100000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ff00000, 0x100000))\n      .WillOnce(Return(0x80000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ff80000, 0x80000))\n      .WillOnce(Return(0x40000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffc0000, 0x40000))\n      .WillOnce(Return(0x20000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffe0000, 0x20000))\n      .WillOnce(Return(0x10000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fff0000, 0x10000))\n      .WillOnce(Return(0x8000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fff8000, 0x8000))\n      .WillOnce(Return(0x4000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fffc000, 0x4000))\n      .WillOnce(Return(0x2000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fffe000, 0x2000))\n      .WillOnce(Return(0x1000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffff000, 0x1000))\n      .WillOnce(Return(0x800));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffff800, 0x800))\n      .WillOnce(Return(0x400));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffffc00, 0x400))\n      .WillOnce(Return(0x200));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffffe00, 0x200))\n      .WillOnce(Return(0x100));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fffff00, 0x100))\n      .WillOnce(Return(0x80));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fffff80, 0x80))\n      .WillOnce(Return(0x40));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fffffc0, 0x40))\n      .WillOnce(Return(0x20));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fffffe0, 0x20))\n      .WillOnce(Return(0x10));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffffff0, 0x10))\n      .WillOnce(Return(0x8));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffffff8, 0x8))\n      .WillOnce(Return(0x4));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffffffc, 0x4))\n      .WillOnce(Return(0x2));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9ffffffe, 0x2))\n      .WillOnce(Return(0x1));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x9fffffff, 0x1))\n      .WillOnce(Return(0x1));\n  EXPECT_TRUE(read_exactly.ReadExactlyInt(false, 0x80000000, 0x20000000));\n}\n\nTEST(FileIO, ReadExactly_LargeSuccess) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  constexpr size_t max = std::numeric_limits<int32_t>::max();\n  constexpr size_t increment = max / 2;\n  EXPECT_CALL(read_exactly, ReadInt(false, 0, max)).WillOnce(Return(increment));\n  EXPECT_CALL(read_exactly, ReadInt(false, increment, max - increment))\n      .WillOnce(Return(increment));\n  EXPECT_CALL(read_exactly, ReadInt(false, 2 * increment, 1))\n      .WillOnce(Return(1));\n  EXPECT_TRUE(read_exactly.ReadExactlyInt(false, 0, max));\n}\n\nTEST(FileIO, ReadExactly_LargeShort) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(false, 0, 0x7fffffff))\n      .WillOnce(Return(0x3fffffff));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x3fffffff, 0x40000000))\n      .WillOnce(Return(0x10000000));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x4fffffff, 0x30000000))\n      .WillOnce(Return(0));\n  EXPECT_FALSE(read_exactly.ReadExactlyInt(false, 0, 0x7fffffff));\n}\n\nTEST(FileIO, ReadExactly_LargeFailure) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  EXPECT_CALL(read_exactly, ReadInt(false, 0, 0x7fffffff))\n      .WillOnce(Return(0x3fffffff));\n  EXPECT_CALL(read_exactly, ReadInt(false, 0x3fffffff, 0x40000000))\n      .WillOnce(Return(-1));\n  EXPECT_FALSE(read_exactly.ReadExactlyInt(false, 0, 0x7fffffff));\n}\n\nTEST(FileIO, ReadExactly_TripleMax) {\n  MockReadExactly read_exactly;\n  InSequence in_sequence;\n  // ReadExactly supports at most int32_t bytes.\n  constexpr size_t max = std::numeric_limits<int32_t>::max();\n  constexpr size_t increment = max / 2;\n  EXPECT_CALL(read_exactly, ReadInt(false, 0, max)).WillOnce(Return(increment));\n  EXPECT_CALL(read_exactly, ReadInt(false, increment, max - increment))\n      .WillOnce(Return(increment));\n  EXPECT_CALL(read_exactly, ReadInt(false, 2 * increment, 1))\n      .WillOnce(Return(1));\n  EXPECT_TRUE(read_exactly.ReadExactlyInt(false, 0, max));\n}\n\nclass MockWriteAll : public internal::WriteAllInternal {\n public:\n  MockWriteAll() : WriteAllInternal() {}\n\n  MockWriteAll(const MockWriteAll&) = delete;\n  MockWriteAll& operator=(const MockWriteAll&) = delete;\n\n  ~MockWriteAll() {}\n\n  // Since it’s more convenient for the test to use uintptr_t than const void*,\n  // WriteAllInt() and WriteInt() adapt the types.\n\n  bool WriteAllInt(uintptr_t data, size_t size) {\n    return WriteAll(reinterpret_cast<const void*>(data), size);\n  }\n\n  MOCK_METHOD(FileOperationResult, WriteInt, (uintptr_t, size_t));\n\n  // WriteAllInternal:\n  FileOperationResult Write(const void* data, size_t size) {\n    return WriteInt(reinterpret_cast<uintptr_t>(data), size);\n  }\n};\n\nTEST(FileIO, WriteAll_Zero) {\n  MockWriteAll write_all;\n  InSequence in_sequence;\n  EXPECT_CALL(write_all, WriteInt(_, _)).Times(0);\n  EXPECT_TRUE(write_all.WriteAllInt(100, 0));\n}\n\nTEST(FileIO, WriteAll_SingleSmallSuccess) {\n  MockWriteAll write_all;\n  InSequence in_sequence;\n  EXPECT_CALL(write_all, WriteInt(1000, 1)).WillOnce(Return(1));\n  EXPECT_TRUE(write_all.WriteAllInt(1000, 1));\n}\n\nTEST(FileIO, WriteAll_SingleSmallFailure) {\n  MockWriteAll write_all;\n  InSequence in_sequence;\n  EXPECT_CALL(write_all, WriteInt(1000, 1)).WillOnce(Return(-1));\n  EXPECT_FALSE(write_all.WriteAllInt(1000, 1));\n}\n\nTEST(FileIO, WriteAll_DoubleSmall) {\n  MockWriteAll write_all;\n  InSequence in_sequence;\n  EXPECT_CALL(write_all, WriteInt(0x1000, 2)).WillOnce(Return(1));\n  EXPECT_CALL(write_all, WriteInt(0x1001, 1)).WillOnce(Return(1));\n  EXPECT_TRUE(write_all.WriteAllInt(0x1000, 2));\n}\n\nTEST(FileIO, WriteAll_Medium) {\n  MockWriteAll write_all;\n  InSequence in_sequence;\n  EXPECT_CALL(write_all, WriteInt(0x80000000, 0x20000000))\n      .WillOnce(Return(0x10000000));\n  EXPECT_CALL(write_all, WriteInt(0x90000000, 0x10000000))\n      .WillOnce(Return(0x8000000));\n  EXPECT_CALL(write_all, WriteInt(0x98000000, 0x8000000))\n      .WillOnce(Return(0x4000000));\n  EXPECT_CALL(write_all, WriteInt(0x9c000000, 0x4000000))\n      .WillOnce(Return(0x2000000));\n  EXPECT_CALL(write_all, WriteInt(0x9e000000, 0x2000000))\n      .WillOnce(Return(0x1000000));\n  EXPECT_CALL(write_all, WriteInt(0x9f000000, 0x1000000))\n      .WillOnce(Return(0x800000));\n  EXPECT_CALL(write_all, WriteInt(0x9f800000, 0x800000))\n      .WillOnce(Return(0x400000));\n  EXPECT_CALL(write_all, WriteInt(0x9fc00000, 0x400000))\n      .WillOnce(Return(0x200000));\n  EXPECT_CALL(write_all, WriteInt(0x9fe00000, 0x200000))\n      .WillOnce(Return(0x100000));\n  EXPECT_CALL(write_all, WriteInt(0x9ff00000, 0x100000))\n      .WillOnce(Return(0x80000));\n  EXPECT_CALL(write_all, WriteInt(0x9ff80000, 0x80000))\n      .WillOnce(Return(0x40000));\n  EXPECT_CALL(write_all, WriteInt(0x9ffc0000, 0x40000))\n      .WillOnce(Return(0x20000));\n  EXPECT_CALL(write_all, WriteInt(0x9ffe0000, 0x20000))\n      .WillOnce(Return(0x10000));\n  EXPECT_CALL(write_all, WriteInt(0x9fff0000, 0x10000))\n      .WillOnce(Return(0x8000));\n  EXPECT_CALL(write_all, WriteInt(0x9fff8000, 0x8000)).WillOnce(Return(0x4000));\n  EXPECT_CALL(write_all, WriteInt(0x9fffc000, 0x4000)).WillOnce(Return(0x2000));\n  EXPECT_CALL(write_all, WriteInt(0x9fffe000, 0x2000)).WillOnce(Return(0x1000));\n  EXPECT_CALL(write_all, WriteInt(0x9ffff000, 0x1000)).WillOnce(Return(0x800));\n  EXPECT_CALL(write_all, WriteInt(0x9ffff800, 0x800)).WillOnce(Return(0x400));\n  EXPECT_CALL(write_all, WriteInt(0x9ffffc00, 0x400)).WillOnce(Return(0x200));\n  EXPECT_CALL(write_all, WriteInt(0x9ffffe00, 0x200)).WillOnce(Return(0x100));\n  EXPECT_CALL(write_all, WriteInt(0x9fffff00, 0x100)).WillOnce(Return(0x80));\n  EXPECT_CALL(write_all, WriteInt(0x9fffff80, 0x80)).WillOnce(Return(0x40));\n  EXPECT_CALL(write_all, WriteInt(0x9fffffc0, 0x40)).WillOnce(Return(0x20));\n  EXPECT_CALL(write_all, WriteInt(0x9fffffe0, 0x20)).WillOnce(Return(0x10));\n  EXPECT_CALL(write_all, WriteInt(0x9ffffff0, 0x10)).WillOnce(Return(0x8));\n  EXPECT_CALL(write_all, WriteInt(0x9ffffff8, 0x8)).WillOnce(Return(0x4));\n  EXPECT_CALL(write_all, WriteInt(0x9ffffffc, 0x4)).WillOnce(Return(0x2));\n  EXPECT_CALL(write_all, WriteInt(0x9ffffffe, 0x2)).WillOnce(Return(0x1));\n  EXPECT_CALL(write_all, WriteInt(0x9fffffff, 0x1)).WillOnce(Return(0x1));\n  EXPECT_TRUE(write_all.WriteAllInt(0x80000000, 0x20000000));\n}\n\nTEST(FileIO, WriteAll_LargeSuccess) {\n  MockWriteAll write_all;\n  InSequence in_sequence;\n  constexpr size_t max = std::numeric_limits<uint32_t>::max();\n  constexpr size_t increment = std::numeric_limits<int32_t>::max();\n  EXPECT_CALL(write_all, WriteInt(0, max)).WillOnce(Return(increment));\n  EXPECT_CALL(write_all, WriteInt(increment, max - increment))\n      .WillOnce(Return(increment));\n  EXPECT_CALL(write_all, WriteInt(2 * increment, 1)).WillOnce(Return(1));\n  EXPECT_TRUE(write_all.WriteAllInt(0, max));\n}\n\nTEST(FileIO, WriteAll_LargeFailure) {\n  MockWriteAll write_all;\n  InSequence in_sequence;\n  EXPECT_CALL(write_all, WriteInt(0, 0xffffffff)).WillOnce(Return(0x7fffffff));\n  EXPECT_CALL(write_all, WriteInt(0x7fffffff, 0x80000000)).WillOnce(Return(-1));\n  EXPECT_FALSE(write_all.WriteAllInt(0, 0xffffffff));\n}\n\nTEST(FileIO, WriteAll_TripleMax) {\n  MockWriteAll write_all;\n  InSequence in_sequence;\n  constexpr size_t max = std::numeric_limits<size_t>::max();\n  constexpr size_t increment =\n      std::numeric_limits<std::make_signed<size_t>::type>::max();\n  EXPECT_CALL(write_all, WriteInt(0, max)).WillOnce(Return(increment));\n  EXPECT_CALL(write_all, WriteInt(increment, max - increment))\n      .WillOnce(Return(increment));\n  EXPECT_CALL(write_all, WriteInt(2 * increment, 1)).WillOnce(Return(1));\n  EXPECT_TRUE(write_all.WriteAllInt(0, max));\n}\n\nvoid TestOpenFileForWrite(FileHandle (*opener)(const base::FilePath&,\n                                               FileWriteMode,\n                                               FilePermissions)) {\n  ScopedTempDir temp_dir;\n  base::FilePath file_path_1 =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"file_1\"));\n  ASSERT_FALSE(FileExists(file_path_1));\n\n  ScopedFileHandle file_handle(opener(file_path_1,\n                                      FileWriteMode::kReuseOrFail,\n                                      FilePermissions::kWorldReadable));\n  EXPECT_EQ(file_handle, kInvalidFileHandle);\n  EXPECT_FALSE(FileExists(file_path_1));\n\n  file_handle.reset(opener(file_path_1,\n                           FileWriteMode::kCreateOrFail,\n                           FilePermissions::kWorldReadable));\n  EXPECT_NE(file_handle, kInvalidFileHandle);\n  EXPECT_TRUE(FileExists(file_path_1));\n  EXPECT_EQ(FileSize(file_path_1), 0);\n\n  file_handle.reset(opener(file_path_1,\n                           FileWriteMode::kReuseOrCreate,\n                           FilePermissions::kWorldReadable));\n  EXPECT_NE(file_handle, kInvalidFileHandle);\n  EXPECT_TRUE(FileExists(file_path_1));\n  EXPECT_EQ(FileSize(file_path_1), 0);\n\n  constexpr char data = '%';\n  EXPECT_TRUE(LoggingWriteFile(file_handle.get(), &data, sizeof(data)));\n\n  // Close file_handle to ensure that the write is flushed to disk.\n  file_handle.reset();\n  EXPECT_EQ(FileSize(file_path_1), implicit_cast<FileOffset>(sizeof(data)));\n\n  file_handle.reset(opener(file_path_1,\n                           FileWriteMode::kReuseOrCreate,\n                           FilePermissions::kWorldReadable));\n  EXPECT_NE(file_handle, kInvalidFileHandle);\n  EXPECT_TRUE(FileExists(file_path_1));\n  EXPECT_EQ(FileSize(file_path_1), implicit_cast<FileOffset>(sizeof(data)));\n\n  file_handle.reset(opener(file_path_1,\n                           FileWriteMode::kCreateOrFail,\n                           FilePermissions::kWorldReadable));\n  EXPECT_EQ(file_handle, kInvalidFileHandle);\n  EXPECT_TRUE(FileExists(file_path_1));\n  EXPECT_EQ(FileSize(file_path_1), implicit_cast<FileOffset>(sizeof(data)));\n\n  file_handle.reset(opener(file_path_1,\n                           FileWriteMode::kReuseOrFail,\n                           FilePermissions::kWorldReadable));\n  EXPECT_NE(file_handle, kInvalidFileHandle);\n  EXPECT_TRUE(FileExists(file_path_1));\n  EXPECT_EQ(FileSize(file_path_1), implicit_cast<FileOffset>(sizeof(data)));\n\n  file_handle.reset(opener(file_path_1,\n                           FileWriteMode::kTruncateOrCreate,\n                           FilePermissions::kWorldReadable));\n  EXPECT_NE(file_handle, kInvalidFileHandle);\n  EXPECT_TRUE(FileExists(file_path_1));\n  EXPECT_EQ(FileSize(file_path_1), 0);\n\n  base::FilePath file_path_2 =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"file_2\"));\n  ASSERT_FALSE(FileExists(file_path_2));\n\n  file_handle.reset(opener(file_path_2,\n                           FileWriteMode::kTruncateOrCreate,\n                           FilePermissions::kWorldReadable));\n  EXPECT_NE(file_handle, kInvalidFileHandle);\n  EXPECT_TRUE(FileExists(file_path_2));\n  EXPECT_EQ(FileSize(file_path_2), 0);\n\n  base::FilePath file_path_3 =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"file_3\"));\n  ASSERT_FALSE(FileExists(file_path_3));\n\n  file_handle.reset(opener(file_path_3,\n                           FileWriteMode::kReuseOrCreate,\n                           FilePermissions::kWorldReadable));\n  EXPECT_NE(file_handle, kInvalidFileHandle);\n  EXPECT_TRUE(FileExists(file_path_3));\n  EXPECT_EQ(FileSize(file_path_3), 0);\n}\n\nTEST(FileIO, OpenFileForWrite) {\n  TestOpenFileForWrite(OpenFileForWrite);\n}\n\nTEST(FileIO, OpenFileForReadAndWrite) {\n  TestOpenFileForWrite(OpenFileForReadAndWrite);\n}\n\nTEST(FileIO, LoggingOpenFileForWrite) {\n  TestOpenFileForWrite(LoggingOpenFileForWrite);\n}\n\nTEST(FileIO, LoggingOpenFileForReadAndWrite) {\n  TestOpenFileForWrite(LoggingOpenFileForReadAndWrite);\n}\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\nTEST(FileIO, LoggingOpenMemoryFileForReadAndWrite) {\n  ScopedFileHandle handle(\n      LoggingOpenMemoryFileForReadAndWrite(base::FilePath(\"memfile\")));\n  ASSERT_TRUE(handle.is_valid());\n\n  static constexpr char kTestData[] = \"somedata\";\n  ASSERT_TRUE(LoggingWriteFile(handle.get(), kTestData, sizeof(kTestData)));\n\n  ASSERT_EQ(LoggingSeekFile(handle.get(), 0, SEEK_SET), 0);\n\n  char buffer[sizeof(kTestData)];\n  ASSERT_TRUE(LoggingReadFileExactly(handle.get(), buffer, sizeof(buffer)));\n  EXPECT_EQ(memcmp(buffer, kTestData, sizeof(buffer)), 0);\n}\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n\nenum class ReadOrWrite : bool {\n  kRead,\n  kWrite,\n};\n\nvoid FileShareModeTest(ReadOrWrite first, ReadOrWrite second) {\n  ScopedTempDir temp_dir;\n  base::FilePath shared_file =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"shared_file\"));\n  {\n    // Create an empty file to work on.\n    ScopedFileHandle create(\n        LoggingOpenFileForWrite(shared_file,\n                                FileWriteMode::kCreateOrFail,\n                                FilePermissions::kOwnerOnly));\n  }\n\n  auto handle1 = ScopedFileHandle(\n      (first == ReadOrWrite::kRead)\n          ? LoggingOpenFileForRead(shared_file)\n          : LoggingOpenFileForWrite(shared_file,\n                                    FileWriteMode::kReuseOrCreate,\n                                    FilePermissions::kOwnerOnly));\n  ASSERT_NE(handle1, kInvalidFileHandle);\n  auto handle2 = ScopedFileHandle(\n      (second == ReadOrWrite::kRead)\n          ? LoggingOpenFileForRead(shared_file)\n          : LoggingOpenFileForWrite(shared_file,\n                                    FileWriteMode::kReuseOrCreate,\n                                    FilePermissions::kOwnerOnly));\n  EXPECT_NE(handle2, kInvalidFileHandle);\n\n  EXPECT_NE(handle1.get(), handle2.get());\n}\n\nTEST(FileIO, FileShareMode_Read_Read) {\n  FileShareModeTest(ReadOrWrite::kRead, ReadOrWrite::kRead);\n}\n\nTEST(FileIO, FileShareMode_Read_Write) {\n  FileShareModeTest(ReadOrWrite::kRead, ReadOrWrite::kWrite);\n}\n\nTEST(FileIO, FileShareMode_Write_Read) {\n  FileShareModeTest(ReadOrWrite::kWrite, ReadOrWrite::kRead);\n}\n\nTEST(FileIO, FileShareMode_Write_Write) {\n  FileShareModeTest(ReadOrWrite::kWrite, ReadOrWrite::kWrite);\n}\n\n// Fuchsia does not currently support any sort of file locking. See\n// https://crashpad.chromium.org/bug/196 and\n// https://crashpad.chromium.org/bug/217.\n// Android can conditionally not support file locking depending on what type of\n// filesystem is being used to store settings.dat.\n#if CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\nTEST(FileIO, MultipleSharedLocks) {\n  ScopedTempDir temp_dir;\n  base::FilePath shared_file =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"file_to_lock\"));\n\n  {\n    // Create an empty file to lock.\n    ScopedFileHandle create(\n        LoggingOpenFileForWrite(shared_file,\n                                FileWriteMode::kCreateOrFail,\n                                FilePermissions::kOwnerOnly));\n  }\n\n  auto handle1 = ScopedFileHandle(LoggingOpenFileForRead(shared_file));\n  ASSERT_NE(handle1, kInvalidFileHandle);\n  EXPECT_EQ(\n      LoggingLockFile(\n          handle1.get(), FileLocking::kShared, FileLockingBlocking::kBlocking),\n      FileLockingResult::kSuccess);\n\n  auto handle2 = ScopedFileHandle(LoggingOpenFileForRead(shared_file));\n  ASSERT_NE(handle1, kInvalidFileHandle);\n  EXPECT_EQ(\n      LoggingLockFile(\n          handle2.get(), FileLocking::kShared, FileLockingBlocking::kBlocking),\n      FileLockingResult::kSuccess);\n\n  EXPECT_TRUE(LoggingUnlockFile(handle1.get()));\n  EXPECT_TRUE(LoggingUnlockFile(handle2.get()));\n}\n\nclass LockingTestThread : public Thread {\n public:\n  LockingTestThread()\n      : file_(), lock_type_(), iterations_(), actual_iterations_() {}\n\n  LockingTestThread(const LockingTestThread&) = delete;\n  LockingTestThread& operator=(const LockingTestThread&) = delete;\n\n  void Init(FileHandle file,\n            FileLocking lock_type,\n            int iterations,\n            base::subtle::Atomic32* actual_iterations) {\n    ASSERT_NE(file, kInvalidFileHandle);\n    file_ = ScopedFileHandle(file);\n    lock_type_ = lock_type;\n    iterations_ = iterations;\n    actual_iterations_ = actual_iterations;\n  }\n\n private:\n  void ThreadMain() override {\n    for (int i = 0; i < iterations_; ++i) {\n      EXPECT_EQ(LoggingLockFile(\n                    file_.get(), lock_type_, FileLockingBlocking::kBlocking),\n                FileLockingResult::kSuccess);\n      base::subtle::NoBarrier_AtomicIncrement(actual_iterations_, 1);\n      EXPECT_TRUE(LoggingUnlockFile(file_.get()));\n    }\n  }\n\n  ScopedFileHandle file_;\n  FileLocking lock_type_;\n  int iterations_;\n  base::subtle::Atomic32* actual_iterations_;\n};\n\nvoid LockingTest(FileLocking main_lock, FileLocking other_locks) {\n  ScopedTempDir temp_dir;\n  base::FilePath shared_file =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"file_to_lock\"));\n\n  {\n    // Create an empty file to lock.\n    ScopedFileHandle create(\n        LoggingOpenFileForWrite(shared_file,\n                                FileWriteMode::kCreateOrFail,\n                                FilePermissions::kOwnerOnly));\n  }\n\n  auto initial = ScopedFileHandle(\n      (main_lock == FileLocking::kShared)\n          ? LoggingOpenFileForRead(shared_file)\n          : LoggingOpenFileForWrite(shared_file,\n                                    FileWriteMode::kReuseOrCreate,\n                                    FilePermissions::kOwnerOnly));\n  ASSERT_NE(initial, kInvalidFileHandle);\n  EXPECT_EQ(\n      LoggingLockFile(initial.get(), main_lock, FileLockingBlocking::kBlocking),\n      FileLockingResult::kSuccess);\n\n  base::subtle::Atomic32 actual_iterations = 0;\n\n  LockingTestThread threads[20];\n  int expected_iterations = 0;\n  for (size_t index = 0; index < std::size(threads); ++index) {\n    int iterations_for_this_thread = static_cast<int>(index * 10);\n    threads[index].Init(\n        (other_locks == FileLocking::kShared)\n            ? LoggingOpenFileForRead(shared_file)\n            : LoggingOpenFileForWrite(shared_file,\n                                      FileWriteMode::kReuseOrCreate,\n                                      FilePermissions::kOwnerOnly),\n        other_locks,\n        iterations_for_this_thread,\n        &actual_iterations);\n    expected_iterations += iterations_for_this_thread;\n\n    ASSERT_NO_FATAL_FAILURE(threads[index].Start());\n  }\n\n  base::subtle::Atomic32 result =\n      base::subtle::NoBarrier_Load(&actual_iterations);\n  EXPECT_EQ(result, 0);\n\n  ASSERT_TRUE(LoggingUnlockFile(initial.get()));\n\n  for (auto& t : threads)\n    t.Join();\n\n  result = base::subtle::NoBarrier_Load(&actual_iterations);\n  EXPECT_EQ(result, expected_iterations);\n}\n\nTEST(FileIO, ExclusiveVsExclusives) {\n  LockingTest(FileLocking::kExclusive, FileLocking::kExclusive);\n}\n\nTEST(FileIO, ExclusiveVsShareds) {\n  LockingTest(FileLocking::kExclusive, FileLocking::kShared);\n}\n\nTEST(FileIO, SharedVsExclusives) {\n  LockingTest(FileLocking::kShared, FileLocking::kExclusive);\n}\n\nTEST(FileIO, ExclusiveVsExclusivesNonBlocking) {\n  ScopedTempDir temp_dir;\n  base::FilePath exclusive_file =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"file_to_lock\"));\n\n  {\n    // Create an empty file to lock.\n    ScopedFileHandle create(\n        LoggingOpenFileForWrite(exclusive_file,\n                                FileWriteMode::kCreateOrFail,\n                                FilePermissions::kOwnerOnly));\n  }\n\n  auto handle1 = ScopedFileHandle(LoggingOpenFileForRead(exclusive_file));\n  ASSERT_NE(handle1, kInvalidFileHandle);\n  EXPECT_EQ(LoggingLockFile(handle1.get(),\n                            FileLocking::kExclusive,\n                            FileLockingBlocking::kBlocking),\n            FileLockingResult::kSuccess);\n\n  // Non-blocking lock should fail.\n  auto handle2 = ScopedFileHandle(LoggingOpenFileForRead(exclusive_file));\n  ASSERT_NE(handle2, kInvalidFileHandle);\n  EXPECT_EQ(LoggingLockFile(handle2.get(),\n                            FileLocking::kExclusive,\n                            FileLockingBlocking::kNonBlocking),\n            FileLockingResult::kWouldBlock);\n\n  // After unlocking, non-blocking lock should succeed.\n  EXPECT_TRUE(LoggingUnlockFile(handle1.get()));\n  EXPECT_EQ(LoggingLockFile(handle2.get(),\n                            FileLocking::kExclusive,\n                            FileLockingBlocking::kNonBlocking),\n            FileLockingResult::kSuccess);\n  EXPECT_TRUE(LoggingUnlockFile(handle2.get()));\n}\n\n#endif  // CRASHPAD_FLOCK_ALWAYS_SUPPORTED\n\nTEST(FileIO, FileSizeByHandle) {\n  EXPECT_EQ(LoggingFileSizeByHandle(kInvalidFileHandle), -1);\n\n  ScopedTempDir temp_dir;\n  base::FilePath file_path =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"file_size\"));\n\n  ScopedFileHandle file_handle(LoggingOpenFileForWrite(\n      file_path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));\n  ASSERT_NE(file_handle.get(), kInvalidFileHandle);\n  EXPECT_EQ(LoggingFileSizeByHandle(file_handle.get()), 0);\n\n  static constexpr char data[] = \"zippyzap\";\n  ASSERT_TRUE(LoggingWriteFile(file_handle.get(), &data, sizeof(data)));\n\n  EXPECT_EQ(LoggingFileSizeByHandle(file_handle.get()), 9);\n}\n\nFileHandle FileHandleForFILE(FILE* file) {\n  int fd = fileno(file);\n#if BUILDFLAG(IS_POSIX)\n  return fd;\n#elif BUILDFLAG(IS_WIN)\n  return reinterpret_cast<HANDLE>(_get_osfhandle(fd));\n#else\n#error Port\n#endif\n}\n\nTEST(FileIO, StdioFileHandle) {\n  EXPECT_EQ(StdioFileHandle(StdioStream::kStandardInput),\n            FileHandleForFILE(stdin));\n  EXPECT_EQ(StdioFileHandle(StdioStream::kStandardOutput),\n            FileHandleForFILE(stdout));\n  EXPECT_EQ(StdioFileHandle(StdioStream::kStandardError),\n            FileHandleForFILE(stderr));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_io_win.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_io.h\"\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/strings/utf_string_conversions.h\"\n\nnamespace {\n\nbool IsSocketHandle(HANDLE file) {\n  if (GetFileType(file) == FILE_TYPE_PIPE) {\n    // FILE_TYPE_PIPE means that it's a socket, a named pipe, or an anonymous\n    // pipe. If we are unable to retrieve the pipe information, we know it's a\n    // socket.\n    return !GetNamedPipeInfo(file, nullptr, nullptr, nullptr, nullptr);\n  }\n  return false;\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nnamespace {\n\n// kMaxReadWriteSize needs to be limited to the range of DWORD for the calls to\n// ::ReadFile() and ::WriteFile(), and also limited to the range of\n// FileOperationResult to be able to adequately express the number of bytes read\n// and written in the return values from ReadFile() and NativeWriteFile(). In a\n// 64-bit build, the former will control, and the limit will be (2^32)-1. In a\n// 32-bit build, the latter will control, and the limit will be (2^31)-1.\nconstexpr size_t kMaxReadWriteSize = std::min(\n    static_cast<size_t>(std::numeric_limits<DWORD>::max()),\n    static_cast<size_t>(std::numeric_limits<FileOperationResult>::max()));\n\nFileHandle OpenFileForOutput(DWORD access,\n                             const base::FilePath& path,\n                             FileWriteMode mode,\n                             FilePermissions permissions) {\n  DCHECK(access & GENERIC_WRITE);\n  DCHECK_EQ(access & ~(GENERIC_READ | GENERIC_WRITE), 0u);\n\n  DWORD disposition = 0;\n  switch (mode) {\n    case FileWriteMode::kReuseOrFail:\n      disposition = OPEN_EXISTING;\n      break;\n    case FileWriteMode::kReuseOrCreate:\n      disposition = OPEN_ALWAYS;\n      break;\n    case FileWriteMode::kTruncateOrCreate:\n      disposition = CREATE_ALWAYS;\n      break;\n    case FileWriteMode::kCreateOrFail:\n      disposition = CREATE_NEW;\n      break;\n  }\n  return CreateFile(path.value().c_str(),\n                    access,\n                    FILE_SHARE_READ | FILE_SHARE_WRITE,\n                    nullptr,\n                    disposition,\n                    FILE_ATTRIBUTE_NORMAL,\n                    nullptr);\n}\n\n}  // namespace\n\nnamespace internal {\n\nFileOperationResult NativeWriteFile(FileHandle file,\n                                    const void* buffer,\n                                    size_t size) {\n  // TODO(scottmg): This might need to handle the limit for pipes across a\n  // network in the future.\n\n  const DWORD write_size =\n      static_cast<DWORD>(std::min(size, kMaxReadWriteSize));\n\n  DWORD bytes_written;\n  if (!::WriteFile(file, buffer, write_size, &bytes_written, nullptr))\n    return -1;\n\n  CHECK_NE(bytes_written, static_cast<DWORD>(-1));\n  DCHECK_LE(static_cast<size_t>(bytes_written), write_size);\n  return bytes_written;\n}\n\n}  // namespace internal\n\nFileOperationResult ReadFile(FileHandle file, void* buffer, size_t size) {\n  DCHECK(!IsSocketHandle(file));\n\n  const DWORD read_size = static_cast<DWORD>(std::min(size, kMaxReadWriteSize));\n\n  while (true) {\n    DWORD bytes_read;\n    BOOL success = ::ReadFile(file, buffer, read_size, &bytes_read, nullptr);\n    if (!success) {\n      if (GetLastError() == ERROR_BROKEN_PIPE) {\n        // When reading a pipe and the write handle has been closed, ReadFile\n        // fails with ERROR_BROKEN_PIPE, but only once all pending data has been\n        // read. Treat this as EOF.\n        return 0;\n      }\n      return -1;\n    }\n\n    CHECK_NE(bytes_read, static_cast<DWORD>(-1));\n    DCHECK_LE(bytes_read, read_size);\n    if (bytes_read != 0 || GetFileType(file) != FILE_TYPE_PIPE) {\n      // Zero bytes read for a file indicates reaching EOF. Zero bytes read from\n      // a pipe indicates only that there was a zero byte WriteFile issued on\n      // the other end, so continue reading.\n      return bytes_read;\n    }\n  }\n}\n\nFileHandle OpenFileForRead(const base::FilePath& path) {\n  return CreateFile(path.value().c_str(),\n                    GENERIC_READ,\n                    FILE_SHARE_READ | FILE_SHARE_WRITE,\n                    nullptr,\n                    OPEN_EXISTING,\n                    0,\n                    nullptr);\n}\n\nFileHandle OpenFileForWrite(const base::FilePath& path,\n                            FileWriteMode mode,\n                            FilePermissions permissions) {\n  return OpenFileForOutput(GENERIC_WRITE, path, mode, permissions);\n}\n\nFileHandle OpenFileForReadAndWrite(const base::FilePath& path,\n                                   FileWriteMode mode,\n                                   FilePermissions permissions) {\n  return OpenFileForOutput(\n      GENERIC_READ | GENERIC_WRITE, path, mode, permissions);\n}\n\nFileHandle LoggingOpenFileForRead(const base::FilePath& path) {\n  FileHandle file = OpenFileForRead(path);\n  PLOG_IF(ERROR, file == INVALID_HANDLE_VALUE)\n      << \"CreateFile \" << base::WideToUTF8(path.value());\n  return file;\n}\n\nFileHandle LoggingOpenFileForWrite(const base::FilePath& path,\n                                   FileWriteMode mode,\n                                   FilePermissions permissions) {\n  FileHandle file = OpenFileForWrite(path, mode, permissions);\n  PLOG_IF(ERROR, file == INVALID_HANDLE_VALUE)\n      << \"CreateFile \" << base::WideToUTF8(path.value());\n  return file;\n}\n\nFileHandle LoggingOpenFileForReadAndWrite(const base::FilePath& path,\n                                          FileWriteMode mode,\n                                          FilePermissions permissions) {\n  FileHandle file = OpenFileForReadAndWrite(path, mode, permissions);\n  PLOG_IF(ERROR, file == INVALID_HANDLE_VALUE)\n      << \"CreateFile \" << base::WideToUTF8(path.value());\n  return file;\n}\n\nFileLockingResult LoggingLockFile(FileHandle file,\n                                  FileLocking locking,\n                                  FileLockingBlocking blocking) {\n  DWORD flags =\n      (locking == FileLocking::kExclusive) ? LOCKFILE_EXCLUSIVE_LOCK : 0;\n  if (blocking == FileLockingBlocking::kNonBlocking)\n    flags |= LOCKFILE_FAIL_IMMEDIATELY;\n\n  // Note that the `Offset` fields of overlapped indicate the start location for\n  // locking (beginning of file in this case), and `hEvent` must be also be set\n  // to 0.\n  OVERLAPPED overlapped = {0};\n  if (!LockFileEx(file, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) {\n    if (GetLastError() == ERROR_LOCK_VIOLATION) {\n      return FileLockingResult::kWouldBlock;\n    }\n    PLOG(ERROR) << \"LockFileEx\";\n    return FileLockingResult::kFailure;\n  }\n  return FileLockingResult::kSuccess;\n}\n\nbool LoggingUnlockFile(FileHandle file) {\n  // Note that the `Offset` fields of overlapped indicate the start location for\n  // locking (beginning of file in this case), and `hEvent` must be also be set\n  // to 0.\n  OVERLAPPED overlapped = {0};\n  if (!UnlockFileEx(file, 0, MAXDWORD, MAXDWORD, &overlapped)) {\n    PLOG(ERROR) << \"UnlockFileEx\";\n    return false;\n  }\n  return true;\n}\n\nFileOffset LoggingSeekFile(FileHandle file, FileOffset offset, int whence) {\n  DWORD method = 0;\n  switch (whence) {\n    case SEEK_SET:\n      method = FILE_BEGIN;\n      break;\n    case SEEK_CUR:\n      method = FILE_CURRENT;\n      break;\n    case SEEK_END:\n      method = FILE_END;\n      break;\n    default:\n      NOTREACHED();\n  }\n\n  LARGE_INTEGER distance_to_move;\n  distance_to_move.QuadPart = offset;\n  LARGE_INTEGER new_offset;\n  BOOL result = SetFilePointerEx(file, distance_to_move, &new_offset, method);\n  if (!result) {\n    PLOG(ERROR) << \"SetFilePointerEx\";\n    return -1;\n  }\n  return new_offset.QuadPart;\n}\n\nbool LoggingTruncateFile(FileHandle file) {\n  if (LoggingSeekFile(file, 0, SEEK_SET) != 0)\n    return false;\n  if (!SetEndOfFile(file)) {\n    PLOG(ERROR) << \"SetEndOfFile\";\n    return false;\n  }\n  return true;\n}\n\nbool LoggingCloseFile(FileHandle file) {\n  BOOL rv = CloseHandle(file);\n  PLOG_IF(ERROR, !rv) << \"CloseHandle\";\n  return !!rv;\n}\n\nFileOffset LoggingFileSizeByHandle(FileHandle file) {\n  LARGE_INTEGER file_size;\n  if (!GetFileSizeEx(file, &file_size)) {\n    PLOG(ERROR) << \"GetFileSizeEx\";\n    return -1;\n  }\n  return file_size.QuadPart;\n}\n\nFileHandle StdioFileHandle(StdioStream stdio_stream) {\n  DWORD standard_handle;\n  switch (stdio_stream) {\n    case StdioStream::kStandardInput:\n      standard_handle = STD_INPUT_HANDLE;\n      break;\n    case StdioStream::kStandardOutput:\n      standard_handle = STD_OUTPUT_HANDLE;\n      break;\n    case StdioStream::kStandardError:\n      standard_handle = STD_ERROR_HANDLE;\n      break;\n    default:\n      NOTREACHED();\n  }\n\n  HANDLE handle = GetStdHandle(standard_handle);\n  PLOG_IF(ERROR, handle == INVALID_HANDLE_VALUE) << \"GetStdHandle\";\n  return handle;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_reader.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_reader.h\"\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\nbool FileReaderInterface::ReadExactly(void* data, size_t size) {\n  return internal::ReadExactly(\n      [this](bool can_log, void* buffer, size_t size) {\n        DCHECK(can_log);\n        return Read(buffer, size);\n      },\n      true,\n      data,\n      size);\n}\n\nWeakFileHandleFileReader::WeakFileHandleFileReader(FileHandle file_handle)\n    : file_handle_(file_handle) {\n}\n\nWeakFileHandleFileReader::~WeakFileHandleFileReader() {\n}\n\nFileOperationResult WeakFileHandleFileReader::Read(void* data, size_t size) {\n  DCHECK_NE(file_handle_, kInvalidFileHandle);\n\n  base::checked_cast<FileOperationResult>(size);\n  FileOperationResult rv = ReadFile(file_handle_, data, size);\n  if (rv < 0) {\n    PLOG(ERROR) << internal::kNativeReadFunctionName;\n    return -1;\n  }\n\n  return rv;\n}\n\nFileOffset WeakFileHandleFileReader::Seek(FileOffset offset, int whence) {\n  DCHECK_NE(file_handle_, kInvalidFileHandle);\n  return LoggingSeekFile(file_handle_, offset, whence);\n}\n\nFileReader::FileReader()\n    : file_(),\n      weak_file_handle_file_reader_(kInvalidFileHandle) {\n}\n\nFileReader::~FileReader() {\n}\n\nbool FileReader::Open(const base::FilePath& path) {\n  CHECK(!file_.is_valid());\n  file_.reset(LoggingOpenFileForRead(path));\n  if (!file_.is_valid()) {\n    return false;\n  }\n\n  weak_file_handle_file_reader_.set_file_handle(file_.get());\n  return true;\n}\n\nvoid FileReader::Close() {\n  CHECK(file_.is_valid());\n\n  weak_file_handle_file_reader_.set_file_handle(kInvalidFileHandle);\n  file_.reset();\n}\n\nFileOperationResult FileReader::Read(void* data, size_t size) {\n  DCHECK(file_.is_valid());\n  return weak_file_handle_file_reader_.Read(data, size);\n}\n\nFileOffset FileReader::Seek(FileOffset offset, int whence) {\n  DCHECK(file_.is_valid());\n  return weak_file_handle_file_reader_.Seek(offset, whence);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_reader.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_FILE_READER_H_\n#define CRASHPAD_UTIL_FILE_FILE_READER_H_\n\n#include <sys/types.h>\n\n#include \"base/files/file_path.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/file_seeker.h\"\n\nnamespace crashpad {\n\n//! \\brief An interface to read to files and other file-like objects with\n//!     semantics matching the underlying platform (POSIX or Windows).\nclass FileReaderInterface : public virtual FileSeekerInterface {\n public:\n  virtual ~FileReaderInterface() {}\n\n  //! \\brief Wraps ReadFile(), or provides an implementation with identical\n  //!     semantics.\n  //!\n  //! \\return The number of bytes actually read if the operation succeeded,\n  //!     which may be `0` or any positive value less than or equal to \\a size.\n  //!     `-1` if the operation failed, with an error message logged.\n  virtual FileOperationResult Read(void* data, size_t size) = 0;\n\n  //! \\brief Wraps Read(), ensuring that the read succeeded and exactly \\a size\n  //!     bytes were read.\n  //!\n  //! Semantically, this behaves as LoggingReadFileExactly().\n  //!\n  //! \\return `true` if the operation succeeded, `false` if it failed, with an\n  //!     error message logged. Short reads are treated as failures.\n  bool ReadExactly(void* data, size_t size);\n};\n\n//! \\brief A file reader backed by a FileHandle.\n//!\n//! FileReader requires users to provide a FilePath to open, but this class\n//! accepts an already-open FileHandle instead. Like FileReader, this class may\n//! read from a filesystem-based file, but unlike FileReader, this class is not\n//! responsible for opening or closing the file. Users of this class must ensure\n//! that the file handle is closed appropriately elsewhere. Objects of this\n//! class may be used to read from file handles not associated with\n//! filesystem-based files, although special attention should be paid to the\n//! Seek() method, which may not function on file handles that do not refer to\n//! disk-based files.\n//!\n//! This class is expected to be used when other code is responsible for\n//! opening files and already provides file handles.\nclass WeakFileHandleFileReader : public FileReaderInterface {\n public:\n  explicit WeakFileHandleFileReader(FileHandle file_handle);\n\n  WeakFileHandleFileReader(const WeakFileHandleFileReader&) = delete;\n  WeakFileHandleFileReader& operator=(const WeakFileHandleFileReader&) = delete;\n\n  ~WeakFileHandleFileReader() override;\n\n  // FileReaderInterface:\n  FileOperationResult Read(void* data, size_t size) override;\n\n  // FileSeekerInterface:\n\n  //! \\copydoc FileReaderInterface::Seek()\n  //!\n  //! \\note This method is only guaranteed to function on file handles referring\n  //!     to disk-based files.\n  FileOffset Seek(FileOffset offset, int whence) override;\n\n private:\n  void set_file_handle(FileHandle file_handle) { file_handle_ = file_handle; }\n\n  FileHandle file_handle_;  // weak\n\n  // FileReader uses this class as its internal implementation, and it needs to\n  // be able to call set_file_handle(). FileReader cannot initialize a\n  // WeakFileHandleFileReader with a correct file descriptor at the time of\n  // construction because no file descriptor will be available until\n  // FileReader::Open() is called.\n  friend class FileReader;\n};\n\n//! \\brief A file reader implementation that wraps traditional system file\n//!     operations on files accessed through the filesystem.\nclass FileReader : public FileReaderInterface {\n public:\n  FileReader();\n\n  FileReader(const FileReader&) = delete;\n  FileReader& operator=(const FileReader&) = delete;\n\n  ~FileReader() override;\n\n  // FileReaderInterface:\n\n  //! \\brief Wraps LoggingOpenFileForRead().\n  //!\n  //! \\return `true` if the operation succeeded, `false` if it failed, with an\n  //!     error message logged.\n  //!\n  //! \\note After a successful call, this method cannot be called again until\n  //!     after Close().\n  bool Open(const base::FilePath& path);\n\n  //! \\brief Wraps CheckedCloseHandle().\n  //!\n  //! \\note It is only valid to call this method on an object that has had a\n  //!     successful Open() that has not yet been matched by a subsequent call\n  //!     to this method.\n  void Close();\n\n  // FileReaderInterface:\n\n  //! \\copydoc FileReaderInterface::Read()\n  //!\n  //! \\note It is only valid to call this method between a successful Open() and\n  //!     a Close().\n  FileOperationResult Read(void* data, size_t size) override;\n\n  // FileSeekerInterface:\n\n  //! \\copydoc FileReaderInterface::Seek()\n  //!\n  //! \\note It is only valid to call this method between a successful Open() and\n  //!     a Close().\n  FileOffset Seek(FileOffset offset, int whence) override;\n\n private:\n  ScopedFileHandle file_;\n  WeakFileHandleFileReader weak_file_handle_file_reader_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_FILE_READER_H_\n"
  },
  {
    "path": "util/file/file_reader_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_reader.h\"\n\n#include <stdint.h>\n\n#include <limits>\n#include <type_traits>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing testing::_;\nusing testing::InSequence;\nusing testing::Return;\n\nclass MockFileReader : public FileReaderInterface {\n public:\n  MockFileReader() : FileReaderInterface() {}\n\n  MockFileReader(const MockFileReader&) = delete;\n  MockFileReader& operator=(const MockFileReader&) = delete;\n\n  ~MockFileReader() override {}\n\n  // Since it’s more convenient for the test to use uintptr_t than void*,\n  // ReadExactlyInt() and ReadInt() adapt the types.\n\n  bool ReadExactlyInt(uintptr_t data, size_t size) {\n    return ReadExactly(reinterpret_cast<void*>(data), size);\n  }\n\n  MOCK_METHOD(FileOperationResult, ReadInt, (uintptr_t, size_t));\n\n  // FileReaderInterface:\n  FileOperationResult Read(void* data, size_t size) override {\n    return ReadInt(reinterpret_cast<uintptr_t>(data), size);\n  }\n\n  // FileSeekerInterface:\n  MOCK_METHOD(FileOffset, Seek, (FileOffset, int), (override));\n};\n\nTEST(FileReader, ReadExactly_Zero) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  EXPECT_CALL(file_reader, ReadInt(_, _)).Times(0);\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_TRUE(file_reader.ReadExactlyInt(100, 0));\n}\n\nTEST(FileReader, ReadExactly_SingleSmallSuccess) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  EXPECT_CALL(file_reader, ReadInt(1000, 1)).WillOnce(Return(1));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_TRUE(file_reader.ReadExactlyInt(1000, 1));\n}\n\nTEST(FileReader, ReadExactly_SingleSmallFailure) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  EXPECT_CALL(file_reader, ReadInt(1000, 1)).WillOnce(Return(-1));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_FALSE(file_reader.ReadExactlyInt(1000, 1));\n}\n\nTEST(FileReader, ReadExactly_DoubleSmallSuccess) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  EXPECT_CALL(file_reader, ReadInt(0x1000, 2)).WillOnce(Return(1));\n  EXPECT_CALL(file_reader, ReadInt(0x1001, 1)).WillOnce(Return(1));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_TRUE(file_reader.ReadExactlyInt(0x1000, 2));\n}\n\nTEST(FileReader, ReadExactly_DoubleSmallShort) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  EXPECT_CALL(file_reader, ReadInt(0x20000, 2)).WillOnce(Return(1));\n  EXPECT_CALL(file_reader, ReadInt(0x20001, 1)).WillOnce(Return(0));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_FALSE(file_reader.ReadExactlyInt(0x20000, 2));\n}\n\nTEST(FileReader, ReadExactly_Medium) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  EXPECT_CALL(file_reader, ReadInt(0x80000000, 0x20000000))\n      .WillOnce(Return(0x10000000));\n  EXPECT_CALL(file_reader, ReadInt(0x90000000, 0x10000000))\n      .WillOnce(Return(0x8000000));\n  EXPECT_CALL(file_reader, ReadInt(0x98000000, 0x8000000))\n      .WillOnce(Return(0x4000000));\n  EXPECT_CALL(file_reader, ReadInt(0x9c000000, 0x4000000))\n      .WillOnce(Return(0x2000000));\n  EXPECT_CALL(file_reader, ReadInt(0x9e000000, 0x2000000))\n      .WillOnce(Return(0x1000000));\n  EXPECT_CALL(file_reader, ReadInt(0x9f000000, 0x1000000))\n      .WillOnce(Return(0x800000));\n  EXPECT_CALL(file_reader, ReadInt(0x9f800000, 0x800000))\n      .WillOnce(Return(0x400000));\n  EXPECT_CALL(file_reader, ReadInt(0x9fc00000, 0x400000))\n      .WillOnce(Return(0x200000));\n  EXPECT_CALL(file_reader, ReadInt(0x9fe00000, 0x200000))\n      .WillOnce(Return(0x100000));\n  EXPECT_CALL(file_reader, ReadInt(0x9ff00000, 0x100000))\n      .WillOnce(Return(0x80000));\n  EXPECT_CALL(file_reader, ReadInt(0x9ff80000, 0x80000))\n      .WillOnce(Return(0x40000));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffc0000, 0x40000))\n      .WillOnce(Return(0x20000));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffe0000, 0x20000))\n      .WillOnce(Return(0x10000));\n  EXPECT_CALL(file_reader, ReadInt(0x9fff0000, 0x10000))\n      .WillOnce(Return(0x8000));\n  EXPECT_CALL(file_reader, ReadInt(0x9fff8000, 0x8000))\n      .WillOnce(Return(0x4000));\n  EXPECT_CALL(file_reader, ReadInt(0x9fffc000, 0x4000))\n      .WillOnce(Return(0x2000));\n  EXPECT_CALL(file_reader, ReadInt(0x9fffe000, 0x2000))\n      .WillOnce(Return(0x1000));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffff000, 0x1000)).WillOnce(Return(0x800));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffff800, 0x800)).WillOnce(Return(0x400));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffffc00, 0x400)).WillOnce(Return(0x200));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffffe00, 0x200)).WillOnce(Return(0x100));\n  EXPECT_CALL(file_reader, ReadInt(0x9fffff00, 0x100)).WillOnce(Return(0x80));\n  EXPECT_CALL(file_reader, ReadInt(0x9fffff80, 0x80)).WillOnce(Return(0x40));\n  EXPECT_CALL(file_reader, ReadInt(0x9fffffc0, 0x40)).WillOnce(Return(0x20));\n  EXPECT_CALL(file_reader, ReadInt(0x9fffffe0, 0x20)).WillOnce(Return(0x10));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffffff0, 0x10)).WillOnce(Return(0x8));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffffff8, 0x8)).WillOnce(Return(0x4));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffffffc, 0x4)).WillOnce(Return(0x2));\n  EXPECT_CALL(file_reader, ReadInt(0x9ffffffe, 0x2)).WillOnce(Return(0x1));\n  EXPECT_CALL(file_reader, ReadInt(0x9fffffff, 0x1)).WillOnce(Return(0x1));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_TRUE(file_reader.ReadExactlyInt(0x80000000, 0x20000000));\n}\n\nTEST(FileReader, ReadExactly_LargeSuccess) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  constexpr size_t max = std::numeric_limits<int32_t>::max();\n  constexpr size_t increment = max / 2;\n  EXPECT_CALL(file_reader, ReadInt(0, max)).WillOnce(Return(increment));\n  EXPECT_CALL(file_reader, ReadInt(increment, max - increment))\n      .WillOnce(Return(increment));\n  EXPECT_CALL(file_reader, ReadInt(2 * increment, 1)).WillOnce(Return(1));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_TRUE(file_reader.ReadExactlyInt(0, max));\n}\n\nTEST(FileReader, ReadExactly_LargeShort) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  EXPECT_CALL(file_reader, ReadInt(0, 0x7fffffff)).WillOnce(Return(0x3fffffff));\n  EXPECT_CALL(file_reader, ReadInt(0x3fffffff, 0x40000000))\n      .WillOnce(Return(0x10000000));\n  EXPECT_CALL(file_reader, ReadInt(0x4fffffff, 0x30000000)).WillOnce(Return(0));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_FALSE(file_reader.ReadExactlyInt(0, 0x7fffffff));\n}\n\nTEST(FileReader, ReadExactly_LargeFailure) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  EXPECT_CALL(file_reader, ReadInt(0, 0x7fffffff)).WillOnce(Return(0x3fffffff));\n  EXPECT_CALL(file_reader, ReadInt(0x3fffffff, 0x40000000))\n      .WillOnce(Return(-1));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_FALSE(file_reader.ReadExactlyInt(0, 0x7fffffff));\n}\n\nTEST(FileReader, ReadExactly_TripleMax) {\n  MockFileReader file_reader;\n  InSequence in_sequence;\n  // ReadExactly supports at most int32_t bytes.\n  constexpr size_t max = std::numeric_limits<int32_t>::max();\n  constexpr size_t increment = max / 2;\n  EXPECT_CALL(file_reader, ReadInt(0, max)).WillOnce(Return(increment));\n  EXPECT_CALL(file_reader, ReadInt(increment, max - increment))\n      .WillOnce(Return(increment));\n  EXPECT_CALL(file_reader, ReadInt(2 * increment, 1)).WillOnce(Return(1));\n  EXPECT_CALL(file_reader, Seek(_, _)).Times(0);\n  EXPECT_TRUE(file_reader.ReadExactlyInt(0, max));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_seeker.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_seeker.h\"\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nFileOffset FileSeekerInterface::SeekGet() {\n  return Seek(0, SEEK_CUR);\n}\n\nbool FileSeekerInterface::SeekSet(FileOffset offset) {\n  FileOffset rv = Seek(offset, SEEK_SET);\n  if (rv < 0) {\n    // Seek() will have logged its own error.\n    return false;\n  } else if (rv != offset) {\n    LOG(ERROR) << \"SeekSet(): expected \" << offset << \", observed \" << rv;\n    return false;\n  }\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_seeker.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_FILE_SEEKER_H_\n#define CRASHPAD_UTIL_FILE_FILE_SEEKER_H_\n\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\n\n//! \\brief An interface to seek in files and other file-like objects with\n//!     semantics matching the underlying platform (POSIX or Windows).\nclass FileSeekerInterface {\n public:\n  //! \\brief Wraps LoggingSeekFile() or provides an alternate implementation\n  //!     with identical semantics.\n  //!\n  //! \\return The return value of LoggingSeekFile(). `-1` on failure,\n  //!     with an error message logged.\n  virtual FileOffset Seek(FileOffset offset, int whence) = 0;\n\n  //! \\brief Wraps Seek(), using `SEEK_CUR` to obtain the file’s current\n  //!     position.\n  //!\n  //! \\return The file’s current position on success. `-1` on failure, with an\n  //!     error message logged.\n  FileOffset SeekGet();\n\n  //! \\brief Wraps Seek(), using `SEEK_SET`, ensuring that the seek succeeded\n  //!     and the file is positioned as desired.\n  //!\n  //! \\return `true` if the operation succeeded, `false` if it failed, with an\n  //!     error message logged. A failure to reposition the file as desired is\n  //!     treated as a failure.\n  bool SeekSet(FileOffset offset);\n\n protected:\n  ~FileSeekerInterface() {}\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_FILE_SEEKER_H_\n"
  },
  {
    "path": "util/file/file_writer.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/file_writer.h\"\n\n#include <limits.h>\n#include <stddef.h>\n#include <string.h>\n\n#include <algorithm>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/implicit_cast.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include <sys/uio.h>\n#include <unistd.h>\n#include \"base/posix/eintr_wrapper.h\"\n#endif  // BUILDFLAG(IS_POSIX)\n\nnamespace crashpad {\n\n#if BUILDFLAG(IS_POSIX)\n// Ensure type compatibility between WritableIoVec and iovec.\nstatic_assert(sizeof(WritableIoVec) == sizeof(iovec), \"WritableIoVec size\");\nstatic_assert(offsetof(WritableIoVec, iov_base) == offsetof(iovec, iov_base),\n              \"WritableIoVec base offset\");\nstatic_assert(offsetof(WritableIoVec, iov_len) == offsetof(iovec, iov_len),\n              \"WritableIoVec len offset\");\n#endif  // BUILDFLAG(IS_POSIX)\n\nWeakFileHandleFileWriter::WeakFileHandleFileWriter(FileHandle file_handle)\n    : file_handle_(file_handle) {\n}\n\nWeakFileHandleFileWriter::~WeakFileHandleFileWriter() {\n}\n\nbool WeakFileHandleFileWriter::Write(const void* data, size_t size) {\n  DCHECK_NE(file_handle_, kInvalidFileHandle);\n  return LoggingWriteFile(file_handle_, data, size);\n}\n\nbool WeakFileHandleFileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) {\n  DCHECK_NE(file_handle_, kInvalidFileHandle);\n\n  if (iovecs->empty()) {\n    LOG(ERROR) << \"WriteIoVec(): no iovecs\";\n    return false;\n  }\n\n#if BUILDFLAG(IS_POSIX)\n\n  ssize_t size = 0;\n  for (const WritableIoVec& iov : *iovecs) {\n    // TODO(mark): Check to avoid overflow of ssize_t, and fail with EINVAL.\n    size += iov.iov_len;\n  }\n\n  // Get an iovec*, because that’s what writev wants. The only difference\n  // between WritableIoVec and iovec is that WritableIoVec’s iov_base is a\n  // pointer to a const buffer, where iovec’s iov_base isn’t. writev doesn’t\n  // actually write to the data, so this cast is safe here. iovec’s iov_base is\n  // non-const because the same structure is used for readv and writev, and\n  // readv needs to write to the buffer that iov_base points to.\n  iovec* iov = reinterpret_cast<iovec*>(&(*iovecs)[0]);\n  size_t remaining_iovecs = iovecs->size();\n\n#if BUILDFLAG(IS_ANDROID)\n  // Android does not expose the IOV_MAX macro, but makes its value available\n  // via sysconf(). See Android 7.0.0 bionic/libc/bionic/sysconf.cpp sysconf().\n  // Bionic defines IOV_MAX at bionic/libc/include/limits.h, but does not ship\n  // this file to the NDK as <limits.h>, substituting\n  // bionic/libc/include/bits/posix_limits.h.\n  const size_t kIovMax = sysconf(_SC_IOV_MAX);\n#else\n  const size_t kIovMax = IOV_MAX;\n#endif\n\n  while (size > 0) {\n    size_t writev_iovec_count = std::min(remaining_iovecs, kIovMax);\n    ssize_t written =\n        HANDLE_EINTR(writev(file_handle_, iov, writev_iovec_count));\n    if (written < 0) {\n      PLOG(ERROR) << \"writev\";\n      return false;\n    } else if (written == 0) {\n      LOG(ERROR) << \"writev: returned 0\";\n      return false;\n    }\n\n    size -= written;\n    DCHECK_GE(size, 0);\n\n    if (size == 0) {\n      remaining_iovecs = 0;\n      break;\n    }\n\n    while (written > 0) {\n      size_t wrote_this_iovec =\n          std::min(implicit_cast<size_t>(written), iov->iov_len);\n      written -= wrote_this_iovec;\n      if (wrote_this_iovec < iov->iov_len) {\n        iov->iov_base =\n            reinterpret_cast<char*>(iov->iov_base) + wrote_this_iovec;\n        iov->iov_len -= wrote_this_iovec;\n      } else {\n        ++iov;\n        --remaining_iovecs;\n      }\n    }\n  }\n\n  DCHECK_EQ(remaining_iovecs, 0u);\n\n#else  // !BUILDFLAG(IS_POSIX)\n\n  for (const WritableIoVec& iov : *iovecs) {\n    if (!Write(iov.iov_base, iov.iov_len))\n      return false;\n  }\n\n#endif  // BUILDFLAG(IS_POSIX)\n\n#ifndef NDEBUG\n  // The interface says that |iovecs| is not sacred, so scramble it to make sure\n  // that nobody depends on it.\n  memset(&(*iovecs)[0], 0xa5, sizeof((*iovecs)[0]) * iovecs->size());\n#endif\n\n  return true;\n}\n\nFileOffset WeakFileHandleFileWriter::Seek(FileOffset offset, int whence) {\n  DCHECK_NE(file_handle_, kInvalidFileHandle);\n  return LoggingSeekFile(file_handle_, offset, whence);\n}\n\nFileWriter::FileWriter()\n    : file_(),\n      weak_file_handle_file_writer_(kInvalidFileHandle) {\n}\n\nFileWriter::~FileWriter() {\n}\n\nbool FileWriter::Open(const base::FilePath& path,\n                      FileWriteMode write_mode,\n                      FilePermissions permissions) {\n  CHECK(!file_.is_valid());\n  file_.reset(LoggingOpenFileForWrite(path, write_mode, permissions));\n  if (!file_.is_valid()) {\n    return false;\n  }\n\n  weak_file_handle_file_writer_.set_file_handle(file_.get());\n  return true;\n}\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\nbool FileWriter::OpenMemfd(const base::FilePath& path) {\n  CHECK(!file_.is_valid());\n  file_.reset(LoggingOpenMemoryFileForReadAndWrite(path));\n  if (!file_.is_valid()) {\n    return false;\n  }\n\n  weak_file_handle_file_writer_.set_file_handle(file_.get());\n  return true;\n}\n\nint FileWriter::fd() {\n  return file_.get();\n}\n#endif\n\nvoid FileWriter::Close() {\n  CHECK(file_.is_valid());\n\n  weak_file_handle_file_writer_.set_file_handle(kInvalidFileHandle);\n  file_.reset();\n}\n\nbool FileWriter::Write(const void* data, size_t size) {\n  DCHECK(file_.is_valid());\n  return weak_file_handle_file_writer_.Write(data, size);\n}\n\nbool FileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) {\n  DCHECK(file_.is_valid());\n  return weak_file_handle_file_writer_.WriteIoVec(iovecs);\n}\n\nFileOffset FileWriter::Seek(FileOffset offset, int whence) {\n  DCHECK(file_.is_valid());\n  return weak_file_handle_file_writer_.Seek(offset, whence);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/file_writer.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_FILE_WRITER_H_\n#define CRASHPAD_UTIL_FILE_FILE_WRITER_H_\n\n#include <sys/types.h>\n\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/file_seeker.h\"\n\nnamespace crashpad {\n\n//! \\brief A version of `iovec` with a `const` #iov_base field.\n//!\n//! This structure is intended to be used for write operations.\n//\n// Type compatibility with iovec is tested with static assertions in the\n// implementation file.\nstruct WritableIoVec {\n  //! \\brief The base address of a memory region for output.\n  const void* iov_base;\n\n  //! \\brief The size of the memory pointed to by #iov_base.\n  size_t iov_len;\n};\n\n//! \\brief An interface to write to files and other file-like objects with\n//!     semantics matching the underlying platform (POSIX or Windows).\nclass FileWriterInterface : public virtual FileSeekerInterface {\n public:\n  virtual ~FileWriterInterface() {}\n\n  //! \\brief Wraps LoggingWriteFile(), or provides an implementation with\n  //!     identical semantics.\n  //!\n  //! \\return `true` if the operation succeeded, `false` if it failed, with an\n  //!     error message logged.\n  virtual bool Write(const void* data, size_t size) = 0;\n\n  //! \\brief Wraps `writev()` on POSIX or provides an alternate implementation\n  //!     with identical semantics. This method will write entire buffers,\n  //!     continuing after a short write or after being interrupted. On\n  //!     non-POSIX this is a simple wrapper around Write().\n  //!\n  //! \\return `true` if the operation succeeded, `false` if it failed, with an\n  //!     error message logged.\n  //!\n  //! \\note The contents of \\a iovecs are undefined when this method returns.\n  virtual bool WriteIoVec(std::vector<WritableIoVec>* iovecs) = 0;\n};\n\n//! \\brief A file writer backed by a FileHandle.\n//!\n//! FileWriter requires users to provide a FilePath to open, but this class\n//! accepts an already-open FileHandle instead. Like FileWriter, this class may\n//! write to a filesystem-based file, but unlike FileWriter, this class is not\n//! responsible for creating or closing the file. Users of this class must\n//! ensure that the file handle is closed appropriately elsewhere. Objects of\n//! this class may be used to write to file handles not associated with\n//! filesystem-based files, although special attention should be paid to the\n//! Seek() method, which may not function on file handles that do not refer to\n//! disk-based files.\n//!\n//! This class is expected to be used when other code is responsible for\n//! creating files and already provides file handles.\nclass WeakFileHandleFileWriter : public FileWriterInterface {\n public:\n  explicit WeakFileHandleFileWriter(FileHandle file_handle);\n\n  WeakFileHandleFileWriter(const WeakFileHandleFileWriter&) = delete;\n  WeakFileHandleFileWriter& operator=(const WeakFileHandleFileWriter&) = delete;\n\n  ~WeakFileHandleFileWriter() override;\n\n  // FileWriterInterface:\n  bool Write(const void* data, size_t size) override;\n  bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;\n\n  // FileSeekerInterface:\n\n  //! \\copydoc FileWriterInterface::Seek()\n  //!\n  //! \\note This method is only guaranteed to function on file handles referring\n  //!     to disk-based files.\n  FileOffset Seek(FileOffset offset, int whence) override;\n\n private:\n  void set_file_handle(FileHandle file_handle) { file_handle_ = file_handle; }\n\n  FileHandle file_handle_;  // weak\n\n  // FileWriter uses this class as its internal implementation, and it needs to\n  // be able to call set_file_handle(). FileWriter cannot initialize a\n  // WeakFileHandleFileWriter with a correct file descriptor at the time of\n  // construction because no file descriptor will be available until\n  // FileWriter::Open() is called.\n  friend class FileWriter;\n};\n\n//! \\brief A file writer implementation that wraps traditional system file\n//!     operations on files accessed through the filesystem.\nclass FileWriter : public FileWriterInterface {\n public:\n  FileWriter();\n\n  FileWriter(const FileWriter&) = delete;\n  FileWriter& operator=(const FileWriter&) = delete;\n\n  ~FileWriter() override;\n\n  // FileWriterInterface:\n\n  //! \\brief Wraps LoggingOpenFileForWrite().\n  //!\n  //! \\return `true` if the operation succeeded, `false` if it failed, with an\n  //!     error message logged.\n  //!\n  //! \\note After a successful call, this method cannot be called again until\n  //!     after Close().\n  bool Open(const base::FilePath& path,\n            FileWriteMode write_mode,\n            FilePermissions permissions);\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  //! \\brief Wraps LoggingOpenMemoryFileForWrite().\n  //!\n  //! \\return `true` if the operation succeeded, `false` if it failed, with an\n  //!     error message logged.\n  //!\n  //! \\note After a successful call, this method or Open() cannot be called\n  //      again until after Close().\n  bool OpenMemfd(const base::FilePath& path);\n\n  //! \\brief Returns the underlying file descriptor.\n  //!\n  //! \\note This is used when this writes to a Memfd.\n  int fd();\n#endif\n\n  //! \\brief Wraps CheckedCloseHandle().\n  //!\n  //! \\note It is only valid to call this method on an object that has had a\n  //!     successful Open() that has not yet been matched by a subsequent call\n  //!     to this method.\n  void Close();\n\n  // FileWriterInterface:\n\n  //! \\copydoc FileWriterInterface::Write()\n  //!\n  //! \\note It is only valid to call this method between a successful Open() and\n  //!     a Close().\n  bool Write(const void* data, size_t size) override;\n\n  //! \\copydoc FileWriterInterface::WriteIoVec()\n  //!\n  //! \\note It is only valid to call this method between a successful Open() and\n  //!     a Close().\n  bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;\n\n  // FileSeekerInterface:\n\n  //! \\copydoc FileWriterInterface::Seek()\n  //!\n  //! \\note It is only valid to call this method between a successful Open() and\n  //!     a Close().\n  FileOffset Seek(FileOffset offset, int whence) override;\n\n private:\n  ScopedFileHandle file_;\n  WeakFileHandleFileWriter weak_file_handle_file_writer_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_FILE_WRITER_H_\n"
  },
  {
    "path": "util/file/filesystem.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_FILESYSTEM_H_\n#define CRASHPAD_UTIL_FILE_FILESYSTEM_H_\n\n#include <time.h>\n\n#include \"base/files/file_path.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\n\n//! \\brief Determines the modification time for a file, directory, or symbolic\n//!     link, logging a message on failure.\n//!\n//! \\param[in] path The file to get the modification time for.\n//! \\param[out] mtime The modification time as seconds since the POSIX Epoch.\n//! \\return `true` on success. `false` on failure with a message logged.\nbool FileModificationTime(const base::FilePath& path, timespec* mtime);\n\n//! \\brief Creates a directory, logging a message on failure.\n//!\n//! \\param[in] path The path to the directory to create.\n//! \\param[in] permissions The permissions to use if the directory is created.\n//! \\param[in] may_reuse If `true`, this function will return `true` if a\n//!     directory or symbolic link to a directory with path \\a path already\n//!     exists. If the directory already exists, it's permissions may differ\n//!     from \\a permissions.\n//! \\return `true` if the directory is successfully created or it already\n//!     existed and \\a may_reuse is `true`. Otherwise, `false`.\nbool LoggingCreateDirectory(const base::FilePath& path,\n                            FilePermissions permissions,\n                            bool may_reuse);\n\n//! \\brief Moves a file, symbolic link, or directory, logging a message on\n//!     failure.\n//!\n//! \\a source must exist and refer to a file, symbolic link, or directory.\n//!\n//! \\a source and \\a dest must be on the same filesystem.\n//!\n//! If \\a dest exists, it may be overwritten:\n//!\n//! If \\a dest exists and refers to a file or to a live or dangling symbolic\n//! link to a file, it will be overwritten if \\a source also refers to a file or\n//! to a live or dangling symbolic link to a file or directory.\n//!\n//! On POSIX, if \\a dest refers to a directory, it will be overwritten only if\n//! it is empty and \\a source also refers to a directory.\n//!\n//! On Windows, if \\a dest refers to a directory or to a live or dangling\n//! symbolic link to a directory, it will not be overwritten.\n//!\n//! \\param[in] source The path to the file to be moved.\n//! \\param[in] dest The new path for the file.\n//! \\return `true` on success. `false` on failure with a message logged.\nbool MoveFileOrDirectory(const base::FilePath& source,\n                         const base::FilePath& dest);\n\n//! \\brief Determines if a path refers to a regular file, logging a message on\n//!     failure.\n//!\n//! On POSIX, this function returns `true` if \\a path refers to a file that is\n//! not a symbolic link, directory, or other kind of special file. If this\n//! function fails because \\a path does not exist, then no message is logged.\n//!\n//! On Windows, this function returns `true` if \\a path refers to a file that\n//! is not a symbolic link or directory.\n//!\n//! \\param[in] path The path to the file to check.\n//! \\return `true` if the file exists and is a regular file. Otherwise `false`.\nbool IsRegularFile(const base::FilePath& path);\n\n//! \\brief Determines if a path refers to a directory, logging a message on\n//!     failure.\n//!\n//! On POSIX, if this function fails because \\a path does not exist, then no\n//! message is logged.\n//!\n//! \\param[in] path The path to check.\n//! \\param[in] allow_symlinks Whether to allow the final component in the path\n//!     to be a symbolic link to a directory.\n//! \\return `true` if the path exists and is a directory. Otherwise `false`.\nbool IsDirectory(const base::FilePath& path, bool allow_symlinks);\n\n//! \\brief Removes a file or a symbolic link to a file or directory, logging a\n//!     message on failure.\n//!\n//! \\param[in] path The path to the file to remove.\n//! \\return `true` on success. `false` on failure with a message logged.\nbool LoggingRemoveFile(const base::FilePath& path);\n\n//! \\brief Non-recurseively removes an empty directory, logging a message on\n//!     failure.\n//!\n//! This function will not remove symbolic links to directories.\n//!\n//! \\param[in] path The to the directory to remove.\n//! \\return `true` if the directory was removed. Otherwise, `false`.\nbool LoggingRemoveDirectory(const base::FilePath& path);\n\n//! \\brief Returns the size of the file at |filepath|.\n//!    The function will ignore symlinks (not follow them, not add them to\n//!    the returned size).\n//!\n//! \\return The size of the file pointed by |filepath|.\nuint64_t GetFileSize(const base::FilePath& filepath);\n\n//! \\brief Returns the recursive sum of the size of the files in |dirPath|.\n//!    The function will ignore symlinks (not follow them, not add them to\n//!    the returned size).\n//!\n//! \\param[in] dirPath The path to the directory\n//!\n//! \\return The sum of the size of the files in |dirPath|.\nuint64_t GetDirectorySize(const base::FilePath& dirPath);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_FILESYSTEM_H_\n"
  },
  {
    "path": "util/file/filesystem_posix.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/filesystem.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"util/file/directory_reader.h\"\n\nnamespace crashpad {\n\nbool FileModificationTime(const base::FilePath& path, timespec* mtime) {\n  struct stat st;\n  if (lstat(path.value().c_str(), &st) != 0) {\n    PLOG(ERROR) << \"lstat \" << path.value();\n    return false;\n  }\n\n#if BUILDFLAG(IS_APPLE)\n  *mtime = st.st_mtimespec;\n#elif BUILDFLAG(IS_ANDROID)\n  // This is needed to compile with traditional NDK headers.\n  mtime->tv_sec = st.st_mtime;\n  mtime->tv_nsec = st.st_mtime_nsec;\n#else\n  *mtime = st.st_mtim;\n#endif\n  return true;\n}\n\nbool LoggingCreateDirectory(const base::FilePath& path,\n                            FilePermissions permissions,\n                            bool may_reuse) {\n  if (mkdir(path.value().c_str(),\n            permissions == FilePermissions::kWorldReadable ? 0755 : 0700) ==\n      0) {\n    return true;\n  }\n  if (may_reuse && errno == EEXIST) {\n    if (!IsDirectory(path, true)) {\n      LOG(ERROR) << path.value() << \" not a directory\";\n      return false;\n    }\n    return true;\n  }\n  PLOG(ERROR) << \"mkdir \" << path.value();\n  return false;\n}\n\nbool MoveFileOrDirectory(const base::FilePath& source,\n                         const base::FilePath& dest) {\n  if (rename(source.value().c_str(), dest.value().c_str()) != 0) {\n    PLOG(ERROR) << \"rename \" << source.value().c_str() << \", \"\n                << dest.value().c_str();\n    return false;\n  }\n  return true;\n}\n\nbool IsRegularFile(const base::FilePath& path) {\n  struct stat st;\n  if (lstat(path.value().c_str(), &st) != 0) {\n    PLOG_IF(ERROR, errno != ENOENT) << \"stat \" << path.value();\n    return false;\n  }\n  return S_ISREG(st.st_mode);\n}\n\nbool IsDirectory(const base::FilePath& path, bool allow_symlinks) {\n  struct stat st;\n  if (allow_symlinks) {\n    if (stat(path.value().c_str(), &st) != 0) {\n      PLOG_IF(ERROR, errno != ENOENT) << \"stat \" << path.value();\n      return false;\n    }\n  } else if (lstat(path.value().c_str(), &st) != 0) {\n    PLOG_IF(ERROR, errno != ENOENT) << \"lstat \" << path.value();\n    return false;\n  }\n  return S_ISDIR(st.st_mode);\n}\n\nbool LoggingRemoveFile(const base::FilePath& path) {\n  if (unlink(path.value().c_str()) != 0) {\n    PLOG(ERROR) << \"unlink \" << path.value();\n    return false;\n  }\n  return true;\n}\n\nbool LoggingRemoveDirectory(const base::FilePath& path) {\n  if (rmdir(path.value().c_str()) != 0) {\n    PLOG(ERROR) << \"rmdir \" << path.value();\n    return false;\n  }\n  return true;\n}\n\nuint64_t GetFileSize(const base::FilePath& filepath) {\n  if (!IsRegularFile(filepath)) {\n    return 0;\n  }\n  struct stat statbuf;\n  if (stat(filepath.value().c_str(), &statbuf) == 0) {\n    return statbuf.st_size;\n  }\n  PLOG(ERROR) << \"stat \" << filepath.value().c_str();\n  return 0;\n}\n\nuint64_t GetDirectorySize(const base::FilePath& dirpath) {\n  if (!IsDirectory(dirpath, /*allow_symlinks=*/false)) {\n    return 0;\n  }\n  DirectoryReader reader;\n  if (!reader.Open(dirpath)) {\n    return 0;\n  }\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  uint64_t size = 0;\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath filepath(dirpath.Append(filename));\n    if (IsDirectory(filepath, /*allow_symlinks=*/false)) {\n      size += GetDirectorySize(filepath);\n    } else {\n      size += GetFileSize(filepath);\n    }\n  }\n  return size;\n}\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/filesystem_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/filesystem.h\"\n\n#include <sys/time.h>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/filesystem.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/misc/time.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr char kTestFileContent[] = \"file_content\";\n\nbool CurrentTime(timespec* now) {\n#if BUILDFLAG(IS_APPLE)\n  timeval now_tv;\n  int res = gettimeofday(&now_tv, nullptr);\n  if (res != 0) {\n    EXPECT_EQ(res, 0) << ErrnoMessage(\"gettimeofday\");\n    return false;\n  }\n  TimevalToTimespec(now_tv, now);\n  return true;\n#elif BUILDFLAG(IS_POSIX)\n  int res = clock_gettime(CLOCK_REALTIME, now);\n  if (res != 0) {\n    EXPECT_EQ(res, 0) << ErrnoMessage(\"clock_gettime\");\n    return false;\n  }\n  return true;\n#else\n  int res = timespec_get(now, TIME_UTC);\n  if (res != TIME_UTC) {\n    EXPECT_EQ(res, TIME_UTC);\n    return false;\n  }\n  return true;\n#endif\n}\n\nTEST(Filesystem, FileModificationTime) {\n  timespec expected_time_start, expected_time_end;\n\n  ASSERT_TRUE(CurrentTime(&expected_time_start));\n  ScopedTempDir temp_dir;\n  ASSERT_TRUE(CurrentTime(&expected_time_end));\n\n  timespec dir_mtime;\n  ASSERT_TRUE(FileModificationTime(temp_dir.path(), &dir_mtime));\n  EXPECT_GE(dir_mtime.tv_sec, expected_time_start.tv_sec - 2);\n  EXPECT_LE(dir_mtime.tv_sec, expected_time_end.tv_sec + 2);\n\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CurrentTime(&expected_time_start));\n  ASSERT_TRUE(CreateFile(file));\n  ASSERT_TRUE(CurrentTime(&expected_time_end));\n\n  timespec file_mtime;\n  ASSERT_TRUE(FileModificationTime(file, &file_mtime));\n  EXPECT_GE(file_mtime.tv_sec, expected_time_start.tv_sec - 2);\n  EXPECT_LE(file_mtime.tv_sec, expected_time_end.tv_sec + 2);\n\n  timespec file_mtime_again;\n  ASSERT_TRUE(FileModificationTime(file, &file_mtime_again));\n  EXPECT_EQ(file_mtime.tv_sec, file_mtime_again.tv_sec);\n  EXPECT_EQ(file_mtime.tv_nsec, file_mtime_again.tv_nsec);\n\n  timespec mtime;\n  EXPECT_FALSE(FileModificationTime(base::FilePath(), &mtime));\n  EXPECT_FALSE(FileModificationTime(\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"notafile\")), &mtime));\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, FileModificationTime_SymbolicLinks) {\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  ScopedTempDir temp_dir;\n\n  const base::FilePath dir_link(\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"dir_link\")));\n\n  timespec expected_time_start, expected_time_end;\n  ASSERT_TRUE(CurrentTime(&expected_time_start));\n  ASSERT_TRUE(CreateSymbolicLink(temp_dir.path(), dir_link));\n  ASSERT_TRUE(CurrentTime(&expected_time_end));\n\n  timespec mtime, mtime2;\n  ASSERT_TRUE(FileModificationTime(temp_dir.path(), &mtime));\n  mtime.tv_sec -= 100;\n  ASSERT_TRUE(SetFileModificationTime(temp_dir.path(), mtime));\n  ASSERT_TRUE(FileModificationTime(temp_dir.path(), &mtime2));\n  EXPECT_GE(mtime2.tv_sec, mtime.tv_sec - 2);\n  EXPECT_LE(mtime2.tv_sec, mtime.tv_sec + 2);\n\n  ASSERT_TRUE(FileModificationTime(dir_link, &mtime));\n  EXPECT_GE(mtime.tv_sec, expected_time_start.tv_sec - 2);\n  EXPECT_LE(mtime.tv_sec, expected_time_end.tv_sec + 2);\n\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n\n  base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n\n  ASSERT_TRUE(CurrentTime(&expected_time_start));\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n  ASSERT_TRUE(CurrentTime(&expected_time_end));\n\n  ASSERT_TRUE(FileModificationTime(file, &mtime));\n  mtime.tv_sec -= 100;\n  ASSERT_TRUE(SetFileModificationTime(file, mtime));\n  ASSERT_TRUE(FileModificationTime(file, &mtime2));\n  EXPECT_GE(mtime2.tv_sec, mtime.tv_sec - 2);\n  EXPECT_LE(mtime2.tv_sec, mtime.tv_sec + 2);\n\n  ASSERT_TRUE(FileModificationTime(link, &mtime));\n  EXPECT_GE(mtime.tv_sec, expected_time_start.tv_sec - 2);\n  EXPECT_LE(mtime.tv_sec, expected_time_end.tv_sec + 2);\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, CreateDirectory) {\n  ScopedTempDir temp_dir;\n\n  base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir\")));\n  EXPECT_FALSE(IsDirectory(dir, false));\n\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n  EXPECT_TRUE(IsDirectory(dir, false));\n\n  EXPECT_FALSE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n\n  base::FilePath file(dir.Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n\n  EXPECT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, true));\n  EXPECT_TRUE(IsRegularFile(file));\n}\n\nTEST(Filesystem, MoveFileOrDirectory) {\n  ScopedTempDir temp_dir;\n\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n\n  // empty paths\n  EXPECT_FALSE(MoveFileOrDirectory(base::FilePath(), base::FilePath()));\n  EXPECT_FALSE(MoveFileOrDirectory(base::FilePath(), file));\n  EXPECT_FALSE(MoveFileOrDirectory(file, base::FilePath()));\n  EXPECT_TRUE(IsRegularFile(file));\n\n  // files\n  base::FilePath file2(temp_dir.path().Append(FILE_PATH_LITERAL(\"file2\")));\n  EXPECT_TRUE(MoveFileOrDirectory(file, file2));\n  EXPECT_TRUE(IsRegularFile(file2));\n  EXPECT_FALSE(PathExists(file));\n\n  EXPECT_FALSE(MoveFileOrDirectory(file, file2));\n  EXPECT_TRUE(IsRegularFile(file2));\n  EXPECT_FALSE(PathExists(file));\n\n  ASSERT_TRUE(CreateFile(file));\n  EXPECT_TRUE(MoveFileOrDirectory(file2, file));\n  EXPECT_TRUE(IsRegularFile(file));\n  EXPECT_FALSE(PathExists(file2));\n\n  // directories\n  base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir\")));\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n\n  base::FilePath nested(dir.Append(FILE_PATH_LITERAL(\"nested\")));\n  ASSERT_TRUE(CreateFile(nested));\n\n  base::FilePath dir2(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir2\")));\n  EXPECT_TRUE(MoveFileOrDirectory(dir, dir2));\n  EXPECT_FALSE(IsDirectory(dir, true));\n  EXPECT_TRUE(IsDirectory(dir2, false));\n  EXPECT_TRUE(IsRegularFile(dir2.Append(nested.BaseName())));\n\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n  EXPECT_FALSE(MoveFileOrDirectory(dir, dir2));\n  EXPECT_TRUE(IsDirectory(dir, false));\n  EXPECT_TRUE(IsDirectory(dir2, false));\n  EXPECT_TRUE(IsRegularFile(dir2.Append(nested.BaseName())));\n\n  // files <-> directories\n  EXPECT_FALSE(MoveFileOrDirectory(file, dir2));\n  EXPECT_TRUE(IsDirectory(dir2, false));\n  EXPECT_TRUE(IsRegularFile(file));\n\n  EXPECT_FALSE(MoveFileOrDirectory(dir2, file));\n  EXPECT_TRUE(IsDirectory(dir2, false));\n  EXPECT_TRUE(IsRegularFile(file));\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, MoveFileOrDirectory_SymbolicLinks) {\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  ScopedTempDir temp_dir;\n\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n\n  // file links\n  base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n\n  base::FilePath link2(temp_dir.path().Append(FILE_PATH_LITERAL(\"link2\")));\n  EXPECT_TRUE(MoveFileOrDirectory(link, link2));\n  EXPECT_TRUE(PathExists(link2));\n  EXPECT_FALSE(PathExists(link));\n\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n  EXPECT_TRUE(MoveFileOrDirectory(link, link2));\n  EXPECT_TRUE(PathExists(link2));\n  EXPECT_FALSE(PathExists(link));\n\n  // file links <-> files\n  EXPECT_TRUE(MoveFileOrDirectory(file, link2));\n  EXPECT_TRUE(IsRegularFile(link2));\n  EXPECT_FALSE(PathExists(file));\n\n  ASSERT_TRUE(MoveFileOrDirectory(link2, file));\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n  EXPECT_TRUE(MoveFileOrDirectory(link, file));\n  EXPECT_TRUE(PathExists(file));\n  EXPECT_FALSE(IsRegularFile(file));\n  EXPECT_FALSE(PathExists(link));\n  EXPECT_TRUE(LoggingRemoveFile(file));\n\n  // dangling file links\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n  EXPECT_TRUE(MoveFileOrDirectory(link, link2));\n  EXPECT_TRUE(PathExists(link2));\n  EXPECT_FALSE(PathExists(link));\n\n  // directory links\n  base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir\")));\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n\n  ASSERT_TRUE(CreateSymbolicLink(dir, link));\n\n  EXPECT_TRUE(MoveFileOrDirectory(link, link2));\n  EXPECT_TRUE(PathExists(link2));\n  EXPECT_FALSE(PathExists(link));\n\n  // dangling directory links\n  ASSERT_TRUE(LoggingRemoveDirectory(dir));\n  EXPECT_TRUE(MoveFileOrDirectory(link2, link));\n  EXPECT_TRUE(PathExists(link));\n  EXPECT_FALSE(PathExists(link2));\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, IsRegularFile) {\n  EXPECT_FALSE(IsRegularFile(base::FilePath()));\n\n  ScopedTempDir temp_dir;\n  EXPECT_FALSE(IsRegularFile(temp_dir.path()));\n\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  EXPECT_FALSE(IsRegularFile(file));\n\n  ASSERT_TRUE(CreateFile(file));\n  EXPECT_TRUE(IsRegularFile(file));\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, IsRegularFile_SymbolicLinks) {\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  ScopedTempDir temp_dir;\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n\n  base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n  EXPECT_FALSE(IsRegularFile(link));\n\n  ASSERT_TRUE(LoggingRemoveFile(file));\n  EXPECT_FALSE(IsRegularFile(link));\n\n  base::FilePath dir_link(\n      temp_dir.path().Append((FILE_PATH_LITERAL(\"dir_link\"))));\n  ASSERT_TRUE(CreateSymbolicLink(temp_dir.path(), dir_link));\n  EXPECT_FALSE(IsRegularFile(dir_link));\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, IsDirectory) {\n  EXPECT_FALSE(IsDirectory(base::FilePath(), false));\n  EXPECT_FALSE(IsDirectory(base::FilePath(), true));\n\n  ScopedTempDir temp_dir;\n  EXPECT_TRUE(IsDirectory(temp_dir.path(), false));\n\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  EXPECT_FALSE(IsDirectory(file, false));\n  EXPECT_FALSE(IsDirectory(file, true));\n\n  ASSERT_TRUE(CreateFile(file));\n  EXPECT_FALSE(IsDirectory(file, false));\n  EXPECT_FALSE(IsDirectory(file, true));\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, IsDirectory_SymbolicLinks) {\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  ScopedTempDir temp_dir;\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n\n  base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n  EXPECT_FALSE(IsDirectory(link, false));\n  EXPECT_FALSE(IsDirectory(link, true));\n\n  ASSERT_TRUE(LoggingRemoveFile(file));\n  EXPECT_FALSE(IsDirectory(link, false));\n  EXPECT_FALSE(IsDirectory(link, true));\n\n  base::FilePath dir_link(\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"dir_link\")));\n  ASSERT_TRUE(CreateSymbolicLink(temp_dir.path(), dir_link));\n  EXPECT_FALSE(IsDirectory(dir_link, false));\n  EXPECT_TRUE(IsDirectory(dir_link, true));\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, RemoveFile) {\n  EXPECT_FALSE(LoggingRemoveFile(base::FilePath()));\n\n  ScopedTempDir temp_dir;\n\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  EXPECT_FALSE(LoggingRemoveFile(file));\n\n  ASSERT_TRUE(CreateFile(file));\n  EXPECT_TRUE(IsRegularFile(file));\n\n  base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir\")));\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n\n  EXPECT_TRUE(LoggingRemoveFile(file));\n  EXPECT_FALSE(PathExists(file));\n  EXPECT_FALSE(LoggingRemoveFile(file));\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, RemoveFile_SymbolicLinks) {\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  ScopedTempDir temp_dir;\n  base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  ASSERT_TRUE(CreateFile(file));\n\n  base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir\")));\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n\n  base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n  EXPECT_TRUE(LoggingRemoveFile(link));\n  EXPECT_FALSE(PathExists(link));\n  EXPECT_TRUE(PathExists(file));\n\n  ASSERT_TRUE(CreateSymbolicLink(dir, link));\n  EXPECT_TRUE(LoggingRemoveFile(link));\n  EXPECT_FALSE(PathExists(link));\n  EXPECT_TRUE(PathExists(dir));\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, RemoveDirectory) {\n  EXPECT_FALSE(LoggingRemoveDirectory(base::FilePath()));\n\n  ScopedTempDir temp_dir;\n\n  base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir\")));\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n\n  base::FilePath file(dir.Append(FILE_PATH_LITERAL(\"file\")));\n  EXPECT_FALSE(LoggingRemoveDirectory(file));\n\n  ASSERT_TRUE(CreateFile(file));\n#if !BUILDFLAG(IS_FUCHSIA)\n  // The current implementation of Fuchsia's rmdir() simply calls unlink(), and\n  // unlink() works on all FS objects. This is incorrect as\n  // http://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html says\n  // \"The directory shall be removed only if it is an empty directory.\" and \"If\n  // the directory is not an empty directory, rmdir() shall fail and set errno\n  // to [EEXIST] or [ENOTEMPTY].\" Upstream bug: US-400.\n  EXPECT_FALSE(LoggingRemoveDirectory(file));\n  EXPECT_FALSE(LoggingRemoveDirectory(dir));\n#endif\n\n  ASSERT_TRUE(LoggingRemoveFile(file));\n  EXPECT_TRUE(LoggingRemoveDirectory(dir));\n}\n\n#if !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, RemoveDirectory_SymbolicLinks) {\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  ScopedTempDir temp_dir;\n  base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir\")));\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n\n  base::FilePath file(dir.Append(FILE_PATH_LITERAL(\"file\")));\n  EXPECT_FALSE(LoggingRemoveDirectory(file));\n\n  base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n  ASSERT_TRUE(CreateSymbolicLink(file, link));\n  EXPECT_FALSE(LoggingRemoveDirectory(link));\n  EXPECT_TRUE(LoggingRemoveFile(link));\n\n  ASSERT_TRUE(CreateSymbolicLink(dir, link));\n  EXPECT_FALSE(LoggingRemoveDirectory(link));\n  EXPECT_TRUE(LoggingRemoveFile(link));\n}\n\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\nTEST(Filesystem, GetFileSize) {\n  ScopedTempDir temp_dir;\n  base::FilePath filepath(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  FileWriter writer;\n  bool is_created = writer.Open(\n      filepath, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly);\n  ASSERT_TRUE(is_created);\n  writer.Write(kTestFileContent, sizeof(kTestFileContent));\n  writer.Close();\n  uint64_t filesize = GetFileSize(filepath);\n  EXPECT_EQ(filesize, sizeof(kTestFileContent));\n\n#if !BUILDFLAG(IS_FUCHSIA)\n  if (!CanCreateSymbolicLinks()) {\n    GTEST_SKIP();\n  }\n\n  // Create a link to a file.\n  base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n  ASSERT_TRUE(CreateSymbolicLink(filepath, link));\n  uint64_t filesize_link = GetFileSize(link);\n  EXPECT_EQ(filesize_link, 0u);\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n}\n\nTEST(Filesystem, GetDirectorySize) {\n  ScopedTempDir temp_dir;\n  base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL(\"dir\")));\n  ASSERT_TRUE(\n      LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));\n  base::FilePath filepath1(temp_dir.path().Append(FILE_PATH_LITERAL(\"file\")));\n  FileWriter writer1;\n  bool is_created1 = writer1.Open(\n      filepath1, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly);\n  ASSERT_TRUE(is_created1);\n  writer1.Write(kTestFileContent, sizeof(kTestFileContent));\n  writer1.Close();\n\n  base::FilePath filepath2(dir.Append(FILE_PATH_LITERAL(\"file\")));\n  FileWriter writer2;\n  bool is_created2 = writer2.Open(\n      filepath2, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly);\n  ASSERT_TRUE(is_created2);\n  writer2.Write(kTestFileContent, sizeof(kTestFileContent));\n  writer2.Close();\n\n#if !BUILDFLAG(IS_FUCHSIA)\n  if (CanCreateSymbolicLinks()) {\n    // Create a link to a file.\n    base::FilePath link(dir.Append(FILE_PATH_LITERAL(\"link\")));\n    ASSERT_TRUE(CreateSymbolicLink(filepath2, link));\n\n    // Create a link to a dir.\n    base::FilePath linkdir(temp_dir.path().Append(FILE_PATH_LITERAL(\"link\")));\n    ASSERT_TRUE(CreateSymbolicLink(dir, linkdir));\n  }\n#endif  // !BUILDFLAG(IS_FUCHSIA)\n\n  uint64_t filesize = GetDirectorySize(temp_dir.path());\n  EXPECT_EQ(filesize, 2 * sizeof(kTestFileContent));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/filesystem_win.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/filesystem.h\"\n\n#include <sys/time.h>\n#include <windows.h>\n\n#include \"base/logging.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/misc/time.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nbool IsSymbolicLink(const base::FilePath& path) {\n  WIN32_FIND_DATA find_data;\n  ScopedSearchHANDLE handle(FindFirstFileEx(path.value().c_str(),\n                                            FindExInfoBasic,\n                                            &find_data,\n                                            FindExSearchNameMatch,\n                                            nullptr,\n                                            0));\n  if (!handle.is_valid()) {\n    PLOG(ERROR) << \"FindFirstFileEx \" << path;\n    return false;\n  }\n\n  return (find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 &&\n         find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK;\n}\n\nbool LoggingRemoveDirectoryImpl(const base::FilePath& path) {\n  if (!RemoveDirectory(path.value().c_str())) {\n    PLOG(ERROR) << \"RemoveDirectory \" << path;\n    return false;\n  }\n  return true;\n}\n\n}  // namespace\n\nbool FileModificationTime(const base::FilePath& path, timespec* mtime) {\n  DWORD flags = FILE_FLAG_OPEN_REPARSE_POINT;\n  if (IsDirectory(path, true)) {\n    // required for directory handles\n    flags |= FILE_FLAG_BACKUP_SEMANTICS;\n  }\n\n  ScopedFileHandle handle(\n      ::CreateFile(path.value().c_str(),\n                   GENERIC_READ,\n                   FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\n                   nullptr,\n                   OPEN_EXISTING,\n                   flags,\n                   nullptr));\n  if (!handle.is_valid()) {\n    PLOG(ERROR) << \"CreateFile \" << path;\n    return false;\n  }\n\n  FILETIME file_mtime;\n  if (!GetFileTime(handle.get(), nullptr, nullptr, &file_mtime)) {\n    PLOG(ERROR) << \"GetFileTime \" << path;\n    return false;\n  }\n  *mtime = FiletimeToTimespecEpoch(file_mtime);\n  return true;\n}\n\nbool LoggingCreateDirectory(const base::FilePath& path,\n                            FilePermissions permissions,\n                            bool may_reuse) {\n  if (CreateDirectory(path.value().c_str(), nullptr)) {\n    return true;\n  }\n  if (may_reuse && GetLastError() == ERROR_ALREADY_EXISTS) {\n    if (!IsDirectory(path, true)) {\n      LOG(ERROR) << path << \" not a directory\";\n      return false;\n    }\n    return true;\n  }\n  PLOG(ERROR) << \"CreateDirectory \" << path;\n  return false;\n}\n\nbool MoveFileOrDirectory(const base::FilePath& source,\n                         const base::FilePath& dest) {\n  if (!MoveFileEx(source.value().c_str(),\n                  dest.value().c_str(),\n                  IsDirectory(source, false) ? 0 : MOVEFILE_REPLACE_EXISTING)) {\n    PLOG(ERROR) << \"MoveFileEx\" << source << \", \" << dest;\n    return false;\n  }\n  return true;\n}\n\nbool IsRegularFile(const base::FilePath& path) {\n  DWORD fileattr = GetFileAttributes(path.value().c_str());\n  if (fileattr == INVALID_FILE_ATTRIBUTES) {\n    PLOG(ERROR) << \"GetFileAttributes \" << path;\n    return false;\n  }\n  if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0 ||\n      (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {\n    return false;\n  }\n  return true;\n}\n\nbool IsDirectory(const base::FilePath& path, bool allow_symlinks) {\n  DWORD fileattr = GetFileAttributes(path.value().c_str());\n  if (fileattr == INVALID_FILE_ATTRIBUTES) {\n    PLOG(ERROR) << \"GetFileAttributes \" << path;\n    return false;\n  }\n  if (!allow_symlinks && (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {\n    return false;\n  }\n  return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;\n}\n\nbool LoggingRemoveFile(const base::FilePath& path) {\n  // RemoveDirectory is used if the file is a symbolic link to a directory.\n  DWORD fileattr = GetFileAttributes(path.value().c_str());\n  if (fileattr != INVALID_FILE_ATTRIBUTES &&\n      (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0 &&\n      (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {\n    return LoggingRemoveDirectoryImpl(path);\n  }\n\n  if (!DeleteFile(path.value().c_str())) {\n    PLOG(ERROR) << \"DeleteFile \" << path;\n    return false;\n  }\n  return true;\n}\n\nbool LoggingRemoveDirectory(const base::FilePath& path) {\n  if (IsSymbolicLink(path)) {\n    LOG(ERROR) << \"Not a directory \" << path;\n    return false;\n  }\n  return LoggingRemoveDirectoryImpl(path);\n}\n\nuint64_t GetFileSize(const base::FilePath& filepath) {\n  struct _stati64 statbuf;\n  if (!IsRegularFile(filepath)) {\n    return 0;\n  }\n  int ret_value = _wstat64(filepath.value().c_str(), &statbuf);\n  if (ret_value == 0) {\n    return statbuf.st_size;\n  }\n  PLOG(ERROR) << \"stat \" << filepath;\n  return 0;\n}\n\nuint64_t GetDirectorySize(const base::FilePath& dirpath) {\n  if (!IsDirectory(dirpath, /*allow_symlinks=*/false)) {\n    return 0;\n  }\n  DirectoryReader reader;\n  if (!reader.Open(dirpath)) {\n    return 0;\n  }\n  base::FilePath filename;\n  DirectoryReader::Result result;\n  uint64_t size = 0;\n  while ((result = reader.NextFile(&filename)) ==\n         DirectoryReader::Result::kSuccess) {\n    const base::FilePath filepath(dirpath.Append(filename));\n    if (IsDirectory(filepath, /*allow_symlinks=*/false)) {\n      size += GetDirectorySize(filepath);\n    } else {\n      size += GetFileSize(filepath);\n    }\n  }\n  return size;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/output_stream_file_writer.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/output_stream_file_writer.h\"\n\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"util/stream/output_stream_interface.h\"\n\nnamespace crashpad {\n\nOutputStreamFileWriter::OutputStreamFileWriter(\n    std::unique_ptr<OutputStreamInterface> output_stream)\n    : output_stream_(std::move(output_stream)),\n      flush_needed_(false),\n      flushed_(false) {}\n\nOutputStreamFileWriter::~OutputStreamFileWriter() {\n  DCHECK(!flush_needed_);\n}\n\nbool OutputStreamFileWriter::Write(const void* data, size_t size) {\n  DCHECK(!flushed_);\n  flush_needed_ =\n      output_stream_->Write(static_cast<const uint8_t*>(data), size);\n  return flush_needed_;\n}\n\nbool OutputStreamFileWriter::WriteIoVec(std::vector<WritableIoVec>* iovecs) {\n  DCHECK(!flushed_);\n  flush_needed_ = true;\n  if (iovecs->empty()) {\n    LOG(ERROR) << \"no iovecs\";\n    flush_needed_ = false;\n    return false;\n  }\n  for (const WritableIoVec& iov : *iovecs) {\n    if (!output_stream_->Write(static_cast<const uint8_t*>(iov.iov_base),\n                               iov.iov_len)) {\n      flush_needed_ = false;\n      return false;\n    }\n  }\n  return true;\n}\n\nFileOffset OutputStreamFileWriter::Seek(FileOffset offset, int whence) {\n  NOTREACHED();\n}\n\nbool OutputStreamFileWriter::Flush() {\n  flush_needed_ = false;\n  flushed_ = true;\n  return output_stream_->Flush();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/output_stream_file_writer.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_\n#define CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_\n\n#include <memory>\n\n#include \"util/file/file_writer.h\"\n\nnamespace crashpad {\n\nclass OutputStreamInterface;\n\n//! \\brief A file writer backed by a OutputSteamInterface.\n//! \\note The \\a Seek related methods don't work and shouldn't be invoked.\nclass OutputStreamFileWriter : public FileWriterInterface {\n public:\n  //! \\param[in] output_stream The output stream that this object writes to.\n  explicit OutputStreamFileWriter(\n      std::unique_ptr<OutputStreamInterface> output_stream);\n\n  OutputStreamFileWriter(const OutputStreamFileWriter&) = delete;\n  OutputStreamFileWriter& operator=(const OutputStreamFileWriter&) = delete;\n\n  ~OutputStreamFileWriter() override;\n\n  // FileWriterInterface:\n  bool Write(const void* data, size_t size) override;\n  bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;\n\n  // FileSeekerInterface:\n\n  //! \\copydoc FileWriterInterface::Seek()\n  //!\n  //! \\note This method doesn't work and shouldn't be invoked.\n  FileOffset Seek(FileOffset offset, int whence) override;\n\n  //! \\brief  Flush data to output_stream.\n  //!\n  //! Either \\a Write() or \\a WriteIoVec() can't be called afterwards.\n  bool Flush();\n\n private:\n  std::unique_ptr<OutputStreamInterface> output_stream_;\n  bool flush_needed_;\n  bool flushed_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_OUTPUT_STREAM_FILE_WRITER_H_\n"
  },
  {
    "path": "util/file/scoped_remove_file.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/scoped_remove_file.h\"\n\n#include \"util/file/filesystem.h\"\n\nnamespace crashpad {\n\nvoid ScopedRemoveFileTraits::Free(const base::FilePath& path) {\n  LoggingRemoveFile(path);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/scoped_remove_file.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_SCOPED_REMOVE_FILE_H_\n#define CRASHPAD_UTIL_FILE_SCOPED_REMOVE_FILE_H_\n\n#include \"base/files/file_path.h\"\n#include \"base/scoped_generic.h\"\n\nnamespace crashpad {\n\nstruct ScopedRemoveFileTraits {\n  static base::FilePath InvalidValue() { return base::FilePath(); }\n  static void Free(const base::FilePath& path);\n};\n\nusing ScopedRemoveFile =\n    base::ScopedGeneric<base::FilePath, ScopedRemoveFileTraits>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_SCOPED_REMOVE_FILE_H_\n"
  },
  {
    "path": "util/file/string_file.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/string_file.h\"\n\n#include <string.h>\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_math.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nStringFile::StringFile() : string_(), offset_(0) {\n}\n\nStringFile::~StringFile() {\n}\n\nvoid StringFile::SetString(const std::string& string) {\n  CHECK_LE(\n      string.size(),\n      implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max()));\n  string_ = string;\n  offset_ = 0;\n}\n\nvoid StringFile::Reset() {\n  string_.clear();\n  offset_ = 0;\n}\n\nFileOperationResult StringFile::Read(void* data, size_t size) {\n  DCHECK(offset_.IsValid());\n\n  const size_t offset = offset_.ValueOrDie();\n  if (offset >= string_.size()) {\n    return 0;\n  }\n\n  const size_t nread = std::min(size, string_.size() - offset);\n\n  base::CheckedNumeric<FileOperationResult> new_offset = offset_;\n  new_offset += nread;\n  if (!new_offset.IsValid()) {\n    LOG(ERROR) << \"Read(): file too large\";\n    return -1;\n  }\n\n  memcpy(data, &string_[offset], nread);\n  offset_ = new_offset;\n\n  return nread;\n}\n\nbool StringFile::Write(const void* data, size_t size) {\n  DCHECK(offset_.IsValid());\n\n  const size_t offset = offset_.ValueOrDie();\n  if (offset > string_.size()) {\n    string_.resize(offset);\n  }\n\n  base::CheckedNumeric<FileOperationResult> new_offset = offset_;\n  new_offset += size;\n  if (!new_offset.IsValid()) {\n    LOG(ERROR) << \"Write(): file too large\";\n    return false;\n  }\n\n  string_.replace(offset, size, reinterpret_cast<const char*>(data), size);\n  offset_ = new_offset;\n\n  return true;\n}\n\nbool StringFile::WriteIoVec(std::vector<WritableIoVec>* iovecs) {\n  DCHECK(offset_.IsValid());\n\n  if (iovecs->empty()) {\n    LOG(ERROR) << \"WriteIoVec(): no iovecs\";\n    return false;\n  }\n\n  // Avoid writing anything at all if it would cause an overflow.\n  base::CheckedNumeric<FileOperationResult> new_offset = offset_;\n  for (const WritableIoVec& iov : *iovecs) {\n    new_offset += iov.iov_len;\n    if (!new_offset.IsValid()) {\n      LOG(ERROR) << \"WriteIoVec(): file too large\";\n      return false;\n    }\n  }\n\n  for (const WritableIoVec& iov : *iovecs) {\n    if (!Write(iov.iov_base, iov.iov_len)) {\n      return false;\n    }\n  }\n\n#ifndef NDEBUG\n  // The interface says that |iovecs| is not sacred, so scramble it to make sure\n  // that nobody depends on it.\n  memset(&(*iovecs)[0], 0xa5, sizeof((*iovecs)[0]) * iovecs->size());\n#endif\n\n  return true;\n}\n\nFileOffset StringFile::Seek(FileOffset offset, int whence) {\n  DCHECK(offset_.IsValid());\n\n  size_t base_offset;\n\n  switch (whence) {\n    case SEEK_SET:\n      base_offset = 0;\n      break;\n\n    case SEEK_CUR:\n      base_offset = offset_.ValueOrDie();\n      break;\n\n    case SEEK_END:\n      base_offset = string_.size();\n      break;\n\n    default:\n      LOG(ERROR) << \"Seek(): invalid whence \" << whence;\n      return -1;\n  }\n\n  FileOffset base_offset_fileoffset;\n  if (!AssignIfInRange(&base_offset_fileoffset, base_offset)) {\n    LOG(ERROR) << \"Seek(): base_offset \" << base_offset\n               << \" invalid for FileOffset\";\n    return -1;\n  }\n  base::CheckedNumeric<FileOffset> new_offset(base_offset_fileoffset);\n  new_offset += offset;\n  if (!new_offset.IsValid()) {\n    LOG(ERROR) << \"Seek(): new_offset invalid\";\n    return -1;\n  }\n  size_t new_offset_sizet;\n  if (!new_offset.AssignIfValid(&new_offset_sizet)) {\n    LOG(ERROR) << \"Seek(): new_offset \" << new_offset.ValueOrDie()\n               << \" invalid for size_t\";\n    return -1;\n  }\n\n  offset_ = new_offset_sizet;\n\n  return base::ValueOrDieForType<FileOffset>(offset_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/file/string_file.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FILE_STRING_FILE_H_\n#define CRASHPAD_UTIL_FILE_STRING_FILE_H_\n\n#include <sys/types.h>\n\n#include <string>\n\n#include \"base/numerics/safe_math.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/file_writer.h\"\n\nnamespace crashpad {\n\n//! \\brief A file reader and writer backed by a virtual file, as opposed to a\n//!     file on disk or other operating system file descriptor-based file.\n//!\n//! The virtual file is a buffer in memory. This class is convenient for use\n//! with other code that normally expects to read or write files, when it is\n//! impractical or inconvenient to read or write a file. It is expected that\n//! tests, in particular, will benefit from using this class.\nclass StringFile : public FileReaderInterface, public FileWriterInterface {\n public:\n  StringFile();\n\n  StringFile(const StringFile&) = delete;\n  StringFile& operator=(const StringFile&) = delete;\n\n  ~StringFile() override;\n\n  //! \\brief Returns a string containing the virtual file’s contents.\n  const std::string& string() const { return string_; }\n\n  //! \\brief Sets the virtual file’s contents to \\a string, and resets its file\n  //!     position to `0`.\n  void SetString(const std::string& string);\n\n  //! \\brief Resets the virtual file’s contents to be empty, and resets its file\n  //!     position to `0`.\n  void Reset();\n\n  // FileReaderInterface:\n  FileOperationResult Read(void* data, size_t size) override;\n\n  // FileWriterInterface:\n  bool Write(const void* data, size_t size) override;\n  bool WriteIoVec(std::vector<WritableIoVec>* iovecs) override;\n\n  // FileSeekerInterface:\n  FileOffset Seek(FileOffset offset, int whence) override;\n\n private:\n  //! \\brief The virtual file’s contents.\n  std::string string_;\n\n  //! \\brief The file offset of the virtual file.\n  //!\n  //! \\note This is stored in a `size_t` to match the characteristics of\n  //!     #string_, the `std::string` used to store the virtual file’s contents.\n  //!     This type will have different characteristics than the `off_t` used to\n  //!     report file offsets. The implementation must take care when converting\n  //!     between these distinct types.\n  base::CheckedNumeric<size_t> offset_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FILE_STRING_FILE_H_\n"
  },
  {
    "path": "util/file/string_file_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/file/string_file.h\"\n\n#include <stdint.h>\n#include <string.h>\n\n#include <algorithm>\n#include <limits>\n\n#include \"gtest/gtest.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(StringFile, EmptyFile) {\n  StringFile string_file;\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n  EXPECT_TRUE(string_file.Write(\"\", 0));\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  char c = '6';\n  EXPECT_EQ(string_file.Read(&c, 1), 0);\n  EXPECT_EQ(c, '6');\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  EXPECT_TRUE(string_file.string().empty());\n}\n\nTEST(StringFile, OneByteFile) {\n  StringFile string_file;\n\n  EXPECT_TRUE(string_file.Write(\"a\", 1));\n  EXPECT_EQ(string_file.string().size(), 1u);\n  EXPECT_EQ(string_file.string(), \"a\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  char c = '6';\n  EXPECT_EQ(string_file.Read(&c, 1), 1);\n  EXPECT_EQ(c, 'a');\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_EQ(string_file.Read(&c, 1), 0);\n  EXPECT_EQ(c, 'a');\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_EQ(string_file.string(), \"a\");\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_TRUE(string_file.Write(\"b\", 1));\n  EXPECT_EQ(string_file.string().size(), 1u);\n  EXPECT_EQ(string_file.string(), \"b\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_EQ(string_file.Read(&c, 1), 1);\n  EXPECT_EQ(c, 'b');\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_EQ(string_file.Read(&c, 1), 0);\n  EXPECT_EQ(c, 'b');\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_EQ(string_file.string(), \"b\");\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_TRUE(string_file.Write(\"\\0\", 1));\n  EXPECT_EQ(string_file.string().size(), 1u);\n  EXPECT_EQ(string_file.string()[0], '\\0');\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_EQ(string_file.string().size(), 1u);\n  EXPECT_EQ(string_file.string()[0], '\\0');\n}\n\nTEST(StringFile, SetString) {\n  char kString1[] = \"Four score\";\n  StringFile string_file;\n  string_file.SetString(kString1);\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  char buf[5] = \"****\";\n  EXPECT_EQ(string_file.Read(buf, 4), 4);\n  EXPECT_STREQ(\"Four\", buf);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 4);\n  EXPECT_EQ(string_file.Seek(0, SEEK_END),\n            static_cast<FileOffset>(strlen(kString1)));\n  EXPECT_EQ(string_file.string(), kString1);\n\n  char kString2[] = \"and seven years ago\";\n  EXPECT_EQ(string_file.Seek(4, SEEK_SET), 4);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 4);\n  string_file.SetString(kString2);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n  EXPECT_EQ(string_file.Read(buf, 4), 4);\n  EXPECT_STREQ(\"and \", buf);\n  EXPECT_EQ(string_file.Seek(0, SEEK_END),\n            static_cast<FileOffset>(strlen(kString2)));\n  EXPECT_EQ(string_file.string(), kString2);\n\n  char kString3[] = \"our fathers\";\n  EXPECT_EQ(string_file.Seek(4, SEEK_SET), 4);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 4);\n  string_file.SetString(kString3);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n  EXPECT_EQ(string_file.Read(buf, 4), 4);\n  EXPECT_STREQ(\"our \", buf);\n  EXPECT_EQ(string_file.Seek(0, SEEK_END),\n            static_cast<FileOffset>(strlen(kString3)));\n  EXPECT_EQ(string_file.string(), kString3);\n}\n\nTEST(StringFile, ReadExactly) {\n  StringFile string_file;\n  string_file.SetString(\"1234567\");\n  char buf[4] = \"***\";\n  EXPECT_TRUE(string_file.ReadExactly(buf, 3));\n  EXPECT_STREQ(\"123\", buf);\n  EXPECT_TRUE(string_file.ReadExactly(buf, 3));\n  EXPECT_STREQ(\"456\", buf);\n  EXPECT_FALSE(string_file.ReadExactly(buf, 3));\n}\n\nTEST(StringFile, Reset) {\n  StringFile string_file;\n\n  EXPECT_TRUE(string_file.Write(\"abc\", 3));\n  EXPECT_EQ(string_file.string().size(), 3u);\n  EXPECT_EQ(string_file.string(), \"abc\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 3);\n  char buf[10] = \"*********\";\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_EQ(string_file.Read(&buf, 10), 3);\n  EXPECT_STREQ(\"abc******\", buf);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 3);\n  EXPECT_FALSE(string_file.string().empty());\n\n  string_file.Reset();\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  EXPECT_TRUE(string_file.Write(\"de\", 2));\n  EXPECT_EQ(string_file.string().size(), 2u);\n  EXPECT_EQ(string_file.string(), \"de\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 2);\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_EQ(string_file.Read(&buf, 10), 2);\n  EXPECT_STREQ(\"dec******\", buf);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 2);\n  EXPECT_FALSE(string_file.string().empty());\n\n  string_file.Reset();\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  EXPECT_TRUE(string_file.Write(\"fghi\", 4));\n  EXPECT_EQ(string_file.string().size(), 4u);\n  EXPECT_EQ(string_file.string(), \"fghi\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 4);\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_EQ(string_file.Read(&buf, 2), 2);\n  EXPECT_STREQ(\"fgc******\", buf);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 2);\n  EXPECT_EQ(string_file.Read(&buf, 2), 2);\n  EXPECT_STREQ(\"hic******\", buf);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 4);\n  EXPECT_FALSE(string_file.string().empty());\n\n  string_file.Reset();\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  // Test resetting after a sparse write.\n  EXPECT_EQ(string_file.Seek(1, SEEK_SET), 1);\n  EXPECT_TRUE(string_file.Write(\"j\", 1));\n  EXPECT_EQ(string_file.string().size(), 2u);\n  EXPECT_EQ(string_file.string(), std::string(\"\\0j\", 2));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 2);\n  EXPECT_FALSE(string_file.string().empty());\n\n  string_file.Reset();\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n}\n\nTEST(StringFile, WriteInvalid) {\n  StringFile string_file;\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  EXPECT_FALSE(string_file.Write(\n      \"\",\n      implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max()) +\n          1));\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  EXPECT_TRUE(string_file.Write(\"a\", 1));\n  EXPECT_FALSE(string_file.Write(\n      \"\",\n      implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max())));\n  EXPECT_EQ(string_file.string().size(), 1u);\n  EXPECT_EQ(string_file.string(), \"a\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n}\n\nTEST(StringFile, WriteIoVec) {\n  StringFile string_file;\n\n  std::vector<WritableIoVec> iovecs;\n  WritableIoVec iov;\n  iov.iov_base = \"\";\n  iov.iov_len = 0;\n  iovecs.push_back(iov);\n  EXPECT_TRUE(string_file.WriteIoVec(&iovecs));\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  iovecs.clear();\n  iov.iov_base = \"a\";\n  iov.iov_len = 1;\n  iovecs.push_back(iov);\n  EXPECT_TRUE(string_file.WriteIoVec(&iovecs));\n  EXPECT_EQ(string_file.string().size(), 1u);\n  EXPECT_EQ(string_file.string(), \"a\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n\n  iovecs.clear();\n  iovecs.push_back(iov);\n  EXPECT_TRUE(string_file.WriteIoVec(&iovecs));\n  EXPECT_EQ(string_file.string().size(), 2u);\n  EXPECT_EQ(string_file.string(), \"aa\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 2);\n\n  iovecs.clear();\n  iovecs.push_back(iov);\n  iov.iov_base = \"bc\";\n  iov.iov_len = 2;\n  iovecs.push_back(iov);\n  EXPECT_TRUE(string_file.WriteIoVec(&iovecs));\n  EXPECT_EQ(string_file.string().size(), 5u);\n  EXPECT_EQ(string_file.string(), \"aaabc\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 5);\n\n  EXPECT_TRUE(string_file.Write(\"def\", 3));\n  EXPECT_EQ(string_file.string().size(), 8u);\n  EXPECT_EQ(string_file.string(), \"aaabcdef\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 8);\n\n  iovecs.clear();\n  iov.iov_base = \"ghij\";\n  iov.iov_len = 4;\n  iovecs.push_back(iov);\n  iov.iov_base = \"klmno\";\n  iov.iov_len = 5;\n  iovecs.push_back(iov);\n  EXPECT_TRUE(string_file.WriteIoVec(&iovecs));\n  EXPECT_EQ(string_file.string().size(), 17u);\n  EXPECT_EQ(string_file.string(), \"aaabcdefghijklmno\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 17);\n\n  string_file.Reset();\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  iovecs.clear();\n  iov.iov_base = \"abcd\";\n  iov.iov_len = 4;\n  iovecs.resize(16, iov);\n  EXPECT_TRUE(string_file.WriteIoVec(&iovecs));\n  EXPECT_EQ(string_file.string().size(), 64u);\n  EXPECT_EQ(string_file.string(),\n            \"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 64);\n}\n\nTEST(StringFile, WriteIoVecInvalid) {\n  StringFile string_file;\n\n  std::vector<WritableIoVec> iovecs;\n  EXPECT_FALSE(string_file.WriteIoVec(&iovecs));\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n\n  WritableIoVec iov;\n  EXPECT_EQ(string_file.Seek(1, SEEK_CUR), 1);\n  iov.iov_base = \"a\";\n  iov.iov_len = std::numeric_limits<FileOperationResult>::max();\n  iovecs.push_back(iov);\n  EXPECT_FALSE(string_file.WriteIoVec(&iovecs));\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n\n  iovecs.clear();\n  iov.iov_base = \"a\";\n  iov.iov_len = 1;\n  iovecs.push_back(iov);\n  iov.iov_len = std::numeric_limits<FileOperationResult>::max() - 1;\n  iovecs.push_back(iov);\n  EXPECT_FALSE(string_file.WriteIoVec(&iovecs));\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n}\n\nTEST(StringFile, Seek) {\n  StringFile string_file;\n\n  EXPECT_TRUE(string_file.Write(\"abcd\", 4));\n  EXPECT_EQ(string_file.string().size(), 4u);\n  EXPECT_EQ(string_file.string(), \"abcd\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 4);\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_TRUE(string_file.Write(\"efgh\", 4));\n  EXPECT_EQ(string_file.string().size(), 4u);\n  EXPECT_EQ(string_file.string(), \"efgh\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 4);\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_TRUE(string_file.Write(\"ijk\", 3));\n  EXPECT_EQ(string_file.string().size(), 4u);\n  EXPECT_EQ(string_file.string(), \"ijkh\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 3);\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0);\n  EXPECT_TRUE(string_file.Write(\"lmnop\", 5));\n  EXPECT_EQ(string_file.string().size(), 5u);\n  EXPECT_EQ(string_file.string(), \"lmnop\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 5);\n\n  EXPECT_EQ(string_file.Seek(1, SEEK_SET), 1);\n  EXPECT_TRUE(string_file.Write(\"q\", 1));\n  EXPECT_EQ(string_file.string().size(), 5u);\n  EXPECT_EQ(string_file.string(), \"lqnop\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 2);\n\n  EXPECT_EQ(string_file.Seek(-1, SEEK_CUR), 1);\n  EXPECT_TRUE(string_file.Write(\"r\", 1));\n  EXPECT_EQ(string_file.string().size(), 5u);\n  EXPECT_EQ(string_file.string(), \"lrnop\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 2);\n\n  EXPECT_TRUE(string_file.Write(\"s\", 1));\n  EXPECT_EQ(string_file.string().size(), 5u);\n  EXPECT_EQ(string_file.string(), \"lrsop\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 3);\n\n  EXPECT_EQ(string_file.Seek(-1, SEEK_CUR), 2);\n  EXPECT_TRUE(string_file.Write(\"t\", 1));\n  EXPECT_EQ(string_file.string().size(), 5u);\n  EXPECT_EQ(string_file.string(), \"lrtop\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 3);\n\n  EXPECT_EQ(string_file.Seek(-1, SEEK_END), 4);\n  EXPECT_TRUE(string_file.Write(\"u\", 1));\n  EXPECT_EQ(string_file.string().size(), 5u);\n  EXPECT_EQ(string_file.string(), \"lrtou\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 5);\n\n  EXPECT_EQ(string_file.Seek(-5, SEEK_END), 0);\n  EXPECT_TRUE(string_file.Write(\"v\", 1));\n  EXPECT_EQ(string_file.string().size(), 5u);\n  EXPECT_EQ(string_file.string(), \"vrtou\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_END), 5);\n  EXPECT_TRUE(string_file.Write(\"w\", 1));\n  EXPECT_EQ(string_file.string().size(), 6u);\n  EXPECT_EQ(string_file.string(), \"vrtouw\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 6);\n\n  EXPECT_EQ(string_file.Seek(2, SEEK_END), 8);\n  EXPECT_EQ(string_file.string().size(), 6u);\n  EXPECT_EQ(string_file.string(), \"vrtouw\");\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_END), 6);\n  EXPECT_TRUE(string_file.Write(\"x\", 1));\n  EXPECT_EQ(string_file.string().size(), 7u);\n  EXPECT_EQ(string_file.string(), \"vrtouwx\");\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 7);\n}\n\nTEST(StringFile, SeekSparse) {\n  StringFile string_file;\n\n  EXPECT_EQ(string_file.Seek(3, SEEK_SET), 3);\n  EXPECT_TRUE(string_file.string().empty());\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 3);\n\n  EXPECT_TRUE(string_file.Write(\"abc\", 3));\n  EXPECT_EQ(string_file.string().size(), 6u);\n  EXPECT_EQ(string_file.string(), std::string(\"\\0\\0\\0abc\", 6));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 6);\n\n  EXPECT_EQ(string_file.Seek(3, SEEK_END), 9);\n  EXPECT_EQ(string_file.string().size(), 6u);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 9);\n  char c;\n  EXPECT_EQ(string_file.Read(&c, 1), 0);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 9);\n  EXPECT_EQ(string_file.string().size(), 6u);\n  EXPECT_TRUE(string_file.Write(\"def\", 3));\n  EXPECT_EQ(string_file.string().size(), 12u);\n  EXPECT_EQ(string_file.string(), std::string(\"\\0\\0\\0abc\\0\\0\\0def\", 12));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 12);\n\n  EXPECT_EQ(string_file.Seek(-5, SEEK_END), 7);\n  EXPECT_EQ(string_file.string().size(), 12u);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 7);\n  EXPECT_TRUE(string_file.Write(\"g\", 1));\n  EXPECT_EQ(string_file.string().size(), 12u);\n  EXPECT_EQ(string_file.string(), std::string(\"\\0\\0\\0abc\\0g\\0def\", 12));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 8);\n\n  EXPECT_EQ(string_file.Seek(7, SEEK_CUR), 15);\n  EXPECT_EQ(string_file.string().size(), 12u);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 15);\n  EXPECT_TRUE(string_file.Write(\"hij\", 3));\n  EXPECT_EQ(string_file.string().size(), 18u);\n  EXPECT_EQ(string_file.string(),\n            std::string(\"\\0\\0\\0abc\\0g\\0def\\0\\0\\0hij\", 18));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 18);\n\n  EXPECT_EQ(string_file.Seek(-17, SEEK_CUR), 1);\n  EXPECT_EQ(string_file.string().size(), 18u);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_TRUE(string_file.Write(\"k\", 1));\n  EXPECT_EQ(string_file.string().size(), 18u);\n  EXPECT_EQ(string_file.string(), std::string(\"\\0k\\0abc\\0g\\0def\\0\\0\\0hij\", 18));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 2);\n\n  EXPECT_TRUE(string_file.Write(\"l\", 1));\n  EXPECT_TRUE(string_file.Write(\"mnop\", 4));\n  EXPECT_EQ(string_file.string().size(), 18u);\n  EXPECT_EQ(string_file.string(), std::string(\"\\0klmnopg\\0def\\0\\0\\0hij\", 18));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 7);\n}\n\nTEST(StringFile, SeekInvalid) {\n  StringFile string_file;\n\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n  EXPECT_EQ(string_file.Seek(1, SEEK_SET), 1);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_LT(string_file.Seek(-1, SEEK_SET), 0);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_LT(string_file.Seek(std::numeric_limits<FileOperationResult>::min(),\n                             SEEK_SET),\n            0);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_LT(string_file.Seek(std::numeric_limits<FileOffset>::min(), SEEK_SET),\n            0);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_TRUE(string_file.string().empty());\n\n  static_assert(SEEK_SET != 3 && SEEK_CUR != 3 && SEEK_END != 3,\n                \"3 must be invalid for whence\");\n  EXPECT_LT(string_file.Seek(0, 3), 0);\n\n  string_file.Reset();\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n  EXPECT_TRUE(string_file.string().empty());\n\n  const FileOffset kMaxOffset = static_cast<FileOffset>(\n      std::min(implicit_cast<uint64_t>(std::numeric_limits<FileOffset>::max()),\n               implicit_cast<uint64_t>(std::numeric_limits<size_t>::max())));\n\n  EXPECT_EQ(string_file.Seek(kMaxOffset, SEEK_SET), kMaxOffset);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), kMaxOffset);\n  EXPECT_LT(string_file.Seek(1, SEEK_CUR), 0);\n\n  EXPECT_EQ(string_file.Seek(1, SEEK_SET), 1);\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_LT(string_file.Seek(kMaxOffset, SEEK_CUR), 0);\n}\n\nTEST(StringFile, SeekSet) {\n  StringFile string_file;\n  EXPECT_TRUE(string_file.SeekSet(1));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 1);\n  EXPECT_TRUE(string_file.SeekSet(0));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 0);\n  EXPECT_TRUE(string_file.SeekSet(10));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 10);\n  EXPECT_FALSE(string_file.SeekSet(-1));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 10);\n  EXPECT_FALSE(\n      string_file.SeekSet(std::numeric_limits<FileOperationResult>::min()));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 10);\n  EXPECT_FALSE(string_file.SeekSet(std::numeric_limits<FileOffset>::min()));\n  EXPECT_EQ(string_file.Seek(0, SEEK_CUR), 10);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/fuchsia/koid_utilities.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/fuchsia/koid_utilities.h\"\n\n#include <lib/fdio/fdio.h>\n#include <lib/zx/channel.h>\n#include <lib/zx/job.h>\n#include <lib/zx/process.h>\n\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Casts |handle| into a container of type T, returning a null handle if the\n// actual handle type does not match that of T.\ntemplate <typename T>\nT CastHandle(zx::handle handle) {\n  zx_info_handle_basic_t actual = {};\n  zx_status_t status = handle.get_info(\n      ZX_INFO_HANDLE_BASIC, &actual, sizeof(actual), nullptr, nullptr);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_object_get_info\";\n    return T();\n  }\n  if (actual.type != T::TYPE) {\n    LOG(ERROR) << \"Wrong type: \" << actual.type << \", expected \" << T::TYPE;\n    return T();\n  }\n  return T(std::move(handle));\n}\n\n// Returns null handle if |koid| is not found or an error occurs. If |was_found|\n// is non-null then it will be set, to distinguish not-found from other errors.\ntemplate <typename T, typename U>\nT GetChildHandleByKoid(const U& parent, zx_koid_t child_koid, bool* was_found) {\n  zx::handle handle;\n  zx_status_t status =\n      parent.get_child(child_koid, ZX_RIGHT_SAME_RIGHTS, &handle);\n  if (was_found)\n    *was_found = (status != ZX_ERR_NOT_FOUND);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_object_get_child\";\n    return T();\n  }\n\n  return CastHandle<T>(std::move(handle));\n}\n\n}  // namespace\n\nstd::vector<zx_koid_t> GetChildKoids(const zx::object_base& parent_object,\n                                     zx_object_info_topic_t child_kind) {\n  size_t actual = 0;\n  size_t available = 0;\n  std::vector<zx_koid_t> result(100);\n  zx::unowned_handle parent(parent_object.get());\n\n  // This is inherently racy. Better if the process is suspended, but there's\n  // still no guarantee that a thread isn't externally created. As a result,\n  // must be in a retry loop.\n  for (;;) {\n    zx_status_t status = parent->get_info(child_kind,\n                                          result.data(),\n                                          result.size() * sizeof(zx_koid_t),\n                                          &actual,\n                                          &available);\n    // If the buffer is too small (even zero), the result is still ZX_OK, not\n    // ZX_ERR_BUFFER_TOO_SMALL.\n    if (status != ZX_OK) {\n      ZX_LOG(ERROR, status) << \"zx_object_get_info\";\n      break;\n    }\n\n    if (actual == available) {\n      break;\n    }\n\n    // Resize to the expected number next time, with a bit of slop to handle the\n    // race between here and the next request.\n    result.resize(available + 10);\n  }\n\n  result.resize(actual);\n  return result;\n}\n\nstd::vector<zx::thread> GetThreadHandles(const zx::process& parent) {\n  auto koids = GetChildKoids(parent, ZX_INFO_PROCESS_THREADS);\n  return GetHandlesForThreadKoids(parent, koids);\n}\n\nstd::vector<zx::thread> GetHandlesForThreadKoids(\n    const zx::process& parent,\n    const std::vector<zx_koid_t>& koids) {\n  std::vector<zx::thread> result;\n  result.reserve(koids.size());\n  for (zx_koid_t koid : koids) {\n    result.emplace_back(GetThreadHandleByKoid(parent, koid));\n  }\n  return result;\n}\n\nzx::thread GetThreadHandleByKoid(const zx::process& parent,\n                                 zx_koid_t child_koid) {\n  return GetChildHandleByKoid<zx::thread>(parent, child_koid, nullptr);\n}\n\nzx_koid_t GetKoidForHandle(const zx::object_base& object) {\n  zx_info_handle_basic_t info;\n  zx_status_t status = zx_object_get_info(object.get(),\n                                          ZX_INFO_HANDLE_BASIC,\n                                          &info,\n                                          sizeof(info),\n                                          nullptr,\n                                          nullptr);\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_object_get_info\";\n    return ZX_KOID_INVALID;\n  }\n  return info.koid;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/fuchsia/koid_utilities.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_\n#define CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_\n\n#include <lib/zx/object.h>\n#include <lib/zx/process.h>\n#include <lib/zx/thread.h>\n#include <zircon/syscalls/object.h>\n#include <zircon/types.h>\n\n#include <vector>\n\nnamespace crashpad {\n\n//! \\brief Get a list of child koids for a parent handle.\n//!\n//! For example, the list of processes in jobs, or the list of threads in a\n//! process.\n//!\n//! \\param[in] parent The handle to the parent object.\n//! \\param[in] child_kind The type of children to retrieve from \\a parent. Valid\n//!     values depend on the type of \\a parent, but include\n//!     `ZX_INFO_JOB_CHILDREN` (child jobs of a job), `ZX_INFO_JOB_PROCESSES`\n//!     (child processes of a job), and `ZX_INFO_PROCESS_THREADS` (child threads\n//!     of a process).\n//! \\return A vector of the koids representing the child objects.\n//!\n//! \\sa GetChildHandles\nstd::vector<zx_koid_t> GetChildKoids(const zx::object_base& parent,\n                                     zx_object_info_topic_t child_kind);\n\n//! \\brief Get handles representing a list of child objects of a given parent.\n//!\n//! \\param[in] parent The handle to the parent object.\n//! \\return The resulting list of handles corresponding to the child objects.\n//!\n//! \\sa GetChildKoids\nstd::vector<zx::thread> GetThreadHandles(const zx::process& parent);\n\n//! \\brief Convert a list of koids that are all children of a particular process\n//!     into thread handles.\n//!\n//! \\param[in] parent The parent object to which the koids belong.\n//! \\param[in] koids The list of koids.\n//! \\return The resulting list of handles corresponding to the koids. If an\n//!     element of \\a koids is invalid or can't be retrieved, there will be a\n//!     corresponding `ZX_HANDLE_INVALID` entry in the return.\nstd::vector<zx::thread> GetHandlesForThreadKoids(\n    const zx::process& parent,\n    const std::vector<zx_koid_t>& koids);\n\n//! \\brief Retrieve the handle of a process' thread, based on koid.\n//!\n//! \\param[in] parent The parent object to which the child belongs.\n//! \\param[in] child_koid The koid of the child to retrieve.\n//! \\return A handle representing \\a child_koid, or `ZX_HANDLE_INVALID` if the\n//!     handle could not be retrieved, in which case an error will be logged.\nzx::thread GetThreadHandleByKoid(const zx::process& parent,\n                                 zx_koid_t child_koid);\n\n//! \\brief Retrieves the koid for a given object handle.\n//!\n//! \\param[in] object The handle for which the koid is to be retrieved.\n//! \\return The koid of \\a handle, or `ZX_HANDLE_INVALID` with an error logged.\nzx_koid_t GetKoidForHandle(const zx::object_base& object);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FUCHSIA_KOID_UTILITIES_H_\n"
  },
  {
    "path": "util/fuchsia/scoped_task_suspend.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/fuchsia/scoped_task_suspend.h\"\n\n#include <lib/zx/time.h>\n#include <zircon/errors.h>\n#include <zircon/status.h>\n#include <zircon/syscalls/object.h>\n\n#include <vector>\n\n#include \"base/check_op.h\"\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"base/logging.h\"\n#include \"util/fuchsia/koid_utilities.h\"\n\nnamespace crashpad {\n\nScopedTaskSuspend::ScopedTaskSuspend(const zx::process& process) {\n  DCHECK_NE(process.get(), zx::process::self()->get());\n\n  const zx_status_t suspend_status = process.suspend(&suspend_token_);\n  if (suspend_status != ZX_OK) {\n    ZX_LOG(ERROR, suspend_status) << \"zx_task_suspend\";\n    return;\n  }\n\n  // suspend() is asynchronous so we now check that each thread is indeed\n  // suspended, up to some deadline.\n  for (const auto& thread : GetThreadHandles(process)) {\n    // We omit the crashed thread (blocked in an exception) as it is technically\n    // not suspended, cf. ZX-3772.\n    zx_info_thread_t info;\n    if (thread.get_info(\n            ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr) == ZX_OK) {\n      if (info.state == ZX_THREAD_STATE_BLOCKED_EXCEPTION) {\n        continue;\n      }\n    }\n\n    zx_signals_t observed = 0u;\n    const zx_status_t wait_status = thread.wait_one(\n        ZX_THREAD_SUSPENDED, zx::deadline_after(zx::msec(50)), &observed);\n    if (wait_status != ZX_OK) {\n      zx_info_thread_t info = {};\n      zx_status_t info_status = thread.get_info(\n          ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr);\n      ZX_LOG(ERROR, wait_status) << \"thread failed to suspend\";\n      LOG(ERROR) << \"Thread info status \" << info_status;\n      if (info_status == ZX_OK) {\n        LOG(ERROR) << \"Thread state \" << info.state;\n      }\n    }\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/fuchsia/scoped_task_suspend.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FUCHSIA_SCOPED_TASK_SUSPEND_H_\n#define CRASHPAD_UTIL_FUCHSIA_SCOPED_TASK_SUSPEND_H_\n\n#include <lib/zx/process.h>\n#include <lib/zx/suspend_token.h>\n#include <lib/zx/thread.h>\n\n#include <vector>\n\n\nnamespace crashpad {\n\n//! \\brief Manages the suspension of another task.\n//!\n//! Suspending a process is asynchronous, and may take an arbitrary amount of\n//! time. As a result, this class is limited to being a best-effort, and\n//! correct suspension/resumption cannot be relied upon.\n//!\n//! Callers should not attempt to suspend the current task as obtained via\n//! `zx_process_self()`.\nclass ScopedTaskSuspend {\n public:\n  explicit ScopedTaskSuspend(const zx::process& process);\n\n  ScopedTaskSuspend(const ScopedTaskSuspend&) = delete;\n  ScopedTaskSuspend& operator=(const ScopedTaskSuspend&) = delete;\n\n  ~ScopedTaskSuspend() = default;\n\n private:\n  zx::suspend_token suspend_token_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FUCHSIA_SCOPED_TASK_SUSPEND_H_\n"
  },
  {
    "path": "util/fuchsia/traits.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_FUCHSIA_TRAITS_H_\n#define CRASHPAD_UTIL_FUCHSIA_TRAITS_H_\n\n#include <stdint.h>\n\nnamespace crashpad {\n\nstruct Traits32 {\n  using Address = uint32_t;\n};\n\nstruct Traits64 {\n  using Address = uint64_t;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_FUCHSIA_TRAITS_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_data.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_data.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nIOSIntermediateDumpData::IOSIntermediateDumpData() : data_() {}\n\nIOSIntermediateDumpData::~IOSIntermediateDumpData() {}\n\nIOSIntermediateDumpObject::Type IOSIntermediateDumpData::GetType() const {\n  return Type::kData;\n}\n\nstd::string IOSIntermediateDumpData::GetString() const {\n  return std::string(reinterpret_cast<const char*>(data_.data()), data_.size());\n}\n\nbool IOSIntermediateDumpData::GetValueInternal(void* value,\n                                               size_t value_size) const {\n  if (value_size == data_.size()) {\n    memcpy(value, data_.data(), data_.size());\n    return true;\n  }\n  return false;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_data.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_DATA_H_\n#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_DATA_H_\n\n#include <string>\n#include <vector>\n\n#include \"util/ios/ios_intermediate_dump_object.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A data object, consisting of a std::vector<uint8_t>.\nclass IOSIntermediateDumpData : public IOSIntermediateDumpObject {\n public:\n  IOSIntermediateDumpData();\n\n  IOSIntermediateDumpData(const IOSIntermediateDumpData&) = delete;\n  IOSIntermediateDumpData& operator=(const IOSIntermediateDumpData&) = delete;\n\n  ~IOSIntermediateDumpData() override;\n\n  //! \\brief Constructs a new data object which owns a std::vector<uint8_t>.\n  //!\n  //! \\param[in] data A vector of uint8_t.\n  IOSIntermediateDumpData(std::vector<uint8_t> data) : data_(std::move(data)) {}\n\n  // IOSIntermediateDumpObject:\n  Type GetType() const override;\n\n  //! \\brief Returns data as a string.\n  std::string GetString() const;\n\n  //! \\brief Copies the data into \\a value if sizeof(T) matches data_.size().\n  //!\n  //! \\param[out] value The data to populate.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false`.\n  template <typename T>\n  bool GetValue(T* value) const {\n    return GetValueInternal(reinterpret_cast<void*>(value), sizeof(*value));\n  }\n\n  const std::vector<uint8_t>& bytes() const { return data_; }\n\n private:\n  bool GetValueInternal(void* value, size_t value_size) const;\n\n  std::vector<uint8_t> data_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_DATA_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_format.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_FORMAT_H_\n#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_FORMAT_H_\n\n#include <stdint.h>\n\nnamespace crashpad {\nnamespace internal {\n\n// Define values for intermediate dump enum class IntermediateDumpKey. Use\n// |INTERMEDIATE_DUMP_KEYS| so it is easier to print human readable keys in\n// logs.\n// clang-format off\n#define INTERMEDIATE_DUMP_KEYS(TD) \\\n  TD(kInvalid, 0) \\\n  TD(kVersion, 1) \\\n  TD(kMachException, 1000) \\\n    TD(kCodes, 1001) \\\n    TD(kException, 1002) \\\n    TD(kFlavor, 1003) \\\n    TD(kState, 1004) \\\n  TD(kSignalException, 2000) \\\n    TD(kSignalNumber, 2001) \\\n    TD(kSignalCode, 2002) \\\n    TD(kSignalAddress, 2003) \\\n  TD(kNSException, 2500) \\\n  TD(kModules, 3000) \\\n    TD(kAddress, 3001) \\\n    TD(kFileType, 3002) \\\n    TD(kName, 3003) \\\n    TD(kSize, 3004) \\\n    TD(kDylibCurrentVersion, 3005) \\\n    TD(kSourceVersion, 3006) \\\n    TD(kTimestamp, 3007) \\\n    TD(kUUID, 3008) \\\n    TD(kAnnotationObjects, 3009) \\\n    TD(kAnnotationsSimpleMap, 3010) \\\n    TD(kAnnotationsVector, 3011) \\\n    TD(kAnnotationType, 3012) \\\n    TD(kAnnotationName, 3013) \\\n    TD(kAnnotationValue, 3014) \\\n    TD(kAnnotationsCrashInfo, 3015) \\\n    TD(kAnnotationsCrashInfoMessage1, 3016) \\\n    TD(kAnnotationsCrashInfoMessage2, 3017) \\\n    TD(kAnnotationsDyldErrorString, 3018) \\\n    TD(kModuleExtraMemoryRegions, 3019) \\\n    TD(kModuleExtraMemoryRegionAddress, 3020) \\\n    TD(kModuleExtraMemoryRegionData, 3021) \\\n    TD(kModuleIntermediateDumpExtraMemoryRegions, 3022) \\\n    TD(kModuleIntermediateDumpExtraMemoryRegionAddress, 3023) \\\n    TD(kModuleIntermediateDumpExtraMemoryRegionData, 3024) \\\n  TD(kProcessInfo, 4000) \\\n    TD(kParentPID, 4001) \\\n    TD(kPID, 4002) \\\n    TD(kStartTime, 4003) \\\n    TD(kSnapshotTime, 4004) \\\n    TD(kTaskBasicInfo, 4005) \\\n    TD(kTaskThreadTimes, 4006) \\\n    TD(kSystemTime, 4007) \\\n    TD(kUserTime, 4008) \\\n  TD(kSystemInfo, 5000) \\\n    TD(kCpuCount, 5001) \\\n    TD(kCpuVendor, 5002) \\\n    TD(kDaylightName, 5003) \\\n    TD(kDaylightOffsetSeconds, 5004) \\\n    TD(kHasDaylightSavingTime, 5005) \\\n    TD(kIsDaylightSavingTime, 5006) \\\n    TD(kMachineDescription, 5007) \\\n    TD(kOSVersionBugfix, 5008) \\\n    TD(kOSVersionBuild, 5009) \\\n    TD(kOSVersionMajor, 5010) \\\n    TD(kOSVersionMinor, 5011) \\\n    TD(kPageSize, 5012) \\\n    TD(kStandardName, 5013) \\\n    TD(kStandardOffsetSeconds, 5014) \\\n    TD(kVMStat, 5015) \\\n    TD(kActive, 5016) \\\n    TD(kFree, 5017) \\\n    TD(kInactive, 5018) \\\n    TD(kWired, 5019) \\\n    TD(kAddressMask, 5020) \\\n    TD(kCrashpadUptime, 5021) \\\n  TD(kThreads, 6000) \\\n    TD(kDebugState, 6001) \\\n    TD(kFloatState, 6002) \\\n    TD(kThreadState, 6003) \\\n    TD(kPriority, 6004) \\\n    TD(kStackRegionAddress, 6005) \\\n    TD(kStackRegionData, 6006) \\\n    TD(kSuspendCount, 6007) \\\n    TD(kThreadID, 6008) \\\n    TD(kThreadDataAddress, 6009) \\\n    TD(kThreadUncaughtNSExceptionFrames, 6010) \\\n    TD(kThreadContextMemoryRegions, 6011) \\\n    TD(kThreadContextMemoryRegionAddress, 6012) \\\n    TD(kThreadContextMemoryRegionData, 6013) \\\n    TD(kThreadName, 6014) \\\n  TD(kMaxValue, 65535) \\\n// clang-format on\n\n//! \\brief They key for items in the intermediate dump file.\n//!\n//! These values are persisted to the intermediate crash dump file. Entries\n//! should not be renumbered and numeric values should never be reused.\nenum class IntermediateDumpKey : uint16_t {\n#define X(NAME, VALUE) NAME = VALUE,\n  INTERMEDIATE_DUMP_KEYS(X)\n#undef X\n};\n\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_FORMAT_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_interface.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_interface.h\"\n\n#include \"util/file/scoped_remove_file.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nbool IOSIntermediateDumpFilePath::Initialize(const base::FilePath& path) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  ScopedRemoveFile file_remover(path);\n  handle_.reset(LoggingOpenFileForRead(path));\n  if (!handle_.is_valid())\n    return false;\n\n  reader_ = std::make_unique<WeakFileHandleFileReader>(handle_.get());\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nFileReaderInterface* IOSIntermediateDumpFilePath::FileReader() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return reader_.get();\n}\n\nFileOffset IOSIntermediateDumpFilePath::Size() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return LoggingFileSizeByHandle(handle_.get());\n}\n\nIOSIntermediateDumpByteArray::IOSIntermediateDumpByteArray(const void* data,\n                                                           size_t size) {\n  string_file_ = std::make_unique<StringFile>();\n  string_file_->SetString(\n      std::string(reinterpret_cast<const char*>(data), size));\n}\n\nFileReaderInterface* IOSIntermediateDumpByteArray::FileReader() const {\n  return string_file_.get();\n}\n\nFileOffset IOSIntermediateDumpByteArray::Size() const {\n  return string_file_->string().size();\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_interface.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_INTERFACE_H_\n#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_INTERFACE_H_\n\n#include \"base/files/file_path.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/file/string_file.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief The base class for reading data into an IOSIntermediateDumpReader.\nclass IOSIntermediateDumpInterface {\n public:\n  virtual FileReaderInterface* FileReader() const = 0;\n  virtual FileOffset Size() const = 0;\n};\n\n//! \\brief An intermediate dump backed by a FilePath. FilePath is unlinked\n//!     immediately upon initialization to ensure files are only processed once\n//!     in the event a crash is introduced by this intermediate dump.\nclass IOSIntermediateDumpFilePath : public IOSIntermediateDumpInterface {\n public:\n  bool Initialize(const base::FilePath& path);\n\n  // IOSIntermediateDumpInterface:\n  FileReaderInterface* FileReader() const override;\n  FileOffset Size() const override;\n\n private:\n  ScopedFileHandle handle_;\n  std::unique_ptr<WeakFileHandleFileReader> reader_;\n  InitializationStateDcheck initialized_;\n};\n\n//! \\brief An intermediate dump backed by a byte array.\nclass IOSIntermediateDumpByteArray : public IOSIntermediateDumpInterface {\n public:\n  IOSIntermediateDumpByteArray(const void* data, size_t size);\n\n  // IOSIntermediateDumpInterface\n  FileReaderInterface* FileReader() const override;\n  FileOffset Size() const override;\n\n private:\n  std::unique_ptr<StringFile> string_file_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_INTERFACE_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_list.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_list.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nIOSIntermediateDumpList::IOSIntermediateDumpList() : list_() {}\n\nIOSIntermediateDumpList::~IOSIntermediateDumpList() {}\n\nIOSIntermediateDumpObject::Type IOSIntermediateDumpList::GetType() const {\n  return Type::kList;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_list.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_LIST_H_\n#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_LIST_H_\n\n#include <vector>\n\n#include \"util/ios/ios_intermediate_dump_map.h\"\n#include \"util/ios/ios_intermediate_dump_object.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A list object, consisting of a vector of IOSIntermediateDumpMap.\n//!\n//! Provides a wrapper around an internal std::vector.\nclass IOSIntermediateDumpList : public IOSIntermediateDumpObject {\n public:\n  IOSIntermediateDumpList();\n\n  IOSIntermediateDumpList(const IOSIntermediateDumpList&) = delete;\n  IOSIntermediateDumpList& operator=(const IOSIntermediateDumpList&) = delete;\n\n  ~IOSIntermediateDumpList() override;\n\n  // IOSIntermediateDumpObject:\n  Type GetType() const override;\n\n  using VectorType = std::vector<std::unique_ptr<const IOSIntermediateDumpMap>>;\n  VectorType::const_iterator begin() const { return list_.begin(); }\n  VectorType::const_iterator end() const { return list_.end(); }\n  VectorType::size_type size() const { return list_.size(); }\n  void push_back(std::unique_ptr<const IOSIntermediateDumpMap> val) {\n    list_.push_back(std::move(val));\n  }\n\n private:\n  VectorType list_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_LIST_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_map.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_map.h\"\n\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_list.h\"\n#include \"util/ios/ios_intermediate_dump_object.h\"\n\nusing crashpad::internal::IntermediateDumpKey;\n\nnamespace crashpad {\nnamespace internal {\n\nIOSIntermediateDumpMap::IOSIntermediateDumpMap() : map_() {}\n\nIOSIntermediateDumpMap::~IOSIntermediateDumpMap() {}\n\nIOSIntermediateDumpMap::Type IOSIntermediateDumpMap::GetType() const {\n  return Type::kMap;\n}\n\nconst IOSIntermediateDumpData* IOSIntermediateDumpMap::GetAsData(\n    const IntermediateDumpKey& key) const {\n  auto object_it = map_.find(key);\n  if (object_it != map_.end()) {\n    IOSIntermediateDumpObject* object = object_it->second.get();\n    if (object->GetType() == Type::kData)\n      return static_cast<IOSIntermediateDumpData*>(object);\n  }\n  return nullptr;\n}\n\nconst IOSIntermediateDumpList* IOSIntermediateDumpMap::GetAsList(\n    const IntermediateDumpKey& key) const {\n  auto object_it = map_.find(key);\n  if (object_it != map_.end()) {\n    IOSIntermediateDumpObject* object = object_it->second.get();\n    if (object->GetType() == Type::kList)\n      return static_cast<IOSIntermediateDumpList*>(object);\n  }\n  return nullptr;\n}\n\nconst IOSIntermediateDumpMap* IOSIntermediateDumpMap::GetAsMap(\n    const IntermediateDumpKey& key) const {\n  auto object_it = map_.find(key);\n  if (object_it != map_.end()) {\n    IOSIntermediateDumpObject* object = object_it->second.get();\n    if (object->GetType() == Type::kMap)\n      return static_cast<IOSIntermediateDumpMap*>(object);\n  }\n  return nullptr;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_map.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_PACK_IOS_MAP_H_\n#define CRASHPAD_UTIL_IOS_PACK_IOS_MAP_H_\n\n#include <map>\n#include <memory>\n\n#include \"util/ios/ios_intermediate_dump_format.h\"\n#include \"util/ios/ios_intermediate_dump_object.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nclass IOSIntermediateDumpList;\nclass IOSIntermediateDumpData;\n\n//! \\brief A map object containing a IntermediateDump Key-Object pair.\n//!\n//! Also provides an element access helper.\nclass IOSIntermediateDumpMap : public IOSIntermediateDumpObject {\n public:\n  IOSIntermediateDumpMap();\n\n  IOSIntermediateDumpMap(const IOSIntermediateDumpMap&) = delete;\n  IOSIntermediateDumpMap& operator=(const IOSIntermediateDumpMap&) = delete;\n\n  ~IOSIntermediateDumpMap() override;\n\n  // IOSIntermediateDumpObject:\n  Type GetType() const override;\n\n  //! \\brief Returns an IOSIntermediateDumpData. If the type is not kData,\n  //!     returns nullptr\n  const IOSIntermediateDumpData* GetAsData(\n      const IntermediateDumpKey& key) const;\n\n  //! \\brief Returns an IOSIntermediateDumpList. If the type is not kList,\n  //!     returns nullptr\n  const IOSIntermediateDumpList* GetAsList(\n      const IntermediateDumpKey& key) const;\n\n  //! \\brief Returns an IOSIntermediateDumpMap.  If the type is not kMap,\n  //!     returns nullptr\n  const IOSIntermediateDumpMap* GetAsMap(const IntermediateDumpKey& key) const;\n\n  //! \\brief Returns `true` if the map is empty.\n  bool empty() const { return map_.empty(); }\n\n private:\n  friend class IOSIntermediateDumpReader;\n  std::map<IntermediateDumpKey, std::unique_ptr<IOSIntermediateDumpObject>>\n      map_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_PACK_IOS_MAP_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_object.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_object.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nIOSIntermediateDumpObject::IOSIntermediateDumpObject() = default;\n\nIOSIntermediateDumpObject::~IOSIntermediateDumpObject() {}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_object.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_OBJECT_H_\n#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_OBJECT_H_\n\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Base class for intermediate dump object types.\nclass IOSIntermediateDumpObject {\n public:\n  IOSIntermediateDumpObject();\n\n  IOSIntermediateDumpObject(const IOSIntermediateDumpObject&) = delete;\n  IOSIntermediateDumpObject& operator=(const IOSIntermediateDumpObject&) =\n      delete;\n\n  virtual ~IOSIntermediateDumpObject();\n\n  //! \\brief The type of object stored in the intermediate dump.  .\n  enum class Type {\n    //! \\brief A data object, containing array of bytes.\n    kData,\n\n    //! \\brief A map object, containing other lists, maps and data objects.\n    kMap,\n\n    //! \\brief A list object, containing a list of map objects.\n    kList,\n  };\n\n  //! \\brief Returns a type.\n  virtual Type GetType() const = 0;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_OBJECT_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_reader.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_reader.h\"\n\n#include <memory>\n#include <stack>\n#include <vector>\n\n#include \"base/logging.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_format.h\"\n#include \"util/ios/ios_intermediate_dump_list.h\"\n#include \"util/ios/ios_intermediate_dump_object.h\"\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nIOSIntermediateDumpReaderInitializeResult IOSIntermediateDumpReader::Initialize(\n    const IOSIntermediateDumpInterface& dump_interface) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  // Don't initialize empty files.\n  FileOffset size = dump_interface.Size();\n  if (size == 0) {\n    return IOSIntermediateDumpReaderInitializeResult::kFailure;\n  }\n\n  IOSIntermediateDumpReaderInitializeResult result =\n      IOSIntermediateDumpReaderInitializeResult::kSuccess;\n  if (!Parse(dump_interface.FileReader(), size)) {\n    LOG(ERROR) << \"Intermediate dump parsing failed\";\n    result = IOSIntermediateDumpReaderInitializeResult::kIncomplete;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return result;\n}\n\nconst IOSIntermediateDumpMap* IOSIntermediateDumpReader::RootMap() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return &intermediate_dump_;\n}\n\nbool IOSIntermediateDumpReader::Parse(FileReaderInterface* reader,\n                                      FileOffset file_size) {\n  std::stack<IOSIntermediateDumpObject*> stack;\n  stack.push(&intermediate_dump_);\n  using Command = IOSIntermediateDumpWriter::CommandType;\n  using Type = IOSIntermediateDumpObject::Type;\n\n  Command command;\n  if (!reader->ReadExactly(&command, sizeof(Command)) ||\n      command != Command::kRootMapStart) {\n    LOG(ERROR) << \"Unexpected start to root map.\";\n    return false;\n  }\n\n  while (reader->ReadExactly(&command, sizeof(Command))) {\n    constexpr int kMaxStackDepth = 10;\n    if (stack.size() > kMaxStackDepth) {\n      LOG(ERROR) << \"Unexpected depth of intermediate dump data.\";\n      return false;\n    }\n\n    IOSIntermediateDumpObject* parent = stack.top();\n    switch (command) {\n      case Command::kMapStart: {\n        std::unique_ptr<IOSIntermediateDumpMap> new_map(\n            new IOSIntermediateDumpMap());\n        if (parent->GetType() == Type::kMap) {\n          const auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent);\n          stack.push(new_map.get());\n          IntermediateDumpKey key;\n          if (!reader->ReadExactly(&key, sizeof(key)))\n            return false;\n          if (key == IntermediateDumpKey::kInvalid)\n            return false;\n          parent_map->map_[key] = std::move(new_map);\n        } else if (parent->GetType() == Type::kList) {\n          const auto parent_list =\n              static_cast<IOSIntermediateDumpList*>(parent);\n          stack.push(new_map.get());\n          parent_list->push_back(std::move(new_map));\n        } else {\n          LOG(ERROR) << \"Unexpected parent (not a map or list).\";\n          return false;\n        }\n        break;\n      }\n      case Command::kArrayStart: {\n        auto new_list = std::make_unique<IOSIntermediateDumpList>();\n        if (parent->GetType() != Type::kMap) {\n          LOG(ERROR) << \"Attempting to push an array not in a map.\";\n          return false;\n        }\n\n        IntermediateDumpKey key;\n        if (!reader->ReadExactly(&key, sizeof(key)))\n          return false;\n        if (key == IntermediateDumpKey::kInvalid)\n          return false;\n\n        auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent);\n        stack.push(new_list.get());\n        parent_map->map_[key] = std::move(new_list);\n        break;\n      }\n      case Command::kMapEnd:\n        if (stack.size() < 2) {\n          LOG(ERROR) << \"Attempting to pop off main map.\";\n          return false;\n        }\n\n        if (parent->GetType() != Type::kMap) {\n          LOG(ERROR) << \"Unexpected map end not in a map.\";\n          return false;\n        }\n        stack.pop();\n        break;\n      case Command::kArrayEnd:\n        if (stack.size() < 2) {\n          LOG(ERROR) << \"Attempting to pop off main map.\";\n          return false;\n        }\n        if (parent->GetType() != Type::kList) {\n          LOG(ERROR) << \"Unexpected list end not in a list.\";\n          return false;\n        }\n        stack.pop();\n        break;\n      case Command::kProperty: {\n        if (parent->GetType() != Type::kMap) {\n          LOG(ERROR) << \"Attempting to add a property not in a map.\";\n          return false;\n        }\n        IntermediateDumpKey key;\n        if (!reader->ReadExactly(&key, sizeof(key)))\n          return false;\n        if (key == IntermediateDumpKey::kInvalid)\n          return false;\n\n        size_t value_length;\n        if (!reader->ReadExactly(&value_length, sizeof(value_length))) {\n          return false;\n        }\n\n        constexpr int kMaximumPropertyLength = 64 * 1024 * 1024;  // 64MB.\n        if (value_length > kMaximumPropertyLength) {\n          LOG(ERROR) << \"Attempting to read a property that's too big: \"\n                     << value_length;\n          return false;\n        }\n\n        std::vector<uint8_t> data(value_length);\n        if (!reader->ReadExactly(data.data(), value_length)) {\n          return false;\n        }\n        auto parent_map = static_cast<IOSIntermediateDumpMap*>(parent);\n        if (parent_map->map_.find(key) != parent_map->map_.end()) {\n          LOG(ERROR) << \"Inserting duplicate key\";\n        }\n        parent_map->map_[key] =\n            std::make_unique<IOSIntermediateDumpData>(std::move(data));\n        break;\n      }\n      case Command::kRootMapEnd: {\n        if (stack.size() != 1) {\n          LOG(ERROR) << \"Unexpected end of root map.\";\n          return false;\n        }\n\n        if (reader->Seek(0, SEEK_CUR) != file_size) {\n          LOG(ERROR) << \"Root map ended before end of file.\";\n          return false;\n        }\n        return true;\n      }\n      default:\n        LOG(ERROR) << \"Failed to parse serialized intermediate minidump.\";\n        return false;\n    }\n  }\n\n  LOG(ERROR) << \"Unexpected end of root map.\";\n  return false;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_reader.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_READER_H_\n#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_READER_H_\n\n#include \"util/ios/ios_intermediate_dump_interface.h\"\n#include \"util/ios/ios_intermediate_dump_map.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief The return value for IOSIntermediateDumpReader::Initialize.\nenum class IOSIntermediateDumpReaderInitializeResult : int {\n  //! \\brief The intermediate dump was read successfully, initialization\n  //!     succeeded.\n  kSuccess,\n\n  //! \\brief The intermediate dump could be loaded, but parsing was incomplete.\n  //!     An attempt to parse the RootMap should still be made, as there may\n  //!     still be valuable information to put into a minidump.\n  kIncomplete,\n\n  //! \\brief The intermediate dump could not be loaded, initialization failed.\n  kFailure,\n};\n\n//! \\brief Open and parse iOS intermediate dumps.\nclass IOSIntermediateDumpReader {\n public:\n  IOSIntermediateDumpReader() {}\n\n  IOSIntermediateDumpReader(const IOSIntermediateDumpReader&) = delete;\n  IOSIntermediateDumpReader& operator=(const IOSIntermediateDumpReader&) =\n      delete;\n\n  //! \\brief Open and parses \\a dump_interface.\n  //!\n  //! Will attempt to parse the binary file, similar to a JSON file, using the\n  //! same format used by IOSIntermediateDumpWriter, resulting in an\n  //! IOSIntermediateDumpMap\n  //!\n  //! \\param[in] dump_interface An interface corresponding to an intermediate\n  //!     dump file.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false`. Clients may\n  //!     still attempt to parse RootMap, as partial minidumps may still be\n  //!     usable.\n  IOSIntermediateDumpReaderInitializeResult Initialize(\n      const IOSIntermediateDumpInterface& dump_interface);\n\n  //! \\brief Returns an IOSIntermediateDumpMap corresponding to the root of the\n  //!     intermediate dump.\n  const IOSIntermediateDumpMap* RootMap();\n\n private:\n  bool Parse(FileReaderInterface* reader, FileOffset file_size);\n  IOSIntermediateDumpMap intermediate_dump_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_READER_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_reader_test.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_reader.h\"\n\n#include <fcntl.h>\n#include <mach/vm_map.h>\n\n#include \"base/posix/eintr_wrapper.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/ios/ios_intermediate_dump_data.h\"\n#include \"util/ios/ios_intermediate_dump_format.h\"\n#include \"util/ios/ios_intermediate_dump_list.h\"\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing Key = internal::IntermediateDumpKey;\nusing Result = internal::IOSIntermediateDumpReaderInitializeResult;\nusing internal::IOSIntermediateDumpWriter;\n\nclass IOSIntermediateDumpReaderTest : public testing::Test {\n protected:\n  // testing::Test:\n\n  void SetUp() override {\n    path_ = temp_dir_.path().Append(\"dump_file\");\n    fd_ = base::ScopedFD(HANDLE_EINTR(\n        ::open(path_.value().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644)));\n    ASSERT_GE(fd_.get(), 0) << ErrnoMessage(\"open\");\n\n    writer_ = std::make_unique<IOSIntermediateDumpWriter>();\n    ASSERT_TRUE(writer_->Open(path_));\n    ASSERT_TRUE(IsRegularFile(path_));\n    dump_interface_.Initialize(path_);\n  }\n\n  void TearDown() override {\n    ASSERT_TRUE(writer_->Close());\n    fd_.reset();\n    writer_.reset();\n    EXPECT_FALSE(IsRegularFile(path_));\n  }\n\n  int fd() { return fd_.get(); }\n\n  const base::FilePath& path() const { return path_; }\n  const auto& dump_interface() const { return dump_interface_; }\n\n  std::unique_ptr<IOSIntermediateDumpWriter> writer_;\n\n private:\n  base::ScopedFD fd_;\n  ScopedTempDir temp_dir_;\n  base::FilePath path_;\n  internal::IOSIntermediateDumpFilePath dump_interface_;\n};\n\nTEST_F(IOSIntermediateDumpReaderTest, ReadNoFile) {\n  internal::IOSIntermediateDumpReader reader;\n  internal::IOSIntermediateDumpFilePath dump_interface;\n  EXPECT_FALSE(dump_interface.Initialize(base::FilePath()));\n  EXPECT_FALSE(IsRegularFile(path()));\n}\n\nTEST_F(IOSIntermediateDumpReaderTest, ReadEmptyFile) {\n  internal::IOSIntermediateDumpReader reader;\n  EXPECT_EQ(reader.Initialize(dump_interface()), Result::kFailure);\n  EXPECT_FALSE(IsRegularFile(path()));\n}\n\nTEST_F(IOSIntermediateDumpReaderTest, ReadHelloWorld) {\n  std::string hello_world(\"hello world.\");\n  EXPECT_TRUE(\n      LoggingWriteFile(fd(), hello_world.c_str(), hello_world.length()));\n  internal::IOSIntermediateDumpReader reader;\n  EXPECT_EQ(reader.Initialize(dump_interface()), Result::kIncomplete);\n  EXPECT_FALSE(IsRegularFile(path()));\n\n  const auto root_map = reader.RootMap();\n  EXPECT_TRUE(root_map->empty());\n}\n\nTEST_F(IOSIntermediateDumpReaderTest, FuzzTestCases) {\n  constexpr uint8_t fuzz1[] = {0x6,\n                               0x5,\n                               0x0,\n                               0xff,\n                               0xff,\n                               0xfd,\n                               0x1,\n                               0xff,\n                               0xff,\n                               0xff,\n                               0xff,\n                               0xff,\n                               0xfd,\n                               0x1,\n                               0x7,\n                               0x16};\n\n  internal::IOSIntermediateDumpByteArray dump_interface(fuzz1, sizeof(fuzz1));\n  internal::IOSIntermediateDumpReader reader;\n  EXPECT_EQ(reader.Initialize(dump_interface), Result::kIncomplete);\n  const auto root_map = reader.RootMap();\n  EXPECT_TRUE(root_map->empty());\n}\n\nTEST_F(IOSIntermediateDumpReaderTest, WriteBadPropertyDataLength) {\n  internal::IOSIntermediateDumpReader reader;\n  IOSIntermediateDumpWriter::CommandType command_type =\n      IOSIntermediateDumpWriter::CommandType::kRootMapStart;\n  EXPECT_TRUE(LoggingWriteFile(fd(), &command_type, sizeof(command_type)));\n\n  command_type = IOSIntermediateDumpWriter::CommandType::kProperty;\n  EXPECT_TRUE(LoggingWriteFile(fd(), &command_type, sizeof(command_type)));\n  Key key = Key::kVersion;\n  EXPECT_TRUE(LoggingWriteFile(fd(), &key, sizeof(key)));\n  uint8_t value = 1;\n  size_t value_length = 999999;\n  EXPECT_TRUE(LoggingWriteFile(fd(), &value_length, sizeof(size_t)));\n  EXPECT_TRUE(LoggingWriteFile(fd(), &value, sizeof(value)));\n  EXPECT_EQ(reader.Initialize(dump_interface()), Result::kIncomplete);\n  EXPECT_FALSE(IsRegularFile(path()));\n\n  const auto root_map = reader.RootMap();\n  EXPECT_TRUE(root_map->empty());\n  const auto version_data = root_map->GetAsData(Key::kVersion);\n  EXPECT_EQ(version_data, nullptr);\n}\n\nTEST_F(IOSIntermediateDumpReaderTest, InvalidArrayInArray) {\n  internal::IOSIntermediateDumpReader reader;\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap scopedRoot(writer_.get());\n    IOSIntermediateDumpWriter::ScopedArray threadArray(writer_.get(),\n                                                       Key::kThreads);\n    IOSIntermediateDumpWriter::ScopedArray innerThreadArray(writer_.get(),\n                                                            Key::kModules);\n\n    // Write version last, so it's not parsed.\n    int8_t version = 1;\n    writer_->AddProperty(Key::kVersion, &version);\n  }\n  EXPECT_TRUE(writer_->Close());\n  EXPECT_EQ(reader.Initialize(dump_interface()), Result::kIncomplete);\n  EXPECT_FALSE(IsRegularFile(path()));\n\n  const auto root_map = reader.RootMap();\n  EXPECT_FALSE(root_map->empty());\n  const auto version_data = root_map->GetAsData(Key::kVersion);\n  EXPECT_EQ(version_data, nullptr);\n}\n\nTEST_F(IOSIntermediateDumpReaderTest, InvalidPropertyInArray) {\n  internal::IOSIntermediateDumpReader reader;\n\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap scopedRoot(writer_.get());\n    IOSIntermediateDumpWriter::ScopedArray threadArray(writer_.get(),\n                                                       Key::kThreads);\n\n    // Write version last, so it's not parsed.\n    int8_t version = 1;\n    writer_->AddProperty(Key::kVersion, &version);\n  }\n  EXPECT_TRUE(writer_->Close());\n  EXPECT_EQ(reader.Initialize(dump_interface()), Result::kIncomplete);\n  EXPECT_FALSE(IsRegularFile(path()));\n\n  const auto root_map = reader.RootMap();\n  EXPECT_FALSE(root_map->empty());\n  const auto version_data = root_map->GetAsData(Key::kVersion);\n  EXPECT_EQ(version_data, nullptr);\n}\n\nTEST_F(IOSIntermediateDumpReaderTest, ReadValidData) {\n  internal::IOSIntermediateDumpReader reader;\n  uint8_t version = 1;\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap scopedRoot(writer_.get());\n    EXPECT_TRUE(writer_->AddProperty(Key::kVersion, &version));\n    {\n      IOSIntermediateDumpWriter::ScopedArray threadArray(\n          writer_.get(), Key::kThreadContextMemoryRegions);\n      IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer_.get());\n\n      std::string random_data(\"random_data\");\n      EXPECT_TRUE(writer_->AddProperty(Key::kThreadContextMemoryRegionAddress,\n                                       &version));\n      EXPECT_TRUE(writer_->AddProperty(Key::kThreadContextMemoryRegionData,\n                                       random_data.c_str(),\n                                       random_data.length()));\n    }\n\n    {\n      IOSIntermediateDumpWriter::ScopedMap map(writer_.get(),\n                                               Key::kProcessInfo);\n      pid_t p_pid = getpid();\n      EXPECT_TRUE(writer_->AddProperty(Key::kPID, &p_pid));\n    }\n  }\n\n  EXPECT_TRUE(writer_->Close());\n  EXPECT_EQ(reader.Initialize(dump_interface()), Result::kSuccess);\n  EXPECT_FALSE(IsRegularFile(path()));\n\n  auto root_map = reader.RootMap();\n  EXPECT_FALSE(root_map->empty());\n  version = -1;\n  const auto version_data = root_map->GetAsData(Key::kVersion);\n  ASSERT_NE(version_data, nullptr);\n  EXPECT_TRUE(version_data->GetValue<uint8_t>(&version));\n  EXPECT_EQ(version, 1);\n\n  const auto process_info = root_map->GetAsMap(Key::kProcessInfo);\n  ASSERT_NE(process_info, nullptr);\n  const auto pid_data = process_info->GetAsData(Key::kPID);\n  ASSERT_NE(pid_data, nullptr);\n  pid_t p_pid = -1;\n  EXPECT_TRUE(pid_data->GetValue<pid_t>(&p_pid));\n  ASSERT_EQ(p_pid, getpid());\n\n  const auto thread_context_memory_regions =\n      root_map->GetAsList(Key::kThreadContextMemoryRegions);\n  EXPECT_EQ(thread_context_memory_regions->size(), 1UL);\n  for (const auto& region : *thread_context_memory_regions) {\n    const auto data = region->GetAsData(Key::kThreadContextMemoryRegionData);\n    ASSERT_NE(data, nullptr);\n    // Load as string.\n    EXPECT_EQ(data->GetString(), \"random_data\");\n\n    // Load as bytes.\n    auto bytes = data->bytes();\n    vm_size_t data_size = bytes.size();\n    EXPECT_EQ(data_size, 11UL);\n\n    const char* data_bytes = reinterpret_cast<const char*>(bytes.data());\n    EXPECT_EQ(std::string(data_bytes, data_size), \"random_data\");\n  }\n\n  const auto system_info = root_map->GetAsMap(Key::kSystemInfo);\n  EXPECT_EQ(system_info, nullptr);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_writer.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n\n#include <fcntl.h>\n#include <mach/mach.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <ostream>\n\n#include \"base/check.h\"\n#include \"base/check_op.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n#include \"util/ios/raw_logging.h\"\n#include \"util/ios/scoped_vm_read.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n// Similar to LoggingWriteFile but with CRASHPAD_RAW_LOG.\nbool RawLoggingWriteFile(int fd, const void* data, size_t size) {\n  const char* data_char = static_cast<const char*>(data);\n  while (size > 0) {\n    ssize_t bytes_written = HANDLE_EINTR(write(fd, data_char, size));\n    if (bytes_written < 0 || bytes_written == 0) {\n      CRASHPAD_RAW_LOG_ERROR(bytes_written, \"RawLoggingWriteFile\");\n      return false;\n    }\n    data_char += bytes_written;\n    size -= bytes_written;\n  }\n  return true;\n}\n\n// Similar to LoggingCloseFile but with CRASHPAD_RAW_LOG.\nbool RawLoggingCloseFile(int fd) {\n  int rv = IGNORE_EINTR(close(fd));\n  if (rv != 0) {\n    CRASHPAD_RAW_LOG_ERROR(rv, \"RawLoggingCloseFile\");\n  }\n  return rv == 0;\n}\n\nIOSIntermediateDumpWriter::~IOSIntermediateDumpWriter() {\n  CHECK_EQ(fd_, -1) << \"Call Close() before this object is destroyed.\";\n}\n\nbool IOSIntermediateDumpWriter::Open(const base::FilePath& path) {\n  // Set data protection class D (No protection). A file with this type of\n  // protection can be read from or written to at any time.\n  // See:\n  // https://support.apple.com/guide/security/data-protection-classes-secb010e978a/web\n  constexpr int PROTECTION_CLASS_D = 4;\n  fd_ = HANDLE_EINTR(open_dprotected_np(path.value().c_str(),\n                                        O_WRONLY | O_CREAT | O_TRUNC,\n                                        PROTECTION_CLASS_D,\n                                        0 /* dpflags */,\n                                        0644 /* mode */));\n  if (fd_ < 0) {\n    CRASHPAD_RAW_LOG_ERROR(fd_, \"open intermediate dump\");\n    CRASHPAD_RAW_LOG(path.value().c_str());\n    return false;\n  }\n\n  return true;\n}\n\nbool IOSIntermediateDumpWriter::Close() {\n  if (fd_ < 0) {\n    return true;\n  }\n  const bool flushed = FlushWriteBuffer();\n  const bool closed = RawLoggingCloseFile(fd_);\n  fd_ = -1;\n  return flushed && closed;\n}\n\nbool IOSIntermediateDumpWriter::AddPropertyCString(IntermediateDumpKey key,\n                                                   size_t max_length,\n                                                   const char* value) {\n  constexpr size_t kMaxStringBytes = 1024;\n  if (max_length > kMaxStringBytes) {\n    CRASHPAD_RAW_LOG(\"AddPropertyCString max_length too large\");\n    return false;\n  }\n\n  char buffer[kMaxStringBytes];\n  size_t string_length;\n  if (ReadCStringInternal(value, buffer, max_length, &string_length)) {\n    return Property(key, buffer, string_length);\n  }\n  return false;\n}\n\nbool IOSIntermediateDumpWriter::ReadCStringInternal(const char* value,\n                                                    char* buffer,\n                                                    size_t max_length,\n                                                    size_t* string_length) {\n  size_t length = 0;\n  while (length < max_length) {\n    vm_address_t data_address = reinterpret_cast<vm_address_t>(value + length);\n    // Calculate bytes to read past `data_address`, either the number of bytes\n    // to the end of the page, or the remaining bytes in `buffer`, whichever is\n    // smaller.\n    size_t data_to_end_of_page =\n        getpagesize() - (data_address - trunc_page(data_address));\n    size_t remaining_bytes_in_buffer = max_length - length;\n    size_t bytes_to_read =\n        std::min(data_to_end_of_page, remaining_bytes_in_buffer);\n\n    char* buffer_start = buffer + length;\n    size_t bytes_read = 0;\n    kern_return_t kr =\n        vm_read_overwrite(mach_task_self(),\n                          data_address,\n                          bytes_to_read,\n                          reinterpret_cast<vm_address_t>(buffer_start),\n                          &bytes_read);\n    if (kr != KERN_SUCCESS || bytes_read <= 0) {\n      CRASHPAD_RAW_LOG(\"ReadCStringInternal vm_read_overwrite failed\");\n      return false;\n    }\n\n    char* nul = static_cast<char*>(memchr(buffer_start, '\\0', bytes_read));\n    if (nul != nullptr) {\n      length += nul - buffer_start;\n      *string_length = length;\n      return true;\n    }\n    length += bytes_read;\n  }\n  CRASHPAD_RAW_LOG(\"unterminated string\");\n  return false;\n}\n\nbool IOSIntermediateDumpWriter::AddPropertyBytes(IntermediateDumpKey key,\n                                                 const void* value,\n                                                 size_t value_length) {\n  constexpr size_t kMaxPropertyBytesSmallSize = 512;\n  if (value_length <= kMaxPropertyBytesSmallSize) {\n    char buffer[kMaxPropertyBytesSmallSize];\n    vm_size_t out_size = 0;\n    kern_return_t kr = vm_read_overwrite(mach_task_self(),\n                                         reinterpret_cast<vm_address_t>(value),\n                                         value_length,\n                                         reinterpret_cast<vm_address_t>(buffer),\n                                         &out_size);\n    if (kr != KERN_SUCCESS) {\n      // It's expected that this will sometimes fail. Don't log here.\n      return false;\n    }\n\n    return Property(key, buffer, out_size);\n  }\n\n  ScopedVMRead<char> vmread;\n  if (!vmread.Read(value, value_length))\n    return false;\n  return Property(key, vmread.get(), value_length);\n}\n\nbool IOSIntermediateDumpWriter::ArrayMapStart() {\n  const CommandType command_type = CommandType::kMapStart;\n  return BufferedWrite(&command_type, sizeof(command_type));\n}\n\nbool IOSIntermediateDumpWriter::MapStart(IntermediateDumpKey key) {\n  const CommandType command_type = CommandType::kMapStart;\n  return BufferedWrite(&command_type, sizeof(command_type)) &&\n         BufferedWrite(&key, sizeof(key));\n}\n\nbool IOSIntermediateDumpWriter::ArrayStart(IntermediateDumpKey key) {\n  const CommandType command_type = CommandType::kArrayStart;\n  return BufferedWrite(&command_type, sizeof(command_type)) &&\n         BufferedWrite(&key, sizeof(key));\n}\n\nbool IOSIntermediateDumpWriter::MapEnd() {\n  const CommandType command_type = CommandType::kMapEnd;\n  return BufferedWrite(&command_type, sizeof(command_type));\n}\n\nbool IOSIntermediateDumpWriter::ArrayEnd() {\n  const CommandType command_type = CommandType::kArrayEnd;\n  return BufferedWrite(&command_type, sizeof(command_type));\n}\n\nbool IOSIntermediateDumpWriter::RootMapStart() {\n  const CommandType command_type = CommandType::kRootMapStart;\n  return BufferedWrite(&command_type, sizeof(command_type));\n}\n\nbool IOSIntermediateDumpWriter::RootMapEnd() {\n  const CommandType command_type = CommandType::kRootMapEnd;\n  return BufferedWrite(&command_type, sizeof(command_type));\n}\n\nbool IOSIntermediateDumpWriter::Property(IntermediateDumpKey key,\n                                         const void* value,\n                                         size_t value_length) {\n  const CommandType command_type = CommandType::kProperty;\n  return BufferedWrite(&command_type, sizeof(command_type)) &&\n         BufferedWrite(&key, sizeof(key)) &&\n         BufferedWrite(&value_length, sizeof(size_t)) &&\n         BufferedWrite(value, value_length);\n}\n\nbool IOSIntermediateDumpWriter::FlushWriteBuffer() {\n  size_t size = buffer_occupied_;\n  buffer_occupied_ = 0;\n  return RawLoggingWriteFile(fd_, buffer_, size);\n}\n\nbool IOSIntermediateDumpWriter::BufferedWrite(const void* data,\n                                              size_t data_size) {\n  const char* data_char = static_cast<const char*>(data);\n  // If `buffer_` is occupied, fill it up first, and flush if full.\n  if (buffer_occupied_ > 0) {\n    size_t data_size_to_copy =\n        std::min(kBufferSize - buffer_occupied_, data_size);\n    memcpy(buffer_ + buffer_occupied_, data_char, data_size_to_copy);\n    buffer_occupied_ += data_size_to_copy;\n    data_char += data_size_to_copy;\n    data_size -= data_size_to_copy;\n\n    if (buffer_occupied_ == kBufferSize) {\n      if (!FlushWriteBuffer()) {\n        return false;\n      }\n    }\n  }\n\n  // Either `data_size` is big enough that it could fill `buffer_`, trigger\n  // a FlushWriteBuffer, and reset `buffer_occuppied_` to zero, or there is no\n  // data left to process.\n  DCHECK(buffer_occupied_ == 0 || data_size == 0);\n\n  // Write the rest of the `data` in an increment of kBufferSize.\n  if (data_size >= kBufferSize) {\n    DCHECK_EQ(buffer_occupied_, 0u);\n    size_t data_size_to_write = data_size - (data_size % kBufferSize);\n    if (!RawLoggingWriteFile(fd_, data_char, data_size_to_write)) {\n      return false;\n    }\n    data_char += data_size_to_write;\n    data_size -= data_size_to_write;\n  }\n\n  // If there's any `data` left, put it in `buffer_`.\n  if (data_size > 0) {\n    DCHECK_EQ(buffer_occupied_, 0u);\n    memcpy(buffer_, data_char, data_size);\n    buffer_occupied_ = data_size;\n  }\n\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_writer.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_WRITER_H_\n#define CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_WRITER_H_\n\n#include <sys/types.h>\n\n#include \"base/files/file_path.h\"\n#include \"util/ios/ios_intermediate_dump_format.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Wrapper class for writing intermediate dump file.\n//!\n//! Due to the limitations of in-process handling, an intermediate dump file is\n//! written during exceptions. The data is streamed to a file using only\n//! in-process safe methods.\n//!\n//! The file format is similar to binary JSON, supporting keyed properties, maps\n//! and arrays.\n//!  - Property [key:int, length:int, value:intarray]\n//!  - StartMap [key:int], followed by repeating Properties until EndMap\n//!  - StartArray [key:int], followed by repeating Maps until EndArray\n//!  - EndMap, EndArray, EndDocument\n//!\n//!  Similar to JSON, maps can contain other maps, arrays and properties.\n//!\n//! Note: All methods are `RUNS-DURING-CRASH`.\nclass IOSIntermediateDumpWriter final {\n public:\n  IOSIntermediateDumpWriter() : buffer_occupied_(0), fd_(-1) {}\n\n  IOSIntermediateDumpWriter(const IOSIntermediateDumpWriter&) = delete;\n  IOSIntermediateDumpWriter& operator=(const IOSIntermediateDumpWriter&) =\n      delete;\n\n  ~IOSIntermediateDumpWriter();\n\n  //! \\brief Command instructions for the intermediate dump reader.\n  enum class CommandType : uint8_t {\n    //! \\brief Indicates a new map, followed by associated key.\n    kMapStart = 0x01,\n\n    //! \\brief Indicates map is complete.\n    kMapEnd = 0x02,\n\n    //! \\brief Indicates a new array, followed by associated key.\n    kArrayStart = 0x03,\n\n    //! \\brief Indicates array is complete.\n    kArrayEnd = 0x04,\n\n    //! \\brief Indicates a new property, followed by a key, length and value.\n    kProperty = 0x05,\n\n    //! \\brief Indicates the start of the root map.\n    kRootMapStart = 0x06,\n\n    //! \\brief Indicates the end of the root map, and that there is nothing left\n    //!     to parse.\n    kRootMapEnd = 0x07,\n  };\n\n  //! \\brief Open and lock an intermediate dump file. This is the only method\n  //!     in the writer class that is generally run outside of a crash.\n  //!\n  //! The client must invoke `Close()` before this object is destroyed.\n  //!\n  //! \\param[in] path The path to the intermediate dump.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false`.\n  bool Open(const base::FilePath& path);\n\n  //! \\brief Completes writing the intermediate dump file and releases the\n  //!     file handle.\n  //!\n  //! \\return On success, returns `true`, otherwise returns `false`.\n  bool Close();\n\n  //! \\brief A scoped wrapper for calls to RootMapStart and RootMapEnd.\n  class ScopedRootMap {\n   public:\n    explicit ScopedRootMap(IOSIntermediateDumpWriter* writer)\n        : writer_(writer) {\n      writer->RootMapStart();\n    }\n\n    ScopedRootMap(const ScopedRootMap&) = delete;\n    ScopedRootMap& operator=(const ScopedRootMap&) = delete;\n\n    ~ScopedRootMap() { writer_->RootMapEnd(); }\n\n   private:\n    IOSIntermediateDumpWriter* writer_;\n  };\n\n  //! \\brief A scoped wrapper for calls to MapStart and MapEnd.\n  class ScopedMap {\n   public:\n    explicit ScopedMap(IOSIntermediateDumpWriter* writer,\n                       IntermediateDumpKey key)\n        : writer_(writer) {\n      writer->MapStart(key);\n    }\n\n    ScopedMap(const ScopedMap&) = delete;\n    ScopedMap& operator=(const ScopedMap&) = delete;\n\n    ~ScopedMap() { writer_->MapEnd(); }\n\n   private:\n    IOSIntermediateDumpWriter* writer_;\n  };\n\n  //! \\brief A scoped wrapper for calls to ArrayMapStart and MapEnd.\n  class ScopedArrayMap {\n   public:\n    explicit ScopedArrayMap(IOSIntermediateDumpWriter* writer)\n        : writer_(writer) {\n      writer->ArrayMapStart();\n    }\n\n    ScopedArrayMap(const ScopedArrayMap&) = delete;\n    ScopedArrayMap& operator=(const ScopedArrayMap&) = delete;\n\n    ~ScopedArrayMap() { writer_->MapEnd(); }\n\n   private:\n    IOSIntermediateDumpWriter* writer_;\n  };\n\n  //! \\brief A scoped wrapper for calls to ArrayStart and ArrayEnd.\n  class ScopedArray {\n   public:\n    explicit ScopedArray(IOSIntermediateDumpWriter* writer,\n                         IntermediateDumpKey key)\n        : writer_(writer) {\n      writer->ArrayStart(key);\n    }\n\n    ScopedArray(const ScopedArray&) = delete;\n    ScopedArray& operator=(const ScopedArray&) = delete;\n\n    ~ScopedArray() { writer_->ArrayEnd(); }\n\n   private:\n    IOSIntermediateDumpWriter* writer_;\n  };\n\n  //! \\return `true` if able to add the property with the \\a key \\a value\n  //!     \\a count tuple.\n  template <typename T>\n  bool AddProperty(IntermediateDumpKey key, const T* value, size_t count = 1) {\n    return AddPropertyBytes(key, value, count * sizeof(T));\n  }\n\n  //! \\brief Adds a property using `vm_read` or `vm_read_overwrite`.\n  //!\n  //! \\return `true` if able to read `value` `value_length` and write a\n  //!     kProperty command with the `key` `value` `value_length` tuple.\n  //!\n  //! \\note This method automatically chooses between `vm_read_overwrite` with\n  //!     a local stack buffer for small reads (<= 512 bytes) and `vm_read`\n  //!     for larger reads to avoid unnecessary virtual memory allocations.\n  bool AddPropertyBytes(IntermediateDumpKey key,\n                        const void* value,\n                        size_t value_length);\n\n  //! \\return `true` if able to vm_read a string of \\a value  and write a\n  //!     kProperty command with the \\a key \\a value up to a NUL byte.\n  //!     The string cannot be longer than \\a max_length with a maximum string\n  //!     length of 1024.\n  bool AddPropertyCString(IntermediateDumpKey key,\n                          size_t max_length,\n                          const char* value);\n\n private:\n  //! \\return `true` if able to vm_read_overwrite \\a value into\n  //!     \\a buffer while only reading one page at a time up to a NUL byte.\n  //!     Sets the final length of \\a buffer to \\a string_length.\n  //!     Returns `false` if unable to vm_read \\a value or when no NUL byte can\n  //!     be found within /a max_length (unterminated).\n  bool ReadCStringInternal(const char* value,\n                           char* buffer,\n                           size_t max_length,\n                           size_t* string_length);\n\n  //! \\return `true` if able to write a kArrayStart command  with the \\a key.\n  bool ArrayStart(IntermediateDumpKey key);\n\n  //! \\return `true` if able to write a kMapStart command with the \\a key.\n  bool MapStart(IntermediateDumpKey key);\n\n  //! \\return `true` if able to write a kMapStart command.\n  bool ArrayMapStart();\n\n  //! \\return `true` if able to write a kArrayEnd command.\n  bool ArrayEnd();\n\n  //! \\return `true` if able to write a kMapEnd command.\n  bool MapEnd();\n\n  //! \\return `true` if able to write a kRootMapStart command.\n  bool RootMapStart();\n\n  //! \\return `true` if able to write a kRootMapEnd command.\n  bool RootMapEnd();\n\n  //! \\return `true` if able to write a kProperty command with the \\a key\n  //!     \\a value \\a value_length tuple.\n  bool Property(IntermediateDumpKey key,\n                const void* value,\n                size_t value_length);\n\n  //! \\return `true` if able to write \\a data up to \\a size. The \\a data might\n  //!     not be written to fd_  until `buffer_` is full or the writer is\n  //!     closed. All writes will be 4096 bytes (the size of your kBufferSize)\n  //!     except for the final flush, which might be partial.\n  bool BufferedWrite(const void* data, size_t size);\n\n  //! \\return `true` if able to write `buffer_` up to `buffer_occupied_`.\n  bool FlushWriteBuffer();\n\n  //! \\brief The maximum size of the write buffer.\n  static constexpr size_t kBufferSize = 4096;\n\n  //! \\brief The write data buffer and amount of that buffer occupied with data\n  //!   to be written.\n  char buffer_[kBufferSize];\n  size_t buffer_occupied_;\n\n  int fd_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_IOS_INTERMEDIATE_DUMP_WRITER_H_\n"
  },
  {
    "path": "util/ios/ios_intermediate_dump_writer_test.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_intermediate_dump_writer.h\"\n\n#include <fcntl.h>\n#include <mach/mach.h>\n\n#include \"base/apple/scoped_mach_vm.h\"\n#include \"base/files/scoped_file.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing Key = internal::IntermediateDumpKey;\nusing internal::IOSIntermediateDumpWriter;\n\nclass IOSIntermediateDumpWriterTest : public testing::Test {\n protected:\n  // testing::Test:\n\n  void SetUp() override {\n    path_ = temp_dir_.path().Append(\"dump_file\");\n    writer_ = std::make_unique<IOSIntermediateDumpWriter>();\n  }\n\n  void TearDown() override {\n    EXPECT_TRUE(writer_->Close());\n    writer_.reset();\n    EXPECT_EQ(unlink(path_.value().c_str()), 0) << ErrnoMessage(\"unlink\");\n  }\n\n  const base::FilePath& path() const { return path_; }\n\n  std::unique_ptr<IOSIntermediateDumpWriter> writer_;\n\n private:\n  ScopedTempDir temp_dir_;\n  base::FilePath path_;\n};\n\nTEST_F(IOSIntermediateDumpWriterTest, Close) {\n  EXPECT_TRUE(writer_->Open(path()));\n  EXPECT_TRUE(writer_->Close());\n\n  std::string contents;\n  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));\n  ASSERT_EQ(contents, \"\");\n}\n\nTEST_F(IOSIntermediateDumpWriterTest, ScopedArray) {\n  EXPECT_TRUE(writer_->Open(path()));\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer_.get());\n    IOSIntermediateDumpWriter::ScopedArray threadArray(writer_.get(),\n                                                       Key::kThreads);\n    IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer_.get());\n  }\n  EXPECT_TRUE(writer_->Close());\n\n  std::string contents;\n  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));\n  std::string result(\"\\6\\x3p\\x17\\1\\2\\4\\a\", 8);\n  ASSERT_EQ(contents, result);\n}\n\nTEST_F(IOSIntermediateDumpWriterTest, ScopedMap) {\n  EXPECT_TRUE(writer_->Open(path()));\n  {\n    IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer_.get());\n    IOSIntermediateDumpWriter::ScopedMap map(writer_.get(),\n                                             Key::kMachException);\n  }\n  EXPECT_TRUE(writer_->Close());\n\n  std::string contents;\n  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));\n  std::string result(\"\\6\\1\\xe8\\3\\2\\a\", 6);\n  ASSERT_EQ(contents, result);\n}\n\nTEST_F(IOSIntermediateDumpWriterTest, Property) {\n  EXPECT_TRUE(writer_->Open(path()));\n  EXPECT_TRUE(writer_->AddProperty(Key::kVersion, \"version\", 7));\n  EXPECT_TRUE(writer_->Close());\n\n  std::string contents;\n  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));\n  std::string result(\"\\5\\1\\0\\a\\0\\0\\0\\0\\0\\0\\0version\", 18);\n  ASSERT_EQ(contents, result);\n}\n\nTEST_F(IOSIntermediateDumpWriterTest, PropertyString) {\n  EXPECT_TRUE(writer_->Open(path()));\n  EXPECT_TRUE(writer_->AddPropertyCString(Key::kVersion, 64, \"version\"));\n  EXPECT_TRUE(writer_->Close());\n\n  std::string contents;\n  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));\n  std::string result(\"\\5\\1\\0\\a\\0\\0\\0\\0\\0\\0\\0version\", 18);\n  ASSERT_EQ(contents, result);\n}\n\nTEST_F(IOSIntermediateDumpWriterTest, PropertyStringShort) {\n  EXPECT_TRUE(writer_->Open(path()));\n  EXPECT_FALSE(\n      writer_->AddPropertyCString(Key::kVersion, 7, \"versionnnnnnnnnnnn\"));\n}\n\nTEST_F(IOSIntermediateDumpWriterTest, PropertyStringLong) {\n  EXPECT_TRUE(writer_->Open(path()));\n\n  char* bad_string = nullptr;\n  EXPECT_FALSE(writer_->AddPropertyCString(Key::kVersion, 1025, bad_string));\n}\n\nTEST_F(IOSIntermediateDumpWriterTest, MissingPropertyString) {\n  char* region;\n  vm_size_t page_size = getpagesize();\n  vm_size_t region_size = page_size * 2;\n  ASSERT_EQ(vm_allocate(mach_task_self(),\n                        reinterpret_cast<vm_address_t*>(&region),\n                        region_size,\n                        VM_FLAGS_ANYWHERE),\n            0);\n  base::apple::ScopedMachVM vm_owner(reinterpret_cast<vm_address_t>(region),\n                                     region_size);\n\n  // Fill first page with 'A' and second with 'B'.\n  memset(region, 'A', page_size);\n  memset(region + page_size, 'B', page_size);\n\n  // Drop a NUL 10 bytes from the end of the first page and into the second\n  // page.\n  region[page_size - 10] = '\\0';\n  region[page_size + 10] = '\\0';\n\n  // Read a string that spans two pages.\n  EXPECT_TRUE(writer_->Open(path()));\n  EXPECT_TRUE(\n      writer_->AddPropertyCString(Key::kVersion, 64, region + page_size - 5));\n  EXPECT_TRUE(writer_->Close());\n  std::string contents;\n  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));\n  std::string result(\"\\x5\\x1\\0\\xF\\0\\0\\0\\0\\0\\0\\0AAAAABBBBBBBBBB\", 26);\n  ASSERT_EQ(contents, result);\n\n  // Dealloc second page.\n  ASSERT_EQ(vm_deallocate(mach_task_self(),\n                          reinterpret_cast<vm_address_t>(region + page_size),\n                          page_size),\n            0);\n\n  // Reading the same string should fail when the next page is dealloc-ed.\n  EXPECT_FALSE(\n      writer_->AddPropertyCString(Key::kVersion, 64, region + page_size - 5));\n\n  // Ensure we can read the first string without loading the second page.\n  EXPECT_TRUE(writer_->Open(path()));\n  EXPECT_TRUE(\n      writer_->AddPropertyCString(Key::kVersion, 64, region + page_size - 20));\n  EXPECT_TRUE(writer_->Close());\n  ASSERT_TRUE(LoggingReadEntireFile(path(), &contents));\n  result.assign(\"\\x5\\x1\\0\\n\\0\\0\\0\\0\\0\\0\\0AAAAAAAAAA\", 21);\n  ASSERT_EQ(contents, result);\n}\n\nTEST_F(IOSIntermediateDumpWriterTest, BadProperty) {\n  EXPECT_TRUE(writer_->Open(path()));\n  ASSERT_FALSE(writer_->AddProperty(Key::kVersion, \"version\", -1));\n  EXPECT_TRUE(writer_->Close());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/ios_system_data_collector.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_\n#define CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_\n\n#import <CoreFoundation/CoreFoundation.h>\n\n#include <functional>\n#include <string>\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Used to collect system level data before a crash occurs.\nclass IOSSystemDataCollector {\n public:\n  IOSSystemDataCollector();\n  ~IOSSystemDataCollector();\n\n  void OSVersion(int* major, int* minor, int* bugfix) const;\n  const std::string& MachineDescription() const { return machine_description_; }\n  int ProcessorCount() const { return processor_count_; }\n  const std::string& Build() const { return build_; }\n  const std::string& BundleIdentifier() const { return bundle_identifier_; }\n  bool IsExtension() const { return is_extension_; }\n  const std::string& CPUVendor() const { return cpu_vendor_; }\n  bool HasDaylightSavingTime() const { return has_next_daylight_saving_time_; }\n  bool IsDaylightSavingTime() const { return is_daylight_saving_time_; }\n  int StandardOffsetSeconds() const { return standard_offset_seconds_; }\n  int DaylightOffsetSeconds() const { return daylight_offset_seconds_; }\n  const std::string& StandardName() const { return standard_name_; }\n  const std::string& DaylightName() const { return daylight_name_; }\n  bool IsApplicationActive() const { return active_; }\n  uint64_t AddressMask() const { return address_mask_; }\n  uint64_t InitializationTime() const { return initialization_time_ns_; }\n\n  // Currently unused by minidump.\n  int Orientation() const { return orientation_; }\n\n  // A completion callback that takes a bool indicating that the application has\n  // become active or inactive.\n  using ActiveApplicationCallback = std::function<void(bool)>;\n\n  void SetActiveApplicationCallback(ActiveApplicationCallback callback) {\n    active_application_callback_ = callback;\n  }\n\n private:\n  // Notification handlers for time zone, orientation and active state.\n  void InstallHandlers();\n  void SystemTimeZoneDidChangeNotification();\n  void OrientationDidChangeNotification();\n  void ApplicationDidChangeActiveNotification();\n\n  int major_version_;\n  int minor_version_;\n  int patch_version_;\n  std::string build_;\n  std::string bundle_identifier_;\n  bool is_extension_;\n  std::string machine_description_;\n  int orientation_;\n  bool active_;\n  int processor_count_;\n  std::string cpu_vendor_;\n  bool has_next_daylight_saving_time_;\n  bool is_daylight_saving_time_;\n  int standard_offset_seconds_;\n  int daylight_offset_seconds_;\n  std::string standard_name_;\n  std::string daylight_name_;\n  ActiveApplicationCallback active_application_callback_;\n  uint64_t address_mask_;\n\n  // Time in nanoseconds as returned by ClockMonotonicNanoseconds() to store the\n  // crashpad start time. This clock increments monotonically but pauses while\n  // the system is asleep. It should not be compared to other system time\n  // sources.\n  uint64_t initialization_time_ns_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_IOS_SYSTEM_DATA_COLLECTOR_H_\n"
  },
  {
    "path": "util/ios/ios_system_data_collector.mm",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/ios_system_data_collector.h\"\n\n#include <sys/sysctl.h>\n#include <sys/utsname.h>\n\n#import <Foundation/Foundation.h>\n#include <TargetConditionals.h>\n#import <UIKit/UIKit.h>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/notreached.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/mac/sysctl.h\"\n#include \"util/misc/clock.h\"\n\nnamespace {\n\ntemplate <typename T, void (T::*M)(void)>\nvoid AddObserver(CFStringRef notification_name, T* observer) {\n  CFNotificationCenterAddObserver(\n      CFNotificationCenterGetLocalCenter(),\n      observer,\n      [](CFNotificationCenterRef center,\n         void* observer_vp,\n         CFNotificationName name,\n         const void* object,\n         CFDictionaryRef userInfo) {\n        T* observer = reinterpret_cast<T*>(observer_vp);\n        (observer->*M)();\n      },\n      notification_name,\n      nullptr,\n      CFNotificationSuspensionBehaviorDeliverImmediately);\n}\n\n}  // namespace\n\nnamespace crashpad {\nnamespace internal {\n\nIOSSystemDataCollector::IOSSystemDataCollector()\n    : major_version_(0),\n      minor_version_(0),\n      patch_version_(0),\n      build_(),\n      machine_description_(),\n      orientation_(0),\n      processor_count_(0),\n      cpu_vendor_(),\n      has_next_daylight_saving_time_(false),\n      is_daylight_saving_time_(false),\n      standard_offset_seconds_(0),\n      daylight_offset_seconds_(0),\n      standard_name_(),\n      daylight_name_(),\n      initialization_time_ns_(ClockMonotonicNanoseconds()) {\n  NSOperatingSystemVersion version =\n      [[NSProcessInfo processInfo] operatingSystemVersion];\n  major_version_ = base::saturated_cast<int>(version.majorVersion);\n  minor_version_ = base::saturated_cast<int>(version.minorVersion);\n  patch_version_ = base::saturated_cast<int>(version.patchVersion);\n  processor_count_ =\n      base::saturated_cast<int>([[NSProcessInfo processInfo] processorCount]);\n  build_ = ReadStringSysctlByName(\"kern.osversion\", false);\n  bundle_identifier_ =\n      base::SysNSStringToUTF8([[NSBundle mainBundle] bundleIdentifier]);\n// If CRASHPAD_IS_IOS_APP_EXTENSION is defined, then the code is compiled with\n// -fapplication-extension and can only be used in an app extension. Otherwise\n// check at runtime whether the code is executing in an app extension or not.\n#if defined(CRASHPAD_IS_IOS_APP_EXTENSION)\n  is_extension_ = true;\n#else\n  is_extension_ = [[NSBundle mainBundle].bundlePath hasSuffix:@\"appex\"];\n#endif\n\n#if defined(ARCH_CPU_X86_64)\n  cpu_vendor_ = ReadStringSysctlByName(\"machdep.cpu.vendor\", false);\n#endif\n  uint32_t addressable_bits = 0;\n  size_t len = sizeof(uint32_t);\n  // `machdep.virtual_address_size` is the number of addressable bits in\n  // userspace virtual addresses\n  if (sysctlbyname(\n          \"machdep.virtual_address_size\", &addressable_bits, &len, NULL, 0) !=\n      0) {\n    addressable_bits = 0;\n  }\n  address_mask_ = ~((1UL << addressable_bits) - 1);\n\n#if TARGET_OS_SIMULATOR\n  // TODO(justincohen): Consider adding board and model information to\n  // |machine_description| as well (similar to MacModelAndBoard in\n  // util/mac/mac_util.cc).\n  const char* model = getenv(\"SIMULATOR_MODEL_IDENTIFIER\");\n  if (model == nullptr) {\n    switch ([[UIDevice currentDevice] userInterfaceIdiom]) {\n      case UIUserInterfaceIdiomPhone:\n        model = \"iPhone\";\n        break;\n      case UIUserInterfaceIdiomPad:\n        model = \"iPad\";\n        break;\n      default:\n        model = \"Unknown\";\n        break;\n    }\n  }\n  machine_description_ = base::StringPrintf(\"iOS Simulator (%s)\", model);\n#elif TARGET_OS_IPHONE\n  utsname uts;\n  if (uname(&uts) == 0) {\n    machine_description_ = uts.machine;\n  }\n#else\n#error \"Unexpected target type OS.\"\n#endif\n\n  InstallHandlers();\n}\n\nIOSSystemDataCollector::~IOSSystemDataCollector() {\n  CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetLocalCenter(),\n                                          this);\n}\n\nvoid IOSSystemDataCollector::OSVersion(int* major,\n                                       int* minor,\n                                       int* bugfix) const {\n  *major = major_version_;\n  *minor = minor_version_;\n  *bugfix = patch_version_;\n}\n\nvoid IOSSystemDataCollector::InstallHandlers() {\n  // Timezone.\n  AddObserver<IOSSystemDataCollector,\n              &IOSSystemDataCollector::SystemTimeZoneDidChangeNotification>(\n      (__bridge CFStringRef)NSSystemTimeZoneDidChangeNotification, this);\n  SystemTimeZoneDidChangeNotification();\n\n#if !BUILDFLAG(IS_IOS_TVOS)\n  // Orientation.\n  AddObserver<IOSSystemDataCollector,\n              &IOSSystemDataCollector::OrientationDidChangeNotification>(\n      (__bridge CFStringRef)UIDeviceOrientationDidChangeNotification, this);\n  OrientationDidChangeNotification();\n#endif\n\n#if !defined(CRASHPAD_IS_IOS_APP_EXTENSION)\n  // Foreground/Background. Extensions shouldn't use UIApplication*.\n  if (!is_extension_) {\n    AddObserver<\n        IOSSystemDataCollector,\n        &IOSSystemDataCollector::ApplicationDidChangeActiveNotification>(\n        (__bridge CFStringRef)UIApplicationDidBecomeActiveNotification, this);\n    AddObserver<\n        IOSSystemDataCollector,\n        &IOSSystemDataCollector::ApplicationDidChangeActiveNotification>(\n        (__bridge CFStringRef)UIApplicationDidEnterBackgroundNotification,\n        this);\n    ApplicationDidChangeActiveNotification();\n  }\n#endif\n}\n\nvoid IOSSystemDataCollector::SystemTimeZoneDidChangeNotification() {\n  NSTimeZone* time_zone = NSTimeZone.localTimeZone;\n  NSDate* transition =\n      [time_zone nextDaylightSavingTimeTransitionAfterDate:[NSDate date]];\n  if (transition == nil) {\n    has_next_daylight_saving_time_ = false;\n    is_daylight_saving_time_ = false;\n    standard_offset_seconds_ =\n        base::saturated_cast<int>([time_zone secondsFromGMTForDate:transition]);\n    standard_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);\n    daylight_offset_seconds_ = standard_offset_seconds_;\n    daylight_name_ = standard_name_;\n  } else {\n    has_next_daylight_saving_time_ = true;\n    is_daylight_saving_time_ = time_zone.isDaylightSavingTime;\n    if (time_zone.isDaylightSavingTime) {\n      standard_offset_seconds_ = base::saturated_cast<int>(\n          [time_zone secondsFromGMTForDate:transition]);\n      standard_name_ =\n          base::SysNSStringToUTF8([time_zone abbreviationForDate:transition]);\n      daylight_offset_seconds_ =\n          base::saturated_cast<int>([time_zone secondsFromGMT]);\n      daylight_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);\n    } else {\n      standard_offset_seconds_ =\n          base::saturated_cast<int>([time_zone secondsFromGMT]);\n      standard_name_ = base::SysNSStringToUTF8([time_zone abbreviation]);\n      daylight_offset_seconds_ = base::saturated_cast<int>(\n          [time_zone secondsFromGMTForDate:transition]);\n      daylight_name_ =\n          base::SysNSStringToUTF8([time_zone abbreviationForDate:transition]);\n    }\n  }\n}\n\nvoid IOSSystemDataCollector::OrientationDidChangeNotification() {\n#if !BUILDFLAG(IS_IOS_TVOS)\n  orientation_ =\n      base::saturated_cast<int>([[UIDevice currentDevice] orientation]);\n#endif\n}\n\nvoid IOSSystemDataCollector::ApplicationDidChangeActiveNotification() {\n#if defined(CRASHPAD_IS_IOS_APP_EXTENSION)\n  NOTREACHED();\n#else\n  dispatch_assert_queue_debug(dispatch_get_main_queue());\n  bool old_active = active_;\n  active_ = [UIApplication sharedApplication].applicationState ==\n            UIApplicationStateActive;\n  if (active_ != old_active && active_application_callback_) {\n    active_application_callback_(active_);\n  }\n#endif\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/raw_logging.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/raw_logging.h\"\n\n#include <atomic>\n\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"base/posix/eintr_wrapper.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nnamespace {\nstatic_assert(std::atomic<FileHandle>::is_always_lock_free,\n              \"std::atomic<FileHandle> may not be signal-safe\");\nstd::atomic<FileHandle> g_file_handle = STDERR_FILENO;\n}  // namespace\n\nvoid SetFileHandleForTesting(FileHandle file_handle) {\n  g_file_handle = file_handle;\n}\n\nvoid RawLogString(const char* message) {\n  const size_t message_len = strlen(message);\n  size_t bytes_written = 0;\n  while (bytes_written < message_len) {\n    int rv = HANDLE_EINTR(write(\n        g_file_handle, message + bytes_written, message_len - bytes_written));\n    if (rv < 0) {\n      // Give up, nothing we can do now.\n      break;\n    }\n    bytes_written += rv;\n  }\n}\n\nvoid RawLogInt(unsigned int number) {\n  char buffer[20];\n  char* digit = &buffer[sizeof(buffer) - 1];\n  *digit = '\\0';\n  do {\n    *(--digit) = (number % 10) + '0';\n    number /= 10;\n  } while (number != 0);\n  RawLogString(digit);\n}\n\n// Prints `path:linenum message:error` (with optional `:error`).\nvoid RawLog(const char* file, int line, const char* message, int error) {\n  RawLogString(file);\n  HANDLE_EINTR(write(g_file_handle, \":\", 1));\n  RawLogInt(line);\n  HANDLE_EINTR(write(g_file_handle, \" \", 1));\n  RawLogString(message);\n  if (error) {\n    RawLogString(\": \");\n    RawLogInt(error);\n  }\n  HANDLE_EINTR(write(g_file_handle, \"\\n\", 1));\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/raw_logging.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_\n#define CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_\n\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Log \\a message to stderr in a way that is safe to run during an\n//!     in-process crash.  Also prints the given file, line number and an\n//!     optional error code.\n//!\n//! Note: RUNS-DURING-CRASH.\nvoid RawLog(const char* file, int line, const char* message, int error);\n\n//! \\brief Direct RawLog to log to \\a file_handle instead of stderr, so tests\n//!     can confirm certain error conditions during in-process crashes. Call\n//!     before before any Crashpad is run.\nvoid SetFileHandleForTesting(FileHandle file_handle);\n\n}  // namespace internal\n}  // namespace crashpad\n\n#define CRASHPAD_RAW_LOG(message) \\\n  ::crashpad::internal::RawLog(__FILE__, __LINE__, message, 0)\n\n#define CRASHPAD_RAW_LOG_ERROR(error, message) \\\n  ::crashpad::internal::RawLog(__FILE__, __LINE__, message, error)\n\n#endif  // CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_\n"
  },
  {
    "path": "util/ios/scoped_background_task.h",
    "content": "// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_SCOPED_BACKGROUND_TASK_H_\n#define CRASHPAD_UTIL_IOS_SCOPED_BACKGROUND_TASK_H_\n\n#include <dispatch/dispatch.h>\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Marks the start of a task that should continue if the application\n//!     enters the background.\nclass ScopedBackgroundTask {\n public:\n  //! \\param[in] task_name A string used in debugging to indicate the reason the\n  //!     activity began. This parameter must not be nullptr or an empty string.\n  ScopedBackgroundTask(const char* task_name);\n\n  ScopedBackgroundTask(const ScopedBackgroundTask&) = delete;\n  ScopedBackgroundTask& operator=(const ScopedBackgroundTask&) = delete;\n  ~ScopedBackgroundTask();\n\n private:\n  dispatch_semaphore_t task_complete_semaphore_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_SCOPED_BACKGROUND_TASK_H_\n"
  },
  {
    "path": "util/ios/scoped_background_task.mm",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/scoped_background_task.h\"\n\n#import <Foundation/Foundation.h>\n\nnamespace crashpad {\nnamespace internal {\n\nScopedBackgroundTask::ScopedBackgroundTask(const char* task_name)\n    : task_complete_semaphore_(dispatch_semaphore_create(0)) {\n  __weak dispatch_semaphore_t weak_task_complete_semaphore =\n      task_complete_semaphore_;\n  NSString* name = [NSString stringWithUTF8String:task_name];\n  [[NSProcessInfo processInfo]\n      performExpiringActivityWithReason:name\n                             usingBlock:^(BOOL expired) {\n                               if (expired) {\n                                 // TODO(crbug.com/crashpad/400): Notify the\n                                 // BG task creator that time's up -- this will\n                                 // be useful for BackgroundTasks and\n                                 // NSURLSessions.\n                                 return;\n                               }\n                               __strong dispatch_semaphore_t\n                                   strong_task_complete_semaphore =\n                                       weak_task_complete_semaphore;\n                               if (!strong_task_complete_semaphore) {\n                                 return;\n                               }\n                               dispatch_semaphore_wait(\n                                   strong_task_complete_semaphore,\n                                   DISPATCH_TIME_FOREVER);\n                             }];\n}\n\nScopedBackgroundTask::~ScopedBackgroundTask() {\n  dispatch_semaphore_signal(task_complete_semaphore_);\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/scoped_vm_map.cc",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/scoped_vm_map.h\"\n\n#include \"util/ios/raw_logging.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nScopedVMMapInternal::ScopedVMMapInternal()\n    : data_(0),\n      region_start_(0),\n      region_size_(0),\n      cur_protection_(VM_PROT_NONE) {}\n\nScopedVMMapInternal::~ScopedVMMapInternal() {\n  Reset();\n}\n\nbool ScopedVMMapInternal::Map(const void* data, const size_t data_length) {\n  Reset();\n\n  vm_address_t data_address = reinterpret_cast<vm_address_t>(data);\n  vm_address_t page_region_address = trunc_page(data_address);\n  region_size_ = round_page(data_address - page_region_address + data_length);\n  if (region_size_ < data_length) {\n    CRASHPAD_RAW_LOG(\"ScopedVMMap data_length overflow\");\n    return false;\n  }\n\n  // Since region_start_ is 0, vm_remap() will choose an address to which the\n  // memory will be mapped and store the mapped address in region_start_ on\n  // success.\n  vm_prot_t max_protection;\n  kern_return_t kr = vm_remap(mach_task_self(),\n                              &region_start_,\n                              region_size_,\n                              0,\n                              TRUE,\n                              mach_task_self(),\n                              page_region_address,\n                              FALSE,\n                              &cur_protection_,\n                              &max_protection,\n                              VM_INHERIT_DEFAULT);\n  if (kr != KERN_SUCCESS) {\n    // It's expected that this will sometimes fail. Don't log here.\n    return false;\n  }\n\n  data_ = region_start_ + (data_address - page_region_address);\n  return true;\n}\n\nvoid ScopedVMMapInternal::Reset() {\n  if (!region_start_) {\n    return;\n  }\n\n  kern_return_t kr =\n      vm_deallocate(mach_task_self(), region_start_, region_size_);\n\n  if (kr != KERN_SUCCESS) {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"vm_deallocate\");\n  }\n\n  data_ = 0;\n  region_start_ = 0;\n  region_size_ = 0;\n  cur_protection_ = VM_PROT_NONE;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/scoped_vm_map.h",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_SCOPED_VM_MAP_H_\n#define CRASHPAD_UTIL_IOS_SCOPED_VM_MAP_H_\n\n#include <mach/mach.h>\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Non-templated internal class to be used by ScopedVMMap.\n//!\n//! Note: RUNS-DURING-CRASH.\nclass ScopedVMMapInternal {\n public:\n  ScopedVMMapInternal();\n\n  ScopedVMMapInternal(const ScopedVMMapInternal&) = delete;\n  ScopedVMMapInternal& operator=(const ScopedVMMapInternal&) = delete;\n\n  ~ScopedVMMapInternal();\n\n  //! \\brief Releases any previously mapped data and vm_remaps \\a data. Logs an\n  //!     error on failure.\n  //!\n  //! \\param[in] data Memory to be mapped by vm_remap.\n  //! \\param[in] data_length Length of \\a data.\n  //!\n  //! \\return `true` if all the data was mapped. Logs an error and returns false\n  //!   on failure.\n  bool Map(const void* data, size_t data_length);\n\n  //! \\brief Returns the current protection for the memory in the region.\n  vm_prot_t CurrentProtection() const { return cur_protection_; }\n\n  vm_address_t data() const { return data_; }\n\n private:\n  //! \\brief Deallocates any resources allocated by this object and resets it\n  //!     to its original state.\n  void Reset();\n\n  // The address within region_start_ at which the mapped data is available.\n  vm_address_t data_;\n\n  // The region returned by vm_remap().\n  vm_address_t region_start_;\n\n  // The size of the region returned by vm_remap().\n  vm_size_t region_size_;\n\n  // The current protection for the memory region.\n  vm_prot_t cur_protection_;\n};\n\n//! \\brief A scoped wrapper for calls to `vm_remap` and `vm_deallocate`.  Allows\n//!     in-process handler to safely read and write memory (modulo its\n//!     protection level) for the intermediate dump.\n//!\n//! Note: RUNS-DURING-CRASH.\ntemplate <typename T>\nclass ScopedVMMap {\n public:\n  ScopedVMMap() : internal_() {}\n\n  ScopedVMMap(const ScopedVMMap&) = delete;\n  ScopedVMMap& operator=(const ScopedVMMap&) = delete;\n\n  ~ScopedVMMap() {}\n\n  //! \\brief Releases any previously mapped data and vm_remaps data.\n  //!\n  //! \\param[in] data Memory to be mapped by vm_remap.\n  //! \\param[in] count Length of \\a data.\n  //!\n  //! \\return `true` if all \\a data was mapped. Returns false on failure.\n  bool Map(const void* data, size_t count = 1) {\n    size_t data_length = count * sizeof(T);\n    return internal_.Map(data, data_length);\n  }\n\n  //! \\brief Releases any previously mapped data and vm_remaps address.\n  //!\n  //! Before reading or writing the memory, check `CurrentProtection()` to\n  //! ensure the data is readable or writable.\n  //!\n  //! \\param[in] address Address of memory to be mapped by vm_remap.\n  //! \\param[in] count Length of \\a data.\n  //!\n  //! \\return `true` if all of \\a address was mapped. Returns false on failure.\n  bool Map(vm_address_t address, size_t count = 1) {\n    return Map(reinterpret_cast<T*>(address), count);\n  }\n\n  //! \\brief Returns the pointer to memory safe to read and write (respecting\n  //!   the CurrentProtection() level) during the in-process crash handler.\n  T* operator->() const { return get(); }\n\n  //! \\brief Returns the pointer to memory safe to read and write (respecting\n  //!   the CurrentProtection() level) during the in-process crash handler.\n  T* get() const { return reinterpret_cast<T*>(internal_.data()); }\n\n  //! \\brief Returns the current protection level of the mapped memory.\n  vm_prot_t CurrentProtection() const { return internal_.CurrentProtection(); }\n\n private:\n  ScopedVMMapInternal internal_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_SCOPED_VM_MAP_H_\n"
  },
  {
    "path": "util/ios/scoped_vm_map_test.cc",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/scoped_vm_map.h\"\n\n#include <sys/time.h>\n\n#include \"base/apple/scoped_mach_vm.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ScopedVMMapTest, BasicFunctionality) {\n  // bad data or count.\n  internal::ScopedVMMap<vm_address_t> vmmap_bad;\n  EXPECT_FALSE(vmmap_bad.Map(nullptr, 100));\n  EXPECT_FALSE(vmmap_bad.Map(reinterpret_cast<void*>(0x1000), 100));\n  vm_address_t invalid_address = 1;\n  EXPECT_FALSE(vmmap_bad.Map(&invalid_address, 1000000000));\n  EXPECT_FALSE(vmmap_bad.Map(&invalid_address, -1));\n\n  vm_address_t valid_address = reinterpret_cast<vm_address_t>(this);\n  EXPECT_FALSE(vmmap_bad.Map(&valid_address, 1000000000));\n  EXPECT_FALSE(vmmap_bad.Map(&valid_address, -1));\n\n  // array\n  static constexpr char map_me[] = \"map me\";\n  internal::ScopedVMMap<char> vmmap_string;\n  ASSERT_TRUE(vmmap_string.Map(map_me, strlen(map_me)));\n  EXPECT_STREQ(vmmap_string.get(), map_me);\n  EXPECT_TRUE(vmmap_string.CurrentProtection() & VM_PROT_READ);\n\n  // struct\n  timeval time_of_day;\n  EXPECT_TRUE(gettimeofday(&time_of_day, nullptr) == 0);\n  internal::ScopedVMMap<timeval> vmmap_time;\n  ASSERT_TRUE(vmmap_time.Map(&time_of_day));\n  constexpr vm_prot_t kReadWrite = VM_PROT_READ | VM_PROT_WRITE;\n  EXPECT_EQ(vmmap_time.CurrentProtection() & kReadWrite, kReadWrite);\n  EXPECT_EQ(vmmap_time->tv_sec, time_of_day.tv_sec);\n  EXPECT_EQ(vmmap_time->tv_usec, time_of_day.tv_usec);\n\n  // reset.\n  timeval time_of_day2;\n  EXPECT_TRUE(gettimeofday(&time_of_day2, nullptr) == 0);\n  ASSERT_TRUE(vmmap_time.Map(&time_of_day2));\n  EXPECT_EQ(vmmap_time.CurrentProtection() & kReadWrite, kReadWrite);\n  EXPECT_EQ(vmmap_time->tv_sec, time_of_day2.tv_sec);\n  EXPECT_EQ(vmmap_time->tv_usec, time_of_day2.tv_usec);\n}\n\nTEST(ScopedVMMapTest, MissingMiddleVM) {\n  char* region;\n  vm_size_t page_size = getpagesize();\n  vm_size_t region_size = page_size * 3;\n  kern_return_t kr = vm_allocate(mach_task_self(),\n                                 reinterpret_cast<vm_address_t*>(&region),\n                                 region_size,\n                                 VM_FLAGS_ANYWHERE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_allocate\");\n\n  base::apple::ScopedMachVM vm_owner(reinterpret_cast<vm_address_t>(region),\n                                     region_size);\n\n  internal::ScopedVMMap<char> vmmap_missing_middle;\n  ASSERT_TRUE(vmmap_missing_middle.Map(region, region_size));\n\n  // Dealloc middle page.\n  kr = vm_deallocate(mach_task_self(),\n                     reinterpret_cast<vm_address_t>(region + page_size),\n                     page_size);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_deallocate\");\n\n  EXPECT_FALSE(vmmap_missing_middle.Map(region, region_size));\n  ASSERT_TRUE(vmmap_missing_middle.Map(region, page_size));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/scoped_vm_read.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/scoped_vm_read.h\"\n\n#include \"util/ios/raw_logging.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nScopedVMReadInternal::ScopedVMReadInternal()\n    : data_(0), region_start_(0), region_size_(0) {}\n\nScopedVMReadInternal::~ScopedVMReadInternal() {\n  Reset();\n}\n\nbool ScopedVMReadInternal::Read(const void* data, const size_t data_length) {\n  Reset();\n\n  vm_address_t data_address = reinterpret_cast<vm_address_t>(data);\n  vm_address_t page_region_address = trunc_page(data_address);\n  vm_size_t page_region_size =\n      round_page(data_address - page_region_address + data_length);\n  if (page_region_size < data_length) {\n    CRASHPAD_RAW_LOG(\"ScopedVMRead data_length overflow\");\n    return false;\n  }\n  kern_return_t kr = vm_read(mach_task_self(),\n                             page_region_address,\n                             page_region_size,\n                             &region_start_,\n                             &region_size_);\n\n  if (kr != KERN_SUCCESS) {\n    // It's expected that this will sometimes fail. Don't log here.\n    return false;\n  }\n\n  data_ = region_start_ + (data_address - page_region_address);\n  return true;\n}\n\nvoid ScopedVMReadInternal::Reset() {\n  if (!region_start_) {\n    return;\n  }\n  kern_return_t kr =\n      vm_deallocate(mach_task_self(), region_start_, region_size_);\n  if (kr != KERN_SUCCESS) {\n    CRASHPAD_RAW_LOG_ERROR(kr, \"vm_deallocate\");\n  }\n  region_start_ = 0;\n  region_size_ = 0;\n  data_ = 0;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/ios/scoped_vm_read.h",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_\n#define CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_\n\n#include <mach/mach.h>\n\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Non-templated internal class to be used by ScopedVMRead.\n//!\n//! Note: RUNS-DURING-CRASH.\nclass ScopedVMReadInternal {\n public:\n  ScopedVMReadInternal();\n\n  ScopedVMReadInternal(const ScopedVMReadInternal&) = delete;\n  ScopedVMReadInternal& operator=(const ScopedVMReadInternal&) = delete;\n\n  ~ScopedVMReadInternal();\n\n  //! \\brief Releases any previously read data and vm_reads \\a data. Logs an\n  //!     error on failure.\n  //!\n  //! \\param[in] data Memory to be read by vm_read.\n  //! \\param[in] data_length Length of \\a data.\n  //!\n  //! \\return `true` if all the data was read. Logs an error and returns false\n  //!   on failure\n  bool Read(const void* data, size_t data_length);\n\n  vm_address_t data() const { return data_; }\n\n private:\n  //! \\brief Deallocates any resources allocated by this object and resets it\n  //!     to its original state.\n  void Reset();\n\n  // The address within region_start_ at which the the data is available.\n  vm_address_t data_;\n\n  // The region returned by vm_read().\n  vm_address_t region_start_;\n\n  // The size of the region returned by vm_read().\n  mach_msg_type_number_t region_size_;\n};\n\n//! \\brief A scoped wrapper for calls to `vm_read` and `vm_deallocate`.  Allows\n//!     in-process handler to safely read memory for the intermediate dump.\n//!\n//! Note: RUNS-DURING-CRASH.\ntemplate <typename T>\nclass ScopedVMRead {\n public:\n  ScopedVMRead() : internal_() {}\n\n  ScopedVMRead(const ScopedVMRead&) = delete;\n  ScopedVMRead& operator=(const ScopedVMRead&) = delete;\n\n  ~ScopedVMRead() {}\n\n  //! \\brief Releases any previously read data and vm_reads data.\n  //!\n  //! \\param[in] data Memory to be read by vm_read.\n  //! \\param[in] count Length of \\a data.\n  //!\n  //! \\return `true` if all \\a data was read. Returns false on failure.\n  bool Read(const void* data, size_t count = 1) {\n    size_t data_length = count * sizeof(T);\n    return internal_.Read(data, data_length);\n  }\n\n  //! \\brief Releases any previously read data and vm_reads address.\n  //!\n  //! \\param[in] address Address of memory to be read by vm_read.\n  //! \\param[in] count Length of \\a data.\n  //!\n  //! \\return `true` if all of \\a address was read. Returns false on failure.\n  bool Read(vm_address_t address, size_t count = 1) {\n    return Read(reinterpret_cast<T*>(address), count);\n  }\n\n  //! \\brief Returns the pointer to memory safe to read during the in-process\n  //!   crash handler.\n  T* operator->() const { return get(); }\n\n  //! \\brief Returns the pointer to memory safe to read during the in-process\n  //!   crash handler.\n  T* get() const { return reinterpret_cast<T*>(internal_.data()); }\n\n private:\n  ScopedVMReadInternal internal_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_\n"
  },
  {
    "path": "util/ios/scoped_vm_read_test.cc",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/ios/scoped_vm_read.h\"\n\n#include <sys/time.h>\n\n#include \"base/apple/scoped_mach_vm.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ScopedVMReadTest, BasicFunctionality) {\n  // bad data or count.\n  internal::ScopedVMRead<vm_address_t> vmread_bad;\n  EXPECT_FALSE(vmread_bad.Read(nullptr, 100));\n  EXPECT_FALSE(vmread_bad.Read(reinterpret_cast<void*>(0x1000), 100));\n  vm_address_t invalid_address = 1;\n  EXPECT_FALSE(vmread_bad.Read(&invalid_address, 1000000000));\n  EXPECT_FALSE(vmread_bad.Read(&invalid_address, -1));\n\n  vm_address_t valid_address = reinterpret_cast<vm_address_t>(this);\n  EXPECT_FALSE(vmread_bad.Read(&valid_address, 1000000000));\n  EXPECT_FALSE(vmread_bad.Read(&valid_address, -1));\n  // array\n  constexpr char read_me[] = \"read me\";\n  internal::ScopedVMRead<char> vmread_string;\n  ASSERT_TRUE(vmread_string.Read(read_me, strlen(read_me)));\n  EXPECT_STREQ(vmread_string.get(), read_me);\n\n  // struct\n  timeval time_of_day;\n  EXPECT_TRUE(gettimeofday(&time_of_day, nullptr) == 0);\n  internal::ScopedVMRead<timeval> vmread_time;\n  ASSERT_TRUE(vmread_time.Read(&time_of_day));\n  EXPECT_EQ(vmread_time->tv_sec, time_of_day.tv_sec);\n  EXPECT_EQ(vmread_time->tv_usec, time_of_day.tv_usec);\n\n  // reset.\n  timeval time_of_day2;\n  EXPECT_TRUE(gettimeofday(&time_of_day2, nullptr) == 0);\n  ASSERT_TRUE(vmread_time.Read(&time_of_day2));\n  EXPECT_EQ(vmread_time->tv_sec, time_of_day2.tv_sec);\n  EXPECT_EQ(vmread_time->tv_usec, time_of_day2.tv_usec);\n}\n\nTEST(ScopedVMReadTest, MissingMiddleVM) {\n  char* region;\n  vm_size_t page_size = getpagesize();\n  vm_size_t region_size = page_size * 3;\n  kern_return_t kr = vm_allocate(mach_task_self(),\n                                 reinterpret_cast<vm_address_t*>(&region),\n                                 region_size,\n                                 VM_FLAGS_ANYWHERE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_allocate\");\n\n  base::apple::ScopedMachVM vm_owner(reinterpret_cast<vm_address_t>(region),\n                                     region_size);\n\n  internal::ScopedVMRead<char> vmread_missing_middle;\n  ASSERT_TRUE(vmread_missing_middle.Read(region, region_size));\n\n  // Dealloc middle page.\n  kr = vm_deallocate(mach_task_self(),\n                     reinterpret_cast<vm_address_t>(region + page_size),\n                     page_size);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_deallocate\");\n\n  EXPECT_FALSE(vmread_missing_middle.Read(region, region_size));\n  ASSERT_TRUE(vmread_missing_middle.Read(region, page_size));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/address_types.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_ADDRESS_TYPES_H_\n#define CRASHPAD_UTIL_LINUX_ADDRESS_TYPES_H_\n\n#include <stdint.h>\n\nnamespace crashpad {\n\n//! \\brief Type used to represent an address in a process, potentially across\n//!     bitness.\nusing LinuxVMAddress = uint64_t;\n\n//! \\brief Type used to represent the size of a memory range (with a\n//!     LinuxVMAddress), potentially across bitness.\nusing LinuxVMSize = uint64_t;\n\n//! \\brief Type used to represent an offset from a LinuxVMAddress, potentially\n//!     across bitness.\nusing LinuxVMOffset = int64_t;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_ADDRESS_TYPES_H_\n"
  },
  {
    "path": "util/linux/auxiliary_vector.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/auxiliary_vector.h\"\n\n#include <linux/auxvec.h>\n#include <stdio.h>\n\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/string_file.h\"\n#include \"util/stdlib/map_insert.h\"\n\nnamespace crashpad {\n\nAuxiliaryVector::AuxiliaryVector() : values_() {}\n\nAuxiliaryVector::~AuxiliaryVector() {}\n\nbool AuxiliaryVector::Initialize(PtraceConnection* connection) {\n  return connection->Is64Bit() ? Read<uint64_t>(connection)\n                               : Read<uint32_t>(connection);\n}\n\ntemplate <typename ULong>\nbool AuxiliaryVector::Read(PtraceConnection* connection) {\n  char path[32];\n  snprintf(path, sizeof(path), \"/proc/%d/auxv\", connection->GetProcessID());\n\n  std::string contents;\n  if (!connection->ReadFileContents(base::FilePath(path), &contents)) {\n    return false;\n  }\n  StringFile aux_file;\n  aux_file.SetString(contents);\n\n  ULong type;\n  ULong value;\n  while (aux_file.ReadExactly(&type, sizeof(type)) &&\n         aux_file.ReadExactly(&value, sizeof(value))) {\n    if (type == AT_NULL && value == 0) {\n      return true;\n    }\n    if (type == AT_IGNORE) {\n      continue;\n    }\n    if (!MapInsertOrReplace(&values_, type, value, nullptr)) {\n      LOG(ERROR) << \"duplicate auxv entry\";\n      return false;\n    }\n  }\n  return false;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/auxiliary_vector.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_\n#define CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_\n\n#include <sys/types.h>\n\n#include <map>\n\n#include \"base/logging.h\"\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/reinterpret_bytes.h\"\n\nnamespace crashpad {\n\n//! \\brief Read the auxiliary vector for a target process.\nclass AuxiliaryVector {\n public:\n  AuxiliaryVector();\n\n  AuxiliaryVector(const AuxiliaryVector&) = delete;\n  AuxiliaryVector& operator=(const AuxiliaryVector&) = delete;\n\n  ~AuxiliaryVector();\n\n  //! \\brief Initializes this object with the auxiliary vector for the process\n  //!     connected via \\a connection.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class.\n  //!\n  //! \\param[in] connection A connection to the target process.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Initialize(PtraceConnection* connection);\n\n  //! \\brief Retrieve a value from the vector.\n  //!\n  //! \\param[in] type Specifies which value should be retrieved. The possible\n  //!     values for this parameter are defined by `<linux/auxvec.h>`.\n  //! \\param[out] value The value, casted to an appropriate type, if found.\n  //! \\return `true` if the value is found.\n  template <typename V>\n  bool GetValue(uint64_t type, V* value) const {\n    auto iter = values_.find(type);\n    if (iter == values_.end()) {\n      LOG(ERROR) << \"value not found\";\n      return false;\n    }\n    return ReinterpretBytes(iter->second, value);\n  }\n\n protected:\n  std::map<uint64_t, uint64_t> values_;\n\n private:\n  template <typename ULong>\n  bool Read(PtraceConnection* connection);\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_AUXILIARY_VECTOR_H_\n"
  },
  {
    "path": "util/linux/auxiliary_vector_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/auxiliary_vector.h\"\n\n#include <linux/auxvec.h>\n#include <sys/utsname.h>\n#include <unistd.h>\n\n#include <limits>\n\n#include \"base/bit_cast.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/linux/fake_ptrace_connection.h\"\n#include \"test/main_arguments.h\"\n#include \"test/multiprocess.h\"\n#include \"util/linux/address_types.h\"\n#include \"util/linux/memory_map.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/numeric/int128.h\"\n#include \"util/process/process_memory_linux.h\"\n\n#if !BUILDFLAG(IS_ANDROID)\n// TODO(jperaza): This symbol isn't defined when building in chromium for\n// Android. There may be another symbol to use.\nextern \"C\" {\n#if defined(ARCH_CPU_MIPS_FAMILY)\n#define START_SYMBOL __start\n#else\n#define START_SYMBOL _start\n#endif\nextern void START_SYMBOL();\n}  // extern \"C\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid TestAgainstCloneOrSelf(pid_t pid) {\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(pid));\n\n  AuxiliaryVector aux;\n  ASSERT_TRUE(aux.Initialize(&connection));\n\n  MemoryMap mappings;\n  ASSERT_TRUE(mappings.Initialize(&connection));\n\n  LinuxVMAddress phdrs;\n  ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));\n  EXPECT_TRUE(mappings.FindMapping(phdrs));\n\n  int pagesize;\n  ASSERT_TRUE(aux.GetValue(AT_PAGESZ, &pagesize));\n  EXPECT_EQ(pagesize, getpagesize());\n\n  LinuxVMAddress interp_base;\n  ASSERT_TRUE(aux.GetValue(AT_BASE, &interp_base));\n  EXPECT_TRUE(mappings.FindMapping(interp_base));\n\n#if !BUILDFLAG(IS_ANDROID)\n  LinuxVMAddress entry_addr;\n  ASSERT_TRUE(aux.GetValue(AT_ENTRY, &entry_addr));\n  EXPECT_EQ(entry_addr, FromPointerCast<LinuxVMAddress>(START_SYMBOL));\n#endif\n\n  uid_t uid;\n  ASSERT_TRUE(aux.GetValue(AT_UID, &uid));\n  EXPECT_EQ(uid, getuid());\n\n  uid_t euid;\n  ASSERT_TRUE(aux.GetValue(AT_EUID, &euid));\n  EXPECT_EQ(euid, geteuid());\n\n  gid_t gid;\n  ASSERT_TRUE(aux.GetValue(AT_GID, &gid));\n  EXPECT_EQ(gid, getgid());\n\n  gid_t egid;\n  ASSERT_TRUE(aux.GetValue(AT_EGID, &egid));\n  EXPECT_EQ(egid, getegid());\n\n  ProcessMemoryLinux memory(&connection);\n\n// AT_PLATFORM is null for RISC-V:\n// https://elixir.bootlin.com/linux/v6.4-rc4/C/ident/ELF_PLATFORM\n#if !defined(ARCH_CPU_RISCV64)\n  LinuxVMAddress platform_addr;\n  ASSERT_TRUE(aux.GetValue(AT_PLATFORM, &platform_addr));\n  std::string platform;\n  ASSERT_TRUE(memory.ReadCStringSizeLimited(platform_addr, 10, &platform));\n#endif  // ARCH_CPU_RISCV64\n\n#if defined(ARCH_CPU_X86)\n  EXPECT_STREQ(platform.c_str(), \"i686\");\n#elif defined(ARCH_CPU_X86_64)\n  EXPECT_STREQ(platform.c_str(), \"x86_64\");\n#elif defined(ARCH_CPU_ARMEL)\n  // Machine name and platform are set in Linux:/arch/arm/kernel/setup.c\n  // Machine typically looks like \"armv7l\".\n  // Platform typically looks like \"v7l\".\n  utsname sys_names;\n  ASSERT_EQ(uname(&sys_names), 0);\n  std::string machine_name(sys_names.machine);\n  EXPECT_NE(machine_name.find(platform), std::string::npos);\n#elif defined(ARCH_CPU_ARM64)\n  EXPECT_STREQ(platform.c_str(), \"aarch64\");\n#endif  // ARCH_CPU_X86\n\n#if defined(AT_SYSINFO_EHDR)\n  LinuxVMAddress vdso_addr;\n  if (aux.GetValue(AT_SYSINFO_EHDR, &vdso_addr)) {\n    EXPECT_TRUE(mappings.FindMapping(vdso_addr));\n  }\n#endif  // AT_SYSINFO_EHDR\n\n#if defined(AT_EXECFN)\n  LinuxVMAddress filename_addr;\n  ASSERT_TRUE(aux.GetValue(AT_EXECFN, &filename_addr));\n  std::string filename;\n  ASSERT_TRUE(memory.ReadCStringSizeLimited(filename_addr, 4096, &filename));\n  EXPECT_TRUE(filename.find(GetMainArguments()[0]) != std::string::npos);\n#endif  // AT_EXECFN\n\n  int ignore;\n  EXPECT_FALSE(aux.GetValue(AT_NULL, &ignore));\n\n  char too_small;\n  EXPECT_FALSE(aux.GetValue(AT_PAGESZ, &too_small));\n\n  uint128_struct big_dest;\n  memset(&big_dest, 0xf, sizeof(big_dest));\n  ASSERT_TRUE(aux.GetValue(AT_PHDR, &big_dest));\n  EXPECT_EQ(big_dest.lo, phdrs);\n}\n\nTEST(AuxiliaryVector, ReadSelf) {\n  TestAgainstCloneOrSelf(getpid());\n}\n\nclass ReadChildTest : public Multiprocess {\n public:\n  ReadChildTest() : Multiprocess() {}\n\n  ReadChildTest(const ReadChildTest&) = delete;\n  ReadChildTest& operator=(const ReadChildTest&) = delete;\n\n  ~ReadChildTest() {}\n\n private:\n  void MultiprocessParent() override { TestAgainstCloneOrSelf(ChildPID()); }\n\n  void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); }\n};\n\nTEST(AuxiliaryVector, ReadChild) {\n  ReadChildTest test;\n  test.Run();\n}\n\nclass AuxVecTester : public AuxiliaryVector {\n public:\n  AuxVecTester() : AuxiliaryVector() {}\n  void Insert(uint64_t type, uint64_t value) { values_[type] = value; }\n};\n\nTEST(AuxiliaryVector, SignedBit) {\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  AuxVecTester aux;\n  ASSERT_TRUE(&connection);\n  constexpr uint64_t type = 0x0000000012345678;\n\n  constexpr int32_t neg1_32 = -1;\n  aux.Insert(type, base::bit_cast<uint32_t>(neg1_32));\n  int32_t outval32s;\n  ASSERT_TRUE(aux.GetValue(type, &outval32s));\n  EXPECT_EQ(outval32s, neg1_32);\n\n  constexpr int32_t int32_max = std::numeric_limits<int32_t>::max();\n  aux.Insert(type, base::bit_cast<uint32_t>(int32_max));\n  ASSERT_TRUE(aux.GetValue(type, &outval32s));\n  EXPECT_EQ(outval32s, int32_max);\n\n  constexpr uint32_t uint32_max = std::numeric_limits<uint32_t>::max();\n  aux.Insert(type, uint32_max);\n  uint32_t outval32u;\n  ASSERT_TRUE(aux.GetValue(type, &outval32u));\n  EXPECT_EQ(outval32u, uint32_max);\n\n  constexpr int64_t neg1_64 = -1;\n  aux.Insert(type, base::bit_cast<uint64_t>(neg1_64));\n  int64_t outval64s;\n  ASSERT_TRUE(aux.GetValue(type, &outval64s));\n  EXPECT_EQ(outval64s, neg1_64);\n\n  constexpr int64_t int64_max = std::numeric_limits<int64_t>::max();\n  aux.Insert(type, base::bit_cast<uint64_t>(int64_max));\n  ASSERT_TRUE(aux.GetValue(type, &outval64s));\n  EXPECT_EQ(outval64s, int64_max);\n\n  constexpr uint64_t uint64_max = std::numeric_limits<uint64_t>::max();\n  aux.Insert(type, uint64_max);\n  uint64_t outval64u;\n  ASSERT_TRUE(aux.GetValue(type, &outval64u));\n  EXPECT_EQ(outval64u, uint64_max);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/checked_linux_address_range.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_CHECKED_LINUX_ADDRESS_RANGE_H_\n#define CRASHPAD_UTIL_LINUX_CHECKED_LINUX_ADDRESS_RANGE_H_\n\n#include \"util/linux/address_types.h\"\n#include \"util/numeric/checked_address_range.h\"\n\nnamespace crashpad {\n\n//! \\brief Ensures that a range, composed of a base and a size, does not\n//!     overflow the pointer type of the process it describes a range in.\n//!\n//! This class checks bases of type #LinuxVMAddress and sizes of type\n//! #LinuxVMSize against a process whose pointer type is either 32 or 64\n//! bits wide.\n//!\n//! Aside from varying the overall range on the basis of a process’ pointer type\n//! width, this class functions very similarly to CheckedRange.\nusing CheckedLinuxAddressRange =\n    internal::CheckedAddressRangeGeneric<LinuxVMAddress, LinuxVMSize>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_CHECKED_LINUX_ADDRESS_RANGE_H_\n"
  },
  {
    "path": "util/linux/direct_ptrace_connection.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/direct_ptrace_connection.h\"\n\n#include <utility>\n\n#include \"util/file/file_io.h\"\n#include \"util/linux/proc_task_reader.h\"\n\nnamespace crashpad {\n\nDirectPtraceConnection::DirectPtraceConnection()\n    : PtraceConnection(),\n      attachments_(),\n      memory_(),\n      pid_(-1),\n      ptracer_(/* can_log= */ true),\n      initialized_() {}\n\nDirectPtraceConnection::~DirectPtraceConnection() {}\n\nbool DirectPtraceConnection::Initialize(pid_t pid) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  if (!Attach(pid) || !ptracer_.Initialize(pid)) {\n    return false;\n  }\n  pid_ = pid;\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\npid_t DirectPtraceConnection::GetProcessID() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return pid_;\n}\n\nbool DirectPtraceConnection::Attach(pid_t tid) {\n  std::unique_ptr<ScopedPtraceAttach> attach(new ScopedPtraceAttach);\n  if (!attach->ResetAttach(tid)) {\n    return false;\n  }\n  attachments_.push_back(std::move(attach));\n  return true;\n}\n\nbool DirectPtraceConnection::Is64Bit() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ptracer_.Is64Bit();\n}\n\nbool DirectPtraceConnection::GetThreadInfo(pid_t tid, ThreadInfo* info) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ptracer_.GetThreadInfo(tid, info);\n}\n\nbool DirectPtraceConnection::ReadFileContents(const base::FilePath& path,\n                                              std::string* contents) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return LoggingReadEntireFile(path, contents);\n}\n\nProcessMemoryLinux* DirectPtraceConnection::Memory() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!memory_) {\n    memory_ = std::make_unique<ProcessMemoryLinux>(this);\n  }\n  return memory_.get();\n}\n\nbool DirectPtraceConnection::Threads(std::vector<pid_t>* threads) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ReadThreadIDs(pid_, threads);\n}\n\nssize_t DirectPtraceConnection::ReadUpTo(VMAddress address,\n                                         size_t size,\n                                         void* buffer) {\n  return ptracer_.ReadUpTo(pid_, address, size, static_cast<char*>(buffer));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/direct_ptrace_connection.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_DIRECT_PTRACE_CONNECTION_H_\n#define CRASHPAD_UTIL_LINUX_DIRECT_PTRACE_CONNECTION_H_\n\n#include <sys/types.h>\n\n#include <memory>\n#include <vector>\n\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/linux/ptracer.h\"\n#include \"util/linux/scoped_ptrace_attach.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory_linux.h\"\n\nnamespace crashpad {\n\n//! \\brief Manages a direct `ptrace` connection to a process.\n//!\n//! This class is used when the current process has `ptrace` capabilities for\n//! the target process.\nclass DirectPtraceConnection : public PtraceConnection {\n public:\n  DirectPtraceConnection();\n\n  DirectPtraceConnection(const DirectPtraceConnection&) = delete;\n  DirectPtraceConnection& operator=(const DirectPtraceConnection&) = delete;\n\n  ~DirectPtraceConnection();\n\n  //! \\brief Initializes this connection for the process whose process ID is\n  //!     \\a pid.\n  //!\n  //! The main thread of the process is automatically attached by this call.\n  //!\n  //! \\param[in] pid The process ID of the process to connect to.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Initialize(pid_t pid);\n\n  // PtraceConnection:\n\n  pid_t GetProcessID() override;\n  bool Attach(pid_t tid) override;\n  bool Is64Bit() override;\n  bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;\n  bool ReadFileContents(const base::FilePath& path,\n                        std::string* contents) override;\n  ProcessMemoryLinux* Memory() override;\n  bool Threads(std::vector<pid_t>* threads) override;\n  ssize_t ReadUpTo(VMAddress, size_t size, void* buffer) override;\n\n private:\n  std::vector<std::unique_ptr<ScopedPtraceAttach>> attachments_;\n  std::unique_ptr<ProcessMemoryLinux> memory_;\n  pid_t pid_;\n  Ptracer ptracer_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_DIRECT_PTRACE_CONNECTION_H_\n"
  },
  {
    "path": "util/linux/exception_handler_client.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/exception_handler_client.h\"\n\n#include <errno.h>\n#include <signal.h>\n#include <sys/prctl.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n#include \"third_party/lss/lss.h\"\n#include \"util/file/file_io.h\"\n#include \"util/linux/ptrace_broker.h\"\n#include \"util/linux/socket.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/posix/signals.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\nclass ScopedSigprocmaskRestore {\n public:\n  explicit ScopedSigprocmaskRestore(const kernel_sigset_t& set_to_block)\n      : orig_mask_(), mask_is_set_(false) {\n    mask_is_set_ = sys_sigprocmask(SIG_BLOCK, &set_to_block, &orig_mask_) == 0;\n    DPLOG_IF(ERROR, !mask_is_set_) << \"sigprocmask\";\n  }\n\n  ScopedSigprocmaskRestore(const ScopedSigprocmaskRestore&) = delete;\n  ScopedSigprocmaskRestore& operator=(const ScopedSigprocmaskRestore&) = delete;\n\n  ~ScopedSigprocmaskRestore() {\n    if (mask_is_set_ &&\n        sys_sigprocmask(SIG_SETMASK, &orig_mask_, nullptr) != 0) {\n      DPLOG(ERROR) << \"sigprocmask\";\n    }\n  }\n\n private:\n  kernel_sigset_t orig_mask_;\n  bool mask_is_set_;\n};\n\n}  // namespace\n\nExceptionHandlerClient::ExceptionHandlerClient(int sock, bool multiple_clients)\n    : server_sock_(sock),\n      ptracer_(-1),\n      can_set_ptracer_(true),\n      multiple_clients_(multiple_clients) {}\n\nExceptionHandlerClient::~ExceptionHandlerClient() = default;\n\nbool ExceptionHandlerClient::GetHandlerCredentials(ucred* creds) {\n  ExceptionHandlerProtocol::ClientToServerMessage message = {};\n  message.type =\n      ExceptionHandlerProtocol::ClientToServerMessage::kTypeCheckCredentials;\n  if (UnixCredentialSocket::SendMsg(server_sock_, &message, sizeof(message)) !=\n      0) {\n    return false;\n  }\n\n  ExceptionHandlerProtocol::ServerToClientMessage response;\n  return UnixCredentialSocket::RecvMsg(\n      server_sock_, &response, sizeof(response), creds);\n}\n\nint ExceptionHandlerClient::RequestCrashDump(\n    const ExceptionHandlerProtocol::ClientInformation& info) {\n  VMAddress sp = FromPointerCast<VMAddress>(&sp);\n\n  if (multiple_clients_) {\n    return SignalCrashDump(info, sp);\n  }\n\n  int status = SendCrashDumpRequest(info, sp);\n  if (status != 0) {\n    return status;\n  }\n  return WaitForCrashDumpComplete();\n}\n\nint ExceptionHandlerClient::SetPtracer(pid_t pid) {\n  if (ptracer_ == pid) {\n    return 0;\n  }\n\n  if (!can_set_ptracer_) {\n    return EPERM;\n  }\n\n  if (prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0) {\n    return 0;\n  }\n  return errno;\n}\n\nvoid ExceptionHandlerClient::SetCanSetPtracer(bool can_set_ptracer) {\n  can_set_ptracer_ = can_set_ptracer;\n}\n\nint ExceptionHandlerClient::SignalCrashDump(\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    VMAddress stack_pointer) {\n  kernel_sigset_t dump_done_sigset;\n  sys_sigemptyset(&dump_done_sigset);\n  sys_sigaddset(&dump_done_sigset, ExceptionHandlerProtocol::kDumpDoneSignal);\n  ScopedSigprocmaskRestore scoped_block(dump_done_sigset);\n\n  int status = SendCrashDumpRequest(info, stack_pointer);\n  if (status != 0) {\n    return status;\n  }\n\n  siginfo_t siginfo = {};\n  timespec timeout;\n  timeout.tv_sec = 5;\n  timeout.tv_nsec = 0;\n  if (HANDLE_EINTR(sys_sigtimedwait(&dump_done_sigset, &siginfo, &timeout)) <\n      0) {\n    return errno;\n  }\n\n  return 0;\n}\n\nint ExceptionHandlerClient::SendCrashDumpRequest(\n    const ExceptionHandlerProtocol::ClientInformation& info,\n    VMAddress stack_pointer) {\n  ExceptionHandlerProtocol::ClientToServerMessage message;\n  message.type =\n      ExceptionHandlerProtocol::ClientToServerMessage::kTypeCrashDumpRequest;\n  message.requesting_thread_stack_address = stack_pointer;\n  message.client_info = info;\n  return UnixCredentialSocket::SendMsg(server_sock_, &message, sizeof(message));\n}\n\nint ExceptionHandlerClient::WaitForCrashDumpComplete() {\n  ExceptionHandlerProtocol::ServerToClientMessage message;\n\n  // If the server hangs up, ReadFileExactly will return false without setting\n  // errno.\n  errno = 0;\n  while (ReadFileExactly(server_sock_, &message, sizeof(message))) {\n    switch (message.type) {\n      case ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker: {\n        Signals::InstallDefaultHandler(SIGCHLD);\n\n        pid_t pid = fork();\n        if (pid <= 0) {\n          ExceptionHandlerProtocol::Errno error = pid < 0 ? errno : 0;\n          if (!WriteFile(server_sock_, &error, sizeof(error))) {\n            return errno;\n          }\n        }\n\n        if (pid < 0) {\n          continue;\n        }\n\n        if (pid == 0) {\n#if defined(ARCH_CPU_64_BITS)\n          constexpr bool am_64_bit = true;\n#else\n          constexpr bool am_64_bit = false;\n#endif  // ARCH_CPU_64_BITS\n\n          PtraceBroker broker(server_sock_, getppid(), am_64_bit);\n          _exit(broker.Run());\n        }\n\n        int status = 0;\n        pid_t child = HANDLE_EINTR(waitpid(pid, &status, 0));\n        DCHECK_EQ(child, pid);\n\n        if (child == pid && status != 0) {\n          return status;\n        }\n        continue;\n      }\n\n      case ExceptionHandlerProtocol::ServerToClientMessage::kTypeSetPtracer: {\n        ExceptionHandlerProtocol::Errno result = SetPtracer(message.pid);\n        if (!WriteFile(server_sock_, &result, sizeof(result))) {\n          return errno;\n        }\n        continue;\n      }\n\n      case ExceptionHandlerProtocol::ServerToClientMessage::kTypeCredentials:\n        DCHECK(false);\n        continue;\n\n      case ExceptionHandlerProtocol::ServerToClientMessage::\n          kTypeCrashDumpComplete:\n      case ExceptionHandlerProtocol::ServerToClientMessage::\n          kTypeCrashDumpFailed:\n        return 0;\n    }\n\n    DCHECK(false);\n  }\n\n  return errno;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/exception_handler_client.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_\n#define CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_\n\n#include <sys/socket.h>\n#include <sys/types.h>\n\n#include \"util/linux/exception_handler_protocol.h\"\n\nnamespace crashpad {\n\n//! A client for an ExceptionHandlerServer\nclass ExceptionHandlerClient {\n public:\n  //! \\brief Constructs this object.\n  //!\n  //! \\param[in] sock A socket connected to an ExceptionHandlerServer.\n  //! \\param[in] multiple_clients `true` if this socket may be used by multiple\n  //!     clients.\n  ExceptionHandlerClient(int sock, bool multiple_clients);\n\n  ExceptionHandlerClient(const ExceptionHandlerClient&) = delete;\n  ExceptionHandlerClient& operator=(const ExceptionHandlerClient&) = delete;\n\n  ~ExceptionHandlerClient();\n\n  //! \\brief Communicates with the handler to determine its credentials.\n  //!\n  //! If using a multi-client socket, this method should be called before\n  //! sharing the client socket end, or the handler's response may not be\n  //! received.\n  //!\n  //! \\param[out] creds The handler process' credentials, valid if this method\n  //!     returns `true`.\n  //! \\return `true` on success. Otherwise, `false` with a message logged.\n  bool GetHandlerCredentials(ucred* creds);\n\n  //! \\brief Request a crash dump from the ExceptionHandlerServer.\n  //!\n  //! This method blocks until the crash dump is complete.\n  //!\n  //! \\param[in] info Information about this client.\n  //! \\return 0 on success or an error code on failure.\n  int RequestCrashDump(const ExceptionHandlerProtocol::ClientInformation& info);\n\n  //! \\brief Uses `prctl(PR_SET_PTRACER, ...)` to set the process with\n  //!     process ID \\a pid as the ptracer for this process.\n  //!\n  //! \\param[in] pid The process ID of the process to be set as this process'\n  //!     ptracer.\n  //! \\return 0 on success or an error code on failure.\n  int SetPtracer(pid_t pid);\n\n  //! \\brief Enables or disables SetPtracer().\n  //! \\param[in] can_set_ptracer Whether SetPtracer should be enabled.\n  void SetCanSetPtracer(bool can_set_ptracer);\n\n private:\n  int SendCrashDumpRequest(\n      const ExceptionHandlerProtocol::ClientInformation& info,\n      VMAddress stack_pointer);\n  int SignalCrashDump(const ExceptionHandlerProtocol::ClientInformation& info,\n                      VMAddress stack_pointer);\n  int WaitForCrashDumpComplete();\n\n  int server_sock_;\n  pid_t ptracer_;\n  bool can_set_ptracer_;\n  bool multiple_clients_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_CLIENT_H_\n"
  },
  {
    "path": "util/linux/exception_handler_protocol.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/exception_handler_protocol.h\"\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\nExceptionHandlerProtocol::ClientInformation::ClientInformation()\n    : exception_information_address(0),\n      sanitization_information_address(0)\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n      ,\n      crash_loop_before_time(0)\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n{\n}\n\nExceptionHandlerProtocol::ClientToServerMessage::ClientToServerMessage()\n    : version(kVersion),\n      type(kTypeCrashDumpRequest),\n      requesting_thread_stack_address(0),\n      client_info() {}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/exception_handler_protocol.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_\n#define CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_\n\n#include <errno.h>\n#include <signal.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include \"build/build_config.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/address_types.h\"\n\nnamespace crashpad {\n\nclass ExceptionHandlerProtocol {\n public:\n#pragma pack(push, 1)\n\n  //! \\brief The type used for error reporting.\n  using Errno = int32_t;\n  static_assert(sizeof(Errno) >= sizeof(errno), \"Errno type is too small\");\n\n  //! \\brief A boolean status suitable for communication between processes.\n  enum Bool : char { kBoolFalse, kBoolTrue };\n\n  //! \\brief Information about a client registered with an\n  //!     ExceptionHandlerServer.\n  struct ClientInformation {\n    //! \\brief Constructs this object.\n    ClientInformation();\n\n    //! \\brief The address in the client's address space of an\n    //!     ExceptionInformation struct.\n    VMAddress exception_information_address;\n\n    //! \\brief The address in the client's address space of a\n    //!     SanitizationInformation struct, or 0 if there is no such struct.\n    VMAddress sanitization_information_address;\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    //! \\brief Indicates that the client is likely in a crash loop if a crash\n    //!     occurs before this timestamp. This value is only used by ChromeOS's\n    //!     `/sbin/crash_reporter`.\n    uint64_t crash_loop_before_time;\n#endif\n  };\n\n  //! \\brief The signal used to indicate a crash dump is complete.\n  //!\n  //! When multiple clients share a single socket connection with the handler,\n  //! the handler sends this signal to the dump requestor to indicate when the\n  //! the dump is either done or has failed and the client may continue.\n  static constexpr int kDumpDoneSignal = SIGCONT;\n\n  //! \\brief The message passed from client to server.\n  struct ClientToServerMessage {\n    static constexpr int32_t kVersion = 1;\n\n    //! \\brief Constructs this object.\n    ClientToServerMessage();\n\n    //! \\brief Indicates what message version is being used.\n    int32_t version;\n\n    enum Type : uint32_t {\n      //! \\brief Request that the server respond with its credentials.\n      kTypeCheckCredentials,\n\n      //! \\brief Used to request a crash dump for the sending client.\n      kTypeCrashDumpRequest\n    };\n\n    Type type;\n\n    //! \\brief A stack address of the thread sending the message.\n    VMAddress requesting_thread_stack_address;\n\n    union {\n      //! \\brief Valid for type == kCrashDumpRequest\n      ClientInformation client_info;\n    };\n  };\n\n  //! \\brief The message passed from server to client.\n  struct ServerToClientMessage {\n    enum Type : uint32_t {\n      //! \\brief Used to pass credentials with `SCM_CREDENTIALS`.\n      kTypeCredentials,\n\n      //! \\brief Indicates that the client should fork a PtraceBroker process.\n      kTypeForkBroker,\n\n      //! \\brief Inidicates that the client should set allow the handler to\n      //!     trace it using PR_SET_PTRACER.\n      kTypeSetPtracer,\n\n      //! \\brief Indicates that the handler has completed a requested crash\n      //!     dump.\n      kTypeCrashDumpComplete,\n\n      //! \\brief Indicicates that the handler was unable to produce a crash\n      //!     dump.\n      kTypeCrashDumpFailed\n    };\n\n    Type type;\n\n    //! \\brief The handler's process ID. Valid for kTypeSetPtracer.\n    pid_t pid;\n  };\n\n  ExceptionHandlerProtocol() = delete;\n  ExceptionHandlerProtocol(const ExceptionHandlerProtocol&) = delete;\n  ExceptionHandlerProtocol& operator=(const ExceptionHandlerProtocol&) = delete;\n\n#pragma pack(pop)\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_EXCEPTION_HANDLER_PROTOCOL_H_\n"
  },
  {
    "path": "util/linux/exception_information.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_EXCEPTION_INFORMATION_H_\n#define CRASHPAD_UTIL_LINUX_EXCEPTION_INFORMATION_H_\n\n#include <sys/types.h>\n\n#include \"util/linux/address_types.h\"\n\nnamespace crashpad {\n\n#pragma pack(push, 1)\n\n//! \\brief Structure read out of the client process by the crash handler when an\n//!     exception occurs.\nstruct ExceptionInformation {\n  //! \\brief The address of the `siginfo_t` passed to the signal handler in the\n  //!     crashed process.\n  LinuxVMAddress siginfo_address;\n\n  //! \\brief The address of the `ucontext_t` passed to the signal handler in the\n  //!     crashed process.\n  LinuxVMAddress context_address;\n\n  //! \\brief The thread ID of the thread which received the signal.\n  pid_t thread_id;\n};\n\n#pragma pack(pop)\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_EXCEPTION_INFORMATION_H_\n"
  },
  {
    "path": "util/linux/initial_signal_dispositions.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/initial_signal_dispositions.h\"\n\n#include <android/api-level.h>\n#include <signal.h>\n\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n#if __ANDROID_API__ <= 23\n\nnamespace {\nbool LoggingSignal(int signum, sighandler_t handler) {\n  sighandler_t previous = signal(signum, handler);\n  PLOG_IF(ERROR, previous == SIG_ERR) << \"signal \" << signum;\n  return previous != SIG_ERR;\n}\n}  // namespace\n\n#endif  // __ANDROID_API__ <= 23\n\nbool InitializeSignalDispositions() {\n#if __ANDROID_API__ <= 23\n  const int api_level = android_get_device_api_level();\n  if (api_level < 0) {\n    LOG(WARNING) << \"bad api level\";\n    return false;\n  }\n\n  // Bionic installs signal handlers which request crash dumps from Android's\n  // debuggerd, but there are errors in how signals which aren't automatically\n  // re-raised are handled on Marshmallow (API 23).\n  //\n  // Before requesting a dump, Bionic acquires a lock to communicate with\n  // debuggerd and expecting imminent death, never releases it.\n  //\n  // While handling the dump request, debuggerd allows the dying process to\n  // continue before ptrace-detaching it. So, when Bionic manually re-raises a\n  // signal, it is intercepted by debuggerd and the dying process is allowed to\n  // live.\n  //\n  // Bionic restores SIG_DFL for the signal it's just handled, but if a\n  // different crash signal is later recieved, Bionic attempts to reacquire the\n  // lock to communicate with debuggerd and blocks forever.\n  //\n  // Disable Bionic's signal handlers for these signals on Marshmallow.\n  bool success = true;\n  if (api_level == 23) {\n    success = LoggingSignal(SIGABRT, SIG_DFL);\n    success = LoggingSignal(SIGFPE, SIG_DFL) && success;\n    success = LoggingSignal(SIGPIPE, SIG_DFL) && success;\n#if defined(SIGSTKFLT)\n    success = LoggingSignal(SIGSTKFLT, SIG_DFL) && success;\n#endif\n    success = LoggingSignal(SIGTRAP, SIG_DFL) && success;\n  }\n\n  return success;\n#else\n  return true;\n#endif  // __ANDROID_API__ <= 23\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/initial_signal_dispositions.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_INITIAL_SIGNAL_DISPOSITIONS_H\n#define CRASHPAD_UTIL_LINUX_INITIAL_SIGNAL_DISPOSITIONS_H\n\nnamespace crashpad {\n\n//! \\brief Establishes signal dispositions for a process based on the platform.\n//!\n//! Default signal dispositions are normally configured by the kernel, but\n//! additional signal handlers might be installed by dependent or preloaded\n//! libraries, e.g. Bionic normally installs signal handlers which log stack\n//! traces to Android's logcat.\n//!\n//! This function initializes signal dispositions when the default dispositions\n//! provided by the platform are broken. This function must be called before any\n//! application level signal handlers have been installed and should be called\n//! early in the process lifetime to reduce the chance of any broken signal\n//! handlers being triggered.\n//!\n//! When running on Android M (API 23), this function installs `SIG_DFL` for\n//! signals: `SIGABRT`, `SIGFPE`, `SIGPIPE`, `SIGSTKFLT`, and `SIGTRAP`.\n//!\n//! \\return `true` on success. Otherwise `false` with a message logged.\nbool InitializeSignalDispositions();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_INITIAL_SIGNAL_DISPOSITIONS_H\n"
  },
  {
    "path": "util/linux/memory_map.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/memory_map.h\"\n\n#include <stdio.h>\n#include <string.h>\n#include <sys/sysmacros.h>\n\n#include \"base/check_op.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"util/file/delimited_file_reader.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/string_file.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n\nnamespace crashpad {\n\nnamespace {\n\ntemplate <typename Type>\nbool HexStringToNumber(const std::string& string, Type* number) {\n  return StringToNumber(\"0x\" + string, number);\n}\n\n// The result from parsing a line from the maps file.\nenum class ParseResult {\n  // A line was successfully parsed.\n  kSuccess = 0,\n\n  // The end of the file was successfully reached.\n  kEndOfFile,\n\n  // There was an error in the file, likely because it was read non-atmoically.\n  // We should try to read it again.\n  kRetry,\n\n  // An error with a message logged.\n  kError\n};\n\n// Reads a line from a maps file being read by maps_file_reader and extends\n// mappings with a new MemoryMap::Mapping describing the line.\nParseResult ParseMapsLine(DelimitedFileReader* maps_file_reader,\n                          std::vector<MemoryMap::Mapping>* mappings) {\n  std::string field;\n  LinuxVMAddress start_address;\n  switch (maps_file_reader->GetDelim('-', &field)) {\n    case DelimitedFileReader::Result::kError:\n      return ParseResult::kError;\n    case DelimitedFileReader::Result::kEndOfFile:\n      return ParseResult::kEndOfFile;\n    case DelimitedFileReader::Result::kSuccess:\n      field.pop_back();\n      if (!HexStringToNumber(field, &start_address)) {\n        LOG(ERROR) << \"format error\";\n        return ParseResult::kError;\n      }\n      if (!mappings->empty() && start_address < mappings->back().range.End()) {\n        return ParseResult::kRetry;\n      }\n  }\n\n  LinuxVMAddress end_address;\n  if (maps_file_reader->GetDelim(' ', &field) !=\n          DelimitedFileReader::Result::kSuccess ||\n      (field.pop_back(), !HexStringToNumber(field, &end_address))) {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n  if (end_address < start_address) {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n  // Skip zero-length mappings.\n  if (end_address == start_address) {\n    std::string rest_of_line;\n    if (maps_file_reader->GetLine(&rest_of_line) !=\n        DelimitedFileReader::Result::kSuccess) {\n      LOG(ERROR) << \"format error\";\n      return ParseResult::kError;\n    }\n    return ParseResult::kSuccess;\n  }\n\n  // TODO(jperaza): set bitness properly\n#if defined(ARCH_CPU_64_BITS)\n  constexpr bool is_64_bit = true;\n#else\n  constexpr bool is_64_bit = false;\n#endif\n\n  MemoryMap::Mapping mapping;\n  mapping.range.SetRange(is_64_bit, start_address, end_address - start_address);\n\n  if (maps_file_reader->GetDelim(' ', &field) !=\n          DelimitedFileReader::Result::kSuccess ||\n      (field.pop_back(), field.size() != 4)) {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n#define SET_FIELD(actual_c, outval, true_chars, false_chars) \\\n  do {                                                       \\\n    if (strchr(true_chars, actual_c)) {                      \\\n      *outval = true;                                        \\\n    } else if (strchr(false_chars, actual_c)) {              \\\n      *outval = false;                                       \\\n    } else {                                                 \\\n      LOG(ERROR) << \"format error\";                          \\\n      return ParseResult::kError;                            \\\n    }                                                        \\\n  } while (false)\n  SET_FIELD(field[0], &mapping.readable, \"r\", \"-\");\n  SET_FIELD(field[1], &mapping.writable, \"w\", \"-\");\n  SET_FIELD(field[2], &mapping.executable, \"x\", \"-\");\n  SET_FIELD(field[3], &mapping.shareable, \"sS\", \"p\");\n#undef SET_FIELD\n\n  if (maps_file_reader->GetDelim(' ', &field) !=\n          DelimitedFileReader::Result::kSuccess ||\n      (field.pop_back(), !HexStringToNumber(field, &mapping.offset))) {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n\n  uint32_t major;\n  if (maps_file_reader->GetDelim(':', &field) !=\n          DelimitedFileReader::Result::kSuccess ||\n      (field.pop_back(), field.size()) < 2 ||\n      !HexStringToNumber(field, &major)) {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n\n  uint32_t minor;\n  if (maps_file_reader->GetDelim(' ', &field) !=\n          DelimitedFileReader::Result::kSuccess ||\n      (field.pop_back(), field.size()) < 2 ||\n      !HexStringToNumber(field, &minor)) {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n\n  mapping.device = makedev(major, minor);\n\n  if (maps_file_reader->GetDelim(' ', &field) !=\n          DelimitedFileReader::Result::kSuccess ||\n      (field.pop_back(), !StringToNumber(field, &mapping.inode))) {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n\n  if (maps_file_reader->GetDelim('\\n', &field) !=\n      DelimitedFileReader::Result::kSuccess) {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n  if (field.back() != '\\n') {\n    LOG(ERROR) << \"format error\";\n    return ParseResult::kError;\n  }\n  field.pop_back();\n\n  mappings->push_back(mapping);\n\n  size_t path_start = field.find_first_not_of(' ');\n  if (path_start != std::string::npos) {\n    mappings->back().name = field.substr(path_start);\n  }\n  return ParseResult::kSuccess;\n}\n\nclass SparseReverseIterator : public MemoryMap::Iterator {\n public:\n  SparseReverseIterator(const std::vector<const MemoryMap::Mapping*>& mappings)\n      : mappings_(mappings), riter_(mappings_.rbegin()) {}\n\n  SparseReverseIterator() : mappings_(), riter_(mappings_.rend()) {}\n\n  SparseReverseIterator(const SparseReverseIterator&) = delete;\n  SparseReverseIterator& operator=(const SparseReverseIterator&) = delete;\n\n  // Iterator:\n  const MemoryMap::Mapping* Next() override {\n    return riter_ == mappings_.rend() ? nullptr : *(riter_++);\n  }\n\n  unsigned int Count() override { return mappings_.rend() - riter_; }\n\n private:\n  std::vector<const MemoryMap::Mapping*> mappings_;\n  std::vector<const MemoryMap::Mapping*>::reverse_iterator riter_;\n};\n\nclass FullReverseIterator : public MemoryMap::Iterator {\n public:\n  FullReverseIterator(\n      std::vector<MemoryMap::Mapping>::const_reverse_iterator rbegin,\n      std::vector<MemoryMap::Mapping>::const_reverse_iterator rend)\n      : riter_(rbegin), rend_(rend) {}\n\n  FullReverseIterator(const FullReverseIterator&) = delete;\n  FullReverseIterator& operator=(const FullReverseIterator&) = delete;\n\n  // Iterator:\n  const MemoryMap::Mapping* Next() override {\n    return riter_ == rend_ ? nullptr : &*riter_++;\n  }\n\n  unsigned int Count() override { return rend_ - riter_; }\n\n private:\n  std::vector<MemoryMap::Mapping>::const_reverse_iterator riter_;\n  std::vector<MemoryMap::Mapping>::const_reverse_iterator rend_;\n};\n\n// Faster than a CheckedRange, for temporary values.\nstruct FastRange {\n  VMAddress base;\n  VMSize size;\n};\n\n}  // namespace\n\nMemoryMap::Mapping::Mapping()\n    : name(),\n      range(false, 0, 0),\n      offset(0),\n      device(0),\n      inode(0),\n      readable(false),\n      writable(false),\n      executable(false),\n      shareable(false) {}\n\nMemoryMap::MemoryMap() : mappings_(), connection_(nullptr), initialized_() {}\n\nMemoryMap::~MemoryMap() {}\n\nbool MemoryMap::Mapping::Equals(const Mapping& other) const {\n  DCHECK_EQ(range.Is64Bit(), other.range.Is64Bit());\n  return range.Base() == other.range.Base() &&\n         range.Size() == other.range.Size() && name == other.name &&\n         offset == other.offset && device == other.device &&\n         inode == other.inode && readable == other.readable &&\n         writable == other.writable && executable == other.executable &&\n         shareable == other.shareable;\n}\n\nbool MemoryMap::Initialize(PtraceConnection* connection) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  connection_ = connection;\n\n  // If the maps file is not read atomically, entries can be read multiple times\n  // or missed entirely. The kernel reads entries from this file into a page\n  // sized buffer, so maps files larger than a page require multiple reads.\n  // Attempt to reduce the time between reads by reading the entire file into a\n  // StringFile before attempting to parse it. If ParseMapsLine detects\n  // duplicate, overlapping, or out-of-order entries, it will trigger restarting\n  // the read up to |attempts| times.\n  int attempts = 3;\n  do {\n    std::string contents;\n    char path[32];\n    snprintf(path, sizeof(path), \"/proc/%d/maps\", connection_->GetProcessID());\n    if (!connection_->ReadFileContents(base::FilePath(path), &contents)) {\n      return false;\n    }\n\n    StringFile maps_file;\n    maps_file.SetString(contents);\n    DelimitedFileReader maps_file_reader(&maps_file);\n\n    ParseResult result;\n    while ((result = ParseMapsLine(&maps_file_reader, &mappings_)) ==\n           ParseResult::kSuccess) {\n    }\n    if (result == ParseResult::kEndOfFile) {\n      INITIALIZATION_STATE_SET_VALID(initialized_);\n      return true;\n    }\n    if (result == ParseResult::kError) {\n      return false;\n    }\n\n    DCHECK(result == ParseResult::kRetry);\n  } while (--attempts > 0);\n\n  LOG(ERROR) << \"retry count exceeded\";\n  return false;\n}\n\nconst MemoryMap::Mapping* MemoryMap::FindMapping(LinuxVMAddress address) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  address = connection_->Memory()->PointerToAddress(address);\n\n  for (const auto& mapping : mappings_) {\n    if (mapping.range.Base() <= address && mapping.range.End() > address) {\n      return &mapping;\n    }\n  }\n  return nullptr;\n}\n\nconst MemoryMap::Mapping* MemoryMap::FindMappingWithName(\n    const std::string& name) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  for (const auto& mapping : mappings_) {\n    if (mapping.name == name) {\n      return &mapping;\n    }\n  }\n  return nullptr;\n}\n\nstd::vector<CheckedRange<VMAddress>> MemoryMap::GetReadableRanges(\n    const CheckedRange<VMAddress, VMSize>& range) const {\n  using Range = CheckedRange<VMAddress, VMSize>;\n\n  VMAddress range_base = range.base();\n  VMAddress range_end = range.end();\n  std::vector<FastRange> overlapping;\n\n  // Find all readable ranges overlapping the target range, maintaining order.\n  for (const auto& mapping : mappings_) {\n    if (!mapping.readable)\n      continue;\n    if (mapping.range.End() < range_base)\n      continue;\n    if (mapping.range.Base() >= range_end)\n      continue;\n    // Special case: the \"[vvar]\" region is marked readable, but we can't\n    // access it.\n    if (mapping.inode == 0 && mapping.name == \"[vvar]\")\n      continue;\n    overlapping.push_back({mapping.range.Base(), mapping.range.Size()});\n  }\n  if (overlapping.empty())\n    return std::vector<Range>();\n\n  // For the first and last, trim to the boundary of the incoming range.\n  FastRange& front = overlapping.front();\n  VMAddress original_front_base = front.base;\n  front.base = std::max(front.base, range_base);\n  front.size = (original_front_base + front.size) - front.base;\n  FastRange& back = overlapping.back();\n  VMAddress back_end = back.base + back.size;\n  back.size = std::min(range_end, back_end) - back.base;\n\n  // Coalesce, and convert to return type.\n  std::vector<Range> result;\n  result.push_back({overlapping[0].base, overlapping[0].size});\n  DCHECK(result.back().IsValid());\n  for (size_t i = 1; i < overlapping.size(); ++i) {\n    if (result.back().end() == overlapping[i].base) {\n      result.back().SetRange(result.back().base(),\n                             result.back().size() + overlapping[i].size);\n    } else {\n      result.push_back({overlapping[i].base, overlapping[i].size});\n    }\n    DCHECK(result.back().IsValid());\n  }\n\n  return result;\n}\n\nstd::unique_ptr<MemoryMap::Iterator> MemoryMap::FindFilePossibleMmapStarts(\n    const Mapping& mapping) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::vector<const Mapping*> possible_starts;\n\n  // If the mapping is anonymous, as is for the VDSO, there is no mapped file to\n  // find the start of, so just return the input mapping.\n  if (mapping.device == 0 && mapping.inode == 0) {\n    for (const auto& candidate : mappings_) {\n      if (mapping.Equals(candidate)) {\n        possible_starts.push_back(&candidate);\n        return std::make_unique<SparseReverseIterator>(possible_starts);\n      }\n    }\n\n    LOG(ERROR) << \"mapping not found\";\n    return std::make_unique<SparseReverseIterator>();\n  }\n\n#if BUILDFLAG(IS_ANDROID)\n  // The Android Chromium linker uses ashmem to share RELRO segments between\n  // processes. The original RELRO segment has been unmapped and replaced with a\n  // mapping named \"/dev/ashmem/RELRO:<libname>\" where <libname> is the base\n  // library name (e.g. libchrome.so) sans any preceding path that may be\n  // present in other mappings for the library.\n  // https://crashpad.chromium.org/bug/253\n  static constexpr char kRelro[] = \"/dev/ashmem/RELRO:\";\n  if (mapping.name.compare(0, strlen(kRelro), kRelro, 0, strlen(kRelro)) == 0) {\n    // The kernel appends \"(deleted)\" to ashmem mappings because there isn't\n    // any corresponding file on the filesystem.\n    static constexpr char kDeleted[] = \" (deleted)\";\n    size_t libname_end = mapping.name.rfind(kDeleted);\n    DCHECK_NE(libname_end, std::string::npos);\n    if (libname_end == std::string::npos) {\n      libname_end = mapping.name.size();\n    }\n\n    std::string libname =\n        mapping.name.substr(strlen(kRelro), libname_end - strlen(kRelro));\n    for (const auto& candidate : mappings_) {\n      if (candidate.name.rfind(libname) != std::string::npos) {\n        possible_starts.push_back(&candidate);\n      }\n      if (mapping.Equals(candidate)) {\n        return std::make_unique<SparseReverseIterator>(possible_starts);\n      }\n    }\n  }\n#endif  // BUILDFLAG(IS_ANDROID)\n\n  for (const auto& candidate : mappings_) {\n    if (candidate.device == mapping.device &&\n        candidate.inode == mapping.inode\n#if !BUILDFLAG(IS_ANDROID)\n        // Libraries on Android may be mapped from zipfiles (APKs), in which\n        // case the offset is not 0.\n        && candidate.offset == 0\n#endif  // !BUILDFLAG(IS_ANDROID)\n    ) {\n      possible_starts.push_back(&candidate);\n    }\n    if (mapping.Equals(candidate)) {\n      return std::make_unique<SparseReverseIterator>(possible_starts);\n    }\n  }\n\n  LOG(ERROR) << \"mapping not found\";\n  return std::make_unique<SparseReverseIterator>();\n}\n\nstd::unique_ptr<MemoryMap::Iterator> MemoryMap::ReverseIteratorFrom(\n    const Mapping& target) const {\n  for (auto riter = mappings_.crbegin(); riter != mappings_.rend(); ++riter) {\n    if (riter->Equals(target)) {\n      return std::make_unique<FullReverseIterator>(riter, mappings_.rend());\n    }\n  }\n  return std::make_unique<FullReverseIterator>(mappings_.rend(),\n                                               mappings_.rend());\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/memory_map.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_MEMORY_MAP_H_\n#define CRASHPAD_UTIL_LINUX_MEMORY_MAP_H_\n\n#include <sys/types.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"util/linux/address_types.h\"\n#include \"util/linux/checked_linux_address_range.h\"\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\n//! \\brief Accesses information about mapped memory in another process.\n//!\n//! The target process must be stopped to guarantee correct mappings. If the\n//! target process is not stopped, mappings may be invalid after the return from\n//! Initialize(), and even mappings existing at the time Initialize() was called\n//! may not be found.\nclass MemoryMap {\n public:\n  //! \\brief Information about a mapped region of memory.\n  struct Mapping {\n    Mapping();\n    bool Equals(const Mapping& other) const;\n\n    std::string name;\n    CheckedLinuxAddressRange range;\n    off64_t offset;\n    dev_t device;\n    ino_t inode;\n    bool readable;\n    bool writable;\n    bool executable;\n    bool shareable;\n  };\n\n  MemoryMap();\n  ~MemoryMap();\n\n  //! \\brief Initializes this object with information about the mapped memory\n  //!     regions in the process connected via \\a connection.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class. This method may only be called once.\n  //!\n  //! \\param[in] connection A connection to the process create a map for.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Initialize(PtraceConnection* connection);\n\n  //! \\return The Mapping containing \\a address or `nullptr` if no match is\n  //!     found. The caller does not take ownership of this object. It is scoped\n  //!     to the lifetime of the MemoryMap object that it was obtained from.\n  const Mapping* FindMapping(LinuxVMAddress address) const;\n\n  //! \\return The Mapping with the lowest base address whose name is \\a name or\n  //!     `nullptr` if no match is found. The caller does not take ownership of\n  //!     this object. It is scoped to the lifetime of the MemoryMap object that\n  //!     it was obtained from.\n  const Mapping* FindMappingWithName(const std::string& name) const;\n\n  //! \\brief Given a range to be read from the target process, returns a vector\n  //!     of ranges, representing the readable portions of the original range.\n  //!\n  //! \\param[in] range The range being identified.\n  //!\n  //! \\return A vector of ranges corresponding to the portion of \\a range that\n  //!     is readable based on the memory map.\n  std::vector<CheckedRange<uint64_t>> GetReadableRanges(\n      const CheckedRange<LinuxVMAddress, LinuxVMSize>& range) const;\n\n  //! \\brief An abstract base class for iterating over ordered sets of mappings\n  //!   in a MemoryMap.\n  class Iterator {\n   public:\n    virtual ~Iterator() = default;\n\n    //! \\return the mapping pointed to by the iterator and advance the iterator\n    //!     to the next mapping. If there are no more mappings, this method\n    //!     returns `nullptr` on all subsequent invocations.\n    virtual const Mapping* Next() = 0;\n\n    //! \\return the number of mappings remaining.\n    virtual unsigned int Count() = 0;\n\n   protected:\n    Iterator() = default;\n  };\n\n  //! \\brief Find possible initial mappings of files mapped over several\n  //!     segments.\n  //!\n  //! Executables and libaries are typically loaded into several mappings with\n  //! varying permissions for different segments. Portions of an ELF file may\n  //! be mapped multiple times as part of loading the file, for example, when\n  //! initializing GNU_RELRO segments.\n  //!\n  //! This method searches for mappings at or below \\a mapping in memory that\n  //! are mapped from the same file as \\a mapping from offset 0.\n  //!\n  //! On Android, ELF modules may be loaded from within a zipfile, so this\n  //! method may return mappings whose offset is not 0.\n  //!\n  //! This method is intended to help identify the possible base address for\n  //! loaded modules, but it is the caller's responsibility to determine which\n  //! returned mapping is correct.\n  //!\n  //! If \\a mapping does not refer to a valid mapping, an empty vector will be\n  //! returned and a message will be logged. If \\a mapping is found but does not\n  //! map a file, \\a mapping is returned in \\a possible_starts.\n  //!\n  //! \\param[in] mapping A Mapping whose series to find the start of.\n  //! \\return a reverse iterator over the possible mapping starts, starting from\n  //!     the mapping with highest base address.\n  std::unique_ptr<Iterator> FindFilePossibleMmapStarts(\n      const Mapping& mapping) const;\n\n  //! \\return A reverse iterator over all mappings in the MemoryMap from \\a\n  //!     mapping to the start of the MemoryMap.\n  std::unique_ptr<Iterator> ReverseIteratorFrom(const Mapping& mapping) const;\n\n private:\n  std::vector<Mapping> mappings_;\n  PtraceConnection* connection_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_MEMORY_MAP_H_\n"
  },
  {
    "path": "util/linux/memory_map_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/memory_map.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include \"base/files/file_path.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/file.h\"\n#include \"test/linux/fake_ptrace_connection.h\"\n#include \"test/multiprocess.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"third_party/lss/lss.h\"\n#include \"util/file/file_io.h\"\n#include \"util/file/scoped_remove_file.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#include \"util/misc/clock.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/posix/scoped_mmap.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(MemoryMap, SelfLargeFiles) {\n  // This test is meant to test the handler's ability to understand files\n  // mapped from large offsets, even if the handler wasn't built with\n  // _FILE_OFFSET_BITS=64. ScopedTempDir needs to stat files to determine\n  // whether to recurse into directories, which may will fail without large file\n  // support. ScopedRemoveFile doesn't have that restriction.\n  ScopedTempDir dir;\n  ScopedRemoveFile large_file_path(dir.path().Append(\"crashpad_test_file\"));\n  ScopedFileHandle handle(\n      LoggingOpenFileForReadAndWrite(large_file_path.get(),\n                                     FileWriteMode::kCreateOrFail,\n                                     FilePermissions::kWorldReadable));\n  ASSERT_TRUE(handle.is_valid());\n\n  // sys_fallocate supports large files as long as the kernel supports them,\n  // regardless of _FILE_OFFSET_BITS.\n  off64_t off = 1llu + UINT32_MAX;\n  ASSERT_EQ(sys_fallocate(handle.get(), 0, off, getpagesize()), 0)\n      << ErrnoMessage(\"fallocate\");\n\n  ScopedMmap mapping;\n  void* addr = sys_mmap(\n      nullptr, getpagesize(), PROT_READ, MAP_SHARED, handle.get(), off);\n  ASSERT_TRUE(addr);\n  ASSERT_TRUE(mapping.ResetAddrLen(addr, getpagesize()));\n\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n  MemoryMap map;\n  ASSERT_TRUE(map.Initialize(&connection));\n}\n\nTEST(MemoryMap, SelfBasic) {\n  ScopedMmap mmapping;\n  ASSERT_TRUE(mmapping.ResetMmap(nullptr,\n                                 getpagesize(),\n                                 PROT_EXEC | PROT_READ,\n                                 MAP_SHARED | MAP_ANON,\n                                 -1,\n                                 0));\n\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  MemoryMap map;\n  ASSERT_TRUE(map.Initialize(&connection));\n\n  auto stack_address = FromPointerCast<LinuxVMAddress>(&map);\n  const MemoryMap::Mapping* mapping = map.FindMapping(stack_address);\n  ASSERT_TRUE(mapping);\n  EXPECT_GE(stack_address, mapping->range.Base());\n  EXPECT_LT(stack_address, mapping->range.End());\n  EXPECT_TRUE(mapping->readable);\n  EXPECT_TRUE(mapping->writable);\n\n  auto code_address = FromPointerCast<LinuxVMAddress>(getpid);\n  mapping = map.FindMapping(code_address);\n  ASSERT_TRUE(mapping);\n  EXPECT_GE(code_address, mapping->range.Base());\n  EXPECT_LT(code_address, mapping->range.End());\n#if !BUILDFLAG(IS_ANDROID)\n  // Android Q+ supports execute only memory.\n  EXPECT_TRUE(mapping->readable);\n#endif\n  EXPECT_FALSE(mapping->writable);\n  EXPECT_TRUE(mapping->executable);\n\n  auto mapping_address = mmapping.addr_as<LinuxVMAddress>();\n  mapping = map.FindMapping(mapping_address);\n  ASSERT_TRUE(mapping);\n  mapping = map.FindMapping(mapping_address + mmapping.len() - 1);\n  ASSERT_TRUE(mapping);\n  EXPECT_EQ(mapping_address, mapping->range.Base());\n  EXPECT_EQ(mapping_address + mmapping.len(), mapping->range.End());\n  EXPECT_TRUE(mapping->readable);\n  EXPECT_FALSE(mapping->writable);\n  EXPECT_TRUE(mapping->executable);\n  EXPECT_TRUE(mapping->shareable);\n}\n\nvoid InitializeFile(const base::FilePath& path,\n                    size_t size,\n                    ScopedFileHandle* handle) {\n  ASSERT_FALSE(FileExists(path));\n\n  handle->reset(LoggingOpenFileForReadAndWrite(\n      path, FileWriteMode::kReuseOrCreate, FilePermissions::kOwnerOnly));\n  ASSERT_TRUE(handle->is_valid());\n  std::string file_contents(size, std::string::value_type());\n  CheckedWriteFile(handle->get(), file_contents.c_str(), file_contents.size());\n}\n\nclass MapChildTest : public Multiprocess {\n public:\n  MapChildTest() : Multiprocess(), page_size_(getpagesize()) {}\n\n  MapChildTest(const MapChildTest&) = delete;\n  MapChildTest& operator=(const MapChildTest&) = delete;\n\n  ~MapChildTest() {}\n\n private:\n  void MultiprocessParent() override {\n    LinuxVMAddress code_address;\n    CheckedReadFileExactly(\n        ReadPipeHandle(), &code_address, sizeof(code_address));\n\n    LinuxVMAddress stack_address;\n    CheckedReadFileExactly(\n        ReadPipeHandle(), &stack_address, sizeof(stack_address));\n\n    LinuxVMAddress mapped_address;\n    CheckedReadFileExactly(\n        ReadPipeHandle(), &mapped_address, sizeof(mapped_address));\n\n    LinuxVMAddress mapped_file_address;\n    CheckedReadFileExactly(\n        ReadPipeHandle(), &mapped_file_address, sizeof(mapped_file_address));\n    LinuxVMSize path_length;\n    CheckedReadFileExactly(ReadPipeHandle(), &path_length, sizeof(path_length));\n    std::string mapped_file_name(path_length, std::string::value_type());\n    CheckedReadFileExactly(ReadPipeHandle(), &mapped_file_name[0], path_length);\n\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(ChildPID()));\n\n    MemoryMap map;\n    ASSERT_TRUE(map.Initialize(&connection));\n\n    const MemoryMap::Mapping* mapping = map.FindMapping(code_address);\n    ASSERT_TRUE(mapping);\n    EXPECT_GE(code_address, mapping->range.Base());\n    EXPECT_LT(code_address, mapping->range.End());\n#if !BUILDFLAG(IS_ANDROID)\n    // Android Q+ supports execute only memory.\n    EXPECT_TRUE(mapping->readable);\n#endif\n    EXPECT_TRUE(mapping->executable);\n    EXPECT_FALSE(mapping->writable);\n\n    mapping = map.FindMapping(stack_address);\n    ASSERT_TRUE(mapping);\n    EXPECT_GE(stack_address, mapping->range.Base());\n    EXPECT_LT(stack_address, mapping->range.End());\n    EXPECT_TRUE(mapping->readable);\n    EXPECT_TRUE(mapping->writable);\n\n    mapping = map.FindMapping(mapped_address);\n    ASSERT_TRUE(mapping);\n    EXPECT_EQ(mapped_address, mapping->range.Base());\n    EXPECT_EQ(mapped_address + page_size_, mapping->range.End());\n    EXPECT_FALSE(mapping->readable);\n    EXPECT_FALSE(mapping->writable);\n    EXPECT_FALSE(mapping->executable);\n    EXPECT_TRUE(mapping->shareable);\n\n    mapping = map.FindMapping(mapped_file_address);\n    ASSERT_TRUE(mapping);\n    EXPECT_EQ(mapped_file_address, mapping->range.Base());\n    EXPECT_EQ(mapping->offset, static_cast<int64_t>(page_size_));\n    EXPECT_TRUE(mapping->readable);\n    EXPECT_TRUE(mapping->writable);\n    EXPECT_FALSE(mapping->executable);\n    EXPECT_FALSE(mapping->shareable);\n    EXPECT_EQ(mapping->name, mapped_file_name);\n    struct stat file_stat;\n    ASSERT_EQ(stat(mapped_file_name.c_str(), &file_stat), 0)\n        << ErrnoMessage(\"stat\");\n    EXPECT_EQ(mapping->device, file_stat.st_dev);\n    EXPECT_EQ(mapping->inode, file_stat.st_ino);\n    EXPECT_EQ(map.FindMappingWithName(mapping->name), mapping);\n  }\n\n  void MultiprocessChild() override {\n    auto code_address = FromPointerCast<LinuxVMAddress>(getpid);\n    CheckedWriteFile(WritePipeHandle(), &code_address, sizeof(code_address));\n\n    auto stack_address = FromPointerCast<LinuxVMAddress>(&code_address);\n    CheckedWriteFile(WritePipeHandle(), &stack_address, sizeof(stack_address));\n\n    ScopedMmap mapping;\n    ASSERT_TRUE(mapping.ResetMmap(\n        nullptr, page_size_, PROT_NONE, MAP_SHARED | MAP_ANON, -1, 0));\n    auto mapped_address = mapping.addr_as<LinuxVMAddress>();\n    CheckedWriteFile(\n        WritePipeHandle(), &mapped_address, sizeof(mapped_address));\n\n    ScopedTempDir temp_dir;\n    base::FilePath path =\n        temp_dir.path().Append(FILE_PATH_LITERAL(\"test_file\"));\n    ScopedFileHandle handle;\n    ASSERT_NO_FATAL_FAILURE(InitializeFile(path, page_size_ * 2, &handle));\n\n    ScopedMmap file_mapping;\n    ASSERT_TRUE(file_mapping.ResetMmap(nullptr,\n                                       page_size_,\n                                       PROT_READ | PROT_WRITE,\n                                       MAP_PRIVATE,\n                                       handle.get(),\n                                       page_size_));\n\n    auto mapped_file_address = file_mapping.addr_as<LinuxVMAddress>();\n    CheckedWriteFile(\n        WritePipeHandle(), &mapped_file_address, sizeof(mapped_file_address));\n    LinuxVMSize path_length = path.value().size();\n    CheckedWriteFile(WritePipeHandle(), &path_length, sizeof(path_length));\n    CheckedWriteFile(WritePipeHandle(), path.value().c_str(), path_length);\n\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  const size_t page_size_;\n};\n\nTEST(MemoryMap, MapChild) {\n  MapChildTest test;\n  test.Run();\n}\n\n// Some systems optimize mappings by allocating new mappings inside existing\n// mappings with matching permissions. Work around this by allocating one large\n// mapping and then switching up the permissions of individual pages to force\n// populating more entries in the maps file.\nvoid InitializeMappings(ScopedMmap* mappings,\n                        size_t num_mappings,\n                        size_t mapping_size) {\n  ASSERT_TRUE(mappings->ResetMmap(nullptr,\n                                  mapping_size * num_mappings,\n                                  PROT_READ,\n                                  MAP_PRIVATE | MAP_ANON,\n                                  -1,\n                                  0));\n\n  auto region = mappings->addr_as<LinuxVMAddress>();\n  for (size_t index = 0; index < num_mappings; index += 2) {\n    ASSERT_EQ(mprotect(reinterpret_cast<void*>(region + index * mapping_size),\n                       mapping_size,\n                       PROT_READ | PROT_WRITE),\n              0)\n        << ErrnoMessage(\"mprotect\");\n  }\n}\n\nvoid ExpectMappings(const MemoryMap& map,\n                    LinuxVMAddress region_addr,\n                    size_t num_mappings,\n                    size_t mapping_size) {\n  for (size_t index = 0; index < num_mappings; ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n\n    auto mapping_address = region_addr + index * mapping_size;\n    const MemoryMap::Mapping* mapping = map.FindMapping(mapping_address);\n    ASSERT_TRUE(mapping);\n    EXPECT_EQ(mapping_address, mapping->range.Base());\n    EXPECT_EQ(mapping_address + mapping_size, mapping->range.End());\n    EXPECT_TRUE(mapping->readable);\n    EXPECT_FALSE(mapping->shareable);\n    EXPECT_FALSE(mapping->executable);\n    if (index % 2 == 0) {\n      EXPECT_TRUE(mapping->writable);\n    } else {\n      EXPECT_FALSE(mapping->writable);\n    }\n  }\n}\n\nTEST(MemoryMap, SelfLargeMapFile) {\n  constexpr size_t kNumMappings = 1024;\n  const size_t page_size = getpagesize();\n  ScopedMmap mappings;\n\n  ASSERT_NO_FATAL_FAILURE(\n      InitializeMappings(&mappings, kNumMappings, page_size));\n\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  MemoryMap map;\n  ASSERT_TRUE(map.Initialize(&connection));\n\n  ExpectMappings(\n      map, mappings.addr_as<LinuxVMAddress>(), kNumMappings, page_size);\n}\n\nclass MapRunningChildTest : public Multiprocess {\n public:\n  MapRunningChildTest() : Multiprocess(), page_size_(getpagesize()) {}\n\n  MapRunningChildTest(const MapRunningChildTest&) = delete;\n  MapRunningChildTest& operator=(const MapRunningChildTest&) = delete;\n\n  ~MapRunningChildTest() {}\n\n private:\n  void MultiprocessParent() override {\n    // Let the child get started\n    LinuxVMAddress region_addr;\n    CheckedReadFileExactly(ReadPipeHandle(), &region_addr, sizeof(region_addr));\n\n    for (int iter = 0; iter < 8; ++iter) {\n      SCOPED_TRACE(base::StringPrintf(\"iter %d\", iter));\n\n      // Let the child get back to its work\n      SleepNanoseconds(1000);\n\n      DirectPtraceConnection connection;\n      ASSERT_TRUE(connection.Initialize(ChildPID()));\n\n      MemoryMap map;\n      ASSERT_TRUE(map.Initialize(&connection));\n\n      // We should at least find the original mappings. The extra mappings may\n      // or not be found depending on scheduling.\n      ExpectMappings(map, region_addr, kNumMappings, page_size_);\n    }\n  }\n\n  void MultiprocessChild() override {\n    ASSERT_EQ(fcntl(ReadPipeHandle(), F_SETFL, O_NONBLOCK), 0)\n        << ErrnoMessage(\"fcntl\");\n\n    ScopedMmap mappings;\n    ASSERT_NO_FATAL_FAILURE(\n        InitializeMappings(&mappings, kNumMappings, page_size_));\n\n    // Let the parent start mapping us\n    auto region_addr = mappings.addr_as<LinuxVMAddress>();\n    CheckedWriteFile(WritePipeHandle(), &region_addr, sizeof(region_addr));\n\n    // But don't stop there!\n    constexpr size_t kNumExtraMappings = 256;\n    ScopedMmap extra_mappings;\n\n    while (true) {\n      ASSERT_NO_FATAL_FAILURE(\n          InitializeMappings(&extra_mappings, kNumExtraMappings, page_size_));\n\n      // Quit when the parent is done\n      char c;\n      FileOperationResult res = ReadFile(ReadPipeHandle(), &c, sizeof(c));\n      if (res == 0) {\n        break;\n      }\n      ASSERT_EQ(errno, EAGAIN);\n    }\n  }\n\n  static constexpr size_t kNumMappings = 1024;\n  const size_t page_size_;\n};\n\nTEST(MemoryMap, MapRunningChild) {\n  MapRunningChildTest test;\n  test.Run();\n}\n\n// Expects first and third pages from mapping_start to refer to the same mapped\n// file. The second page should not.\nvoid ExpectFindFilePossibleMmapStarts(LinuxVMAddress mapping_start,\n                                      LinuxVMSize page_size) {\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  MemoryMap map;\n  ASSERT_TRUE(map.Initialize(&connection));\n\n  auto mapping1 = map.FindMapping(mapping_start);\n  ASSERT_TRUE(mapping1);\n  auto mapping2 = map.FindMapping(mapping_start + page_size);\n  ASSERT_TRUE(mapping2);\n  auto mapping3 = map.FindMapping(mapping_start + page_size * 2);\n  ASSERT_TRUE(mapping3);\n\n  ASSERT_NE(mapping1, mapping2);\n  ASSERT_NE(mapping2, mapping3);\n\n  auto mappings = map.FindFilePossibleMmapStarts(*mapping1);\n  ASSERT_EQ(mappings->Count(), 1u);\n  EXPECT_EQ(mappings->Next(), mapping1);\n\n  mappings = map.FindFilePossibleMmapStarts(*mapping2);\n  ASSERT_EQ(mappings->Count(), 1u);\n  EXPECT_EQ(mappings->Next(), mapping2);\n\n  mappings = map.FindFilePossibleMmapStarts(*mapping3);\n#if BUILDFLAG(IS_ANDROID)\n  EXPECT_EQ(mappings->Count(), 2u);\n#else\n  ASSERT_EQ(mappings->Count(), 1u);\n  EXPECT_EQ(mappings->Next(), mapping1);\n#endif\n}\n\nTEST(MemoryMap, FindFilePossibleMmapStarts) {\n  const size_t page_size = getpagesize();\n\n  ScopedTempDir temp_dir;\n  base::FilePath path = temp_dir.path().Append(\n      FILE_PATH_LITERAL(\"FindFilePossibleMmapStartsTestFile\"));\n  ScopedFileHandle handle;\n  size_t file_length = page_size * 3;\n  ASSERT_NO_FATAL_FAILURE(InitializeFile(path, file_length, &handle));\n\n  ScopedMmap file_mapping;\n  ASSERT_TRUE(file_mapping.ResetMmap(\n      nullptr, file_length, PROT_READ, MAP_PRIVATE, handle.get(), 0));\n  auto mapping_start = file_mapping.addr_as<LinuxVMAddress>();\n\n  // Change the permissions on the second page to split the mapping into three\n  // parts.\n  ASSERT_EQ(mprotect(file_mapping.addr_as<char*>() + page_size,\n                     page_size,\n                     PROT_READ | PROT_WRITE),\n            0);\n\n  // Basic\n  {\n    FakePtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(getpid()));\n\n    MemoryMap map;\n    ASSERT_TRUE(map.Initialize(&connection));\n\n    auto mapping1 = map.FindMapping(mapping_start);\n    ASSERT_TRUE(mapping1);\n    auto mapping2 = map.FindMapping(mapping_start + page_size);\n    ASSERT_TRUE(mapping2);\n    auto mapping3 = map.FindMapping(mapping_start + page_size * 2);\n    ASSERT_TRUE(mapping3);\n\n    ASSERT_NE(mapping1, mapping2);\n    ASSERT_NE(mapping2, mapping3);\n\n#if BUILDFLAG(IS_ANDROID)\n    auto mappings = map.FindFilePossibleMmapStarts(*mapping1);\n    EXPECT_EQ(mappings->Count(), 1u);\n    EXPECT_EQ(mappings->Next(), mapping1);\n    EXPECT_EQ(mappings->Next(), nullptr);\n\n    mappings = map.FindFilePossibleMmapStarts(*mapping2);\n    EXPECT_EQ(mappings->Count(), 2u);\n\n    mappings = map.FindFilePossibleMmapStarts(*mapping3);\n    EXPECT_EQ(mappings->Count(), 3u);\n#else\n    auto mappings = map.FindFilePossibleMmapStarts(*mapping1);\n    ASSERT_EQ(mappings->Count(), 1u);\n    EXPECT_EQ(mappings->Next(), mapping1);\n    EXPECT_EQ(mappings->Next(), nullptr);\n\n    mappings = map.FindFilePossibleMmapStarts(*mapping2);\n    ASSERT_EQ(mappings->Count(), 1u);\n    EXPECT_EQ(mappings->Next(), mapping1);\n\n    mappings = map.FindFilePossibleMmapStarts(*mapping3);\n    ASSERT_EQ(mappings->Count(), 1u);\n    EXPECT_EQ(mappings->Next(), mapping1);\n#endif\n\n#if defined(ARCH_CPU_64_BITS)\n    constexpr bool is_64_bit = true;\n#else\n    constexpr bool is_64_bit = false;\n#endif\n    MemoryMap::Mapping bad_mapping;\n    bad_mapping.range.SetRange(is_64_bit, 0, 1);\n    mappings = map.FindFilePossibleMmapStarts(bad_mapping);\n    EXPECT_EQ(mappings->Count(), 0u);\n    EXPECT_EQ(mappings->Next(), nullptr);\n  }\n\n  // Make the second page an anonymous mapping\n  file_mapping.ResetAddrLen(file_mapping.addr_as<void*>(), page_size);\n  ScopedMmap page2_mapping;\n  ASSERT_TRUE(page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size,\n                                      page_size,\n                                      PROT_READ | PROT_WRITE,\n                                      MAP_PRIVATE | MAP_ANON | MAP_FIXED,\n                                      -1,\n                                      0));\n  ScopedMmap page3_mapping;\n  ASSERT_TRUE(\n      page3_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size * 2,\n                              page_size,\n                              PROT_READ,\n                              MAP_PRIVATE | MAP_FIXED,\n                              handle.get(),\n                              page_size * 2));\n  ExpectFindFilePossibleMmapStarts(mapping_start, page_size);\n\n  // Map the second page to another file.\n  ScopedFileHandle handle2;\n  base::FilePath path2 = temp_dir.path().Append(\n      FILE_PATH_LITERAL(\"FindFilePossibleMmapStartsTestFile2\"));\n  ASSERT_NO_FATAL_FAILURE(InitializeFile(path2, page_size, &handle2));\n\n  page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size,\n                          page_size,\n                          PROT_READ,\n                          MAP_PRIVATE | MAP_FIXED,\n                          handle2.get(),\n                          0);\n  ExpectFindFilePossibleMmapStarts(mapping_start, page_size);\n}\n\nTEST(MemoryMap, FindFilePossibleMmapStarts_MultipleStarts) {\n  ScopedTempDir temp_dir;\n  base::FilePath path =\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"MultipleStartsTestFile\"));\n  const size_t page_size = getpagesize();\n  ScopedFileHandle handle;\n  ASSERT_NO_FATAL_FAILURE(InitializeFile(path, page_size * 2, &handle));\n\n  // Locate a sequence of pages to setup a test in.\n  char* seq_addr;\n  {\n    ScopedMmap whole_mapping;\n    ASSERT_TRUE(whole_mapping.ResetMmap(\n        nullptr, page_size * 8, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0));\n    seq_addr = whole_mapping.addr_as<char*>();\n  }\n\n  // Arrange file and anonymous mappings in the sequence.\n  ScopedMmap file_mapping0;\n  ASSERT_TRUE(file_mapping0.ResetMmap(seq_addr,\n                                      page_size,\n                                      PROT_READ,\n                                      MAP_PRIVATE | MAP_FIXED,\n                                      handle.get(),\n                                      page_size));\n\n  ScopedMmap file_mapping1;\n  ASSERT_TRUE(file_mapping1.ResetMmap(seq_addr + page_size,\n                                      page_size * 2,\n                                      PROT_READ,\n                                      MAP_PRIVATE | MAP_FIXED,\n                                      handle.get(),\n                                      0));\n\n  ScopedMmap file_mapping2;\n  ASSERT_TRUE(file_mapping2.ResetMmap(seq_addr + page_size * 3,\n                                      page_size,\n                                      PROT_READ,\n                                      MAP_PRIVATE | MAP_FIXED,\n                                      handle.get(),\n                                      0));\n\n  // Skip a page\n\n  ScopedMmap file_mapping3;\n  ASSERT_TRUE(file_mapping3.ResetMmap(seq_addr + page_size * 5,\n                                      page_size,\n                                      PROT_READ,\n                                      MAP_PRIVATE | MAP_FIXED,\n                                      handle.get(),\n                                      0));\n\n  ScopedMmap anon_mapping;\n  ASSERT_TRUE(anon_mapping.ResetMmap(seq_addr + page_size * 6,\n                                     page_size,\n                                     PROT_READ,\n                                     MAP_PRIVATE | MAP_ANON | MAP_FIXED,\n                                     -1,\n                                     0));\n\n  ScopedMmap file_mapping4;\n  ASSERT_TRUE(file_mapping4.ResetMmap(seq_addr + page_size * 7,\n                                      page_size,\n                                      PROT_READ,\n                                      MAP_PRIVATE | MAP_FIXED,\n                                      handle.get(),\n                                      0));\n\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n  MemoryMap map;\n  ASSERT_TRUE(map.Initialize(&connection));\n\n  auto mapping = map.FindMapping(file_mapping0.addr_as<VMAddress>());\n  ASSERT_TRUE(mapping);\n  auto possible_starts = map.FindFilePossibleMmapStarts(*mapping);\n#if BUILDFLAG(IS_ANDROID)\n  EXPECT_EQ(possible_starts->Count(), 1u);\n#else\n  EXPECT_EQ(possible_starts->Count(), 0u);\n#endif\n\n  mapping = map.FindMapping(file_mapping1.addr_as<VMAddress>());\n  ASSERT_TRUE(mapping);\n  possible_starts = map.FindFilePossibleMmapStarts(*mapping);\n#if BUILDFLAG(IS_ANDROID)\n  EXPECT_EQ(possible_starts->Count(), 2u);\n#else\n  EXPECT_EQ(possible_starts->Count(), 1u);\n#endif\n\n  mapping = map.FindMapping(file_mapping2.addr_as<VMAddress>());\n  ASSERT_TRUE(mapping);\n  possible_starts = map.FindFilePossibleMmapStarts(*mapping);\n#if BUILDFLAG(IS_ANDROID)\n  EXPECT_EQ(possible_starts->Count(), 3u);\n#else\n  EXPECT_EQ(possible_starts->Count(), 2u);\n#endif\n\n  mapping = map.FindMapping(file_mapping3.addr_as<VMAddress>());\n  ASSERT_TRUE(mapping);\n  possible_starts = map.FindFilePossibleMmapStarts(*mapping);\n#if BUILDFLAG(IS_ANDROID)\n  EXPECT_EQ(possible_starts->Count(), 4u);\n#else\n  EXPECT_EQ(possible_starts->Count(), 3u);\n#endif\n\n  mapping = map.FindMapping(file_mapping4.addr_as<VMAddress>());\n  ASSERT_TRUE(mapping);\n  possible_starts = map.FindFilePossibleMmapStarts(*mapping);\n#if BUILDFLAG(IS_ANDROID)\n  EXPECT_EQ(possible_starts->Count(), 5u);\n#else\n  EXPECT_EQ(possible_starts->Count(), 4u);\n#endif\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/pac_helper.cc",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/pac_helper.h\"\n\n#if defined(__has_feature)\n#define CRASHPAD_HAS_FEATURE(x) __has_feature(x)\n#else\n#define CRASHPAD_HAS_FEATURE(x) 0\n#endif\n\n#if CRASHPAD_HAS_FEATURE(ptrauth_intrinsics)\n  #include <ptrauth.h>\n#endif\n\n#include \"util/misc/address_types.h\"\n\nnamespace crashpad {\n\nVMAddress StripPACBits(VMAddress address) {\n#if CRASHPAD_HAS_FEATURE(ptrauth_intrinsics)\n    address = ptrauth_strip(address, ptrauth_key_function_pointer);\n#elif defined(ARCH_CPU_ARM64)\n    // Strip any pointer authentication bits that are assigned to the address.\n    register uintptr_t x30 __asm(\"x30\") = address;\n    asm(\"xpaclri\" : \"+r\"(x30));\n    address = x30;\n#endif\n    return address;\n}\n\n}  // namespace crashpad\n\n"
  },
  {
    "path": "util/linux/pac_helper.h",
    "content": "// Copyright 2023 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_PAC_HELPER_H_\n#define CRASHPAD_UTIL_LINUX_PAC_HELPER_H_\n\n#include \"util/misc/address_types.h\"\n\nnamespace crashpad {\n\n//! \\brief Strips PAC bits from an address\nVMAddress StripPACBits(VMAddress address);\n\n}  // namespace crashpad\n\n\n#endif  // CRASHPAD_UTIL_LINUX_PAC_HELPER_H_\n\n"
  },
  {
    "path": "util/linux/proc_stat_reader.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/proc_stat_reader.h\"\n\n#include <stdio.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <iterator>\n\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/lexing.h\"\n#include \"util/misc/time.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nlong GetClockTicksPerSecond() {\n  long clock_ticks_per_s = sysconf(_SC_CLK_TCK);\n  if (clock_ticks_per_s <= 0) {\n    PLOG(ERROR) << \"sysconf\";\n  }\n  return clock_ticks_per_s;\n}\n\n}  // namespace\n\nProcStatReader::ProcStatReader()\n    : contents_(), third_column_position_(0), initialized_() {}\n\nProcStatReader::~ProcStatReader() {}\n\nbool ProcStatReader::Initialize(PtraceConnection* connection, pid_t tid) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  char path[32];\n  snprintf(path, std::size(path), \"/proc/%d/stat\", tid);\n  if (!connection->ReadFileContents(base::FilePath(path), &contents_)) {\n    return false;\n  }\n\n  // The first column is process ID and the second column is the executable name\n  // in parentheses. This class only cares about columns after the second, so\n  // find the start of the third here and save it for later.\n  // The executable name may have parentheses itself, so find the end of the\n  // second column by working backwards to find the last closing parens.\n  size_t stat_pos = contents_.rfind(')');\n  if (stat_pos == std::string::npos) {\n    LOG(ERROR) << \"format error\";\n    return false;\n  }\n\n  third_column_position_ = contents_.find(' ', stat_pos);\n  if (third_column_position_ == std::string::npos ||\n      ++third_column_position_ >= contents_.size()) {\n    LOG(ERROR) << \"format error\";\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcStatReader::UserCPUTime(timeval* user_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ReadTimeAtIndex(13, user_time);\n}\n\nbool ProcStatReader::SystemCPUTime(timeval* system_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ReadTimeAtIndex(14, system_time);\n}\n\nbool ProcStatReader::StartTime(const timeval& boot_time,\n                               timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  timeval time_after_boot;\n  if (!ReadTimeAtIndex(21, &time_after_boot)) {\n    return false;\n  }\n\n  timeradd(&boot_time, &time_after_boot, start_time);\n  return true;\n}\n\nbool ProcStatReader::FindColumn(int col_index, const char** column) const {\n  size_t position = third_column_position_;\n  for (int index = 2; index < col_index; ++index) {\n    position = contents_.find(' ', position);\n    if (position == std::string::npos) {\n      break;\n    }\n    ++position;\n  }\n  if (position >= contents_.size()) {\n    LOG(ERROR) << \"format error\";\n    return false;\n  }\n  *column = &contents_[position];\n  return true;\n}\n\nbool ProcStatReader::ReadTimeAtIndex(int index, timeval* time_val) const {\n  const char* ticks_ptr;\n  if (!FindColumn(index, &ticks_ptr)) {\n    return false;\n  }\n\n  uint64_t ticks;\n  if (!AdvancePastNumber<uint64_t>(&ticks_ptr, &ticks)) {\n    LOG(ERROR) << \"format error\";\n    return false;\n  }\n\n  static long clock_ticks_per_s = GetClockTicksPerSecond();\n  if (clock_ticks_per_s <= 0) {\n    return false;\n  }\n\n  time_val->tv_sec = ticks / clock_ticks_per_s;\n  time_val->tv_usec = (ticks % clock_ticks_per_s) *\n                      (static_cast<long>(1E6) / clock_ticks_per_s);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/proc_stat_reader.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_\n#define CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_\n\n#include <stddef.h>\n#include <sys/time.h>\n#include <sys/types.h>\n\n#include <string>\n\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\n//! \\brief Reads the /proc/[pid]/stat file for a thread.\nclass ProcStatReader {\n public:\n  ProcStatReader();\n\n  ProcStatReader(const ProcStatReader&) = delete;\n  ProcStatReader& operator=(const ProcStatReader&) = delete;\n\n  ~ProcStatReader();\n\n  //! \\brief Initializes the reader.\n  //!\n  //! This method must be successfully called before calling any other.\n  //!\n  //! \\param[in] connection A connection to the process to which the target\n  //!     thread belongs.\n  //! \\param[in] tid The thread ID to read the stat file for.\n  bool Initialize(PtraceConnection* connection, pid_t tid);\n\n  //! \\brief Determines the time the thread has spent executing in user mode.\n  //!\n  //! \\param[out] user_time The time spent executing in user mode.\n  //!\n  //! \\return `true` on success, with \\a user_time set. Otherwise, `false` with\n  //!     a message logged.\n  bool UserCPUTime(timeval* user_time) const;\n\n  //! \\brief Determines the time the thread has spent executing in system mode.\n  //!\n  //! \\param[out] system_time The time spent executing in system mode.\n  //!\n  //! \\return `true` on success, with \\a system_time set. Otherwise, `false`\n  //!     with a message logged.\n  bool SystemCPUTime(timeval* system_time) const;\n\n  //! \\brief Determines the target thread’s start time.\n  //!\n  //! \\param[in] boot_time The kernel boot time.\n  //! \\param[out] start_time The time that the thread started.\n  //!\n  //! \\return `true` on success, with \\a start_time set. Otherwise, `false` with\n  //!     a message logged.\n  bool StartTime(const timeval& boot_time, timeval* start_time) const;\n\n private:\n  bool FindColumn(int index, const char** column) const;\n  bool ReadTimeAtIndex(int index, timeval* time_val) const;\n\n  std::string contents_;\n  size_t third_column_position_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_PROC_STAT_READER_H_\n"
  },
  {
    "path": "util/linux/proc_stat_reader_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/proc_stat_reader.h\"\n\n#include <sys/syscall.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"gtest/gtest.h\"\n#include \"test/linux/fake_ptrace_connection.h\"\n#include \"util/misc/time.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ProcStatReader, Basic) {\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  ProcStatReader stat;\n  ASSERT_TRUE(stat.Initialize(&connection, getpid()));\n\n  timespec boot_time_ts;\n  ASSERT_TRUE(GetBootTime(&boot_time_ts));\n  timeval boot_time;\n  TimespecToTimeval(boot_time_ts, &boot_time);\n\n  timeval start_time;\n  ASSERT_TRUE(stat.StartTime(boot_time, &start_time));\n\n  time_t now;\n  time(&now);\n  EXPECT_LE(start_time.tv_sec, now);\n\n  time_t elapsed_sec = now - start_time.tv_sec;\n\n  timeval user_time;\n  ASSERT_TRUE(stat.UserCPUTime(&user_time));\n  EXPECT_LE(user_time.tv_sec, elapsed_sec);\n\n  timeval system_time;\n  ASSERT_TRUE(stat.SystemCPUTime(&system_time));\n  EXPECT_LE(system_time.tv_sec, elapsed_sec);\n}\n\npid_t gettid() {\n  return syscall(SYS_gettid);\n}\n\nvoid GetStartTime(const timeval& boot_time, timeval* start_time) {\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n\n  ProcStatReader stat;\n  ASSERT_TRUE(stat.Initialize(&connection, gettid()));\n  ASSERT_TRUE(stat.StartTime(boot_time, start_time));\n}\n\nclass StatTimeThread : public Thread {\n public:\n  StatTimeThread(const timeval& boot_time, timeval* start_time)\n      : boot_time_(boot_time), start_time_(start_time) {}\n\n private:\n  void ThreadMain() override { GetStartTime(boot_time_, start_time_); }\n\n  const timeval& boot_time_;\n  timeval* start_time_;\n};\n\nTEST(ProcStatReader, Threads) {\n  timespec boot_time_ts;\n  ASSERT_TRUE(GetBootTime(&boot_time_ts));\n  timeval boot_time;\n  TimespecToTimeval(boot_time_ts, &boot_time);\n\n  timeval main_time;\n  ASSERT_NO_FATAL_FAILURE(GetStartTime(boot_time, &main_time));\n\n  timeval thread_time;\n  StatTimeThread thread(boot_time, &thread_time);\n  thread.Start();\n  ASSERT_NO_FATAL_FAILURE(thread.Join());\n\n  EXPECT_PRED4(\n      [](time_t main_sec,\n         suseconds_t main_usec,\n         time_t thread_sec,\n         suseconds_t thread_usec) {\n        return (thread_sec > main_sec) ||\n               (thread_sec == main_sec && thread_usec >= main_usec);\n      },\n      main_time.tv_sec,\n      main_time.tv_usec,\n      thread_time.tv_sec,\n      thread_time.tv_usec);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/proc_task_reader.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/proc_task_reader.h\"\n\n#include <stdio.h>\n\n#include <iterator>\n\n#include \"base/check_op.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/misc/as_underlying_type.h\"\n\nnamespace crashpad {\n\nbool ReadThreadIDs(pid_t pid, std::vector<pid_t>* tids) {\n  DCHECK(tids->empty());\n\n  char path[32];\n  snprintf(path, std::size(path), \"/proc/%d/task\", pid);\n  DirectoryReader reader;\n  if (!reader.Open(base::FilePath(path))) {\n    return false;\n  }\n\n  std::vector<pid_t> local_tids;\n  base::FilePath tid_str;\n  DirectoryReader::Result result;\n  while ((result = reader.NextFile(&tid_str)) ==\n         DirectoryReader::Result::kSuccess) {\n    pid_t tid;\n    if (!base::StringToInt(tid_str.value(), &tid)) {\n      LOG(ERROR) << \"format error\";\n      continue;\n    }\n\n    local_tids.push_back(tid);\n  }\n  DCHECK_EQ(AsUnderlyingType(result),\n            AsUnderlyingType(DirectoryReader::Result::kNoMoreFiles));\n  DCHECK(!local_tids.empty());\n\n  tids->swap(local_tids);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/proc_task_reader.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_PROC_TASK_READER_H_\n#define CRASHPAD_UTIL_LINUX_PROC_TASK_READER_H_\n\n#include <sys/types.h>\n\n#include <vector>\n\nnamespace crashpad {\n\n//! \\brief Enumerates the thread IDs of a process by reading\n//!     <code>/proc/<i>pid</i>/task</code>.\n//!\n//! \\param[in] pid The process ID for which to read thread IDs.\n//! \\param[out] tids The read thread IDs.\n//! \\return `true` if the task directory was successfully read. Format errors\n//!     are logged, but won't cause this function to return `false`.\nbool ReadThreadIDs(pid_t pid, std::vector<pid_t>* tids);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_PROC_TASK_READER_H_\n"
  },
  {
    "path": "util/linux/proc_task_reader_test.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/proc_task_reader.h\"\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"third_party/lss/lss.h\"\n#include \"util/synchronization/semaphore.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nbool FindThreadID(pid_t tid, const std::vector<pid_t>& threads) {\n  for (const auto& thread : threads) {\n    if (thread == tid) {\n      return true;\n    }\n  }\n  return false;\n}\n\nclass ScopedBlockingThread : public Thread {\n public:\n  ScopedBlockingThread() : tid_sem_(0), join_sem_(0), tid_(-1) {}\n\n  ~ScopedBlockingThread() {\n    join_sem_.Signal();\n    Join();\n  }\n\n  pid_t ThreadID() {\n    tid_sem_.Wait();\n    return tid_;\n  }\n\n private:\n  void ThreadMain() override {\n    tid_ = sys_gettid();\n    tid_sem_.Signal();\n    join_sem_.Wait();\n  }\n\n  Semaphore tid_sem_;\n  Semaphore join_sem_;\n  pid_t tid_;\n};\n\nTEST(ProcTaskReader, Self) {\n  std::vector<pid_t> tids;\n  ASSERT_TRUE(ReadThreadIDs(getpid(), &tids));\n  EXPECT_TRUE(FindThreadID(getpid(), tids));\n  EXPECT_TRUE(FindThreadID(sys_gettid(), tids));\n\n  ScopedBlockingThread thread1;\n  thread1.Start();\n\n  ScopedBlockingThread thread2;\n  thread2.Start();\n\n  pid_t thread1_tid = thread1.ThreadID();\n  pid_t thread2_tid = thread2.ThreadID();\n\n  tids.clear();\n  ASSERT_TRUE(ReadThreadIDs(getpid(), &tids));\n  EXPECT_TRUE(FindThreadID(getpid(), tids));\n  EXPECT_TRUE(FindThreadID(thread1_tid, tids));\n  EXPECT_TRUE(FindThreadID(thread2_tid, tids));\n}\n\nTEST(ProcTaskReader, BadPID) {\n  std::vector<pid_t> tids;\n  EXPECT_FALSE(ReadThreadIDs(-1, &tids));\n\n  tids.clear();\n  EXPECT_FALSE(ReadThreadIDs(0, &tids));\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ProcTaskTestChild) {\n  FileHandle in = StdioFileHandle(StdioStream::kStandardInput);\n  FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);\n\n  pid_t tid = getpid();\n  CheckedWriteFile(out, &tid, sizeof(tid));\n\n  tid = sys_gettid();\n  CheckedWriteFile(out, &tid, sizeof(tid));\n\n  ScopedBlockingThread thread1;\n  thread1.Start();\n\n  ScopedBlockingThread thread2;\n  thread2.Start();\n\n  tid = thread1.ThreadID();\n  CheckedWriteFile(out, &tid, sizeof(tid));\n\n  tid = thread2.ThreadID();\n  CheckedWriteFile(out, &tid, sizeof(tid));\n\n  CheckedReadFileAtEOF(in);\n  return 0;\n}\n\nclass ProcTaskTest : public MultiprocessExec {\n public:\n  ProcTaskTest() : MultiprocessExec() {\n    SetChildTestMainFunction(\"ProcTaskTestChild\");\n  }\n\n  ProcTaskTest(const ProcTaskTest&) = delete;\n  ProcTaskTest& operator=(const ProcTaskTest&) = delete;\n\n private:\n  bool ReadIDFromChild(std::vector<pid_t>* threads) {\n    pid_t tid;\n    if (!LoggingReadFileExactly(ReadPipeHandle(), &tid, sizeof(tid))) {\n      return false;\n    }\n    threads->push_back(tid);\n    return true;\n  }\n\n  void MultiprocessParent() override {\n    std::vector<pid_t> ids_to_find;\n    for (size_t id_count = 0; id_count < 4; ++id_count) {\n      ASSERT_TRUE(ReadIDFromChild(&ids_to_find));\n    }\n\n    std::vector<pid_t> threads;\n    ASSERT_TRUE(ReadThreadIDs(ChildPID(), &threads));\n    for (size_t index = 0; index < ids_to_find.size(); ++index) {\n      SCOPED_TRACE(\n          base::StringPrintf(\"index %zd, tid %d\", index, ids_to_find[index]));\n      EXPECT_TRUE(FindThreadID(ids_to_find[index], threads));\n    }\n  }\n};\n\nTEST(ProcTaskReader, ReadChild) {\n  ProcTaskTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/ptrace_broker.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/ptrace_broker.h\"\n\n#include <fcntl.h>\n#include <limits.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <syscall.h>\n#include <unistd.h>\n\n#include <algorithm>\n\n#include \"base/check_op.h\"\n#include \"base/memory/page_size.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"third_party/lss/lss.h\"\n#include \"util/linux/scoped_ptrace_attach.h\"\n#include \"util/misc/memory_sanitizer.h\"\n#include \"util/posix/scoped_mmap.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nsize_t FormatPID(char* buffer, pid_t pid) {\n  DCHECK_GE(pid, 0);\n\n  char pid_buf[16];\n  size_t length = 0;\n  do {\n    DCHECK_LT(length, sizeof(pid_buf));\n\n    pid_buf[length] = '0' + pid % 10;\n    pid /= 10;\n    ++length;\n  } while (pid > 0);\n\n  for (size_t index = 0; index < length; ++index) {\n    buffer[index] = pid_buf[length - index - 1];\n  }\n\n  return length;\n}\n\n}  // namespace\n\nclass PtraceBroker::AttachmentsArray {\n public:\n  AttachmentsArray() : allocation_(false), attach_count_(0) {}\n\n  AttachmentsArray(const AttachmentsArray&) = delete;\n  AttachmentsArray& operator=(const AttachmentsArray&) = delete;\n\n  ~AttachmentsArray() {\n    for (size_t index = 0; index < attach_count_; ++index) {\n      PtraceDetach(Attachments()[index], false);\n    }\n  }\n\n  bool Initialize() {\n    return allocation_.ResetMmap(nullptr,\n                                 base::GetPageSize(),\n                                 PROT_READ | PROT_WRITE,\n                                 MAP_PRIVATE | MAP_ANONYMOUS,\n                                 -1,\n                                 0);\n  }\n\n  bool Attach(pid_t pid) {\n    pid_t* attach = AllocateAttachment();\n    if (!attach || !PtraceAttach(pid, false)) {\n      return false;\n    }\n\n    *attach = pid;\n    return true;\n  }\n\n private:\n  pid_t* AllocateAttachment() {\n    if (attach_count_ >= (allocation_.len() / sizeof(pid_t))) {\n      return nullptr;\n    }\n    return &Attachments()[attach_count_++];\n  }\n\n  pid_t* Attachments() { return allocation_.addr_as<pid_t*>(); }\n\n  ScopedMmap allocation_;\n  size_t attach_count_;\n};\n\nPtraceBroker::PtraceBroker(int sock, pid_t pid, bool is_64_bit)\n    : ptracer_(is_64_bit, /* can_log= */ false),\n      file_root_(file_root_buffer_),\n      memory_file_(),\n      sock_(sock),\n      memory_pid_(pid),\n      tried_opening_mem_file_(false) {\n  static constexpr char kProc[] = \"/proc/\";\n  size_t root_length = strlen(kProc);\n  memcpy(file_root_buffer_, kProc, root_length);\n\n  if (pid >= 0) {\n    root_length += FormatPID(file_root_buffer_ + root_length, pid);\n    DCHECK_LT(root_length, sizeof(file_root_buffer_));\n    file_root_buffer_[root_length] = '/';\n    ++root_length;\n  }\n\n  DCHECK_LT(root_length, sizeof(file_root_buffer_));\n  file_root_buffer_[root_length] = '\\0';\n}\n\nPtraceBroker::~PtraceBroker() = default;\n\nvoid PtraceBroker::SetFileRoot(const char* new_root) {\n  DCHECK_EQ(new_root[strlen(new_root) - 1], '/');\n  memory_pid_ = -1;\n  file_root_ = new_root;\n}\n\nint PtraceBroker::Run() {\n  AttachmentsArray attachments;\n  attachments.Initialize();\n  return RunImpl(&attachments);\n}\n\nint PtraceBroker::RunImpl(AttachmentsArray* attachments) {\n  while (true) {\n    Request request = {};\n    if (!ReadFileExactly(sock_, &request, sizeof(request))) {\n      return errno;\n    }\n\n    if (request.version != Request::kVersion) {\n      return EINVAL;\n    }\n\n    switch (request.type) {\n      case Request::kTypeAttach: {\n        ExceptionHandlerProtocol::Bool status =\n            attachments->Attach(request.tid)\n                ? ExceptionHandlerProtocol::kBoolTrue\n                : ExceptionHandlerProtocol::kBoolFalse;\n\n        if (!WriteFile(sock_, &status, sizeof(status))) {\n          return errno;\n        }\n\n        if (status == ExceptionHandlerProtocol::kBoolFalse) {\n          ExceptionHandlerProtocol::Errno error = errno;\n          if (!WriteFile(sock_, &error, sizeof(error))) {\n            return errno;\n          }\n        }\n\n        continue;\n      }\n\n      case Request::kTypeIs64Bit: {\n        ExceptionHandlerProtocol::Bool is_64_bit =\n            ptracer_.Is64Bit() ? ExceptionHandlerProtocol::kBoolTrue\n                               : ExceptionHandlerProtocol::kBoolFalse;\n        if (!WriteFile(sock_, &is_64_bit, sizeof(is_64_bit))) {\n          return errno;\n        }\n        continue;\n      }\n\n      case Request::kTypeGetThreadInfo: {\n        GetThreadInfoResponse response;\n        response.success = ptracer_.GetThreadInfo(request.tid, &response.info)\n                               ? ExceptionHandlerProtocol::kBoolTrue\n                               : ExceptionHandlerProtocol::kBoolFalse;\n\n        if (!WriteFile(sock_, &response, sizeof(response))) {\n          return errno;\n        }\n\n        if (response.success == ExceptionHandlerProtocol::kBoolFalse) {\n          ExceptionHandlerProtocol::Errno error = errno;\n          if (!WriteFile(sock_, &error, sizeof(error))) {\n            return errno;\n          }\n        }\n        continue;\n      }\n\n      case Request::kTypeReadFile: {\n        ScopedFileHandle handle;\n        int result = ReceiveAndOpenFilePath(request.path.path_length,\n                                            /* is_directory= */ false,\n                                            &handle);\n        if (result != 0) {\n          return result;\n        }\n\n        if (!handle.is_valid()) {\n          continue;\n        }\n\n        result = SendFileContents(handle.get());\n        if (result != 0) {\n          return result;\n        }\n        continue;\n      }\n\n      case Request::kTypeReadMemory: {\n        int result =\n            SendMemory(request.tid, request.iov.base, request.iov.size);\n        if (result != 0) {\n          return result;\n        }\n        continue;\n      }\n\n      case Request::kTypeListDirectory: {\n        ScopedFileHandle handle;\n        int result = ReceiveAndOpenFilePath(request.path.path_length,\n                                            /* is_directory= */ true,\n                                            &handle);\n        if (result != 0) {\n          return result;\n        }\n\n        if (!handle.is_valid()) {\n          continue;\n        }\n\n        result = SendDirectory(handle.get());\n        if (result != 0) {\n          return result;\n        }\n        continue;\n      }\n\n      case Request::kTypeExit:\n        return 0;\n    }\n\n    DCHECK(false);\n    return EINVAL;\n  }\n}\n\nint PtraceBroker::SendError(ExceptionHandlerProtocol::Errno err) {\n  return WriteFile(sock_, &err, sizeof(err)) ? 0 : errno;\n}\n\nint PtraceBroker::SendReadError(ReadError error) {\n  int32_t rv = -1;\n  return WriteFile(sock_, &rv, sizeof(rv)) &&\n                 WriteFile(sock_, &error, sizeof(error))\n             ? 0\n             : errno;\n}\n\nint PtraceBroker::SendOpenResult(OpenResult result) {\n  return WriteFile(sock_, &result, sizeof(result)) ? 0 : errno;\n}\n\nint PtraceBroker::SendFileContents(FileHandle handle) {\n  char buffer[4096];\n  int32_t rv;\n  do {\n    rv = ReadFile(handle, buffer, sizeof(buffer));\n\n    if (rv < 0) {\n      return SendReadError(static_cast<ReadError>(errno));\n    }\n\n    if (!WriteFile(sock_, &rv, sizeof(rv))) {\n      return errno;\n    }\n\n    if (rv > 0) {\n      if (!WriteFile(sock_, buffer, static_cast<size_t>(rv))) {\n        return errno;\n      }\n    }\n  } while (rv > 0);\n\n  return 0;\n}\n\nvoid PtraceBroker::TryOpeningMemFile() {\n  if (tried_opening_mem_file_) {\n    return;\n  }\n  tried_opening_mem_file_ = true;\n\n  if (memory_pid_ < 0) {\n    return;\n  }\n\n  char mem_path[32];\n  size_t root_length = strlen(file_root_buffer_);\n  static constexpr char kMem[] = \"mem\";\n\n  DCHECK_LT(root_length + strlen(kMem) + 1, sizeof(mem_path));\n  memcpy(mem_path, file_root_buffer_, root_length);\n  // Include the trailing NUL.\n  memcpy(mem_path + root_length, kMem, strlen(kMem) + 1);\n  memory_file_.reset(\n      HANDLE_EINTR(open(mem_path, O_RDONLY | O_CLOEXEC | O_NOCTTY)));\n}\n\nint PtraceBroker::SendMemory(pid_t pid, VMAddress address, VMSize size) {\n  if (memory_pid_ >= 0 && pid != memory_pid_) {\n    return SendReadError(kReadErrorAccessDenied);\n  }\n\n  TryOpeningMemFile();\n  auto read_memory = [this, pid](VMAddress address, size_t size, char* buffer) {\n    return this->memory_file_.is_valid()\n               ? HANDLE_EINTR(\n                     pread64(this->memory_file_.get(), buffer, size, address))\n               : this->ptracer_.ReadUpTo(pid, address, size, buffer);\n  };\n\n  char buffer[4096];\n  while (size > 0) {\n    size_t to_read = std::min(size, VMSize{sizeof(buffer)});\n\n    int32_t bytes_read = read_memory(address, to_read, buffer);\n\n    if (bytes_read < 0) {\n      return SendReadError(static_cast<ReadError>(errno));\n    }\n\n    if (!WriteFile(sock_, &bytes_read, sizeof(bytes_read))) {\n      return errno;\n    }\n\n    if (bytes_read == 0) {\n      return 0;\n    }\n\n    if (!WriteFile(sock_, buffer, bytes_read)) {\n      return errno;\n    }\n\n    size -= bytes_read;\n    address += bytes_read;\n  }\n  return 0;\n}\n\n#if defined(MEMORY_SANITIZER)\n// MSan doesn't intercept syscall() and doesn't see that buffer is initialized.\n__attribute__((no_sanitize(\"memory\")))\n#endif  // defined(MEMORY_SANITIZER)\nint PtraceBroker::SendDirectory(FileHandle handle) {\n  char buffer[4096];\n  int rv;\n  do {\n    rv = syscall(SYS_getdents64, handle, buffer, sizeof(buffer));\n\n    if (rv < 0) {\n      return SendReadError(static_cast<ReadError>(errno));\n    }\n\n    if (!WriteFile(sock_, &rv, sizeof(rv))) {\n      return errno;\n    }\n\n    if (rv > 0) {\n      if (!WriteFile(sock_, buffer, static_cast<size_t>(rv))) {\n        return errno;\n      }\n    }\n  } while (rv > 0);\n\n  return 0;\n}\n\nint PtraceBroker::ReceiveAndOpenFilePath(VMSize path_length,\n                                         bool is_directory,\n                                         ScopedFileHandle* handle) {\n  char path[std::max(4096, PATH_MAX)];\n\n  if (path_length >= sizeof(path)) {\n    return SendOpenResult(kOpenResultTooLong);\n  }\n\n  if (!ReadFileExactly(sock_, path, path_length)) {\n    return errno;\n  }\n  path[path_length] = '\\0';\n\n  if (strncmp(path, file_root_, strlen(file_root_)) != 0) {\n    return SendOpenResult(kOpenResultAccessDenied);\n  }\n\n  int flags = O_RDONLY | O_CLOEXEC | O_NOCTTY;\n  if (is_directory) {\n    flags |= O_DIRECTORY;\n  }\n  ScopedFileHandle local_handle(HANDLE_EINTR(open(path, flags)));\n  if (!local_handle.is_valid()) {\n    return SendOpenResult(static_cast<OpenResult>(errno));\n  }\n\n  handle->reset(local_handle.release());\n  return SendOpenResult(kOpenResultSuccess);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/ptrace_broker.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_PTRACE_BROKER_H_\n#define CRASHPAD_UTIL_LINUX_PTRACE_BROKER_H_\n\n#include <errno.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include \"util/file/file_io.h\"\n#include \"util/linux/exception_handler_protocol.h\"\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/linux/ptracer.h\"\n#include \"util/linux/thread_info.h\"\n#include \"util/misc/address_types.h\"\n\nnamespace crashpad {\n\n//! \\brief Implements a PtraceConnection over a socket.\n//!\n//! This class is the server half of the connection. The broker should be run\n//! in a process with `ptrace` capabilities for the target process and may run\n//! in a compromised context.\nclass PtraceBroker {\n public:\n#pragma pack(push, 1)\n  //! \\brief A request sent to a PtraceBroker from a PtraceClient.\n  struct Request {\n    static constexpr uint16_t kVersion = 1;\n\n    //! \\brief The version number for this Request.\n    uint16_t version = kVersion;\n\n    //! \\brief The type of request to serve.\n    enum Type : uint16_t {\n      //! \\brief `ptrace`-attach the specified thread ID. Responds with\n      //!     kBoolTrue on success, otherwise kBoolFalse, followed by an Errno.\n      kTypeAttach,\n\n      //! \\brief Responds with kBoolTrue if the target process is 64-bit.\n      //!     Otherwise, kBoolFalse.\n      kTypeIs64Bit,\n\n      //! \\brief Responds with a GetThreadInfoResponse containing a ThreadInfo\n      //!     for the specified thread ID. If an error occurs,\n      //!     GetThreadInfoResponse::success is set to kBoolFalse and is\n      //!     followed by an Errno.\n      kTypeGetThreadInfo,\n\n      //! \\brief Reads memory from the attached process. The data is returned in\n      //!     a series of messages. Each message begins with an int32_t\n      //!     indicating the number of bytes read, 0 for end-of-file, or -1 for\n      //!     errors, followed by a ReadError. On success the bytes read follow.\n      kTypeReadMemory,\n\n      //! \\brief Read a file's contents. The data is returned in a series of\n      //!     messages. The first message is an OpenResult, indicating the\n      //!     validity of the received file path. If the OpenResult is\n      //!     kOpenResultSuccess, each subsequent message begins with an int32_t\n      //!     indicating the number of bytes read, 0 for end-of-file, or -1 for\n      //!     errors, followed by an Errno. On success, the bytes read follow.\n      kTypeReadFile,\n\n      //! \\brief Reads the contents of a directory. The data is returned in a\n      //!     series of messages. The first message is an OpenResult, indicating\n      //!     the validity of the received file path. If the OpenResult is\n      //!     kOpenResultSuccess, the subsequent messages return the contents of\n      //!     the directory as a dirent stream, as read by `getdents64()`. Each\n      //!     subsequent message begins with an int32_t indicating the number of\n      //!     bytes read, 0 for end-of-file, or -1 for errors, followed by an\n      //!     Errno. On success, the bytes read follow.\n      kTypeListDirectory,\n\n      //! \\brief Causes the broker to return from Run(), detaching all attached\n      //!     threads. Does not respond.\n      kTypeExit\n    } type;\n\n    //! \\brief The thread ID associated with this request. Valid for kTypeAttach,\n    //!     kTypeGetThreadInfo, and kTypeReadMemory.\n    pid_t tid;\n\n    union {\n      //! \\brief Specifies the memory region to read for a kTypeReadMemory\n      //! request.\n      struct {\n        //! \\brief The base address of the memory region.\n        VMAddress base;\n\n        //! \\brief The size of the memory region.\n        VMSize size;\n      } iov;\n\n      //! \\brief Specifies the file path to read for a kTypeReadFile request.\n      struct {\n        //! \\brief The number of bytes in #path. The path should not include a\n        //!     `NUL`-terminator.\n        VMSize path_length;\n\n        //! \\brief The file path to read.\n        char path[];\n      } path;\n    };\n  };\n\n  //! \\brief A result used in operations that accept paths.\n  //!\n  //! Positive values of this enum are reserved for sending errno values.\n  enum OpenResult : int32_t {\n    //! \\brief Access to the path is denied.\n    kOpenResultAccessDenied = -2,\n\n    //! \\brief The path name is too long.\n    kOpenResultTooLong = -1,\n\n    //! \\brief The file was successfully opened.\n    kOpenResultSuccess = 0,\n  };\n\n  //! \\brief A result used in operations that read from memory or files.\n  //!\n  //! Positive values of this enum are reserved for sending errno values.\n  enum ReadError : int32_t {\n    //! \\brief Access to this data is denied.\n    kReadErrorAccessDenied = -1,\n  };\n\n  //! \\brief The response sent for a Request with type kTypeGetThreadInfo.\n  struct GetThreadInfoResponse {\n    //! \\brief Information about the specified thread. Only valid if #success\n    //!     is kBoolTrue.\n    ThreadInfo info;\n\n    //! \\brief Specifies the success or failure of this call.\n    ExceptionHandlerProtocol::Bool success;\n  };\n#pragma pack(pop)\n\n  //! \\brief Constructs this object.\n  //!\n  //! \\param[in] sock A socket on which to read requests from a connected\n  //!     PtraceClient. Does not take ownership of the socket.\n  //! \\param[in] pid The process ID of the process the broker is expected to\n  //!     trace. Setting this value exends the default file root to\n  //!     \"/proc/[pid]/\" and enables memory reading via /proc/[pid]/mem. The\n  //!     broker will deny any requests to read memory from processes whose\n  //!     processID is not \\a pid. If pid is -1, the broker will serve requests\n  //!     to read memory from any process it is able to via `ptrace PEEKDATA`.\n  //! \\param[in] is_64_bit Whether this broker should be configured to trace a\n  //!     64-bit process.\n  PtraceBroker(int sock, pid_t pid, bool is_64_bit);\n\n  PtraceBroker(const PtraceBroker&) = delete;\n  PtraceBroker& operator=(const PtraceBroker&) = delete;\n\n  ~PtraceBroker();\n\n  //! \\brief Restricts the broker to serving the contents of files under \\a\n  //!     root.\n  //!\n  //! If this method is not called, the broker defaults to only serving files\n  //! under \"/proc/\" or \"/proc/[pid]/\" if a pid was set.\n  //!\n  //! Calling this function disables reading from a memory file if one has not\n  //! already been opened.\n  //!\n  //! \\param[in] root A NUL-terminated c-string containing the path to the new\n  //!     root. \\a root must not be `nullptr`, must end in a '/', and the caller\n  //!     should ensure that \\a root remains valid for the lifetime of the\n  //!     broker.\n  void SetFileRoot(const char* root);\n\n  //! \\brief Begin serving requests on the configured socket.\n  //!\n  //! This method returns when a PtraceBrokerRequest with type kTypeExit is\n  //! received or an error is encountered on the socket.\n  //!\n  //! \\return 0 if Run() exited due to an exit request. Otherwise an error code.\n  int Run();\n\n private:\n  class AttachmentsArray;\n\n  int RunImpl(AttachmentsArray*);\n  int SendError(ExceptionHandlerProtocol::Errno err);\n  int SendReadError(ReadError err);\n  int SendOpenResult(OpenResult result);\n  int SendFileContents(FileHandle handle);\n  int SendDirectory(FileHandle handle);\n  void TryOpeningMemFile();\n  int SendMemory(pid_t pid, VMAddress address, VMSize size);\n  int ReceiveAndOpenFilePath(VMSize path_length,\n                             bool is_directory,\n                             ScopedFileHandle* handle);\n\n  char file_root_buffer_[32];\n  Ptracer ptracer_;\n  const char* file_root_;\n  ScopedFileHandle memory_file_;\n  int sock_;\n  pid_t memory_pid_;\n  bool tried_opening_mem_file_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_PTRACE_BROKER_H_\n"
  },
  {
    "path": "util/linux/ptrace_broker_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/ptrace_broker.h\"\n\n#include <sys/socket.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <utility>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/filesystem.h\"\n#include \"test/linux/get_tls.h\"\n#include \"test/multiprocess.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_io.h\"\n#include \"util/linux/ptrace_client.h\"\n#include \"util/posix/scoped_mmap.h\"\n#include \"util/synchronization/semaphore.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass ScopedTimeoutThread : public Thread {\n public:\n  ScopedTimeoutThread() : join_sem_(0) {}\n\n  ScopedTimeoutThread(const ScopedTimeoutThread&) = delete;\n  ScopedTimeoutThread& operator=(const ScopedTimeoutThread&) = delete;\n\n  ~ScopedTimeoutThread() { EXPECT_TRUE(JoinWithTimeout(5.0)); }\n\n protected:\n  void ThreadMain() override { join_sem_.Signal(); }\n\n private:\n  bool JoinWithTimeout(double timeout) {\n    if (!join_sem_.TimedWait(timeout)) {\n      return false;\n    }\n    Join();\n    return true;\n  }\n\n  Semaphore join_sem_;\n};\n\nclass RunBrokerThread : public ScopedTimeoutThread {\n public:\n  RunBrokerThread(PtraceBroker* broker)\n      : ScopedTimeoutThread(), broker_(broker) {}\n\n  RunBrokerThread(const RunBrokerThread&) = delete;\n  RunBrokerThread& operator=(const RunBrokerThread&) = delete;\n\n  ~RunBrokerThread() {}\n\n private:\n  void ThreadMain() override {\n    EXPECT_EQ(broker_->Run(), 0);\n    ScopedTimeoutThread::ThreadMain();\n  }\n\n  PtraceBroker* broker_;\n};\n\nclass BlockOnReadThread : public ScopedTimeoutThread {\n public:\n  BlockOnReadThread(int readfd, int writefd)\n      : ScopedTimeoutThread(), readfd_(readfd), writefd_(writefd) {}\n\n  BlockOnReadThread(const BlockOnReadThread&) = delete;\n  BlockOnReadThread& operator=(const BlockOnReadThread&) = delete;\n\n  ~BlockOnReadThread() {}\n\n private:\n  void ThreadMain() override {\n    pid_t pid = syscall(SYS_gettid);\n    LoggingWriteFile(writefd_, &pid, sizeof(pid));\n\n    LinuxVMAddress tls = GetTLS();\n    LoggingWriteFile(writefd_, &tls, sizeof(tls));\n\n    CheckedReadFileAtEOF(readfd_);\n    ScopedTimeoutThread::ThreadMain();\n  }\n\n  int readfd_;\n  int writefd_;\n};\n\nclass SameBitnessTest : public Multiprocess {\n public:\n  SameBitnessTest() : Multiprocess(), mapping_() {}\n\n  SameBitnessTest(const SameBitnessTest&) = delete;\n  SameBitnessTest& operator=(const SameBitnessTest&) = delete;\n\n  ~SameBitnessTest() {}\n\n protected:\n  void PreFork() override {\n    ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork());\n\n    size_t page_size = getpagesize();\n    ASSERT_TRUE(mapping_.ResetMmap(nullptr,\n                                   page_size * 3,\n                                   PROT_READ | PROT_WRITE,\n                                   MAP_PRIVATE | MAP_ANON,\n                                   -1,\n                                   0));\n    ASSERT_TRUE(mapping_.ResetAddrLen(mapping_.addr(), page_size * 2));\n\n    auto buffer = mapping_.addr_as<char*>();\n    for (size_t index = 0; index < mapping_.len(); ++index) {\n      buffer[index] = index % 256;\n    }\n  }\n\n private:\n  void BrokerTests(bool set_broker_pid,\n                   LinuxVMAddress child1_tls,\n                   LinuxVMAddress child2_tls,\n                   pid_t child2_tid,\n                   const base::FilePath& file_dir,\n                   const base::FilePath& test_file,\n                   const std::string& expected_file_contents) {\n    int socks[2];\n    ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0);\n    ScopedFileHandle broker_sock(socks[0]);\n    ScopedFileHandle client_sock(socks[1]);\n\n#if defined(ARCH_CPU_64_BITS)\n    constexpr bool am_64_bit = true;\n#else\n    constexpr bool am_64_bit = false;\n#endif  // ARCH_CPU_64_BITS\n\n    PtraceBroker broker(\n        broker_sock.get(), set_broker_pid ? ChildPID() : -1, am_64_bit);\n    RunBrokerThread broker_thread(&broker);\n    broker_thread.Start();\n\n    PtraceClient client;\n    ASSERT_TRUE(client.Initialize(client_sock.get(), ChildPID()));\n\n    EXPECT_EQ(client.GetProcessID(), ChildPID());\n\n    std::vector<pid_t> threads;\n    ASSERT_TRUE(client.Threads(&threads));\n    EXPECT_EQ(threads.size(), 2u);\n    if (threads[0] == ChildPID()) {\n      EXPECT_EQ(threads[1], child2_tid);\n    } else {\n      EXPECT_EQ(threads[0], child2_tid);\n      EXPECT_EQ(threads[1], ChildPID());\n    }\n\n    EXPECT_TRUE(client.Attach(child2_tid));\n    EXPECT_EQ(client.Is64Bit(), am_64_bit);\n\n    ThreadInfo info1;\n    ASSERT_TRUE(client.GetThreadInfo(ChildPID(), &info1));\n    EXPECT_EQ(info1.thread_specific_data_address, child1_tls);\n\n    ThreadInfo info2;\n    ASSERT_TRUE(client.GetThreadInfo(child2_tid, &info2));\n    EXPECT_EQ(info2.thread_specific_data_address, child2_tls);\n\n    auto expected_buffer = mapping_.addr_as<char*>();\n    char first;\n    ASSERT_EQ(\n        client.ReadUpTo(mapping_.addr_as<VMAddress>(), sizeof(first), &first),\n        1);\n    EXPECT_EQ(first, expected_buffer[0]);\n\n    char last;\n    ASSERT_EQ(\n        client.ReadUpTo(mapping_.addr_as<VMAddress>() + mapping_.len() - 1,\n                        sizeof(last),\n                        &last),\n        1);\n    EXPECT_EQ(last, expected_buffer[mapping_.len() - 1]);\n\n    char unmapped;\n    EXPECT_EQ(client.ReadUpTo(mapping_.addr_as<VMAddress>() + mapping_.len(),\n                              sizeof(unmapped),\n                              &unmapped),\n              -1);\n\n    std::string file_root = file_dir.value() + '/';\n    broker.SetFileRoot(file_root.c_str());\n\n    std::string file_contents;\n    ASSERT_TRUE(client.ReadFileContents(test_file, &file_contents));\n    EXPECT_EQ(file_contents, expected_file_contents);\n\n    ScopedTempDir temp_dir2;\n    base::FilePath test_file2(temp_dir2.path().Append(\"test_file2\"));\n    ASSERT_TRUE(CreateFile(test_file2));\n    EXPECT_FALSE(client.ReadFileContents(test_file2, &file_contents));\n  }\n\n  void MultiprocessParent() override {\n    LinuxVMAddress child1_tls;\n    ASSERT_TRUE(LoggingReadFileExactly(\n        ReadPipeHandle(), &child1_tls, sizeof(child1_tls)));\n\n    pid_t child2_tid;\n    ASSERT_TRUE(LoggingReadFileExactly(\n        ReadPipeHandle(), &child2_tid, sizeof(child2_tid)));\n\n    LinuxVMAddress child2_tls;\n    ASSERT_TRUE(LoggingReadFileExactly(\n        ReadPipeHandle(), &child2_tls, sizeof(child2_tls)));\n\n    ScopedTempDir temp_dir;\n    base::FilePath file_path(temp_dir.path().Append(\"test_file\"));\n    std::string expected_file_contents;\n    {\n      expected_file_contents.resize(4097);\n      for (size_t i = 0; i < expected_file_contents.size(); ++i) {\n        expected_file_contents[i] = static_cast<char>(i % 256);\n      }\n      ScopedFileHandle handle(\n          LoggingOpenFileForWrite(file_path,\n                                  FileWriteMode::kCreateOrFail,\n                                  FilePermissions::kWorldReadable));\n      ASSERT_TRUE(LoggingWriteFile(handle.get(),\n                                   expected_file_contents.data(),\n                                   expected_file_contents.size()));\n    }\n\n    BrokerTests(true,\n                child1_tls,\n                child2_tls,\n                child2_tid,\n                temp_dir.path(),\n                file_path,\n                expected_file_contents);\n    BrokerTests(false,\n                child1_tls,\n                child2_tls,\n                child2_tid,\n                temp_dir.path(),\n                file_path,\n                expected_file_contents);\n  }\n\n  void MultiprocessChild() override {\n    LinuxVMAddress tls = GetTLS();\n    ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &tls, sizeof(tls)));\n\n    BlockOnReadThread thread(ReadPipeHandle(), WritePipeHandle());\n    thread.Start();\n\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  ScopedMmap mapping_;\n};\n\n// TODO(https://crbug.com/1459865): This test consistently fails on ASAN/LSAN\n// but it's not clear if this test is correct in the general case (see comment 2\n// on that issue).\nTEST(PtraceBroker, DISABLED_SameBitness) {\n  SameBitnessTest test;\n  test.Run();\n}\n\n// TODO(jperaza): Test against a process with different bitness.\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/ptrace_client.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/ptrace_client.h\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n\n#include <iterator>\n#include <string>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"util/file/file_io.h\"\n#include \"util/linux/ptrace_broker.h\"\n#include \"util/process/process_memory_linux.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nbool ReceiveAndLogError(int sock, const std::string& operation) {\n  ExceptionHandlerProtocol::Errno error;\n  if (!LoggingReadFileExactly(sock, &error, sizeof(error))) {\n    return false;\n  }\n  errno = error;\n  PLOG(ERROR) << operation;\n  return true;\n}\n\nbool ReceiveAndLogReadError(int sock, const std::string& operation) {\n  PtraceBroker::ReadError err;\n  if (!LoggingReadFileExactly(sock, &err, sizeof(err))) {\n    return false;\n  }\n  switch (err) {\n    case PtraceBroker::kReadErrorAccessDenied:\n      LOG(ERROR) << operation << \" access denied\";\n      return true;\n    default:\n      if (err <= 0) {\n        LOG(ERROR) << operation << \" invalid error \" << err;\n        DCHECK(false);\n        return false;\n      }\n      errno = err;\n      PLOG(ERROR) << operation;\n      return true;\n  }\n}\n\nbool AttachImpl(int sock, pid_t tid) {\n  PtraceBroker::Request request = {};\n  request.type = PtraceBroker::Request::kTypeAttach;\n  request.tid = tid;\n  if (!LoggingWriteFile(sock, &request, sizeof(request))) {\n    return false;\n  }\n\n  ExceptionHandlerProtocol::Bool success;\n  if (!LoggingReadFileExactly(sock, &success, sizeof(success))) {\n    return false;\n  }\n\n  if (success != ExceptionHandlerProtocol::kBoolTrue) {\n    ReceiveAndLogError(sock, \"PtraceBroker Attach\");\n    return false;\n  }\n\n  return true;\n}\n\nstruct Dirent64 {\n  ino64_t d_ino;\n  off64_t d_off;\n  unsigned short d_reclen;\n  unsigned char d_type;\n  char d_name[];\n};\n\nvoid ReadDentsAsThreadIDs(char* buffer,\n                          size_t size,\n                          std::vector<pid_t>* threads) {\n  while (size > offsetof(Dirent64, d_name)) {\n    auto dirent = reinterpret_cast<Dirent64*>(buffer);\n    if (size < dirent->d_reclen) {\n      LOG(ERROR) << \"short dirent\";\n      break;\n    }\n    buffer += dirent->d_reclen;\n    size -= dirent->d_reclen;\n\n    const size_t max_name_length =\n        dirent->d_reclen - offsetof(Dirent64, d_name);\n    size_t name_len = strnlen(dirent->d_name, max_name_length);\n    if (name_len >= max_name_length) {\n      LOG(ERROR) << \"format error\";\n      break;\n    }\n\n    if (strcmp(dirent->d_name, \".\") == 0 || strcmp(dirent->d_name, \"..\") == 0) {\n      continue;\n    }\n\n    pid_t tid;\n    if (!base::StringToInt(dirent->d_name, &tid)) {\n      LOG(ERROR) << \"format error\";\n      continue;\n    }\n    threads->push_back(tid);\n  }\n  DCHECK_EQ(size, 0u);\n}\n\n}  // namespace\n\nPtraceClient::PtraceClient()\n    : PtraceConnection(),\n      memory_(),\n      sock_(kInvalidFileHandle),\n      pid_(-1),\n      is_64_bit_(false),\n      initialized_() {}\n\nPtraceClient::~PtraceClient() {\n  if (sock_ != kInvalidFileHandle) {\n    PtraceBroker::Request request = {};\n    request.type = PtraceBroker::Request::kTypeExit;\n    LoggingWriteFile(sock_, &request, sizeof(request));\n  }\n}\n\nbool PtraceClient::Initialize(int sock, pid_t pid) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  sock_ = sock;\n  pid_ = pid;\n\n  if (!AttachImpl(sock_, pid_)) {\n    return false;\n  }\n\n  PtraceBroker::Request request = {};\n  request.type = PtraceBroker::Request::kTypeIs64Bit;\n  request.tid = pid_;\n\n  if (!LoggingWriteFile(sock_, &request, sizeof(request))) {\n    return false;\n  }\n\n  ExceptionHandlerProtocol::Bool is_64_bit;\n  if (!LoggingReadFileExactly(sock_, &is_64_bit, sizeof(is_64_bit))) {\n    return false;\n  }\n  is_64_bit_ = is_64_bit == ExceptionHandlerProtocol::kBoolTrue;\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\npid_t PtraceClient::GetProcessID() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return pid_;\n}\n\nbool PtraceClient::Attach(pid_t tid) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return AttachImpl(sock_, tid);\n}\n\nbool PtraceClient::Is64Bit() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return is_64_bit_;\n}\n\nbool PtraceClient::GetThreadInfo(pid_t tid, ThreadInfo* info) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  PtraceBroker::Request request = {};\n  request.type = PtraceBroker::Request::kTypeGetThreadInfo;\n  request.tid = tid;\n  if (!LoggingWriteFile(sock_, &request, sizeof(request))) {\n    return false;\n  }\n\n  PtraceBroker::GetThreadInfoResponse response;\n  if (!LoggingReadFileExactly(sock_, &response, sizeof(response))) {\n    return false;\n  }\n\n  if (response.success == ExceptionHandlerProtocol::kBoolTrue) {\n    *info = response.info;\n    return true;\n  }\n\n  ReceiveAndLogError(sock_, \"PtraceBroker GetThreadInfo\");\n  return false;\n}\n\nbool PtraceClient::ReadFileContents(const base::FilePath& path,\n                                    std::string* contents) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  PtraceBroker::Request request = {};\n  request.type = PtraceBroker::Request::kTypeReadFile;\n  request.path.path_length = path.value().size();\n\n  if (!LoggingWriteFile(sock_, &request, sizeof(request)) ||\n      !SendFilePath(path.value().c_str(), request.path.path_length)) {\n    return false;\n  }\n\n  std::string local_contents;\n  int32_t read_result;\n  do {\n    if (!LoggingReadFileExactly(sock_, &read_result, sizeof(read_result))) {\n      return false;\n    }\n\n    if (read_result < 0) {\n      ReceiveAndLogReadError(sock_, \"ReadFileContents\");\n      return false;\n    }\n\n    if (read_result > 0) {\n      size_t old_length = local_contents.size();\n      local_contents.resize(old_length + read_result);\n      if (!LoggingReadFileExactly(\n              sock_, &local_contents[old_length], read_result)) {\n        return false;\n      }\n    }\n  } while (read_result > 0);\n\n  contents->swap(local_contents);\n  return true;\n}\n\nProcessMemoryLinux* PtraceClient::Memory() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!memory_) {\n    memory_ = std::make_unique<ProcessMemoryLinux>(this);\n  }\n  return memory_.get();\n}\n\nbool PtraceClient::Threads(std::vector<pid_t>* threads) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  DCHECK(threads->empty());\n\n  // If the broker is unable to read thread IDs, fall-back to just the main\n  // thread's ID.\n  threads->push_back(pid_);\n\n  char path[32];\n  snprintf(path, std::size(path), \"/proc/%d/task\", pid_);\n\n  PtraceBroker::Request request = {};\n  request.type = PtraceBroker::Request::kTypeListDirectory;\n  request.path.path_length = strlen(path);\n\n  if (!LoggingWriteFile(sock_, &request, sizeof(request)) ||\n      !SendFilePath(path, request.path.path_length)) {\n    return false;\n  }\n\n  std::vector<pid_t> local_threads;\n  int32_t read_result;\n  do {\n    if (!LoggingReadFileExactly(sock_, &read_result, sizeof(read_result))) {\n      return false;\n    }\n\n    if (read_result < 0) {\n      return ReceiveAndLogReadError(sock_, \"Threads\");\n    }\n\n    if (read_result > 0) {\n      auto buffer = std::make_unique<char[]>(read_result);\n      if (!LoggingReadFileExactly(sock_, buffer.get(), read_result)) {\n        return false;\n      }\n\n      ReadDentsAsThreadIDs(buffer.get(), read_result, &local_threads);\n    }\n  } while (read_result > 0);\n\n  threads->swap(local_threads);\n  return true;\n}\n\nssize_t PtraceClient::ReadUpTo(VMAddress address, size_t size, void* buffer) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  char* buffer_c = reinterpret_cast<char*>(buffer);\n\n  PtraceBroker::Request request = {};\n  request.type = PtraceBroker::Request::kTypeReadMemory;\n  request.tid = pid_;\n  request.iov.base = address;\n  request.iov.size = size;\n\n  if (!LoggingWriteFile(sock_, &request, sizeof(request))) {\n    return false;\n  }\n\n  ssize_t total_read = 0;\n  while (size > 0) {\n    int32_t bytes_read;\n    if (!LoggingReadFileExactly(sock_, &bytes_read, sizeof(bytes_read))) {\n      return -1;\n    }\n\n    if (bytes_read < 0) {\n      ReceiveAndLogReadError(sock_, \"PtraceBroker ReadMemory\");\n      return -1;\n    }\n\n    if (bytes_read == 0) {\n      return total_read;\n    }\n\n    if (static_cast<size_t>(bytes_read) > size) {\n      LOG(ERROR) << \"invalid size \" << bytes_read;\n      return -1;\n    }\n\n    if (!LoggingReadFileExactly(sock_, buffer_c, bytes_read)) {\n      return -1;\n    }\n\n    size -= bytes_read;\n    buffer_c += bytes_read;\n    total_read += bytes_read;\n  }\n\n  return total_read;\n}\n\nbool PtraceClient::SendFilePath(const char* path, size_t length) {\n  if (!LoggingWriteFile(sock_, path, length)) {\n    return false;\n  }\n\n  PtraceBroker::OpenResult result;\n  if (!LoggingReadFileExactly(sock_, &result, sizeof(result))) {\n    return false;\n  }\n\n  switch (result) {\n    case PtraceBroker::kOpenResultAccessDenied:\n      LOG(ERROR) << \"Broker Open: access denied\";\n      return false;\n\n    case PtraceBroker::kOpenResultTooLong:\n      LOG(ERROR) << \"Broker Open: path too long\";\n      return false;\n\n    case PtraceBroker::kOpenResultSuccess:\n      return true;\n\n    default:\n      if (result < 0) {\n        LOG(ERROR) << \"Broker Open: invalid result \" << result;\n        DCHECK(false);\n      } else {\n        errno = result;\n        PLOG(ERROR) << \"Broker Open\";\n      }\n      return false;\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/ptrace_client.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_PTRACE_CLIENT_H_\n#define CRASHPAD_UTIL_LINUX_PTRACE_CLIENT_H_\n\n#include <sys/types.h>\n\n#include <memory>\n\n#include \"util/linux/ptrace_connection.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\n\n//! \\brief Implements a PtraceConnection over a socket.\n//!\n//! This class forms the client half of the connection and is typically used\n//! when the current process does not have `ptrace` capabilities on the target\n//! process. It should be created with a socket connected to a PtraceBroker.\nclass PtraceClient : public PtraceConnection {\n public:\n  PtraceClient();\n\n  PtraceClient(const PtraceClient&) = delete;\n  PtraceClient& operator=(const PtraceClient&) = delete;\n\n  ~PtraceClient();\n\n  //! \\brief Initializes this object.\n  //!\n  //! This method must be successfully called before any other method in this\n  //! class.\n  //!\n  //! \\param[in] sock A socket connected to a PtraceBroker. Does not take\n  //!     ownership of the socket.\n  //! \\param[in] pid The process ID of the process to form a PtraceConnection\n  //!     with.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Initialize(int sock, pid_t pid);\n\n  // PtraceConnection:\n\n  pid_t GetProcessID() override;\n  bool Attach(pid_t tid) override;\n  bool Is64Bit() override;\n  bool GetThreadInfo(pid_t tid, ThreadInfo* info) override;\n  bool ReadFileContents(const base::FilePath& path,\n                        std::string* contents) override;\n  ProcessMemoryLinux* Memory() override;\n  bool Threads(std::vector<pid_t>* threads) override;\n  ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) override;\n\n private:\n  bool SendFilePath(const char* path, size_t length);\n\n  std::unique_ptr<ProcessMemoryLinux> memory_;\n  int sock_;\n  pid_t pid_;\n  bool is_64_bit_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_PTRACE_CLIENT_H_\n"
  },
  {
    "path": "util/linux/ptrace_connection.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_PTRACE_CONNECTION_H_\n#define CRASHPAD_UTIL_LINUX_PTRACE_CONNECTION_H_\n\n#include <sys/types.h>\n\n#include <string>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"util/linux/thread_info.h\"\n#include \"util/process/process_memory_linux.h\"\n\nnamespace crashpad {\n\n//! \\brief Provides an interface for making `ptrace` requests against a process\n//!     and its threads.\nclass PtraceConnection {\n public:\n  virtual ~PtraceConnection() {}\n\n  //! \\brief Returns the process ID of the connected process.\n  virtual pid_t GetProcessID() = 0;\n\n  //! \\brief Adds a new thread to this connection.\n  //!\n  //! \\param[in] tid The thread ID of the thread to attach.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  virtual bool Attach(pid_t tid) = 0;\n\n  //! \\brief Returns `true` if connected to a 64-bit process.\n  virtual bool Is64Bit() = 0;\n\n  //! \\brief Retrieves a ThreadInfo for a target thread.\n  //!\n  //! \\param[in] tid The thread ID of the target thread.\n  //! \\param[out] info Information about the thread.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  virtual bool GetThreadInfo(pid_t tid, ThreadInfo* info) = 0;\n\n  //! \\brief Reads the entire contents of a file.\n  //!\n  //! \\param[in] path The path of the file to read.\n  //! \\param[out] contents The file contents, valid if this method returns\n  //!     `true`.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  virtual bool ReadFileContents(const base::FilePath& path,\n                                std::string* contents) = 0;\n\n  //! \\brief Returns a memory reader for the connected process.\n  //!\n  //! The caller does not take ownership of the reader. The reader is valid for\n  //! the lifetime of the PtraceConnection that created it.\n  virtual ProcessMemoryLinux* Memory() = 0;\n\n  //! \\brief Determines the thread IDs of the threads in the connected process.\n  //!\n  //! \\param[out] threads The list of thread IDs.\n  //! \\return `true` on success, `false` on failure with a message logged. If\n  //!     this method returns `false`, \\a threads may contain a partial list of\n  //!     thread IDs.\n  virtual bool Threads(std::vector<pid_t>* threads) = 0;\n\n  //! \\brief Copies memory from the connected process into a caller-provided\n  //!     buffer in the current process, up to a maximum number of bytes.\n  //!\n  //! \\param[in] address The address, in the connected process' address space,\n  //!     of the memory region to copy.\n  //! \\param[in] size The maximum size, in bytes, of the memory region to copy.\n  //!     \\a buffer must be at least this size.\n  //! \\param[out] buffer The buffer into which the contents of the other\n  //!     process' memory will be copied.\n  //!\n  //! \\return the number of bytes copied, 0 if there is no more data to read, or\n  //!     -1 on failure with a message logged.\n  virtual ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_PTRACE_CONNECTION_H_\n"
  },
  {
    "path": "util/linux/ptracer.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/ptracer.h\"\n\n#include <errno.h>\n#include <linux/elf.h>\n#include <string.h>\n#include <sys/ptrace.h>\n#include <sys/uio.h>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/from_pointer_cast.h\"\n\n#if defined(ARCH_CPU_X86_FAMILY)\n#include <asm/ldt.h>\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\n#if defined(ARCH_CPU_X86_FAMILY)\n\ntemplate <typename Destination>\nbool GetRegisterSet(pid_t tid, int set, Destination* dest, bool can_log) {\n  iovec iov;\n  iov.iov_base = dest;\n  iov.iov_len = sizeof(*dest);\n  if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(set), &iov) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  if (iov.iov_len != sizeof(*dest)) {\n    LOG_IF(ERROR, can_log) << \"Unexpected registers size \" << iov.iov_len\n                           << \" != \" << sizeof(*dest);\n    return false;\n  }\n  return true;\n}\n\nbool GetFloatingPointRegisters32(pid_t tid,\n                                 FloatContext* context,\n                                 bool can_log) {\n  return GetRegisterSet(tid, NT_PRXFPREG, &context->f32.fxsave, can_log);\n}\n\nbool GetFloatingPointRegisters64(pid_t tid,\n                                 FloatContext* context,\n                                 bool can_log) {\n  return GetRegisterSet(tid, NT_PRFPREG, &context->f64.fxsave, can_log);\n}\n\nbool GetThreadArea32(pid_t tid,\n                     const ThreadContext& context,\n                     LinuxVMAddress* address,\n                     bool can_log) {\n  size_t index = (context.t32.xgs & 0xffff) >> 3;\n  user_desc desc;\n  if (ptrace(\n          PTRACE_GET_THREAD_AREA, tid, reinterpret_cast<void*>(index), &desc) !=\n      0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n\n  *address = desc.base_addr;\n  return true;\n}\n\nbool GetThreadArea64(pid_t tid,\n                     const ThreadContext& context,\n                     LinuxVMAddress* address,\n                     bool can_log) {\n  *address = context.t64.fs_base;\n  return true;\n}\n\n#elif defined(ARCH_CPU_ARM_FAMILY)\n\n#if defined(ARCH_CPU_ARMEL)\n// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel\n// support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including\n// x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for ARM only since\n// Linux 3.5.0 (0693bf68148c4). Older Linux kernels support PTRACE_GETREGS,\n// PTRACE_GETFPREGS, and PTRACE_GETVFPREGS instead, which don't allow checking\n// the size of data copied.\n//\n// Fortunately, 64-bit ARM support only appeared in Linux 3.7.0, so if\n// PTRACE_GETREGSET fails on ARM with EIO, indicating that the request is not\n// supported, the kernel must be old enough that 64-bit ARM isn’t supported\n// either.\n//\n// TODO(mark): Once helpers to interpret the kernel version are available, add\n// a DCHECK to ensure that the kernel is older than 3.5.\n\nbool GetGeneralPurposeRegistersLegacy(pid_t tid,\n                                      ThreadContext* context,\n                                      bool can_log) {\n  if (ptrace(PTRACE_GETREGS, tid, nullptr, &context->t32) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  return true;\n}\n\nbool GetFloatingPointRegistersLegacy(pid_t tid,\n                                     FloatContext* context,\n                                     bool can_log) {\n  if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  context->f32.have_fpregs = true;\n\n  if (ptrace(PTRACE_GETVFPREGS, tid, nullptr, &context->f32.vfp) != 0) {\n    switch (errno) {\n      case EINVAL:\n        // These registers are optional on 32-bit ARM cpus\n        break;\n      default:\n        PLOG_IF(ERROR, can_log) << \"ptrace\";\n        return false;\n    }\n  } else {\n    context->f32.have_vfp = true;\n  }\n  return true;\n}\n#endif  // ARCH_CPU_ARMEL\n\n// Normally, the Linux kernel will copy out register sets according to the size\n// of the struct that contains them. Tracing a 32-bit ARM process running in\n// compatibility mode on a 64-bit ARM cpu will only copy data for the number of\n// registers times the size of the register, which won't include any possible\n// trailing padding in the struct. These are the sizes of the register data, not\n// including any possible padding.\nconstexpr size_t kArmVfpSize = 32 * 8 + 4;\n\n// Target is 32-bit\nbool GetFloatingPointRegisters32(pid_t tid,\n                                 FloatContext* context,\n                                 bool can_log) {\n  context->f32.have_fpregs = false;\n  context->f32.have_vfp = false;\n\n  iovec iov;\n  iov.iov_base = &context->f32.fpregs;\n  iov.iov_len = sizeof(context->f32.fpregs);\n  if (ptrace(\n          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=\n      0) {\n    switch (errno) {\n#if defined(ARCH_CPU_ARMEL)\n      case EIO:\n        return GetFloatingPointRegistersLegacy(tid, context, can_log);\n#endif  // ARCH_CPU_ARMEL\n      case EINVAL:\n        // A 32-bit process running on a 64-bit CPU doesn't have this register\n        // set. It should have a VFP register set instead.\n        break;\n      default:\n        PLOG_IF(ERROR, can_log) << \"ptrace\";\n        return false;\n    }\n  } else {\n    if (iov.iov_len != sizeof(context->f32.fpregs)) {\n      LOG_IF(ERROR, can_log) << \"Unexpected registers size \" << iov.iov_len\n                             << \" != \" << sizeof(context->f32.fpregs);\n      return false;\n    }\n    context->f32.have_fpregs = true;\n  }\n\n  iov.iov_base = &context->f32.vfp;\n  iov.iov_len = sizeof(context->f32.vfp);\n  if (ptrace(\n          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_ARM_VFP), &iov) !=\n      0) {\n    switch (errno) {\n      case EINVAL:\n        // VFP may not be present on 32-bit ARM cpus.\n        break;\n      default:\n        PLOG_IF(ERROR, can_log) << \"ptrace\";\n        return false;\n    }\n  } else {\n    if (iov.iov_len != kArmVfpSize && iov.iov_len != sizeof(context->f32.vfp)) {\n      LOG_IF(ERROR, can_log) << \"Unexpected registers size \" << iov.iov_len\n                             << \" != \" << sizeof(context->f32.vfp);\n      return false;\n    }\n    context->f32.have_vfp = true;\n  }\n\n  if (!(context->f32.have_fpregs || context->f32.have_vfp)) {\n    LOG_IF(ERROR, can_log) << \"Unable to collect registers\";\n    return false;\n  }\n  return true;\n}\n\nbool GetFloatingPointRegisters64(pid_t tid,\n                                 FloatContext* context,\n                                 bool can_log) {\n  iovec iov;\n  iov.iov_base = context;\n  iov.iov_len = sizeof(*context);\n  if (ptrace(\n          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=\n      0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  if (iov.iov_len != sizeof(context->f64)) {\n    LOG_IF(ERROR, can_log) << \"Unexpected registers size \" << iov.iov_len\n                           << \" != \" << sizeof(context->f64);\n    return false;\n  }\n  return true;\n}\n\nbool GetThreadArea32(pid_t tid,\n                     const ThreadContext& context,\n                     LinuxVMAddress* address,\n                     bool can_log) {\n#if defined(ARCH_CPU_ARMEL)\n  void* result;\n  if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  *address = FromPointerCast<LinuxVMAddress>(result);\n  return true;\n#else\n  // TODO(jperaza): it doesn't look like there is a way for a 64-bit ARM process\n  // to get the thread area for a 32-bit ARM process with ptrace.\n  LOG_IF(WARNING, can_log)\n      << \"64-bit ARM cannot trace TLS area for a 32-bit process\";\n  return false;\n#endif  // ARCH_CPU_ARMEL\n}\n\nbool GetThreadArea64(pid_t tid,\n                     const ThreadContext& context,\n                     LinuxVMAddress* address,\n                     bool can_log) {\n  iovec iov;\n  iov.iov_base = address;\n  iov.iov_len = sizeof(*address);\n  if (ptrace(\n          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_ARM_TLS), &iov) !=\n      0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  if (iov.iov_len != 8) {\n    LOG_IF(ERROR, can_log) << \"address size mismatch\";\n    return false;\n  }\n  return true;\n}\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n// PTRACE_GETREGSET, introduced in Linux 2.6.34 (2225a122ae26), requires kernel\n// support enabled by HAVE_ARCH_TRACEHOOK. This has been set for x86 (including\n// x86_64) since Linux 2.6.28 (99bbc4b1e677a), but for MIPS only since\n// Linux 3.13 (c0ff3c53d4f99). Older Linux kernels support PTRACE_GETREGS,\n// and PTRACE_GETFPREGS instead, which don't allow checking the size of data\n// copied. Also, PTRACE_GETREGS assumes register size of 64 bits even for 32 bit\n// MIPS CPU (contrary to PTRACE_GETREGSET behavior), so we need buffer\n// structure here.\n\nbool GetGeneralPurposeRegistersLegacy(pid_t tid,\n                                      ThreadContext* context,\n                                      bool can_log) {\n  ThreadContext context_buffer;\n  if (ptrace(PTRACE_GETREGS, tid, nullptr, &context_buffer.t64) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n// Bitness of target process can't be determined through ptrace here, so we\n// assume target process has the same as current process, making cross-bit\n// ptrace unsupported on MIPS for kernels older than 3.13\n#if defined(ARCH_CPU_MIPSEL)\n#define THREAD_CONTEXT_FIELD t32\n#elif defined(ARCH_CPU_MIPS64EL)\n#define THREAD_CONTEXT_FIELD t64\n#endif\n  for (size_t reg = 0; reg < 32; ++reg) {\n    context->THREAD_CONTEXT_FIELD.regs[reg] = context_buffer.t64.regs[reg];\n  }\n  context->THREAD_CONTEXT_FIELD.lo = context_buffer.t64.lo;\n  context->THREAD_CONTEXT_FIELD.hi = context_buffer.t64.hi;\n  context->THREAD_CONTEXT_FIELD.cp0_epc = context_buffer.t64.cp0_epc;\n  context->THREAD_CONTEXT_FIELD.cp0_badvaddr = context_buffer.t64.cp0_badvaddr;\n  context->THREAD_CONTEXT_FIELD.cp0_status = context_buffer.t64.cp0_status;\n  context->THREAD_CONTEXT_FIELD.cp0_cause = context_buffer.t64.cp0_cause;\n#undef THREAD_CONTEXT_FIELD\n  return true;\n}\n\nbool GetFloatingPointRegistersLegacy(pid_t tid,\n                                     FloatContext* context,\n                                     bool can_log) {\n  if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  return true;\n}\n\nbool GetFloatingPointRegisters32(pid_t tid,\n                                 FloatContext* context,\n                                 bool can_log) {\n  iovec iov;\n  iov.iov_base = &context->f32.fpregs;\n  iov.iov_len = sizeof(context->f32.fpregs);\n  if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f32.fpregs) != 0) {\n    switch (errno) {\n      case EINVAL:\n        // fp may not be present\n        break;\n      case EIO:\n        return GetFloatingPointRegistersLegacy(tid, context, can_log);\n      default:\n        PLOG_IF(ERROR, can_log) << \"ptrace\";\n        return false;\n    }\n  }\n  return true;\n}\n\nbool GetFloatingPointRegisters64(pid_t tid,\n                                 FloatContext* context,\n                                 bool can_log) {\n  iovec iov;\n  iov.iov_base = &context->f64.fpregs;\n  iov.iov_len = sizeof(context->f64.fpregs);\n  if (ptrace(PTRACE_GETFPREGS, tid, nullptr, &context->f64.fpregs) != 0) {\n    switch (errno) {\n      case EINVAL:\n        // fp may not be present\n        break;\n      case EIO:\n        return GetFloatingPointRegistersLegacy(tid, context, can_log);\n      default:\n        PLOG_IF(ERROR, can_log) << \"ptrace\";\n        return false;\n    }\n  }\n  return true;\n}\n\nbool GetThreadArea32(pid_t tid,\n                     const ThreadContext& context,\n                     LinuxVMAddress* address,\n                     bool can_log) {\n#if defined(ARCH_CPU_MIPSEL)\n  void* result;\n  if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  *address = FromPointerCast<LinuxVMAddress>(result);\n  return true;\n#else\n  return false;\n#endif\n}\n\nbool GetThreadArea64(pid_t tid,\n                     const ThreadContext& context,\n                     LinuxVMAddress* address,\n                     bool can_log) {\n  void* result;\n#if defined(ARCH_CPU_MIPSEL)\n  if (ptrace(PTRACE_GET_THREAD_AREA_3264, tid, nullptr, &result) != 0) {\n#else\n  if (ptrace(PTRACE_GET_THREAD_AREA, tid, nullptr, &result) != 0) {\n#endif\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  *address = FromPointerCast<LinuxVMAddress>(result);\n  return true;\n}\n\n#elif defined(ARCH_CPU_RISCV64)\n\nbool GetFloatingPointRegisters64(pid_t tid,\n                                 FloatContext* context,\n                                 bool can_log) {\n  iovec iov;\n  iov.iov_base = context;\n  iov.iov_len = sizeof(*context);\n  if (ptrace(\n          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRFPREG), &iov) !=\n      0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  if (iov.iov_len != sizeof(context->f64)) {\n    LOG_IF(ERROR, can_log) << \"Unexpected registers size \" << iov.iov_len\n                           << \" != \" << sizeof(context->f64);\n    return false;\n  }\n  return true;\n}\n\nbool GetThreadArea64(pid_t tid,\n                     const ThreadContext& context,\n                     LinuxVMAddress* address,\n                     bool can_log) {\n  // Thread pointer register\n  *address = context.t64.regs[3];\n  return true;\n}\n\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY\n\nsize_t GetGeneralPurposeRegistersAndLength(pid_t tid,\n                                           ThreadContext* context,\n                                           bool can_log) {\n  iovec iov;\n  iov.iov_base = context;\n  iov.iov_len = sizeof(*context);\n  if (ptrace(\n          PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iov) !=\n      0) {\n    switch (errno) {\n#if defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_MIPS_FAMILY)\n      case EIO:\n        return GetGeneralPurposeRegistersLegacy(tid, context, can_log)\n                   ? sizeof(context->t32)\n                   : 0;\n#endif  // ARCH_CPU_ARMEL\n      default:\n        PLOG_IF(ERROR, can_log) << \"ptrace\";\n        return 0;\n    }\n  }\n  return iov.iov_len;\n}\n\n#if !defined(ARCH_CPU_RISCV64)\nbool GetGeneralPurposeRegisters32(pid_t tid,\n                                  ThreadContext* context,\n                                  bool can_log) {\n  size_t length = GetGeneralPurposeRegistersAndLength(tid, context, can_log);\n  if (length != sizeof(context->t32)) {\n    LOG_IF(ERROR, can_log) << \"Unexpected registers size \" << length\n                           << \" != \" << sizeof(context->t32);\n    return false;\n  }\n  return true;\n}\n#endif\n\nbool GetGeneralPurposeRegisters64(pid_t tid,\n                                  ThreadContext* context,\n                                  bool can_log) {\n  size_t length = GetGeneralPurposeRegistersAndLength(tid, context, can_log);\n  if (length != sizeof(context->t64)) {\n    LOG_IF(ERROR, can_log) << \"Unexpected registers size \" << length\n                           << \" != \" << sizeof(context->t64);\n    return false;\n  }\n  return true;\n}\n\n}  // namespace\n\nPtracer::Ptracer(bool can_log)\n    : is_64_bit_(false), can_log_(can_log), initialized_() {}\n\nPtracer::Ptracer(bool is_64_bit, bool can_log)\n    : is_64_bit_(is_64_bit), can_log_(can_log), initialized_() {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n}\n\nPtracer::~Ptracer() {}\n\nbool Ptracer::Initialize(pid_t pid) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  ThreadContext context;\n  size_t length = GetGeneralPurposeRegistersAndLength(pid, &context, can_log_);\n  if (length == sizeof(context.t64)) {\n    is_64_bit_ = true;\n  } else if (length == sizeof(context.t32)) {\n    is_64_bit_ = false;\n  } else {\n    LOG_IF(ERROR, can_log_)\n        << \"Unexpected registers size \" << length\n        << \" != \" << sizeof(context.t64) << \", \" << sizeof(context.t32);\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool Ptracer::Is64Bit() {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return is_64_bit_;\n}\n\nbool Ptracer::GetThreadInfo(pid_t tid, ThreadInfo* info) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (is_64_bit_) {\n    return GetGeneralPurposeRegisters64(tid, &info->thread_context, can_log_) &&\n           GetFloatingPointRegisters64(tid, &info->float_context, can_log_) &&\n           GetThreadArea64(tid,\n                           info->thread_context,\n                           &info->thread_specific_data_address,\n                           can_log_);\n  }\n\n#if !defined(ARCH_CPU_RISCV64)\n  return GetGeneralPurposeRegisters32(tid, &info->thread_context, can_log_) &&\n         GetFloatingPointRegisters32(tid, &info->float_context, can_log_) &&\n         GetThreadArea32(tid,\n                         info->thread_context,\n                         &info->thread_specific_data_address,\n                         can_log_);\n#else\n  return false;\n#endif\n}\n\nssize_t Ptracer::ReadUpTo(pid_t pid,\n                          LinuxVMAddress address,\n                          size_t size,\n                          char* buffer) {\n  size_t bytes_read = 0;\n  while (size > 0) {\n    errno = 0;\n\n    if (size >= sizeof(long)) {\n      *reinterpret_cast<long*>(buffer) =\n          ptrace(PTRACE_PEEKDATA, pid, address, nullptr);\n\n      if (errno == EIO) {\n        ssize_t last_bytes = ReadLastBytes(pid, address, size, buffer);\n        return last_bytes >= 0 ? bytes_read + last_bytes : -1;\n      }\n\n      if (errno != 0) {\n        PLOG_IF(ERROR, can_log_) << \"ptrace\";\n        return -1;\n      }\n\n      size -= sizeof(long);\n      buffer += sizeof(long);\n      address += sizeof(long);\n      bytes_read += sizeof(long);\n    } else {\n      long word = ptrace(PTRACE_PEEKDATA, pid, address, nullptr);\n\n      if (errno == 0) {\n        memcpy(buffer, reinterpret_cast<char*>(&word), size);\n        return bytes_read + size;\n      }\n\n      if (errno == EIO) {\n        ssize_t last_bytes = ReadLastBytes(pid, address, size, buffer);\n        return last_bytes >= 0 ? bytes_read + last_bytes : -1;\n      }\n\n      PLOG_IF(ERROR, can_log_);\n      return -1;\n    }\n  }\n\n  return bytes_read;\n}\n\n// Handles an EIO by reading at most size bytes from address into buffer if\n// address was within a word of a possible page boundary, by aligning to read\n// the last word of the page and extracting the desired bytes.\nssize_t Ptracer::ReadLastBytes(pid_t pid,\n                               LinuxVMAddress address,\n                               size_t size,\n                               char* buffer) {\n  LinuxVMAddress aligned = ((address + 4095) & ~4095) - sizeof(long);\n  if (aligned >= address || aligned == address - sizeof(long)) {\n    PLOG_IF(ERROR, can_log_) << \"ptrace\";\n    return -1;\n  }\n  DCHECK_GT(aligned, address - sizeof(long));\n\n  errno = 0;\n  long word = ptrace(PTRACE_PEEKDATA, pid, aligned, nullptr);\n  if (errno != 0) {\n    PLOG_IF(ERROR, can_log_) << \"ptrace\";\n    return -1;\n  }\n\n  size_t bytes_read = address - aligned;\n  size_t last_bytes = std::min(sizeof(long) - bytes_read, size);\n  memcpy(buffer, reinterpret_cast<char*>(&word) + bytes_read, last_bytes);\n  return last_bytes;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/ptracer.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_PTRACER_H_\n#define CRASHPAD_UTIL_LINUX_PTRACER_H_\n\n#include <sys/types.h>\n\n#include \"util/linux/address_types.h\"\n#include \"util/linux/thread_info.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\n//! \\brief Provides an architecturally agnostic interface for collecting\n//!     information with `ptrace`.\n//!\n//! A ptracer is configured for a particular bitness. It is an error to make any\n//! calls via this object against a thread whose bitness does not match the\n//! bitness this object was initialized with.\nclass Ptracer {\n public:\n  //! \\brief Constructs this object with a pre-determined bitness.\n  //!\n  //! \\param[in] is_64_bit `true` if this object is to be configured for 64-bit.\n  //! \\param[in] can_log Whether methods in this class can log error messages.\n  Ptracer(bool is_64_bit, bool can_log);\n\n  //! \\brief Constructs this object without a pre-determined bitness.\n  //!\n  //! Initialize() must be successfully called before making any other calls on\n  //! this object.\n  //!\n  //! \\param[in] can_log Whether methods in this class can log error messages.\n  explicit Ptracer(bool can_log);\n\n  Ptracer(const Ptracer&) = delete;\n  Ptracer& operator=(const Ptracer&) = delete;\n\n  ~Ptracer();\n\n  //! \\brief Initializes this object to the bitness of the process whose process\n  //!     ID is \\a pid.\n  //!\n  //! \\param[in] pid The process ID of the process to initialize with.\n  //! \\return `true` on success. `false` on failure with a message logged, if\n  //!     enabled.\n  bool Initialize(pid_t pid);\n\n  //! \\brief Return `true` if this object is configured for 64-bit.\n  bool Is64Bit();\n\n  //! \\brief Uses `ptrace` to collect information about the thread with thread\n  //!     ID \\a tid.\n  //!\n  //! The target thread should be attached before calling this method.\n  //! \\see ScopedPtraceAttach\n  //!\n  //! \\param[in] tid The thread ID of the thread to collect information for.\n  //! \\param[out] info A ThreadInfo for the thread.\n  //! \\return `true` on success. `false` on failure with a message logged, if\n  //!     enabled.\n  bool GetThreadInfo(pid_t tid, ThreadInfo* info);\n\n  //! \\brief Uses `ptrace` to read memory from the process with process ID \\a\n  //!     pid, up to a maximum number of bytes.\n  //!\n  //! The target process should already be attached before calling this method.\n  //! \\see ScopedPtraceAttach\n  //!\n  //! \\param[in] pid The process ID whose memory to read.\n  //! \\param[in] address The base address of the region to read.\n  //! \\param[in] size The size of the memory region to read. \\a buffer must be\n  //!     at least this size.\n  //! \\param[out] buffer The buffer to fill with the data read.\n  //! \\return the number of bytes read, 0 if there are no more bytes to read, or\n  //!     -1 on failure with a message logged if logging is enabled.\n  ssize_t ReadUpTo(pid_t pid,\n                   LinuxVMAddress address,\n                   size_t size,\n                   char* buffer);\n\n private:\n  ssize_t ReadLastBytes(pid_t pid,\n                        LinuxVMAddress address,\n                        size_t size,\n                        char* buffer);\n\n  bool is_64_bit_;\n  bool can_log_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_PTRACER_H_\n"
  },
  {
    "path": "util/linux/ptracer_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/ptracer.h\"\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/linux/get_tls.h\"\n#include \"test/multiprocess.h\"\n#include \"util/file/file_io.h\"\n#include \"util/linux/scoped_ptrace_attach.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass SameBitnessTest : public Multiprocess {\n public:\n  SameBitnessTest() : Multiprocess() {}\n\n  SameBitnessTest(const SameBitnessTest&) = delete;\n  SameBitnessTest& operator=(const SameBitnessTest&) = delete;\n\n  ~SameBitnessTest() {}\n\n private:\n  void MultiprocessParent() override {\n    LinuxVMAddress expected_tls;\n    CheckedReadFileExactly(\n        ReadPipeHandle(), &expected_tls, sizeof(expected_tls));\n\n#if defined(ARCH_CPU_64_BITS)\n    constexpr bool am_64_bit = true;\n#else\n    constexpr bool am_64_bit = false;\n#endif  // ARCH_CPU_64_BITS\n\n    ScopedPtraceAttach attach;\n    ASSERT_TRUE(attach.ResetAttach(ChildPID()));\n\n    Ptracer ptracer(am_64_bit, /* can_log= */ true);\n\n    EXPECT_EQ(ptracer.Is64Bit(), am_64_bit);\n\n    ThreadInfo thread_info;\n    ASSERT_TRUE(ptracer.GetThreadInfo(ChildPID(), &thread_info));\n\n#if defined(ARCH_CPU_X86_64)\n    EXPECT_EQ(thread_info.thread_context.t64.fs_base, expected_tls);\n#endif  // ARCH_CPU_X86_64\n\n    EXPECT_EQ(thread_info.thread_specific_data_address, expected_tls);\n  }\n\n  void MultiprocessChild() override {\n    LinuxVMAddress expected_tls = GetTLS();\n    CheckedWriteFile(WritePipeHandle(), &expected_tls, sizeof(expected_tls));\n\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n};\n\nTEST(Ptracer, SameBitness) {\n  SameBitnessTest test;\n  test.Run();\n}\n\n// TODO(jperaza): Test against a process with different bitness.\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/scoped_pr_set_dumpable.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/scoped_pr_set_dumpable.h\"\n\n#include <sys/prctl.h>\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nScopedPrSetDumpable::ScopedPrSetDumpable(bool may_log) : may_log_(may_log) {\n  int result = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);\n  PLOG_IF(ERROR, result < 0 && may_log_) << \"prctl\";\n  was_dumpable_ = result > 0;\n\n  if (!was_dumpable_) {\n    result = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);\n    PLOG_IF(ERROR, result != 0 && may_log_) << \"prctl\";\n  }\n}\n\nScopedPrSetDumpable::~ScopedPrSetDumpable() {\n  if (!was_dumpable_) {\n    int result = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);\n    PLOG_IF(ERROR, result != 0 && may_log_) << \"prctl\";\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/scoped_pr_set_dumpable.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_DUMPABLE_H_\n#define CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_DUMPABLE_H_\n\n\nnamespace crashpad {\n\nclass ScopedPrSetDumpable {\n public:\n  //! \\brief Uses `PR_SET_DUMPABLE` to make the current process dumpable.\n  //!\n  //! Restores the dumpable flag to its original value on destruction. If the\n  //! original value couldn't be determined, the destructor attempts to restore\n  //! the flag to 0 (non-dumpable).\n  //!\n  //! \\param[in] may_log `true` if this object may log error messages.\n  explicit ScopedPrSetDumpable(bool may_log);\n\n  ScopedPrSetDumpable(const ScopedPrSetDumpable&) = delete;\n  ScopedPrSetDumpable& operator=(const ScopedPrSetDumpable&) = delete;\n\n  ~ScopedPrSetDumpable();\n\n private:\n  bool was_dumpable_;\n  bool may_log_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_DUMPABLE_H_\n"
  },
  {
    "path": "util/linux/scoped_pr_set_ptracer.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/scoped_pr_set_ptracer.h\"\n\n#include <errno.h>\n#include <sys/prctl.h>\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nScopedPrSetPtracer::ScopedPrSetPtracer(pid_t pid, bool may_log)\n    : success_(false), may_log_(may_log) {\n  success_ = prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0;\n  PLOG_IF(ERROR, !success_ && may_log && errno != EINVAL) << \"prctl\";\n}\n\nScopedPrSetPtracer::~ScopedPrSetPtracer() {\n  if (success_) {\n    int res = prctl(PR_SET_PTRACER, 0, 0, 0, 0);\n    PLOG_IF(ERROR, res != 0 && may_log_) << \"prctl\";\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/scoped_pr_set_ptracer.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_PTRACER_H_\n#define CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_PTRACER_H_\n\n#include <sys/types.h>\n\n\nnamespace crashpad {\n\nclass ScopedPrSetPtracer {\n public:\n  //! \\brief Uses `PR_SET_PTRACER` to set \\a pid as the caller's ptracer.\n  //!\n  //! `PR_SET_PTRACER` is only supported if the Yama Linux security module (LSM)\n  //! is enabled. Otherwise, `prctl(PR_SET_PTRACER, ...)` fails with `EINVAL`.\n  //! See linux-4.9.20/security/yama/yama_lsm.c yama_task_prctl() and\n  //! linux-4.9.20/kernel/sys.c [sys_]prctl().\n  //!\n  //! An error message will be logged on failure only if \\a may_log is `true`\n  //! and `prctl` does not fail with `EINVAL`;\n  //!\n  //! \\param[in] pid The process ID of the process to make the caller's ptracer.\n  //! \\param[in] may_log if `true`, this class may log error messages.\n  ScopedPrSetPtracer(pid_t pid, bool may_log);\n\n  ScopedPrSetPtracer(const ScopedPrSetPtracer&) = delete;\n  ScopedPrSetPtracer& operator=(const ScopedPrSetPtracer&) = delete;\n\n  ~ScopedPrSetPtracer();\n\n private:\n  bool success_;\n  bool may_log_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_SCOPED_PR_SET_PTRACER_H_\n"
  },
  {
    "path": "util/linux/scoped_ptrace_attach.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/scoped_ptrace_attach.h\"\n\n#include <sys/ptrace.h>\n#include <sys/wait.h>\n\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n\nnamespace crashpad {\n\nbool PtraceAttach(pid_t pid, bool can_log) {\n  if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n\n  int status;\n  if (HANDLE_EINTR(waitpid(pid, &status, __WALL)) < 0) {\n    PLOG_IF(ERROR, can_log) << \"waitpid\";\n    return false;\n  }\n  if (!WIFSTOPPED(status)) {\n    LOG_IF(ERROR, can_log) << \"process not stopped\";\n    return false;\n  }\n  return true;\n}\n\nbool PtraceDetach(pid_t pid, bool can_log) {\n  if (pid >= 0 && ptrace(PTRACE_DETACH, pid, nullptr, nullptr) != 0) {\n    PLOG_IF(ERROR, can_log) << \"ptrace\";\n    return false;\n  }\n  return true;\n}\n\nScopedPtraceAttach::ScopedPtraceAttach()\n    : pid_(-1) {}\n\nScopedPtraceAttach::~ScopedPtraceAttach() {\n  Reset();\n}\n\nbool ScopedPtraceAttach::Reset() {\n  if (!PtraceDetach(pid_, true)) {\n    return false;\n  }\n  pid_ = -1;\n  return true;\n}\n\nbool ScopedPtraceAttach::ResetAttach(pid_t pid) {\n  Reset();\n\n  if (!PtraceAttach(pid, true)) {\n    return false;\n  }\n\n  pid_ = pid;\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/scoped_ptrace_attach.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_SCOPED_PTRACE_ATTACH_H_\n#define CRASHPAD_UTIL_LINUX_SCOPED_PTRACE_ATTACH_H_\n\n#include <sys/types.h>\n\nnamespace crashpad {\n\n//! \\brief Attaches to the process with process ID \\a pid and blocks until the\n//!     target process has stopped by calling `waitpid()`.\n//!\n//! \\param pid The process ID of the process to attach to.\n//! \\param can_log Whether this function may log messages on failure.\n//! \\return `true` on success. `false` on failure with a message logged if \\a\n//!     can_log is `true`.\nbool PtraceAttach(pid_t pid, bool can_log = true);\n\n//! \\brief Detaches the process  with process ID \\a pid. The process must\n//!     already be ptrace attached.\n//!\n//! \\param pid The process ID of the process to detach.\n//! \\param can_log Whether this function may log messages on failure.\n//! \\return `true` on success. `false` on failure with a message logged if \\a\n//!     can_log is `true`.\nbool PtraceDetach(pid_t pid, bool can_log = true);\n\n//! \\brief Maintains a `ptrace()` attachment to a process.\n//!\n//! On destruction, the process will be detached.\nclass ScopedPtraceAttach {\n public:\n  ScopedPtraceAttach();\n\n  ScopedPtraceAttach(const ScopedPtraceAttach&) = delete;\n  ScopedPtraceAttach& operator=(const ScopedPtraceAttach&) = delete;\n\n  ~ScopedPtraceAttach();\n\n  //! \\brief Detaches from the process by calling `ptrace()`.\n  //!\n  //! \\return `true` on success. `false` on failure, with a message logged.\n  bool Reset();\n\n  //! \\brief Detaches from any previously attached process, attaches to the\n  //!      process with process ID \\a pid, and blocks until the target process\n  //!      has stopped by calling `waitpid()`.\n  //!\n  //! \\return `true` on success. `false` on failure, with a message logged.\n  bool ResetAttach(pid_t pid);\n\n private:\n  pid_t pid_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_SCOPED_PTRACE_ATTACH_H_\n"
  },
  {
    "path": "util/linux/scoped_ptrace_attach_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/scoped_ptrace_attach.h\"\n\n#include <errno.h>\n#include <sys/ptrace.h>\n#include <unistd.h>\n\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/multiprocess.h\"\n#include \"util/file/file_io.h\"\n#include \"util/linux/scoped_pr_set_ptracer.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// If Yama is not enabled, the default ptrace restrictions should be\n// sufficient for these tests.\n//\n// If Yama is enabled, then /proc/sys/kernel/yama/ptrace_scope must be 0\n// (YAMA_SCOPE_DISABLED) or 1 (YAMA_SCOPE_RELATIONAL) for these tests to\n// succeed. If it is 2 (YAMA_SCOPE_CAPABILITY) then the test requires\n// CAP_SYS_PTRACE, and if it is 3 (YAMA_SCOPE_NO_ATTACH), these tests will fail.\n\nclass AttachTest : public Multiprocess {\n public:\n  AttachTest() : Multiprocess() {}\n\n  AttachTest(const AttachTest&) = delete;\n  AttachTest& operator=(const AttachTest&) = delete;\n\n  ~AttachTest() {}\n\n protected:\n  const long kWord = 42;\n};\n\nclass AttachToChildTest : public AttachTest {\n public:\n  AttachToChildTest() : AttachTest() {}\n\n  AttachToChildTest(const AttachToChildTest&) = delete;\n  AttachToChildTest& operator=(const AttachToChildTest&) = delete;\n\n  ~AttachToChildTest() {}\n\n private:\n  void MultiprocessParent() override {\n    // Wait for the child to set the parent as its ptracer.\n    char c;\n    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));\n\n    pid_t pid = ChildPID();\n\n    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);\n    EXPECT_EQ(errno, ESRCH) << ErrnoMessage(\"ptrace\");\n\n    ScopedPtraceAttach attachment;\n    ASSERT_EQ(attachment.ResetAttach(pid), true);\n    EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)\n        << ErrnoMessage(\"ptrace\");\n    attachment.Reset();\n\n    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);\n    EXPECT_EQ(errno, ESRCH) << ErrnoMessage(\"ptrace\");\n  }\n\n  void MultiprocessChild() override {\n    ScopedPrSetPtracer set_ptracer(getppid(), /* may_log= */ true);\n\n    char c = '\\0';\n    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));\n\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n};\n\nTEST(ScopedPtraceAttach, AttachChild) {\n  AttachToChildTest test;\n  test.Run();\n}\n\nclass AttachToParentResetTest : public AttachTest {\n public:\n  AttachToParentResetTest() : AttachTest() {}\n\n  AttachToParentResetTest(const AttachToParentResetTest&) = delete;\n  AttachToParentResetTest& operator=(const AttachToParentResetTest&) = delete;\n\n  ~AttachToParentResetTest() {}\n\n private:\n  void MultiprocessParent() override {\n    ScopedPrSetPtracer set_ptracer(ChildPID(), /* may_log= */ true);\n    char c = '\\0';\n    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));\n\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  void MultiprocessChild() override {\n    // Wait for the parent to set the child as its ptracer.\n    char c;\n    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));\n\n    pid_t pid = getppid();\n\n    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);\n    EXPECT_EQ(errno, ESRCH) << ErrnoMessage(\"ptrace\");\n\n    ScopedPtraceAttach attachment;\n    ASSERT_EQ(attachment.ResetAttach(pid), true);\n    EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)\n        << ErrnoMessage(\"ptrace\");\n    attachment.Reset();\n\n    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);\n    EXPECT_EQ(errno, ESRCH) << ErrnoMessage(\"ptrace\");\n  }\n};\n\nTEST(ScopedPtraceAttach, AttachParentReset) {\n  AttachToParentResetTest test;\n  test.Run();\n}\n\nclass AttachToParentDestructorTest : public AttachTest {\n public:\n  AttachToParentDestructorTest() : AttachTest() {}\n\n  AttachToParentDestructorTest(const AttachToParentDestructorTest&) = delete;\n  AttachToParentDestructorTest& operator=(const AttachToParentDestructorTest&) =\n      delete;\n\n  ~AttachToParentDestructorTest() {}\n\n private:\n  void MultiprocessParent() override {\n    ScopedPrSetPtracer set_ptracer(ChildPID(), /* may_log= */ true);\n    char c = '\\0';\n    CheckedWriteFile(WritePipeHandle(), &c, sizeof(c));\n\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  void MultiprocessChild() override {\n    // Wait for the parent to set the child as its ptracer.\n    char c;\n    CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c));\n\n    pid_t pid = getppid();\n    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);\n    EXPECT_EQ(errno, ESRCH) << ErrnoMessage(\"ptrace\");\n    {\n      ScopedPtraceAttach attachment;\n      ASSERT_EQ(attachment.ResetAttach(pid), true);\n      EXPECT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), kWord)\n          << ErrnoMessage(\"ptrace\");\n    }\n    ASSERT_EQ(ptrace(PTRACE_PEEKDATA, pid, &kWord, nullptr), -1);\n    EXPECT_EQ(errno, ESRCH) << ErrnoMessage(\"ptrace\");\n  }\n};\n\nTEST(ScopedPtraceAttach, AttachParentDestructor) {\n  AttachToParentDestructorTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/socket.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/socket.h\"\n\n#include <unistd.h>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"third_party/lss/lss.h\"\n\nnamespace crashpad {\n\nconstexpr size_t UnixCredentialSocket::kMaxSendRecvMsgFDs;\n\n// static\nbool UnixCredentialSocket::CreateCredentialSocketpair(ScopedFileHandle* sock1,\n                                                      ScopedFileHandle* sock2) {\n  int socks[2];\n  if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socks) != 0) {\n    PLOG(ERROR) << \"socketpair\";\n    return false;\n  }\n  ScopedFileHandle local_sock1(socks[0]);\n  ScopedFileHandle local_sock2(socks[1]);\n\n  int optval = 1;\n  socklen_t optlen = sizeof(optval);\n  if (setsockopt(local_sock1.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) !=\n          0 ||\n      setsockopt(local_sock2.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) !=\n          0) {\n    PLOG(ERROR) << \"setsockopt\";\n    return false;\n  }\n\n  sock1->swap(local_sock1);\n  sock2->swap(local_sock2);\n  return true;\n}\n\n// static\nint UnixCredentialSocket::SendMsg(int fd,\n                                  const void* buf,\n                                  size_t buf_size,\n                                  const int* fds,\n                                  size_t fd_count) {\n  // This function is intended to be used after a crash. fds is an integer\n  // array instead of a vector to avoid forcing callers to provide a vector,\n  // which they would have to create prior to the crash.\n  if (fds && fd_count > kMaxSendRecvMsgFDs) {\n    DLOG(ERROR) << \"too many fds \" << fd_count;\n    return EINVAL;\n  }\n\n  iovec iov;\n  iov.iov_base = const_cast<void*>(buf);\n  iov.iov_len = buf_size;\n\n  msghdr msg = {};\n  msg.msg_iov = &iov;\n  msg.msg_iovlen = 1;\n\n  char cmsg_buf[CMSG_SPACE(sizeof(int) * kMaxSendRecvMsgFDs)];\n  if (fds) {\n    msg.msg_control = cmsg_buf;\n    msg.msg_controllen = CMSG_SPACE(sizeof(int) * fd_count);\n\n    cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n    DCHECK(cmsg);\n\n    cmsg->cmsg_level = SOL_SOCKET;\n    cmsg->cmsg_type = SCM_RIGHTS;\n    cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fd_count);\n    memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * fd_count);\n  }\n\n  // TODO(jperaza): Use sys_sendmsg when lss has macros for maniuplating control\n  // messages. https://crbug.com/crashpad/265\n  if (HANDLE_EINTR(sendmsg(fd, &msg, MSG_NOSIGNAL)) < 0) {\n    DPLOG(ERROR) << \"sendmsg\";\n    return errno;\n  }\n  return 0;\n}\n\n// static\nbool UnixCredentialSocket::RecvMsg(int fd,\n                                   void* buf,\n                                   size_t buf_size,\n                                   ucred* creds,\n                                   std::vector<ScopedFileHandle>* fds) {\n  iovec iov;\n  iov.iov_base = buf;\n  iov.iov_len = buf_size;\n\n  msghdr msg = {};\n  msg.msg_iov = &iov;\n  msg.msg_iovlen = 1;\n\n  char cmsg_buf[CMSG_SPACE(sizeof(ucred)) +\n                CMSG_SPACE(sizeof(int) * kMaxSendRecvMsgFDs)];\n  msg.msg_control = cmsg_buf;\n  msg.msg_controllen = sizeof(cmsg_buf);\n\n  int res = HANDLE_EINTR(recvmsg(fd, &msg, 0));\n  if (res < 0) {\n    PLOG(ERROR) << \"recvmsg\";\n    return false;\n  }\n\n  ucred* local_creds = nullptr;\n  std::vector<ScopedFileHandle> local_fds;\n  bool unhandled_cmsgs = false;\n\n  for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);\n       cmsg;\n       cmsg = CMSG_NXTHDR(&msg, cmsg)) {\n    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {\n      int* fdp = reinterpret_cast<int*>(CMSG_DATA(cmsg));\n      size_t fd_count = (reinterpret_cast<char*>(cmsg) + cmsg->cmsg_len -\n                         reinterpret_cast<char*>(fdp)) /\n                        sizeof(int);\n      DCHECK_LE(fd_count, kMaxSendRecvMsgFDs);\n      for (size_t index = 0; index < fd_count; ++index) {\n        if (fds) {\n          local_fds.emplace_back(fdp[index]);\n        } else if (IGNORE_EINTR(close(fdp[index])) != 0) {\n          PLOG(ERROR) << \"close\";\n        }\n      }\n      continue;\n    }\n\n    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) {\n      DCHECK(!local_creds);\n      local_creds = reinterpret_cast<ucred*>(CMSG_DATA(cmsg));\n      continue;\n    }\n\n    LOG(ERROR) << \"unhandled cmsg \" << cmsg->cmsg_level << \", \"\n               << cmsg->cmsg_type;\n    unhandled_cmsgs = true;\n  }\n\n  if (unhandled_cmsgs) {\n    return false;\n  }\n\n  if (msg.msg_name != nullptr || msg.msg_namelen != 0) {\n    LOG(ERROR) << \"unexpected msg name\";\n    return false;\n  }\n\n  if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {\n    LOG(ERROR) << \"truncated msg\";\n    return false;\n  }\n\n  // Credentials are missing from the message either when the recv socket wasn't\n  // configured with SO_PASSCRED or when all sending sockets have been closed.\n  // In the latter case, res == 0. This case is also indistinguishable from an\n  // empty message sent to a recv socket which hasn't set SO_PASSCRED.\n  if (!local_creds) {\n    LOG_IF(ERROR, res != 0) << \"missing credentials\";\n    return false;\n  }\n\n  if (static_cast<size_t>(res) != buf_size) {\n    LOG(ERROR) << \"incorrect payload size \" << res;\n    return false;\n  }\n\n  *creds = *local_creds;\n  if (fds) {\n    fds->swap(local_fds);\n  }\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/socket.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_SOCKET_H_\n#define CRASHPAD_UTIL_LINUX_SOCKET_H_\n\n#include <sys/socket.h>\n#include <sys/types.h>\n\n#include <vector>\n\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\n\n//! \\brief Utilities for communicating over `SO_PASSCRED` enabled `AF_UNIX`\n//!     sockets.\nclass UnixCredentialSocket {\n public:\n  UnixCredentialSocket() = delete;\n  UnixCredentialSocket(const UnixCredentialSocket&) = delete;\n  UnixCredentialSocket& operator=(const UnixCredentialSocket&) = delete;\n\n  //! \\brief Creates an `AF_UNIX` family socket pair with `SO_PASSCRED` set on\n  //!     each socket.\n  //!\n  //! \\param[out] s1 One end of the connected pair.\n  //! \\param[out] s2 The other end of the connected pair.\n  //! \\return `true` on success. Otherwise, `false` with a message logged.\n  static bool CreateCredentialSocketpair(ScopedFileHandle* s1,\n                                         ScopedFileHandle* s2);\n\n  //! \\brief The maximum number of file descriptors that may be sent/received\n  //!     with `SendMsg()` or `RecvMsg()`.\n  static constexpr size_t kMaxSendRecvMsgFDs = 4;\n\n  //! \\brief Wraps `sendmsg()` to send a message with file descriptors.\n  //!\n  //! This function is intended for use with `AF_UNIX` family sockets and\n  //! passes file descriptors with `SCM_RIGHTS`.\n  //!\n  //! This function may be used in a compromised context.\n  //!\n  //! \\param[in] fd The file descriptor to write the message to.\n  //! \\param[in] buf The buffer containing the message.\n  //! \\param[in] buf_size The size of the message.\n  //! \\param[in] fds An array of at most `kMaxSendRecvMsgFDs` file descriptors.\n  //!     Optional.\n  //! \\param[in] fd_count The number of file descriptors in \\a fds. Required\n  //!     only if \\a fds was set.\n  //! \\return 0 on success or an error code on failure.\n  static int SendMsg(int fd,\n                     const void* buf,\n                     size_t buf_size,\n                     const int* fds = nullptr,\n                     size_t fd_count = 0);\n\n  //! \\brief Wraps `recvmsg()` to receive a message with file descriptors and\n  //!     credentials.\n  //!\n  //! This function is intended to be used with `AF_UNIX` family sockets. Up to\n  //! `kMaxSendRecvMsgFDs` file descriptors may be received (via `SCM_RIGHTS`).\n  //! The socket must have `SO_PASSCRED` set.\n  //!\n  //! \\param[in] fd The file descriptor to receive the message on.\n  //! \\param[out] buf The buffer to fill with the message.\n  //! \\param[in] buf_size The size of the message.\n  //! \\param[out] creds The credentials of the sender.\n  //! \\param[out] fds The recieved file descriptors. Optional. If `nullptr`, all\n  //!     received file descriptors will be closed.\n  //! \\return `true` on success. Otherwise, `false`, with a message logged. No\n  //!     message will be logged if the message was detected to be an EOF\n  //!     condition triggered by all clients disconnecting. This case is\n  //!     indistinguishable from misuses of this interface that haven't set\n  //!     `SO_PASSCRED` on \\a fd.\n  static bool RecvMsg(int fd,\n                      void* buf,\n                      size_t buf_size,\n                      ucred* creds,\n                      std::vector<ScopedFileHandle>* fds = nullptr);\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_SOCKET_H_\n"
  },
  {
    "path": "util/linux/socket_test.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/socket.h\"\n\n#include \"base/posix/eintr_wrapper.h\"\n#include \"gtest/gtest.h\"\n#include \"util/linux/socket.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Socket, Credentials) {\n  ScopedFileHandle send_sock, recv_sock;\n  ASSERT_TRUE(\n      UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));\n\n  char msg = 42;\n  ASSERT_EQ(UnixCredentialSocket::SendMsg(send_sock.get(), &msg, sizeof(msg)),\n            0);\n\n  char recv_msg = 0;\n  ucred creds;\n  ASSERT_TRUE(UnixCredentialSocket::RecvMsg(\n      recv_sock.get(), &recv_msg, sizeof(recv_msg), &creds));\n  EXPECT_EQ(recv_msg, msg);\n  EXPECT_EQ(creds.pid, getpid());\n  EXPECT_EQ(creds.uid, geteuid());\n  EXPECT_EQ(creds.gid, getegid());\n}\n\nTEST(Socket, EmptyMessages) {\n  ScopedFileHandle send_sock, recv_sock;\n  ASSERT_TRUE(\n      UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));\n\n  ASSERT_EQ(UnixCredentialSocket::SendMsg(send_sock.get(), nullptr, 0), 0);\n\n  ucred creds;\n  ASSERT_TRUE(\n      UnixCredentialSocket::RecvMsg(recv_sock.get(), nullptr, 0, &creds));\n  EXPECT_EQ(creds.pid, getpid());\n  EXPECT_EQ(creds.uid, geteuid());\n  EXPECT_EQ(creds.gid, getegid());\n}\n\nTEST(Socket, Hangup) {\n  ScopedFileHandle send_sock, recv_sock;\n  ASSERT_TRUE(\n      UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));\n\n  send_sock.reset();\n\n  char recv_msg = 0;\n  ucred creds;\n  EXPECT_FALSE(UnixCredentialSocket::RecvMsg(\n      recv_sock.get(), &recv_msg, sizeof(recv_msg), &creds));\n}\n\nTEST(Socket, FileDescriptors) {\n  ScopedFileHandle send_sock, recv_sock;\n  ASSERT_TRUE(\n      UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));\n\n  ScopedFileHandle test_fd1, test_fd2;\n  ASSERT_TRUE(\n      UnixCredentialSocket::CreateCredentialSocketpair(&test_fd1, &test_fd2));\n\n  char msg = 42;\n  ASSERT_EQ(UnixCredentialSocket::SendMsg(\n                send_sock.get(), &msg, sizeof(msg), &test_fd1.get(), 1),\n            0);\n\n  char recv_msg = 0;\n  ucred creds;\n  std::vector<ScopedFileHandle> fds;\n  ASSERT_TRUE(UnixCredentialSocket::RecvMsg(\n      recv_sock.get(), &recv_msg, sizeof(recv_msg), &creds, &fds));\n  ASSERT_EQ(fds.size(), 1u);\n}\n\nTEST(Socket, RecvClosesFileDescriptors) {\n  ScopedFileHandle send_sock, recv_sock;\n  ASSERT_TRUE(\n      UnixCredentialSocket::CreateCredentialSocketpair(&send_sock, &recv_sock));\n\n  ScopedFileHandle send_fds[UnixCredentialSocket::kMaxSendRecvMsgFDs];\n  ScopedFileHandle recv_fds[UnixCredentialSocket::kMaxSendRecvMsgFDs];\n  int raw_recv_fds[UnixCredentialSocket::kMaxSendRecvMsgFDs];\n  for (size_t index = 0; index < UnixCredentialSocket::kMaxSendRecvMsgFDs;\n       ++index) {\n    ASSERT_TRUE(UnixCredentialSocket::CreateCredentialSocketpair(\n        &send_fds[index], &recv_fds[index]));\n    raw_recv_fds[index] = recv_fds[index].get();\n  }\n\n  char msg = 42;\n  ASSERT_EQ(\n      UnixCredentialSocket::SendMsg(send_sock.get(),\n                                    &msg,\n                                    sizeof(msg),\n                                    raw_recv_fds,\n                                    UnixCredentialSocket::kMaxSendRecvMsgFDs),\n      0);\n\n  char recv_msg = 0;\n  ucred creds;\n  ASSERT_TRUE(UnixCredentialSocket::RecvMsg(\n      recv_sock.get(), &recv_msg, sizeof(recv_msg), &creds));\n  EXPECT_EQ(creds.pid, getpid());\n\n  for (size_t index = 0; index < UnixCredentialSocket::kMaxSendRecvMsgFDs;\n       ++index) {\n    recv_fds[index].reset();\n    char c = 0;\n    EXPECT_EQ(\n        HANDLE_EINTR(send(send_fds[index].get(), &c, sizeof(c), MSG_NOSIGNAL)),\n        -1);\n    EXPECT_EQ(errno, EPIPE);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/thread_info.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/linux/thread_info.h\"\n\n#include <string.h>\n\nnamespace crashpad {\n\nThreadContext::ThreadContext() {\n  memset(static_cast<void*>(this), 0, sizeof(*this));\n}\n\nThreadContext::~ThreadContext() {}\n\nFloatContext::FloatContext() {\n  memset(static_cast<void*>(this), 0, sizeof(*this));\n}\n\nFloatContext::~FloatContext() {}\n\nThreadInfo::ThreadInfo()\n    : thread_context(), float_context(), thread_specific_data_address(0) {}\n\nThreadInfo::~ThreadInfo() {}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/linux/thread_info.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_THREAD_INFO_H_\n#define CRASHPAD_UTIL_LINUX_THREAD_INFO_H_\n\n#include <features.h>\n#include <stdint.h>\n#include <sys/user.h>\n\n#include <type_traits>\n\n#include \"build/build_config.h\"\n#include \"util/linux/address_types.h\"\n#include \"util/numeric/int128.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#endif\n\n// x86_64 has compilation errors if asm/ptrace.h is #included.\n#if defined(ARCH_CPU_RISCV64)\n#include <asm/ptrace.h>\n#endif\n\nnamespace crashpad {\n\n//! \\brief The set of general purpose registers for an architecture family.\nunion ThreadContext {\n  ThreadContext();\n  ~ThreadContext();\n\n  //! \\brief The general purpose registers used by the 32-bit variant of the\n  //!     architecture.\n  struct t32_t {\n#if defined(ARCH_CPU_X86_FAMILY)\n    // Reflects user_regs_struct in sys/user.h.\n    uint32_t ebx;\n    uint32_t ecx;\n    uint32_t edx;\n    uint32_t esi;\n    uint32_t edi;\n    uint32_t ebp;\n    uint32_t eax;\n    uint32_t xds;\n    uint32_t xes;\n    uint32_t xfs;\n    uint32_t xgs;\n    uint32_t orig_eax;\n    uint32_t eip;\n    uint32_t xcs;\n    uint32_t eflags;\n    uint32_t esp;\n    uint32_t xss;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n    // Reflects user_regs in sys/user.h.\n    uint32_t regs[11];\n    uint32_t fp;\n    uint32_t ip;\n    uint32_t sp;\n    uint32_t lr;\n    uint32_t pc;\n    uint32_t cpsr;\n    uint32_t orig_r0;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n    // Reflects output format of static int gpr32_get(), defined in\n    // arch/mips/kernel/ptrace.c in kernel source\n    uint32_t padding0_[6];\n    uint32_t regs[32];\n    uint32_t lo;\n    uint32_t hi;\n    uint32_t cp0_epc;\n    uint32_t cp0_badvaddr;\n    uint32_t cp0_status;\n    uint32_t cp0_cause;\n    uint32_t padding1_;\n#elif defined(ARCH_CPU_RISCV64)\n    // 32 bit RISC-V not supported\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY\n  } t32;\n\n  //! \\brief The general purpose registers used by the 64-bit variant of the\n  //!     architecture.\n  struct t64_t {\n#if defined(ARCH_CPU_X86_FAMILY)\n    // Reflects user_regs_struct in sys/user.h.\n    uint64_t r15;\n    uint64_t r14;\n    uint64_t r13;\n    uint64_t r12;\n    uint64_t rbp;\n    uint64_t rbx;\n    uint64_t r11;\n    uint64_t r10;\n    uint64_t r9;\n    uint64_t r8;\n    uint64_t rax;\n    uint64_t rcx;\n    uint64_t rdx;\n    uint64_t rsi;\n    uint64_t rdi;\n    uint64_t orig_rax;\n    uint64_t rip;\n    uint64_t cs;\n    uint64_t eflags;\n    uint64_t rsp;\n    uint64_t ss;\n    uint64_t fs_base;\n    uint64_t gs_base;\n    uint64_t ds;\n    uint64_t es;\n    uint64_t fs;\n    uint64_t gs;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n    // Reflects user_regs_struct in sys/user.h.\n    uint64_t regs[31];\n    uint64_t sp;\n    uint64_t pc;\n    uint64_t pstate;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n    // Reflects output format of static int gpr64_get(), defined in\n    // arch/mips/kernel/ptrace.c in kernel source\n    uint64_t regs[32];\n    uint64_t lo;\n    uint64_t hi;\n    uint64_t cp0_epc;\n    uint64_t cp0_badvaddr;\n    uint64_t cp0_status;\n    uint64_t cp0_cause;\n#elif defined(ARCH_CPU_RISCV64)\n    // Reflects user_regs_struct in asm/ptrace.h.\n    uint64_t pc;\n    uint64_t regs[31];\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY\n  } t64;\n\n#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64) || \\\n    defined(ARCH_CPU_RISCV64)\n  using NativeThreadContext = user_regs_struct;\n#elif defined(ARCH_CPU_ARMEL)\n  using NativeThreadContext = user_regs;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n// No appropriate NativeThreadsContext type available for MIPS\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY || ARCH_CPU_ARM64 || ARCH_CPU_RISCV64\n\n#if !defined(ARCH_CPU_MIPS_FAMILY)\n#if defined(ARCH_CPU_32_BITS)\n  static_assert(sizeof(t32_t) == sizeof(NativeThreadContext), \"Size mismatch\");\n#else  // ARCH_CPU_64_BITS\n  static_assert(sizeof(t64_t) == sizeof(NativeThreadContext), \"Size mismatch\");\n#endif  // ARCH_CPU_32_BITS\n#endif  // !ARCH_CPU_MIPS_FAMILY\n};\nstatic_assert(std::is_standard_layout<ThreadContext>::value,\n              \"Not standard layout\");\n\n//! \\brief The floating point registers used for an architecture family.\nunion FloatContext {\n  FloatContext();\n  ~FloatContext();\n\n  //! \\brief The floating point registers used by the 32-bit variant of the\n  //!     architecture.\n  struct f32_t {\n#if defined(ARCH_CPU_X86_FAMILY)\n    // Reflects user_fpxregs_struct in sys/user.h\n    struct fxsave {\n      uint16_t cwd;\n      uint16_t swd;\n      uint16_t twd;\n      uint16_t fop;\n      uint32_t fip;\n      uint32_t fcs;\n      uint32_t foo;\n      uint32_t fos;\n      uint32_t mxcsr;\n      uint32_t reserved;\n      uint32_t st_space[32];\n      uint32_t xmm_space[32];\n      uint32_t padding[56];\n    } fxsave;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n    // Reflects user_fpregs in sys/user.h.\n    struct fpregs {\n      struct fp_reg {\n        uint32_t sign1 : 1;\n        uint32_t unused : 15;\n        uint32_t sign2 : 1;\n        uint32_t exponent : 14;\n        uint32_t j : 1;\n        uint32_t mantissa1 : 31;\n        uint32_t mantisss0 : 32;\n      } fpregs[8];\n      uint32_t fpsr : 32;\n      uint32_t fpcr : 32;\n      uint8_t type[8];\n      uint32_t init_flag;\n    } fpregs;\n\n    // Reflects user_vfp in sys/user.h.\n    struct vfp_t {\n      uint64_t fpregs[32];\n      uint32_t fpscr;\n    } vfp;\n\n    bool have_fpregs;\n    bool have_vfp;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n    // Reflects data format filled by ptrace_getfpregs() in\n    // arch/mips/kernel/ptrace.c\n    struct {\n      float _fp_fregs;\n      unsigned int _fp_pad;\n    } fpregs[32];\n    uint32_t fpcsr;\n    uint32_t fpu_id;\n#elif defined(ARCH_CPU_RISCV64)\n    // 32 bit RISC-V not supported\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY\n  } f32;\n\n  //! \\brief The floating point registers used by the 64-bit variant of the\n  //!     architecture.\n  struct f64_t {\n#if defined(ARCH_CPU_X86_FAMILY)\n    // Refelects user_fpregs_struct in sys/user.h\n    struct fxsave {\n      uint16_t cwd;\n      uint16_t swd;\n      uint16_t ftw;\n      uint16_t fop;\n      uint64_t rip;\n      uint64_t rdp;\n      uint32_t mxcsr;\n      uint32_t mxcr_mask;\n      uint32_t st_space[32];\n      uint32_t xmm_space[64];\n      uint32_t padding[24];\n    } fxsave;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n    uint128_struct vregs[32];\n    uint32_t fpsr;\n    uint32_t fpcr;\n    uint8_t padding[8];\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n    // Reflects data format filled by ptrace_getfpregs() in\n    // arch/mips/kernel/ptrace.c\n    double fpregs[32];\n    uint32_t fpcsr;\n    uint32_t fpu_id;\n#elif defined(ARCH_CPU_RISCV64)\n    // Reflects __riscv_d_ext_state in asm/ptrace.h\n    uint64_t fpregs[32];\n    uint64_t fcsr;\n#else\n#error Port.\n#endif  // ARCH_CPU_X86_FAMILY\n  } f64;\n\n#if defined(ARCH_CPU_X86)\n// __ANDROID_API_N__ is a proxy for determining whether unified headers are in\n// use. It’s only defined by unified headers. Unified headers call this\n// structure user_fpxregs_struct regardless of API level.\n#if BUILDFLAG(IS_ANDROID) && __ANDROID_API__ <= 19 && \\\n    !defined(__ANDROID_API_N__)\n  using NativeFpxregs = user_fxsr_struct;\n#else\n  using NativeFpxregs = user_fpxregs_struct;\n#endif  // BUILDFLAG(IS_ANDROID)\n  static_assert(sizeof(f32_t::fxsave) == sizeof(NativeFpxregs),\n                \"Size mismatch\");\n#elif defined(ARCH_CPU_X86_64)\n  static_assert(sizeof(f64_t::fxsave) == sizeof(user_fpregs_struct),\n                \"Size mismatch\");\n#elif defined(ARCH_CPU_ARMEL)\n  static_assert(sizeof(f32_t::fpregs) == sizeof(user_fpregs), \"Size mismatch\");\n#if defined(__BIONIC__)\n  static_assert(sizeof(f32_t::vfp) == sizeof(user_vfp), \"Size mismatch\");\n#endif\n#elif defined(ARCH_CPU_ARM64)\n  static_assert(sizeof(f64) == sizeof(user_fpsimd_struct), \"Size mismatch\");\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n// No appropriate floating point context native type for available MIPS.\n#elif defined(ARCH_CPU_RISCV64)\n  static_assert(sizeof(f64) == sizeof(__riscv_d_ext_state), \"Size mismatch\");\n#else\n#error Port.\n#endif  // ARCH_CPU_X86\n};\nstatic_assert(std::is_standard_layout<FloatContext>::value,\n              \"Not standard layout\");\n\n//! \\brief A collection of `ptrace`-able information about a thread.\nstruct ThreadInfo {\n  ThreadInfo();\n  ~ThreadInfo();\n\n  //! \\brief The general purpose registers for the thread.\n  ThreadContext thread_context;\n\n  //! \\brief The floating point registers for the thread.\n  FloatContext float_context;\n\n  //! \\brief The thread-local storage address for the thread.\n  LinuxVMAddress thread_specific_data_address;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_THREAD_INFO_H_\n"
  },
  {
    "path": "util/linux/traits.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_LINUX_TRAITS_H_\n#define CRASHPAD_UTIL_LINUX_TRAITS_H_\n\n#include <stdint.h>\n\nnamespace crashpad {\n\nstruct Traits32 {\n  using Nothing = char[0];\n  using Address = uint32_t;\n  using Long = int32_t;\n  using ULong = uint32_t;\n  using Clock = Long;\n  using Size = uint32_t;\n  using Char_64Only = Nothing;\n  using ULong_32Only = ULong;\n  using ULong_64Only = Nothing;\n  using UInteger32_64Only = Nothing;\n};\n\nstruct Traits64 {\n  using Nothing = char[0];\n  using Address = uint64_t;\n  using Long = int64_t;\n  using ULong = uint64_t;\n  using Clock = Long;\n  using Size = uint64_t;\n  using Char_64Only = char;\n  using ULong_32Only = Nothing;\n  using ULong_64Only = ULong;\n  using UInteger32_64Only = uint32_t;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_LINUX_TRAITS_H_\n"
  },
  {
    "path": "util/mac/checked_mach_address_range.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_\n#define CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_\n\n#include <mach/mach.h>\n\n#include \"util/numeric/checked_address_range.h\"\n\nnamespace crashpad {\n\n//! \\brief Ensures that a range, composed of a base and a size, does not\n//!     overflow the pointer type of the process it describes a range in.\n//!\n//! This class checks bases of type `mach_vm_address_t` and sizes of type\n//! `mach_vm_address_t` against a process whose pointer type is either 32 or 64\n//! bits wide.\n//!\n//! Aside from varying the overall range on the basis of a process’ pointer type\n//! width, this class functions very similarly to CheckedRange.\nusing CheckedMachAddressRange =\n    internal::CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_\n"
  },
  {
    "path": "util/mac/checked_mach_address_range_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/checked_mach_address_range.h\"\n\n#include <mach/mach.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <limits>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nenum Validity {\n  kInvalid = false,\n  kValid,\n  kValid64Invalid32,\n};\n\nbool ExpectationForValidity32(Validity validity) {\n  return validity == kValid;\n}\n\nbool ExpectationForValidity64(Validity validity) {\n  return validity == kValid || validity == kValid64Invalid32;\n}\n\nTEST(CheckedMachAddressRange, IsValid) {\n  static constexpr struct {\n    mach_vm_address_t base;\n    mach_vm_size_t size;\n    Validity validity;\n  } kTestData[] = {\n      {0, 0, kValid},\n      {0, 1, kValid},\n      {0, 2, kValid},\n      {0, 0x7fffffff, kValid},\n      {0, 0x80000000, kValid},\n      {0, 0xfffffffe, kValid},\n      {0, 0xffffffff, kValid},\n      {0, 0xffffffffffffffff, kValid64Invalid32},\n      {1, 0, kValid},\n      {1, 1, kValid},\n      {1, 2, kValid},\n      {1, 0x7fffffff, kValid},\n      {1, 0x80000000, kValid},\n      {1, 0xfffffffe, kValid},\n      {1, 0xffffffff, kValid64Invalid32},\n      {1, 0xfffffffffffffffe, kValid64Invalid32},\n      {1, 0xffffffffffffffff, kInvalid},\n      {0x7fffffff, 0, kValid},\n      {0x7fffffff, 1, kValid},\n      {0x7fffffff, 2, kValid},\n      {0x7fffffff, 0x7fffffff, kValid},\n      {0x7fffffff, 0x80000000, kValid},\n      {0x7fffffff, 0xfffffffe, kValid64Invalid32},\n      {0x7fffffff, 0xffffffff, kValid64Invalid32},\n      {0x80000000, 0, kValid},\n      {0x80000000, 1, kValid},\n      {0x80000000, 2, kValid},\n      {0x80000000, 0x7fffffff, kValid},\n      {0x80000000, 0x80000000, kValid64Invalid32},\n      {0x80000000, 0xfffffffe, kValid64Invalid32},\n      {0x80000000, 0xffffffff, kValid64Invalid32},\n      {0xfffffffe, 0, kValid},\n      {0xfffffffe, 1, kValid},\n      {0xfffffffe, 2, kValid64Invalid32},\n      {0xfffffffe, 0x7fffffff, kValid64Invalid32},\n      {0xfffffffe, 0x80000000, kValid64Invalid32},\n      {0xfffffffe, 0xfffffffe, kValid64Invalid32},\n      {0xfffffffe, 0xffffffff, kValid64Invalid32},\n      {0xffffffff, 0, kValid},\n      {0xffffffff, 1, kValid64Invalid32},\n      {0xffffffff, 2, kValid64Invalid32},\n      {0xffffffff, 0x7fffffff, kValid64Invalid32},\n      {0xffffffff, 0x80000000, kValid64Invalid32},\n      {0xffffffff, 0xfffffffe, kValid64Invalid32},\n      {0xffffffff, 0xffffffff, kValid64Invalid32},\n      {0x7fffffffffffffff, 0, kValid64Invalid32},\n      {0x7fffffffffffffff, 1, kValid64Invalid32},\n      {0x7fffffffffffffff, 2, kValid64Invalid32},\n      {0x7fffffffffffffff, 0x7fffffffffffffff, kValid64Invalid32},\n      {0x7fffffffffffffff, 0x8000000000000000, kValid64Invalid32},\n      {0x7fffffffffffffff, 0x8000000000000001, kInvalid},\n      {0x7fffffffffffffff, 0xfffffffffffffffe, kInvalid},\n      {0x7fffffffffffffff, 0xffffffffffffffff, kInvalid},\n      {0x8000000000000000, 0, kValid64Invalid32},\n      {0x8000000000000000, 1, kValid64Invalid32},\n      {0x8000000000000000, 2, kValid64Invalid32},\n      {0x8000000000000000, 0x7fffffffffffffff, kValid64Invalid32},\n      {0x8000000000000000, 0x8000000000000000, kInvalid},\n      {0x8000000000000000, 0x8000000000000001, kInvalid},\n      {0x8000000000000000, 0xfffffffffffffffe, kInvalid},\n      {0x8000000000000000, 0xffffffffffffffff, kInvalid},\n      {0xfffffffffffffffe, 0, kValid64Invalid32},\n      {0xfffffffffffffffe, 1, kValid64Invalid32},\n      {0xfffffffffffffffe, 2, kInvalid},\n      {0xffffffffffffffff, 0, kValid64Invalid32},\n      {0xffffffffffffffff, 1, kInvalid},\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"index %zu, base 0x%llx, size 0x%llx\",\n                                    index,\n                                    testcase.base,\n                                    testcase.size));\n\n    CheckedMachAddressRange range_32(false, testcase.base, testcase.size);\n    EXPECT_EQ(range_32.IsValid(), ExpectationForValidity32(testcase.validity));\n\n    CheckedMachAddressRange range_64(true, testcase.base, testcase.size);\n    EXPECT_EQ(range_64.IsValid(), ExpectationForValidity64(testcase.validity));\n  }\n}\n\nTEST(CheckedMachAddressRange, ContainsValue) {\n  static constexpr struct {\n    mach_vm_address_t value;\n    bool expectation;\n  } kTestData[] = {\n      {0, false},\n      {1, false},\n      {0x1fff, false},\n      {0x2000, true},\n      {0x2001, true},\n      {0x2ffe, true},\n      {0x2fff, true},\n      {0x3000, false},\n      {0x3001, false},\n      {0x7fffffff, false},\n      {0x80000000, false},\n      {0x80000001, false},\n      {0x80001fff, false},\n      {0x80002000, false},\n      {0x80002001, false},\n      {0x80002ffe, false},\n      {0x80002fff, false},\n      {0x80003000, false},\n      {0x80003001, false},\n      {0xffffcfff, false},\n      {0xffffdfff, false},\n      {0xffffefff, false},\n      {0xffffffff, false},\n      {0x100000000, false},\n      {0xffffffffffffffff, false},\n  };\n\n  CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000);\n  ASSERT_TRUE(parent_range_32.IsValid());\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(\n        base::StringPrintf(\"index %zu, value 0x%llx\", index, testcase.value));\n\n    EXPECT_EQ(parent_range_32.ContainsValue(testcase.value),\n              testcase.expectation);\n  }\n\n  CheckedMachAddressRange parent_range_64(true, 0x100000000, 0x1000);\n  ASSERT_TRUE(parent_range_64.IsValid());\n  EXPECT_FALSE(parent_range_64.ContainsValue(0xffffffff));\n  EXPECT_TRUE(parent_range_64.ContainsValue(0x100000000));\n  EXPECT_TRUE(parent_range_64.ContainsValue(0x100000001));\n  EXPECT_TRUE(parent_range_64.ContainsValue(0x100000fff));\n  EXPECT_FALSE(parent_range_64.ContainsValue(0x100001000));\n}\n\nTEST(CheckedMachAddressRange, ContainsRange) {\n  static constexpr struct {\n    mach_vm_address_t base;\n    mach_vm_size_t size;\n    bool expectation;\n  } kTestData[] = {\n      {0, 0, false},\n      {0, 1, false},\n      {0x2000, 0x1000, true},\n      {0, 0x2000, false},\n      {0x3000, 0x1000, false},\n      {0x1800, 0x1000, false},\n      {0x2800, 0x1000, false},\n      {0x2000, 0x800, true},\n      {0x2800, 0x800, true},\n      {0x2400, 0x800, true},\n      {0x2800, 0, true},\n      {0x2000, 0xffffdfff, false},\n      {0x2800, 0xffffd7ff, false},\n      {0x3000, 0xffffcfff, false},\n      {0xfffffffe, 1, false},\n      {0xffffffff, 0, false},\n      {0x1fff, 0, false},\n      {0x2000, 0, true},\n      {0x2001, 0, true},\n      {0x2fff, 0, true},\n      {0x3000, 0, true},\n      {0x3001, 0, false},\n      {0x1fff, 1, false},\n      {0x2000, 1, true},\n      {0x2001, 1, true},\n      {0x2fff, 1, true},\n      {0x3000, 1, false},\n      {0x3001, 1, false},\n  };\n\n  CheckedMachAddressRange parent_range_32(false, 0x2000, 0x1000);\n  ASSERT_TRUE(parent_range_32.IsValid());\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"index %zu, base 0x%llx, size 0x%llx\",\n                                    index,\n                                    testcase.base,\n                                    testcase.size));\n\n    CheckedMachAddressRange child_range_32(false, testcase.base, testcase.size);\n    ASSERT_TRUE(child_range_32.IsValid());\n    EXPECT_EQ(parent_range_32.ContainsRange(child_range_32),\n              testcase.expectation);\n  }\n\n  CheckedMachAddressRange parent_range_64(true, 0x100000000, 0x1000);\n  ASSERT_TRUE(parent_range_64.IsValid());\n\n  CheckedMachAddressRange child_range_64(true, 0xffffffff, 2);\n  EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));\n\n  child_range_64.SetRange(true, 0x100000000, 2);\n  EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));\n\n  child_range_64.SetRange(true, 0x100000ffe, 2);\n  EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));\n\n  child_range_64.SetRange(true, 0x100000fff, 2);\n  EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/launchd.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MAC_LAUNCHD_H_\n#define CRASHPAD_UTIL_MAC_LAUNCHD_H_\n\n#include <CoreFoundation/CoreFoundation.h>\n#include <launch.h>\n#include <sys/types.h>\n\nnamespace crashpad {\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n\n//! \\{\n//! \\brief Wraps the `<launch.h>` function of the same name.\n//!\n//! The OS X 10.10 SDK deprecates `<launch.h>`, although the functionality it\n//! provides is still useful. These wrappers allow the deprecated functions to\n//! be called without triggering deprecated-declaration warnings.\n\ninline launch_data_t LaunchDataAlloc(launch_data_type_t type) {\n  return launch_data_alloc(type);\n}\n\ninline launch_data_type_t LaunchDataGetType(const launch_data_t data) {\n  return launch_data_get_type(data);\n}\n\ninline void LaunchDataFree(launch_data_t data) {\n  return launch_data_free(data);\n}\n\ninline bool LaunchDataDictInsert(launch_data_t dict,\n                                 const launch_data_t value,\n                                 const char* key) {\n  return launch_data_dict_insert(dict, value, key);\n}\n\ninline launch_data_t LaunchDataDictLookup(const launch_data_t dict,\n                                          const char* key) {\n  return launch_data_dict_lookup(dict, key);\n}\n\ninline size_t LaunchDataDictGetCount(launch_data_t dict) {\n  return launch_data_dict_get_count(dict);\n}\n\ninline bool LaunchDataArraySetIndex(launch_data_t array,\n                                    const launch_data_t value,\n                                    size_t index) {\n  return launch_data_array_set_index(array, value, index);\n}\n\ninline launch_data_t LaunchDataArrayGetIndex(launch_data_t array,\n                                             size_t index) {\n  return launch_data_array_get_index(array, index);\n}\n\ninline size_t LaunchDataArrayGetCount(launch_data_t array) {\n  return launch_data_array_get_count(array);\n}\n\ninline launch_data_t LaunchDataNewInteger(long long integer) {\n  return launch_data_new_integer(integer);\n}\n\ninline launch_data_t LaunchDataNewBool(bool boolean) {\n  return launch_data_new_bool(boolean);\n}\n\ninline launch_data_t LaunchDataNewReal(double real) {\n  return launch_data_new_real(real);\n}\n\ninline launch_data_t LaunchDataNewString(const char* string) {\n  return launch_data_new_string(string);\n}\n\ninline launch_data_t LaunchDataNewOpaque(const void* opaque, size_t size) {\n  return launch_data_new_opaque(opaque, size);\n}\n\ninline long long LaunchDataGetInteger(const launch_data_t data) {\n  return launch_data_get_integer(data);\n}\n\ninline bool LaunchDataGetBool(const launch_data_t data) {\n  return launch_data_get_bool(data);\n}\n\ninline double LaunchDataGetReal(const launch_data_t data) {\n  return launch_data_get_real(data);\n}\n\ninline const char* LaunchDataGetString(const launch_data_t data) {\n  return launch_data_get_string(data);\n}\n\ninline void* LaunchDataGetOpaque(const launch_data_t data) {\n  return launch_data_get_opaque(data);\n}\n\ninline size_t LaunchDataGetOpaqueSize(const launch_data_t data) {\n  return launch_data_get_opaque_size(data);\n}\n\ninline int LaunchDataGetErrno(const launch_data_t data) {\n  return launch_data_get_errno(data);\n}\n\ninline launch_data_t LaunchMsg(const launch_data_t data) {\n  return launch_msg(data);\n}\n\n//! \\}\n\n#pragma clang diagnostic pop\n\n//! \\brief Converts a Core Foundation-type property list to a launchd-type\n//!     `launch_data_t`.\n//!\n//! \\param[in] property_cf The Core Foundation-type property list to convert.\n//!\n//! \\return The converted launchd-type `launch_data_t`. The caller takes\n//!     ownership of the returned value. On error, returns `nullptr`.\n//!\n//! \\note This function handles all `CFPropertyListRef` types except for\n//!     `CFDateRef`, because there’s no `launch_data_type_t` analogue. Not all\n//!     types supported in a launchd-type `launch_data_t` have\n//!     `CFPropertyListRef` analogues.\nlaunch_data_t CFPropertyToLaunchData(CFPropertyListRef property_cf);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MAC_LAUNCHD_H_\n"
  },
  {
    "path": "util/mac/launchd.mm",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/launchd.h\"\n\n#import <Foundation/Foundation.h>\n\n#include \"base/apple/bridging.h\"\n#include \"base/apple/foundation_util.h\"\n#include \"base/apple/scoped_cftyperef.h\"\n#include \"base/mac/scoped_launch_data.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\n\nlaunch_data_t CFPropertyToLaunchData(CFPropertyListRef property_cf) {\n  @autoreleasepool {\n    // This function mixes Foundation and Core Foundation access to property\n    // list elements according to which is more convenient and correct for any\n    // specific task.\n\n    launch_data_t data_launch = nullptr;\n    CFTypeID type_id_cf = CFGetTypeID(property_cf);\n\n    if (type_id_cf == CFDictionaryGetTypeID()) {\n      NSDictionary* dictionary_ns = base::apple::CFToNSPtrCast(\n          base::apple::CFCastStrict<CFDictionaryRef>(property_cf));\n      base::mac::ScopedLaunchData dictionary_launch(\n          LaunchDataAlloc(LAUNCH_DATA_DICTIONARY));\n\n      for (NSString* key in dictionary_ns) {\n        if (![key isKindOfClass:[NSString class]]) {\n          return nullptr;\n        }\n\n        CFPropertyListRef value_cf =\n            (__bridge CFPropertyListRef)dictionary_ns[key];\n        launch_data_t value_launch = CFPropertyToLaunchData(value_cf);\n        if (!value_launch) {\n          return nullptr;\n        }\n\n        LaunchDataDictInsert(\n            dictionary_launch.get(), value_launch, [key UTF8String]);\n      }\n\n      data_launch = dictionary_launch.release();\n\n    } else if (type_id_cf == CFArrayGetTypeID()) {\n      NSArray* array_ns = base::apple::CFToNSPtrCast(\n          base::apple::CFCastStrict<CFArrayRef>(property_cf));\n      base::mac::ScopedLaunchData array_launch(\n          LaunchDataAlloc(LAUNCH_DATA_ARRAY));\n      size_t index = 0;\n\n      for (id element_ns in array_ns) {\n        CFPropertyListRef element_cf = (__bridge CFPropertyListRef)element_ns;\n        launch_data_t element_launch = CFPropertyToLaunchData(element_cf);\n        if (!element_launch) {\n          return nullptr;\n        }\n\n        LaunchDataArraySetIndex(array_launch.get(), element_launch, index++);\n      }\n\n      data_launch = array_launch.release();\n\n    } else if (type_id_cf == CFNumberGetTypeID()) {\n      CFNumberRef number_cf =\n          base::apple::CFCastStrict<CFNumberRef>(property_cf);\n      NSNumber* number_ns = base::apple::CFToNSPtrCast(number_cf);\n      switch (CFNumberGetType(number_cf)) {\n        case kCFNumberSInt8Type:\n        case kCFNumberSInt16Type:\n        case kCFNumberSInt32Type:\n        case kCFNumberSInt64Type:\n        case kCFNumberCharType:\n        case kCFNumberShortType:\n        case kCFNumberIntType:\n        case kCFNumberLongType:\n        case kCFNumberLongLongType:\n        case kCFNumberCFIndexType:\n        case kCFNumberNSIntegerType: {\n          data_launch = LaunchDataNewInteger([number_ns longLongValue]);\n          break;\n        }\n\n        case kCFNumberFloat32Type:\n        case kCFNumberFloat64Type:\n        case kCFNumberFloatType:\n        case kCFNumberDoubleType: {\n          data_launch = LaunchDataNewReal([number_ns doubleValue]);\n          break;\n        }\n\n        default: { return nullptr; }\n      }\n\n    } else if (type_id_cf == CFBooleanGetTypeID()) {\n      CFBooleanRef boolean_cf =\n          base::apple::CFCastStrict<CFBooleanRef>(property_cf);\n      data_launch = LaunchDataNewBool(CFBooleanGetValue(boolean_cf));\n\n    } else if (type_id_cf == CFStringGetTypeID()) {\n      NSString* string_ns = base::apple::CFToNSPtrCast(\n          base::apple::CFCastStrict<CFStringRef>(property_cf));\n\n      // -fileSystemRepresentation might be more correct than -UTF8String,\n      // because these strings can hold paths. The analogous function in\n      // launchctl, CF2launch_data() (10.9.4\n      // launchd-842.92.1/support/launchctl.c), uses UTF-8 instead of filesystem\n      // encoding, so do the same here. Note that there’s another occurrence of\n      // -UTF8String above, used for dictionary keys.\n      data_launch = LaunchDataNewString([string_ns UTF8String]);\n\n    } else if (type_id_cf == CFDataGetTypeID()) {\n      NSData* data_ns = base::apple::CFToNSPtrCast(\n          base::apple::CFCastStrict<CFDataRef>(property_cf));\n      data_launch = LaunchDataNewOpaque([data_ns bytes], [data_ns length]);\n    } else {\n      base::apple::ScopedCFTypeRef<CFStringRef> type_name_cf(\n          CFCopyTypeIDDescription(type_id_cf));\n      DLOG(ERROR) << \"unable to convert CFTypeID \" << type_id_cf << \" (\"\n                  << base::SysCFStringRefToUTF8(type_name_cf.get()) << \")\";\n    }\n\n    return data_launch;\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/launchd_test.mm",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/launchd.h\"\n\n#import <Foundation/Foundation.h>\n#include <launch.h>\n#include <stdint.h>\n#include <string.h>\n\n#include <cmath>\n#include <iterator>\n#include <limits>\n\n#include \"base/apple/bridging.h\"\n#include \"base/mac/scoped_launch_data.h\"\n#include \"gtest/gtest.h\"\n#include \"util/stdlib/objc.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Launchd, CFPropertyToLaunchData_Integer) {\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    NSNumber* integer_nses[] = {\n      @0,\n      @1,\n      @-1,\n      @0x7f,\n      @0x80,\n      @0xff,\n      @0x0100,\n      @0x7fff,\n      @0x8000,\n      @0xffff,\n      @0x00010000,\n      @0x7fffffff,\n      @0x80000000,\n      @0xffffffff,\n      @0x1000000000000000,\n      @0x7fffffffffffffff,\n      @0x8000000000000000,\n      @0xffffffffffffffff,\n      @0x0123456789abcdef,\n      @0xfedcba9876543210,\n    };\n\n    for (size_t index = 0; index < std::size(integer_nses); ++index) {\n      NSNumber* integer_ns = integer_nses[index];\n      launch_data.reset(\n          CFPropertyToLaunchData(base::apple::NSToCFPtrCast(integer_ns)));\n      ASSERT_TRUE(launch_data.get());\n      ASSERT_EQ(LaunchDataGetType(launch_data.get()), LAUNCH_DATA_INTEGER);\n      EXPECT_EQ(LaunchDataGetInteger(launch_data.get()),\n                [integer_ns longLongValue])\n          << \"index \" << index;\n    }\n  }\n}\n\nTEST(Launchd, CFPropertyToLaunchData_FloatingPoint) {\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    NSNumber* double_nses[] = {\n      @0.0,\n      @1.0,\n      @-1.0,\n      @(std::numeric_limits<float>::min()),\n      @(std::numeric_limits<float>::max()),\n      @(std::numeric_limits<double>::min()),\n      @(std::numeric_limits<double>::max()),\n      @3.1415926535897932,\n      @(std::numeric_limits<double>::infinity()),\n      @(std::numeric_limits<double>::quiet_NaN()),\n      @(std::numeric_limits<double>::signaling_NaN()),\n    };\n\n    for (size_t index = 0; index < std::size(double_nses); ++index) {\n      NSNumber* double_ns = double_nses[index];\n      launch_data.reset(\n          CFPropertyToLaunchData(base::apple::NSToCFPtrCast(double_ns)));\n      ASSERT_TRUE(launch_data.get());\n      ASSERT_EQ(LaunchDataGetType(launch_data.get()), LAUNCH_DATA_REAL);\n      double expected_double_value = [double_ns doubleValue];\n      double observed_double_value = LaunchDataGetReal(launch_data.get());\n      bool expected_is_nan = std::isnan(expected_double_value);\n      EXPECT_EQ(std::isnan(observed_double_value), expected_is_nan);\n      if (!expected_is_nan) {\n        EXPECT_DOUBLE_EQ(expected_double_value, observed_double_value)\n            << \"index \" << index;\n      }\n    }\n  }\n}\n\nTEST(Launchd, CFPropertyToLaunchData_Boolean) {\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    // Use CFBooleanRefs here because calling NSToCFPtrCast on an NSNumber\n    // boolean can fail. Casting an NSNumber expects a CFNumberRef as a result\n    // but a cast boolean will end up as a CFBooleanRef.\n    CFBooleanRef bools[] = {\n        kCFBooleanFalse,\n        kCFBooleanTrue,\n    };\n\n    for (CFBooleanRef bool_cf : bools) {\n      launch_data.reset(CFPropertyToLaunchData(bool_cf));\n      ASSERT_TRUE(launch_data.get());\n      ASSERT_EQ(LaunchDataGetType(launch_data.get()), LAUNCH_DATA_BOOL);\n      if (CFBooleanGetValue(bool_cf)) {\n        EXPECT_TRUE(LaunchDataGetBool(launch_data.get()));\n      } else {\n        EXPECT_FALSE(LaunchDataGetBool(launch_data.get()));\n      }\n    }\n  }\n}\n\nTEST(Launchd, CFPropertyToLaunchData_String) {\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    NSString* string_nses[] = {\n      @\"\",\n      @\"string\",\n      @\"Üñîçø∂é\",\n    };\n\n    for (size_t index = 0; index < std::size(string_nses); ++index) {\n      NSString* string_ns = string_nses[index];\n      launch_data.reset(\n          CFPropertyToLaunchData(base::apple::NSToCFPtrCast(string_ns)));\n      ASSERT_TRUE(launch_data.get());\n      ASSERT_EQ(LaunchDataGetType(launch_data.get()), LAUNCH_DATA_STRING);\n      EXPECT_STREQ([string_ns UTF8String],\n                   LaunchDataGetString(launch_data.get()));\n    }\n  }\n}\n\nTEST(Launchd, CFPropertyToLaunchData_Data) {\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    static constexpr uint8_t data_c[] = {\n        1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 9, 8, 7, 6, 5, 4, 3, 2};\n    NSData* data_ns = [NSData dataWithBytes:data_c length:sizeof(data_c)];\n    launch_data.reset(\n        CFPropertyToLaunchData(base::apple::NSToCFPtrCast(data_ns)));\n    ASSERT_TRUE(launch_data.get());\n    ASSERT_EQ(LaunchDataGetType(launch_data.get()), LAUNCH_DATA_OPAQUE);\n    EXPECT_EQ(LaunchDataGetOpaqueSize(launch_data.get()), sizeof(data_c));\n    EXPECT_EQ(\n        memcmp(LaunchDataGetOpaque(launch_data.get()), data_c, sizeof(data_c)),\n        0);\n  }\n}\n\nTEST(Launchd, CFPropertyToLaunchData_Dictionary) {\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    NSDictionary* dictionary_ns = @{\n      @\"key\" : @\"value\",\n    };\n\n    launch_data.reset(\n        CFPropertyToLaunchData(base::apple::NSToCFPtrCast(dictionary_ns)));\n    ASSERT_TRUE(launch_data.get());\n    ASSERT_EQ(LaunchDataGetType(launch_data.get()), LAUNCH_DATA_DICTIONARY);\n    EXPECT_EQ(LaunchDataDictGetCount(launch_data.get()), [dictionary_ns count]);\n\n    launch_data_t launch_lookup_data =\n        LaunchDataDictLookup(launch_data.get(), \"key\");\n    ASSERT_TRUE(launch_lookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_lookup_data), LAUNCH_DATA_STRING);\n    EXPECT_STREQ(\"value\", LaunchDataGetString(launch_lookup_data));\n  }\n}\n\nTEST(Launchd, CFPropertyToLaunchData_Array) {\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    NSArray* array_ns = @[ @\"element_1\", @\"element_2\", ];\n\n    launch_data.reset(\n        CFPropertyToLaunchData(base::apple::NSToCFPtrCast(array_ns)));\n    ASSERT_TRUE(launch_data.get());\n    ASSERT_EQ(LaunchDataGetType(launch_data.get()), LAUNCH_DATA_ARRAY);\n    EXPECT_EQ(LaunchDataArrayGetCount(launch_data.get()), [array_ns count]);\n\n    launch_data_t launch_lookup_data =\n        LaunchDataArrayGetIndex(launch_data.get(), 0);\n    ASSERT_TRUE(launch_lookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_lookup_data), LAUNCH_DATA_STRING);\n    EXPECT_STREQ(\"element_1\", LaunchDataGetString(launch_lookup_data));\n\n    launch_lookup_data = LaunchDataArrayGetIndex(launch_data.get(), 1);\n    ASSERT_TRUE(launch_lookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_lookup_data), LAUNCH_DATA_STRING);\n    EXPECT_STREQ(\"element_2\", LaunchDataGetString(launch_lookup_data));\n  }\n}\n\nTEST(Launchd, CFPropertyToLaunchData_NSDate) {\n  // Test that NSDate conversions fail as advertised. There’s no type for\n  // storing date values in a launch_data_t.\n\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    NSDate* date = [NSDate date];\n    launch_data.reset(CFPropertyToLaunchData(base::apple::NSToCFPtrCast(date)));\n    EXPECT_FALSE(launch_data.get());\n\n    NSDictionary* date_dictionary = @{\n      @\"key\" : @\"value\",\n      @\"date\" : date,\n    };\n    launch_data.reset(\n        CFPropertyToLaunchData(base::apple::NSToCFPtrCast(date_dictionary)));\n    EXPECT_FALSE(launch_data.get());\n\n    NSArray* date_array = @[ @\"string_1\", date, @\"string_2\", ];\n    launch_data.reset(\n        CFPropertyToLaunchData(base::apple::NSToCFPtrCast(date_array)));\n    EXPECT_FALSE(launch_data.get());\n  }\n}\n\nTEST(Launchd, CFPropertyToLaunchData_RealWorldJobDictionary) {\n  @autoreleasepool {\n    base::mac::ScopedLaunchData launch_data;\n\n    NSDictionary* job_dictionary = @{\n      @LAUNCH_JOBKEY_LABEL : @\"com.example.job.rebooter\",\n      @LAUNCH_JOBKEY_ONDEMAND : @YES,\n      @LAUNCH_JOBKEY_PROGRAMARGUMENTS :\n          @[ @\"/bin/bash\", @\"-c\", @\"/sbin/reboot\", ],\n      @LAUNCH_JOBKEY_MACHSERVICES : @{\n        @\"com.example.service.rebooter\" : @YES,\n      },\n    };\n\n    launch_data.reset(\n        CFPropertyToLaunchData(base::apple::NSToCFPtrCast(job_dictionary)));\n    ASSERT_TRUE(launch_data.get());\n    ASSERT_EQ(LaunchDataGetType(launch_data.get()), LAUNCH_DATA_DICTIONARY);\n    EXPECT_EQ(LaunchDataDictGetCount(launch_data.get()), 4u);\n\n    launch_data_t launch_lookup_data =\n        LaunchDataDictLookup(launch_data.get(), LAUNCH_JOBKEY_LABEL);\n    ASSERT_TRUE(launch_lookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_lookup_data), LAUNCH_DATA_STRING);\n    EXPECT_STREQ(\"com.example.job.rebooter\",\n                 LaunchDataGetString(launch_lookup_data));\n\n    launch_lookup_data =\n        LaunchDataDictLookup(launch_data.get(), LAUNCH_JOBKEY_ONDEMAND);\n    ASSERT_TRUE(launch_lookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_lookup_data), LAUNCH_DATA_BOOL);\n    EXPECT_TRUE(LaunchDataGetBool(launch_lookup_data));\n\n    launch_lookup_data =\n        LaunchDataDictLookup(launch_data.get(), LAUNCH_JOBKEY_PROGRAMARGUMENTS);\n    ASSERT_TRUE(launch_lookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_lookup_data), LAUNCH_DATA_ARRAY);\n    EXPECT_EQ(LaunchDataArrayGetCount(launch_lookup_data), 3u);\n\n    launch_data_t launch_sublookup_data =\n        LaunchDataArrayGetIndex(launch_lookup_data, 0);\n    ASSERT_TRUE(launch_sublookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_sublookup_data), LAUNCH_DATA_STRING);\n    EXPECT_STREQ(\"/bin/bash\", LaunchDataGetString(launch_sublookup_data));\n\n    launch_sublookup_data = LaunchDataArrayGetIndex(launch_lookup_data, 1);\n    ASSERT_TRUE(launch_sublookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_sublookup_data), LAUNCH_DATA_STRING);\n    EXPECT_STREQ(\"-c\", LaunchDataGetString(launch_sublookup_data));\n\n    launch_sublookup_data = LaunchDataArrayGetIndex(launch_lookup_data, 2);\n    ASSERT_TRUE(launch_sublookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_sublookup_data), LAUNCH_DATA_STRING);\n    EXPECT_STREQ(\"/sbin/reboot\", LaunchDataGetString(launch_sublookup_data));\n\n    launch_lookup_data =\n        LaunchDataDictLookup(launch_data.get(), LAUNCH_JOBKEY_MACHSERVICES);\n    ASSERT_TRUE(launch_lookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_lookup_data), LAUNCH_DATA_DICTIONARY);\n    EXPECT_EQ(LaunchDataDictGetCount(launch_lookup_data), 1u);\n\n    launch_sublookup_data = LaunchDataDictLookup(\n        launch_lookup_data, \"com.example.service.rebooter\");\n    ASSERT_TRUE(launch_sublookup_data);\n    ASSERT_EQ(LaunchDataGetType(launch_sublookup_data), LAUNCH_DATA_BOOL);\n    EXPECT_TRUE(LaunchDataGetBool(launch_sublookup_data));\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/mac_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/mac_util.h\"\n\n#include <CoreFoundation/CoreFoundation.h>\n#include <IOKit/IOKitLib.h>\n#include <sys/types.h>\n\n#include <string_view>\n\n#include \"base/apple/foundation_util.h\"\n#include \"base/apple/scoped_cftyperef.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/mac/scoped_ioobject.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/mac/sysctl.h\"\n\nextern \"C\" {\n// Private CoreFoundation internals. See 10.9.2 CF-855.14/CFPriv.h and\n// CF-855.14/CFUtilities.c. These are marked for weak import because they’re\n// private and subject to change.\n\n#define WEAK_IMPORT __attribute__((weak_import))\n\n// Don’t call these functions directly, call them through the\n// TryCFCopy*VersionDictionary() helpers to account for the possibility that\n// they may not be present at runtime.\nCFDictionaryRef _CFCopySystemVersionDictionary() WEAK_IMPORT;\n\n// Don’t use these constants with CFDictionaryGetValue() directly, use them with\n// the TryCFDictionaryGetValue() wrapper to account for the possibility that\n// they may not be present at runtime.\nextern const CFStringRef _kCFSystemVersionProductNameKey WEAK_IMPORT;\nextern const CFStringRef _kCFSystemVersionProductVersionKey WEAK_IMPORT;\nextern const CFStringRef _kCFSystemVersionProductVersionExtraKey WEAK_IMPORT;\nextern const CFStringRef _kCFSystemVersionBuildVersionKey WEAK_IMPORT;\n\n#undef WEAK_IMPORT\n\n}  // extern \"C\"\n\nnamespace {\n\n// Helpers for the weak-imported private CoreFoundation internals.\n\nCFDictionaryRef TryCFCopySystemVersionDictionary() {\n  if (_CFCopySystemVersionDictionary) {\n    return _CFCopySystemVersionDictionary();\n  }\n  return nullptr;\n}\n\nconst void* TryCFDictionaryGetValue(CFDictionaryRef dictionary,\n                                    const void* value) {\n  if (value) {\n    return CFDictionaryGetValue(dictionary, value);\n  }\n  return nullptr;\n}\n\n// Converts |version| to a triplet of version numbers on behalf of\n// MacOSVersionNumber() and MacOSVersionComponents(). Returns true on success.\n// If |version| does not have the expected format, returns false. |version| must\n// be in the form \"10.9.2\" or just \"10.9\". In the latter case, |bugfix| will be\n// set to 0.\nbool StringToVersionNumbers(const std::string& version,\n                            int* major,\n                            int* minor,\n                            int* bugfix) {\n  size_t first_dot = version.find_first_of('.');\n  if (first_dot == 0 || first_dot == std::string::npos ||\n      first_dot == version.length() - 1) {\n    LOG(ERROR) << \"version has unexpected format\";\n    return false;\n  }\n  if (!base::StringToInt(std::string_view(&version[0], first_dot), major)) {\n    LOG(ERROR) << \"version has unexpected format\";\n    return false;\n  }\n\n  size_t second_dot = version.find_first_of('.', first_dot + 1);\n  if (second_dot == version.length() - 1) {\n    LOG(ERROR) << \"version has unexpected format\";\n    return false;\n  } else if (second_dot == std::string::npos) {\n    second_dot = version.length();\n  }\n\n  if (!base::StringToInt(\n          std::string_view(&version[first_dot + 1], second_dot - first_dot - 1),\n          minor)) {\n    LOG(ERROR) << \"version has unexpected format\";\n    return false;\n  }\n\n  if (second_dot == version.length()) {\n    *bugfix = 0;\n  } else if (!base::StringToInt(\n                 std::string_view(&version[second_dot + 1],\n                                  version.length() - second_dot - 1),\n                 bugfix)) {\n    LOG(ERROR) << \"version has unexpected format\";\n    return false;\n  }\n\n  return true;\n}\n\nstd::string IORegistryEntryDataPropertyAsString(io_registry_entry_t entry,\n                                                CFStringRef key) {\n  base::apple::ScopedCFTypeRef<CFTypeRef> property(\n      IORegistryEntryCreateCFProperty(entry, key, kCFAllocatorDefault, 0));\n  CFDataRef data = base::apple::CFCast<CFDataRef>(property.get());\n  if (data && CFDataGetLength(data) > 0) {\n    return reinterpret_cast<const char*>(CFDataGetBytePtr(data));\n  }\n\n  return std::string();\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nint MacOSVersionNumber() {\n  static int macos_version_number = []() {\n    // kern.osproductversion is a lightweight way to get the operating system\n    // version from the kernel without having to open any files or spin up any\n    // threads, but it’s only available in macOS 10.13.4 and later.\n    std::string macos_version_number_string = ReadStringSysctlByName(\n        \"kern.osproductversion\", true);\n    DCHECK(!macos_version_number_string.empty());\n\n    int major;\n    int minor;\n    int bugfix;\n    bool success = StringToVersionNumbers(\n            macos_version_number_string, &major, &minor, &bugfix);\n    DCHECK(success);\n\n    DCHECK_GE(major, 10);\n    DCHECK_LE(major, 99);\n    DCHECK_GE(minor, 0);\n    DCHECK_LE(minor, 99);\n    DCHECK_GE(bugfix, 0);\n    DCHECK_LE(bugfix, 99);\n\n    return major * 1'00'00 + minor * 1'00 + bugfix;\n  }();\n\n  return macos_version_number;\n}\n\nbool MacOSVersionComponents(int* major,\n                            int* minor,\n                            int* bugfix,\n                            std::string* build,\n                            std::string* version_string) {\n  base::apple::ScopedCFTypeRef<CFDictionaryRef> dictionary(\n      TryCFCopySystemVersionDictionary());\n  if (!dictionary) {\n    LOG(ERROR) << \"_CFCopySystemVersionDictionary failed\";\n    return false;\n  }\n\n  bool success = true;\n\n  CFStringRef version_cf =\n      base::apple::CFCast<CFStringRef>(TryCFDictionaryGetValue(\n          dictionary.get(), _kCFSystemVersionProductVersionKey));\n  std::string version;\n  if (!version_cf) {\n    LOG(ERROR) << \"version_cf not found\";\n    success = false;\n  } else {\n    version = base::SysCFStringRefToUTF8(version_cf);\n    if (!StringToVersionNumbers(version, major, minor, bugfix)) {\n      success = false;\n    } else {\n      DCHECK_GE(*major, 10);\n      DCHECK_LE(*major, 99);\n      DCHECK_GE(*minor, 0);\n      DCHECK_LE(*minor, 99);\n      DCHECK_GE(*bugfix, 0);\n      DCHECK_LE(*bugfix, 99);\n    }\n  }\n\n  CFStringRef build_cf =\n      base::apple::CFCast<CFStringRef>(TryCFDictionaryGetValue(\n          dictionary.get(), _kCFSystemVersionBuildVersionKey));\n  if (!build_cf) {\n    LOG(ERROR) << \"build_cf not found\";\n    success = false;\n  } else {\n    build->assign(base::SysCFStringRefToUTF8(build_cf));\n  }\n\n  CFStringRef product_cf =\n      base::apple::CFCast<CFStringRef>(TryCFDictionaryGetValue(\n          dictionary.get(), _kCFSystemVersionProductNameKey));\n  std::string product;\n  if (!product_cf) {\n    LOG(ERROR) << \"product_cf not found\";\n    success = false;\n  } else {\n    product = base::SysCFStringRefToUTF8(product_cf);\n  }\n\n  // This key is not required, and in fact is normally not present.\n  CFStringRef extra_cf =\n      base::apple::CFCast<CFStringRef>(TryCFDictionaryGetValue(\n          dictionary.get(), _kCFSystemVersionProductVersionExtraKey));\n  std::string extra;\n  if (extra_cf) {\n    extra = base::SysCFStringRefToUTF8(extra_cf);\n  }\n\n  if (!product.empty() || !version.empty() || !build->empty()) {\n    if (!extra.empty()) {\n      version_string->assign(base::StringPrintf(\"%s %s %s (%s)\",\n                                                product.c_str(),\n                                                version.c_str(),\n                                                extra.c_str(),\n                                                build->c_str()));\n    } else {\n      version_string->assign(base::StringPrintf(\n          \"%s %s (%s)\", product.c_str(), version.c_str(), build->c_str()));\n    }\n  }\n\n  return success;\n}\n\nvoid MacModelAndBoard(std::string* model, std::string* board_id) {\n  base::mac::ScopedIOObject<io_service_t> platform_expert(\n      IOServiceGetMatchingService(kIOMainPortDefault,\n                                  IOServiceMatching(\"IOPlatformExpertDevice\")));\n  if (platform_expert) {\n    model->assign(IORegistryEntryDataPropertyAsString(platform_expert.get(),\n                                                      CFSTR(\"model\")));\n#if defined(ARCH_CPU_X86_FAMILY)\n    board_id->assign(IORegistryEntryDataPropertyAsString(platform_expert.get(),\n                                                         CFSTR(\"board-id\")));\n#elif defined(ARCH_CPU_ARM64)\n    board_id->assign(IORegistryEntryDataPropertyAsString(\n        platform_expert.get(), CFSTR(\"target-sub-type\")));\n    if (board_id->empty()) {\n      board_id->assign(IORegistryEntryDataPropertyAsString(\n          platform_expert.get(), CFSTR(\"target-type\")));\n    }\n#else\n#error Port.\n#endif\n  } else {\n    model->clear();\n    board_id->clear();\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/mac_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MAC_MAC_UTIL_H_\n#define CRASHPAD_UTIL_MAC_MAC_UTIL_H_\n\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief Returns the version of the running operating system.\n//!\n//! \\return The version of the operating system, such as `15'04'01` for macOS\n//!     15.4.1.\n//!\n//! This function returns the major, minor, and bugfix components combined into\n//! a single number. The format of the return value matches what is used by the\n//! <Availability.h> `__MAC_OS_X_VERSION_MIN_REQUIRED`,\n//! `__MAC_OS_X_VERSION_MAX_ALLOWED`, and per-version `__MAC_*` macros, for\n//! versions since OS X 10.10.\n//!\n//! \\note This is similar to the base::mac::IsOS*() family of functions, but is\n//!     provided for situations where the caller needs to obtain version\n//!     information beyond what is provided by Chromium’s base, or for when the\n//!     caller needs the actual minor version value.\nint MacOSVersionNumber();\n\n//! \\brief Returns the version of the running operating system.\n//!\n//! All parameters are required. No parameter may be `nullptr`.\n//!\n//! \\param[out] major The major version of the operating system, such as `15`\n//!     for macOS 15.4.1.\n//! \\param[out] minor The major version of the operating system, such as `4`\n//!     for macOS 15.4.1.\n//! \\param[out] bugfix The bugfix version of the operating system, such as `1`\n//!     for macOS 15.4.1.\n//! \\param[out] build The operating system’s build string, such as `\"24E263\"`\n//!     for macOS 15.4.1.\n//! \\param[out] version_string A string representing the full operating system\n//!     version, such as `\"macOS 15.4.1 (24E263)\"`. If \\a bugfix is 0, it will\n//!     not be included in \\a version_string: `\"macOS 15.5 (24F74)\"`.\n//!\n//! \\return `true` on success, `false` on failure, with an error message logged.\n//!     A failure is considered to have occurred if any element could not be\n//!     determined. When this happens, their values will be untouched, but other\n//!     values that could be determined will still be set properly.\nbool MacOSVersionComponents(int* major,\n                            int* minor,\n                            int* bugfix,\n                            std::string* build,\n                            std::string* version_string);\n\n//! \\brief Returns the model name and board ID of the running system.\n//!\n//! \\param[out] model The system’s model name. A mid-2012 15\\\" MacBook Pro would\n//!     report “MacBookPro10,1”, and a 2021 16\\\" M1 Max MacBook Pro would report\n//!     “MacBookPro18,2”.\n//! \\param[out] board_id The system’s board ID or target type. An x86_64 system\n//!     reports a board ID: a mid-2012 15\\\" MacBook Pro would report\n//!     “Mac-C3EC7CD22292981F”. An arm64 system reports a target subtype or\n//!     target type: a 2021 16\\\" M1 Max MacBook Pro would report “J316cAP”.\n//!\n//! If a value cannot be determined, its string is cleared.\nvoid MacModelAndBoard(std::string* model, std::string* board_id);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MAC_MAC_UTIL_H_\n"
  },
  {
    "path": "util/mac/mac_util_test.mm",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/mac_util.h\"\n\n#import <Foundation/Foundation.h>\n#include <stdlib.h>\n\n#include <string>\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Runs /usr/bin/sw_vers with a single argument, |argument|, and places the\n// command’s standard output into |output| after stripping the trailing newline.\n// Fatal Google Test assertions report tool failures, which the caller should\n// check for with ASSERT_NO_FATAL_FAILURE() or testing::Test::HasFatalFailure().\nvoid SwVers(NSString* argument, std::string* output) {\n  @autoreleasepool {\n    NSPipe* pipe = [[NSPipe alloc] init];\n    NSTask* task = [[NSTask alloc] init];\n    [task setStandardOutput:pipe];\n    [task setLaunchPath:@\"/usr/bin/sw_vers\"];\n    [task setArguments:@[ argument ]];\n\n    @try {\n      [task launch];\n    } @catch (NSException* exception) {\n      FAIL() << [[exception name] UTF8String] << \": \"\n             << [[exception reason] UTF8String];\n    }\n\n    NSData* data = [[pipe fileHandleForReading] readDataToEndOfFile];\n    [task waitUntilExit];\n\n    ASSERT_EQ([task terminationReason], NSTaskTerminationReasonExit);\n    ASSERT_EQ([task terminationStatus], EXIT_SUCCESS);\n\n    output->assign(reinterpret_cast<const char*>([data bytes]), [data length]);\n\n    EXPECT_FALSE(output->empty());\n    if (!output->empty()) {\n      EXPECT_EQ(output->back(), '\\n');\n      output->pop_back();\n    }\n  }\n}\n\nTEST(MacUtil, MacOSVersionComponents) {\n  int major;\n  int minor;\n  int bugfix;\n  std::string build;\n  std::string version_string;\n  ASSERT_TRUE(\n      MacOSVersionComponents(&major, &minor, &bugfix, &build, &version_string));\n\n  EXPECT_GE(major, 10);\n  EXPECT_LE(major, 99);\n  EXPECT_GE(minor, 0);\n  EXPECT_LE(minor, 99);\n  EXPECT_GE(bugfix, 0);\n  EXPECT_LE(bugfix, 99);\n\n  if (major == 10) {\n    EXPECT_LE(minor, 15);\n  } else {\n    EXPECT_TRUE(major <= 15 || major >= 26);\n  }\n\n  std::string version;\n  if (bugfix) {\n    version = base::StringPrintf(\"%d.%d.%d\", major, minor, bugfix);\n  } else {\n    // x.y.0 releases report their version string as simply x.y.\n    version = base::StringPrintf(\"%d.%d\", major, minor);\n  }\n\n  std::string expected_product_version;\n  ASSERT_NO_FATAL_FAILURE(\n      SwVers(@\"-productVersion\", &expected_product_version));\n\n  EXPECT_EQ(version, expected_product_version);\n\n  std::string expected_build_version;\n  ASSERT_NO_FATAL_FAILURE(SwVers(@\"-buildVersion\", &expected_build_version));\n\n  EXPECT_EQ(build, expected_build_version);\n\n  std::string expected_product_name;\n  ASSERT_NO_FATAL_FAILURE(SwVers(@\"-productName\", &expected_product_name));\n\n  std::string expected_version_string_start =\n      expected_product_name + ' ' + expected_product_version + ' ';\n  std::string expected_version_string_end = \" (\" + expected_build_version + ')';\n\n  EXPECT_TRUE(version_string.starts_with(expected_version_string_start));\n  EXPECT_TRUE(version_string.ends_with(expected_version_string_end));\n}\n\nTEST(MacUtil, MacOSVersionNumber) {\n  // Make sure that MacOSVersionNumber() and MacOSVersionComponents() agree. The\n  // two have their own distinct implementations, and the latter was checked\n  // against sw_vers above.\n  int macos_version_number = MacOSVersionNumber();\n  EXPECT_GE(macos_version_number, 10'00'00);\n  EXPECT_LE(macos_version_number, 99'99'99);\n\n  int major;\n  int minor;\n  int bugfix;\n  std::string build;\n  std::string version_string;\n  ASSERT_TRUE(\n      MacOSVersionComponents(&major, &minor, &bugfix, &build, &version_string));\n\n  EXPECT_EQ(macos_version_number, major * 1'00'00 + minor * 1'00 + bugfix);\n}\n\nTEST(MacUtil, MacModelAndBoard) {\n  // There’s not much that can be done to test these, so just make sure they’re\n  // not empty. The model could be compared against the parsed output of\n  // “system_profiler SPHardwareDataType”, but the board doesn’t show up\n  // anywhere other than the I/O Registry, and that’s exactly how\n  // MacModelAndBoard() gets the data, so it wouldn’t be a very useful test.\n  std::string model;\n  std::string board;\n  MacModelAndBoard(&model, &board);\n\n  EXPECT_FALSE(model.empty());\n  EXPECT_FALSE(board.empty());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/service_management.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/service_management.h\"\n\n#include <errno.h>\n#include <launch.h>\n\n#include \"base/mac/scoped_launch_data.h\"\n#include \"util/mac/launchd.h\"\n#include \"util/misc/clock.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nlaunch_data_t LaunchDataDictionaryForJob(const std::string& label) {\n  base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY));\n  LaunchDataDictInsert(\n      request.get(), LaunchDataNewString(label.c_str()), LAUNCH_KEY_GETJOB);\n\n  base::mac::ScopedLaunchData response(LaunchMsg(request.get()));\n  if (LaunchDataGetType(response.get()) != LAUNCH_DATA_DICTIONARY) {\n    return nullptr;\n  }\n\n  return response.release();\n}\n\n}  // namespace\n\nbool ServiceManagementSubmitJob(CFDictionaryRef job_cf) {\n  base::mac::ScopedLaunchData job_launch(CFPropertyToLaunchData(job_cf));\n  if (!job_launch.get()) {\n    return false;\n  }\n\n  base::mac::ScopedLaunchData jobs(LaunchDataAlloc(LAUNCH_DATA_ARRAY));\n  LaunchDataArraySetIndex(jobs.get(), job_launch.release(), 0);\n\n  base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY));\n  LaunchDataDictInsert(request.get(), jobs.release(), LAUNCH_KEY_SUBMITJOB);\n\n  base::mac::ScopedLaunchData response(LaunchMsg(request.get()));\n  if (LaunchDataGetType(response.get()) != LAUNCH_DATA_ARRAY) {\n    return false;\n  }\n\n  if (LaunchDataArrayGetCount(response.get()) != 1) {\n    return false;\n  }\n\n  launch_data_t response_element = LaunchDataArrayGetIndex(response.get(), 0);\n  if (LaunchDataGetType(response_element) != LAUNCH_DATA_ERRNO) {\n    return false;\n  }\n\n  int err = LaunchDataGetErrno(response_element);\n  if (err != 0) {\n    return false;\n  }\n\n  return true;\n}\n\nbool ServiceManagementRemoveJob(const std::string& label, bool wait) {\n  base::mac::ScopedLaunchData request(LaunchDataAlloc(LAUNCH_DATA_DICTIONARY));\n  LaunchDataDictInsert(\n      request.get(), LaunchDataNewString(label.c_str()), LAUNCH_KEY_REMOVEJOB);\n\n  base::mac::ScopedLaunchData response(LaunchMsg(request.get()));\n  if (LaunchDataGetType(response.get()) != LAUNCH_DATA_ERRNO) {\n    return false;\n  }\n\n  int err = LaunchDataGetErrno(response.get());\n  if (err == EINPROGRESS) {\n    if (wait) {\n      // TODO(mark): Use a kqueue to wait for the process to exit. To avoid a\n      // race, the kqueue would need to be set up prior to asking launchd to\n      // remove the job. Even so, the job’s PID may change between the time it’s\n      // obtained and the time the kqueue is set up, so this is nontrivial.\n      do {\n        SleepNanoseconds(1E5);  // 100 microseconds\n      } while (ServiceManagementIsJobLoaded(label));\n    }\n\n    return true;\n  }\n\n  if (err != 0) {\n    return false;\n  }\n\n  return true;\n}\n\nbool ServiceManagementIsJobLoaded(const std::string& label) {\n  base::mac::ScopedLaunchData dictionary(LaunchDataDictionaryForJob(label));\n  if (!dictionary.is_valid()) {\n    return false;\n  }\n\n  return true;\n}\n\npid_t ServiceManagementIsJobRunning(const std::string& label) {\n  base::mac::ScopedLaunchData dictionary(LaunchDataDictionaryForJob(label));\n  if (!dictionary.is_valid()) {\n    return 0;\n  }\n\n  launch_data_t pid = LaunchDataDictLookup(dictionary.get(), LAUNCH_JOBKEY_PID);\n  if (!pid) {\n    return 0;\n  }\n\n  if (LaunchDataGetType(pid) != LAUNCH_DATA_INTEGER) {\n    return 0;\n  }\n\n  return LaunchDataGetInteger(pid);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/service_management.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT_H_\n#define CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT_H_\n\n#include <CoreFoundation/CoreFoundation.h>\n#include <unistd.h>\n\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief Submits a job to the user launchd domain as in `SMJobSubmit()`.\n//!\n//! \\param[in] job_cf A dictionary describing a job.\n//!\n//! \\return `true` if the job was submitted successfully, otherwise `false`.\n//!\n//! \\note This function is provided because `SMJobSubmit()` is deprecated in OS\n//!     X 10.10. It may or may not be implemented using `SMJobSubmit()` from\n//!     `ServiceManagement.framework`.\nbool ServiceManagementSubmitJob(CFDictionaryRef job_cf);\n\n//! \\brief Removes a job from the user launchd domain as in `SMJobRemove()`.\n//!\n//! \\param[in] label The label for the job to remove.\n//! \\param[in] wait `true` if this function should block, waiting for the job to\n//!     be removed. `false` if the job may be removed asynchronously.\n//!\n//! \\return `true` if the job was removed successfully or if an asynchronous\n//!     attempt to remove the job was started successfully, otherwise `false`.\n//!\n//! \\note This function is provided because `SMJobRemove()` is deprecated in OS\n//!     X 10.10. On OS X 10.10, observed in DP8 14A361c, it also blocks for far\n//!     too long (`_block_until_job_exits()` contains a one-second `sleep()`,\n//!     filed as radar 18398683) and does not signal failure via its return\n//!     value when asked to remove a nonexistent job (filed as radar 18268941).\nbool ServiceManagementRemoveJob(const std::string& label, bool wait);\n\n//! \\brief Determines whether a specified job is loaded in the user launchd\n//!     domain.\n//!\n//! \\param[in] label The label for the job to look up.\n//!\n//! \\return `true` if the job is loaded, otherwise `false`.\n//!\n//! \\note A loaded job is not necessarily presently running, nor has it\n//!     necessarily ever run in the past.\n//! \\note This function is provided because `SMJobCopyDictionary()` is\n//!     deprecated in OS X 10.10. It may or may not be implemented using\n//!     `SMJobCopyDictionary()` from `ServiceManagement.framework`.\nbool ServiceManagementIsJobLoaded(const std::string& label);\n\n//! \\brief Determines whether a specified job is running in the user launchd\n//!     domain.\n//!\n//! \\param[in] label The label for the job to look up.\n//!\n//! \\return The job’s process ID if running, otherwise `0`.\n//!\n//! \\note This function is provided because `SMJobCopyDictionary()` is\n//!     deprecated in OS X 10.10. It may or may not be implemented using\n//!     `SMJobCopyDictionary()` from `ServiceManagement.framework`.\npid_t ServiceManagementIsJobRunning(const std::string& label);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MAC_SERVICE_MANAGEMENT\n"
  },
  {
    "path": "util/mac/service_management_test.mm",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/service_management.h\"\n\n#import <Foundation/Foundation.h>\n#include <launch.h>\n\n#include <string>\n#include <vector>\n\n#include \"base/apple/bridging.h\"\n#include \"base/apple/scoped_cftyperef.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"gtest/gtest.h\"\n#include \"util/misc/clock.h\"\n#include \"util/misc/random_string.h\"\n#include \"util/posix/process_info.h\"\n#include \"util/stdlib/objc.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Ensures that the process with the specified PID is running, identifying it by\n// requiring that its argv[argc - 1] compare equal to last_arg.\nvoid ExpectProcessIsRunning(pid_t pid, std::string& last_arg) {\n  ProcessInfo process_info;\n  ASSERT_TRUE(process_info.InitializeWithPid(pid));\n\n  // The process may not have called exec yet, so loop with a small delay while\n  // looking for the cookie.\n  int outer_tries = 10;\n  std::vector<std::string> job_argv;\n  while (outer_tries--) {\n    // If the process is in the middle of calling exec, process_info.Arguments()\n    // may fail. Loop with a small retry delay while waiting for the expected\n    // successful call.\n    int inner_tries = 10;\n    bool success;\n    do {\n      success = process_info.Arguments(&job_argv);\n      if (success) {\n        break;\n      }\n      if (inner_tries > 0) {\n        SleepNanoseconds(1E7);  // 10 milliseconds\n      }\n    } while (inner_tries--);\n    ASSERT_TRUE(success);\n\n    ASSERT_FALSE(job_argv.empty());\n    if (job_argv.back() == last_arg) {\n      break;\n    }\n\n    if (outer_tries > 0) {\n      SleepNanoseconds(1E6);  // 1 millisecond\n    }\n  }\n\n  ASSERT_FALSE(job_argv.empty());\n  EXPECT_EQ(job_argv.back(), last_arg);\n}\n\n// Ensures that the process with the specified PID is not running. Because the\n// PID may be reused for another process, a process is only treated as running\n// if its argv[argc - 1] compares equal to last_arg.\nvoid ExpectProcessIsNotRunning(pid_t pid, std::string& last_arg) {\n  // The process may not have exited yet, so loop with a small delay while\n  // checking that it has exited.\n  int tries = 10;\n  std::vector<std::string> job_argv;\n  while (tries--) {\n    ProcessInfo process_info;\n    if (!process_info.InitializeWithPid(pid) ||\n        !process_info.Arguments(&job_argv)) {\n      // The PID was not found.\n      return;\n    }\n\n    // The PID was found. It may have been recycled for another process. Make\n    // sure that the cookie isn’t found.\n    ASSERT_FALSE(job_argv.empty());\n    if (job_argv.back() != last_arg) {\n      break;\n    }\n\n    if (tries > 0) {\n      SleepNanoseconds(1E6);  // 1 millisecond\n    }\n  }\n\n  ASSERT_FALSE(job_argv.empty());\n  EXPECT_NE(job_argv.back(), last_arg);\n}\n\nTEST(ServiceManagement, SubmitRemoveJob) {\n  @autoreleasepool {\n    const std::string cookie = RandomString();\n\n    std::string shell_script =\n        base::StringPrintf(\"sleep 10; echo %s\", cookie.c_str());\n    NSString* shell_script_ns = base::SysUTF8ToNSString(shell_script);\n\n    static constexpr char kJobLabel[] =\n        \"org.chromium.crashpad.test.service_management\";\n    NSDictionary* job_dictionary_ns = @{\n      @LAUNCH_JOBKEY_LABEL : @\"org.chromium.crashpad.test.service_management\",\n      @LAUNCH_JOBKEY_RUNATLOAD : @YES,\n      @LAUNCH_JOBKEY_PROGRAMARGUMENTS : @[\n        @\"/bin/sh\",\n        @\"-c\",\n        shell_script_ns,\n      ],\n    };\n    CFDictionaryRef job_dictionary_cf =\n        base::apple::NSToCFPtrCast(job_dictionary_ns);\n\n    // The job may be left over from a failed previous run.\n    if (ServiceManagementIsJobLoaded(kJobLabel)) {\n      EXPECT_TRUE(ServiceManagementRemoveJob(kJobLabel, true));\n    }\n\n    EXPECT_FALSE(ServiceManagementIsJobLoaded(kJobLabel));\n    ASSERT_FALSE(ServiceManagementIsJobRunning(kJobLabel));\n\n    // Submit the job.\n    ASSERT_TRUE(ServiceManagementSubmitJob(job_dictionary_cf));\n    EXPECT_TRUE(ServiceManagementIsJobLoaded(kJobLabel));\n\n    // launchd started the job because RunAtLoad is true.\n    pid_t job_pid = ServiceManagementIsJobRunning(kJobLabel);\n    ASSERT_GT(job_pid, 0);\n\n    ExpectProcessIsRunning(job_pid, shell_script);\n\n    // Remove the job.\n    ASSERT_TRUE(ServiceManagementRemoveJob(kJobLabel, true));\n    EXPECT_FALSE(ServiceManagementIsJobLoaded(kJobLabel));\n    EXPECT_EQ(ServiceManagementIsJobRunning(kJobLabel), 0);\n\n    // Now that the job is unloaded, a subsequent attempt to unload it should be\n    // an error.\n    EXPECT_FALSE(ServiceManagementRemoveJob(kJobLabel, false));\n\n    ExpectProcessIsNotRunning(job_pid, shell_script);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/sysctl.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/sysctl.h\"\n\n#include <errno.h>\n#include <sys/sysctl.h>\n#include <sys/types.h>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nstd::string ReadStringSysctlByName(const char* name, bool may_log_enoent) {\n  size_t buf_len;\n  if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) {\n    PLOG_IF(WARNING, may_log_enoent || errno != ENOENT)\n        << \"sysctlbyname (size) \" << name;\n    return std::string();\n  }\n\n  DCHECK_GE(buf_len, 1u);\n\n  std::string value(buf_len - 1, '\\0');\n  if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) {\n    PLOG(WARNING) << \"sysctlbyname \" << name;\n    return std::string();\n  }\n\n  DCHECK_EQ(value[buf_len - 1], '\\0');\n\n  return value;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/sysctl.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MAC_SYSCTL_H_\n#define CRASHPAD_UTIL_MAC_SYSCTL_H_\n\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief Calls `sysctlbyname` to read a string.\n//!\n//! \\param[in] name The string name of the sysctl to raed.\n//! \\param[in] may_log_enoent If `true`, allows a warning to be logged if the\n//!     sysctl is not found, indicated by `sysctlbyname` setting `errno` to\n//!     `ENOENT`. If `false`, no warning will be logged if the sysctl is\n//!     missing, and an empty string will be returned silently.\n//!\n//! \\return The value of the sysctl read on success. On failure, an empty string\n//!     with a warning logged, subject to \\a may_log_enoent.\nstd::string ReadStringSysctlByName(const char* name, bool may_log_enoent);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MAC_SYSCTL_H_\n"
  },
  {
    "path": "util/mac/sysctl_test.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/sysctl.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Sysctl, ReadStringSysctlByName) {\n  // kern.ostype is always provided by the kernel, and it’s a constant across\n  // all versions, so it makes for a good test.\n  EXPECT_EQ(ReadStringSysctlByName(\"kern.ostype\", true), \"Darwin\");\n\n  // Names expected to not exist.\n  EXPECT_TRUE(ReadStringSysctlByName(\"kern.scheisskopf\", true).empty());\n  EXPECT_TRUE(ReadStringSysctlByName(\"kern.sanders\", false).empty());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/xattr.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/xattr.h\"\n\n#include <errno.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <sys/xattr.h>\n\n#include <string_view>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n\nnamespace crashpad {\n\nXattrStatus ReadXattr(const base::FilePath& file,\n                      std::string_view name,\n                      std::string* value) {\n  // First get the size of the attribute value.\n  ssize_t buffer_size = getxattr(file.value().c_str(), name.data(), nullptr,\n                                 0, 0, 0);\n  if (buffer_size < 0) {\n    if (errno == ENOATTR)\n      return XattrStatus::kNoAttribute;\n    PLOG(ERROR) << \"getxattr size \" << name << \" on file \" << file.value();\n    return XattrStatus::kOtherError;\n  }\n\n  // Resize the buffer and read into it.\n  value->resize(buffer_size);\n  if (!value->empty()) {\n    ssize_t bytes_read = getxattr(file.value().c_str(), name.data(),\n                                  &(*value)[0], value->size(),\n                                  0, 0);\n    if (bytes_read < 0) {\n      PLOG(ERROR) << \"getxattr \" << name << \" on file \" << file.value();\n      return XattrStatus::kOtherError;\n    }\n    DCHECK_EQ(bytes_read, buffer_size);\n  }\n\n  return XattrStatus::kOK;\n}\n\nbool WriteXattr(const base::FilePath& file,\n                std::string_view name,\n                const std::string& value) {\n  int rv = setxattr(file.value().c_str(), name.data(), value.c_str(),\n      value.length(), 0, 0);\n  PLOG_IF(ERROR, rv != 0) << \"setxattr \" << name << \" on file \"\n                          << file.value();\n  return rv == 0;\n}\n\nXattrStatus ReadXattrBool(const base::FilePath& file,\n                          std::string_view name,\n                          bool* value) {\n  std::string tmp;\n  XattrStatus status;\n  if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK)\n    return status;\n  if (tmp == \"1\") {\n    *value = true;\n    return XattrStatus::kOK;\n  } else if (tmp == \"0\") {\n    *value = false;\n    return XattrStatus::kOK;\n  } else {\n    LOG(ERROR) << \"ReadXattrBool \" << name << \" on file \" << file.value()\n               << \" could not be interpreted as boolean\";\n    return XattrStatus::kOtherError;\n  }\n}\n\nbool WriteXattrBool(const base::FilePath& file,\n                    std::string_view name,\n                    bool value) {\n  return WriteXattr(file, name, (value ? \"1\" : \"0\"));\n}\n\nXattrStatus ReadXattrInt(const base::FilePath& file,\n                         std::string_view name,\n                         int* value) {\n  std::string tmp;\n  XattrStatus status;\n  if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK)\n    return status;\n  if (!base::StringToInt(tmp, value)) {\n    LOG(ERROR) << \"ReadXattrInt \" << name << \" on file \" << file.value()\n               << \" could not be converted to an int\";\n    return XattrStatus::kOtherError;\n  }\n  return XattrStatus::kOK;\n}\n\nbool WriteXattrInt(const base::FilePath& file,\n                   std::string_view name,\n                   int value) {\n  std::string tmp = base::StringPrintf(\"%d\", value);\n  return WriteXattr(file, name, tmp);\n}\n\nXattrStatus ReadXattrTimeT(const base::FilePath& file,\n                           std::string_view name,\n                           time_t* value) {\n  // time_t on macOS is defined as a long, but it will be read into an int64_t\n  // here, since there is no string conversion method for long.\n  std::string tmp;\n  XattrStatus status;\n  if ((status = ReadXattr(file, name, &tmp)) != XattrStatus::kOK)\n    return status;\n\n  int64_t encoded_value;\n  if (!base::StringToInt64(tmp, &encoded_value)) {\n    LOG(ERROR) << \"ReadXattrTimeT \" << name << \" on file \" << file.value()\n               << \" could not be converted to an int\";\n    return XattrStatus::kOtherError;\n  }\n\n  *value = base::saturated_cast<time_t>(encoded_value);\n  if (!base::IsValueInRangeForNumericType<time_t>(encoded_value)) {\n    LOG(ERROR) << \"ReadXattrTimeT \" << name << \" on file \" << file.value()\n               << \" read over-sized value and will saturate\";\n    return XattrStatus::kOtherError;\n  }\n\n  return XattrStatus::kOK;\n}\n\nbool WriteXattrTimeT(const base::FilePath& file,\n                     std::string_view name,\n                     time_t value) {\n  std::string tmp = base::StringPrintf(\"%ld\", value);\n  return WriteXattr(file, name, tmp);\n}\n\nXattrStatus RemoveXattr(const base::FilePath& file, std::string_view name) {\n  int rv = removexattr(file.value().c_str(), name.data(), 0);\n  if (rv != 0) {\n    if (errno == ENOATTR)\n      return XattrStatus::kNoAttribute;\n    PLOG(ERROR) << \"removexattr \" << name << \" on file \" << file.value();\n    return XattrStatus::kOtherError;\n  }\n  return XattrStatus::kOK;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mac/xattr.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MAC_XATTR_H_\n#define CRASHPAD_UTIL_MAC_XATTR_H_\n\n#include <time.h>\n\n#include <string>\n#include <string_view>\n\n#include \"base/files/file_path.h\"\n\nnamespace crashpad {\n\n//! \\brief The result code for a ReadXattr operation.\nenum class XattrStatus {\n  //! \\brief No error occured. No message is logged.\n  kOK = 0,\n\n  //! \\brief The attribute does not exist. No message is logged.\n  kNoAttribute,\n\n  //! \\brief An error occurred and an error message was logged.\n  kOtherError,\n};\n\n//! \\brief Reads an extended attribute on a file.\n//!\n//! \\param[in] file The path to the file.\n//! \\param[in] name The name of the extended attribute to read.\n//! \\param[out] value The value of the attribute.\n//!\n//! \\return XattrStatus\nXattrStatus ReadXattr(const base::FilePath& file,\n                      std::string_view name,\n                      std::string* value);\n\n//! \\brief Writes an extended attribute on a file.\n//!\n//! \\param[in] file The path to the file.\n//! \\param[in] name The name of the extended attribute to write.\n//! \\param[in] value The value of the attribute.\n//!\n//! \\return `true` if the write was successful. `false` on error, with a message\n//!     logged.\nbool WriteXattr(const base::FilePath& file,\n                std::string_view name,\n                const std::string& value);\n\n//! \\copydoc ReadXattr\n//!\n//! Only the values `\"0\"` and `\"1\"`, for `false` and `true` respectively, are\n//! valid conversions.\nXattrStatus ReadXattrBool(const base::FilePath& file,\n                          std::string_view name,\n                          bool* value);\n\n//! \\copydoc WriteXattr\nbool WriteXattrBool(const base::FilePath& file,\n                    std::string_view name,\n                    bool value);\n\n//! \\copydoc ReadXattr\nXattrStatus ReadXattrInt(const base::FilePath& file,\n                         std::string_view name,\n                         int* value);\n\n//! \\copydoc WriteXattr\nbool WriteXattrInt(const base::FilePath& file,\n                   std::string_view name,\n                   int value);\n\n//! \\copydoc ReadXattr\nXattrStatus ReadXattrTimeT(const base::FilePath& file,\n                           std::string_view name,\n                           time_t* value);\n\n//! \\copydoc WriteXattr\nbool WriteXattrTimeT(const base::FilePath& file,\n                     std::string_view name,\n                     time_t value);\n\n//! \\brief Removes an extended attribute from a file.\n//!\n//! \\param[in] file The path to the file.\n//! \\param[in] name The name of the extended attribute to remove.\n//!\n//! \\return XattrStatus\nXattrStatus RemoveXattr(const base::FilePath& file, std::string_view name);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MAC_XATTR_H_\n"
  },
  {
    "path": "util/mac/xattr_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mac/xattr.h\"\n\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <limits>\n\n#include \"base/files/scoped_file.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_temp_dir.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass Xattr : public testing::Test {\n protected:\n  // testing::Test:\n\n  void SetUp() override {\n    path_ = temp_dir_.path().Append(\"xattr_file\");\n    base::ScopedFD tmp(\n        HANDLE_EINTR(open(path_.value().c_str(),\n                          O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY | O_CLOEXEC,\n                          0644)));\n    EXPECT_GE(tmp.get(), 0) << ErrnoMessage(\"open\");\n  }\n\n  void TearDown() override {\n    EXPECT_EQ(unlink(path_.value().c_str()), 0) << ErrnoMessage(\"unlink\");\n  }\n\n  const base::FilePath& path() const { return path_; }\n\n private:\n  ScopedTempDir temp_dir_;\n  base::FilePath path_;\n};\n\nconstexpr char kKey[] = \"org.chromium.crashpad.test\";\n\nTEST_F(Xattr, ReadNonExistentXattr) {\n  std::string value;\n  EXPECT_EQ(ReadXattr(path(), kKey, &value), XattrStatus::kNoAttribute);\n}\n\nTEST_F(Xattr, WriteAndReadString) {\n  std::string value = \"hello world\";\n  EXPECT_TRUE(WriteXattr(path(), kKey, value));\n\n  std::string actual;\n  EXPECT_EQ(ReadXattr(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_EQ(actual, value);\n}\n\nTEST_F(Xattr, WriteAndReadVeryLongString) {\n  std::string value(533, 'A');\n  EXPECT_TRUE(WriteXattr(path(), kKey, value));\n\n  std::string actual;\n  EXPECT_EQ(ReadXattr(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_EQ(actual, value);\n}\n\nTEST_F(Xattr, WriteAndReadBool) {\n  EXPECT_TRUE(WriteXattrBool(path(), kKey, true));\n  bool actual = false;\n  EXPECT_EQ(ReadXattrBool(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_TRUE(actual);\n\n  EXPECT_TRUE(WriteXattrBool(path(), kKey, false));\n  EXPECT_EQ(ReadXattrBool(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_FALSE(actual);\n}\n\nTEST_F(Xattr, WriteAndReadInt) {\n  int expected = 42;\n  int actual;\n\n  EXPECT_TRUE(WriteXattrInt(path(), kKey, expected));\n  EXPECT_EQ(ReadXattrInt(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_EQ(actual, expected);\n\n  expected = std::numeric_limits<int>::max();\n  EXPECT_TRUE(WriteXattrInt(path(), kKey, expected));\n  EXPECT_EQ(ReadXattrInt(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_EQ(actual, expected);\n}\n\nTEST_F(Xattr, WriteAndReadTimeT) {\n  time_t expected = time(nullptr);\n  time_t actual;\n\n  EXPECT_TRUE(WriteXattrTimeT(path(), kKey, expected));\n  EXPECT_EQ(ReadXattrTimeT(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_EQ(actual, expected);\n\n  expected = std::numeric_limits<time_t>::max();\n  EXPECT_TRUE(WriteXattrTimeT(path(), kKey, expected));\n  EXPECT_EQ(ReadXattrTimeT(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_EQ(actual, expected);\n}\n\nTEST_F(Xattr, RemoveAndRead) {\n  const std::string value = \"hello world\";\n  EXPECT_TRUE(WriteXattr(path(), kKey, value));\n\n  std::string actual;\n  EXPECT_EQ(ReadXattr(path(), kKey, &actual), XattrStatus::kOK);\n  EXPECT_EQ(actual, value);\n\n  EXPECT_EQ(RemoveXattr(path(), kKey), XattrStatus::kOK);\n  EXPECT_EQ(ReadXattr(path(), kKey, &actual), XattrStatus::kNoAttribute);\n\n  EXPECT_EQ(RemoveXattr(path(), kKey), XattrStatus::kNoAttribute);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/bootstrap.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/bootstrap.h\"\n\n#include <mach/mach.h>\n#include <servers/bootstrap.h>\n\n#include \"base/apple/mach_logging.h\"\n\nnamespace {\n\n// This forms the internal implementation for BootstrapCheckIn() and\n// BootstrapLookUp(), which follow the same logic aside from the routine called\n// and the right type returned.\n\nstruct BootstrapCheckInTraits {\n  using Type = base::apple::ScopedMachReceiveRight;\n  static kern_return_t Call(mach_port_t bootstrap_port,\n                            const char* service_name,\n                            mach_port_t* service_port) {\n    return bootstrap_check_in(bootstrap_port, service_name, service_port);\n  }\n  static constexpr char kName[] = \"bootstrap_check_in\";\n};\nconstexpr char BootstrapCheckInTraits::kName[];\n\nstruct BootstrapLookUpTraits {\n  using Type = base::apple::ScopedMachSendRight;\n  static kern_return_t Call(mach_port_t bootstrap_port,\n                            const char* service_name,\n                            mach_port_t* service_port) {\n    return bootstrap_look_up(bootstrap_port, service_name, service_port);\n  }\n  static constexpr char kName[] = \"bootstrap_look_up\";\n};\nconstexpr char BootstrapLookUpTraits::kName[];\n\ntemplate <typename Traits>\ntypename Traits::Type BootstrapCheckInOrLookUp(\n    const std::string& service_name) {\n  // bootstrap_check_in() and bootstrap_look_up() silently truncate service\n  // names longer than BOOTSTRAP_MAX_NAME_LEN. This check ensures that the name\n  // will not be truncated.\n  if (service_name.size() >= BOOTSTRAP_MAX_NAME_LEN) {\n    LOG(ERROR) << Traits::kName << \" \" << service_name << \": name too long\";\n    return typename Traits::Type(MACH_PORT_NULL);\n  }\n\n  mach_port_t service_port;\n  kern_return_t kr =\n      Traits::Call(bootstrap_port, service_name.c_str(), &service_port);\n  if (kr != BOOTSTRAP_SUCCESS) {\n    BOOTSTRAP_LOG(ERROR, kr) << Traits::kName << \" \" << service_name;\n    service_port = MACH_PORT_NULL;\n  }\n\n  return typename Traits::Type(service_port);\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nbase::apple::ScopedMachReceiveRight BootstrapCheckIn(\n    const std::string& service_name) {\n  return BootstrapCheckInOrLookUp<BootstrapCheckInTraits>(service_name);\n}\n\nbase::apple::ScopedMachSendRight BootstrapLookUp(\n    const std::string& service_name) {\n  base::apple::ScopedMachSendRight send(\n      BootstrapCheckInOrLookUp<BootstrapLookUpTraits>(service_name));\n\n  // It’s possible to race the bootstrap server when the receive right\n  // corresponding to the looked-up send right is destroyed immediately before\n  // the bootstrap_look_up() call. If the bootstrap server believes that\n  // |service_name| is still registered before processing the port-destroyed\n  // notification sent to it by the kernel, it will respond to a\n  // bootstrap_look_up() request with a send right that has become a dead name,\n  // which will be returned to the bootstrap_look_up() caller, translated into\n  // the caller’s IPC port name space, as the special MACH_PORT_DEAD port name.\n  // Check for that and return MACH_PORT_NULL in its place, as though the\n  // bootstrap server had fully processed the port-destroyed notification before\n  // responding to bootstrap_look_up().\n  if (send.get() == MACH_PORT_DEAD) {\n    LOG(ERROR) << \"bootstrap_look_up \" << service_name << \": service is dead\";\n    send.reset();\n  }\n\n  return send;\n}\n\nbase::apple::ScopedMachSendRight SystemCrashReporterHandler() {\n  return BootstrapLookUp(\"com.apple.ReportCrash\");\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/bootstrap.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_BOOTSTRAP_H_\n#define CRASHPAD_UTIL_MACH_BOOTSTRAP_H_\n\n#include <string>\n\n#include \"base/apple/scoped_mach_port.h\"\n\nnamespace crashpad {\n\n//! \\brief Makes a `boostrap_check_in()` call to the process’ bootstrap server.\n//!\n//! This function is provided to make it easier to call `bootstrap_check_in()`\n//! while avoiding accidental leaks of the returned receive right.\n//!\n//! \\param[in] service_name The service name to check in.\n//!\n//! \\return On success, a receive right to the service port. On failure,\n//!     `MACH_PORT_NULL` with a message logged.\nbase::apple::ScopedMachReceiveRight BootstrapCheckIn(\n    const std::string& service_name);\n\n//! \\brief Makes a `boostrap_look_up()` call to the process’ bootstrap server.\n//!\n//! This function is provided to make it easier to call `bootstrap_look_up()`\n//! while avoiding accidental leaks of the returned send right.\n//!\n//! \\param[in] service_name The service name to look up.\n//!\n//! \\return On success, a send right to the service port. On failure,\n//!     `MACH_PORT_NULL` with a message logged.\nbase::apple::ScopedMachSendRight BootstrapLookUp(\n    const std::string& service_name);\n\n//! \\brief Obtains the system’s default Mach exception handler for crash-type\n//!     exceptions.\n//!\n//! This is obtained by looking up `\"com.apple.ReportCrash\"` with the bootstrap\n//! server. The service name comes from the first launch agent loaded by\n//! `launchd` with a `MachServices` entry having `ExceptionServer` set. This\n//! launch agent is normally loaded from\n//! `/System/Library/LaunchAgents/com.apple.ReportCrash.plist`.\n//!\n//! \\return On success, a send right to an `exception_handler_t` corresponding\n//!     to the system’s default crash reporter. On failure, `MACH_PORT_NULL`,\n//!     with a message logged.\nbase::apple::ScopedMachSendRight SystemCrashReporterHandler();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_BOOTSTRAP_H_\n"
  },
  {
    "path": "util/mach/bootstrap_test.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/bootstrap.h\"\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"gtest/gtest.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/random_string.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Bootstrap, BootstrapCheckInAndLookUp) {\n  // This should always exist.\n  base::apple::ScopedMachSendRight report_crash(\n      BootstrapLookUp(\"com.apple.ReportCrash\"));\n  EXPECT_NE(report_crash, kMachPortNull);\n\n  std::string service_name = \"org.chromium.crashpad.test.bootstrap_check_in.\";\n  service_name.append(RandomString());\n\n  {\n    // The new service hasn’t checked in yet, so this should fail.\n    base::apple::ScopedMachSendRight send(BootstrapLookUp(service_name));\n    EXPECT_EQ(send, kMachPortNull);\n\n    // Check it in.\n    base::apple::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));\n    EXPECT_NE(receive, kMachPortNull);\n\n    // Now it should be possible to look up the new service.\n    send = BootstrapLookUp(service_name);\n    EXPECT_NE(send, kMachPortNull);\n\n    // It shouldn’t be possible to check the service in while it’s active.\n    base::apple::ScopedMachReceiveRight receive_2(\n        BootstrapCheckIn(service_name));\n    EXPECT_EQ(receive_2, kMachPortNull);\n  }\n\n  // The new service should be gone now.\n  base::apple::ScopedMachSendRight send(BootstrapLookUp(service_name));\n  EXPECT_EQ(send, kMachPortNull);\n\n  // It should be possible to check it in again.\n  base::apple::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));\n  EXPECT_NE(receive, kMachPortNull);\n}\n\nTEST(Bootstrap, SystemCrashReporterHandler) {\n  base::apple::ScopedMachSendRight system_crash_reporter_handler(\n      SystemCrashReporterHandler());\n  EXPECT_TRUE(system_crash_reporter_handler.is_valid());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/child_port.defs",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <mach/mach_types.defs>\n#include <mach/std_types.defs>\n\n// child_port provides an interface for port rights to be transferred between\n// tasks. Its expected usage is for processes to be able to pass port rights\n// across IPC boundaries. A child process may wish to give its parent a copy of\n// a send right to its own task port, or a parent process may wish to give a\n// receive right to a child process that implements a server.\n//\n// This Mach subsystem defines the lowest-level interface for these rights to be\n// transferred. Most users will not user this interface directly, but will use\n// ChildPortHandshake, which builds on this interface by providing client and\n// server implementations, along with a protocol for establishing communication\n// in a parent-child process relationship.\nsubsystem child_port 10011;\n\nserverprefix handle_;\n\ntype child_port_server_t = mach_port_t;\ntype child_port_token_t = uint64_t;\n\nimport \"util/mach/child_port_types.h\";\n\n// Sends a Mach port right across an IPC boundary.\n//\n// server[in]: The server to send the port right to.\n// token[in]: A random opaque token, generated by the server and communicated to\n//     the client through some secure means such as a shared pipe. The client\n//     includes the token in its request to prove its authenticity to the\n//     server. This parameter is necessary for instances where the server must\n//     publish its service broadly, such as via the bootstrap server. When this\n//     is done, anyone with access to the bootstrap server will be able to gain\n//     rights to communicate with |server|, and |token| serves as a shared\n//     secret allowing the server to verify that it has received a request from\n//     the intended client. |server| will reject requests with an invalid\n//     |token|.\n// port[in]: A port right to transfer to the server.\n//\n// Return value: As this is a “simpleroutine”, the server does not respond to\n//     the client request, and the client does not block waiting for a response\n//     after sending its request. The return value is MACH_MSG_SUCCESS if the\n//     request was queued for the server, without any indication of whether the\n//     server considered the request valid or took any action. On data\n//     validation or mach_msg() failure, another code will be returned\n//     indicating the nature of the error.\nsimpleroutine child_port_check_in(server: child_port_server_t;\n                                  token: child_port_token_t;\n                                  port: mach_port_poly_t);\n"
  },
  {
    "path": "util/mach/child_port_handshake.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/child_port_handshake.h\"\n\n#include <errno.h>\n#include <pthread.h>\n#include <stdint.h>\n#include <sys/event.h>\n#include <sys/socket.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <iterator>\n#include <utility>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/rand_util.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"util/file/file_io.h\"\n#include \"util/mach/bootstrap.h\"\n#include \"util/mach/child_port.h\"\n#include \"util/mach/child_port_server.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/misc/random_string.h\"\n\nnamespace crashpad {\nnamespace {\n\nclass ChildPortHandshakeServer final : public ChildPortServer::Interface {\n public:\n  ChildPortHandshakeServer();\n\n  ChildPortHandshakeServer(const ChildPortHandshakeServer&) = delete;\n  ChildPortHandshakeServer& operator=(const ChildPortHandshakeServer&) = delete;\n\n  ~ChildPortHandshakeServer();\n\n  mach_port_t RunServer(base::ScopedFD server_write_fd,\n                        ChildPortHandshake::PortRightType port_right_type);\n\n private:\n  // ChildPortServer::Interface:\n  kern_return_t HandleChildPortCheckIn(child_port_server_t server,\n                                       child_port_token_t token,\n                                       mach_port_t port,\n                                       mach_msg_type_name_t right_type,\n                                       const mach_msg_trailer_t* trailer,\n                                       bool* destroy_request) override;\n\n  child_port_token_t token_;\n  mach_port_t port_;\n  mach_msg_type_name_t right_type_;\n  bool checked_in_;\n};\n\nChildPortHandshakeServer::ChildPortHandshakeServer()\n    : token_(0),\n      port_(MACH_PORT_NULL),\n      right_type_(MACH_MSG_TYPE_PORT_NONE),\n      checked_in_(false) {\n}\n\nChildPortHandshakeServer::~ChildPortHandshakeServer() {\n}\n\nmach_port_t ChildPortHandshakeServer::RunServer(\n    base::ScopedFD server_write_fd,\n    ChildPortHandshake::PortRightType port_right_type) {\n  DCHECK_EQ(port_, kMachPortNull);\n  DCHECK(!checked_in_);\n  DCHECK(server_write_fd.is_valid());\n\n  // Initialize the token and share it with the client via the pipe.\n  token_ = base::RandUint64();\n  if (!LoggingWriteFile(server_write_fd.get(), &token_, sizeof(token_))) {\n    LOG(WARNING) << \"no client check-in\";\n    return MACH_PORT_NULL;\n  }\n\n  // Create a unique name for the bootstrap service mapping. Make it unguessable\n  // to prevent outsiders from grabbing the name first, which would cause\n  // bootstrap_check_in() to fail.\n  uint64_t thread_id;\n  errno = pthread_threadid_np(pthread_self(), &thread_id);\n  PCHECK(errno == 0) << \"pthread_threadid_np\";\n  std::string service_name = base::StringPrintf(\n      \"org.chromium.crashpad.child_port_handshake.%d.%llu.%s\",\n      getpid(),\n      thread_id,\n      RandomString().c_str());\n\n  // Check the new service in with the bootstrap server, obtaining a receive\n  // right for it.\n  base::apple::ScopedMachReceiveRight server_port(\n      BootstrapCheckIn(service_name));\n  CHECK(server_port.is_valid());\n\n  // Share the service name with the client via the pipe.\n  uint32_t service_name_length = service_name.size();\n  if (!LoggingWriteFile(server_write_fd.get(),\n                        &service_name_length,\n                        sizeof(service_name_length))) {\n    LOG(WARNING) << \"no client check-in\";\n    return MACH_PORT_NULL;\n  }\n\n  if (!LoggingWriteFile(\n          server_write_fd.get(), service_name.c_str(), service_name_length)) {\n    LOG(WARNING) << \"no client check-in\";\n    return MACH_PORT_NULL;\n  }\n\n  // Prior to macOS 10.12, a kqueue cannot monitor a raw Mach receive right with\n  // EVFILT_MACHPORT. It requires a port set. Compare 10.11.6\n  // xnu-3248.60.10/osfmk/ipc/ipc_pset.c filt_machportattach(), which requires\n  // MACH_PORT_RIGHT_PORT_SET, to 10.12.0 xnu-3789.1.32/osfmk/ipc/ipc_pset.c\n  // filt_machportattach(), which also handles MACH_PORT_TYPE_RECEIVE. Create a\n  // new port set and add the receive right to it.\n  base::apple::ScopedMachPortSet server_port_set(\n      NewMachPort(MACH_PORT_RIGHT_PORT_SET));\n  CHECK(server_port_set.is_valid());\n\n  kern_return_t kr = mach_port_insert_member(\n      mach_task_self(), server_port.get(), server_port_set.get());\n  MACH_CHECK(kr == KERN_SUCCESS, kr) << \"mach_port_insert_member\";\n\n  // Set up a kqueue to monitor both the server’s receive right and the write\n  // side of the pipe. Messages from the client will be received via the receive\n  // right, and the pipe will show EOF if the client closes its read side\n  // prematurely.\n  base::ScopedFD kq(kqueue());\n  PCHECK(kq != -1) << \"kqueue\";\n\n  struct kevent changelist[2];\n  EV_SET(&changelist[0],\n         server_port_set.get(),\n         EVFILT_MACHPORT,\n         EV_ADD | EV_CLEAR,\n         0,\n         0,\n         nullptr);\n  EV_SET(&changelist[1],\n         server_write_fd.get(),\n         EVFILT_WRITE,\n         EV_ADD | EV_CLEAR,\n         0,\n         0,\n         nullptr);\n  int rv = HANDLE_EINTR(\n      kevent(kq.get(), changelist, std::size(changelist), nullptr, 0, nullptr));\n  PCHECK(rv != -1) << \"kevent\";\n\n  ChildPortServer child_port_server(this);\n\n  bool blocking = true;\n  DCHECK(!checked_in_);\n  while (!checked_in_) {\n    DCHECK_EQ(port_, kMachPortNull);\n\n    // Get a kevent from the kqueue. Block while waiting for an event unless the\n    // write pipe has arrived at EOF, in which case the kevent() should be\n    // nonblocking. Although the client sends its check-in message before\n    // closing the read side of the pipe, this organization allows the events to\n    // be delivered out of order and the check-in message will still be\n    // processed.\n    struct kevent event;\n    constexpr timespec nonblocking_timeout = {};\n    const timespec* timeout = blocking ? nullptr : &nonblocking_timeout;\n    rv = HANDLE_EINTR(kevent(kq.get(), nullptr, 0, &event, 1, timeout));\n    PCHECK(rv != -1) << \"kevent\";\n\n    if (rv == 0) {\n      // Non-blocking kevent() with no events to return.\n      DCHECK(!blocking);\n      LOG(WARNING) << \"no client check-in\";\n      return MACH_PORT_NULL;\n    }\n\n    DCHECK_EQ(rv, 1);\n\n    if (event.flags & EV_ERROR) {\n      // kevent() may have put its error here.\n      errno = event.data;\n      PLOG(FATAL) << \"kevent\";\n    }\n\n    switch (event.filter) {\n      case EVFILT_MACHPORT: {\n        // There’s something to receive on the port set.\n        DCHECK_EQ(event.ident, server_port_set.get());\n\n        // Run the message server in an inner loop instead of using\n        // MachMessageServer::kPersistent. This allows the loop to exit as soon\n        // as child_port_ is set, even if other messages are queued. This needs\n        // to drain all messages, because the use of edge triggering (EV_CLEAR)\n        // means that if more than one message is in the queue when kevent()\n        // returns, no more notifications will be generated.\n        while (!checked_in_) {\n          // If a proper message is received from child_port_check_in(),\n          // this will call HandleChildPortCheckIn().\n          mach_msg_return_t mr =\n              MachMessageServer::Run(&child_port_server,\n                                     server_port_set.get(),\n                                     MACH_MSG_OPTION_NONE,\n                                     MachMessageServer::kOneShot,\n                                     MachMessageServer::kReceiveLargeIgnore,\n                                     kMachMessageTimeoutNonblocking);\n          if (mr == MACH_RCV_TIMED_OUT) {\n            break;\n          } else if (mr != MACH_MSG_SUCCESS) {\n            MACH_LOG(ERROR, mr) << \"MachMessageServer::Run\";\n            return MACH_PORT_NULL;\n          }\n        }\n        break;\n      }\n\n      case EVFILT_WRITE:\n        // The write pipe is ready to be written to, or it’s at EOF. The former\n        // case is uninteresting, but a notification for this may be presented\n        // because the write pipe will be ready to be written to, at the latest,\n        // when the client reads its messages from the read side of the same\n        // pipe. Ignore that case. Multiple notifications for that situation\n        // will not be generated because edge triggering (EV_CLEAR) is used\n        // above.\n        DCHECK_EQ(implicit_cast<int>(event.ident), server_write_fd.get());\n        if (event.flags & EV_EOF) {\n          // There are no readers attached to the write pipe. The client has\n          // closed its side of the pipe. There can be one last shot at\n          // receiving messages, in case the check-in message is delivered\n          // out of order, after the EOF notification.\n          blocking = false;\n        }\n        break;\n\n      default:\n        NOTREACHED();\n    }\n  }\n\n  if (port_ == MACH_PORT_NULL) {\n    return MACH_PORT_NULL;\n  }\n\n  bool mismatch = false;\n  switch (port_right_type) {\n    case ChildPortHandshake::PortRightType::kReceiveRight:\n      if (right_type_ != MACH_MSG_TYPE_PORT_RECEIVE) {\n        LOG(ERROR) << \"expected receive right, observed \" << right_type_;\n        mismatch = true;\n      }\n      break;\n    case ChildPortHandshake::PortRightType::kSendRight:\n      if (right_type_ != MACH_MSG_TYPE_PORT_SEND &&\n          right_type_ != MACH_MSG_TYPE_PORT_SEND_ONCE) {\n        LOG(ERROR) << \"expected send or send-once right, observed \"\n                   << right_type_;\n        mismatch = true;\n      }\n      break;\n  }\n\n  if (mismatch) {\n    MachMessageDestroyReceivedPort(port_, right_type_);\n    port_ = MACH_PORT_NULL;\n    return MACH_PORT_NULL;\n  }\n\n  mach_port_t port = MACH_PORT_NULL;\n  std::swap(port_, port);\n  return port;\n}\n\nkern_return_t ChildPortHandshakeServer::HandleChildPortCheckIn(\n    child_port_server_t server,\n    const child_port_token_t token,\n    mach_port_t port,\n    mach_msg_type_name_t right_type,\n    const mach_msg_trailer_t* trailer,\n    bool* destroy_request) {\n  DCHECK_EQ(port_, kMachPortNull);\n  DCHECK(!checked_in_);\n\n  if (token != token_) {\n    // If the token’s not correct, someone’s attempting to spoof the legitimate\n    // client.\n    LOG(WARNING) << \"ignoring incorrect token\";\n    *destroy_request = true;\n  } else {\n    checked_in_ = true;\n\n    if (right_type != MACH_MSG_TYPE_PORT_RECEIVE &&\n        right_type != MACH_MSG_TYPE_PORT_SEND &&\n        right_type != MACH_MSG_TYPE_PORT_SEND_ONCE) {\n      // The message needs to carry a receive, send, or send-once right.\n      LOG(ERROR) << \"invalid right type \" << right_type;\n      *destroy_request = true;\n    } else {\n      // Communicate the child port and right type back to the RunServer().\n      // *destroy_request is left at false, because RunServer() needs the right\n      // to remain intact. It gives ownership of the right to its caller.\n      port_ = port;\n      right_type_ = right_type;\n    }\n  }\n\n  // This is a MIG simpleroutine, there is no reply message.\n  return MIG_NO_REPLY;\n}\n\n}  // namespace\n\nChildPortHandshake::ChildPortHandshake()\n    : client_read_fd_(),\n      server_write_fd_() {\n  // Use socketpair() instead of pipe(). There is no way to suppress SIGPIPE on\n  // pipes in Mac OS X 10.6, because the F_SETNOSIGPIPE fcntl() command was not\n  // introduced until 10.7.\n  int pipe_fds[2];\n  PCHECK(socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fds) == 0)\n      << \"socketpair\";\n\n  client_read_fd_.reset(pipe_fds[0]);\n  server_write_fd_.reset(pipe_fds[1]);\n\n  // Simulate pipe() semantics by shutting down the “wrong” sides of the socket.\n  PCHECK(shutdown(server_write_fd_.get(), SHUT_RD) == 0) << \"shutdown SHUT_RD\";\n  PCHECK(shutdown(client_read_fd_.get(), SHUT_WR) == 0) << \"shutdown SHUT_WR\";\n\n  // SIGPIPE is undesirable when writing to this pipe. Allow broken-pipe writes\n  // to fail with EPIPE instead.\n  constexpr int value = 1;\n  PCHECK(setsockopt(server_write_fd_.get(),\n                    SOL_SOCKET,\n                    SO_NOSIGPIPE,\n                    &value,\n                    sizeof(value)) == 0) << \"setsockopt\";\n}\n\nChildPortHandshake::~ChildPortHandshake() {\n}\n\nbase::ScopedFD ChildPortHandshake::ClientReadFD() {\n  DCHECK(client_read_fd_.is_valid());\n  return std::move(client_read_fd_);\n}\n\nbase::ScopedFD ChildPortHandshake::ServerWriteFD() {\n  DCHECK(server_write_fd_.is_valid());\n  return std::move(server_write_fd_);\n}\n\nmach_port_t ChildPortHandshake::RunServer(PortRightType port_right_type) {\n  client_read_fd_.reset();\n  return RunServerForFD(std::move(server_write_fd_), port_right_type);\n}\n\nbool ChildPortHandshake::RunClient(mach_port_t port,\n                                   mach_msg_type_name_t right_type) {\n  server_write_fd_.reset();\n  return RunClientForFD(std::move(client_read_fd_), port, right_type);\n}\n\n// static\nmach_port_t ChildPortHandshake::RunServerForFD(base::ScopedFD server_write_fd,\n                                               PortRightType port_right_type) {\n  ChildPortHandshakeServer server;\n  return server.RunServer(std::move(server_write_fd), port_right_type);\n}\n\n// static\nbool ChildPortHandshake::RunClientForFD(base::ScopedFD client_read_fd,\n                                        mach_port_t port,\n                                        mach_msg_type_name_t right_type) {\n  DCHECK(client_read_fd.is_valid());\n\n  // Read the token and the service name from the read side of the pipe.\n  child_port_token_t token;\n  std::string service_name;\n  if (!RunClientInternal_ReadPipe(\n          client_read_fd.get(), &token, &service_name)) {\n    return false;\n  }\n\n  // Look up the server and check in with it by providing the token and port.\n  return RunClientInternal_SendCheckIn(service_name, token, port, right_type);\n}\n\n// static\nbool ChildPortHandshake::RunClientInternal_ReadPipe(int client_read_fd,\n                                                    child_port_token_t* token,\n                                                    std::string* service_name) {\n  // Read the token from the pipe.\n  if (!LoggingReadFileExactly(client_read_fd, token, sizeof(*token))) {\n    return false;\n  }\n\n  // Read the service name from the pipe.\n  uint32_t service_name_length;\n  if (!LoggingReadFileExactly(\n          client_read_fd, &service_name_length, sizeof(service_name_length))) {\n    return false;\n  }\n\n  service_name->resize(service_name_length);\n  if (!service_name->empty() &&\n      !LoggingReadFileExactly(\n          client_read_fd, &(*service_name)[0], service_name_length)) {\n    return false;\n  }\n\n  return true;\n}\n\n// static\nbool ChildPortHandshake::RunClientInternal_SendCheckIn(\n    const std::string& service_name,\n    child_port_token_t token,\n    mach_port_t port,\n    mach_msg_type_name_t right_type) {\n  // Get a send right to the server by looking up the service with the bootstrap\n  // server by name.\n  base::apple::ScopedMachSendRight server_port(BootstrapLookUp(service_name));\n  if (server_port == kMachPortNull) {\n    return false;\n  }\n\n  // Check in with the server.\n  kern_return_t kr = child_port_check_in(\n      server_port.get(), token, port, right_type);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << \"child_port_check_in\";\n    return false;\n  }\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/child_port_handshake.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_\n#define CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_\n\n#include <mach/mach.h>\n\n#include <string>\n#include <tuple>\n\n#include \"base/files/scoped_file.h\"\n#include \"util/mach/child_port_types.h\"\n\nnamespace crashpad {\n\nnamespace test {\nnamespace {\nclass ChildPortHandshakeTest;\n}  // namespace\n}  // namespace test\n\n//! \\brief Implements a handshake protocol that allows processes to exchange\n//!     port rights.\n//!\n//! Ordinarily, there is no way for parent and child processes to exchange port\n//! rights, outside of the rights that children inherit from their parents.\n//! These include task-special ports and exception ports, but all of these have\n//! system-defined uses, and cannot reliably be replaced: in a multi-threaded\n//! parent, it is impossible to temporarily change an inheritable port while\n//! maintaining a guarantee that another thread will not attempt to use it, and\n//! in children, it difficult to guarantee that nothing will attempt to use an\n//! inheritable port before it can be replaced with the correct one. This latter\n//! concern is becoming increasingly more pronounced as system libraries perform\n//! more operations that rely on an inherited port in module initializers.\n//!\n//! The protocol implemented by this class involves a server that runs in one\n//! process. The server is published with the bootstrap server, which the other\n//! process has access to because the bootstrap port is one of the inherited\n//! task-special ports. The two processes also share a pipe, which the server\n//! can write to and the client can read from. The server will write a random\n//! token to this pipe, along with the name under which its service has been\n//! registered with the bootstrap server. The client can then obtain a send\n//! right to this service with `bootstrap_look_up()`, and send a check-in\n//! message containing the token value and the port right of its choice by\n//! calling `child_port_check_in()`.\n//!\n//! The inclusion of the token authenticates the client to the server. This is\n//! necessary because the service is published with the bootstrap server, which\n//! opens up access to it to more than the intended client. Because the token is\n//! passed to the client by a shared pipe, it constitutes a shared secret not\n//! known by other processes that may have incidental access to the server. The\n//! ChildPortHandshake server considers its randomly-generated token valid until\n//! a client checks in with it. This mechanism is used instead of examining the\n//! request message’s audit trailer to verify the sender’s process ID because in\n//! some process architectures, it may be impossible to verify the client’s\n//! process ID.\n//!\n//! The shared pipe serves another purpose: the server monitors it for an\n//! end-of-file (no readers) condition. Once detected, it will stop its blocking\n//! wait for a client to check in. This mechanism was also chosen for its\n//! ability to function properly in diverse process architectures.\n//!\n//! This class can be used to allow a child process to provide its parent with a\n//! send right to its task port, in cases where it is desirable for the parent\n//! to have such access. It can also be used to allow a parent process to\n//! transfer a receive right to a child process that implements the server for\n//! that right, or for a child process to establish its own server and provide\n//! its parent with a send right to that server, for cases where a service is\n//! provided and it is undesirable or impossible to provide it via the bootstrap\n//! or launchd interfaces.\n//!\n//! Example parent process, running a client that sends a receive right to its\n//! child:\n//! \\code\n//!   ChildPortHandshake child_port_handshake;\n//!   base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD();\n//!   std::string server_write_fd_string =\n//!       base::StringPrintf(\"%d\", server_write_fd.get());\n//!\n//!   pid_t pid = fork();\n//!   if (pid == 0) {\n//!     // Child\n//!\n//!     // Close all file descriptors above STDERR_FILENO except for\n//!     // server_write_fd. Let the child know what file descriptor to use for\n//!     // server_write_fd by passing it as argv[1]. Example code for the child\n//!     // process is below.\n//!     CloseMultipleNowOrOnExec(STDERR_FILENO + 1, server_write_fd.get());\n//!     execlp(\"./child\", \"child\", server_write_fd_string.c_str(), nullptr);\n//!   }\n//!\n//!   // Parent\n//!\n//!   // Close the child’s end of the pipe.\n//!   server_write_fd.reset();\n//!\n//!   // Make a new Mach receive right.\n//!   base::apple::ScopedMachReceiveRight\n//!       receive_right(NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n//!\n//!   // Make a send right corresponding to the receive right.\n//!   mach_port_t send_right;\n//!   mach_msg_type_name_t send_right_type;\n//!   mach_port_extract_right(mach_task_self(),\n//!                           receive_right.get(),\n//!                           MACH_MSG_TYPE_MAKE_SEND,\n//!                           &send_right,\n//!                           &send_right_type);\n//!   base::apple::ScopedMachSendRight send_right_owner(send_right);\n//!\n//!   // Send the receive right to the child process, retaining the send right\n//!   // for use in the parent process.\n//!   if (child_port_handshake.RunClient(receive_right.get(),\n//!                                      MACH_MSG_TYPE_MOVE_RECEIVE)) {\n//!     std::ignore = receive_right.release();\n//!   }\n//! \\endcode\n//!\n//! Example child process, running a server that receives a receive right from\n//! its parent:\n//! \\code\n//!   int main(int argc, char* argv[]) {\n//!     // The parent passed server_write_fd in argv[1].\n//!     base::ScopedFD server_write_fd(atoi(argv[1]));\n//!\n//!     // Obtain a receive right from the parent process.\n//!     base::apple::ScopedMachReceiveRight receive_right(\n//!         ChildPortHandshake::RunServerForFD(\n//!             std::move(server_write_fd),\n//!             ChildPortHandshake::PortRightType::kReceiveRight));\n//!   }\n//! \\endcode\nclass ChildPortHandshake {\n public:\n  //! \\brief Controls whether a receive or send right is expected to be\n  //!     obtained from the client by the server’s call to RunServer().\n  enum class PortRightType {\n    //! \\brief The server expects to receive a receive right.\n    kReceiveRight = 0,\n\n    //! \\brief The server expects to receive a send or send-once right.\n    kSendRight,\n  };\n\n  ChildPortHandshake();\n\n  ChildPortHandshake(const ChildPortHandshake&) = delete;\n  ChildPortHandshake& operator=(const ChildPortHandshake&) = delete;\n\n  ~ChildPortHandshake();\n\n  //! \\brief Obtains the “read” side of the pipe, to be used by the client.\n  //!\n  //! This file descriptor must be passed to RunClientForFD().\n  //!\n  //! \\return The file descriptor that the client should read from.\n  base::ScopedFD ClientReadFD();\n\n  //! \\brief Obtains the “write” side of the pipe, to be used by the server.\n  //!\n  //! This file descriptor must be passed to RunServerForFD().\n  //!\n  //! \\return The file descriptor that the server should write to.\n  base::ScopedFD ServerWriteFD();\n\n  //! \\brief Runs the server.\n  //!\n  //! This method closes the “read” side of the pipe in-process, so that the\n  //! client process holds the only file descriptor that can read from the pipe.\n  //! It then calls RunServerForFD() using the “write” side of the pipe. If\n  //! ClientReadFD() has already been called in the server process, the caller\n  //! must ensure that the file descriptor returned by ClientReadFD() is closed\n  //! prior to calling this method.\n  mach_port_t RunServer(PortRightType port_right_type);\n\n  //! \\brief Runs the client.\n  //!\n  //! This method closes the “write” side of the pipe in-process, so that the\n  //! server process holds the only file descriptor that can write to the pipe.\n  //! It then calls RunClientForFD() using the “read” side of the pipe. If\n  //! ServerWriteFD() has already been called in the client process, the caller\n  //! must ensure that the file descriptor returned by ServerWriteFD() is closed\n  //! prior to calling this method.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool RunClient(mach_port_t port, mach_msg_type_name_t right_type);\n\n  //! \\brief Runs the server.\n  //!\n  //! If a ChildPortHandshake object is available, don’t call this static\n  //! function. Instead, call RunServer(), which wraps this function. When using\n  //! this function, the caller is responsible for ensuring that the client\n  //! “read” side of the pipe is closed in the server process prior to calling\n  //! this function.\n  //!\n  //! This function performs these tasks:\n  //!  - Creates a random token and sends it via the pipe.\n  //!  - Checks its service in with the bootstrap server, and sends the name\n  //!    of its bootstrap service mapping via the pipe.\n  //!  - Simultaneously receives messages on its Mach server and monitors the\n  //!    pipe for end-of-file. This is a blocking operation.\n  //!  - When a Mach message is received, calls HandleChildPortCheckIn() to\n  //!    interpret and validate it, and if the message is valid, returns the\n  //!    port right extracted from the message. If the message is not valid,\n  //!    this method will continue waiting for a valid message. Valid messages\n  //!    are properly formatted and have the correct token. The right carried in\n  //!    a valid message will be returned. If a message is not valid, this\n  //!    method will continue waiting for pipe EOF or a valid message.\n  //!  - When notified of pipe EOF, returns `MACH_PORT_NULL`.\n  //!  - Regardless of return value, destroys the server’s receive right and\n  //!    closes the pipe.\n  //!\n  //! \\param[in] server_write_fd The write side of the pipe shared with the\n  //!     client process. This function takes ownership of this file descriptor,\n  //!     and will close it prior to returning.\n  //! \\param[in] port_right_type The port right type expected to be received\n  //!     from the client. If the port right received from the client does not\n  //!     match the expected type, the received port right will be destroyed,\n  //!     and `MACH_PORT_NULL` will be returned.\n  //!\n  //! \\return On success, the port right provided by the client. The caller\n  //!     takes ownership of this right. On failure, `MACH_PORT_NULL`,\n  //!     indicating that the client did not check in properly before\n  //!     terminating, where termination is detected by detecting that the read\n  //!     side of the shared pipe has closed. On failure, a message indicating\n  //!     the nature of the failure will be logged.\n  static mach_port_t RunServerForFD(base::ScopedFD server_write_fd,\n                                    PortRightType port_right_type);\n\n  //! \\brief Runs the client.\n  //!\n  //! If a ChildPortHandshake object is available, don’t call this static\n  //! function. Instead, call RunClient(), which wraps this function. When using\n  //! this function, the caller is responsible for ensuring that the server\n  //! “write” side of the pipe is closed in the client process prior to calling\n  //! this function.\n  //!\n  //! This function performs these tasks:\n  //!  - Reads the token from the pipe.\n  //!  - Reads the bootstrap service name from the pipe.\n  //!  - Obtains a send right to the server by calling `bootstrap_look_up()`.\n  //!  - Sends a check-in message to the server by calling\n  //!    `child_port_check_in()`, providing the token and the user-supplied port\n  //!    right.\n  //!  - Deallocates the send right to the server, and closes the pipe.\n  //!\n  //! There is no return value because `child_port_check_in()` is a MIG\n  //! `simpleroutine`, and the server does not send a reply. This allows\n  //! check-in to occur without blocking to wait for a reply.\n  //!\n  //! \\param[in] client_read_fd The “read” side of the pipe shared with the\n  //!     server process. This function takes ownership of this file descriptor,\n  //!     and will close it prior to returning.\n  //! \\param[in] port The port right that will be passed to the server by\n  //!     `child_port_check_in()`.\n  //! \\param[in] right_type The right type to furnish the server with. If \\a\n  //!     port is a send right, this can be `MACH_MSG_TYPE_COPY_SEND` or\n  //!     `MACH_MSG_TYPE_MOVE_SEND`. If \\a port is a send-once right, this can\n  //!     be `MACH_MSG_TYPE_MOVE_SEND_ONCE`. If \\a port is a receive right, this\n  //!     can be `MACH_MSG_TYPE_MAKE_SEND`, `MACH_MSG_TYPE_MAKE_SEND_ONCE`, or\n  //!     `MACH_MSG_TYPE_MOVE_RECEIVE`.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged. On\n  //!     failure, the port right corresponding to a \\a right_type of\n  //!     `MACH_MSG_TYPE_MOVE_*` is not consumed, and the caller must dispose of\n  //!     the right if necessary.\n  static bool RunClientForFD(base::ScopedFD client_read_fd,\n                             mach_port_t port,\n                             mach_msg_type_name_t right_type);\n\n private:\n  //! \\brief Runs the read-from-pipe portion of the client’s side of the\n  //!     handshake. This is an implementation detail of RunClient and is only\n  //!     exposed for testing purposes.\n  //!\n  //! When using this function and RunClientInternal_SendCheckIn(), the caller\n  //! is responsible for closing \\a pipe_read at an appropriate time, normally\n  //! after calling RunClientInternal_SendCheckIn().\n  //!\n  //! \\param[in] pipe_read The “read” side of the pipe shared with the server\n  //!     process.\n  //! \\param[out] token The token value read from \\a pipe_read.\n  //! \\param[out] service_name The service name as registered with the bootstrap\n  //!     server, read from \\a pipe_read.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  static bool RunClientInternal_ReadPipe(int pipe_read,\n                                         child_port_token_t* token,\n                                         std::string* service_name);\n\n  //! \\brief Runs the check-in portion of the client’s side of the handshake.\n  //!     This is an implementation detail of RunClient and is only exposed for\n  //!     testing purposes.\n  //!\n  //! When using this RunClientInternal_ReadPipe() and this function, the caller\n  //! is responsible for closing the “read” side of the pipe at an appropriate\n  //! time, normally after calling this function.\n  //!\n  //! \\param[in] service_name The service name as registered with the bootstrap\n  //!     server, to be looked up with `bootstrap_look_up()`.\n  //! \\param[in] token The token value to provide during check-in.\n  //! \\param[in] port The port that will be passed to the server by\n  //!     `child_port_check_in()`.\n  //! \\param[in] right_type The right type to furnish the server with.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  static bool RunClientInternal_SendCheckIn(const std::string& service_name,\n                                            child_port_token_t token,\n                                            mach_port_t port,\n                                            mach_msg_type_name_t right_type);\n\n  base::ScopedFD client_read_fd_;\n  base::ScopedFD server_write_fd_;\n\n  friend class test::ChildPortHandshakeTest;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_CHILD_PORT_HANDSHAKE_H_\n"
  },
  {
    "path": "util/mach/child_port_handshake_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/child_port_handshake.h\"\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"gtest/gtest.h\"\n#include \"test/multiprocess.h\"\n#include \"util/mach/child_port_types.h\"\n#include \"util/mach/mach_extensions.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass ChildPortHandshakeTest : public Multiprocess {\n public:\n  enum class ClientProcess {\n    // The child runs the client and the parent runs the server.\n    kChildClient = 0,\n\n    // The parent runs the client and the child runs the server.\n    kParentClient,\n  };\n\n  enum class TestType {\n    // The client checks in with the server, transferring a receive right.\n    kClientChecksIn_ReceiveRight = 0,\n\n    // In this test, the client checks in with the server normally. It sends a\n    // copy of its bootstrap port to the server, because both parent and child\n    // should have the same bootstrap port, allowing for verification.\n    kClientChecksIn_SendRight,\n\n    // The client checks in with the server, transferring a send-once right.\n    kClientChecksIn_SendOnceRight,\n\n    // In this test, the client reads from its pipe, and subsequently exits\n    // without checking in. This tests that the server properly detects that it\n    // has lost its client after sending instructions to it via the pipe, while\n    // waiting for a check-in message.\n    kClientDoesNotCheckIn,\n\n    // In this test, the client exits without checking in. This tests that the\n    // server properly detects that it has lost a client. Whether or not the\n    // client closes the pipe before the server writes to it is a race, and the\n    // server needs to be able to detect client loss in both cases, so the\n    // ClientDoesNotCheckIn_ReadsPipe and NoClient tests also exist to test\n    // these individual cases more deterministically.\n    kClientDoesNotCheckIn_ReadsPipe,\n\n    // In this test, the client checks in with the server with an incorrect\n    // token value and a copy of its own task port. The server should reject the\n    // message because of the invalid token, and return MACH_PORT_NULL to its\n    // caller.\n    kTokenIncorrect,\n\n    // In this test, the client checks in with the server with an incorrect\n    // token value and a copy of its own task port, and subsequently, the\n    // correct token value and a copy of its bootstrap port. The server should\n    // reject the first because of the invalid token, but it should continue\n    // waiting for a message with a valid token as long as the pipe remains\n    // open. It should wind wind up returning the bootstrap port, allowing for\n    // verification.\n    kTokenIncorrectThenCorrect,\n\n    // The server dies. The failure should be reported in the client. This test\n    // type is only compatible with ClientProcess::kParentClient.\n    kServerDies,\n  };\n\n  ChildPortHandshakeTest(ClientProcess client_process, TestType test_type)\n      : Multiprocess(),\n        child_port_handshake_(),\n        client_process_(client_process),\n        test_type_(test_type) {\n  }\n\n  ChildPortHandshakeTest(const ChildPortHandshakeTest&) = delete;\n  ChildPortHandshakeTest& operator=(const ChildPortHandshakeTest&) = delete;\n\n  ~ChildPortHandshakeTest() {\n  }\n\n private:\n  void RunServer() {\n    if (test_type_ == TestType::kServerDies) {\n      return;\n    }\n\n    base::apple::ScopedMachReceiveRight receive_right;\n    base::apple::ScopedMachSendRight send_right;\n    if (test_type_ == TestType::kClientChecksIn_ReceiveRight) {\n      receive_right.reset(child_port_handshake_.RunServer(\n          ChildPortHandshake::PortRightType::kReceiveRight));\n    } else {\n      send_right.reset(child_port_handshake_.RunServer(\n          ChildPortHandshake::PortRightType::kSendRight));\n    }\n\n    switch (test_type_) {\n      case TestType::kClientChecksIn_ReceiveRight:\n        EXPECT_TRUE(receive_right.is_valid());\n        break;\n\n      case TestType::kClientChecksIn_SendRight:\n      case TestType::kTokenIncorrectThenCorrect:\n        EXPECT_EQ(send_right, bootstrap_port);\n        break;\n\n      case TestType::kClientChecksIn_SendOnceRight:\n        EXPECT_TRUE(send_right.is_valid());\n        EXPECT_NE(send_right, bootstrap_port);\n        break;\n\n      case TestType::kClientDoesNotCheckIn:\n      case TestType::kClientDoesNotCheckIn_ReadsPipe:\n      case TestType::kTokenIncorrect:\n        EXPECT_FALSE(send_right.is_valid());\n        break;\n\n      case TestType::kServerDies:\n        // This was special-cased as an early return above.\n        FAIL();\n    }\n  }\n\n  void RunClient() {\n    switch (test_type_) {\n      case TestType::kClientChecksIn_SendRight: {\n        ASSERT_TRUE(child_port_handshake_.RunClient(bootstrap_port,\n                                                    MACH_MSG_TYPE_COPY_SEND));\n        break;\n      }\n\n      case TestType::kClientChecksIn_ReceiveRight: {\n        mach_port_t receive_right = NewMachPort(MACH_PORT_RIGHT_RECEIVE);\n        ASSERT_TRUE(child_port_handshake_.RunClient(\n              receive_right, MACH_MSG_TYPE_MOVE_RECEIVE));\n        break;\n      }\n\n      case TestType::kClientChecksIn_SendOnceRight: {\n        base::apple::ScopedMachReceiveRight receive_right(\n            NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n        ASSERT_TRUE(child_port_handshake_.RunClient(\n            receive_right.get(), MACH_MSG_TYPE_MAKE_SEND_ONCE));\n        break;\n      }\n\n      case TestType::kClientDoesNotCheckIn: {\n        child_port_handshake_.ServerWriteFD().reset();\n        child_port_handshake_.ClientReadFD().reset();\n        break;\n      }\n\n      case TestType::kClientDoesNotCheckIn_ReadsPipe: {\n        // Don’t run the standard client routine. Instead, drain the pipe, which\n        // will get the parent to the point that it begins waiting for a\n        // check-in message. Then, exit. The pipe is drained using the same\n        // implementation that the real client would use.\n        child_port_handshake_.ServerWriteFD().reset();\n        base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD();\n        child_port_token_t token;\n        std::string service_name;\n        ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe(\n            client_read_fd.get(), &token, &service_name));\n        break;\n      }\n\n      case TestType::kTokenIncorrect: {\n        // Don’t run the standard client routine. Instead, read the token and\n        // service name, mutate the token, and then check in with the bad token.\n        // The parent should reject the message.\n        child_port_handshake_.ServerWriteFD().reset();\n        base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD();\n        child_port_token_t token;\n        std::string service_name;\n        ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe(\n            client_read_fd.get(), &token, &service_name));\n        child_port_token_t bad_token = ~token;\n        ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn(\n            service_name,\n            bad_token,\n            mach_task_self(),\n            MACH_MSG_TYPE_COPY_SEND));\n        break;\n      }\n\n      case TestType::kTokenIncorrectThenCorrect: {\n        // Don’t run the standard client routine. Instead, read the token and\n        // service name. Mutate the token, and check in with the bad token,\n        // expecting the parent to reject the message. Then, check in with the\n        // correct token, expecting the parent to accept it.\n        child_port_handshake_.ServerWriteFD().reset();\n        base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD();\n        child_port_token_t token;\n        std::string service_name;\n        ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe(\n            client_read_fd.release(), &token, &service_name));\n        child_port_token_t bad_token = ~token;\n        ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn(\n            service_name,\n            bad_token,\n            mach_task_self(),\n            MACH_MSG_TYPE_COPY_SEND));\n        ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn(\n            service_name, token, bootstrap_port, MACH_MSG_TYPE_COPY_SEND));\n        break;\n      }\n\n      case TestType::kServerDies: {\n        ASSERT_EQ(client_process_, ClientProcess::kParentClient);\n        ASSERT_FALSE(child_port_handshake_.RunClient(bootstrap_port,\n                                                     MACH_MSG_TYPE_COPY_SEND));\n        break;\n      }\n    }\n  }\n\n  // Multiprocess:\n\n  void MultiprocessParent() override {\n    switch (client_process_) {\n      case ClientProcess::kChildClient:\n        RunServer();\n        break;\n      case ClientProcess::kParentClient:\n        RunClient();\n        break;\n    }\n  }\n\n  void MultiprocessChild() override {\n    switch (client_process_) {\n      case ClientProcess::kChildClient:\n        RunClient();\n        break;\n      case ClientProcess::kParentClient:\n        RunServer();\n        break;\n    }\n  }\n\n private:\n  ChildPortHandshake child_port_handshake_;\n  ClientProcess client_process_;\n  TestType test_type_;\n};\n\nTEST(ChildPortHandshake, ChildClientChecksIn_ReceiveRight) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kChildClient,\n      ChildPortHandshakeTest::TestType::kClientChecksIn_ReceiveRight);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ChildClientChecksIn_SendRight) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kChildClient,\n      ChildPortHandshakeTest::TestType::kClientChecksIn_SendRight);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ChildClientChecksIn_SendOnceRight) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kChildClient,\n      ChildPortHandshakeTest::TestType::kClientChecksIn_SendOnceRight);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ChildClientDoesNotCheckIn) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kChildClient,\n      ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ChildClientDoesNotCheckIn_ReadsPipe) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kChildClient,\n      ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ChildClientTokenIncorrect) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kChildClient,\n      ChildPortHandshakeTest::TestType::kTokenIncorrect);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ChildClientTokenIncorrectThenCorrect) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kChildClient,\n      ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ParentClientChecksIn_ReceiveRight) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kParentClient,\n      ChildPortHandshakeTest::TestType::kClientChecksIn_ReceiveRight);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ParentClientChecksIn_SendRight) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kParentClient,\n      ChildPortHandshakeTest::TestType::kClientChecksIn_SendRight);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ParentClientChecksIn_SendOnceRight) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kParentClient,\n      ChildPortHandshakeTest::TestType::kClientChecksIn_SendOnceRight);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ParentClientDoesNotCheckIn) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kParentClient,\n      ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ParentClientDoesNotCheckIn_ReadsPipe) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kParentClient,\n      ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ParentClientTokenIncorrect) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kParentClient,\n      ChildPortHandshakeTest::TestType::kTokenIncorrect);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ParentClientTokenIncorrectThenCorrect) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kParentClient,\n      ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, ParentClientServerDies) {\n  ChildPortHandshakeTest test(\n      ChildPortHandshakeTest::ClientProcess::kParentClient,\n      ChildPortHandshakeTest::TestType::kServerDies);\n  test.Run();\n}\n\nTEST(ChildPortHandshake, NoClient) {\n  // In this test, the client never checks in with the server because it never\n  // even runs. This tests that the server properly detects that it has no\n  // client at all, and does not terminate execution with an error such as\n  // “broken pipe” when attempting to send instructions to the client. This test\n  // is similar to kClientDoesNotCheckIn, but because there’s no client at all,\n  // the server is guaranteed to see that its pipe partner is gone.\n  ChildPortHandshake child_port_handshake;\n  base::apple::ScopedMachSendRight child_port(child_port_handshake.RunServer(\n      ChildPortHandshake::PortRightType::kSendRight));\n  EXPECT_FALSE(child_port.is_valid());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/child_port_server.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/child_port_server.h\"\n\n#include <iterator>\n\n#include \"util/mach/child_portServer.h\"\n#include \"util/mach/mach_message.h\"\n\nnamespace {\n\n// There is no predefined constant for this.\nenum MachMessageID : mach_msg_id_t {\n  kMachMessageIDChildPortCheckIn = 10011,\n};\n\n// The MIG-generated __MIG_check__Request__*() functions are not declared as\n// accepting const data, but they could have been because they in fact do not\n// modify the data. This wrapper function is provided to bridge the const gap\n// between the code in this file, which is const-correct and treats request\n// message data as const, and the generated function.\n\nkern_return_t MIGCheckRequestChildPortCheckIn(\n    const __Request__child_port_check_in_t* in_request) {\n  using Request = __Request__child_port_check_in_t;\n  return __MIG_check__Request__child_port_check_in_t(\n      const_cast<Request*>(in_request));\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nChildPortServer::ChildPortServer(ChildPortServer::Interface* interface)\n    : MachMessageServer::Interface(),\n      interface_(interface) {\n}\n\nbool ChildPortServer::MachMessageServerFunction(\n    const mach_msg_header_t* in_header,\n    mach_msg_header_t* out_header,\n    bool* destroy_complex_request) {\n  PrepareMIGReplyFromRequest(in_header, out_header);\n\n  const mach_msg_trailer_t* in_trailer =\n      MachMessageTrailerFromHeader(in_header);\n\n  switch (in_header->msgh_id) {\n    case kMachMessageIDChildPortCheckIn: {\n      // child_port_check_in(), handle_child_port_check_in().\n      using Request = __Request__child_port_check_in_t;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n      kern_return_t kr = MIGCheckRequestChildPortCheckIn(in_request);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = __Reply__child_port_check_in_t;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->RetCode =\n          interface_->HandleChildPortCheckIn(in_header->msgh_local_port,\n                                             in_request->token,\n                                             in_request->port.name,\n                                             in_request->port.disposition,\n                                             in_trailer,\n                                             destroy_complex_request);\n      return true;\n    }\n\n    default: {\n      SetMIGReplyError(out_header, MIG_BAD_ID);\n      return false;\n    }\n  }\n}\n\nstd::set<mach_msg_id_t> ChildPortServer::MachMessageServerRequestIDs() {\n  static constexpr mach_msg_id_t request_ids[] =\n      {kMachMessageIDChildPortCheckIn};\n  return std::set<mach_msg_id_t>(&request_ids[0],\n                                 &request_ids[std::size(request_ids)]);\n}\n\nmach_msg_size_t ChildPortServer::MachMessageServerRequestSize() {\n  return sizeof(__RequestUnion__handle_child_port_subsystem);\n}\n\nmach_msg_size_t ChildPortServer::MachMessageServerReplySize() {\n  return sizeof(__ReplyUnion__handle_child_port_subsystem);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/child_port_server.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_\n#define CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_\n\n#include <mach/mach.h>\n\n#include <set>\n\n#include \"util/mach/child_port_types.h\"\n#include \"util/mach/mach_message_server.h\"\n\nnamespace crashpad {\n\n//! \\brief A server interface for the `child_port` Mach subsystem.\nclass ChildPortServer : public MachMessageServer::Interface {\n public:\n  //! \\brief An interface that the request message that is a part of the\n  //!     `child_port` Mach subsystem can be dispatched to.\n  class Interface {\n   public:\n    //! \\brief Handles check-ins sent by `child_port_check_in()`.\n    //!\n    //! This behaves equivalently to a `handle_child_port_check_in()` function\n    //! used with `child_port_server()`.\n    //!\n    //! \\param[in] server\n    //! \\param[in] token\n    //! \\param[in] port\n    //! \\param[in] right_type\n    //! \\param[in] trailer The trailer received with the request message.\n    //! \\param[out] destroy_request `true` if the request message is to be\n    //!     destroyed even when this method returns success. See\n    //!     MachMessageServer::Interface.\n    virtual kern_return_t HandleChildPortCheckIn(\n        child_port_server_t server,\n        const child_port_token_t token,\n        mach_port_t port,\n        mach_msg_type_name_t right_type,\n        const mach_msg_trailer_t* trailer,\n        bool* destroy_request) = 0;\n\n   protected:\n    ~Interface() {}\n  };\n\n  //! \\brief Constructs an object of this class.\n  //!\n  //! \\param[in] interface The interface to dispatch requests to. Weak.\n  explicit ChildPortServer(Interface* interface);\n\n  ChildPortServer(const ChildPortServer&) = delete;\n  ChildPortServer& operator=(const ChildPortServer&) = delete;\n\n  // MachMessageServer::Interface:\n  bool MachMessageServerFunction(const mach_msg_header_t* in_header,\n                                 mach_msg_header_t* out_header,\n                                 bool* destroy_complex_request) override;\n  std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;\n  mach_msg_size_t MachMessageServerRequestSize() override;\n  mach_msg_size_t MachMessageServerReplySize() override;\n\n private:\n  Interface* interface_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_CHILD_PORT_SERVER_H_\n"
  },
  {
    "path": "util/mach/child_port_server_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/child_port_server.h\"\n\n#include <string.h>\n\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing testing::Eq;\nusing testing::Pointee;\nusing testing::Return;\n\n// Fake Mach ports. These aren’t used as ports in these tests, they’re just used\n// as cookies to make sure that the correct values get passed to the correct\n// places.\nconstexpr mach_port_t kServerLocalPort = 0x05050505;\nconstexpr mach_port_t kCheckInPort = 0x06060606;\n\n// Other fake values.\nconstexpr mach_msg_type_name_t kCheckInPortRightType = MACH_MSG_TYPE_PORT_SEND;\nconstexpr child_port_token_t kCheckInToken = 0xfedcba9876543210;\n\n// The definition of the request structure from child_port.h isn’t available\n// here. It needs custom initialization code, so duplicate the expected\n// definition of the structure from child_port.h here in this file, and provide\n// the initialization code as a method in true object-oriented fashion.\n\nstruct __attribute__((packed, aligned(4))) ChildPortCheckInRequest {\n  ChildPortCheckInRequest() {\n    memset(this, 0xa5, sizeof(*this));\n    Head.msgh_bits =\n        MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND) | MACH_MSGH_BITS_COMPLEX;\n    Head.msgh_size = sizeof(*this) - sizeof(trailer);\n    Head.msgh_remote_port = MACH_PORT_NULL;\n    Head.msgh_local_port = kServerLocalPort;\n    Head.msgh_id = 10011;\n    msgh_body.msgh_descriptor_count = 1;\n    port.name = kCheckInPort;\n    port.disposition = kCheckInPortRightType;\n    port.type = MACH_MSG_PORT_DESCRIPTOR;\n    NDR = NDR_record;\n    token = kCheckInToken;\n  }\n\n  mach_msg_header_t Head;\n  mach_msg_body_t msgh_body;\n  mach_msg_port_descriptor_t port;\n  NDR_record_t NDR;\n  child_port_token_t token;\n  mach_msg_trailer_t trailer;\n};\n\nstruct MIGReply : public mig_reply_error_t {\n  MIGReply() {\n    memset(this, 0x5a, sizeof(*this));\n    RetCode = KERN_FAILURE;\n  }\n\n  void Verify() {\n    EXPECT_EQ(Head.msgh_bits,\n              implicit_cast<mach_msg_bits_t>(MACH_MSGH_BITS(0, 0)));\n    EXPECT_EQ(Head.msgh_size, sizeof(*this));\n    EXPECT_EQ(Head.msgh_remote_port, kMachPortNull);\n    EXPECT_EQ(Head.msgh_local_port, kMachPortNull);\n    EXPECT_EQ(Head.msgh_id, 10111);\n    EXPECT_EQ(memcmp(&NDR, &NDR_record, sizeof(NDR)), 0);\n    EXPECT_EQ(RetCode, MIG_NO_REPLY);\n  }\n};\n\nclass MockChildPortServerInterface : public ChildPortServer::Interface {\n public:\n  MOCK_METHOD(kern_return_t,\n              HandleChildPortCheckIn,\n              (child_port_server_t server,\n               const child_port_token_t token,\n               mach_port_t port,\n               mach_msg_type_name_t right_type,\n               const mach_msg_trailer_t* trailer,\n               bool* destroy_request));\n};\n\nTEST(ChildPortServer, MockChildPortCheckIn) {\n  MockChildPortServerInterface server_interface;\n  ChildPortServer server(&server_interface);\n\n  std::set<mach_msg_id_t> expect_request_ids;\n  expect_request_ids.insert(10011);  // There is no constant for this.\n  EXPECT_EQ(server.MachMessageServerRequestIDs(), expect_request_ids);\n\n  ChildPortCheckInRequest request;\n  EXPECT_EQ(server.MachMessageServerRequestSize(), request.Head.msgh_size);\n\n  MIGReply reply;\n  EXPECT_EQ(server.MachMessageServerReplySize(), sizeof(reply));\n\n  EXPECT_CALL(server_interface,\n              HandleChildPortCheckIn(kServerLocalPort,\n                                     kCheckInToken,\n                                     kCheckInPort,\n                                     kCheckInPortRightType,\n                                     Eq(&request.trailer),\n                                     Pointee(Eq(false))))\n      .WillOnce(Return(MIG_NO_REPLY))\n      .RetiresOnSaturation();\n\n  bool destroy_request = false;\n  EXPECT_TRUE(server.MachMessageServerFunction(\n      reinterpret_cast<mach_msg_header_t*>(&request),\n      reinterpret_cast<mach_msg_header_t*>(&reply),\n      &destroy_request));\n  EXPECT_FALSE(destroy_request);\n\n  reply.Verify();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/child_port_types.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_\n#define CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_\n\n#include <mach/mach.h>\n#include <stdint.h>\n\n// This file is #included by C (non-C++) files, and must remain strictly C.\n\ntypedef mach_port_t child_port_server_t;\ntypedef uint64_t child_port_token_t;\n\n#endif  // CRASHPAD_UTIL_MACH_CHILD_PORT_TYPES_H_\n"
  },
  {
    "path": "util/mach/composite_mach_message_server.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/composite_mach_message_server.h\"\n\n#include <algorithm>\n#include <ostream>\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"util/mach/mach_message.h\"\n\nnamespace crashpad {\n\nCompositeMachMessageServer::CompositeMachMessageServer()\n    : MachMessageServer::Interface(),\n      handler_map_(),\n      request_size_(sizeof(mach_msg_header_t)),\n      reply_size_(sizeof(mig_reply_error_t)) {\n}\n\nCompositeMachMessageServer::~CompositeMachMessageServer() {\n}\n\nvoid CompositeMachMessageServer::AddHandler(\n    MachMessageServer::Interface* handler) {\n  // Other cycles would be invalid as well, but they aren’t currently checked.\n  DCHECK_NE(handler, this);\n\n  std::set<mach_msg_id_t> request_ids = handler->MachMessageServerRequestIDs();\n  for (mach_msg_id_t request_id : request_ids) {\n    std::pair<HandlerMap::const_iterator, bool> result =\n        handler_map_.insert(std::make_pair(request_id, handler));\n    CHECK(result.second) << \"duplicate request ID \" << request_id;\n  }\n\n  request_size_ =\n      std::max(request_size_, handler->MachMessageServerRequestSize());\n  reply_size_ = std::max(reply_size_, handler->MachMessageServerReplySize());\n}\n\nbool CompositeMachMessageServer::MachMessageServerFunction(\n    const mach_msg_header_t* in,\n    mach_msg_header_t* out,\n    bool* destroy_complex_request) {\n  HandlerMap::const_iterator iterator = handler_map_.find(in->msgh_id);\n  if (iterator == handler_map_.end()) {\n    // Do what MIG-generated server routines do when they can’t dispatch a\n    // message.\n    PrepareMIGReplyFromRequest(in, out);\n    SetMIGReplyError(out, MIG_BAD_ID);\n    return false;\n  }\n\n  MachMessageServer::Interface* handler = iterator->second;\n  return handler->MachMessageServerFunction(in, out, destroy_complex_request);\n}\n\nstd::set<mach_msg_id_t>\nCompositeMachMessageServer::MachMessageServerRequestIDs() {\n  std::set<mach_msg_id_t> request_ids;\n  for (const auto& entry : handler_map_) {\n    request_ids.insert(entry.first);\n  }\n  return request_ids;\n}\n\nmach_msg_size_t CompositeMachMessageServer::MachMessageServerRequestSize() {\n  return request_size_;\n}\n\nmach_msg_size_t CompositeMachMessageServer::MachMessageServerReplySize() {\n  return reply_size_;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/composite_mach_message_server.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_\n#define CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_\n\n#include <mach/mach.h>\n\n#include <map>\n#include <set>\n\n#include \"util/mach/mach_message_server.h\"\n\nnamespace crashpad {\n\n//! \\brief Adapts multiple MachMessageServer::Interface implementations for\n//!     simultaneous use in a single MachMessageServer::Run() call.\n//!\n//! This class implements a MachMessageServer::Interface that contains other\n//! MachMessageServer::Interface objects.\n//!\n//! In some situations, it may be desirable for a Mach message server to handle\n//! messages from distinct MIG subsystems with distinct\n//! MachMessageServer::Interface implementations. This may happen if a single\n//! receive right is shared for multiple subsystems, or if distinct receive\n//! rights are combined in a Mach port set. In these cases, this class performs\n//! a first-level demultiplexing to forward request messages to the proper\n//! subsystem-level demultiplexers.\nclass CompositeMachMessageServer : public MachMessageServer::Interface {\n public:\n  CompositeMachMessageServer();\n\n  CompositeMachMessageServer(const CompositeMachMessageServer&) = delete;\n  CompositeMachMessageServer& operator=(const CompositeMachMessageServer&) =\n      delete;\n\n  ~CompositeMachMessageServer();\n\n  //! \\brief Adds a handler that messages can be dispatched to based on request\n  //!     message ID.\n  //!\n  //! \\param[in] handler A MachMessageServer handler. Ownership of this object\n  //!     is not taken. Cycles must not be created between objects. It is\n  //!     invalid to add an object as its own handler.\n  //!\n  //! If \\a handler claims to support any request ID that this object is already\n  //! able to handle, execution will be terminated.\n  void AddHandler(MachMessageServer::Interface* handler);\n\n  // MachMessageServer::Interface:\n\n  //! \\copydoc MachMessageServer::Interface::MachMessageServerFunction()\n  //!\n  //! This implementation forwards the message to an appropriate handler added\n  //! by AddHandler() on the basis of the \\a in request message’s message ID. If\n  //! no appropriate handler exists, the \\a out reply message is treated as a\n  //! `mig_reply_error_t`, its return code is set to `MIG_BAD_ID`, and `false`\n  //! is returned.\n  bool MachMessageServerFunction(const mach_msg_header_t* in,\n                                 mach_msg_header_t* out,\n                                 bool* destroy_complex_request) override;\n\n  //! \\copydoc MachMessageServer::Interface::MachMessageServerRequestIDs()\n  //!\n  //! This implementation returns the set of all request message Mach message\n  //! IDs of all handlers added by AddHandler().\n  std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;\n\n  //! \\copydoc MachMessageServer::Interface::MachMessageServerRequestSize()\n  //!\n  //! This implementation returns the maximum request message size of all\n  //! handlers added by AddHandler(). If no handlers are present, returns the\n  //! size of `mach_msg_header_t`, the minimum size of a MIG request message\n  //! that can be received for demultiplexing purposes.\n  mach_msg_size_t MachMessageServerRequestSize() override;\n\n  //! \\copydoc MachMessageServer::Interface::MachMessageServerReplySize()\n  //!\n  //! This implementation returns the maximum reply message size of all handlers\n  //! added by AddHandler(). If no handlers are present, returns the size of\n  //! `mig_reply_error_t`, the minimum size of a MIG reply message.\n  mach_msg_size_t MachMessageServerReplySize() override;\n\n private:\n  using HandlerMap = std::map<mach_msg_id_t, MachMessageServer::Interface*>;\n\n  HandlerMap handler_map_;  // weak\n  mach_msg_size_t request_size_;\n  mach_msg_size_t reply_size_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_COMPOSITE_MACH_MESSAGE_SERVER_H_\n"
  },
  {
    "path": "util/mach/composite_mach_message_server_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/composite_mach_message_server.h\"\n\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n#include \"util/mach/mach_message.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(CompositeMachMessageServer, Empty) {\n  CompositeMachMessageServer server;\n\n  EXPECT_TRUE(server.MachMessageServerRequestIDs().empty());\n\n  mach_msg_empty_rcv_t request = {};\n  EXPECT_EQ(server.MachMessageServerRequestSize(), sizeof(request.header));\n\n  mig_reply_error_t reply = {};\n  EXPECT_EQ(server.MachMessageServerReplySize(), sizeof(reply));\n\n  bool destroy_complex_request = false;\n  EXPECT_FALSE(server.MachMessageServerFunction(\n      &request.header, &reply.Head, &destroy_complex_request));\n  EXPECT_EQ(reply.RetCode, MIG_BAD_ID);\n}\n\nclass TestMachMessageHandler : public MachMessageServer::Interface {\n public:\n  TestMachMessageHandler()\n      : MachMessageServer::Interface(),\n        request_ids_(),\n        request_size_(0),\n        reply_size_(0),\n        return_code_(KERN_FAILURE),\n        return_value_(false),\n        destroy_complex_request_(false) {\n  }\n\n  TestMachMessageHandler(const TestMachMessageHandler&) = delete;\n  TestMachMessageHandler& operator=(const TestMachMessageHandler&) = delete;\n\n  ~TestMachMessageHandler() {\n  }\n\n  void SetReturnCodes(bool return_value,\n                      kern_return_t return_code,\n                      bool destroy_complex_request) {\n    return_value_ = return_value;\n    return_code_ = return_code;\n    destroy_complex_request_ = destroy_complex_request;\n  }\n\n  void AddRequestID(mach_msg_id_t request_id) {\n    request_ids_.insert(request_id);\n  }\n\n  void SetRequestSize(mach_msg_size_t request_size) {\n    request_size_ = request_size;\n  }\n\n  void SetReplySize(mach_msg_size_t reply_size) {\n    reply_size_ = reply_size;\n  }\n\n  // MachMessageServer::Interface:\n\n  bool MachMessageServerFunction(const mach_msg_header_t* in,\n                                 mach_msg_header_t* out,\n                                 bool* destroy_complex_request) override {\n    EXPECT_NE(request_ids_.find(in->msgh_id), request_ids_.end());\n\n    *destroy_complex_request = destroy_complex_request_;\n    PrepareMIGReplyFromRequest(in, out);\n    SetMIGReplyError(out, return_code_);\n    return return_value_;\n  }\n\n  std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {\n    return request_ids_;\n  }\n\n  mach_msg_size_t MachMessageServerRequestSize() override {\n    return request_size_;\n  }\n\n  mach_msg_size_t MachMessageServerReplySize() override {\n    return reply_size_;\n  }\n\n private:\n  std::set<mach_msg_id_t> request_ids_;\n  mach_msg_size_t request_size_;\n  mach_msg_size_t reply_size_;\n  kern_return_t return_code_;\n  bool return_value_;\n  bool destroy_complex_request_;\n};\n\nTEST(CompositeMachMessageServer, HandlerDoesNotHandle) {\n  TestMachMessageHandler handler;\n\n  CompositeMachMessageServer server;\n  server.AddHandler(&handler);\n\n  EXPECT_TRUE(server.MachMessageServerRequestIDs().empty());\n\n  mach_msg_empty_rcv_t request = {};\n  EXPECT_EQ(server.MachMessageServerRequestSize(), sizeof(request.header));\n\n  mig_reply_error_t reply = {};\n  EXPECT_EQ(server.MachMessageServerReplySize(), sizeof(reply));\n\n  bool destroy_complex_request = false;\n  EXPECT_FALSE(server.MachMessageServerFunction(\n      &request.header, &reply.Head, &destroy_complex_request));\n  EXPECT_EQ(reply.RetCode, MIG_BAD_ID);\n  EXPECT_FALSE(destroy_complex_request);\n}\n\nTEST(CompositeMachMessageServer, OneHandler) {\n  constexpr mach_msg_id_t kRequestID = 100;\n  constexpr mach_msg_size_t kRequestSize = 256;\n  constexpr mach_msg_size_t kReplySize = 128;\n  constexpr kern_return_t kReturnCode = KERN_SUCCESS;\n\n  TestMachMessageHandler handler;\n  handler.AddRequestID(kRequestID);\n  handler.SetRequestSize(kRequestSize);\n  handler.SetReplySize(kReplySize);\n  handler.SetReturnCodes(true, kReturnCode, true);\n\n  CompositeMachMessageServer server;\n\n  // The chosen request and reply sizes must be larger than the defaults for\n  // that portion fo the test to be valid.\n  EXPECT_GT(kRequestSize, server.MachMessageServerRequestSize());\n  EXPECT_GT(kReplySize, server.MachMessageServerReplySize());\n\n  server.AddHandler(&handler);\n\n  std::set<mach_msg_id_t> expect_request_ids;\n  expect_request_ids.insert(kRequestID);\n  EXPECT_EQ(server.MachMessageServerRequestIDs(), expect_request_ids);\n\n  EXPECT_EQ(server.MachMessageServerRequestSize(), kRequestSize);\n  EXPECT_EQ(server.MachMessageServerReplySize(), kReplySize);\n\n  mach_msg_empty_rcv_t request = {};\n  mig_reply_error_t reply = {};\n\n  // Send a message with an unknown request ID.\n  request.header.msgh_id = 0;\n  bool destroy_complex_request = false;\n  EXPECT_FALSE(server.MachMessageServerFunction(\n      &request.header, &reply.Head, &destroy_complex_request));\n  EXPECT_EQ(reply.RetCode, MIG_BAD_ID);\n  EXPECT_FALSE(destroy_complex_request);\n\n  // Send a message with a known request ID.\n  request.header.msgh_id = kRequestID;\n  EXPECT_TRUE(server.MachMessageServerFunction(\n      &request.header, &reply.Head, &destroy_complex_request));\n  EXPECT_EQ(reply.RetCode, kReturnCode);\n  EXPECT_TRUE(destroy_complex_request);\n}\n\nTEST(CompositeMachMessageServer, ThreeHandlers) {\n  static constexpr mach_msg_id_t kRequestIDs0[] = {5};\n  constexpr kern_return_t kReturnCode0 = KERN_SUCCESS;\n\n  static constexpr mach_msg_id_t kRequestIDs1[] = {4, 7};\n  constexpr kern_return_t kReturnCode1 = KERN_PROTECTION_FAILURE;\n\n  static constexpr mach_msg_id_t kRequestIDs2[] = {10, 0, 20};\n  constexpr mach_msg_size_t kRequestSize2 = 6144;\n  constexpr mach_msg_size_t kReplySize2 = 16384;\n  constexpr kern_return_t kReturnCode2 = KERN_NOT_RECEIVER;\n\n  TestMachMessageHandler handlers[3];\n  std::set<mach_msg_id_t> expect_request_ids;\n\n  for (size_t index = 0; index < std::size(kRequestIDs0); ++index) {\n    const mach_msg_id_t request_id = kRequestIDs0[index];\n    handlers[0].AddRequestID(request_id);\n    expect_request_ids.insert(request_id);\n  }\n  handlers[0].SetRequestSize(sizeof(mach_msg_header_t));\n  handlers[0].SetReplySize(sizeof(mig_reply_error_t));\n  handlers[0].SetReturnCodes(true, kReturnCode0, false);\n\n  for (size_t index = 0; index < std::size(kRequestIDs1); ++index) {\n    const mach_msg_id_t request_id = kRequestIDs1[index];\n    handlers[1].AddRequestID(request_id);\n    expect_request_ids.insert(request_id);\n  }\n  handlers[1].SetRequestSize(100);\n  handlers[1].SetReplySize(200);\n  handlers[1].SetReturnCodes(false, kReturnCode1, true);\n\n  for (size_t index = 0; index < std::size(kRequestIDs2); ++index) {\n    const mach_msg_id_t request_id = kRequestIDs2[index];\n    handlers[2].AddRequestID(request_id);\n    expect_request_ids.insert(request_id);\n  }\n  handlers[2].SetRequestSize(kRequestSize2);\n  handlers[2].SetReplySize(kReplySize2);\n  handlers[2].SetReturnCodes(true, kReturnCode2, true);\n\n  CompositeMachMessageServer server;\n\n  // The chosen request and reply sizes must be larger than the defaults for\n  // that portion fo the test to be valid.\n  EXPECT_GT(kRequestSize2, server.MachMessageServerRequestSize());\n  EXPECT_GT(kReplySize2, server.MachMessageServerReplySize());\n\n  server.AddHandler(&handlers[0]);\n  server.AddHandler(&handlers[1]);\n  server.AddHandler(&handlers[2]);\n\n  EXPECT_EQ(server.MachMessageServerRequestIDs(), expect_request_ids);\n\n  EXPECT_EQ(server.MachMessageServerRequestSize(), kRequestSize2);\n  EXPECT_EQ(server.MachMessageServerReplySize(), kReplySize2);\n\n  mach_msg_empty_rcv_t request = {};\n  mig_reply_error_t reply = {};\n\n  // Send a message with an unknown request ID.\n  request.header.msgh_id = 100;\n  bool destroy_complex_request = false;\n  EXPECT_FALSE(server.MachMessageServerFunction(\n      &request.header, &reply.Head, &destroy_complex_request));\n  EXPECT_EQ(reply.RetCode, MIG_BAD_ID);\n  EXPECT_FALSE(destroy_complex_request);\n\n  // Send messages with known request IDs.\n\n  for (size_t index = 0; index < std::size(kRequestIDs0); ++index) {\n    request.header.msgh_id = kRequestIDs0[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"handler 0, index %zu, id %d\", index, request.header.msgh_id));\n\n    EXPECT_TRUE(server.MachMessageServerFunction(\n        &request.header, &reply.Head, &destroy_complex_request));\n    EXPECT_EQ(reply.RetCode, kReturnCode0);\n    EXPECT_FALSE(destroy_complex_request);\n  }\n\n  for (size_t index = 0; index < std::size(kRequestIDs1); ++index) {\n    request.header.msgh_id = kRequestIDs1[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"handler 1, index %zu, id %d\", index, request.header.msgh_id));\n\n    EXPECT_FALSE(server.MachMessageServerFunction(\n        &request.header, &reply.Head, &destroy_complex_request));\n    EXPECT_EQ(reply.RetCode, kReturnCode1);\n    EXPECT_TRUE(destroy_complex_request);\n  }\n\n  for (size_t index = 0; index < std::size(kRequestIDs2); ++index) {\n    request.header.msgh_id = kRequestIDs2[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"handler 2, index %zu, id %d\", index, request.header.msgh_id));\n\n    EXPECT_TRUE(server.MachMessageServerFunction(\n        &request.header, &reply.Head, &destroy_complex_request));\n    EXPECT_EQ(reply.RetCode, kReturnCode2);\n    EXPECT_TRUE(destroy_complex_request);\n  }\n}\n\n// CompositeMachMessageServer can’t deal with two handlers that want to handle\n// the same request ID.\nTEST(CompositeMachMessageServerDeathTest, DuplicateRequestID) {\n  constexpr mach_msg_id_t kRequestID = 400;\n\n  TestMachMessageHandler handlers[2];\n  handlers[0].AddRequestID(kRequestID);\n  handlers[1].AddRequestID(kRequestID);\n\n  CompositeMachMessageServer server;\n\n  server.AddHandler(&handlers[0]);\n  EXPECT_DEATH_CHECK(server.AddHandler(&handlers[1]), \"duplicate request ID\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exc_client_variants.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exc_client_variants.h\"\n\n#include <sys/types.h>\n\n#include <vector>\n\n#include \"base/notreached.h\"\n#include \"util/mach/exc.h\"\n#include \"util/mach/mach_exc.h\"\n\nnamespace crashpad {\n\nkern_return_t UniversalExceptionRaise(exception_behavior_t behavior,\n                                      exception_handler_t exception_port,\n                                      thread_t thread,\n                                      task_t task,\n                                      exception_type_t exception,\n                                      const mach_exception_data_type_t* code,\n                                      mach_msg_type_number_t code_count,\n                                      thread_state_flavor_t* flavor,\n                                      ConstThreadState old_state,\n                                      mach_msg_type_number_t old_state_count,\n                                      thread_state_t new_state,\n                                      mach_msg_type_number_t* new_state_count) {\n  // This function is similar to 10.9.4 xnu-2422.110.17/osfmk/kern/exception.c\n  // exception_deliver() as far as the delivery logic is concerned. Unlike\n  // exception_deliver(), this function does not get or set thread states for\n  // behavior values that require this, as that is left to the caller to do if\n  // needed.\n\n  std::vector<exception_data_type_t> small_code_vector;\n  exception_data_t small_code = nullptr;\n  if ((behavior & MACH_EXCEPTION_CODES) == 0 && code_count) {\n    small_code_vector.reserve(code_count);\n    for (size_t code_index = 0; code_index < code_count; ++code_index) {\n      small_code_vector.push_back(code[code_index]);\n    }\n    small_code = &small_code_vector[0];\n  }\n\n  // The *exception_raise*() family has bad declarations. Their code and\n  // old_state arguments aren’t pointers to const data, although they should be.\n  // The generated stubs in excUser.c and mach_excUser.c make it clear that the\n  // data is never modified, and these parameters could be declared with const\n  // appropriately. The uses of const_cast below are thus safe.\n\n  switch (behavior) {\n    case EXCEPTION_DEFAULT:\n      return exception_raise(\n          exception_port, thread, task, exception, small_code, code_count);\n\n    case EXCEPTION_STATE:\n      return exception_raise_state(exception_port,\n                                   exception,\n                                   small_code,\n                                   code_count,\n                                   flavor,\n                                   const_cast<thread_state_t>(old_state),\n                                   old_state_count,\n                                   new_state,\n                                   new_state_count);\n\n    case EXCEPTION_STATE_IDENTITY:\n      return exception_raise_state_identity(\n          exception_port,\n          thread,\n          task,\n          exception,\n          small_code,\n          code_count,\n          flavor,\n          const_cast<thread_state_t>(old_state),\n          old_state_count,\n          new_state,\n          new_state_count);\n\n    case EXCEPTION_DEFAULT | kMachExceptionCodes:\n      return mach_exception_raise(exception_port,\n                                  thread,\n                                  task,\n                                  exception,\n                                  const_cast<mach_exception_data_type_t*>(code),\n                                  code_count);\n\n    case EXCEPTION_STATE | kMachExceptionCodes:\n      return mach_exception_raise_state(\n          exception_port,\n          exception,\n          const_cast<mach_exception_data_type_t*>(code),\n          code_count,\n          flavor,\n          const_cast<thread_state_t>(old_state),\n          old_state_count,\n          new_state,\n          new_state_count);\n\n    case EXCEPTION_STATE_IDENTITY | kMachExceptionCodes:\n      return mach_exception_raise_state_identity(\n          exception_port,\n          thread,\n          task,\n          exception,\n          const_cast<mach_exception_data_type_t*>(code),\n          code_count,\n          flavor,\n          const_cast<thread_state_t>(old_state),\n          old_state_count,\n          new_state,\n          new_state_count);\n\n    default:\n      NOTREACHED();\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exc_client_variants.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_\n#define CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_\n\n#include <mach/mach.h>\n\n#include \"util/mach/mach_extensions.h\"\n\nnamespace crashpad {\n\n//! \\brief Calls the appropriate `*exception_raise*()` function for the\n//!     specified \\a behavior.\n//!\n//! The function called will be `exception_raise()` for `EXCEPTION_DEFAULT`,\n//! `exception_raise_state()` for `EXCEPTION_STATE`, or\n//! `exception_raise_state_identity()` for `EXCEPTION_STATE_IDENTITY`. If\n//! `MACH_EXCEPTION_CODES` is also set, the function called will instead be\n//! `mach_exception_raise()`, `mach_exception_raise_state()` or\n//! `mach_exception_raise_state_identity()`, respectively.\n//!\n//! This function does not fetch the existing thread state for \\a behavior\n//! values that require a thread state. The caller must provide the existing\n//! thread state in the \\a flavor, \\a old_state, and \\a old_state_count\n//! parameters for \\a behavior values that require a thread state. Thread states\n//! may be obtained by calling `thread_get_state()` if needed. Similarly, this\n//! function does not do anything with the new thread state returned for these\n//! \\a behavior values. Callers that wish to make use of the new thread state\n//! may do so by using the returned \\a flavor, \\a new_state, and \\a\n//! new_state_count values. Thread states may be set by calling\n//! `thread_set_state()` if needed.\n//!\n//! \\a thread and \\a task are only used when \\a behavior indicates that the\n//! exception message will carry identity information, when it has the value\n//! `EXCEPTION_DEFAULT` or `EXCEPTION_STATE_IDENTITY`, possibly with\n//! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused\n//! and may be set to `THREAD_NULL` and `TASK_NULL`, respectively.\n//!\n//! \\a flavor, \\a old_state, \\a old_state_count, \\a new_state, and \\a\n//! new_state_count are only used when \\a behavior indicates that the exception\n//! message will carry thread state information, when it has the value\n//! `EXCEPTION_STATE` or `EXCEPTION_STATE_IDENTITY`, possibly with\n//! `MACH_EXCEPTION_CODES` also set. In other cases, these parameters are unused\n//! and may be set to `0` (\\a old_state_count) or `nullptr` (the remaining\n//! parameters).\n//!\n//! Except as noted, the parameters and return value are equivalent to those of\n//! the `*exception_raise*()` family of functions.\n//!\n//! \\param[in] behavior The exception behavior, which dictates which function\n//!     will be called. It is an error to call this function with an invalid\n//!     value for \\a behavior.\n//! \\param[in] exception_port\n//! \\param[in] thread\n//! \\param[in] task\n//! \\param[in] exception\n//! \\param[in] code If \\a behavior indicates a behavior without\n//!     `MACH_EXCEPTION_CODES`, the elements of \\a code will be truncated in\n//!     order to be passed to the appropriate exception handler.\n//! \\param[in] code_count\n//! \\param[in,out] flavor\n//! \\param[in] old_state\n//! \\param[in] old_state_count\n//! \\param[out] new_state\n//! \\param[out] new_state_count\n//!\n//! \\return The return value of the function called.\nkern_return_t UniversalExceptionRaise(exception_behavior_t behavior,\n                                      exception_handler_t exception_port,\n                                      thread_t thread,\n                                      task_t task,\n                                      exception_type_t exception,\n                                      const mach_exception_data_type_t* code,\n                                      mach_msg_type_number_t code_count,\n                                      thread_state_flavor_t* flavor,\n                                      ConstThreadState old_state,\n                                      mach_msg_type_number_t old_state_count,\n                                      thread_state_t new_state,\n                                      mach_msg_type_number_t* new_state_count);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_EXC_CLIENT_VARIANTS_H_\n"
  },
  {
    "path": "util/mach/exc_client_variants_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exc_client_variants.h\"\n\n#include <mach/mach.h>\n#include <pthread.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"test/mac/mach_multiprocess.h\"\n#include \"util/mach/exc_server_variants.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass TestExcClientVariants : public MachMultiprocess,\n                              public UniversalMachExcServer::Interface {\n public:\n  TestExcClientVariants(exception_behavior_t behavior, bool all_fields)\n      : MachMultiprocess(),\n        UniversalMachExcServer::Interface(),\n        behavior_(behavior),\n        all_fields_(all_fields),\n        handled_(false) {\n    ++exception_;\n    ++exception_code_;\n    ++exception_subcode_;\n  }\n\n  TestExcClientVariants(const TestExcClientVariants&) = delete;\n  TestExcClientVariants& operator=(const TestExcClientVariants&) = delete;\n\n  // UniversalMachExcServer::Interface:\n\n  virtual kern_return_t CatchMachException(\n      exception_behavior_t behavior,\n      exception_handler_t exception_port,\n      thread_t thread,\n      task_t task,\n      exception_type_t exception,\n      const mach_exception_data_type_t* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer,\n      bool* destroy_complex_request) override {\n    *destroy_complex_request = true;\n\n    EXPECT_FALSE(handled_);\n    handled_ = true;\n\n    EXPECT_EQ(behavior, behavior_);\n    EXPECT_EQ(exception_port, LocalPort());\n\n    if (HasIdentity()) {\n      EXPECT_NE(thread, THREAD_NULL);\n      EXPECT_EQ(task, ChildTask());\n    } else {\n      EXPECT_EQ(thread, THREAD_NULL);\n      EXPECT_EQ(task, TASK_NULL);\n    }\n\n    mach_exception_code_t expect_code = exception_code_;\n    mach_exception_subcode_t expect_subcode = exception_subcode_;\n    if ((behavior & MACH_EXCEPTION_CODES) == 0) {\n      expect_code = implicit_cast<exception_data_type_t>(expect_code);\n      expect_subcode = implicit_cast<exception_data_type_t>(expect_subcode);\n    }\n\n    EXPECT_EQ(exception, exception_);\n    EXPECT_EQ(code_count, 2u);\n\n    // The code_count check above would ideally use ASSERT_EQ so that the next\n    // conditionals would not be necessary, but ASSERT_* requires a function\n    // returning type void, and the interface dictates otherwise here.\n    if (code_count >= 1) {\n      EXPECT_EQ(code[0], expect_code);\n    }\n    if (code_count >= 2) {\n      EXPECT_EQ(code[1], expect_subcode);\n    }\n\n    if (HasState()) {\n      EXPECT_EQ(*flavor, exception_ + 10);\n      EXPECT_EQ(old_state_count, MACHINE_THREAD_STATE_COUNT);\n      EXPECT_NE(old_state, nullptr);\n      EXPECT_EQ(*new_state_count,\n                implicit_cast<mach_msg_type_number_t>(THREAD_STATE_MAX));\n      EXPECT_NE(new_state, nullptr);\n\n      for (size_t index = 0; index < old_state_count; ++index) {\n        EXPECT_EQ(old_state[index], index);\n      }\n\n      // Use a flavor known to be different from the incoming flavor, for a test\n      // of the “out” side of the in-out flavor parameter.\n      *flavor = exception_ + 20;\n      *new_state_count = MACHINE_THREAD_STATE_COUNT;\n\n      // Send a new state back to the client.\n      for (size_t index = 0; index < *new_state_count; ++index) {\n        new_state[index] = MACHINE_THREAD_STATE_COUNT - index;\n      }\n    } else {\n      EXPECT_EQ(*flavor, THREAD_STATE_NONE);\n      EXPECT_EQ(old_state_count, 0u);\n      EXPECT_EQ(old_state, nullptr);\n      EXPECT_EQ(*new_state_count, 0u);\n      EXPECT_EQ(new_state, nullptr);\n    }\n\n    return KERN_SUCCESS;\n  }\n\n private:\n  // MachMultiprocess:\n\n  void MachMultiprocessParent() override {\n    UniversalMachExcServer universal_mach_exc_server(this);\n\n    kern_return_t kr =\n        MachMessageServer::Run(&universal_mach_exc_server,\n                               LocalPort(),\n                               MACH_MSG_OPTION_NONE,\n                               MachMessageServer::kOneShot,\n                               MachMessageServer::kReceiveLargeError,\n                               kMachMessageTimeoutWaitIndefinitely);\n    EXPECT_EQ(kr, KERN_SUCCESS)\n        << MachErrorMessage(kr, \"MachMessageServer::Run\");\n\n    EXPECT_TRUE(handled_);\n  }\n\n  void MachMultiprocessChild() override {\n    const exception_type_t exception = exception_;\n    const mach_exception_data_type_t code[] = {\n        exception_code_,\n        exception_subcode_\n    };\n\n    thread_t thread = THREAD_NULL;\n    task_t task = TASK_NULL;\n    if (all_fields_ || HasIdentity()) {\n      thread = MachThreadSelf();\n      task = mach_task_self();\n    }\n\n    thread_state_flavor_t flavor;\n    thread_state_flavor_t* flavor_p = nullptr;\n    natural_t old_state[MACHINE_THREAD_STATE_COUNT];\n    thread_state_t old_state_p = nullptr;\n    mach_msg_type_number_t old_state_count = 0;\n    natural_t new_state[THREAD_STATE_MAX];\n    thread_state_t new_state_p = nullptr;\n    mach_msg_type_number_t new_state_count;\n    mach_msg_type_number_t* new_state_count_p = nullptr;\n    if (all_fields_ || HasState()) {\n      // Pick a different flavor each time based on the value of exception_.\n      // These aren’t real flavors, it’s just for testing.\n      flavor = exception_ + 10;\n      flavor_p = &flavor;\n      for (size_t index = 0; index < std::size(old_state); ++index) {\n        old_state[index] = index;\n      }\n      old_state_p = reinterpret_cast<thread_state_t>(&old_state);\n      old_state_count = std::size(old_state);\n\n      // new_state and new_state_count are out parameters that the server should\n      // never see or use, so set them to bogus values. The call to the server\n      // should overwrite these.\n      memset(new_state, 0xa5, sizeof(new_state));\n      new_state_p = reinterpret_cast<thread_state_t>(&new_state);\n      new_state_count = 0x5a;\n      new_state_count_p = &new_state_count;\n    }\n\n    EXPECT_EQ(UniversalExceptionRaise(behavior_,\n                                      RemotePort(),\n                                      thread,\n                                      task,\n                                      exception,\n                                      code,\n                                      std::size(code),\n                                      flavor_p,\n                                      old_state_p,\n                                      old_state_count,\n                                      new_state_p,\n                                      new_state_count_p),\n              KERN_SUCCESS);\n\n    if (HasState()) {\n      // Verify the out parameters.\n\n      EXPECT_EQ(flavor, exception_ + 20);\n      EXPECT_EQ(new_state_count, MACHINE_THREAD_STATE_COUNT);\n\n      for (size_t index = 0; index < new_state_count; ++index) {\n        EXPECT_EQ(new_state[index], MACHINE_THREAD_STATE_COUNT - index);\n      }\n    }\n  }\n\n  bool HasIdentity() const {\n    return ExceptionBehaviorHasIdentity(behavior_);\n  }\n\n  bool HasState() const {\n    return ExceptionBehaviorHasState(behavior_);\n  }\n\n  // The behavior to test.\n  exception_behavior_t behavior_;\n\n  // If false, only fields required for the current value of behavior_ are set\n  // in a call to UniversalExceptionRaise(). The thread and task fields are only\n  // set for identity-carrying behaviors, and the flavor and state fields are\n  // only set for state-carrying behaviors. If true, all fields are set\n  // regardless of the behavior. Testing in both ways verifies that\n  // UniversalExceptionRaise() can tolerate the null arguments documented as\n  // usable when the behavior allows it, and that it ignores these arguments\n  // even when set when the behavior does not make use of them.\n  bool all_fields_;\n\n  // true if an exception message was handled.\n  bool handled_;\n\n  // These fields will increment for each instantiation of the test class.\n  static exception_type_t exception_;\n  static mach_exception_code_t exception_code_;\n  static mach_exception_subcode_t exception_subcode_;\n};\n\nexception_type_t TestExcClientVariants::exception_ = 0;\n\n// exception_code_ and exception_subcode_ are always large enough to require\n// 64 bits, so that when the 32-bit-only variants not using MACH_EXCEPITON_CODES\n// are tested, the code and subcode fields can be checked for proper truncation.\nmach_exception_code_t TestExcClientVariants::exception_code_ = 0x100000000;\nmach_exception_subcode_t TestExcClientVariants::exception_subcode_ =\n        0xffffffff00000000;\n\nTEST(ExcClientVariants, UniversalExceptionRaise) {\n  static constexpr exception_behavior_t kBehaviors[] = {\n      EXCEPTION_DEFAULT,\n      EXCEPTION_STATE,\n      EXCEPTION_STATE_IDENTITY,\n      kMachExceptionCodes | EXCEPTION_DEFAULT,\n      kMachExceptionCodes | EXCEPTION_STATE,\n      kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,\n  };\n\n  for (size_t index = 0; index < std::size(kBehaviors); ++index) {\n    exception_behavior_t behavior = kBehaviors[index];\n    SCOPED_TRACE(base::StringPrintf(\"index %zu, behavior %d\", index, behavior));\n\n    {\n      SCOPED_TRACE(\"all_fields = false\");\n\n      TestExcClientVariants test_exc_client_variants(behavior, false);\n      test_exc_client_variants.Run();\n    }\n\n    {\n      SCOPED_TRACE(\"all_fields = true\");\n\n      TestExcClientVariants test_exc_client_variants(behavior, true);\n      test_exc_client_variants.Run();\n    }\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exc_server_variants.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exc_server_variants.h\"\n\n#include <Availability.h>\n#include <string.h>\n\n#include <algorithm>\n#include <iterator>\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/mach/composite_mach_message_server.h\"\n#include \"util/mach/exc.h\"\n#include \"util/mach/excServer.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/mach_exc.h\"\n#include \"util/mach/mach_excServer.h\"\n#include \"util/mach/mach_message.h\"\n\n#if BUILDFLAG(IS_IOS)\n#include \"util/ios/raw_logging.h\"\n#else\n#include \"base/apple/mach_logging.h\"\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\n// Traits for ExcServer<> and SimplifiedExcServer<> adapting them for use with\n// the exc subsystem.\nstruct ExcTraits {\n  using ExceptionCode = exception_data_type_t;\n\n  using RequestUnion = __RequestUnion__exc_subsystem;\n  using ReplyUnion = __ReplyUnion__exc_subsystem;\n\n  using ExceptionRaiseRequest = __Request__exception_raise_t;\n  using ExceptionRaiseStateRequest = __Request__exception_raise_state_t;\n  using ExceptionRaiseStateIdentityRequest =\n      __Request__exception_raise_state_identity_t;\n\n  using ExceptionRaiseReply = __Reply__exception_raise_t;\n  using ExceptionRaiseStateReply = __Reply__exception_raise_state_t;\n  using ExceptionRaiseStateIdentityReply =\n      __Reply__exception_raise_state_identity_t;\n\n  // The MIG-generated __MIG_check__Request__*() functions are not declared as\n  // accepting const data, but they could have been because they in fact do not\n  // modify the data.\n\n  static kern_return_t MIGCheckRequestExceptionRaise(\n      const ExceptionRaiseRequest* in_request) {\n    return __MIG_check__Request__exception_raise_t(\n        const_cast<ExceptionRaiseRequest*>(in_request));\n  }\n\n  static kern_return_t MIGCheckRequestExceptionRaiseState(\n      const ExceptionRaiseStateRequest* in_request,\n      const ExceptionRaiseStateRequest** in_request_1) {\n    return __MIG_check__Request__exception_raise_state_t(\n        const_cast<ExceptionRaiseStateRequest*>(in_request),\n        const_cast<ExceptionRaiseStateRequest**>(in_request_1));\n  }\n\n  static kern_return_t MIGCheckRequestExceptionRaiseStateIdentity(\n      const ExceptionRaiseStateIdentityRequest* in_request,\n      const ExceptionRaiseStateIdentityRequest** in_request_1) {\n    return __MIG_check__Request__exception_raise_state_identity_t(\n        const_cast<ExceptionRaiseStateIdentityRequest*>(in_request),\n        const_cast<ExceptionRaiseStateIdentityRequest**>(in_request_1));\n  }\n\n  // There are no predefined constants for these.\n  static const mach_msg_id_t kMachMessageIDExceptionRaise = 2401;\n  static const mach_msg_id_t kMachMessageIDExceptionRaiseState = 2402;\n  static const mach_msg_id_t kMachMessageIDExceptionRaiseStateIdentity = 2403;\n  static const mach_msg_id_t\n      kMachMessageIDExceptionRaiseStateIdentityProtected = 2411;\n\n  static const exception_behavior_t kExceptionBehavior = 0;\n};\n\n// Traits for ExcServer<> and SimplifiedExcServer<> adapting them for use with\n// the mach_exc subsystem.\nstruct MachExcTraits {\n  using ExceptionCode = mach_exception_data_type_t;\n\n  using RequestUnion = __RequestUnion__mach_exc_subsystem;\n  using ReplyUnion = __ReplyUnion__mach_exc_subsystem;\n\n  using ExceptionRaiseRequest = __Request__mach_exception_raise_t;\n  using ExceptionRaiseStateRequest = __Request__mach_exception_raise_state_t;\n  using ExceptionRaiseStateIdentityRequest =\n      __Request__mach_exception_raise_state_identity_t;\n  using ExceptionRaiseStateIdentityProtectedRequest =\n      __Request__mach_exception_raise_state_identity_protected_t;\n\n  using ExceptionRaiseReply = __Reply__mach_exception_raise_t;\n  using ExceptionRaiseStateReply = __Reply__mach_exception_raise_state_t;\n  using ExceptionRaiseStateIdentityReply =\n      __Reply__mach_exception_raise_state_identity_t;\n  using ExceptionRaiseStateIdentityProtectedReply =\n      __Reply__mach_exception_raise_state_identity_protected_t;\n\n  // The MIG-generated __MIG_check__Request__*() functions are not declared as\n  // accepting const data, but they could have been because they in fact do not\n  // modify the data.\n\n  static kern_return_t MIGCheckRequestExceptionRaise(\n      const ExceptionRaiseRequest* in_request) {\n    return __MIG_check__Request__mach_exception_raise_t(\n        const_cast<ExceptionRaiseRequest*>(in_request));\n  }\n\n  static kern_return_t MIGCheckRequestExceptionRaiseState(\n      const ExceptionRaiseStateRequest* in_request,\n      const ExceptionRaiseStateRequest** in_request_1) {\n    return __MIG_check__Request__mach_exception_raise_state_t(\n        const_cast<ExceptionRaiseStateRequest*>(in_request),\n        const_cast<ExceptionRaiseStateRequest**>(in_request_1));\n  }\n\n  static kern_return_t MIGCheckRequestExceptionRaiseStateIdentity(\n      const ExceptionRaiseStateIdentityRequest* in_request,\n      const ExceptionRaiseStateIdentityRequest** in_request_1) {\n    return __MIG_check__Request__mach_exception_raise_state_identity_t(\n        const_cast<ExceptionRaiseStateIdentityRequest*>(in_request),\n        const_cast<ExceptionRaiseStateIdentityRequest**>(in_request_1));\n  }\n\n  static kern_return_t MIGCheckRequestExceptionRaiseStateIdentityProtected(\n      const ExceptionRaiseStateIdentityProtectedRequest* in_request,\n      const ExceptionRaiseStateIdentityProtectedRequest** in_request_1) {\n    return\n        __MIG_check__Request__mach_exception_raise_state_identity_protected_t(\n            const_cast<ExceptionRaiseStateIdentityProtectedRequest*>(\n                in_request),\n            const_cast<ExceptionRaiseStateIdentityProtectedRequest**>(\n                in_request_1));\n  }\n\n  // There are no predefined constants for these.\n  static const mach_msg_id_t kMachMessageIDExceptionRaise = 2405;\n  static const mach_msg_id_t kMachMessageIDExceptionRaiseState = 2406;\n  static const mach_msg_id_t kMachMessageIDExceptionRaiseStateIdentity = 2407;\n  static const mach_msg_id_t\n      kMachMessageIDExceptionRaiseStateIdentityProtected = 2410;\n\n  static const exception_behavior_t kExceptionBehavior = MACH_EXCEPTION_CODES;\n};\n\n//! \\brief A server interface for the `exc` or `mach_exc` Mach subsystems.\ntemplate <typename Traits>\nclass ExcServer : public MachMessageServer::Interface {\n public:\n  //! \\brief An interface that the different request messages that are a part of\n  //!     the `exc` or `mach_exc` Mach subsystems can be dispatched to.\n  class Interface {\n   public:\n    //! \\brief Handles exceptions raised by `exception_raise()` or\n    //!     `mach_exception_raise()`.\n    //!\n    //! This behaves equivalently to a `catch_exception_raise()` function used\n    //! with `exc_server()`, or a `catch_mach_exception_raise()` function used\n    //! with `mach_exc_server()`.\n    //!\n    //! \\param[in] trailer The trailer received with the request message.\n    //! \\param[out] destroy_request `true` if the request message is to be\n    //!     destroyed even when this method returns success. See\n    //!     MachMessageServer::Interface.\n    virtual kern_return_t CatchExceptionRaise(\n        exception_handler_t exception_port,\n        thread_t thread,\n        task_t task,\n        exception_type_t exception,\n        const typename Traits::ExceptionCode* code,\n        mach_msg_type_number_t code_count,\n        const mach_msg_trailer_t* trailer,\n        bool* destroy_request) = 0;\n\n    //! \\brief Handles exceptions raised by `exception_raise_state()` or\n    //!     `mach_exception_raise_state()`.\n    //!\n    //! This behaves equivalently to a `catch_exception_raise_state()` function\n    //! used with `exc_server()`, or a `catch_mach_exception_raise_state()`\n    //! function used with `mach_exc_server()`.\n    //!\n    //! There is no \\a destroy_request parameter because, unlike\n    //! CatchExceptionRaise() and CatchExceptionRaiseStateIdentity(), the\n    //! request message is not complex (it does not carry the \\a thread or \\a\n    //! task port rights) and thus there is nothing to destroy.\n    //!\n    //! \\param[in] trailer The trailer received with the request message.\n    virtual kern_return_t CatchExceptionRaiseState(\n        exception_handler_t exception_port,\n        exception_type_t exception,\n        const typename Traits::ExceptionCode* code,\n        mach_msg_type_number_t code_count,\n        thread_state_flavor_t* flavor,\n        ConstThreadState old_state,\n        mach_msg_type_number_t old_state_count,\n        thread_state_t new_state,\n        mach_msg_type_number_t* new_state_count,\n        const mach_msg_trailer_t* trailer) = 0;\n\n    //! \\brief Handles exceptions raised by `exception_raise_state_identity()`\n    //!     or `mach_exception_raise_state_identity()`.\n    //!\n    //! This behaves equivalently to a `catch_exception_raise_state_identity()`\n    //! function used with `exc_server()`, or a\n    //! `catch_mach_exception_raise_state_identity()` function used with\n    //! `mach_exc_server()`.\n    //!\n    //! \\param[in] trailer The trailer received with the request message.\n    //! \\param[out] destroy_request `true` if the request message is to be\n    //!     destroyed even when this method returns success. See\n    //!     MachMessageServer::Interface.\n    virtual kern_return_t CatchExceptionRaiseStateIdentity(\n        exception_handler_t exception_port,\n        thread_t thread,\n        task_t task,\n        exception_type_t exception,\n        const typename Traits::ExceptionCode* code,\n        mach_msg_type_number_t code_count,\n        thread_state_flavor_t* flavor,\n        ConstThreadState old_state,\n        mach_msg_type_number_t old_state_count,\n        thread_state_t new_state,\n        mach_msg_type_number_t* new_state_count,\n        const mach_msg_trailer_t* trailer,\n        bool* destroy_request) = 0;\n\n    //! \\brief Handles exceptions raised by\n    //!     `mach_exception_raise_state_identity_protected()`.\n    //!\n    //! This behaves equivalently to a\n    //! `catch_mach_exception_raise_state_identity_protected()` function used\n    //! with `mach_exc_server()`.\n    //!\n    //! \\param[in] trailer The trailer received with the request message.\n    //! \\param[out] destroy_request `true` if the request message is to be\n    //!     destroyed even when this method returns success. See\n    //!     MachMessageServer::Interface.\n    virtual kern_return_t CatchExceptionRaiseStateIdentityProtected(\n        exception_handler_t exception_port,\n        thread_t thread,\n        task_t task,\n        exception_type_t exception,\n        const typename Traits::ExceptionCode* code,\n        mach_msg_type_number_t code_count,\n        thread_state_flavor_t* flavor,\n        ConstThreadState old_state,\n        mach_msg_type_number_t old_state_count,\n        thread_state_t new_state,\n        mach_msg_type_number_t* new_state_count,\n        const mach_msg_trailer_t* trailer,\n        bool* destroy_request) = 0;\n\n   protected:\n    ~Interface() {}\n  };\n\n  //! \\brief Constructs an object of this class.\n  //!\n  //! \\param[in] interface The interface to dispatch requests to. Weak.\n  explicit ExcServer(Interface* interface)\n      : MachMessageServer::Interface(), interface_(interface) {}\n\n  ExcServer(const ExcServer&) = delete;\n  ExcServer& operator=(const ExcServer&) = delete;\n\n  // MachMessageServer::Interface:\n\n  bool MachMessageServerFunction(const mach_msg_header_t* in_header,\n                                 mach_msg_header_t* out_header,\n                                 bool* destroy_complex_request) override;\n\n  std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {\n    constexpr mach_msg_id_t request_ids[] = {\n        Traits::kMachMessageIDExceptionRaise,\n        Traits::kMachMessageIDExceptionRaiseState,\n        Traits::kMachMessageIDExceptionRaiseStateIdentity,\n        Traits::kMachMessageIDExceptionRaiseStateIdentityProtected,\n    };\n    return std::set<mach_msg_id_t>(&request_ids[0],\n                                   &request_ids[std::size(request_ids)]);\n  }\n\n  mach_msg_size_t MachMessageServerRequestSize() override {\n    return sizeof(typename Traits::RequestUnion);\n  }\n\n  mach_msg_size_t MachMessageServerReplySize() override {\n    return sizeof(typename Traits::ReplyUnion);\n  }\n\n private:\n  Interface* interface_;  // weak\n};\n\ntemplate <typename Traits>\nbool ExcServer<Traits>::MachMessageServerFunction(\n    const mach_msg_header_t* in_header,\n    mach_msg_header_t* out_header,\n    bool* destroy_complex_request) {\n  PrepareMIGReplyFromRequest(in_header, out_header);\n\n  const mach_msg_trailer_t* in_trailer =\n      MachMessageTrailerFromHeader(in_header);\n\n  switch (in_header->msgh_id) {\n    case Traits::kMachMessageIDExceptionRaise: {\n      // exception_raise(), catch_exception_raise(), mach_exception_raise(),\n      // catch_mach_exception_raise().\n      using Request = typename Traits::ExceptionRaiseRequest;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n      kern_return_t kr = Traits::MIGCheckRequestExceptionRaise(in_request);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = typename Traits::ExceptionRaiseReply;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->RetCode =\n          interface_->CatchExceptionRaise(in_header->msgh_local_port,\n                                          in_request->thread.name,\n                                          in_request->task.name,\n                                          in_request->exception,\n                                          in_request->code,\n                                          in_request->codeCnt,\n                                          in_trailer,\n                                          destroy_complex_request);\n      if (out_reply->RetCode != KERN_SUCCESS) {\n        return true;\n      }\n\n      out_header->msgh_size = sizeof(*out_reply);\n      return true;\n    }\n\n    case Traits::kMachMessageIDExceptionRaiseState: {\n      // exception_raise_state(), catch_exception_raise_state(),\n      // mach_exception_raise_state(), catch_mach_exception_raise_state().\n      using Request = typename Traits::ExceptionRaiseStateRequest;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n\n      // in_request_1 is used for the portion of the request after the codes,\n      // which in theory can be variable-length. The check function will set it.\n      const Request* in_request_1;\n      kern_return_t kr =\n          Traits::MIGCheckRequestExceptionRaiseState(in_request, &in_request_1);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = typename Traits::ExceptionRaiseStateReply;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->flavor = in_request_1->flavor;\n      out_reply->new_stateCnt = std::size(out_reply->new_state);\n      out_reply->RetCode =\n          interface_->CatchExceptionRaiseState(in_header->msgh_local_port,\n                                               in_request->exception,\n                                               in_request->code,\n                                               in_request->codeCnt,\n                                               &out_reply->flavor,\n                                               in_request_1->old_state,\n                                               in_request_1->old_stateCnt,\n                                               out_reply->new_state,\n                                               &out_reply->new_stateCnt,\n                                               in_trailer);\n      if (out_reply->RetCode != KERN_SUCCESS) {\n        return true;\n      }\n\n      out_header->msgh_size =\n          sizeof(*out_reply) - sizeof(out_reply->new_state) +\n          sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;\n      return true;\n    }\n\n    case Traits::kMachMessageIDExceptionRaiseStateIdentity: {\n      // exception_raise_state_identity(),\n      // catch_exception_raise_state_identity(),\n      // mach_exception_raise_state_identity(),\n      // catch_mach_exception_raise_state_identity().\n      using Request = typename Traits::ExceptionRaiseStateIdentityRequest;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n\n      // in_request_1 is used for the portion of the request after the codes,\n      // which in theory can be variable-length. The check function will set it.\n      const Request* in_request_1;\n      kern_return_t kr = Traits::MIGCheckRequestExceptionRaiseStateIdentity(\n          in_request, &in_request_1);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = typename Traits::ExceptionRaiseStateIdentityReply;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->flavor = in_request_1->flavor;\n      out_reply->new_stateCnt = std::size(out_reply->new_state);\n      out_reply->RetCode = interface_->CatchExceptionRaiseStateIdentity(\n          in_header->msgh_local_port,\n          in_request->thread.name,\n          in_request->task.name,\n          in_request->exception,\n          in_request->code,\n          in_request->codeCnt,\n          &out_reply->flavor,\n          in_request_1->old_state,\n          in_request_1->old_stateCnt,\n          out_reply->new_state,\n          &out_reply->new_stateCnt,\n          in_trailer,\n          destroy_complex_request);\n      if (out_reply->RetCode != KERN_SUCCESS) {\n        return true;\n      }\n\n      out_header->msgh_size =\n          sizeof(*out_reply) - sizeof(out_reply->new_state) +\n          sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;\n      return true;\n    }\n\n    case MachExcTraits::kMachMessageIDExceptionRaiseStateIdentityProtected: {\n      // mach_exception_raise_state_identity_protected(),\n      // catch_mach_exception_raise_state_identity_protected().\n      using Request =\n          typename MachExcTraits::ExceptionRaiseStateIdentityProtectedRequest;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n\n      // in_request_1 is used for the portion of the request after the codes,\n      // which in theory can be variable-length. The check function will set it.\n      const Request* in_request_1;\n      kern_return_t kr =\n          MachExcTraits::MIGCheckRequestExceptionRaiseStateIdentityProtected(\n              in_request, &in_request_1);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      task_t task_port = TASK_NULL;\n      if (in_request->task_id_token_t.name != MACH_PORT_NULL) {\n        kr = task_identity_token_get_task_port(\n            in_request->task_id_token_t.name, TASK_FLAVOR_CONTROL, &task_port);\n        if (kr != MACH_MSG_SUCCESS) {\n          // If this fails, pass TASK_NULL to\n          // CatchExceptionRaiseStateIdentityProtected\n#if BUILDFLAG(IS_IOS)\n          CRASHPAD_RAW_LOG_ERROR(kr, \"task_identity_token_get_task_port\");\n#else\n          MACH_LOG(WARNING, kr) << \"task_identity_token_get_task_port\";\n#endif\n        }\n      }\n\n      // Get the thread_t matching in_request->thread_id.\n      thread_t thread_port = THREAD_NULL;\n      if (task_port != TASK_NULL) {\n        thread_act_array_t threads;\n        mach_msg_type_number_t thread_count;\n        kr = task_threads(task_port, &threads, &thread_count);\n        if (kr == KERN_SUCCESS) {\n          for (mach_msg_type_number_t thread_index = 0;\n               thread_index < thread_count;\n               ++thread_index) {\n            if (thread_port != THREAD_NULL) {\n              mach_port_deallocate(mach_task_self(), threads[thread_index]);\n              continue;\n            }\n            thread_identifier_info_data_t thread_id_info;\n            mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;\n            kr = thread_info(threads[thread_index],\n                             THREAD_IDENTIFIER_INFO,\n                             (thread_info_t)&thread_id_info,\n                             &count);\n            if (kr != KERN_SUCCESS) {\n              mach_port_deallocate(mach_task_self(), threads[thread_index]);\n              continue;\n            }\n            if (thread_id_info.thread_id == in_request->thread_id) {\n              thread_port = threads[thread_index];\n            } else {\n              mach_port_deallocate(mach_task_self(), threads[thread_index]);\n            }\n          }\n          vm_deallocate(mach_task_self(),\n                        (vm_address_t)threads,\n                        thread_count * sizeof(thread_t));\n        }\n      }\n\n      using Reply = MachExcTraits::ExceptionRaiseStateIdentityProtectedReply;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->flavor = in_request_1->flavor;\n      out_reply->new_stateCnt = std::size(out_reply->new_state);\n      out_reply->RetCode =\n          interface_->CatchExceptionRaiseStateIdentityProtected(\n              in_header->msgh_local_port,\n              thread_port,\n              task_port,\n              in_request->exception,\n              (const typename Traits::ExceptionCode*)in_request->code,\n              in_request->codeCnt,\n              &out_reply->flavor,\n              in_request_1->old_state,\n              in_request_1->old_stateCnt,\n              out_reply->new_state,\n              &out_reply->new_stateCnt,\n              in_trailer,\n              destroy_complex_request);\n\n      if (task_port != TASK_NULL) {\n        mach_port_deallocate(mach_task_self(), task_port);\n      }\n\n      if (thread_port != THREAD_NULL) {\n        mach_port_deallocate(mach_task_self(), thread_port);\n      }\n\n      if (out_reply->RetCode != KERN_SUCCESS) {\n        return true;\n      }\n\n      out_header->msgh_size =\n          sizeof(*out_reply) - sizeof(out_reply->new_state) +\n          sizeof(out_reply->new_state[0]) * out_reply->new_stateCnt;\n      return true;\n    }\n\n    default: {\n      SetMIGReplyError(out_header, MIG_BAD_ID);\n      return false;\n    }\n  }\n}\n\n//! \\brief A server interface for the `exc` or `mach_exc` Mach subsystems,\n//!     simplified to have only a single interface method needing\n//!     implementation.\ntemplate <typename Traits>\nclass SimplifiedExcServer final : public ExcServer<Traits>,\n                                  public ExcServer<Traits>::Interface {\n public:\n  //! \\brief An interface that the different request messages that are a part of\n  //!     the `exc` or `mach_exc` Mach subsystems can be dispatched to.\n  class Interface {\n   public:\n    //! \\brief Handles exceptions raised by `exception_raise()`,\n    //!     `exception_raise_state()`, and `exception_raise_state_identity()`;\n    //!     or `mach_exception_raise()`, `mach_exception_raise_state()`, and\n    //!     `mach_exception_raise_state_identity()`.\n    //!\n    //! For convenience in implementation, these different “behaviors” of\n    //! exception messages are all mapped to a single interface method. The\n    //! exception’s original “behavior” is specified in the \\a behavior\n    //! parameter. Only parameters that were supplied in the request message\n    //! are populated, other parameters are set to reasonable default values.\n    //!\n    //! The meanings of most parameters are identical to that of\n    //! ExcServer<>::Interface::CatchExceptionRaiseStateIdentity().\n    //!\n    //! \\param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`,\n    //!     `EXCEPTION_STATE_IDENTITY`, or\n    //!     `EXCEPTION_STATE_IDENTITY_PROTECTED` identifying which exception\n    //!     request message was processed and thus which other parameters are\n    //!     valid. When used with the `mach_exc` subsystem,\n    //!     `MACH_EXCEPTION_CODES` will be ORed in to this parameter.\n    virtual kern_return_t CatchException(\n        exception_behavior_t behavior,\n        exception_handler_t exception_port,\n        thread_t thread,\n        task_t task,\n        exception_type_t exception,\n        const typename Traits::ExceptionCode* code,\n        mach_msg_type_number_t code_count,\n        thread_state_flavor_t* flavor,\n        ConstThreadState old_state,\n        mach_msg_type_number_t old_state_count,\n        thread_state_t new_state,\n        mach_msg_type_number_t* new_state_count,\n        const mach_msg_trailer_t* trailer,\n        bool* destroy_complex_request) = 0;\n\n   protected:\n    ~Interface() {}\n  };\n\n  //! \\brief Constructs an object of this class.\n  //!\n  //! \\param[in] interface The interface to dispatch requests to. Weak.\n  explicit SimplifiedExcServer(Interface* interface)\n      : ExcServer<Traits>(this),\n        ExcServer<Traits>::Interface(),\n        interface_(interface) {}\n\n  SimplifiedExcServer(const SimplifiedExcServer&) = delete;\n  SimplifiedExcServer& operator=(const SimplifiedExcServer&) = delete;\n\n  // ExcServer::Interface:\n\n  kern_return_t CatchExceptionRaise(exception_handler_t exception_port,\n                                    thread_t thread,\n                                    task_t task,\n                                    exception_type_t exception,\n                                    const typename Traits::ExceptionCode* code,\n                                    mach_msg_type_number_t code_count,\n                                    const mach_msg_trailer_t* trailer,\n                                    bool* destroy_request) override {\n    thread_state_flavor_t flavor = THREAD_STATE_NONE;\n    mach_msg_type_number_t new_state_count = 0;\n    return interface_->CatchException(\n        Traits::kExceptionBehavior | EXCEPTION_DEFAULT,\n        exception_port,\n        thread,\n        task,\n        exception,\n        code_count ? code : nullptr,\n        code_count,\n        &flavor,\n        nullptr,\n        0,\n        nullptr,\n        &new_state_count,\n        trailer,\n        destroy_request);\n  }\n\n  kern_return_t CatchExceptionRaiseState(\n      exception_handler_t exception_port,\n      exception_type_t exception,\n      const typename Traits::ExceptionCode* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer) override {\n    bool destroy_complex_request = false;\n    return interface_->CatchException(\n        Traits::kExceptionBehavior | EXCEPTION_STATE,\n        exception_port,\n        THREAD_NULL,\n        TASK_NULL,\n        exception,\n        code_count ? code : nullptr,\n        code_count,\n        flavor,\n        old_state_count ? old_state : nullptr,\n        old_state_count,\n        new_state_count ? new_state : nullptr,\n        new_state_count,\n        trailer,\n        &destroy_complex_request);\n  }\n\n  kern_return_t CatchExceptionRaiseStateIdentity(\n      exception_handler_t exception_port,\n      thread_t thread,\n      task_t task,\n      exception_type_t exception,\n      const typename Traits::ExceptionCode* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer,\n      bool* destroy_request) override {\n    return interface_->CatchException(\n        Traits::kExceptionBehavior | EXCEPTION_STATE_IDENTITY,\n        exception_port,\n        thread,\n        task,\n        exception,\n        code_count ? code : nullptr,\n        code_count,\n        flavor,\n        old_state_count ? old_state : nullptr,\n        old_state_count,\n        new_state_count ? new_state : nullptr,\n        new_state_count,\n        trailer,\n        destroy_request);\n  }\n\n  kern_return_t CatchExceptionRaiseStateIdentityProtected(\n      exception_handler_t exception_port,\n      thread_t thread,\n      task_t task,\n      exception_type_t exception,\n      const typename Traits::ExceptionCode* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer,\n      bool* destroy_request) override {\n    return interface_->CatchException(\n        Traits::kExceptionBehavior | EXCEPTION_STATE_IDENTITY_PROTECTED,\n        exception_port,\n        thread,\n        task,\n        exception,\n        code_count ? code : nullptr,\n        code_count,\n        flavor,\n        old_state_count ? old_state : nullptr,\n        old_state_count,\n        new_state_count ? new_state : nullptr,\n        new_state_count,\n        trailer,\n        destroy_request);\n  }\n\n private:\n  Interface* interface_;  // weak\n};\n\n}  // namespace\n\nnamespace internal {\n\nclass UniversalMachExcServerImpl final\n    : public CompositeMachMessageServer,\n      public SimplifiedExcServer<ExcTraits>::Interface,\n      public SimplifiedExcServer<MachExcTraits>::Interface {\n public:\n  explicit UniversalMachExcServerImpl(\n      UniversalMachExcServer::Interface* interface)\n      : CompositeMachMessageServer(),\n        SimplifiedExcServer<ExcTraits>::Interface(),\n        SimplifiedExcServer<MachExcTraits>::Interface(),\n        exc_server_(this),\n        mach_exc_server_(this),\n        interface_(interface) {\n    AddHandler(&exc_server_);\n    AddHandler(&mach_exc_server_);\n  }\n\n  UniversalMachExcServerImpl(const UniversalMachExcServerImpl&) = delete;\n  UniversalMachExcServerImpl& operator=(const UniversalMachExcServerImpl&) =\n      delete;\n\n  ~UniversalMachExcServerImpl() {}\n\n  // SimplifiedExcServer<ExcTraits>::Interface:\n  kern_return_t CatchException(exception_behavior_t behavior,\n                               exception_handler_t exception_port,\n                               thread_t thread,\n                               task_t task,\n                               exception_type_t exception,\n                               const exception_data_type_t* code,\n                               mach_msg_type_number_t code_count,\n                               thread_state_flavor_t* flavor,\n                               ConstThreadState old_state,\n                               mach_msg_type_number_t old_state_count,\n                               thread_state_t new_state,\n                               mach_msg_type_number_t* new_state_count,\n                               const mach_msg_trailer_t* trailer,\n                               bool* destroy_complex_request) {\n    std::vector<mach_exception_data_type_t> mach_codes;\n    mach_codes.reserve(code_count);\n    for (size_t index = 0; index < code_count; ++index) {\n      mach_codes.push_back(code[index]);\n    }\n\n    return interface_->CatchMachException(behavior,\n                                          exception_port,\n                                          thread,\n                                          task,\n                                          exception,\n                                          code_count ? &mach_codes[0] : nullptr,\n                                          code_count,\n                                          flavor,\n                                          old_state_count ? old_state : nullptr,\n                                          old_state_count,\n                                          new_state_count ? new_state : nullptr,\n                                          new_state_count,\n                                          trailer,\n                                          destroy_complex_request);\n  }\n\n  // SimplifiedExcServer<MachExcTraits>::Interface:\n  kern_return_t CatchException(exception_behavior_t behavior,\n                               exception_handler_t exception_port,\n                               thread_t thread,\n                               task_t task,\n                               exception_type_t exception,\n                               const mach_exception_data_type_t* code,\n                               mach_msg_type_number_t code_count,\n                               thread_state_flavor_t* flavor,\n                               ConstThreadState old_state,\n                               mach_msg_type_number_t old_state_count,\n                               thread_state_t new_state,\n                               mach_msg_type_number_t* new_state_count,\n                               const mach_msg_trailer_t* trailer,\n                               bool* destroy_complex_request) {\n    return interface_->CatchMachException(behavior,\n                                          exception_port,\n                                          thread,\n                                          task,\n                                          exception,\n                                          code_count ? code : nullptr,\n                                          code_count,\n                                          flavor,\n                                          old_state_count ? old_state : nullptr,\n                                          old_state_count,\n                                          new_state_count ? new_state : nullptr,\n                                          new_state_count,\n                                          trailer,\n                                          destroy_complex_request);\n  }\n\n private:\n  SimplifiedExcServer<ExcTraits> exc_server_;\n  SimplifiedExcServer<MachExcTraits> mach_exc_server_;\n  UniversalMachExcServer::Interface* interface_;  // weak\n};\n\n}  // namespace internal\n\nUniversalMachExcServer::UniversalMachExcServer(\n    UniversalMachExcServer::Interface* interface)\n    : MachMessageServer::Interface(),\n      impl_(new internal::UniversalMachExcServerImpl(interface)) {\n}\n\nUniversalMachExcServer::~UniversalMachExcServer() {\n}\n\nbool UniversalMachExcServer::MachMessageServerFunction(\n    const mach_msg_header_t* in_header,\n    mach_msg_header_t* out_header,\n    bool* destroy_complex_request) {\n  return impl_->MachMessageServerFunction(\n      in_header, out_header, destroy_complex_request);\n}\n\nstd::set<mach_msg_id_t> UniversalMachExcServer::MachMessageServerRequestIDs() {\n  return impl_->MachMessageServerRequestIDs();\n}\n\nmach_msg_size_t UniversalMachExcServer::MachMessageServerRequestSize() {\n  return impl_->MachMessageServerRequestSize();\n}\n\nmach_msg_size_t UniversalMachExcServer::MachMessageServerReplySize() {\n  return impl_->MachMessageServerReplySize();\n}\n\nkern_return_t ExcServerSuccessfulReturnValue(exception_type_t exception,\n                                             exception_behavior_t behavior,\n                                             bool set_thread_state) {\n  if (exception == EXC_CRASH\n#if BUILDFLAG(IS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_11\n      && MacOSVersionNumber() >= 10'11'00\n#endif\n  ) {\n    return KERN_SUCCESS;\n  }\n\n  if (!set_thread_state && ExceptionBehaviorHasState(behavior)) {\n    return MACH_RCV_PORT_DIED;\n  }\n\n  return KERN_SUCCESS;\n}\n\nvoid ExcServerCopyState(exception_behavior_t behavior,\n                        ConstThreadState old_state,\n                        mach_msg_type_number_t old_state_count,\n                        thread_state_t new_state,\n                        mach_msg_type_number_t* new_state_count) {\n  if (ExceptionBehaviorHasState(behavior)) {\n    *new_state_count = std::min(old_state_count, *new_state_count);\n    memcpy(new_state, old_state, *new_state_count * sizeof(old_state[0]));\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exc_server_variants.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_\n#define CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_\n\n#include <mach/mach.h>\n\n#include <memory>\n#include <set>\n\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message_server.h\"\n\nnamespace crashpad {\n\nnamespace internal {\nclass UniversalMachExcServerImpl;\n}  // namespace internal\n\n//! \\brief A server interface for the `exc` and `mach_exc` Mach subsystems,\n//!     unified to handle exceptions delivered to either subsystem, and\n//!     simplified to have only a single interface method needing\n//!     implementation.\n//!\n//! The `<mach/exc.defs>` and `<mach/mach_exc.defs>` interfaces are identical,\n//! except that the latter allows for 64-bit exception codes, and is requested\n//! by setting the MACH_EXCEPTION_CODES behavior bit associated with an\n//! exception port.\n//!\n//! UniversalMachExcServer operates by translating messages received in the\n//! `exc` subsystem to a variant that is compatible with the `mach_exc`\n//! subsystem. This involves changing the format of \\a code, the exception code\n//! field, from `exception_data_type_t` to `mach_exception_data_type_t`.\nclass UniversalMachExcServer final : public MachMessageServer::Interface {\n public:\n  //! \\brief An interface that the different request messages that are a part of\n  //!     the `exc` and `mach_exc` Mach subsystems can be dispatched to.\n  class Interface {\n   public:\n    //! \\brief Handles exceptions raised by `exception_raise()`,\n    //!     `exception_raise_state()`, `exception_raise_state_identity()`,\n    //!     `mach_exception_raise()`, `mach_exception_raise_state()`, and\n    //!     `mach_exception_raise_state_identity()`.\n    //!\n    //! For convenience in implementation, these different “behaviors” of\n    //! exception messages are all mapped to a single interface method. The\n    //! exception’s original “behavior” is specified in the \\a behavior\n    //! parameter. Only parameters that were supplied in the request message\n    //! are populated, other parameters are set to reasonable default values.\n    //!\n    //! This behaves equivalently to a `catch_exception_raise_state_identity()`\n    //! function used with `exc_server()`, or a\n    //! `catch_mach_exception_raise_state_identity()` function used with\n    //! `mach_exc_server()`. Except as noted, the parameters and return value\n    //! are equivalent to those of these other functions.\n    //!\n    //! \\param[in] behavior `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`,\n    //!     or `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES`\n    //!     ORed in. This identifies which exception request message was\n    //!     processed and thus which other parameters are valid.\n    //! \\param[in] exception_port\n    //! \\param[in] thread\n    //! \\param[in] task\n    //! \\param[in] exception\n    //! \\param[in] code\n    //! \\param[in] code_count\n    //! \\param[in,out] flavor\n    //! \\param[in] old_state\n    //! \\param[in] old_state_count\n    //! \\param[out] new_state\n    //! \\param[out] new_state_count\n    //! \\param[in] trailer The trailer received with the request message.\n    //! \\param[out] destroy_complex_request `true` if the request message is to\n    //!     be destroyed even when this method returns success. See\n    //!     MachMessageServer::Interface.\n    //!\n    //! \\return A code indicating whether the exception was handled. See\n    //!     ExcServerSuccessfulReturnValue() for success codes. On failure,\n    //!     a code such as `KERN_FAILURE`.\n    virtual kern_return_t CatchMachException(\n        exception_behavior_t behavior,\n        exception_handler_t exception_port,\n        thread_t thread,\n        task_t task,\n        exception_type_t exception,\n        const mach_exception_data_type_t* code,\n        mach_msg_type_number_t code_count,\n        thread_state_flavor_t* flavor,\n        ConstThreadState old_state,\n        mach_msg_type_number_t old_state_count,\n        thread_state_t new_state,\n        mach_msg_type_number_t* new_state_count,\n        const mach_msg_trailer_t* trailer,\n        bool* destroy_complex_request) = 0;\n\n   protected:\n    ~Interface() {}\n  };\n\n  //! \\brief Constructs an object of this class.\n  //!\n  //! \\param[in] interface The interface to dispatch requests to. Weak.\n  explicit UniversalMachExcServer(Interface* interface);\n\n  UniversalMachExcServer(const UniversalMachExcServer&) = delete;\n  UniversalMachExcServer& operator=(const UniversalMachExcServer&) = delete;\n\n  ~UniversalMachExcServer();\n\n  // MachMessageServer::Interface:\n  bool MachMessageServerFunction(const mach_msg_header_t* in_header,\n                                 mach_msg_header_t* out_header,\n                                 bool* destroy_complex_request) override;\n  std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;\n  mach_msg_size_t MachMessageServerRequestSize() override;\n  mach_msg_size_t MachMessageServerReplySize() override;\n\n private:\n  std::unique_ptr<internal::UniversalMachExcServerImpl> impl_;\n};\n\n//! \\brief Computes an approriate successful return value for an exception\n//!     handler function.\n//!\n//! For exception handlers that respond to state-carrying behaviors, when the\n//! handler is called by the kernel (as it is normally), the kernel will attempt\n//! to set a new thread state when the exception handler returns successfully.\n//! Other code that mimics the kernel’s exception-delivery semantics may\n//! implement the same or similar behavior. In some situations, it is\n//! undesirable to set a new thread state. If the exception handler were to\n//! return unsuccessfully, however, the kernel would continue searching for an\n//! exception handler at a wider (task or host) scope. This may also be\n//! undesirable.\n//!\n//! If such exception handlers return `MACH_RCV_PORT_DIED`, the kernel will not\n//! set a new thread state and will also not search for another exception\n//! handler. See 10.9.4 `xnu-2422.110.17/osfmk/kern/exception.c`.\n//! `exception_deliver()` will only set a new thread state if the handler’s\n//! return code was `MACH_MSG_SUCCESS` (a synonym for `KERN_SUCCESS`), and\n//! subsequently, `exception_triage()` will not search for a new handler if the\n//! handler’s return code was `KERN_SUCCESS` or `MACH_RCV_PORT_DIED`.\n//!\n//! This function allows exception handlers to compute an appropriate return\n//! code to influence their caller (the kernel) in the desired way with respect\n//! to setting a new thread state while suppressing the caller’s subsequent\n//! search for other exception handlers. An exception handler should return the\n//! value returned by this function.\n//!\n//! This function is useful even for `EXC_CRASH` handlers, where returning\n//! `KERN_SUCCESS` and allowing the kernel to set a new thread state has been\n//! observed to cause a perceptible and unnecessary waste of time. The victim\n//! task in an `EXC_CRASH` handler is already being terminated and is no longer\n//! schedulable, so there is no point in setting the states of any of its\n//! threads.\n//!\n//! On OS X 10.11, the `MACH_RCV_PORT_DIED` mechanism cannot be used with an\n//! `EXC_CRASH` handler without triggering an undesirable `EXC_CORPSE_NOTIFY`\n//! exception. In that case, `KERN_SUCCESS` is always returned. Because this\n//! function may return `KERN_SUCCESS` for a state-carrying exception, it is\n//! important to ensure that the state returned by a state-carrying exception\n//! handler is valid, because it will be passed to `thread_set_status()`.\n//! ExcServerCopyState() may be used to achieve this.\n//!\n//! \\param[in] exception The exception type passed to the exception handler.\n//!     This may be taken directly from the \\a exception parameter of\n//!     internal::SimplifiedExcServer::Interface::CatchException(), for example.\n//! \\param[in] behavior The behavior of the exception handler as invoked. This\n//!     may be taken directly from the \\a behavior parameter of\n//!     internal::SimplifiedExcServer::Interface::CatchException(), for example.\n//! \\param[in] set_thread_state `true` if the handler would like its caller to\n//!     set the new thread state using the \\a flavor, \\a new_state, and \\a\n//!     new_state_count out parameters. This can only happen when \\a behavior is\n//!     a state-carrying behavior.\n//!\n//! \\return `KERN_SUCCESS` or `MACH_RCV_PORT_DIED`. `KERN_SUCCESS` is used when\n//!     \\a behavior is not a state-carrying behavior, or when it is a\n//!     state-carrying behavior and \\a set_thread_state is `true`, or for\n//!     `EXC_CRASH` exceptions on OS X 10.11 and later. Otherwise,\n//!     `MACH_RCV_PORT_DIED` is used.\nkern_return_t ExcServerSuccessfulReturnValue(exception_type_t exception,\n                                             exception_behavior_t behavior,\n                                             bool set_thread_state);\n\n//! \\brief Copies the old state to the new state for state-carrying exceptions.\n//!\n//! When the kernel sends a state-carrying exception request and the response is\n//! successful (`MACH_MSG_SUCCESS`, a synonym for `KERN_SUCCESS`), it will set\n//! a new thread state based on \\a new_state and \\a new_state_count. To ease\n//! initialization of the new state, this function copies \\a old_state and\n//! \\a old_state_count. This is only done if \\a behavior indicates a\n//! state-carrying exception.\n//!\n//! \\param[in] behavior The behavior of the exception handler as invoked. This\n//!     may be taken directly from the \\a behavior parameter of\n//!     internal::SimplifiedExcServer::Interface::CatchException(), for example.\n//! \\param[in] old_state The original state value. This may be taken directly\n//!     from the \\a old_state parameter of\n//!     internal::SimplifiedExcServer::Interface::CatchException(), for example.\n//! \\param[in] old_state_count The number of significant `natural_t` words in \\a\n//!     old_state. This may be taken directly from the \\a old_state_count\n//!     parameter of internal::SimplifiedExcServer::Interface::CatchException(),\n//!     for example.\n//! \\param[out] new_state The state value to be set. This may be taken directly\n//!     from the \\a new_state parameter of\n//!     internal::SimplifiedExcServer::Interface::CatchException(), for example.\n//!     This parameter is untouched if \\a behavior is not state-carrying.\n//! \\param[in,out] new_state_count On entry, the number of `natural_t` words\n//!     available to be written to in \\a new_state. On return, the number of\n//!     significant `natural_t` words in \\a new_state. This may be taken\n//!     directly from the \\a new_state_count parameter of\n//!     internal::SimplifiedExcServer::Interface::CatchException(), for example.\n//!     This parameter is untouched if \\a behavior is not state-carrying. If \\a\n//!     \\a behavior is state-carrying, this parameter should be at least as\n//!     large as \\a old_state_count.\nvoid ExcServerCopyState(exception_behavior_t behavior,\n                        ConstThreadState old_state,\n                        mach_msg_type_number_t old_state_count,\n                        thread_state_t new_state,\n                        mach_msg_type_number_t* new_state_count);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_EXC_SERVER_VARIANTS_H_\n"
  },
  {
    "path": "util/mach/exc_server_variants_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exc_server_variants.h\"\n\n#include <mach/mach.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/exception_types.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/misc/implicit_cast.h\"\n\n#if BUILDFLAG(IS_MAC)\n#include \"test/mac/mach_multiprocess.h\"\n#endif  // BUILDFLAG(IS_MAC)\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing testing::DefaultValue;\nusing testing::Eq;\nusing testing::Pointee;\nusing testing::Return;\n\n// Fake Mach ports. These aren’t used as ports in these tests, they’re just used\n// as cookies to make sure that the correct values get passed to the correct\n// places.\nconstexpr mach_port_t kClientRemotePort = 0x01010101;\nconstexpr mach_port_t kServerLocalPort = 0x02020202;\nconstexpr thread_t kExceptionThreadPort = 0x03030303;\nconstexpr task_t kExceptionTaskPort = 0x04040404;\n\n// Other fake exception values.\nconstexpr exception_type_t kExceptionType = EXC_BAD_ACCESS;\n\n// Test using an exception code with the high bit set to ensure that it gets\n// promoted to the wider mach_exception_data_type_t type as a signed quantity.\nconstexpr exception_data_type_t kTestExceptonCodes[] = {\n    KERN_PROTECTION_FAILURE,\n    implicit_cast<exception_data_type_t>(0xfedcba98),\n};\n\nconstexpr mach_exception_data_type_t kTestMachExceptionCodes[] = {\n    KERN_PROTECTION_FAILURE,\n    implicit_cast<mach_exception_data_type_t>(0xfedcba9876543210),\n};\n\nconstexpr thread_state_flavor_t kThreadStateFlavor = MACHINE_THREAD_STATE;\nconstexpr mach_msg_type_number_t kThreadStateFlavorCount =\n    MACHINE_THREAD_STATE_COUNT;\n\nvoid InitializeMachMsgPortDescriptor(mach_msg_port_descriptor_t* descriptor,\n                                     mach_port_t port) {\n  descriptor->name = port;\n  descriptor->disposition = MACH_MSG_TYPE_PORT_SEND;\n  descriptor->type = MACH_MSG_PORT_DESCRIPTOR;\n}\n\n// The definitions of the request and reply structures from mach_exc.h aren’t\n// available here. They need custom initialization code, and the reply\n// structures need verification code too, so duplicate the expected definitions\n// of the structures from both exc.h and mach_exc.h here in this file, and\n// provide the initialization and verification code as methods in true\n// object-oriented fashion.\n\nstruct __attribute__((packed, aligned(4))) ExceptionRaiseRequest {\n  ExceptionRaiseRequest() {\n    memset(this, 0xa5, sizeof(*this));\n    Head.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |\n        MACH_MSGH_BITS_COMPLEX;\n    Head.msgh_size = sizeof(*this) - sizeof(trailer);\n    Head.msgh_remote_port = kClientRemotePort;\n    Head.msgh_local_port = kServerLocalPort;\n    Head.msgh_id = 2401;\n    msgh_body.msgh_descriptor_count = 2;\n    InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);\n    InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);\n    NDR = NDR_record;\n    exception = kExceptionType;\n    codeCnt = 2;\n    code[0] = kTestExceptonCodes[0];\n    code[1] = kTestExceptonCodes[1];\n  }\n\n  mach_msg_header_t Head;\n  mach_msg_body_t msgh_body;\n  mach_msg_port_descriptor_t thread;\n  mach_msg_port_descriptor_t task;\n  NDR_record_t NDR;\n  exception_type_t exception;\n  mach_msg_type_number_t codeCnt;\n  integer_t code[2];\n  mach_msg_trailer_t trailer;\n};\n\nstruct __attribute__((packed, aligned(4))) ExceptionRaiseReply {\n  ExceptionRaiseReply() {\n    memset(this, 0x5a, sizeof(*this));\n    RetCode = KERN_FAILURE;\n  }\n\n  // Verify accepts a |behavior| parameter because the same message format and\n  // verification function is used for ExceptionRaiseReply and\n  // MachExceptionRaiseReply. Knowing which behavior is expected allows the\n  // message ID to be checked.\n  void Verify(exception_behavior_t behavior) {\n    EXPECT_EQ(Head.msgh_bits,\n              implicit_cast<mach_msg_bits_t>(\n                  MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)));\n    EXPECT_EQ(Head.msgh_size, sizeof(*this));\n    EXPECT_EQ(Head.msgh_remote_port, kClientRemotePort);\n    EXPECT_EQ(Head.msgh_local_port, kMachPortNull);\n    switch (behavior) {\n      case EXCEPTION_DEFAULT:\n        EXPECT_EQ(Head.msgh_id, 2501);\n        break;\n      case EXCEPTION_DEFAULT | kMachExceptionCodes:\n        EXPECT_EQ(Head.msgh_id, 2505);\n        break;\n      default:\n        ADD_FAILURE() << \"behavior \" << behavior << \", Head.msgh_id \"\n                      << Head.msgh_id;\n        break;\n    }\n    EXPECT_EQ(memcmp(&NDR, &NDR_record, sizeof(NDR)), 0);\n    EXPECT_EQ(RetCode, KERN_SUCCESS);\n  }\n\n  mach_msg_header_t Head;\n  NDR_record_t NDR;\n  kern_return_t RetCode;\n};\n\nstruct __attribute__((packed, aligned(4))) ExceptionRaiseStateRequest {\n  ExceptionRaiseStateRequest() {\n    memset(this, 0xa5, sizeof(*this));\n    Head.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND);\n    Head.msgh_size = sizeof(*this) - sizeof(trailer);\n    Head.msgh_remote_port = kClientRemotePort;\n    Head.msgh_local_port = kServerLocalPort;\n    Head.msgh_id = 2402;\n    NDR = NDR_record;\n    exception = kExceptionType;\n    codeCnt = 2;\n    code[0] = kTestExceptonCodes[0];\n    code[1] = kTestExceptonCodes[1];\n    flavor = kThreadStateFlavor;\n    old_stateCnt = kThreadStateFlavorCount;\n\n    // Adjust the message size for the data that it’s actually carrying, which\n    // may be smaller than the maximum that it can carry.\n    Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);\n  }\n\n  // Because the message size has been adjusted, the trailer may not appear in\n  // its home member variable. This computes the actual address of the trailer.\n  const mach_msg_trailer_t* Trailer() const {\n    return MachMessageTrailerFromHeader(&Head);\n  }\n\n  mach_msg_header_t Head;\n  NDR_record_t NDR;\n  exception_type_t exception;\n  mach_msg_type_number_t codeCnt;\n  integer_t code[2];\n  int flavor;\n  mach_msg_type_number_t old_stateCnt;\n  natural_t old_state[THREAD_STATE_MAX];\n  mach_msg_trailer_t trailer;\n};\n\nstruct __attribute__((packed, aligned(4))) ExceptionRaiseStateReply {\n  ExceptionRaiseStateReply() {\n    memset(this, 0x5a, sizeof(*this));\n    RetCode = KERN_FAILURE;\n  }\n\n  // Verify accepts a |behavior| parameter because the same message format and\n  // verification function is used for ExceptionRaiseStateReply,\n  // ExceptionRaiseStateIdentityReply, MachExceptionRaiseStateReply, and\n  // MachExceptionRaiseStateIdentityReply. Knowing which behavior is expected\n  // allows the message ID to be checked.\n  void Verify(exception_behavior_t behavior) {\n    EXPECT_EQ(Head.msgh_bits,\n              implicit_cast<mach_msg_bits_t>(\n                  MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)));\n    EXPECT_EQ(Head.msgh_size, sizeof(*this));\n    EXPECT_EQ(Head.msgh_remote_port, kClientRemotePort);\n    EXPECT_EQ(Head.msgh_local_port, kMachPortNull);\n    switch (behavior) {\n      case EXCEPTION_STATE:\n        EXPECT_EQ(Head.msgh_id, 2502);\n        break;\n      case EXCEPTION_STATE_IDENTITY:\n        EXPECT_EQ(Head.msgh_id, 2503);\n        break;\n      case EXCEPTION_STATE | kMachExceptionCodes:\n        EXPECT_EQ(Head.msgh_id, 2506);\n        break;\n      case EXCEPTION_STATE_IDENTITY | kMachExceptionCodes:\n        EXPECT_EQ(Head.msgh_id, 2507);\n        break;\n      case EXCEPTION_STATE_IDENTITY_PROTECTED | kMachExceptionCodes:\n        EXPECT_EQ(Head.msgh_id, 2510);\n        break;\n      default:\n        ADD_FAILURE() << \"behavior \" << behavior << \", Head.msgh_id \"\n                      << Head.msgh_id;\n        break;\n    }\n    EXPECT_EQ(memcmp(&NDR, &NDR_record, sizeof(NDR)), 0);\n    EXPECT_EQ(RetCode, KERN_SUCCESS);\n    EXPECT_EQ(flavor, kThreadStateFlavor);\n    EXPECT_EQ(new_stateCnt, std::size(new_state));\n  }\n\n  mach_msg_header_t Head;\n  NDR_record_t NDR;\n  kern_return_t RetCode;\n  int flavor;\n  mach_msg_type_number_t new_stateCnt;\n  natural_t new_state[THREAD_STATE_MAX];\n};\n\nstruct __attribute__((packed, aligned(4))) ExceptionRaiseStateIdentityRequest {\n  ExceptionRaiseStateIdentityRequest() {\n    memset(this, 0xa5, sizeof(*this));\n    Head.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |\n        MACH_MSGH_BITS_COMPLEX;\n    Head.msgh_size = sizeof(*this) - sizeof(trailer);\n    Head.msgh_remote_port = kClientRemotePort;\n    Head.msgh_local_port = kServerLocalPort;\n    Head.msgh_id = 2403;\n    msgh_body.msgh_descriptor_count = 2;\n    InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);\n    InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);\n    NDR = NDR_record;\n    exception = kExceptionType;\n    codeCnt = 2;\n    code[0] = kTestExceptonCodes[0];\n    code[1] = kTestExceptonCodes[1];\n    flavor = kThreadStateFlavor;\n    old_stateCnt = kThreadStateFlavorCount;\n\n    // Adjust the message size for the data that it’s actually carrying, which\n    // may be smaller than the maximum that it can carry.\n    Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);\n  }\n\n  // Because the message size has been adjusted, the trailer may not appear in\n  // its home member variable. This computes the actual address of the trailer.\n  const mach_msg_trailer_t* Trailer() const {\n    return MachMessageTrailerFromHeader(&Head);\n  }\n\n  mach_msg_header_t Head;\n  mach_msg_body_t msgh_body;\n  mach_msg_port_descriptor_t thread;\n  mach_msg_port_descriptor_t task;\n  NDR_record_t NDR;\n  exception_type_t exception;\n  mach_msg_type_number_t codeCnt;\n  integer_t code[2];\n  int flavor;\n  mach_msg_type_number_t old_stateCnt;\n  natural_t old_state[THREAD_STATE_MAX];\n  mach_msg_trailer_t trailer;\n};\n\n// The reply messages for exception_raise_state and\n// exception_raise_state_identity are identical.\nusing ExceptionRaiseStateIdentityReply = ExceptionRaiseStateReply;\n\nstruct __attribute__((packed, aligned(4))) MachExceptionRaiseRequest {\n  MachExceptionRaiseRequest() {\n    memset(this, 0xa5, sizeof(*this));\n    Head.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |\n        MACH_MSGH_BITS_COMPLEX;\n    Head.msgh_size = sizeof(*this) - sizeof(trailer);\n    Head.msgh_remote_port = kClientRemotePort;\n    Head.msgh_local_port = kServerLocalPort;\n    Head.msgh_id = 2405;\n    msgh_body.msgh_descriptor_count = 2;\n    InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);\n    InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);\n    NDR = NDR_record;\n    exception = kExceptionType;\n    codeCnt = 2;\n    code[0] = kTestMachExceptionCodes[0];\n    code[1] = kTestMachExceptionCodes[1];\n  }\n\n  mach_msg_header_t Head;\n  mach_msg_body_t msgh_body;\n  mach_msg_port_descriptor_t thread;\n  mach_msg_port_descriptor_t task;\n  NDR_record_t NDR;\n  exception_type_t exception;\n  mach_msg_type_number_t codeCnt;\n  int64_t code[2];\n  mach_msg_trailer_t trailer;\n};\n\n// The reply messages for exception_raise and mach_exception_raise are\n// identical.\nusing MachExceptionRaiseReply = ExceptionRaiseReply;\n\nstruct __attribute__((packed, aligned(4))) MachExceptionRaiseStateRequest {\n  MachExceptionRaiseStateRequest() {\n    memset(this, 0xa5, sizeof(*this));\n    Head.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND);\n    Head.msgh_size = sizeof(*this) - sizeof(trailer);\n    Head.msgh_remote_port = kClientRemotePort;\n    Head.msgh_local_port = kServerLocalPort;\n    Head.msgh_id = 2406;\n    NDR = NDR_record;\n    exception = kExceptionType;\n    codeCnt = 2;\n    code[0] = kTestMachExceptionCodes[0];\n    code[1] = kTestMachExceptionCodes[1];\n    flavor = kThreadStateFlavor;\n    old_stateCnt = kThreadStateFlavorCount;\n\n    // Adjust the message size for the data that it’s actually carrying, which\n    // may be smaller than the maximum that it can carry.\n    Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);\n  }\n\n  // Because the message size has been adjusted, the trailer may not appear in\n  // its home member variable. This computes the actual address of the trailer.\n  const mach_msg_trailer_t* Trailer() const {\n    return MachMessageTrailerFromHeader(&Head);\n  }\n\n  mach_msg_header_t Head;\n  NDR_record_t NDR;\n  exception_type_t exception;\n  mach_msg_type_number_t codeCnt;\n  int64_t code[2];\n  int flavor;\n  mach_msg_type_number_t old_stateCnt;\n  natural_t old_state[THREAD_STATE_MAX];\n  mach_msg_trailer_t trailer;\n};\n\n// The reply messages for exception_raise_state and mach_exception_raise_state\n// are identical.\nusing MachExceptionRaiseStateReply = ExceptionRaiseStateReply;\n\nstruct __attribute__((packed, aligned(4)))\nMachExceptionRaiseStateIdentityRequest {\n  MachExceptionRaiseStateIdentityRequest() {\n    memset(this, 0xa5, sizeof(*this));\n    Head.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |\n        MACH_MSGH_BITS_COMPLEX;\n    Head.msgh_size = sizeof(*this) - sizeof(trailer);\n    Head.msgh_remote_port = kClientRemotePort;\n    Head.msgh_local_port = kServerLocalPort;\n    Head.msgh_id = 2407;\n    msgh_body.msgh_descriptor_count = 2;\n    InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);\n    InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);\n    NDR = NDR_record;\n    exception = kExceptionType;\n    codeCnt = 2;\n    code[0] = kTestMachExceptionCodes[0];\n    code[1] = kTestMachExceptionCodes[1];\n    flavor = kThreadStateFlavor;\n    old_stateCnt = kThreadStateFlavorCount;\n\n    // Adjust the message size for the data that it’s actually carrying, which\n    // may be smaller than the maximum that it can carry.\n    Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);\n  }\n\n  // Because the message size has been adjusted, the trailer may not appear in\n  // its home member variable. This computes the actual address of the trailer.\n  const mach_msg_trailer_t* Trailer() const {\n    return MachMessageTrailerFromHeader(&Head);\n  }\n\n  mach_msg_header_t Head;\n  mach_msg_body_t msgh_body;\n  mach_msg_port_descriptor_t thread;\n  mach_msg_port_descriptor_t task;\n  NDR_record_t NDR;\n  exception_type_t exception;\n  mach_msg_type_number_t codeCnt;\n  int64_t code[2];\n  int flavor;\n  mach_msg_type_number_t old_stateCnt;\n  natural_t old_state[THREAD_STATE_MAX];\n  mach_msg_trailer_t trailer;\n};\n\n// The reply messages for exception_raise_state_identity and\n// mach_exception_raise_state_identity are identical.\nusing MachExceptionRaiseStateIdentityReply = ExceptionRaiseStateIdentityReply;\n\nstruct __attribute__((packed, aligned(4)))\nMachExceptionRaiseStateIdentityProtectedRequest {\n  MachExceptionRaiseStateIdentityProtectedRequest() {\n    memset(this, 0xa5, sizeof(*this));\n    Head.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND) |\n        MACH_MSGH_BITS_COMPLEX;\n    Head.msgh_size = sizeof(*this) - sizeof(trailer);\n    Head.msgh_remote_port = kClientRemotePort;\n    Head.msgh_local_port = kServerLocalPort;\n    Head.msgh_id = 2410;\n    msgh_body.msgh_descriptor_count = 1;\n    thread_id = 0;\n    InitializeMachMsgPortDescriptor(&task_id_token_t, kExceptionTaskPort);\n    NDR = NDR_record;\n    exception = kExceptionType;\n    codeCnt = 2;\n    code[0] = kTestMachExceptionCodes[0];\n    code[1] = kTestMachExceptionCodes[1];\n    flavor = kThreadStateFlavor;\n    old_stateCnt = kThreadStateFlavorCount;\n\n    // Adjust the message size for the data that it’s actually carrying, which\n    // may be smaller than the maximum that it can carry.\n    Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);\n  }\n\n  // Because the message size has been adjusted, the trailer may not appear in\n  // its home member variable. This computes the actual address of the trailer.\n  const mach_msg_trailer_t* Trailer() const {\n    return MachMessageTrailerFromHeader(&Head);\n  }\n\n  mach_msg_header_t Head;\n  mach_msg_body_t msgh_body;\n  mach_msg_port_descriptor_t task_id_token_t;\n  NDR_record_t NDR;\n  int64_t thread_id;\n  exception_type_t exception;\n  mach_msg_type_number_t codeCnt;\n  int64_t code[2];\n  int flavor;\n  mach_msg_type_number_t old_stateCnt;\n  natural_t old_state[THREAD_STATE_MAX];\n  mach_msg_trailer_t trailer;\n};\n\n// The reply messages for mach_exception_raise_state_identity_protected and\n// mach_exception_raise_state_identity are identical.\nusing MachExceptionRaiseStateIdentityProtectedReply =\n    MachExceptionRaiseStateIdentityReply;\n\n// InvalidRequest and BadIDErrorReply are used to test that\n// UniversalMachExcServer deals appropriately with messages that it does not\n// understand: messages with an unknown Head.msgh_id.\n\nstruct InvalidRequest : public mach_msg_empty_send_t {\n  explicit InvalidRequest(mach_msg_id_t id) {\n    memset(this, 0xa5, sizeof(*this));\n    header.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND);\n    header.msgh_size = sizeof(*this);\n    header.msgh_remote_port = kClientRemotePort;\n    header.msgh_local_port = kServerLocalPort;\n    header.msgh_id = id;\n  }\n};\n\nstruct BadIDErrorReply : public mig_reply_error_t {\n  BadIDErrorReply() {\n    memset(this, 0x5a, sizeof(*this));\n    RetCode = KERN_FAILURE;\n  }\n\n  void Verify(mach_msg_id_t id) {\n    EXPECT_EQ(Head.msgh_bits,\n              implicit_cast<mach_msg_bits_t>(\n                  MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)));\n    EXPECT_EQ(Head.msgh_size, sizeof(*this));\n    EXPECT_EQ(Head.msgh_remote_port, kClientRemotePort);\n    EXPECT_EQ(Head.msgh_local_port, kMachPortNull);\n    EXPECT_EQ(Head.msgh_id, id + 100);\n    EXPECT_EQ(memcmp(&NDR, &NDR_record, sizeof(NDR)), 0);\n    EXPECT_EQ(RetCode, MIG_BAD_ID);\n  }\n};\n\nclass MockUniversalMachExcServer : public UniversalMachExcServer::Interface {\n public:\n  struct ConstExceptionCodes {\n    const mach_exception_data_type_t* code;\n    mach_msg_type_number_t code_count;\n  };\n  struct ThreadStateAndCount {\n    thread_state_t state;\n    mach_msg_type_number_t* state_count;\n  };\n  struct ConstThreadStateAndCount {\n    ConstThreadState state;\n    mach_msg_type_number_t* state_count;\n  };\n\n  // UniversalMachExcServer::Interface:\n\n  // CatchMachException is the method to mock, but it has 13 parameters, and\n  // Google Mock can only mock methods with up to 10 parameters. Coalesce some\n  // related parameters together into structs, and call a mocked method.\n  virtual kern_return_t CatchMachException(\n      exception_behavior_t behavior,\n      exception_handler_t exception_port,\n      thread_t thread,\n      task_t task,\n      exception_type_t exception,\n      const mach_exception_data_type_t* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer,\n      bool* destroy_complex_request) override {\n    *destroy_complex_request = true;\n    const ConstExceptionCodes exception_codes = {code, code_count};\n    const ConstThreadStateAndCount old_thread_state = {old_state,\n                                                       &old_state_count};\n    ThreadStateAndCount new_thread_state = {new_state, new_state_count};\n    return MockCatchMachException(behavior,\n                                  exception_port,\n                                  thread,\n                                  task,\n                                  exception,\n                                  &exception_codes,\n                                  flavor,\n                                  &old_thread_state,\n                                  &new_thread_state,\n                                  trailer);\n  }\n\n  MOCK_METHOD(kern_return_t,\n              MockCatchMachException,\n              (exception_behavior_t behavior,\n               exception_handler_t exception_port,\n               thread_t thread,\n               task_t task,\n               exception_type_t exception,\n               const ConstExceptionCodes* exception_codes,\n               thread_state_flavor_t* flavor,\n               const ConstThreadStateAndCount* old_thread_state,\n               ThreadStateAndCount* new_thread_state,\n               const mach_msg_trailer_t* trailer));\n};\n\n// Matcher for ConstExceptionCodes, testing that it carries 2 codes matching\n// code_0 and code_1.\nMATCHER_P2(AreExceptionCodes, code_0, code_1, \"\") {\n  if (!arg) {\n    return false;\n  }\n\n  if (arg->code_count == 2 && arg->code[0] == code_0 &&\n      arg->code[1] == code_1) {\n    return true;\n  }\n\n  *result_listener << \"codes (\";\n  for (size_t index = 0; index < arg->code_count; ++index) {\n    *result_listener << arg->code[index];\n    if (index < arg->code_count - 1) {\n      *result_listener << \", \";\n    }\n  }\n  *result_listener << \")\";\n\n  return false;\n}\n\n// Matcher for ThreadStateAndCount and ConstThreadStateAndCount, testing that\n// *state_count is present and matches the specified value. If 0 is specified\n// for the count, |state| must be nullptr (not present), otherwise |state| must\n// not be nullptr (present).\nMATCHER_P(IsThreadStateAndCount, state_count, \"\") {\n  if (!arg) {\n    return false;\n  }\n  if (!arg->state_count) {\n    *result_listener << \"state_count nullptr\";\n    return false;\n  }\n  if (*(arg->state_count) != state_count) {\n    *result_listener << \"*state_count \" << *(arg->state_count);\n    return false;\n  }\n  if (state_count) {\n    if (!arg->state) {\n      *result_listener << \"*state_count \" << state_count << \", state nullptr\";\n      return false;\n    }\n  } else {\n    if (arg->state) {\n      *result_listener << \"*state_count 0, state non-nullptr (\" << arg->state\n                       << \")\";\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename T>\nclass ScopedDefaultValue {\n public:\n  explicit ScopedDefaultValue(const T& default_value) {\n    DefaultValue<T>::Set(default_value);\n  }\n\n  ~ScopedDefaultValue() { DefaultValue<T>::Clear(); }\n};\n\nTEST(ExcServerVariants, MockExceptionRaise) {\n  ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  std::set<mach_msg_id_t> ids =\n      universal_mach_exc_server.MachMessageServerRequestIDs();\n  EXPECT_NE(ids.find(2401), ids.end());  // There is no constant for this.\n\n  ExceptionRaiseRequest request;\n  EXPECT_LE(request.Head.msgh_size,\n            universal_mach_exc_server.MachMessageServerRequestSize());\n\n  ExceptionRaiseReply reply;\n  EXPECT_LE(sizeof(reply),\n            universal_mach_exc_server.MachMessageServerReplySize());\n\n  constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT;\n\n  EXPECT_CALL(server,\n              MockCatchMachException(kExceptionBehavior,\n                                     kServerLocalPort,\n                                     kExceptionThreadPort,\n                                     kExceptionTaskPort,\n                                     kExceptionType,\n                                     AreExceptionCodes(kTestExceptonCodes[0],\n                                                       kTestExceptonCodes[1]),\n                                     Pointee(Eq(THREAD_STATE_NONE)),\n                                     IsThreadStateAndCount(0u),\n                                     IsThreadStateAndCount(0u),\n                                     Eq(&request.trailer)))\n      .WillOnce(Return(KERN_SUCCESS))\n      .RetiresOnSaturation();\n\n  bool destroy_complex_request = false;\n  EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(\n      reinterpret_cast<mach_msg_header_t*>(&request),\n      reinterpret_cast<mach_msg_header_t*>(&reply),\n      &destroy_complex_request));\n  EXPECT_TRUE(destroy_complex_request);\n\n  reply.Verify(kExceptionBehavior);\n}\n\nTEST(ExcServerVariants, MockMachExceptionRaiseStateIdentityProtected) {\n  ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  std::set<mach_msg_id_t> ids =\n      universal_mach_exc_server.MachMessageServerRequestIDs();\n  EXPECT_NE(ids.find(2410), ids.end());  // There is no constant for this.\n\n  MachExceptionRaiseStateIdentityProtectedRequest request;  // Protected\n  EXPECT_LE(request.Head.msgh_size,\n            universal_mach_exc_server.MachMessageServerRequestSize());\n\n  MachExceptionRaiseStateIdentityProtectedReply reply;\n  EXPECT_LE(sizeof(reply),\n            universal_mach_exc_server.MachMessageServerReplySize());\n\n  constexpr exception_behavior_t kExceptionBehavior =\n      EXCEPTION_STATE_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES;\n\n  EXPECT_CALL(\n      server,\n      MockCatchMachException(kExceptionBehavior,\n                             kServerLocalPort,\n                             0,  // kExceptionThreadPort,\n                             0,  // kExceptionTaskPort,\n                             kExceptionType,\n                             AreExceptionCodes(kTestMachExceptionCodes[0],\n                                               kTestMachExceptionCodes[1]),\n                             Pointee(Eq(kThreadStateFlavor)),\n                             IsThreadStateAndCount(kThreadStateFlavorCount),\n                             IsThreadStateAndCount(std::size(reply.new_state)),\n                             Eq(request.Trailer())))\n      .WillOnce(Return(KERN_SUCCESS))\n      .RetiresOnSaturation();\n\n  bool destroy_complex_request = false;\n  EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(\n      reinterpret_cast<mach_msg_header_t*>(&request),\n      reinterpret_cast<mach_msg_header_t*>(&reply),\n      &destroy_complex_request));\n  EXPECT_TRUE(destroy_complex_request);\n\n  reply.Verify(kExceptionBehavior);\n}\n\nTEST(ExcServerVariants, MockExceptionRaiseState) {\n  ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  std::set<mach_msg_id_t> ids =\n      universal_mach_exc_server.MachMessageServerRequestIDs();\n  EXPECT_NE(ids.find(2402), ids.end());  // There is no constant for this.\n\n  ExceptionRaiseStateRequest request;\n  EXPECT_LE(request.Head.msgh_size,\n            universal_mach_exc_server.MachMessageServerRequestSize());\n\n  ExceptionRaiseStateReply reply;\n  EXPECT_LE(sizeof(reply),\n            universal_mach_exc_server.MachMessageServerReplySize());\n\n  constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_STATE;\n\n  EXPECT_CALL(\n      server,\n      MockCatchMachException(\n          kExceptionBehavior,\n          kServerLocalPort,\n          THREAD_NULL,\n          TASK_NULL,\n          kExceptionType,\n          AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]),\n          Pointee(Eq(kThreadStateFlavor)),\n          IsThreadStateAndCount(kThreadStateFlavorCount),\n          IsThreadStateAndCount(std::size(reply.new_state)),\n          Eq(request.Trailer())))\n      .WillOnce(Return(KERN_SUCCESS))\n      .RetiresOnSaturation();\n\n  bool destroy_complex_request = false;\n  EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(\n      reinterpret_cast<mach_msg_header_t*>(&request),\n      reinterpret_cast<mach_msg_header_t*>(&reply),\n      &destroy_complex_request));\n\n  // The request wasn’t complex, so nothing got a chance to change the value of\n  // this variable.\n  EXPECT_FALSE(destroy_complex_request);\n\n  reply.Verify(kExceptionBehavior);\n}\n\nTEST(ExcServerVariants, MockExceptionRaiseStateIdentity) {\n  ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  std::set<mach_msg_id_t> ids =\n      universal_mach_exc_server.MachMessageServerRequestIDs();\n  EXPECT_NE(ids.find(2403), ids.end());  // There is no constant for this.\n\n  ExceptionRaiseStateIdentityRequest request;\n  EXPECT_LE(request.Head.msgh_size,\n            universal_mach_exc_server.MachMessageServerRequestSize());\n\n  ExceptionRaiseStateIdentityReply reply;\n  EXPECT_LE(sizeof(reply),\n            universal_mach_exc_server.MachMessageServerReplySize());\n\n  constexpr exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY;\n\n  EXPECT_CALL(\n      server,\n      MockCatchMachException(\n          kExceptionBehavior,\n          kServerLocalPort,\n          kExceptionThreadPort,\n          kExceptionTaskPort,\n          kExceptionType,\n          AreExceptionCodes(kTestExceptonCodes[0], kTestExceptonCodes[1]),\n          Pointee(Eq(kThreadStateFlavor)),\n          IsThreadStateAndCount(kThreadStateFlavorCount),\n          IsThreadStateAndCount(std::size(reply.new_state)),\n          Eq(request.Trailer())))\n      .WillOnce(Return(KERN_SUCCESS))\n      .RetiresOnSaturation();\n\n  bool destroy_complex_request = false;\n  EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(\n      reinterpret_cast<mach_msg_header_t*>(&request),\n      reinterpret_cast<mach_msg_header_t*>(&reply),\n      &destroy_complex_request));\n  EXPECT_TRUE(destroy_complex_request);\n\n  reply.Verify(kExceptionBehavior);\n}\n\nTEST(ExcServerVariants, MockMachExceptionRaise) {\n  ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  std::set<mach_msg_id_t> ids =\n      universal_mach_exc_server.MachMessageServerRequestIDs();\n  EXPECT_NE(ids.find(2405), ids.end());  // There is no constant for this.\n\n  MachExceptionRaiseRequest request;\n  EXPECT_LE(request.Head.msgh_size,\n            universal_mach_exc_server.MachMessageServerRequestSize());\n\n  MachExceptionRaiseReply reply;\n  EXPECT_LE(sizeof(reply),\n            universal_mach_exc_server.MachMessageServerReplySize());\n\n  constexpr exception_behavior_t kExceptionBehavior =\n      EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES;\n\n  EXPECT_CALL(\n      server,\n      MockCatchMachException(kExceptionBehavior,\n                             kServerLocalPort,\n                             kExceptionThreadPort,\n                             kExceptionTaskPort,\n                             kExceptionType,\n                             AreExceptionCodes(kTestMachExceptionCodes[0],\n                                               kTestMachExceptionCodes[1]),\n                             Pointee(Eq(THREAD_STATE_NONE)),\n                             IsThreadStateAndCount(0u),\n                             IsThreadStateAndCount(0u),\n                             Eq(&request.trailer)))\n      .WillOnce(Return(KERN_SUCCESS))\n      .RetiresOnSaturation();\n\n  bool destroy_complex_request = false;\n  EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(\n      reinterpret_cast<mach_msg_header_t*>(&request),\n      reinterpret_cast<mach_msg_header_t*>(&reply),\n      &destroy_complex_request));\n  EXPECT_TRUE(destroy_complex_request);\n\n  reply.Verify(kExceptionBehavior);\n}\n\nTEST(ExcServerVariants, MockMachExceptionRaiseState) {\n  ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  std::set<mach_msg_id_t> ids =\n      universal_mach_exc_server.MachMessageServerRequestIDs();\n  EXPECT_NE(ids.find(2406), ids.end());  // There is no constant for this.\n\n  MachExceptionRaiseStateRequest request;\n  EXPECT_LE(request.Head.msgh_size,\n            universal_mach_exc_server.MachMessageServerRequestSize());\n\n  MachExceptionRaiseStateReply reply;\n  EXPECT_LE(sizeof(reply),\n            universal_mach_exc_server.MachMessageServerReplySize());\n\n  constexpr exception_behavior_t kExceptionBehavior =\n      EXCEPTION_STATE | MACH_EXCEPTION_CODES;\n\n  EXPECT_CALL(\n      server,\n      MockCatchMachException(kExceptionBehavior,\n                             kServerLocalPort,\n                             THREAD_NULL,\n                             TASK_NULL,\n                             kExceptionType,\n                             AreExceptionCodes(kTestMachExceptionCodes[0],\n                                               kTestMachExceptionCodes[1]),\n                             Pointee(Eq(kThreadStateFlavor)),\n                             IsThreadStateAndCount(kThreadStateFlavorCount),\n                             IsThreadStateAndCount(std::size(reply.new_state)),\n                             Eq(request.Trailer())))\n      .WillOnce(Return(KERN_SUCCESS))\n      .RetiresOnSaturation();\n\n  bool destroy_complex_request = false;\n  EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(\n      reinterpret_cast<mach_msg_header_t*>(&request),\n      reinterpret_cast<mach_msg_header_t*>(&reply),\n      &destroy_complex_request));\n\n  // The request wasn’t complex, so nothing got a chance to change the value of\n  // this variable.\n  EXPECT_FALSE(destroy_complex_request);\n\n  reply.Verify(kExceptionBehavior);\n}\n\nTEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) {\n  ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  std::set<mach_msg_id_t> ids =\n      universal_mach_exc_server.MachMessageServerRequestIDs();\n  EXPECT_NE(ids.find(2407), ids.end());  // There is no constant for this.\n\n  MachExceptionRaiseStateIdentityRequest request;\n  EXPECT_LE(request.Head.msgh_size,\n            universal_mach_exc_server.MachMessageServerRequestSize());\n\n  MachExceptionRaiseStateIdentityReply reply;\n  EXPECT_LE(sizeof(reply),\n            universal_mach_exc_server.MachMessageServerReplySize());\n\n  constexpr exception_behavior_t kExceptionBehavior =\n      EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES;\n\n  EXPECT_CALL(\n      server,\n      MockCatchMachException(kExceptionBehavior,\n                             kServerLocalPort,\n                             kExceptionThreadPort,\n                             kExceptionTaskPort,\n                             kExceptionType,\n                             AreExceptionCodes(kTestMachExceptionCodes[0],\n                                               kTestMachExceptionCodes[1]),\n                             Pointee(Eq(kThreadStateFlavor)),\n                             IsThreadStateAndCount(kThreadStateFlavorCount),\n                             IsThreadStateAndCount(std::size(reply.new_state)),\n                             Eq(request.Trailer())))\n      .WillOnce(Return(KERN_SUCCESS))\n      .RetiresOnSaturation();\n\n  bool destroy_complex_request = false;\n  EXPECT_TRUE(universal_mach_exc_server.MachMessageServerFunction(\n      reinterpret_cast<mach_msg_header_t*>(&request),\n      reinterpret_cast<mach_msg_header_t*>(&reply),\n      &destroy_complex_request));\n  EXPECT_TRUE(destroy_complex_request);\n\n  reply.Verify(kExceptionBehavior);\n}\n\nTEST(ExcServerVariants, MockUnknownID) {\n  ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  // Make sure that a message with an unknown ID is handled appropriately.\n  // UniversalMachExcServer should not dispatch the message to\n  // MachMessageServerFunction, but should generate a MIG_BAD_ID error reply.\n\n  static constexpr mach_msg_id_t unknown_ids[] = {\n      // Reasonable things to check.\n      -101,\n      -100,\n      -99,\n      -1,\n      0,\n      1,\n      99,\n      100,\n      101,\n\n      // Invalid IDs right around valid ones.\n      2400,\n      2404,\n      2408,\n\n      // Valid and invalid IDs in the range used for replies, not requests.\n      2500,\n      2501,\n      2502,\n      2503,\n      2504,\n      2505,\n      2506,\n      2507,\n      2508,\n  };\n\n  for (size_t index = 0; index < std::size(unknown_ids); ++index) {\n    mach_msg_id_t id = unknown_ids[index];\n\n    SCOPED_TRACE(base::StringPrintf(\"unknown id %d\", id));\n\n    std::set<mach_msg_id_t> ids =\n        universal_mach_exc_server.MachMessageServerRequestIDs();\n    EXPECT_EQ(ids.find(id), ids.end());\n\n    InvalidRequest request(id);\n    EXPECT_LE(sizeof(request),\n              universal_mach_exc_server.MachMessageServerRequestSize());\n\n    BadIDErrorReply reply;\n    EXPECT_LE(sizeof(reply),\n              universal_mach_exc_server.MachMessageServerReplySize());\n\n    bool destroy_complex_request = false;\n    EXPECT_FALSE(universal_mach_exc_server.MachMessageServerFunction(\n        reinterpret_cast<mach_msg_header_t*>(&request),\n        reinterpret_cast<mach_msg_header_t*>(&reply),\n        &destroy_complex_request));\n\n    // The request wasn’t handled, nothing got a chance to change the value of\n    // this variable. MachMessageServer would destroy the request if it was\n    // complex, regardless of what was done to this variable, because the\n    // return code was not KERN_SUCCESS or MIG_NO_REPLY.\n    EXPECT_FALSE(destroy_complex_request);\n\n    reply.Verify(id);\n  }\n}\n\nTEST(ExcServerVariants, MachMessageServerRequestIDs) {\n  std::set<mach_msg_id_t> expect_request_ids;\n\n  // There are no constants for these.\n  expect_request_ids.insert(2401);\n  expect_request_ids.insert(2402);\n  expect_request_ids.insert(2403);\n  expect_request_ids.insert(2405);\n  expect_request_ids.insert(2406);\n  expect_request_ids.insert(2407);\n  expect_request_ids.insert(2410);\n  expect_request_ids.insert(2411);\n\n  MockUniversalMachExcServer server;\n  UniversalMachExcServer universal_mach_exc_server(&server);\n\n  EXPECT_EQ(universal_mach_exc_server.MachMessageServerRequestIDs(),\n            expect_request_ids);\n}\n\n#if BUILDFLAG(IS_MAC)\n\nclass TestExcServerVariants : public MachMultiprocess,\n                              public UniversalMachExcServer::Interface {\n public:\n  TestExcServerVariants(exception_behavior_t behavior,\n                        thread_state_flavor_t flavor,\n                        mach_msg_type_number_t state_count)\n      : MachMultiprocess(),\n        UniversalMachExcServer::Interface(),\n        behavior_(behavior),\n        flavor_(flavor),\n        state_count_(state_count),\n        handled_(false) {\n    SetExpectedChildTerminationBuiltinTrap();\n  }\n\n  TestExcServerVariants(const TestExcServerVariants&) = delete;\n  TestExcServerVariants& operator=(const TestExcServerVariants&) = delete;\n\n  // UniversalMachExcServer::Interface:\n\n  virtual kern_return_t CatchMachException(\n      exception_behavior_t behavior,\n      exception_handler_t exception_port,\n      thread_t thread,\n      task_t task,\n      exception_type_t exception,\n      const mach_exception_data_type_t* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer,\n      bool* destroy_complex_request) override {\n    *destroy_complex_request = true;\n\n    EXPECT_FALSE(handled_);\n    handled_ = true;\n\n    EXPECT_EQ(behavior, behavior_);\n\n    EXPECT_EQ(exception_port, LocalPort());\n\n    if (ExceptionBehaviorHasIdentity(behavior)) {\n      EXPECT_NE(thread, THREAD_NULL);\n      EXPECT_EQ(task, ChildTask());\n    } else {\n      EXPECT_EQ(thread, THREAD_NULL);\n      EXPECT_EQ(task, TASK_NULL);\n    }\n\n    EXPECT_EQ(exception, EXC_CRASH);\n    EXPECT_EQ(code_count, 2u);\n\n    // The exception and code_count checks above would ideally use ASSERT_EQ so\n    // that the next conditional would not be necessary, but ASSERT_* requires a\n    // function returning type void, and the interface dictates otherwise here.\n    if (exception == EXC_CRASH && code_count >= 1) {\n      int signal;\n      ExcCrashRecoverOriginalException(code[0], nullptr, &signal);\n    }\n\n    const bool has_state = ExceptionBehaviorHasState(behavior);\n    if (has_state) {\n      EXPECT_EQ(*flavor, flavor_);\n      EXPECT_EQ(old_state_count, state_count_);\n      EXPECT_NE(old_state, nullptr);\n      EXPECT_EQ(*new_state_count,\n                implicit_cast<mach_msg_type_number_t>(THREAD_STATE_MAX));\n      EXPECT_NE(new_state, nullptr);\n    } else {\n      EXPECT_EQ(*flavor, THREAD_STATE_NONE);\n      EXPECT_EQ(old_state_count, 0u);\n      EXPECT_EQ(old_state, nullptr);\n      EXPECT_EQ(*new_state_count, 0u);\n      EXPECT_EQ(new_state, nullptr);\n    }\n\n    EXPECT_EQ(\n        trailer->msgh_trailer_type,\n        implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0));\n    EXPECT_EQ(trailer->msgh_trailer_size,\n              REQUESTED_TRAILER_SIZE(kMachMessageOptions));\n\n    ExcServerCopyState(\n        behavior, old_state, old_state_count, new_state, new_state_count);\n\n    return ExcServerSuccessfulReturnValue(exception, behavior, false);\n  }\n\n private:\n  // MachMultiprocess:\n\n  void MachMultiprocessParent() override {\n    UniversalMachExcServer universal_mach_exc_server(this);\n\n    kern_return_t kr =\n        MachMessageServer::Run(&universal_mach_exc_server,\n                               LocalPort(),\n                               kMachMessageOptions,\n                               MachMessageServer::kOneShot,\n                               MachMessageServer::kReceiveLargeError,\n                               kMachMessageTimeoutWaitIndefinitely);\n    EXPECT_EQ(kr, KERN_SUCCESS)\n        << MachErrorMessage(kr, \"MachMessageServer::Run\");\n\n    EXPECT_TRUE(handled_);\n  }\n\n  void MachMultiprocessChild() override {\n    // Set the parent as the exception handler for EXC_CRASH.\n    kern_return_t kr = task_set_exception_ports(\n        mach_task_self(), EXC_MASK_CRASH, RemotePort(), behavior_, flavor_);\n    ASSERT_EQ(kr, KERN_SUCCESS)\n        << MachErrorMessage(kr, \"task_set_exception_ports\");\n\n    // Now crash.\n    __builtin_trap();\n  }\n\n  exception_behavior_t behavior_;\n  thread_state_flavor_t flavor_;\n  mach_msg_type_number_t state_count_;\n  bool handled_;\n\n  static const mach_msg_option_t kMachMessageOptions =\n      MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0);\n};\n\nTEST(ExcServerVariants, ExceptionRaise) {\n  TestExcServerVariants test_exc_server_variants(\n      EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0);\n  test_exc_server_variants.Run();\n}\n\nTEST(ExcServerVariants, ExceptionRaiseState) {\n  TestExcServerVariants test_exc_server_variants(\n      EXCEPTION_STATE, MACHINE_THREAD_STATE, MACHINE_THREAD_STATE_COUNT);\n  test_exc_server_variants.Run();\n}\n\nTEST(ExcServerVariants, ExceptionRaiseStateIdentity) {\n  TestExcServerVariants test_exc_server_variants(EXCEPTION_STATE_IDENTITY,\n                                                 MACHINE_THREAD_STATE,\n                                                 MACHINE_THREAD_STATE_COUNT);\n  test_exc_server_variants.Run();\n}\n\nTEST(ExcServerVariants, MachExceptionRaise) {\n  TestExcServerVariants test_exc_server_variants(\n      MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0);\n  test_exc_server_variants.Run();\n}\n\nTEST(ExcServerVariants, MachExceptionRaiseState) {\n  TestExcServerVariants test_exc_server_variants(\n      MACH_EXCEPTION_CODES | EXCEPTION_STATE,\n      MACHINE_THREAD_STATE,\n      MACHINE_THREAD_STATE_COUNT);\n  test_exc_server_variants.Run();\n}\n\nTEST(ExcServerVariants, MachExceptionRaiseStateIdentity) {\n  TestExcServerVariants test_exc_server_variants(\n      MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,\n      MACHINE_THREAD_STATE,\n      MACHINE_THREAD_STATE_COUNT);\n  test_exc_server_variants.Run();\n}\n\nTEST(ExcServerVariants, ThreadStates) {\n  // So far, all of the tests worked with MACHINE_THREAD_STATE. Now try all of\n  // the other thread state flavors that are expected to work.\n\n  static constexpr struct {\n    thread_state_flavor_t flavor;\n    mach_msg_type_number_t count;\n  } test_data[] = {\n#if defined(ARCH_CPU_X86_FAMILY)\n    // For the x86 family, exception handlers can only properly receive the\n    // thread, float, and exception state flavors. There’s a bug in the kernel\n    // that causes it to call thread_getstatus() (a wrapper for the more\n    // familiar thread_get_state()) with an incorrect state buffer size\n    // parameter when delivering an exception. 10.9.4\n    // xnu-2422.110.17/osfmk/kern/exception.c exception_deliver() uses the\n    // _MachineStateCount[] array indexed by the flavor number to obtain the\n    // buffer size. 10.9.4 xnu-2422.110.17/osfmk/i386/pcb.c contains the\n    // definition of this array for the x86 family. The slots corresponding to\n    // thread, float, and exception state flavors in both native-width (32- and\n    // 64-bit) and universal are correct, but the remaining elements in the\n    // array are not. This includes elements that would correspond to debug and\n    // AVX state flavors, so these cannot be tested here.\n    //\n    // When machine_thread_get_state() (the machine-specific implementation of\n    // thread_get_state()) encounters an undersized buffer as reported by the\n    // buffer size parameter, it returns KERN_INVALID_ARGUMENT, which causes\n    // exception_deliver() to not actually deliver the exception and instead\n    // return that error code to exception_triage() as well.\n    //\n    // This bug is filed as radar 18312067.\n    //\n    // Additionaly, the AVX state flavors are also not tested because they’re\n    // not available on all CPUs and OS versions.\n    {x86_THREAD_STATE, x86_THREAD_STATE_COUNT},\n    {x86_FLOAT_STATE, x86_FLOAT_STATE_COUNT},\n    {x86_EXCEPTION_STATE, x86_EXCEPTION_STATE_COUNT},\n#if defined(ARCH_CPU_X86)\n    {x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT},\n    {x86_FLOAT_STATE32, x86_FLOAT_STATE32_COUNT},\n    {x86_EXCEPTION_STATE32, x86_EXCEPTION_STATE32_COUNT},\n#elif defined(ARCH_CPU_X86_64)\n    {x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT},\n    {x86_FLOAT_STATE64, x86_FLOAT_STATE64_COUNT},\n    {x86_EXCEPTION_STATE64, x86_EXCEPTION_STATE64_COUNT},\n#endif\n#elif defined(ARCH_CPU_ARM64)\n    {ARM_UNIFIED_THREAD_STATE, ARM_UNIFIED_THREAD_STATE_COUNT},\n    {ARM_THREAD_STATE64, ARM_THREAD_STATE64_COUNT},\n    {ARM_NEON_STATE64, ARM_NEON_STATE64_COUNT},\n    {ARM_EXCEPTION_STATE64, ARM_EXCEPTION_STATE64_COUNT},\n#else\n#error Port this test to your CPU architecture.\n#endif\n  };\n\n  for (size_t index = 0; index < std::size(test_data); ++index) {\n    const auto& test = test_data[index];\n    SCOPED_TRACE(\n        base::StringPrintf(\"index %zu, flavor %d\", index, test.flavor));\n\n    TestExcServerVariants test_exc_server_variants(\n        MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,\n        test.flavor,\n        test.count);\n    test_exc_server_variants.Run();\n  }\n}\n\n#endif  // BUILDFLAG(IS_MAC)\n\nTEST(ExcServerVariants, ExcServerSuccessfulReturnValue) {\n#if BUILDFLAG(IS_IOS)\n  // iOS 9 ≅ OS X 10.11.\n  const kern_return_t prefer_not_set_thread_state = KERN_SUCCESS;\n#else\n  const kern_return_t prefer_not_set_thread_state =\n      MacOSVersionNumber() < 10'11'00 ? MACH_RCV_PORT_DIED : KERN_SUCCESS;\n#endif\n\n  const struct {\n    exception_type_t exception;\n    exception_behavior_t behavior;\n    bool set_thread_state;\n    kern_return_t kr;\n  } kTestData[] = {\n      {EXC_CRASH, EXCEPTION_DEFAULT, false, KERN_SUCCESS},\n      {EXC_CRASH, EXCEPTION_STATE, false, prefer_not_set_thread_state},\n      {EXC_CRASH, EXCEPTION_STATE_IDENTITY, false, prefer_not_set_thread_state},\n      {EXC_CRASH, kMachExceptionCodes | EXCEPTION_DEFAULT, false, KERN_SUCCESS},\n      {EXC_CRASH,\n       kMachExceptionCodes | EXCEPTION_STATE,\n       false,\n       prefer_not_set_thread_state},\n      {EXC_CRASH,\n       kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,\n       false,\n       prefer_not_set_thread_state},\n      {EXC_CRASH, EXCEPTION_DEFAULT, true, KERN_SUCCESS},\n      {EXC_CRASH, EXCEPTION_STATE, true, KERN_SUCCESS},\n      {EXC_CRASH, EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS},\n      {EXC_CRASH, kMachExceptionCodes | EXCEPTION_DEFAULT, true, KERN_SUCCESS},\n      {EXC_CRASH, kMachExceptionCodes | EXCEPTION_STATE, true, KERN_SUCCESS},\n      {EXC_CRASH,\n       kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,\n       true,\n       KERN_SUCCESS},\n      {EXC_BAD_ACCESS, EXCEPTION_DEFAULT, false, KERN_SUCCESS},\n      {EXC_BAD_INSTRUCTION, EXCEPTION_STATE, false, MACH_RCV_PORT_DIED},\n      {EXC_ARITHMETIC, EXCEPTION_STATE_IDENTITY, false, MACH_RCV_PORT_DIED},\n      {EXC_EMULATION,\n       kMachExceptionCodes | EXCEPTION_DEFAULT,\n       false,\n       KERN_SUCCESS},\n      {EXC_SOFTWARE,\n       kMachExceptionCodes | EXCEPTION_STATE,\n       false,\n       MACH_RCV_PORT_DIED},\n      {EXC_BREAKPOINT,\n       kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,\n       false,\n       MACH_RCV_PORT_DIED},\n      {EXC_SYSCALL, EXCEPTION_DEFAULT, true, KERN_SUCCESS},\n      {EXC_MACH_SYSCALL, EXCEPTION_STATE, true, KERN_SUCCESS},\n      {EXC_RPC_ALERT, EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS},\n      {EXC_RESOURCE,\n       kMachExceptionCodes | EXCEPTION_DEFAULT,\n       true,\n       KERN_SUCCESS},\n      {EXC_GUARD, kMachExceptionCodes | EXCEPTION_STATE, true, KERN_SUCCESS},\n      {EXC_CORPSE_NOTIFY,\n       kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,\n       true,\n       KERN_SUCCESS},\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& test_data = kTestData[index];\n    SCOPED_TRACE(\n        base::StringPrintf(\"index %zu, behavior %d, set_thread_state %s\",\n                           index,\n                           test_data.behavior,\n                           test_data.set_thread_state ? \"true\" : \"false\"));\n\n    EXPECT_EQ(ExcServerSuccessfulReturnValue(test_data.exception,\n                                             test_data.behavior,\n                                             test_data.set_thread_state),\n              test_data.kr);\n  }\n}\n\nTEST(ExcServerVariants, ExcServerCopyState) {\n  static constexpr natural_t old_state[] = {1, 2, 3, 4, 5};\n  natural_t new_state[10] = {};\n\n  constexpr mach_msg_type_number_t old_state_count = std::size(old_state);\n  mach_msg_type_number_t new_state_count = std::size(new_state);\n\n  // EXCEPTION_DEFAULT (with or without MACH_EXCEPTION_CODES) is not\n  // state-carrying. new_state and new_state_count should be untouched.\n  ExcServerCopyState(EXCEPTION_DEFAULT,\n                     old_state,\n                     old_state_count,\n                     new_state,\n                     &new_state_count);\n  EXPECT_EQ(new_state_count, std::size(new_state));\n  for (size_t i = 0; i < std::size(new_state); ++i) {\n    EXPECT_EQ(new_state[i], 0u) << \"i \" << i;\n  }\n\n  ExcServerCopyState(MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT,\n                     old_state,\n                     old_state_count,\n                     new_state,\n                     &new_state_count);\n  EXPECT_EQ(new_state_count, std::size(new_state));\n  for (size_t i = 0; i < std::size(new_state); ++i) {\n    EXPECT_EQ(new_state[i], 0u) << \"i \" << i;\n  }\n\n  // This is a state-carrying exception where old_state_count is small.\n  mach_msg_type_number_t copy_limit = 2;\n  ExcServerCopyState(\n      EXCEPTION_STATE, old_state, copy_limit, new_state, &new_state_count);\n  EXPECT_EQ(new_state_count, copy_limit);\n  for (size_t i = 0; i < copy_limit; ++i) {\n    EXPECT_EQ(new_state[i], old_state[i]) << \"i \" << i;\n  }\n  for (size_t i = copy_limit; i < std::size(new_state); ++i) {\n    EXPECT_EQ(new_state[i], 0u) << \"i \" << i;\n  }\n\n  // This is a state-carrying exception where new_state_count is small.\n  copy_limit = 3;\n  new_state_count = copy_limit;\n  ExcServerCopyState(EXCEPTION_STATE_IDENTITY,\n                     old_state,\n                     old_state_count,\n                     new_state,\n                     &new_state_count);\n  EXPECT_EQ(new_state_count, copy_limit);\n  for (size_t i = 0; i < copy_limit; ++i) {\n    EXPECT_EQ(new_state[i], old_state[i]) << \"i \" << i;\n  }\n  for (size_t i = copy_limit; i < std::size(new_state); ++i) {\n    EXPECT_EQ(new_state[i], 0u) << \"i \" << i;\n  }\n\n  // This is a state-carrying exception where all of old_state is copied to\n  // new_state, which is large enough to receive it and then some.\n  new_state_count = std::size(new_state);\n  ExcServerCopyState(MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,\n                     old_state,\n                     old_state_count,\n                     new_state,\n                     &new_state_count);\n  EXPECT_EQ(new_state_count, old_state_count);\n  for (size_t i = 0; i < std::size(old_state); ++i) {\n    EXPECT_EQ(new_state[i], old_state[i]) << \"i \" << i;\n  }\n  for (size_t i = std::size(old_state); i < std::size(new_state); ++i) {\n    EXPECT_EQ(new_state[i], 0u) << \"i \" << i;\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exception_behaviors.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exception_behaviors.h\"\n\nnamespace crashpad {\n\nbool ExceptionBehaviorHasState(exception_behavior_t behavior) {\n  const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior);\n  return basic_behavior == EXCEPTION_STATE ||\n         basic_behavior == EXCEPTION_STATE_IDENTITY;\n}\n\nbool ExceptionBehaviorHasIdentity(exception_behavior_t behavior) {\n  const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior);\n  return basic_behavior == EXCEPTION_DEFAULT ||\n         basic_behavior == EXCEPTION_STATE_IDENTITY;\n}\n\nbool ExceptionBehaviorHasMachExceptionCodes(exception_behavior_t behavior) {\n  return (behavior & MACH_EXCEPTION_CODES) != 0;\n}\n\nexception_behavior_t ExceptionBehaviorBasic(exception_behavior_t behavior) {\n  return behavior & ~MACH_EXCEPTION_CODES;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exception_behaviors.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_\n#define CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_\n\n#include <mach/mach.h>\n\nnamespace crashpad {\n\n//! \\brief Determines whether \\a behavior indicates an exception behavior that\n//!     carries thread state information.\n//!\n//! When this function returns `true`, an exception message of \\a behavior will\n//! carry thread state information. Its \\a flavor, \\a old_state, \\a\n//! old_state_count, \\a new_state, and \\a new_state_count fields will be valid.\n//! When this function returns `false`, these fields will not be valid.\n//!\n//! Exception behaviors that carry thread state information are\n//! `EXCEPTION_STATE` and `EXCEPTION_STATE_IDENTITY`. `MACH_EXCEPTION_CODES` may\n//! also be set. These behaviors correspond to `exception_raise_state()`,\n//! `exception_raise_state_identity()`, `mach_exception_raise_state()`, and\n//! `mach_exception_raise_state_identity()`.\n//!\n//! \\param[in] behavior An exception behavior value.\n//!\n//! \\return `true` if \\a behavior is `EXCEPTION_STATE` or\n//!     `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES` also\n//!      set.\nbool ExceptionBehaviorHasState(exception_behavior_t behavior);\n\n//! \\brief Determines whether \\a behavior indicates an exception behavior that\n//!     carries thread and task identities.\n//!\n//! When this function returns `true`, an exception message of \\a behavior will\n//! carry thread and task identities in the form of send rights to the thread\n//! and task ports. Its \\a thread and \\a task fields will be valid. When this\n//! function returns `false`, these fields will not be valid.\n//!\n//! Exception behaviors that carry thread and task identity information are\n//! `EXCEPTION_DEFAULT` and `EXCEPTION_STATE_IDENTITY`. `MACH_EXCEPTION_CODES`\n//! may also be set. These behaviors correspond to `exception_raise()`,\n//! `exception_raise_state_identity()`, `mach_exception_raise()`, and\n//! `mach_exception_raise_state_identity()`.\n//!\n//! \\param[in] behavior An exception behavior value.\n//!\n//! \\return `true` if \\a behavior is `EXCEPTION_DEFAULT` or\n//!     `EXCEPTION_STATE_IDENTITY`, possibly with `MACH_EXCEPTION_CODES` also\n//!      set.\nbool ExceptionBehaviorHasIdentity(exception_behavior_t behavior);\n\n//! \\brief Determines whether \\a behavior indicates an exception behavior that\n//!     carries 64-bit exception codes (“Mach exception codes”).\n//!\n//! When this function returns `true`, an exception message of \\a behavior will\n//! carry 64-bit exception codes of type `mach_exception_code_t` in its \\a code\n//! field. When this function returns `false`, the exception message will carry\n//! 32-bit exception codes of type `exception_data_type_t` in its \\a code field.\n//!\n//! Exception behaviors that carry 64-bit exception codes are those that have\n//! `MACH_EXCEPTION_CODES` set. These behaviors correspond to\n//! `mach_exception_raise()`, `mach_exception_raise_state()`, and\n//! `mach_exception_raise_state_identity()`.\n//!\n//! \\param[in] behavior An exception behavior value.\n//!\n//! \\return `true` if `MACH_EXCEPTION_CODES` is set in \\a behavior.\nbool ExceptionBehaviorHasMachExceptionCodes(exception_behavior_t behavior);\n\n//! \\brief Returns the basic behavior value of \\a behavior, its value without\n//!     `MACH_EXCEPTION_CODES` set.\n//!\n//! \\param[in] behavior An exception behavior value.\n//!\n//! \\return `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or\n//!     `EXCEPTION_STATE_IDENTITY`, assuming \\a behavior was a correct exception\n//!     behavior value.\nexception_behavior_t ExceptionBehaviorBasic(exception_behavior_t behavior);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_EXCEPTION_BEHAVIORS_H_\n"
  },
  {
    "path": "util/mach/exception_behaviors_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exception_behaviors.h\"\n\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"util/mach/mach_extensions.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ExceptionBehaviors, ExceptionBehaviors) {\n  static constexpr struct {\n    exception_behavior_t behavior;\n    bool state;\n    bool identity;\n    bool mach_exception_codes;\n    exception_behavior_t basic_behavior;\n  } kTestData[] = {\n      {EXCEPTION_DEFAULT, false, true, false, EXCEPTION_DEFAULT},\n      {EXCEPTION_STATE, true, false, false, EXCEPTION_STATE},\n      {EXCEPTION_STATE_IDENTITY, true, true, false, EXCEPTION_STATE_IDENTITY},\n      {kMachExceptionCodes | EXCEPTION_DEFAULT,\n       false,\n       true,\n       true,\n       EXCEPTION_DEFAULT},\n      {kMachExceptionCodes | EXCEPTION_STATE,\n       true,\n       false,\n       true,\n       EXCEPTION_STATE},\n      {kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,\n       true,\n       true,\n       true,\n       EXCEPTION_STATE_IDENTITY},\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& test_data = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %zu, behavior %d\", index, test_data.behavior));\n\n    EXPECT_EQ(ExceptionBehaviorHasState(test_data.behavior), test_data.state);\n    EXPECT_EQ(ExceptionBehaviorHasIdentity(test_data.behavior),\n              test_data.identity);\n    EXPECT_EQ(ExceptionBehaviorHasMachExceptionCodes(test_data.behavior),\n              test_data.mach_exception_codes);\n    EXPECT_EQ(ExceptionBehaviorBasic(test_data.behavior),\n              test_data.basic_behavior);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exception_ports.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exception_ports.h\"\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n\nnamespace crashpad {\n\nExceptionPorts::ExceptionHandlerVector::ExceptionHandlerVector()\n    : vector_() {\n}\n\nExceptionPorts::ExceptionHandlerVector::~ExceptionHandlerVector() {\n  Deallocate();\n}\n\nvoid ExceptionPorts::ExceptionHandlerVector::clear() {\n  Deallocate();\n  vector_.clear();\n}\n\nvoid ExceptionPorts::ExceptionHandlerVector::Deallocate() {\n  for (ExceptionHandler& exception_handler : vector_) {\n    if (exception_handler.port != MACH_PORT_NULL) {\n      kern_return_t kr =\n          mach_port_deallocate(mach_task_self(), exception_handler.port);\n      MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << \"mach_port_deallocate\";\n    }\n  }\n}\n\nExceptionPorts::ExceptionPorts(TargetType target_type, mach_port_t target_port)\n    : target_port_(target_port), dealloc_target_port_(false) {\n  switch (target_type) {\n    case kTargetTypeHost:\n      get_exception_ports_ = host_get_exception_ports;\n      set_exception_ports_ = host_set_exception_ports;\n      swap_exception_ports_ = host_swap_exception_ports;\n      target_name_ = \"host\";\n      if (target_port_ == HOST_NULL) {\n        target_port_ = mach_host_self();\n        dealloc_target_port_ = true;\n      }\n      break;\n\n    case kTargetTypeTask:\n      get_exception_ports_ = task_get_exception_ports;\n      set_exception_ports_ = task_set_exception_ports;\n      swap_exception_ports_ = task_swap_exception_ports;\n      target_name_ = \"task\";\n      if (target_port_ == TASK_NULL) {\n        target_port_ = mach_task_self();\n        // Don’t deallocate mach_task_self().\n      }\n      break;\n\n    case kTargetTypeThread:\n      get_exception_ports_ = thread_get_exception_ports;\n      set_exception_ports_ = thread_set_exception_ports;\n      swap_exception_ports_ = thread_swap_exception_ports;\n      target_name_ = \"thread\";\n      if (target_port_ == THREAD_NULL) {\n        target_port_ = mach_thread_self();\n        dealloc_target_port_ = true;\n      }\n      break;\n\n    default:\n      NOTREACHED();\n  }\n}\n\nExceptionPorts::~ExceptionPorts() {\n  if (dealloc_target_port_) {\n    kern_return_t kr = mach_port_deallocate(mach_task_self(), target_port_);\n    MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << \"mach_port_deallocate\";\n  }\n}\n\nbool ExceptionPorts::GetExceptionPorts(exception_mask_t mask,\n                                       ExceptionHandlerVector* handlers) const {\n  // <mach/mach_types.defs> says that these arrays have room for 32 elements,\n  // despite EXC_TYPES_COUNT only being as low as 11 (in the 10.6 SDK), and\n  // later operating system versions have defined more exception types. The\n  // generated task_get_exception_ports() in taskUser.c expects there to be room\n  // for 32.\n  constexpr int kMaxPorts = 32;\n\n  // task_get_exception_ports() doesn’t actually use the initial value of\n  // handler_count, but 10.9.4\n  // xnu-2422.110.17/osfmk/man/task_get_exception_ports.html says it does. Humor\n  // the documentation.\n  mach_msg_type_number_t handler_count = kMaxPorts;\n\n  exception_mask_t masks[kMaxPorts];\n  exception_handler_t ports[kMaxPorts];\n  exception_behavior_t behaviors[kMaxPorts];\n  thread_state_flavor_t flavors[kMaxPorts];\n\n  kern_return_t kr = get_exception_ports_(\n      target_port_, mask, masks, &handler_count, ports, behaviors, flavors);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << TargetTypeName() << \"_get_exception_ports\";\n    return false;\n  }\n\n  handlers->clear();\n  for (mach_msg_type_number_t index = 0; index < handler_count; ++index) {\n    if (ports[index] != MACH_PORT_NULL) {\n      ExceptionHandler handler;\n      handler.mask = masks[index];\n      handler.port = ports[index];\n      handler.behavior = behaviors[index];\n      handler.flavor = flavors[index];\n      handlers->push_back(handler);\n    }\n  }\n\n  return true;\n}\n\nbool ExceptionPorts::SetExceptionPort(exception_mask_t mask,\n                                      exception_handler_t port,\n                                      exception_behavior_t behavior,\n                                      thread_state_flavor_t flavor) const {\n  kern_return_t kr =\n      set_exception_ports_(target_port_, mask, port, behavior, flavor);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << TargetTypeName() << \"_set_exception_ports\";\n    return false;\n  }\n\n  return true;\n}\n\nbool ExceptionPorts::SwapExceptionPorts(\n    exception_mask_t mask,\n    exception_handler_t new_port,\n    exception_behavior_t new_behavior,\n    thread_state_flavor_t new_flavor,\n    ExceptionHandlerVector* old_handlers) const {\n  // <mach/mach_types.defs> says that these arrays have room for 32 elements,\n  // despite EXC_TYPES_COUNT only being as low as 11 (in the 10.6 SDK), and\n  // later operating system versions have defined more exception types. The\n  // generated task_swap_exception_ports() in taskUser.c expects there to be\n  // room for 32.\n  constexpr int kMaxPorts = 32;\n\n  // task_swap_exception_ports() doesn’t actually use the initial value of\n  // handler_count, but 10.15.1\n  // xnu-6153.41.3/osfmk/man/task_get_exception_ports.html says it does. Humor\n  // the documentation.\n  mach_msg_type_number_t old_handler_count = kMaxPorts;\n\n  exception_mask_t old_masks[kMaxPorts];\n  exception_handler_t old_ports[kMaxPorts];\n  exception_behavior_t old_behaviors[kMaxPorts];\n  thread_state_flavor_t old_flavors[kMaxPorts];\n\n  kern_return_t kr = swap_exception_ports_(target_port_,\n                                           mask,\n                                           new_port,\n                                           new_behavior,\n                                           new_flavor,\n                                           old_masks,\n                                           &old_handler_count,\n                                           old_ports,\n                                           old_behaviors,\n                                           old_flavors);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << TargetTypeName() << \"_swap_exception_ports\";\n    return false;\n  }\n\n  old_handlers->clear();\n  for (mach_msg_type_number_t index = 0; index < old_handler_count; ++index) {\n    if (old_ports[index] != MACH_PORT_NULL) {\n      ExceptionHandler old_handler;\n      old_handler.mask = old_masks[index];\n      old_handler.port = old_ports[index];\n      old_handler.behavior = old_behaviors[index];\n      old_handler.flavor = old_flavors[index];\n      old_handlers->push_back(old_handler);\n    }\n  }\n\n  return true;\n}\n\nconst char* ExceptionPorts::TargetTypeName() const {\n  return target_name_;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exception_ports.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_\n#define CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_\n\n#include <mach/mach.h>\n\n#include <vector>\n\n\nnamespace crashpad {\n\n//! \\brief A better interface to `*_get_exception_ports()` and\n//!     `*_set_exception_ports()`.\n//!\n//! The same generic interface can be used to operate on host, task, and thread\n//! exception ports. The “get” interface is superior to the system’s native\n//! interface because it keeps related data about a single exception handler\n//! together in one struct, rather than separating it into four parallel arrays.\nclass ExceptionPorts {\n public:\n  //! \\brief Various entities which can have their own exception ports set.\n  enum TargetType {\n    //! \\brief The host exception target.\n    //!\n    //! `host_get_exception_ports()` and `host_set_exception_ports()` will be\n    //! used. If no target port is explicitly provided, `mach_host_self()` will\n    //! be used as the target port. `mach_host_self()` is the only target port\n    //! for this type that is expected to function properly.\n    //!\n    //! \\note Operations on this target type are not expected to succeed as\n    //!     non-root, because `mach_host_self()` doesn’t return the privileged\n    //!     `host_priv` port to non-root users, and this is the target port\n    //!     that’s required for `host_get_exception_ports()` and\n    //!     `host_set_exception_ports()`.\n    kTargetTypeHost = 0,\n\n    //! \\brief A task exception target.\n    //!\n    //! `task_get_exception_ports()` and `task_set_exception_ports()` will be\n    //! used. If no target port is explicitly provided, `mach_task_self()` will\n    //! be used as the target port.\n    kTargetTypeTask,\n\n    //! \\brief A thread exception target.\n    //!\n    //! `thread_get_exception_ports()` and `thread_set_exception_ports()` will\n    //! be used. If no target port is explicitly provided, `mach_thread_self()`\n    //! will be used as the target port.\n    kTargetTypeThread,\n  };\n\n  //! \\brief Information about a registered exception handler.\n  struct ExceptionHandler {\n    //! \\brief A mask specifying the exception types to direct to \\a port,\n    //!     containing `EXC_MASK_*` values.\n    exception_mask_t mask;\n\n    //! \\brief A send right to a Mach port that will handle exceptions of the\n    //!     types indicated in \\a mask.\n    exception_handler_t port;\n\n    //! \\brief The “behavior” that the exception handler at \\a port implements:\n    //!     `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or\n    //!     `EXCEPTION_STATE_IDENTITY`, possibly combined with\n    //!     `MACH_EXCEPTION_CODES`.\n    exception_behavior_t behavior;\n\n    //! \\brief The thread state flavor that the exception handler at \\a port\n    //!     will receive and possibly modify. This member has no effect for \\a\n    //!     \\a behavior values that indicate a “default” behavior.\n    thread_state_flavor_t flavor;\n  };\n\n  //! \\brief Wraps `std::vector<ExceptionHandler>`, providing proper cleanup of\n  //!     the send rights contained in each element’s ExceptionHandler::port.\n  //!\n  //! Upon destruction or clear(), an object of this class will deallocate all\n  //! send rights it contains. Otherwise, it is an interface-compatible drop-in\n  //! replacement for `std::vector<ExceptionHandler>`. Note that non-`const`\n  //! mutators are not provided to avoid accidental Mach right leaks.\n  class ExceptionHandlerVector {\n   public:\n    using VectorType = std::vector<ExceptionHandler>;\n\n    ExceptionHandlerVector();\n\n    ExceptionHandlerVector(const ExceptionHandlerVector&) = delete;\n    ExceptionHandlerVector& operator=(const ExceptionHandlerVector&) = delete;\n\n    ~ExceptionHandlerVector();\n\n    VectorType::const_iterator begin() const { return vector_.begin(); }\n    VectorType::const_iterator end() const { return vector_.end(); }\n    VectorType::size_type size() const { return vector_.size(); }\n    bool empty() const { return vector_.empty(); }\n    VectorType::const_reference operator[](VectorType::size_type index) const {\n      return vector_[index];\n    }\n    void push_back(VectorType::value_type& value) { vector_.push_back(value); }\n    void clear();\n\n   private:\n    void Deallocate();\n\n    VectorType vector_;\n  };\n\n  //! \\brief Constructs an interface object to get or set exception ports on a\n  //!     host, task, or thread port.\n  //!\n  //! \\param[in] target_type The type of target on which the exception ports are\n  //!     to be get or set: #kTargetTypeHost, #kTargetTypeTask, or or\n  //!     #kTargetTypeThread. The correct functions for\n  //!     `*_get_exception_ports()` and `*_set_exception_ports()` will be used.\n  //! \\param[in] target_port The target on which to call\n  //!     `*_get_exception_ports()` or `*_set_exception_ports()`. The target\n  //!     port must be a send right to a port of the type specified in \\a\n  //!     target_type. In this case, ownership of \\a target_port is not given to\n  //!     the new ExceptionPorts object. \\a target_port may also be\n  //!     `HOST_NULL`, `TASK_NULL`, or `THREAD_NULL`, in which case\n  //!     `mach_host_self()`, `mach_task_self()`, or `mach_thread_self()` will\n  //!     be used as the target port depending on the value of \\a target_type.\n  //!     In this case, ownership of the target port will be managed\n  //!     appropriately for \\a target_type.\n  ExceptionPorts(TargetType target_type, mach_port_t target_port);\n\n  ExceptionPorts(const ExceptionPorts&) = delete;\n  ExceptionPorts& operator=(const ExceptionPorts&) = delete;\n\n  ~ExceptionPorts();\n\n  //! \\brief Calls `*_get_exception_ports()` on the target.\n  //!\n  //! \\param[in] mask The exception mask, containing the `EXC_MASK_*` values to\n  //!     be looked up and returned in \\a handlers.\n  //! \\param[out] handlers The exception handlers registered for \\a target_port\n  //!     to handle exceptions indicated in \\a mask. If no execption port is\n  //!     registered for a bit in \\a mask, \\a handlers will not contain an entry\n  //!     corresponding to that bit. This is a departure from the\n  //!     `*_get_exception_ports()` functions, which may return a handler whose\n  //!     port is set to `EXCEPTION_PORT_NULL` in this case. On failure, this\n  //!     argument is untouched.\n  //!\n  //! \\return `true` if `*_get_exception_ports()` returned `KERN_SUCCESS`, with\n  //!     \\a handlers set appropriately. `false` otherwise, with an appropriate\n  //!     message logged.\n  bool GetExceptionPorts(exception_mask_t mask,\n                         ExceptionHandlerVector* handlers) const;\n\n  //! \\brief Calls `*_set_exception_ports()` on the target.\n  //!\n  //! \\param[in] mask A mask specifying the exception types to direct to \\a\n  //!     port, containing `EXC_MASK_*` values.\n  //! \\param[in] port A send right to a Mach port that will handle exceptions\n  //!     sustained by \\a target_port of the types indicated in \\a mask. The\n  //!     send right is copied, not consumed, by this call.\n  //! \\param[in] behavior The “behavior” that the exception handler at \\a port\n  //!     implements: `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or\n  //!     `EXCEPTION_STATE_IDENTITY`, possibly combined with\n  //!     `MACH_EXCEPTION_CODES`.\n  //! \\param[in] flavor The thread state flavor that the exception handler at \\a\n  //!     port expects to receive and possibly modify. This argument has no\n  //!     effect for \\a behavior values that indicate a “default” behavior.\n  //!\n  //! \\return `true` if `*_set_exception_ports()` returned `KERN_SUCCESS`.\n  //!     `false` otherwise, with an appropriate message logged.\n  bool SetExceptionPort(exception_mask_t mask,\n                        exception_handler_t port,\n                        exception_behavior_t behavior,\n                        thread_state_flavor_t flavor) const;\n\n  //! \\brief Calls `*_swap_exception_ports()` on the target.\n  //!\n  //! \\param[in] mask A mask specifying the exception types to direct to \\a\n  //!     port, containing `EXC_MASK_*` values.\n  //! \\param[in] new_port A send right to a Mach port that will handle\n  //!     exceptions sustained by \\a target_port of the types indicated in \\a\n  //!     mask. The send right is copied, not consumed, by this call.\n  //! \\param[in] new_behavior The “behavior” that the exception handler at \\a\n  //!     port implements: `EXCEPTION_DEFAULT`, `EXCEPTION_STATE`, or\n  //!     `EXCEPTION_STATE_IDENTITY`, possibly combined with\n  //!     `MACH_EXCEPTION_CODES`.\n  //! \\param[in] new_flavor The thread state flavor that the exception handler\n  //!     at \\a port expects to receive and possibly modify. This argument has\n  //!     no effect for \\a new_behavior values that indicate a “default”\n  //!     behavior.\n  //! \\param[out] old_handlers The exception handlers registered for \\a\n  //!     target_port to handle exceptions indicated in \\a mask. If no execption\n  //!     port is registered for a bit in \\a mask, \\a old_handlers will not\n  //!     contain an entry corresponding to that bit. This is a departure from\n  //!     the `*_get_exception_ports()` functions, which may return a handler\n  //!     whose port is set to `EXCEPTION_PORT_NULL` in this case. On failure,\n  //!     this argument is untouched.\n  //!\n  //! \\return `true` if `*_swap_exception_ports()` returned `KERN_SUCCESS`, with\n  //!     \\a old_handlers set appropriately. . `false` otherwise, with an\n  //!     appropriate message logged.\n  bool SwapExceptionPorts(exception_mask_t mask,\n                          exception_handler_t new_port,\n                          exception_behavior_t new_behavior,\n                          thread_state_flavor_t new_flavor,\n                          ExceptionHandlerVector* old_handlers) const;\n\n  //! \\brief Returns a string identifying the target type.\n  //!\n  //! \\return `\"host\"`, `\"task\"`, or `\"thread\"`, as appropriate.\n  const char* TargetTypeName() const;\n\n private:\n  using GetExceptionPortsType = kern_return_t(*)(mach_port_t,\n                                                 exception_mask_t,\n                                                 exception_mask_array_t,\n                                                 mach_msg_type_number_t*,\n                                                 exception_handler_array_t,\n                                                 exception_behavior_array_t,\n                                                 exception_flavor_array_t);\n\n  using SetExceptionPortsType = kern_return_t(*)(mach_port_t,\n                                                 exception_mask_t,\n                                                 exception_handler_t,\n                                                 exception_behavior_t,\n                                                 thread_state_flavor_t);\n\n  using SwapExceptionPortsType = kern_return_t(*)(mach_port_t,\n                                                  exception_mask_t,\n                                                  exception_handler_t,\n                                                  exception_behavior_t,\n                                                  thread_state_flavor_t,\n                                                  exception_mask_array_t,\n                                                  mach_msg_type_number_t*,\n                                                  exception_handler_array_t,\n                                                  exception_behavior_array_t,\n                                                  exception_flavor_array_t);\n\n  GetExceptionPortsType get_exception_ports_;\n  SetExceptionPortsType set_exception_ports_;\n  SwapExceptionPortsType swap_exception_ports_;\n  const char* target_name_;\n  mach_port_t target_port_;\n\n  // If true, target_port_ will be deallocated in the destructor. This will\n  // always be false when the user provides a non-null target_port to the\n  // constructor. It will also be false when target_type is kTargetTypeTask,\n  // even with a TASK_NULL target_port, because it is incorrect to deallocate\n  // the result of mach_task_self().\n  bool dealloc_target_port_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_EXCEPTION_PORTS_H_\n"
  },
  {
    "path": "util/mach/exception_ports_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exception_ports.h\"\n\n#include <mach/mach.h>\n#include <pthread.h>\n#include <signal.h>\n#include <unistd.h>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/check.h\"\n#include \"base/notreached.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"test/mac/mach_multiprocess.h\"\n#include \"util/file/file_io.h\"\n#include \"util/mach/exc_server_variants.h\"\n#include \"util/mach/exception_types.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n#include \"util/misc/scoped_forbid_return.h\"\n#include \"util/synchronization/semaphore.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Calls GetExceptionPorts() on its |exception_ports| argument to look up the\n// EXC_MASK_CRASH handler. If |expect_port| is not MACH_PORT_NULL, it expects to\n// find a handler for this mask whose port matches |expect_port| and whose\n// behavior matches |expect_behavior| exactly. In this case, if\n// |expect_behavior| is a state-carrying behavior, the looked-up thread state\n// flavor is expected to be MACHINE_THREAD_STATE, otherwise, it is expected to\n// be THREAD_STATE_NONE. If |expect_port| is MACH_PORT_NULL, no handler for\n// EXC_MASK_CRASH is expected to be found.\n//\n// A second GetExceptionPorts() lookup is also performed on a wider exception\n// mask, EXC_MASK_ALL | EXC_MASK_CRASH. The EXC_MASK_CRASH handler’s existence\n// and properties from this second lookup are validated in the same way.\n//\n// This function uses Google Test EXPECT_* and ASSERT_* macros to perform its\n// validation.\nvoid TestGetExceptionPorts(const ExceptionPorts& exception_ports,\n                           mach_port_t expect_port,\n                           exception_behavior_t expect_behavior) {\n  constexpr exception_mask_t kExceptionMask = EXC_MASK_CRASH;\n\n  thread_state_flavor_t expect_flavor = (expect_behavior == EXCEPTION_DEFAULT)\n                                            ? THREAD_STATE_NONE\n                                            : MACHINE_THREAD_STATE;\n\n  ExceptionPorts::ExceptionHandlerVector crash_handler;\n  ASSERT_TRUE(\n      exception_ports.GetExceptionPorts(kExceptionMask, &crash_handler));\n\n  if (expect_port != MACH_PORT_NULL) {\n    ASSERT_EQ(crash_handler.size(), 1u);\n\n    EXPECT_EQ(crash_handler[0].mask, kExceptionMask);\n    EXPECT_EQ(crash_handler[0].port, expect_port);\n    EXPECT_EQ(crash_handler[0].behavior, expect_behavior);\n    EXPECT_EQ(crash_handler[0].flavor, expect_flavor);\n  } else {\n    EXPECT_TRUE(crash_handler.empty());\n  }\n\n  ExceptionPorts::ExceptionHandlerVector handlers;\n  ASSERT_TRUE(exception_ports.GetExceptionPorts(ExcMaskValid(), &handlers));\n\n  EXPECT_GE(handlers.size(), crash_handler.size());\n  bool found = false;\n  for (const ExceptionPorts::ExceptionHandler& handler : handlers) {\n    if ((handler.mask & kExceptionMask) != 0) {\n      EXPECT_FALSE(found);\n      found = true;\n      EXPECT_EQ(handler.port, expect_port);\n      EXPECT_EQ(handler.behavior, expect_behavior);\n      EXPECT_EQ(handler.flavor, expect_flavor);\n    }\n  }\n\n  if (expect_port != MACH_PORT_NULL) {\n    EXPECT_TRUE(found);\n  } else {\n    EXPECT_FALSE(found);\n  }\n}\n\nclass TestExceptionPorts : public MachMultiprocess,\n                           public UniversalMachExcServer::Interface {\n public:\n  // Whether to call SetExceptionPort or SwapExceptionPorts.\n  enum SetOrSwap {\n    kSetExceptionPort = 0,\n    kSwapExceptionPorts,\n  };\n\n  // Which entities to set exception ports for.\n  enum SetOn {\n    kSetOnTaskOnly = 0,\n    kSetOnTaskAndThreads,\n  };\n\n  // Where to call ExceptionPorts::SetExceptionPort() from.\n  enum SetType {\n    // Call it from the child process on itself.\n    kSetInProcess = 0,\n\n    // Call it from the parent process on the child.\n    kSetOutOfProcess,\n  };\n\n  // Which thread in the child process is expected to crash.\n  enum WhoCrashes {\n    kNobodyCrashes = 0,\n    kMainThreadCrashes,\n    kOtherThreadCrashes,\n  };\n\n  TestExceptionPorts(SetOrSwap set_or_swap,\n                     SetOn set_on,\n                     SetType set_type,\n                     WhoCrashes who_crashes)\n      : MachMultiprocess(),\n        UniversalMachExcServer::Interface(),\n        set_or_swap_(set_or_swap),\n        set_on_(set_on),\n        set_type_(set_type),\n        who_crashes_(who_crashes),\n        handled_(false) {\n    if (who_crashes_ != kNobodyCrashes) {\n      SetExpectedChildTerminationBuiltinTrap();\n    }\n  }\n\n  TestExceptionPorts(const TestExceptionPorts&) = delete;\n  TestExceptionPorts& operator=(const TestExceptionPorts&) = delete;\n\n  SetOrSwap set_or_swap() const { return set_or_swap_; }\n  SetOn set_on() const { return set_on_; }\n  SetType set_type() const { return set_type_; }\n  WhoCrashes who_crashes() const { return who_crashes_; }\n\n  // UniversalMachExcServer::Interface:\n\n  virtual kern_return_t CatchMachException(\n      exception_behavior_t behavior,\n      exception_handler_t exception_port,\n      thread_t thread,\n      task_t task,\n      exception_type_t exception,\n      const mach_exception_data_type_t* code,\n      mach_msg_type_number_t code_count,\n      thread_state_flavor_t* flavor,\n      ConstThreadState old_state,\n      mach_msg_type_number_t old_state_count,\n      thread_state_t new_state,\n      mach_msg_type_number_t* new_state_count,\n      const mach_msg_trailer_t* trailer,\n      bool* destroy_complex_request) override {\n    *destroy_complex_request = true;\n\n    EXPECT_FALSE(handled_);\n    handled_ = true;\n\n    // To be able to distinguish between which handler was actually triggered,\n    // the different handlers are registered with different behavior values.\n    exception_behavior_t expect_behavior;\n    if (set_on_ == kSetOnTaskOnly) {\n      expect_behavior = EXCEPTION_DEFAULT;\n    } else if (who_crashes_ == kMainThreadCrashes) {\n      expect_behavior = EXCEPTION_STATE;\n    } else if (who_crashes_ == kOtherThreadCrashes) {\n      expect_behavior = EXCEPTION_STATE_IDENTITY;\n    } else {\n      NOTREACHED();\n    }\n\n    EXPECT_EQ(behavior, expect_behavior);\n\n    EXPECT_EQ(exception_port, LocalPort());\n\n    EXPECT_EQ(exception, EXC_CRASH);\n    EXPECT_EQ(code_count, 2u);\n\n    // The exception and code_count checks above would ideally use ASSERT_EQ so\n    // that the next conditional would not be necessary, but ASSERT_* requires a\n    // function returning type void, and the interface dictates otherwise here.\n    if (exception == EXC_CRASH && code_count >= 1) {\n      int signal;\n      ExcCrashRecoverOriginalException(code[0], nullptr, &signal);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n      constexpr int kBuiltinTrapSignal = SIGILL;\n#elif defined(ARCH_CPU_ARM64)\n      constexpr int kBuiltinTrapSignal = SIGTRAP;\n#else\n#error Port\n#endif\n      EXPECT_EQ(signal, kBuiltinTrapSignal);\n    }\n\n    EXPECT_EQ(AuditPIDFromMachMessageTrailer(trailer), 0);\n\n    ExcServerCopyState(\n        behavior, old_state, old_state_count, new_state, new_state_count);\n    return ExcServerSuccessfulReturnValue(exception, behavior, false);\n  }\n\n private:\n  class Child {\n   public:\n    explicit Child(TestExceptionPorts* test_exception_ports)\n        : test_exception_ports_(test_exception_ports),\n          thread_(),\n          init_semaphore_(0),\n          crash_semaphore_(0) {}\n\n    Child(const Child&) = delete;\n    Child& operator=(const Child&) = delete;\n\n    ~Child() {}\n\n    void Run() {\n      ExceptionPorts self_task_ports(ExceptionPorts::kTargetTypeTask,\n                                     TASK_NULL);\n      ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread,\n                                       THREAD_NULL);\n\n      mach_port_t remote_port = test_exception_ports_->RemotePort();\n\n      // Set the task’s and this thread’s exception ports, if appropriate.\n      if (test_exception_ports_->set_type() == kSetInProcess) {\n        switch (test_exception_ports_->set_or_swap()) {\n          case kSetExceptionPort: {\n            ASSERT_TRUE(self_task_ports.SetExceptionPort(EXC_MASK_CRASH,\n                                                         remote_port,\n                                                         EXCEPTION_DEFAULT,\n                                                         THREAD_STATE_NONE));\n\n            if (test_exception_ports_->set_on() == kSetOnTaskAndThreads) {\n              ASSERT_TRUE(\n                  self_thread_ports.SetExceptionPort(EXC_MASK_CRASH,\n                                                     remote_port,\n                                                     EXCEPTION_STATE,\n                                                     MACHINE_THREAD_STATE));\n            }\n\n            break;\n          }\n\n          case kSwapExceptionPorts: {\n            ExceptionPorts::ExceptionHandlerVector old_handlers;\n            ASSERT_TRUE(self_task_ports.SwapExceptionPorts(EXC_MASK_CRASH,\n                                                           remote_port,\n                                                           EXCEPTION_DEFAULT,\n                                                           THREAD_STATE_NONE,\n                                                           &old_handlers));\n\n            if (test_exception_ports_->set_on() == kSetOnTaskAndThreads) {\n              ASSERT_TRUE(\n                  self_thread_ports.SwapExceptionPorts(EXC_MASK_CRASH,\n                                                       remote_port,\n                                                       EXCEPTION_STATE,\n                                                       MACHINE_THREAD_STATE,\n                                                       &old_handlers));\n            }\n\n            break;\n          }\n\n          default: {\n            NOTREACHED();\n          }\n        }\n      }\n\n      int rv_int = pthread_create(&thread_, nullptr, ThreadMainThunk, this);\n      ASSERT_EQ(rv_int, 0);\n\n      // Wait for the new thread to be ready.\n      init_semaphore_.Wait();\n\n      // Tell the parent process that everything is set up.\n      char c = '\\0';\n      CheckedWriteFile(test_exception_ports_->WritePipeHandle(), &c, 1);\n\n      // Wait for the parent process to say that its end is set up.\n      CheckedReadFileExactly(test_exception_ports_->ReadPipeHandle(), &c, 1);\n      EXPECT_EQ(c, '\\0');\n\n      // Regardless of where ExceptionPorts::SetExceptionPort() ran,\n      // ExceptionPorts::GetExceptionPorts() can always be tested in-process.\n      {\n        SCOPED_TRACE(\"task\");\n        TestGetExceptionPorts(self_task_ports, remote_port, EXCEPTION_DEFAULT);\n      }\n\n      {\n        SCOPED_TRACE(\"main_thread\");\n        mach_port_t thread_handler =\n            (test_exception_ports_->set_on() == kSetOnTaskAndThreads)\n                ? remote_port\n                : MACH_PORT_NULL;\n        TestGetExceptionPorts(\n            self_thread_ports, thread_handler, EXCEPTION_STATE);\n      }\n\n      // Let the other thread know it’s safe to proceed.\n      crash_semaphore_.Signal();\n\n      // If this thread is the one that crashes, do it.\n      if (test_exception_ports_->who_crashes() == kMainThreadCrashes) {\n        Crash();\n      }\n\n      // Reap the other thread.\n      rv_int = pthread_join(thread_, nullptr);\n      ASSERT_EQ(rv_int, 0);\n    }\n\n   private:\n    // Calls ThreadMain().\n    static void* ThreadMainThunk(void* argument) {\n      Child* self = reinterpret_cast<Child*>(argument);\n      return self->ThreadMain();\n    }\n\n    // Runs the “other” thread.\n    void* ThreadMain() {\n      ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread,\n                                       THREAD_NULL);\n      mach_port_t remote_port = test_exception_ports_->RemotePort();\n\n      // Set this thread’s exception handler, if appropriate.\n      if (test_exception_ports_->set_type() == kSetInProcess &&\n          test_exception_ports_->set_on() == kSetOnTaskAndThreads) {\n        switch (test_exception_ports_->set_or_swap()) {\n          case kSetExceptionPort: {\n            CHECK(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH,\n                                                     remote_port,\n                                                     EXCEPTION_STATE_IDENTITY,\n                                                     MACHINE_THREAD_STATE));\n            break;\n          }\n          case kSwapExceptionPorts: {\n            ExceptionPorts::ExceptionHandlerVector old_handlers;\n            CHECK(self_thread_ports.SwapExceptionPorts(EXC_MASK_CRASH,\n                                                       remote_port,\n                                                       EXCEPTION_STATE_IDENTITY,\n                                                       MACHINE_THREAD_STATE,\n                                                       &old_handlers));\n            break;\n          }\n          default: {\n            NOTREACHED();\n          }\n        }\n      }\n\n      // Let the main thread know that this thread is ready.\n      init_semaphore_.Signal();\n\n      // Wait for the main thread to signal that it’s safe to proceed.\n      crash_semaphore_.Wait();\n\n      // Regardless of where ExceptionPorts::SetExceptionPort() ran,\n      // ExceptionPorts::GetExceptionPorts() can always be tested in-process.\n      {\n        SCOPED_TRACE(\"other_thread\");\n        mach_port_t thread_handler =\n            (test_exception_ports_->set_on() == kSetOnTaskAndThreads)\n                ? remote_port\n                : MACH_PORT_NULL;\n        TestGetExceptionPorts(\n            self_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY);\n      }\n\n      // If this thread is the one that crashes, do it.\n      if (test_exception_ports_->who_crashes() == kOtherThreadCrashes) {\n        Crash();\n      }\n\n      return nullptr;\n    }\n\n    static void Crash() {\n      __builtin_trap();\n    }\n\n    // The parent object.\n    TestExceptionPorts* test_exception_ports_;  // weak\n\n    // The “other” thread.\n    pthread_t thread_;\n\n    // The main thread waits on this for the other thread to start up and\n    // perform its own initialization.\n    Semaphore init_semaphore_;\n\n    // The child thread waits on this for the parent thread to indicate that the\n    // child can test its exception ports and possibly crash, as appropriate.\n    Semaphore crash_semaphore_;\n  };\n\n  // MachMultiprocess:\n\n  void MachMultiprocessParent() override {\n    // Wait for the child process to be ready. It needs to have all of its\n    // threads set up before proceeding if in kSetOutOfProcess mode.\n    char c;\n    CheckedReadFileExactly(ReadPipeHandle(), &c, 1);\n    EXPECT_EQ(c, '\\0');\n\n    mach_port_t local_port = LocalPort();\n\n    // Get an ExceptionPorts object for the task and each of its threads.\n    ExceptionPorts task_ports(ExceptionPorts::kTargetTypeTask, ChildTask());\n    EXPECT_STREQ(\"task\", task_ports.TargetTypeName());\n\n    // Hopefully the threads returned by task_threads() are in order, with the\n    // main thread first and the other thread second. This is currently always\n    // the case, although nothing guarantees that it will remain so.\n    thread_act_array_t threads;\n    mach_msg_type_number_t thread_count = 0;\n    kern_return_t kr = task_threads(ChildTask(), &threads, &thread_count);\n    ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"task_threads\");\n\n    ScopedForbidReturn threads_need_owners;\n    ASSERT_EQ(thread_count, 2u);\n    base::apple::ScopedMachSendRight main_thread(threads[0]);\n    base::apple::ScopedMachSendRight other_thread(threads[1]);\n    threads_need_owners.Disarm();\n\n    ExceptionPorts main_thread_ports(ExceptionPorts::kTargetTypeThread,\n                                     main_thread.get());\n    ExceptionPorts other_thread_ports(ExceptionPorts::kTargetTypeThread,\n                                      other_thread.get());\n    EXPECT_STREQ(\"thread\", main_thread_ports.TargetTypeName());\n    EXPECT_STREQ(\"thread\", other_thread_ports.TargetTypeName());\n\n    if (set_type_ == kSetOutOfProcess) {\n      // Test ExceptionPorts::SetExceptionPort() being called from\n      // out-of-process.\n      //\n      // local_port is only a receive right, but a send right is needed for\n      // ExceptionPorts::SetExceptionPort(). Make a send right, which can be\n      // deallocated once the calls to ExceptionPorts::SetExceptionPort() are\n      // done.\n      kr = mach_port_insert_right(\n          mach_task_self(), local_port, local_port, MACH_MSG_TYPE_MAKE_SEND);\n      ASSERT_EQ(kr, KERN_SUCCESS)\n          << MachErrorMessage(kr, \"mach_port_insert_right\");\n      base::apple::ScopedMachSendRight send_owner(local_port);\n\n      switch (set_or_swap_) {\n        case kSetExceptionPort: {\n          ASSERT_TRUE(task_ports.SetExceptionPort(EXC_MASK_CRASH,\n                                                  local_port,\n                                                  EXCEPTION_DEFAULT,\n                                                  THREAD_STATE_NONE));\n\n          if (set_on_ == kSetOnTaskAndThreads) {\n            ASSERT_TRUE(\n                main_thread_ports.SetExceptionPort(EXC_MASK_CRASH,\n                                                   local_port,\n                                                   EXCEPTION_STATE,\n                                                   MACHINE_THREAD_STATE));\n\n            ASSERT_TRUE(\n                other_thread_ports.SetExceptionPort(EXC_MASK_CRASH,\n                                                    local_port,\n                                                    EXCEPTION_STATE_IDENTITY,\n                                                    MACHINE_THREAD_STATE));\n          }\n          break;\n        }\n\n        case kSwapExceptionPorts: {\n          ExceptionPorts::ExceptionHandlerVector old_handlers;\n\n          ASSERT_TRUE(task_ports.SwapExceptionPorts(EXC_MASK_CRASH,\n                                                    local_port,\n                                                    EXCEPTION_DEFAULT,\n                                                    THREAD_STATE_NONE,\n                                                    &old_handlers));\n\n          if (set_on_ == kSetOnTaskAndThreads) {\n            ASSERT_TRUE(\n                main_thread_ports.SwapExceptionPorts(EXC_MASK_CRASH,\n                                                     local_port,\n                                                     EXCEPTION_STATE,\n                                                     MACHINE_THREAD_STATE,\n                                                     &old_handlers));\n\n            ASSERT_TRUE(\n                other_thread_ports.SwapExceptionPorts(EXC_MASK_CRASH,\n                                                      local_port,\n                                                      EXCEPTION_STATE_IDENTITY,\n                                                      MACHINE_THREAD_STATE,\n                                                      &old_handlers));\n          }\n          break;\n        }\n\n        default: {\n          NOTREACHED();\n        }\n      }\n    }\n\n    // Regardless of where ExceptionPorts::SetExceptionPort() ran,\n    // ExceptionPorts::GetExceptionPorts() can always be tested out-of-process.\n    {\n      SCOPED_TRACE(\"task\");\n      TestGetExceptionPorts(task_ports, local_port, EXCEPTION_DEFAULT);\n    }\n\n    mach_port_t thread_handler =\n        (set_on_ == kSetOnTaskAndThreads) ? local_port : MACH_PORT_NULL;\n\n    {\n      SCOPED_TRACE(\"main_thread\");\n      TestGetExceptionPorts(main_thread_ports, thread_handler, EXCEPTION_STATE);\n    }\n\n    {\n      SCOPED_TRACE(\"other_thread\");\n      TestGetExceptionPorts(\n          other_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY);\n    }\n\n    // Let the child process know that everything in the parent process is set\n    // up.\n    c = '\\0';\n    CheckedWriteFile(WritePipeHandle(), &c, 1);\n\n    if (who_crashes_ != kNobodyCrashes) {\n      UniversalMachExcServer universal_mach_exc_server(this);\n\n      constexpr mach_msg_timeout_t kTimeoutMs = 50;\n      kr = MachMessageServer::Run(&universal_mach_exc_server,\n                                  local_port,\n                                  kMachMessageReceiveAuditTrailer,\n                                  MachMessageServer::kOneShot,\n                                  MachMessageServer::kReceiveLargeError,\n                                  kTimeoutMs);\n      EXPECT_EQ(kr, KERN_SUCCESS)\n          << MachErrorMessage(kr, \"MachMessageServer::Run\");\n\n      EXPECT_TRUE(handled_);\n    }\n\n    // Wait for the child process to exit or terminate, as indicated by it\n    // closing its pipe. This keeps LocalPort() alive in the child as\n    // RemotePort(), for the child’s use in its TestGetExceptionPorts().\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n\n  void MachMultiprocessChild() override {\n    Child child(this);\n    child.Run();\n  }\n\n  SetOrSwap set_or_swap_;\n  SetOn set_on_;\n  SetType set_type_;\n  WhoCrashes who_crashes_;\n\n  // true if an exception message was handled.\n  bool handled_;\n};\n\nTEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_NoCrash) {\n  TestExceptionPorts test_exception_ports(TestExceptionPorts::kSetExceptionPort,\n                                          TestExceptionPorts::kSetOnTaskOnly,\n                                          TestExceptionPorts::kSetInProcess,\n                                          TestExceptionPorts::kNobodyCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_MainThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kMainThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SetInProcess_OtherThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kOtherThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_NoCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kNobodyCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetInProcess_MainThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kMainThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts,\n     TaskAndThreadExceptionPorts_SetInProcess_OtherThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kOtherThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_NoCrash) {\n  TestExceptionPorts test_exception_ports(TestExceptionPorts::kSetExceptionPort,\n                                          TestExceptionPorts::kSetOnTaskOnly,\n                                          TestExceptionPorts::kSetOutOfProcess,\n                                          TestExceptionPorts::kNobodyCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_MainThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kMainThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SetOutOfProcess_OtherThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kOtherThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskAndThreadExceptionPorts_SetOutOfProcess_NoCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kNobodyCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts,\n     TaskAndThreadExceptionPorts_SetOutOfProcess_MainThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kMainThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts,\n     TaskAndThreadExceptionPorts_SetOutOfProcess_OtherThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSetExceptionPort,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kOtherThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SwapInProcess_NoCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kNobodyCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SwapInProcess_MainThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kMainThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SwapInProcess_OtherThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kOtherThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskAndThreadExceptionPorts_SwapInProcess_NoCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kNobodyCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts,\n     TaskAndThreadExceptionPorts_SwapInProcess_MainThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kMainThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts,\n     TaskAndThreadExceptionPorts_SwapInProcess_OtherThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetInProcess,\n      TestExceptionPorts::kOtherThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SwapOutOfProcess_NoCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kNobodyCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SwapOutOfProcess_MainThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kMainThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskExceptionPorts_SwapOutOfProcess_OtherThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskOnly,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kOtherThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, TaskAndThreadExceptionPorts_SwapOutOfProcess_NoCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kNobodyCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts,\n     TaskAndThreadExceptionPorts_SwapOutOfProcess_MainThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kMainThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts,\n     TaskAndThreadExceptionPorts_SwapOutOfProcess_OtherThreadCrash) {\n  TestExceptionPorts test_exception_ports(\n      TestExceptionPorts::kSwapExceptionPorts,\n      TestExceptionPorts::kSetOnTaskAndThreads,\n      TestExceptionPorts::kSetOutOfProcess,\n      TestExceptionPorts::kOtherThreadCrashes);\n  test_exception_ports.Run();\n}\n\nTEST(ExceptionPorts, HostExceptionPorts) {\n  // ExceptionPorts isn’t expected to work as non-root. Just do a quick test to\n  // make sure that TargetTypeName() returns the right string, and that the\n  // underlying host_get_exception_ports() function appears to be called by\n  // looking for a KERN_INVALID_ARGUMENT return value. Or, on the off chance\n  // that the test is being run as root, just look for KERN_SUCCESS.\n  // host_set_exception_ports() is not tested, because if the test were running\n  // as root and the call succeeded, it would have global effects.\n\n  const bool expect_success = geteuid() == 0;\n\n  base::apple::ScopedMachSendRight host(mach_host_self());\n  ExceptionPorts explicit_host_ports(ExceptionPorts::kTargetTypeHost,\n                                     host.get());\n  EXPECT_STREQ(\"host\", explicit_host_ports.TargetTypeName());\n\n  ExceptionPorts::ExceptionHandlerVector explicit_handlers;\n  bool rv =\n      explicit_host_ports.GetExceptionPorts(ExcMaskValid(), &explicit_handlers);\n  EXPECT_EQ(rv, expect_success);\n\n  ExceptionPorts implicit_host_ports(ExceptionPorts::kTargetTypeHost,\n                                     HOST_NULL);\n  EXPECT_STREQ(\"host\", implicit_host_ports.TargetTypeName());\n\n  ExceptionPorts::ExceptionHandlerVector implicit_handlers;\n  rv =\n      implicit_host_ports.GetExceptionPorts(ExcMaskValid(), &implicit_handlers);\n  EXPECT_EQ(rv, expect_success);\n\n  EXPECT_EQ(implicit_handlers.size(), explicit_handlers.size());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exception_types.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exception_types.h\"\n\n#include <Availability.h>\n#include <dlfcn.h>\n#include <errno.h>\n#include <kern/exc_resource.h>\n#include <libproc.h>\n#include <strings.h>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/no_cfi_icall.h\"\n#include \"util/numeric/in_range_cast.h\"\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_9\n\nextern \"C\" {\n\n// proc_get_wakemon_params() is present in the OS X 10.9 SDK, but no declaration\n// is provided. This provides a declaration and marks it for weak import if the\n// deployment target is below 10.9.\nint proc_get_wakemon_params(pid_t pid, int* rate_hz, int* flags)\n    __OSX_AVAILABLE_STARTING(__MAC_10_9, __IPHONE_7_0);\n\n// Redeclare the method without the availability annotation to suppress the\n// -Wpartial-availability warning.\nint proc_get_wakemon_params(pid_t pid, int* rate_hz, int* flags);\n\n}  // extern \"C\"\n\n#else\n\nnamespace {\n\nusing ProcGetWakemonParamsType = int (*)(pid_t, int*, int*);\n\n// The SDK doesn’t have proc_get_wakemon_params() to link against, even with\n// weak import. This function returns a function pointer to it if it exists at\n// runtime, or nullptr if it doesn’t. proc_get_wakemon_params() is looked up in\n// the same module that provides proc_pidinfo().\nProcGetWakemonParamsType GetProcGetWakemonParams() {\n  Dl_info dl_info;\n  if (!dladdr(reinterpret_cast<void*>(proc_pidinfo), &dl_info)) {\n    return nullptr;\n  }\n\n  void* dl_handle =\n      dlopen(dl_info.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);\n  if (!dl_handle) {\n    return nullptr;\n  }\n\n  ProcGetWakemonParamsType proc_get_wakemon_params =\n      reinterpret_cast<ProcGetWakemonParamsType>(\n          dlsym(dl_handle, \"proc_get_wakemon_params\"));\n  return proc_get_wakemon_params;\n}\n\n}  // namespace\n\n#endif\n\nnamespace {\n\n// Wraps proc_get_wakemon_params(), calling it if the system provides it. It’s\n// present on OS X 10.9 and later. If it’s not available, sets errno to ENOSYS\n// and returns -1.\nint ProcGetWakemonParams(pid_t pid, int* rate_hz, int* flags) {\n#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_9\n  // proc_get_wakemon_params() isn’t in the SDK. Look it up dynamically.\n  static crashpad::NoCfiIcall<ProcGetWakemonParamsType> proc_get_wakemon_params(\n      GetProcGetWakemonParams());\n#endif\n\n#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9\n  // proc_get_wakemon_params() is definitely available if the deployment target\n  // is 10.9 or newer.\n  if (!proc_get_wakemon_params) {\n    errno = ENOSYS;\n    return -1;\n  }\n#endif\n\n  return proc_get_wakemon_params(pid, rate_hz, flags);\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nexception_type_t ExcCrashRecoverOriginalException(\n    mach_exception_code_t code_0,\n    mach_exception_code_t* original_code_0,\n    int* signal) {\n  // 10.9.4 xnu-2422.110.17/bsd/kern/kern_exit.c proc_prepareexit() sets code[0]\n  // based on the signal value, original exception type, and low 20 bits of the\n  // original code[0] before calling xnu-2422.110.17/osfmk/kern/exception.c\n  // task_exception_notify() to raise an EXC_CRASH.\n  //\n  // The list of core-generating signals (as used in proc_prepareexit()’s call\n  // to hassigprop()) is in 10.9.4 xnu-2422.110.17/bsd/sys/signalvar.h sigprop:\n  // entires with SA_CORE are in the set. These signals are SIGQUIT, SIGILL,\n  // SIGTRAP, SIGABRT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV, and SIGSYS. Processes\n  // killed for code-signing reasons will be killed by SIGKILL and are also\n  // eligible for EXC_CRASH handling, but processes killed by SIGKILL for other\n  // reasons are not.\n  if (signal) {\n    *signal = (code_0 >> 24) & 0xff;\n  }\n\n  if (original_code_0) {\n    *original_code_0 = code_0 & 0xfffff;\n  }\n\n  return (code_0 >> 20) & 0xf;\n}\n\nbool ExcCrashCouldContainException(exception_type_t exception) {\n  // EXC_CRASH should never be wrapped in another EXC_CRASH.\n  //\n  // EXC_RESOURCE is a software exceptions that is never wrapped in EXC_CRASH.\n  // The same applies to EXC_GUARD below macOS 13. The only time EXC_CRASH is\n  // generated is for processes exiting due to an unhandled core-generating\n  // signal or being killed by SIGKILL for code-signing reasons. Neither of\n  // these apply to EXC_RESOURCE or EXC_GUARD. See 10.10\n  // xnu-2782.1.97/bsd/kern/kern_exit.c proc_prepareexit(). Receiving these\n  // exception types wrapped in EXC_CRASH would lose information because their\n  // code[0] uses all 64 bits (see ExceptionSnapshotMac::Initialize()) and the\n  // code[0] recovered from EXC_CRASH only contains 20 significant bits.\n  //\n  // EXC_CORPSE_NOTIFY may be generated from EXC_CRASH, but the opposite should\n  // never occur.\n  //\n  // kMachExceptionSimulated is a non-fatal Crashpad-specific pseudo-exception\n  // that never exists as an exception within the kernel and should thus never\n  // be wrapped in EXC_CRASH.\n  if (MacOSVersionNumber() < 13'00'00 && exception == EXC_GUARD) {\n    return false;\n  }\n  return exception != EXC_CRASH && exception != EXC_RESOURCE &&\n         exception != EXC_CORPSE_NOTIFY && exception != kMachExceptionSimulated;\n}\n\nint32_t ExceptionCodeForMetrics(exception_type_t exception,\n                                mach_exception_code_t code_0) {\n  if (exception == kMachExceptionSimulated) {\n    return exception;\n  }\n\n  int signal = 0;\n  if (exception == EXC_CRASH) {\n    const exception_type_t original_exception =\n        ExcCrashRecoverOriginalException(code_0, &code_0, &signal);\n    if (!ExcCrashCouldContainException(original_exception)) {\n      LOG(WARNING) << \"EXC_CRASH should not contain exception \"\n                   << original_exception;\n      return InRangeCast<uint16_t>(original_exception, 0xffff) << 16;\n    }\n    exception = original_exception;\n  }\n\n  uint16_t metrics_exception = InRangeCast<uint16_t>(exception, 0xffff);\n\n  uint16_t metrics_code_0;\n  switch (exception) {\n    case EXC_RESOURCE:\n      metrics_code_0 = (EXC_RESOURCE_DECODE_RESOURCE_TYPE(code_0) << 8) |\n                       EXC_RESOURCE_DECODE_FLAVOR(code_0);\n      break;\n\n    case EXC_GUARD: {\n      // This will be GUARD_TYPE_MACH_PORT (1) from <mach/port.h> or\n      // GUARD_TYPE_FD (2) from 10.12.2 xnu-3789.31.2/bsd/sys/guarded.h\n      const uint8_t guard_type = (code_0) >> 61;\n\n      // These exceptions come through 10.12.2\n      // xnu-3789.31.2/osfmk/ipc/mach_port.c mach_port_guard_exception() or\n      // xnu-3789.31.2/bsd/kern/kern_guarded.c fp_guard_exception(). In each\n      // case, bits 32-60 of code_0 encode the guard type-specific “flavor”. For\n      // Mach port guards, these flavor codes come from the\n      // mach_port_guard_exception_codes enum in <mach/port.h>. For file\n      // descriptor guards, they come from the guard_exception_codes enum in\n      // xnu-3789.31.2/bsd/sys/guarded.h. Both of these enums define shifted-bit\n      // values (1 << 0, 1 << 1, 1 << 2, etc.) In actual usage as determined by\n      // callers to these functions, these “flavor” codes are never ORed with\n      // one another. For the purposes of encoding these codes for metrics,\n      // convert the flavor codes to their corresponding bit shift values.\n      const uint32_t guard_flavor = (code_0 >> 32) & 0x1fffffff;\n      const int first_bit = ffs(guard_flavor);\n      uint8_t metrics_guard_flavor;\n      if (first_bit) {\n        metrics_guard_flavor = first_bit - 1;\n\n        const uint32_t test_guard_flavor = 1 << metrics_guard_flavor;\n        if (guard_flavor != test_guard_flavor) {\n          // Another bit is set.\n          DCHECK_EQ(guard_flavor, test_guard_flavor);\n          metrics_guard_flavor = 0xff;\n        }\n      } else {\n        metrics_guard_flavor = 0xff;\n      }\n\n      metrics_code_0 = (guard_type << 8) | metrics_guard_flavor;\n      break;\n    }\n\n    case EXC_CORPSE_NOTIFY:\n      // code_0 may be a pointer. See 10.12.2 xnu-3789.31.2/osfmk/kern/task.c\n      // task_deliver_crash_notification(). Just encode 0 for metrics purposes.\n      metrics_code_0 = 0;\n      break;\n\n    default:\n      metrics_code_0 = InRangeCast<uint16_t>(code_0, 0xffff);\n      if (exception == 0 && metrics_code_0 == 0 && signal != 0) {\n        // This exception came from a signal that did not originate as another\n        // Mach exception. Encode the signal number, using EXC_CRASH as the\n        // top-level exception type. This is safe because EXC_CRASH will not\n        // otherwise appear as metrics_exception.\n        metrics_exception = EXC_CRASH;\n        metrics_code_0 = signal;\n      }\n      break;\n  }\n\n  return (metrics_exception << 16) | metrics_code_0;\n}\n\nbool IsExceptionNonfatalResource(exception_type_t exception,\n                                 mach_exception_code_t code_0,\n                                 pid_t pid) {\n  if (exception != EXC_RESOURCE) {\n    return false;\n  }\n\n  const int resource_type = EXC_RESOURCE_DECODE_RESOURCE_TYPE(code_0);\n  const int resource_flavor = EXC_RESOURCE_DECODE_FLAVOR(code_0);\n\n  if (resource_type == RESOURCE_TYPE_CPU &&\n      (resource_flavor == FLAVOR_CPU_MONITOR ||\n       resource_flavor == FLAVOR_CPU_MONITOR_FATAL)) {\n    // These exceptions may be fatal. They are not fatal by default at task\n    // creation but can be made fatal by calling proc_rlimit_control() with\n    // RLIMIT_CPU_USAGE_MONITOR as the second argument and CPUMON_MAKE_FATAL set\n    // in the flags.\n    if (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||\n        MacOSVersionNumber() >= 10'10'00) {\n      // In OS X 10.10, the exception code indicates whether the exception is\n      // fatal. See 10.10 xnu-2782.1.97/osfmk/kern/thread.c\n      // THIS_THREAD_IS_CONSUMING_TOO_MUCH_CPU__SENDING_EXC_RESOURCE().\n      return resource_flavor == FLAVOR_CPU_MONITOR;\n    }\n\n    // In OS X 10.9, there’s no way to determine whether the exception is fatal.\n    // Unlike RESOURCE_TYPE_WAKEUPS below, there’s no way to determine this\n    // outside the kernel. proc_rlimit_control()’s RLIMIT_CPU_USAGE_MONITOR is\n    // the only interface to modify CPUMON_MAKE_FATAL, but it’s only able to set\n    // this bit, not obtain its current value.\n    //\n    // Default to assuming that these exceptions are nonfatal. They are nonfatal\n    // by default and no users of proc_rlimit_control() were found on 10.9.5\n    // 13F1066 in /System and /usr outside of Metadata.framework and associated\n    // tools.\n    return true;\n  }\n\n  if (resource_type == RESOURCE_TYPE_WAKEUPS &&\n      resource_flavor == FLAVOR_WAKEUPS_MONITOR) {\n    // These exceptions may be fatal. They are not fatal by default at task\n    // creation, but can be made fatal by calling proc_rlimit_control() with\n    // RLIMIT_WAKEUPS_MONITOR as the second argument and WAKEMON_MAKE_FATAL set\n    // in the flags.\n    //\n    // proc_get_wakemon_params() (which calls\n    // through to proc_rlimit_control() with RLIMIT_WAKEUPS_MONITOR) determines\n    // whether these exceptions are fatal. See 10.10\n    // xnu-2782.1.97/osfmk/kern/task.c\n    // THIS_PROCESS_IS_CAUSING_TOO_MANY_WAKEUPS__SENDING_EXC_RESOURCE().\n    //\n    // If proc_get_wakemon_params() fails, default to assuming that these\n    // exceptions are nonfatal. They are nonfatal by default and no users of\n    // proc_rlimit_control() were found on 10.9.5 13F1066 in /System and /usr\n    // outside of Metadata.framework and associated tools.\n    int wm_rate;\n    int wm_flags;\n    int rv = ProcGetWakemonParams(pid, &wm_rate, &wm_flags);\n    if (rv < 0) {\n      PLOG(WARNING) << \"ProcGetWakemonParams\";\n      return true;\n    }\n\n    return !(wm_flags & WAKEMON_MAKE_FATAL);\n  }\n\n  if (resource_type == RESOURCE_TYPE_MEMORY &&\n      resource_flavor == FLAVOR_HIGH_WATERMARK) {\n    // These exceptions were never fatal prior to 10.12. See 10.10\n    // xnu-2782.1.97/osfmk/kern/task.c\n    // THIS_PROCESS_CROSSED_HIGH_WATERMARK__SENDING_EXC_RESOURCE().\n    //\n    // A superficial examination of 10.12 shows that these exceptions may be\n    // fatal, as determined by the P_MEMSTAT_FATAL_MEMLIMIT bit of the\n    // kernel-internal struct proc::p_memstat_state. See 10.12.3\n    // xnu-3789.41.3/osfmk/kern/task.c task_footprint_exceeded(). This bit is\n    // not exposed to user space, which makes it difficult to determine whether\n    // the kernel considers a given instance of this exception fatal. However, a\n    // close read reveals that it is only possible for this bit to become set\n    // when xnu-3789.41.3/bsd/kern/kern_memorystatus.c\n    // memorystatus_cmd_set_memlimit_properties() is called, which is only\n    // possible when the kernel is built with CONFIG_JETSAM set, or if the\n    // kern.memorystatus_highwater_enabled sysctl is used, which is only\n    // possible when the kernel is built with DEVELOPMENT or DEBUG set. Although\n    // CONFIG_JETSAM is used on iOS, it is not used on macOS. DEVELOPMENT and\n    // DEBUG are also not set for production kernels. It therefore remains\n    // impossible for these exceptions to be fatal, even on 10.12.\n    return true;\n  }\n\n  if (resource_type == RESOURCE_TYPE_IO) {\n    // These exceptions are never fatal. See 10.12.3\n    // xnu-3789.41.3/osfmk/kern/task.c\n    // SENDING_NOTIFICATION__THIS_PROCESS_IS_CAUSING_TOO_MUCH_IO().\n    return true;\n  }\n\n  // Treat unknown exceptions as fatal. This is the conservative approach: it\n  // may result in more crash reports being generated, but the type-flavor\n  // combinations can be evaluated to determine appropriate handling.\n  LOG(WARNING) << \"unknown resource type \" << resource_type << \" flavor \"\n               << resource_flavor;\n  return false;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/exception_types.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_\n#define CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_\n\n#include <inttypes.h>\n#include <mach/mach.h>\n#include <sys/types.h>\n\nnamespace crashpad {\n\n//! \\brief Recovers the original exception, first exception code, and signal\n//!     from the encoded form of the first exception code delivered with\n//!     `EXC_CRASH` exceptions.\n//!\n//! `EXC_CRASH` exceptions are generated when the kernel has committed to\n//! terminating a process as a result of a core-generating POSIX signal and, for\n//! hardware exceptions, an earlier Mach exception. Information about this\n//! earlier exception and signal is made available to the `EXC_CRASH` handler\n//! via its `code[0]` parameter. This function recovers the original exception,\n//! the value of `code[0]` from the original exception, and the value of the\n//! signal responsible for process termination.\n//!\n//! \\param[in] code_0 The first exception code (`code[0]`) passed to a Mach\n//!     exception handler in an `EXC_CRASH` exception. It is invalid to call\n//!     this function with an exception code from any exception other than\n//!     `EXC_CRASH`.\n//! \\param[out] original_code_0 The first exception code (`code[0]`) passed to\n//!     the Mach exception handler for a hardware exception that resulted in the\n//!     generation of a POSIX signal that caused process termination. If the\n//!     signal that caused termination was not sent as a result of a hardware\n//!     exception, this will be `0`. Callers that do not need this value may\n//!     pass `nullptr`.\n//! \\param[out] signal The POSIX signal that caused process termination. Callers\n//!     that do not need this value may pass `nullptr`.\n//!\n//! \\return The original exception for a hardware exception that resulted in the\n//!     generation of a POSIX signal that caused process termination. If the\n//!     signal that caused termination was not sent as a result of a hardware\n//!     exception, this will be `0`.\nexception_type_t ExcCrashRecoverOriginalException(\n    mach_exception_code_t code_0,\n    mach_exception_code_t* original_code_0,\n    int* signal);\n\n//! \\brief Determines whether a given exception type could plausibly be carried\n//!     within an `EXC_CRASH` exception.\n//!\n//! \\param[in] exception The exception type to test.\n//!\n//! \\return `true` if an `EXC_CRASH` exception could plausibly carry \\a\n//!     exception.\n//!\n//! An `EXC_CRASH` exception can wrap exceptions that originate as hardware\n//! faults, as well as exceptions that originate from certain software sources\n//! such as POSIX signals. It cannot wrap another `EXC_CRASH` exception, nor can\n//! it wrap `EXC_RESOURCE`, `EXC_GUARD`, or `EXC_CORPSE_NOTIFY` exceptions. It\n//! also cannot wrap Crashpad-specific #kMachExceptionSimulated exceptions.\nbool ExcCrashCouldContainException(exception_type_t exception);\n\n//! \\brief Returns the exception code to report via a configured metrics system.\n//!\n//! \\param[in] exception The exception type as received by a Mach exception\n//!     handler.\n//! \\param[in] code_0 The first exception code (`code[0]`) as received by a\n//!     Mach exception handler.\n//!\n//! \\return An exception code that maps useful information from \\a exception and\n//!     \\a code_0 to the more limited data type available for metrics reporting.\n//!\n//! For classic Mach exceptions (including hardware faults reported as Mach\n//! exceptions), the mapping is `(exception << 16) | code_0`.\n//!\n//! For `EXC_CRASH` exceptions that originate as Mach exceptions described\n//! above, the mapping above is used, with the original exception’s values. For\n//! `EXC_CRASH` exceptions that originate as POSIX signals without an underlying\n//! Mach exception, the mapping is `(EXC_CRASH << 16) | code_0`.\n//!\n//! `EXC_RESOURCE` and `EXC_GUARD` exceptions both contain exception-specific\n//! “type” values and type-specific “flavor” values. In these cases, the mapping\n//! is `(exception << 16) | (type << 8) | flavor`. For `EXC_GUARD`, the “flavor”\n//! value is rewritten to be more space-efficient by replacing the\n//! kernel-supplied bitmask having exactly one bit set with the index of the set\n//! bit.\n//!\n//! `EXC_CORPSE_NOTIFY` exceptions are reported as classic Mach exceptions with\n//! the \\a code_0 field set to `0`.\n//!\n//! If \\a exception is #kMachExceptionSimulated, that value is returned as-is.\n//!\n//! Overflow conditions in any field are handled via saturation.\nint32_t ExceptionCodeForMetrics(exception_type_t exception,\n                                mach_exception_code_t code_0);\n\n//! \\brief Determines whether an exception is a non-fatal `EXC_RESOURCE`.\n//!\n//! \\param[in] exception The exception type as received by a Mach exception\n//!     handler.\n//! \\param[in] code_0 The first exception code (`code[0]`) as received by a\n//!     Mach exception handler.\n//! \\param[in] pid The process ID that the exception occurred in. In some cases,\n//!     process may need to be queried to determine whether an `EXC_RESOURCE`\n//!     exception is fatal.\n//!\n//! \\return `true` if the exception is a non-fatal `EXC_RESOURCE`. `false`\n//!     otherwise. If the exception is `EXC_RESOURCE` of a recognized type but\n//!     it is not possible to determine whether it is fatal, returns `true`\n//!     under the assumption that all known `EXC_RESOURCE` exceptions are\n//!     non-fatal by default. If the exception is not `EXC_RESOURCE` or is an\n//!     unknown `EXC_RESOURCE` type, returns `false`.\nbool IsExceptionNonfatalResource(exception_type_t exception,\n                                 mach_exception_code_t code_0,\n                                 pid_t pid);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_EXCEPTION_TYPES_H_\n"
  },
  {
    "path": "util/mach/exception_types_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/exception_types.h\"\n\n#include <kern/exc_resource.h>\n#include <signal.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <iterator>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"util/mac/mac_util.h\"\n#include \"util/mach/mach_extensions.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ExceptionTypes, ExcCrashRecoverOriginalException) {\n  static constexpr struct {\n    mach_exception_code_t code_0;\n    exception_type_t exception;\n    mach_exception_code_t original_code_0;\n    int signal;\n  } kTestData[] = {\n    {0xb100001, EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, SIGSEGV},\n    {0xb100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGSEGV},\n    {0xa100002, EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE, SIGBUS},\n    {0xa100005, EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE, SIGBUS},\n    {0x9100032, EXC_BAD_ACCESS, KERN_CODESIGN_ERROR, SIGKILL},\n    {0x0700080, EXC_SYSCALL, 128, 0},\n    {0x0706000, EXC_SYSCALL, 0x6000, 0},\n    {0x3000000, 0, 0, SIGQUIT},\n    {0x4000000, 0, 0, SIGILL},\n    {0x5000000, 0, 0, SIGTRAP},\n    {0x6000000, 0, 0, SIGABRT},\n    {0x7000000, 0, 0, SIGEMT},\n    {0x8000000, 0, 0, SIGFPE},\n    {0xa000000, 0, 0, SIGBUS},\n    {0xb000000, 0, 0, SIGSEGV},\n    {0xc000000, 0, 0, SIGSYS},\n    {0, 0, 0, 0},\n#if defined(ARCH_CPU_X86_FAMILY)\n    {0xa10000d, EXC_BAD_ACCESS, EXC_I386_GPFLT, SIGBUS},\n    {0x4200001, EXC_BAD_INSTRUCTION, EXC_I386_INVOP, SIGILL},\n    {0x420000b, EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT, SIGILL},\n    {0x420000c, EXC_BAD_INSTRUCTION, EXC_I386_STKFLT, SIGILL},\n    {0x8300001, EXC_ARITHMETIC, EXC_I386_DIV, SIGFPE},\n    {0x8300002, EXC_ARITHMETIC, EXC_I386_INTO, SIGFPE},\n    {0x8300005, EXC_ARITHMETIC, EXC_I386_EXTERR, SIGFPE},\n    {0x8300008, EXC_ARITHMETIC, EXC_I386_SSEEXTERR, SIGFPE},\n    {0x5500007, EXC_SOFTWARE, EXC_I386_BOUND, SIGTRAP},\n    {0x5600001, EXC_BREAKPOINT, EXC_I386_SGL, SIGTRAP},\n    {0x5600002, EXC_BREAKPOINT, EXC_I386_BPT, SIGTRAP},\n#endif\n    // TODO(macos_arm64): Add arm64 test data.\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& test_data = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %zu, code_0 0x%llx\", index, test_data.code_0));\n\n    mach_exception_code_t original_code_0;\n    int signal;\n    exception_type_t exception = ExcCrashRecoverOriginalException(\n        test_data.code_0, &original_code_0, &signal);\n\n    EXPECT_EQ(exception, test_data.exception);\n    EXPECT_EQ(original_code_0, test_data.original_code_0);\n    EXPECT_EQ(signal, test_data.signal);\n  }\n\n  // Now make sure that ExcCrashRecoverOriginalException() properly ignores\n  // optional arguments.\n  static_assert(std::size(kTestData) >= 1, \"must have something to test\");\n  const auto& test_data = kTestData[0];\n  EXPECT_EQ(\n      ExcCrashRecoverOriginalException(test_data.code_0, nullptr, nullptr),\n      test_data.exception);\n\n  mach_exception_code_t original_code_0;\n  EXPECT_EQ(ExcCrashRecoverOriginalException(\n                test_data.code_0, &original_code_0, nullptr),\n            test_data.exception);\n  EXPECT_EQ(original_code_0, test_data.original_code_0);\n\n  int signal;\n  EXPECT_EQ(\n      ExcCrashRecoverOriginalException(test_data.code_0, nullptr, &signal),\n      test_data.exception);\n  EXPECT_EQ(signal, test_data.signal);\n}\n\nTEST(ExceptionTypes, ExcCrashCouldContainException) {\n  // This seems wrong, but it happens when EXC_CRASH carries an exception not\n  // originally caused by a hardware fault, such as SIGABRT.\n  EXPECT_TRUE(ExcCrashCouldContainException(0));\n\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_BAD_ACCESS));\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_BAD_INSTRUCTION));\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_ARITHMETIC));\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_EMULATION));\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_SOFTWARE));\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_BREAKPOINT));\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_SYSCALL));\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_MACH_SYSCALL));\n  EXPECT_TRUE(ExcCrashCouldContainException(EXC_RPC_ALERT));\n  EXPECT_FALSE(ExcCrashCouldContainException(EXC_CRASH));\n  EXPECT_FALSE(ExcCrashCouldContainException(EXC_RESOURCE));\n  EXPECT_FALSE(ExcCrashCouldContainException(EXC_CORPSE_NOTIFY));\n  EXPECT_FALSE(ExcCrashCouldContainException(kMachExceptionSimulated));\n}\n\n// This macro is adapted from those in the #ifdef KERNEL section of\n// <kern/exc_resource.h>: 10.12.2 xnu-3789.31.2/osfmk/kern/exc_resource.h.\n#define EXC_RESOURCE_ENCODE_TYPE_FLAVOR(type, flavor)   \\\n  (static_cast<mach_exception_code_t>(                  \\\n      (((static_cast<uint64_t>(type) & 0x7ull) << 61) | \\\n       (static_cast<uint64_t>(flavor) & 0x7ull) << 58)))\n\nTEST(ExceptionTypes, ExceptionCodeForMetrics) {\n  static constexpr struct {\n    exception_type_t exception;\n    mach_exception_code_t code_0;\n    int32_t metrics_code;\n  } kTestData[] = {\n#define ENCODE_EXC(type, code_0) \\\n  { (type), (code_0), ((type) << 16) | (code_0) }\n    ENCODE_EXC(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS),\n    ENCODE_EXC(EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE),\n    ENCODE_EXC(EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE),\n    ENCODE_EXC(EXC_BAD_ACCESS, KERN_CODESIGN_ERROR),\n    ENCODE_EXC(EXC_SYSCALL, 128),\n    ENCODE_EXC(EXC_SYSCALL, 0x6000),\n#if defined(ARCH_CPU_X86_FAMILY)\n    ENCODE_EXC(EXC_BAD_ACCESS, EXC_I386_GPFLT),\n    ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_INVOP),\n    ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT),\n    ENCODE_EXC(EXC_BAD_INSTRUCTION, EXC_I386_STKFLT),\n    ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_DIV),\n    ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_INTO),\n    ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_EXTERR),\n    ENCODE_EXC(EXC_ARITHMETIC, EXC_I386_SSEEXTERR),\n    ENCODE_EXC(EXC_SOFTWARE, EXC_I386_BOUND),\n    ENCODE_EXC(EXC_BREAKPOINT, EXC_I386_SGL),\n    ENCODE_EXC(EXC_BREAKPOINT, EXC_I386_BPT),\n#endif\n    // TODO(macos_arm64): Add arm64 test data.\n#undef ENCODE_EXC\n\n#define ENCODE_EXC_CRASH(type, code_0)                        \\\n  {                                                           \\\n    EXC_CRASH, (((type) & 0xf) << 20) | ((code_0) & 0xfffff), \\\n        ((type) << 16) | (code_0)                             \\\n  }\n    ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS),\n    ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_PROTECTION_FAILURE),\n    ENCODE_EXC_CRASH(EXC_BAD_ACCESS, VM_PROT_READ | VM_PROT_EXECUTE),\n    ENCODE_EXC_CRASH(EXC_BAD_ACCESS, KERN_CODESIGN_ERROR),\n    ENCODE_EXC_CRASH(EXC_SYSCALL, 128),\n    ENCODE_EXC_CRASH(EXC_SYSCALL, 0x6000),\n#if defined(ARCH_CPU_X86_FAMILY)\n    ENCODE_EXC_CRASH(EXC_BAD_ACCESS, EXC_I386_GPFLT),\n    ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_INVOP),\n    ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_SEGNPFLT),\n    ENCODE_EXC_CRASH(EXC_BAD_INSTRUCTION, EXC_I386_STKFLT),\n    ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_DIV),\n    ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_INTO),\n    ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_EXTERR),\n    ENCODE_EXC_CRASH(EXC_ARITHMETIC, EXC_I386_SSEEXTERR),\n    ENCODE_EXC_CRASH(EXC_SOFTWARE, EXC_I386_BOUND),\n    ENCODE_EXC_CRASH(EXC_BREAKPOINT, EXC_I386_SGL),\n    ENCODE_EXC_CRASH(EXC_BREAKPOINT, EXC_I386_BPT),\n#endif\n    // TODO(macos_arm64): Add arm64 test data.\n#undef ENCODE_EXC_CRASH\n\n#define ENCODE_EXC_CRASH_SIGNAL(signal) \\\n  { EXC_CRASH, (((signal) & 0xff) << 24), (EXC_CRASH << 16) | (signal) }\n    ENCODE_EXC_CRASH_SIGNAL(SIGQUIT),\n    ENCODE_EXC_CRASH_SIGNAL(SIGILL),\n    ENCODE_EXC_CRASH_SIGNAL(SIGTRAP),\n    ENCODE_EXC_CRASH_SIGNAL(SIGABRT),\n    ENCODE_EXC_CRASH_SIGNAL(SIGEMT),\n    ENCODE_EXC_CRASH_SIGNAL(SIGFPE),\n    ENCODE_EXC_CRASH_SIGNAL(SIGBUS),\n    ENCODE_EXC_CRASH_SIGNAL(SIGSEGV),\n    ENCODE_EXC_CRASH_SIGNAL(SIGSYS),\n#undef ENCODE_EXC_CRASH_SIGNAL\n\n#define ENCODE_EXC_RESOURCE(type, flavor)                            \\\n  {                                                                  \\\n    EXC_RESOURCE, EXC_RESOURCE_ENCODE_TYPE_FLAVOR((type), (flavor)), \\\n        (EXC_RESOURCE << 16) | ((type) << 8) | (flavor)              \\\n  }\n    ENCODE_EXC_RESOURCE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR),\n    ENCODE_EXC_RESOURCE(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR_FATAL),\n    ENCODE_EXC_RESOURCE(RESOURCE_TYPE_WAKEUPS, FLAVOR_WAKEUPS_MONITOR),\n    ENCODE_EXC_RESOURCE(RESOURCE_TYPE_MEMORY, FLAVOR_HIGH_WATERMARK),\n    ENCODE_EXC_RESOURCE(RESOURCE_TYPE_IO, FLAVOR_IO_PHYSICAL_WRITES),\n    ENCODE_EXC_RESOURCE(RESOURCE_TYPE_IO, FLAVOR_IO_LOGICAL_WRITES),\n#undef ENCODE_EXC_RESOURCE\n\n#define ENCODE_EXC_GUARD(type, flavor)                                         \\\n  {                                                                            \\\n    EXC_GUARD,                                                                 \\\n        static_cast<mach_exception_code_t>(static_cast<uint64_t>((type) & 0x7) \\\n                                           << 61) |                            \\\n            (static_cast<uint64_t>((1 << (flavor)) & 0x1ffffffff) << 32),      \\\n        (EXC_GUARD << 16) | ((type) << 8) | (flavor)                           \\\n  }\n    ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 0),  // kGUARD_EXC_DESTROY\n    ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 1),  // kGUARD_EXC_MOD_REFS\n    ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 2),  // kGUARD_EXC_SET_CONTEXT\n    ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 3),  // kGUARD_EXC_UNGUARDED\n    ENCODE_EXC_GUARD(GUARD_TYPE_MACH_PORT, 4),  // kGUARD_EXC_INCORRECT_GUARD\n\n    // 2 is GUARD_TYPE_FD from 10.12.2 xnu-3789.31.2/bsd/sys/guarded.h.\n    ENCODE_EXC_GUARD(2, 0),  // kGUARD_EXC_CLOSE\n    ENCODE_EXC_GUARD(2, 1),  // kGUARD_EXC_DUP\n    ENCODE_EXC_GUARD(2, 2),  // kGUARD_EXC_NOCLOEXEC\n    ENCODE_EXC_GUARD(2, 3),  // kGUARD_EXC_SOCKET_IPC\n    ENCODE_EXC_GUARD(2, 4),  // kGUARD_EXC_FILEPORT\n    ENCODE_EXC_GUARD(2, 5),  // kGUARD_EXC_MISMATCH\n    ENCODE_EXC_GUARD(2, 6),  // kGUARD_EXC_WRITE\n#undef ENCODE_EXC_GUARD\n\n    // Test that overflow saturates.\n    {0x00010000, 0x00001000, static_cast<int32_t>(0xffff1000)},\n    {0x00001000, 0x00010000, 0x1000ffff},\n    {0x00010000, 0x00010000, static_cast<int32_t>(0xffffffff)},\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& test_data = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"index %zu, exception 0x%x, code_0 0x%llx\",\n                                    index,\n                                    test_data.exception,\n                                    test_data.code_0));\n\n    int32_t metrics_code =\n        ExceptionCodeForMetrics(test_data.exception, test_data.code_0);\n\n    EXPECT_EQ(metrics_code, test_data.metrics_code);\n  }\n}\n\nTEST(ExceptionTypes, IsExceptionNonfatalResource) {\n  const pid_t pid = getpid();\n\n  mach_exception_code_t code =\n      EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR);\n  EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));\n\n  if (MacOSVersionNumber() >= 10'10'00) {\n    // FLAVOR_CPU_MONITOR_FATAL was introduced in OS X 10.10.\n    code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_CPU,\n                                           FLAVOR_CPU_MONITOR_FATAL);\n    EXPECT_FALSE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));\n  }\n\n  // This assumes that WAKEMON_MAKE_FATAL is not set for this process. The\n  // default is for WAKEMON_MAKE_FATAL to not be set, there’s no public API to\n  // enable it, and nothing in this process should have enabled it.\n  code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_WAKEUPS,\n                                         FLAVOR_WAKEUPS_MONITOR);\n  EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));\n\n  code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_MEMORY,\n                                         FLAVOR_HIGH_WATERMARK);\n  EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));\n\n  // Non-EXC_RESOURCE exceptions should never be considered nonfatal resource\n  // exceptions, because they aren’t resource exceptions at all.\n  EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0xb100001, pid));\n  EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0x0b00000, pid));\n  EXPECT_FALSE(IsExceptionNonfatalResource(EXC_CRASH, 0x6000000, pid));\n  EXPECT_FALSE(\n      IsExceptionNonfatalResource(EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, pid));\n  EXPECT_FALSE(IsExceptionNonfatalResource(EXC_BAD_INSTRUCTION, 1, pid));\n  EXPECT_FALSE(IsExceptionNonfatalResource(EXC_ARITHMETIC, 1, pid));\n  EXPECT_FALSE(IsExceptionNonfatalResource(EXC_BREAKPOINT, 2, pid));\n  EXPECT_FALSE(IsExceptionNonfatalResource(0, 0, pid));\n  EXPECT_FALSE(IsExceptionNonfatalResource(kMachExceptionSimulated, 0, pid));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/mach_extensions.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/mach_extensions.h\"\n\n#include <Availability.h>\n#include <pthread.h>\n\n#include \"base/apple/mach_logging.h\"\n#include \"build/build_config.h\"\n#include \"util/mac/mac_util.h\"\n\nnamespace crashpad {\n\nthread_t MachThreadSelf() {\n  // The pthreads library keeps its own copy of the thread port. Using it does\n  // not increment its reference count.\n  return pthread_mach_thread_np(pthread_self());\n}\n\nmach_port_t NewMachPort(mach_port_right_t right) {\n  mach_port_t port = MACH_PORT_NULL;\n  kern_return_t kr = mach_port_allocate(mach_task_self(), right, &port);\n  MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << \"mach_port_allocate\";\n  return port;\n}\n\nexception_mask_t ExcMaskAll() {\n  // This is necessary because of the way that the kernel validates\n  // exception_mask_t arguments to\n  // {host,task,thread}_{get,set,swap}_exception_ports(). It is strict,\n  // rejecting attempts to operate on any bits that it does not recognize. See\n  // 10.9.4 xnu-2422.110.17/osfmk/mach/ipc_host.c and\n  // xnu-2422.110.17/osfmk/mach/ipc_tt.c.\n\n#if BUILDFLAG(IS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0\n// iOS 7 ≅ OS X 10.9.\n#error This code was not ported to iOS versions older than 7\n#endif\n\n#if BUILDFLAG(IS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9\n  const int macos_version_number = MacOSVersionNumber();\n#endif\n\n  // See 10.6.8 xnu-1504.15.3/osfmk/mach/exception_types.h. 10.7 uses the same\n  // definition as 10.6. See 10.7.5 xnu-1699.32.7/osfmk/mach/exception_types.h\n  constexpr exception_mask_t kExcMaskAll_10_6 =\n      EXC_MASK_BAD_ACCESS |\n      EXC_MASK_BAD_INSTRUCTION |\n      EXC_MASK_ARITHMETIC |\n      EXC_MASK_EMULATION |\n      EXC_MASK_SOFTWARE |\n      EXC_MASK_BREAKPOINT |\n      EXC_MASK_SYSCALL |\n      EXC_MASK_MACH_SYSCALL |\n      EXC_MASK_RPC_ALERT |\n      EXC_MASK_MACHINE;\n#if BUILDFLAG(IS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8\n  if (macos_version_number < 10'08'00) {\n    return kExcMaskAll_10_6;\n  }\n#endif\n\n  // 10.8 added EXC_MASK_RESOURCE. See 10.8.5\n  // xnu-2050.48.11/osfmk/mach/exception_types.h.\n  constexpr exception_mask_t kExcMaskAll_10_8 =\n      kExcMaskAll_10_6 | EXC_MASK_RESOURCE;\n#if BUILDFLAG(IS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9\n  if (macos_version_number < 10'09'00) {\n    return kExcMaskAll_10_8;\n  }\n#endif\n\n  // 10.9 added EXC_MASK_GUARD. See 10.9.4\n  // xnu-2422.110.17/osfmk/mach/exception_types.h.\n  constexpr exception_mask_t kExcMaskAll_10_9 =\n      kExcMaskAll_10_8 | EXC_MASK_GUARD;\n  return kExcMaskAll_10_9;\n}\n\nexception_mask_t ExcMaskValid() {\n  const exception_mask_t kExcMaskValid_10_6 = ExcMaskAll() | EXC_MASK_CRASH;\n#if BUILDFLAG(IS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0\n// iOS 9 ≅ OS X 10.11.\n#error This code was not ported to iOS versions older than 9\n#endif\n\n#if BUILDFLAG(IS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_11\n  if (MacOSVersionNumber() < 10'11'00) {\n    return kExcMaskValid_10_6;\n  }\n#endif\n\n  // 10.11 added EXC_MASK_CORPSE_NOTIFY. See 10.11 <mach/exception_types.h>.\n  const exception_mask_t kExcMaskValid_10_11 =\n      kExcMaskValid_10_6 | EXC_MASK_CORPSE_NOTIFY;\n  return kExcMaskValid_10_11;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/mach_extensions.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_\n#define CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_\n\n#include <mach/mach.h>\n\nnamespace crashpad {\n\n//! \\brief `MACH_PORT_NULL` with the correct type for a Mach port,\n//!     `mach_port_t`.\n//!\n//! For situations where implicit conversions between signed and unsigned types\n//! are not performed, use kMachPortNull instead of an explicit `implicit_cast`\n//! of `MACH_PORT_NULL` to `mach_port_t`. This is useful for logging and testing\n//! assertions.\nconstexpr mach_port_t kMachPortNull = MACH_PORT_NULL;\n\n//! \\brief `MACH_EXCEPTION_CODES` with the correct type for a Mach exception\n//!     behavior, `exception_behavior_t`.\n//!\n//! Signedness problems can occur when ORing `MACH_EXCEPTION_CODES` as a signed\n//! integer, because a signed integer overflow results. This constant can be\n//! used instead of `MACH_EXCEPTION_CODES` in such cases.\nconstexpr exception_behavior_t kMachExceptionCodes = MACH_EXCEPTION_CODES;\n\n// Because exception_mask_t is an int and has one bit for each defined\n// exception_type_t, it’s reasonable to assume that there cannot be any\n// officially-defined exception_type_t values higher than 31.\n// kMachExceptionSimulated uses a value well outside this range because it does\n// not require a corresponding mask value. Simulated exceptions are delivered to\n// the exception handler registered for EXC_CRASH exceptions using\n// EXC_MASK_CRASH.\n\n//! \\brief An exception type to use for simulated exceptions.\nconstexpr exception_type_t kMachExceptionSimulated = 'CPsx';\n\n//! \\brief An exception type to use for uncaught NSExceptions.\nconstexpr exception_type_t kMachExceptionFromNSException = 'CPnx';\n\n//! \\brief A const version of `thread_state_t`.\n//!\n//! This is useful as the \\a old_state parameter to exception handler functions.\n//! Normally, these parameters are of type `thread_state_t`, but this allows\n//! modification of the state, which is conceptually `const`.\nusing ConstThreadState = const natural_t*;\n\n//! \\brief Like `mach_thread_self()`, but without the obligation to release the\n//!     send right.\n//!\n//! `mach_thread_self()` returns a send right to the current thread port,\n//! incrementing its reference count. This burdens the caller with maintaining\n//! this send right, and calling `mach_port_deallocate()` when it is no longer\n//! needed. This is burdensome, and is at odds with the normal operation of\n//! `mach_task_self()`, which does not increment the task port’s reference count\n//! whose result must not be deallocated.\n//!\n//! Callers can use this function in preference to `mach_thread_self()`. This\n//! function returns an extant reference to the current thread’s port without\n//! incrementing its reference count.\n//!\n//! \\return The value of `mach_thread_self()` without incrementing its reference\n//!     count. The returned port must not be deallocated by\n//!     `mach_port_deallocate()`. The returned value is valid as long as the\n//!     thread continues to exist as a `pthread_t`.\nthread_t MachThreadSelf();\n\n//! \\brief Creates a new Mach port in the current task.\n//!\n//! This function wraps the `mach_port_allocate()` providing a simpler\n//! interface.\n//!\n//! \\param[in] right The type of right to create.\n//!\n//! \\return The new Mach port. On failure, `MACH_PORT_NULL` with a message\n//!     logged.\nmach_port_t NewMachPort(mach_port_right_t right);\n\n//! \\brief The value for `EXC_MASK_ALL` appropriate for the operating system at\n//!     run time.\n//!\n//! The SDK’s definition of `EXC_MASK_ALL` has changed over time, with later\n//! versions containing more bits set than earlier versions. However, older\n//! kernels will reject exception masks that contain bits set that they don’t\n//! recognize. Calling this function will return a value for `EXC_MASK_ALL`\n//! appropriate for the system at run time.\n//!\n//! \\note `EXC_MASK_ALL` does not include the value of `EXC_MASK_CRASH` or\n//!     `EXC_MASK_CORPSE_NOTIFY`. Consumers that want `EXC_MASK_ALL` along with\n//!     `EXC_MASK_CRASH` may use ExcMaskAll() `| EXC_MASK_CRASH`. Consumers may\n//!     use ExcMaskValid() for `EXC_MASK_ALL` along with `EXC_MASK_CRASH`,\n//!     `EXC_MASK_CORPSE_NOTIFY`, and any values that come into existence in the\n//!     future.\nexception_mask_t ExcMaskAll();\n\n//! \\brief An exception mask containing every possible exception understood by\n//!     the operating system at run time.\n//!\n//! `EXC_MASK_ALL`, and thus ExcMaskAll(), never includes the value of\n//! `EXC_MASK_CRASH` or `EXC_MASK_CORPSE_NOTIFY`. For situations where an\n//! exception mask corresponding to every possible exception understood by the\n//! running kernel is desired, use this function instead.\n//!\n//! Should new exception types be introduced in the future, this function will\n//! be updated to include their bits in the returned mask value when run time\n//! support is present.\nexception_mask_t ExcMaskValid();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_\n"
  },
  {
    "path": "util/mach/mach_extensions_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/mach_extensions.h\"\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"util/mac/mac_util.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(MachExtensions, MachThreadSelf) {\n  base::apple::ScopedMachSendRight thread_self(mach_thread_self());\n  EXPECT_EQ(MachThreadSelf(), thread_self);\n}\n\nTEST(MachExtensions, NewMachPort_Receive) {\n  base::apple::ScopedMachReceiveRight port(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_NE(port, kMachPortNull);\n\n  mach_port_type_t type;\n  kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"mach_port_get_type\");\n\n  EXPECT_EQ(type, MACH_PORT_TYPE_RECEIVE);\n}\n\nTEST(MachExtensions, NewMachPort_PortSet) {\n  base::apple::ScopedMachPortSet port(NewMachPort(MACH_PORT_RIGHT_PORT_SET));\n  ASSERT_NE(port, kMachPortNull);\n\n  mach_port_type_t type;\n  kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"mach_port_get_type\");\n\n  EXPECT_EQ(type, MACH_PORT_TYPE_PORT_SET);\n}\n\nTEST(MachExtensions, NewMachPort_DeadName) {\n  base::apple::ScopedMachSendRight port(NewMachPort(MACH_PORT_RIGHT_DEAD_NAME));\n  ASSERT_NE(port, kMachPortNull);\n\n  mach_port_type_t type;\n  kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"mach_port_get_type\");\n\n  EXPECT_EQ(type, MACH_PORT_TYPE_DEAD_NAME);\n}\n\nconstexpr exception_mask_t kExcMaskBasic =\n    EXC_MASK_BAD_ACCESS |\n    EXC_MASK_BAD_INSTRUCTION |\n    EXC_MASK_ARITHMETIC |\n    EXC_MASK_EMULATION |\n    EXC_MASK_SOFTWARE |\n    EXC_MASK_BREAKPOINT |\n    EXC_MASK_SYSCALL |\n    EXC_MASK_MACH_SYSCALL |\n    EXC_MASK_RPC_ALERT;\n\nTEST(MachExtensions, ExcMaskAll) {\n  const exception_mask_t exc_mask_all = ExcMaskAll();\n  EXPECT_EQ(exc_mask_all & kExcMaskBasic, kExcMaskBasic);\n\n  EXPECT_FALSE(exc_mask_all & EXC_MASK_CRASH);\n  EXPECT_FALSE(exc_mask_all & EXC_MASK_CORPSE_NOTIFY);\n\n#if BUILDFLAG(IS_IOS)\n  // Assume at least iOS 7 (≅ OS X 10.9).\n  EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);\n  EXPECT_TRUE(exc_mask_all & EXC_MASK_GUARD);\n#else  // BUILDFLAG(IS_IOS)\n  const int macos_version_number = MacOSVersionNumber();\n  if (macos_version_number >= 10'08'00) {\n    EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);\n  } else {\n    EXPECT_FALSE(exc_mask_all & EXC_MASK_RESOURCE);\n  }\n\n  if (macos_version_number >= 10'09'00) {\n    EXPECT_TRUE(exc_mask_all & EXC_MASK_GUARD);\n  } else {\n    EXPECT_FALSE(exc_mask_all & EXC_MASK_GUARD);\n  }\n#endif  // BUILDFLAG(IS_IOS)\n\n  // Bit 0 should not be set.\n  EXPECT_FALSE(ExcMaskAll() & 1);\n\n  // Every bit set in ExcMaskAll() must also be set in ExcMaskValid().\n  EXPECT_EQ(ExcMaskAll() & ExcMaskValid(), ExcMaskAll());\n}\n\nTEST(MachExtensions, ExcMaskValid) {\n  const exception_mask_t exc_mask_valid = ExcMaskValid();\n  EXPECT_EQ(exc_mask_valid & kExcMaskBasic, kExcMaskBasic);\n\n  EXPECT_TRUE(exc_mask_valid & EXC_MASK_CRASH);\n\n#if BUILDFLAG(IS_IOS)\n  // Assume at least iOS 9 (≅ OS X 10.11).\n  EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);\n  EXPECT_TRUE(exc_mask_valid & EXC_MASK_GUARD);\n  EXPECT_TRUE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);\n#else  // BUILDFLAG(IS_IOS)\n  const int macos_version_number = MacOSVersionNumber();\n  if (macos_version_number >= 10'08'00) {\n    EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);\n  } else {\n    EXPECT_FALSE(exc_mask_valid & EXC_MASK_RESOURCE);\n  }\n\n  if (macos_version_number >= 10'09'00) {\n    EXPECT_TRUE(exc_mask_valid & EXC_MASK_GUARD);\n  } else {\n    EXPECT_FALSE(exc_mask_valid & EXC_MASK_GUARD);\n  }\n\n  if (macos_version_number >= 10'11'00) {\n    EXPECT_TRUE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);\n  } else {\n    EXPECT_FALSE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);\n  }\n#endif  // BUILDFLAG(IS_IOS)\n\n  // Bit 0 should not be set.\n  EXPECT_FALSE(ExcMaskValid() & 1);\n\n  // There must be bits set in ExcMaskValid() that are not set in ExcMaskAll().\n  EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/mach_message.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/mach_message.h\"\n\n#include <Availability.h>\n\n#include <limits>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/clock.h\"\n#include \"util/misc/implicit_cast.h\"\n\n#if BUILDFLAG(IS_MAC)\n#include <bsm/libbsm.h>\n#endif  // BUILDFLAG(IS_MAC)\n\nnamespace crashpad {\n\nnamespace {\n\nconstexpr int kNanosecondsPerMillisecond = 1E6;\n\n// TimerRunning() determines whether |deadline| has passed. If |deadline| is\n// kMachMessageDeadlineWaitIndefinitely, |*timeout_options| is set to\n// MACH_MSG_OPTION_NONE, |*remaining_ms| is set to MACH_MSG_TIMEOUT_NONE, and\n// this function returns true. When used with mach_msg(), this will cause\n// indefinite waiting. In any other case, |*timeout_options| is set to\n// MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT, so mach_msg() will enforce a timeout\n// specified by |*remaining_ms|. If |deadline| is in the future, |*remaining_ms|\n// is set to the number of milliseconds remaining, which will always be a\n// positive value, and this function returns true. If |deadline| is\n// kMachMessageDeadlineNonblocking (indicating that no timer is in effect),\n// |*remaining_ms| is set to zero and this function returns true. Otherwise,\n// this function sets |*remaining_ms| to zero and returns false.\nbool TimerRunning(uint64_t deadline,\n                  mach_msg_timeout_t* remaining_ms,\n                  mach_msg_option_t* timeout_options) {\n  if (deadline == kMachMessageDeadlineWaitIndefinitely) {\n    *remaining_ms = MACH_MSG_TIMEOUT_NONE;\n    *timeout_options = MACH_MSG_OPTION_NONE;\n    return true;\n  }\n\n  *timeout_options = MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT;\n\n  if (deadline == kMachMessageDeadlineNonblocking) {\n    *remaining_ms = 0;\n    return true;\n  }\n\n  uint64_t now = ClockMonotonicNanoseconds();\n\n  if (now >= deadline) {\n    *remaining_ms = 0;\n  } else {\n    uint64_t remaining = deadline - now;\n\n    // Round to the nearest millisecond, taking care not to overflow.\n    constexpr int kHalfMillisecondInNanoseconds =\n        kNanosecondsPerMillisecond / 2;\n    if (remaining <=\n        std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) {\n      *remaining_ms = (remaining + kHalfMillisecondInNanoseconds) /\n                      kNanosecondsPerMillisecond;\n    } else {\n      *remaining_ms = remaining / kNanosecondsPerMillisecond;\n    }\n  }\n\n  return *remaining_ms != 0;\n}\n\n// This is an internal implementation detail of MachMessageWithDeadline(). It\n// determines whether |deadline| has expired, and what timeout value and\n// timeout-related options to pass to mach_msg() based on the value of\n// |deadline|. mach_msg() will only be called if TimerRunning() returns true or\n// if run_even_if_expired is true.\nmach_msg_return_t MachMessageWithDeadlineInternal(mach_msg_header_t* message,\n                                                  mach_msg_option_t options,\n                                                  mach_msg_size_t receive_size,\n                                                  mach_port_name_t receive_port,\n                                                  MachMessageDeadline deadline,\n                                                  mach_port_name_t notify_port,\n                                                  bool run_even_if_expired) {\n  mach_msg_timeout_t remaining_ms;\n  mach_msg_option_t timeout_options;\n  if (!TimerRunning(deadline, &remaining_ms, &timeout_options) &&\n      !run_even_if_expired) {\n    // Simulate the timed-out return values from mach_msg().\n    if (options & MACH_SEND_MSG) {\n      return MACH_SEND_TIMED_OUT;\n    }\n    if (options & MACH_RCV_MSG) {\n      return MACH_RCV_TIMED_OUT;\n    }\n    return MACH_MSG_SUCCESS;\n  }\n\n  // Turn off the passed-in timeout bits and replace them with the ones from\n  // TimerRunning(). Get the send_size value from message->msgh_size if sending\n  // a message.\n  return mach_msg(\n      message,\n      (options & ~(MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT)) | timeout_options,\n      options & MACH_SEND_MSG ? message->msgh_size : 0,\n      receive_size,\n      receive_port,\n      remaining_ms,\n      notify_port);\n}\n\n}  // namespace\n\nMachMessageDeadline MachMessageDeadlineFromTimeout(\n    mach_msg_timeout_t timeout_ms) {\n  switch (timeout_ms) {\n    case kMachMessageTimeoutNonblocking:\n      return kMachMessageDeadlineNonblocking;\n    case kMachMessageTimeoutWaitIndefinitely:\n      return kMachMessageDeadlineWaitIndefinitely;\n    default:\n      return ClockMonotonicNanoseconds() +\n             implicit_cast<uint64_t>(timeout_ms) * kNanosecondsPerMillisecond;\n  }\n}\n\nmach_msg_return_t MachMessageWithDeadline(mach_msg_header_t* message,\n                                          mach_msg_option_t options,\n                                          mach_msg_size_t receive_size,\n                                          mach_port_name_t receive_port,\n                                          MachMessageDeadline deadline,\n                                          mach_port_name_t notify_port,\n                                          bool run_even_if_expired) {\n  // mach_msg() actaully does return MACH_MSG_SUCCESS when not asked to send or\n  // receive anything. See 10.9.5 xnu-1504.15.3/osfmk/ipc/mach_msg.c\n  // mach_msg_overwrite_trap().\n  mach_msg_return_t mr = MACH_MSG_SUCCESS;\n\n  // Break up the send and receive into separate operations, so that the timeout\n  // can be recomputed from the deadline for each. Otherwise, the computed\n  // timeout will apply individually to the send and then to the receive, and\n  // the desired deadline could be exceeded.\n  //\n  // During sends, always set MACH_SEND_INTERRUPT, and during receives, always\n  // set MACH_RCV_INTERRUPT. If the caller didn’t specify these options, the\n  // calls will be retried with a recomputed deadline. If these bits weren’t\n  // set, the libsyscall wrapper (10.9.5\n  // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg() would restart\n  // interrupted calls with the original timeout value computed from the\n  // deadline, which would no longer correspond to the actual deadline. If the\n  // caller did specify these bits, don’t restart anything, because the caller\n  // wants to be notified of any interrupted calls.\n\n  if (options & MACH_SEND_MSG) {\n    do {\n      mr = MachMessageWithDeadlineInternal(\n          message,\n          (options & ~MACH_RCV_MSG) | MACH_SEND_INTERRUPT,\n          0,\n          MACH_PORT_NULL,\n          deadline,\n          notify_port,\n          run_even_if_expired);\n    } while (mr == MACH_SEND_INTERRUPTED && !(options & MACH_SEND_INTERRUPT));\n\n    if (mr != MACH_MSG_SUCCESS) {\n      return mr;\n    }\n  }\n\n  if (options & MACH_RCV_MSG) {\n    do {\n      mr = MachMessageWithDeadlineInternal(\n          message,\n          (options & ~MACH_SEND_MSG) | MACH_RCV_INTERRUPT,\n          receive_size,\n          receive_port,\n          deadline,\n          notify_port,\n          run_even_if_expired);\n    } while (mr == MACH_RCV_INTERRUPTED && !(options & MACH_RCV_INTERRUPT));\n  }\n\n  return mr;\n}\n\nvoid PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header,\n                                mach_msg_header_t* out_header) {\n  out_header->msgh_bits =\n      MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in_header->msgh_bits), 0);\n  out_header->msgh_size = sizeof(mig_reply_error_t);\n  out_header->msgh_remote_port = in_header->msgh_remote_port;\n  out_header->msgh_local_port = MACH_PORT_NULL;\n  out_header->msgh_reserved = 0;\n  out_header->msgh_id = in_header->msgh_id + 100;\n  reinterpret_cast<mig_reply_error_t*>(out_header)->NDR = NDR_record;\n}\n\nvoid SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error) {\n  reinterpret_cast<mig_reply_error_t*>(out_header)->RetCode = error;\n}\n\nconst mach_msg_trailer_t* MachMessageTrailerFromHeader(\n    const mach_msg_header_t* header) {\n  vm_address_t header_address = reinterpret_cast<vm_address_t>(header);\n  vm_address_t trailer_address = header_address + round_msg(header->msgh_size);\n  return reinterpret_cast<const mach_msg_trailer_t*>(trailer_address);\n}\n\nbool MachMessageDestroyReceivedPort(mach_port_t port,\n                                    mach_msg_type_name_t port_right_type) {\n  // This implements a subset of 10.10.5\n  // xnu-2782.40.9/libsyscall/mach/mach_msg.c mach_msg_destroy_port() that deals\n  // only with port rights that can be received in Mach messages.\n  switch (port_right_type) {\n    case MACH_MSG_TYPE_PORT_RECEIVE: {\n      kern_return_t kr = mach_port_mod_refs(\n          mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);\n      if (kr != KERN_SUCCESS) {\n        MACH_LOG(ERROR, kr) << \"mach_port_mod_refs\";\n        return false;\n      }\n      return true;\n    }\n\n    case MACH_MSG_TYPE_PORT_SEND:\n    case MACH_MSG_TYPE_PORT_SEND_ONCE: {\n      kern_return_t kr = mach_port_deallocate(mach_task_self(), port);\n      if (kr != KERN_SUCCESS) {\n        MACH_LOG(ERROR, kr) << \"mach_port_deallocate\";\n        return false;\n      }\n      return true;\n    }\n\n    default: {\n      LOG(ERROR) << \"unexpected port right type \" << port_right_type;\n      return false;\n    }\n  }\n}\n\n#if BUILDFLAG(IS_MAC)\n\npid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer) {\n  if (trailer->msgh_trailer_type != MACH_MSG_TRAILER_FORMAT_0) {\n    LOG(ERROR) << \"unexpected msgh_trailer_type \" << trailer->msgh_trailer_type;\n    return -1;\n  }\n  if (trailer->msgh_trailer_size <\n      REQUESTED_TRAILER_SIZE(kMachMessageReceiveAuditTrailer)) {\n    LOG(ERROR) << \"small msgh_trailer_size \" << trailer->msgh_trailer_size;\n    return -1;\n  }\n\n  const mach_msg_audit_trailer_t* audit_trailer =\n      reinterpret_cast<const mach_msg_audit_trailer_t*>(trailer);\n\n#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8\n  pid_t audit_pid;\n  audit_token_to_au32(audit_trailer->msgh_audit,\n                      nullptr,\n                      nullptr,\n                      nullptr,\n                      nullptr,\n                      nullptr,\n                      &audit_pid,\n                      nullptr,\n                      nullptr);\n#else\n  pid_t audit_pid = audit_token_to_pid(audit_trailer->msgh_audit);\n#endif\n\n  return audit_pid;\n}\n\n#endif  // BUILDFLAG(IS_MAC)\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/mach_message.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_\n#define CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_\n\n#include <mach/mach.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n//! \\brief A Mach message option specifying that an audit trailer should be\n//!     delivered during a receive operation.\n//!\n//! This constant is provided because the macros normally used to request this\n//! behavior are cumbersome.\nconstexpr mach_msg_option_t kMachMessageReceiveAuditTrailer =\n    MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |\n    MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);\n\n//! \\brief Special constants used as `mach_msg_timeout_t` values.\nenum : mach_msg_timeout_t {\n  //! \\brief When passed to MachMessageDeadlineFromTimeout(), that function will\n  //!     return #kMachMessageDeadlineNonblocking.\n  kMachMessageTimeoutNonblocking = 0,\n\n  //! \\brief When passed to MachMessageDeadlineFromTimeout(), that function will\n  //!     return #kMachMessageDeadlineWaitIndefinitely.\n  kMachMessageTimeoutWaitIndefinitely = 0xffffffff,\n};\n\n//! \\brief The time before which a MachMessageWithDeadline() call should\n//!     complete.\n//!\n//! A value of this type may be one of the special constants\n//! #kMachMessageDeadlineNonblocking or #kMachMessageDeadlineWaitIndefinitely.\n//! Any other values should be produced by calling\n//! MachMessageDeadlineFromTimeout().\n//!\n//! Internally, these are currently specified on the same time base as\n//! ClockMonotonicNanoseconds(), although this is an implementation detail.\nusing MachMessageDeadline = uint64_t;\n\n//! \\brief Special constants used as \\ref crashpad::MachMessageDeadline\n//!     \"MachMessageDeadline\" values.\nenum : MachMessageDeadline {\n  //! \\brief MachMessageWithDeadline() should not block at all in its operation.\n  kMachMessageDeadlineNonblocking = 0,\n\n  //! \\brief MachMessageWithDeadline() should wait indefinitely for the\n  //!     requested operation to complete.\n  kMachMessageDeadlineWaitIndefinitely = 0xffffffffffffffff,\n};\n\n//! \\brief Computes the deadline for a specified timeout value.\n//!\n//! While deadlines exist on an absolute time scale, timeouts are relative. This\n//! function calculates the deadline as \\a timeout_ms milliseconds after it\n//! executes.\n//!\n//! If \\a timeout_ms is #kMachMessageDeadlineNonblocking, this function will\n//! return #kMachMessageDeadlineNonblocking. If \\a timeout_ms is\n//! #kMachMessageTimeoutWaitIndefinitely, this function will return\n//! #kMachMessageDeadlineWaitIndefinitely.\nMachMessageDeadline MachMessageDeadlineFromTimeout(\n    mach_msg_timeout_t timeout_ms);\n\n//! \\brief Runs `mach_msg()` with a deadline, as opposed to a timeout.\n//!\n//! This function is similar to `mach_msg()`, with the following differences:\n//!  - The `timeout` parameter has been replaced by \\a deadline. The deadline\n//!    applies uniformly to a call that is requested to both send and receive\n//!    a message.\n//!  - The `MACH_SEND_TIMEOUT` and `MACH_RCV_TIMEOUT` bits in \\a options are\n//!    not used. Timeouts are specified by the \\a deadline argument.\n//!  - The `send_size` parameter has been removed. Its value is implied by\n//!    \\a message when \\a options contains `MACH_SEND_MSG`.\n//!  - The \\a run_even_if_expired parameter has been added.\n//!\n//! Like the `mach_msg()` wrapper in `libsyscall`, this function will retry\n//! operations when experiencing `MACH_SEND_INTERRUPTED` and\n//! `MACH_RCV_INTERRUPTED`, unless \\a options contains `MACH_SEND_INTERRUPT` or\n//! `MACH_RCV_INTERRUPT`. Unlike `mach_msg()`, which restarts the call with the\n//! full timeout when this occurs, this function continues enforcing the\n//! user-specified \\a deadline.\n//!\n//! Except as noted, the parameters and return value are identical to those of\n//! `mach_msg()`.\n//!\n//! \\param[in,out] message\n//! \\param[in] options\n//! \\param[in] receive_size\n//! \\param[in] receive_port\n//! \\param[in] deadline The time by which this call should complete. If the\n//!     deadline is exceeded, this call will return `MACH_SEND_TIMED_OUT` or\n//!     `MACH_RCV_TIMED_OUT`.\n//! \\param[in] notify_port\n//! \\param[in] run_even_if_expired If `true`, a deadline that is expired when\n//!     this function is called will be treated as though a deadline of\n//!     #kMachMessageDeadlineNonblocking had been specified. When `false`, an\n//!     expired deadline will result in a `MACH_SEND_TIMED_OUT` or\n//!     `MACH_RCV_TIMED_OUT` return value, even if the deadline is already\n//!     expired when the function is called.\n//!\n//! \\return The return value of `mach_msg()`\nmach_msg_return_t MachMessageWithDeadline(mach_msg_header_t* message,\n                                          mach_msg_option_t options,\n                                          mach_msg_size_t receive_size,\n                                          mach_port_name_t receive_port,\n                                          MachMessageDeadline deadline,\n                                          mach_port_name_t notify_port,\n                                          bool run_even_if_expired);\n\n//! \\brief Initializes a reply message for a MIG server routine based on its\n//!     corresponding request.\n//!\n//! If a request is handled by a server routine, it may be necessary to revise\n//! some of the fields set by this function, such as `msgh_size` and any fields\n//! defined in a routine’s reply structure type.\n//!\n//! \\param[in] in_header The request message to base the reply on.\n//! \\param[out] out_header The reply message to initialize. \\a out_header will\n//!     be treated as a `mig_reply_error_t*` and all of its fields will be set\n//!     except for `RetCode`, which must be set by SetMIGReplyError(). This\n//!     argument is accepted as a `mach_msg_header_t*` instead of a\n//!     `mig_reply_error_t*` because that is the type that callers are expected\n//!     to possess in the C API.\nvoid PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header,\n                                mach_msg_header_t* out_header);\n\n//! \\brief Sets the error code in a reply message for a MIG server routine.\n//!\n//! \\param[in,out] out_header The reply message to operate on. \\a out_header\n//!     will be treated as a `mig_reply_error_t*` and its `RetCode` field will\n//!     be set. This argument is accepted as a `mach_msg_header_t*` instead of a\n//!     `mig_reply_error_t*` because that is the type that callers are expected\n//!     to possess in the C API.\n//! \\param[in] error The error code to store in \\a out_header.\n//!\n//! \\sa PrepareMIGReplyFromRequest()\nvoid SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error);\n\n//! \\brief Returns a Mach message trailer for a message that has been received.\n//!\n//! This function must only be called on Mach messages that have been received\n//! via the Mach messaging interface, such as `mach_msg()`. Messages constructed\n//! for sending do not contain trailers.\n//!\n//! \\param[in] header A pointer to a received Mach message.\n//!\n//! \\return A pointer to the trailer following the received Mach message’s body.\n//!     The contents of the trailer depend on the options provided to\n//!     `mach_msg()` or a similar function when the message was received.\nconst mach_msg_trailer_t* MachMessageTrailerFromHeader(\n    const mach_msg_header_t* header);\n\n//! \\brief Destroys or deallocates a Mach port received in a Mach message.\n//!\n//! This function disposes of port rights received in a Mach message. Receive\n//! rights will be destroyed with `mach_port_mod_refs()`. Send and send-once\n//! rights will be deallocated with `mach_port_deallocate()`.\n//!\n//! \\param[in] port The port to destroy or deallocate.\n//! \\param[in] port_right_type The right type held for \\a port:\n//!     `MACH_MSG_TYPE_PORT_RECEIVE`, `MACH_MSG_TYPE_PORT_SEND`, or\n//!     `MACH_MSG_TYPE_PORT_SEND_ONCE`.\n//!\n//! \\return `true` on success, or `false` on failure with a message logged.\nbool MachMessageDestroyReceivedPort(mach_port_t port,\n                                    mach_msg_type_name_t port_right_type);\n\n#if BUILDFLAG(IS_MAC) || DOXYGEN\n\n//! \\brief Returns the process ID of a Mach message’s sender from its audit\n//!     trailer.\n//!\n//! For the audit trailer to be present, the message must have been received\n//! with #kMachMessageReceiveAuditTrailer or its macro equivalent specified in\n//! the receive options.\n//!\n//! If the kernel is the message’s sender, a process ID of `0` will be returned.\n//!\n//! \\param[in] trailer The trailer received with a Mach message.\n//!\n//! \\return The process ID of the message’s sender, or `-1` on failure with a\n//!     message logged. It is considered a failure for \\a trailer to not contain\n//!     audit information.\npid_t AuditPIDFromMachMessageTrailer(const mach_msg_trailer_t* trailer);\n\n#endif  // BUILDFLAG(IS_MAC)\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_MACH_MESSAGE_H_\n"
  },
  {
    "path": "util/mach/mach_message_server.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/mach_message_server.h\"\n\n#include <string.h>\n\n#include <limits>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/apple/scoped_mach_vm.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/mach/mach_message.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n//! \\brief Manages a dynamically-allocated buffer to be used for Mach messaging.\nclass MachMessageBuffer {\n public:\n  MachMessageBuffer() : vm_() {}\n\n  MachMessageBuffer(const MachMessageBuffer&) = delete;\n  MachMessageBuffer& operator=(const MachMessageBuffer&) = delete;\n\n  ~MachMessageBuffer() {}\n\n  //! \\return A pointer to the buffer.\n  mach_msg_header_t* Header() const {\n    return reinterpret_cast<mach_msg_header_t*>(vm_.address());\n  }\n\n  //! \\brief Ensures that this object has a buffer of exactly \\a size bytes\n  //!     available.\n  //!\n  //! If the existing buffer is a different size, it will be reallocated without\n  //! copying any of the old buffer’s contents to the new buffer. The contents\n  //! of the buffer are unspecified after this call, even if no reallocation is\n  //! performed.\n  kern_return_t Reallocate(vm_size_t size) {\n    // This test uses == instead of > so that a large reallocation to receive a\n    // large message doesn’t cause permanent memory bloat for the duration of\n    // a MachMessageServer::Run() loop.\n    if (size != vm_.size()) {\n      // reset() first, so that two allocations don’t exist simultaneously.\n      vm_.reset();\n\n      if (size) {\n        vm_address_t address;\n        kern_return_t kr =\n            vm_allocate(mach_task_self(),\n                        &address,\n                        size,\n                        VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG));\n        if (kr != KERN_SUCCESS) {\n          return kr;\n        }\n\n        vm_.reset(address, size);\n      }\n    }\n\n#if !defined(NDEBUG)\n    // Regardless of whether the allocation was changed, scribble over the\n    // memory to make sure that nothing relies on zero-initialization or stale\n    // contents.\n    memset(Header(), 0x66, size);\n#endif\n\n    return KERN_SUCCESS;\n  }\n\n private:\n  base::apple::ScopedMachVM vm_;\n};\n\n// Wraps MachMessageWithDeadline(), using a MachMessageBuffer argument which\n// will be resized to |receive_size| (after being page-rounded). MACH_RCV_MSG\n// is always combined into |options|.\nmach_msg_return_t MachMessageAllocateReceive(MachMessageBuffer* request,\n                                             mach_msg_option_t options,\n                                             mach_msg_size_t receive_size,\n                                             mach_port_name_t receive_port,\n                                             MachMessageDeadline deadline,\n                                             mach_port_name_t notify_port,\n                                             bool run_even_if_expired) {\n  mach_msg_size_t request_alloc = round_page(receive_size);\n  kern_return_t kr = request->Reallocate(request_alloc);\n  if (kr != KERN_SUCCESS) {\n    return kr;\n  }\n\n  return MachMessageWithDeadline(request->Header(),\n                                 options | MACH_RCV_MSG,\n                                 receive_size,\n                                 receive_port,\n                                 deadline,\n                                 notify_port,\n                                 run_even_if_expired);\n}\n\n}  // namespace\n\n// This method implements a server similar to 10.9.4\n// xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(). The server\n// callback function and |max_size| parameter have been replaced with a C++\n// interface. The |persistent| parameter has been added, allowing this method to\n// serve as a stand-in for mach_msg_server(). The |timeout_ms| parameter has\n// been added, allowing this function to not block indefinitely.\n//\n// static\nmach_msg_return_t MachMessageServer::Run(Interface* interface,\n                                         mach_port_t receive_port,\n                                         mach_msg_options_t options,\n                                         Persistent persistent,\n                                         ReceiveLarge receive_large,\n                                         mach_msg_timeout_t timeout_ms) {\n  options &= ~(MACH_RCV_MSG | MACH_SEND_MSG);\n\n  const MachMessageDeadline deadline =\n      MachMessageDeadlineFromTimeout(timeout_ms);\n\n  if (receive_large == kReceiveLargeResize) {\n    options |= MACH_RCV_LARGE;\n  } else {\n    options &= ~MACH_RCV_LARGE;\n  }\n\n  const mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options);\n  const mach_msg_size_t expected_receive_size =\n      round_msg(interface->MachMessageServerRequestSize()) + trailer_alloc;\n  const mach_msg_size_t request_size = (receive_large == kReceiveLargeResize)\n                                           ? round_page(expected_receive_size)\n                                           : expected_receive_size;\n  DCHECK_GE(request_size, sizeof(mach_msg_empty_rcv_t));\n\n  // mach_msg_server() and mach_msg_server_once() would consider whether\n  // |options| contains MACH_SEND_TRAILER and include MAX_TRAILER_SIZE in this\n  // computation if it does, but that option is ineffective on macOS.\n  const mach_msg_size_t reply_size = interface->MachMessageServerReplySize();\n  DCHECK_GE(reply_size, sizeof(mach_msg_empty_send_t));\n  const mach_msg_size_t reply_alloc = round_page(reply_size);\n\n  MachMessageBuffer request;\n  MachMessageBuffer reply;\n  bool received_any_request = false;\n  bool retry;\n\n  kern_return_t kr;\n\n  do {\n    retry = false;\n\n    kr = MachMessageAllocateReceive(&request,\n                                    options,\n                                    request_size,\n                                    receive_port,\n                                    deadline,\n                                    MACH_PORT_NULL,\n                                    !received_any_request);\n    if (kr == MACH_RCV_TOO_LARGE) {\n      switch (receive_large) {\n        case kReceiveLargeError:\n          break;\n\n        case kReceiveLargeIgnore:\n          // Try again, even in one-shot mode. The caller is expecting this\n          // method to take action on the first message in the queue, and has\n          // indicated that they want large messages to be ignored. The\n          // alternatives, which might involve returning MACH_MSG_SUCCESS,\n          // MACH_RCV_TIMED_OUT, or MACH_RCV_TOO_LARGE to a caller that\n          // specified one-shot behavior, all seem less correct than retrying.\n          MACH_LOG(WARNING, kr) << \"mach_msg: ignoring large message\";\n          retry = true;\n          continue;\n\n        case kReceiveLargeResize: {\n          mach_msg_size_t this_request_size = round_page(\n              round_msg(request.Header()->msgh_size) + trailer_alloc);\n          DCHECK_GT(this_request_size, request_size);\n\n          kr = MachMessageAllocateReceive(&request,\n                                          options & ~MACH_RCV_LARGE,\n                                          this_request_size,\n                                          receive_port,\n                                          deadline,\n                                          MACH_PORT_NULL,\n                                          !received_any_request);\n\n          break;\n        }\n      }\n    }\n\n    if (kr != MACH_MSG_SUCCESS) {\n      return kr;\n    }\n\n    received_any_request = true;\n\n    kr = reply.Reallocate(reply_alloc);\n    if (kr != KERN_SUCCESS) {\n      return kr;\n    }\n\n    mach_msg_header_t* request_header = request.Header();\n    mach_msg_header_t* reply_header = reply.Header();\n    bool destroy_complex_request = false;\n    interface->MachMessageServerFunction(\n        request_header, reply_header, &destroy_complex_request);\n\n    if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {\n      // This only works if the reply message is not complex, because otherwise,\n      // the location of the RetCode field is not known. It should be possible\n      // to locate the RetCode field by looking beyond the descriptors in a\n      // complex reply message, but this is not currently done. This behavior\n      // has not proven itself necessary in practice, and it’s not done by\n      // mach_msg_server() or mach_msg_server_once() either.\n      mig_reply_error_t* reply_mig =\n          reinterpret_cast<mig_reply_error_t*>(reply_header);\n      if (reply_mig->RetCode == MIG_NO_REPLY) {\n        reply_header->msgh_remote_port = MACH_PORT_NULL;\n      } else if (reply_mig->RetCode != KERN_SUCCESS &&\n                 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {\n        destroy_complex_request = true;\n      }\n    }\n\n    if (destroy_complex_request &&\n        request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {\n      request_header->msgh_remote_port = MACH_PORT_NULL;\n      mach_msg_destroy(request_header);\n    }\n\n    if (reply_header->msgh_remote_port != MACH_PORT_NULL) {\n      // Avoid blocking indefinitely. This duplicates the logic in 10.9.5\n      // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg_server_once(),\n      // although the special provision for sending to a send-once right is not\n      // made, because kernel keeps sends to a send-once right on the fast path\n      // without considering the user-specified timeout. See 10.9.5\n      // xnu-2422.115.4/osfmk/ipc/ipc_mqueue.c ipc_mqueue_send().\n      const MachMessageDeadline send_deadline =\n          deadline == kMachMessageDeadlineWaitIndefinitely\n              ? kMachMessageDeadlineNonblocking\n              : deadline;\n\n      kr = MachMessageWithDeadline(reply_header,\n                                   options | MACH_SEND_MSG,\n                                   0,\n                                   MACH_PORT_NULL,\n                                   send_deadline,\n                                   MACH_PORT_NULL,\n                                   true);\n\n      if (kr != MACH_MSG_SUCCESS) {\n        if (kr == MACH_SEND_INVALID_DEST ||\n            kr == MACH_SEND_TIMED_OUT ||\n            kr == MACH_SEND_INTERRUPTED) {\n          mach_msg_destroy(reply_header);\n        }\n        return kr;\n      }\n    }\n  } while (persistent == kPersistent || retry);\n\n  return kr;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/mach_message_server.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_\n#define CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_\n\n#include <mach/mach.h>\n\n#include <set>\n\n\nnamespace crashpad {\n\n//! \\brief Runs a Mach message server to handle a Mach RPC request for MIG\n//!     servers.\n//!\n//! The principal entry point to this interface is the static Run() method.\nclass MachMessageServer {\n public:\n  //! \\brief A Mach RPC callback interface, called by Run().\n  class Interface {\n   public:\n    //! \\brief Handles a Mach RPC request.\n    //!\n    //! This method is a stand-in for a MIG-generated Mach RPC server “demux”\n    //! function such as `exc_server()` and `mach_exc_server()`. Implementations\n    //! may call such a function directly. This method is expected to behave\n    //! exactly as these functions behave.\n    //!\n    //! \\param[in] in The request message, received as a Mach message. Note that\n    //!     this interface uses a `const` parameter for this purpose, whereas\n    //!     MIG-generated “demux” functions do not.\n    //! \\param[out] out The reply message. The caller allocates storage, and the\n    //!     callee is expected to populate the reply message appropriately.\n    //!     After returning, the caller will send this reply as a Mach message\n    //!     via the message’s reply port.\n    //! \\param[out] destroy_complex_request `true` if a complex request message\n    //!     is to be destroyed even when handled successfully, `false`\n    //!     otherwise. The traditional behavior is `false`. In this case, the\n    //!     caller only destroys the request message in \\a in when the reply\n    //!     message in \\a out is not complex and when it indicates a return code\n    //!     other than `KERN_SUCCESS` or `MIG_NO_REPLY`. The assumption is that\n    //!     the rights or out-of-line data carried in a complex message may be\n    //!     retained by the server in this situation, and that it is the\n    //!     responsibility of the server to release these resources as needed.\n    //!     However, in many cases, these resources are not needed beyond the\n    //!     duration of a request-reply transaction, and in such cases, it is\n    //!     less error-prone to always have the caller,\n    //!     MachMessageServer::Run(), destroy complex request messages. To\n    //!     choose this behavior, this parameter should be set to `true`.\n    //!\n    //! \\return `true` on success and `false` on failure, although the caller\n    //!     ignores the return value. However, the return code to be included in\n    //!     the reply message should be set as `mig_reply_error_t::RetCode`. The\n    //!     non-`void` return value is used for increased compatibility with\n    //!     MIG-generated functions.\n    virtual bool MachMessageServerFunction(const mach_msg_header_t* in,\n                                           mach_msg_header_t* out,\n                                           bool* destroy_complex_request) = 0;\n\n    //! \\return The set of request message Mach message IDs that\n    //!     MachMessageServerFunction() is able to handle.\n    virtual std::set<mach_msg_id_t> MachMessageServerRequestIDs() = 0;\n\n    //! \\return The expected or maximum size, in bytes, of a request message to\n    //!     be received as the \\a in parameter of MachMessageServerFunction().\n    virtual mach_msg_size_t MachMessageServerRequestSize() = 0;\n\n    //! \\return The maximum size, in bytes, of a reply message to be sent via\n    //!     the \\a out parameter of MachMessageServerFunction(). This value does\n    //!     not need to include the size of any trailer to be sent with the\n    //!     message.\n    virtual mach_msg_size_t MachMessageServerReplySize() = 0;\n\n   protected:\n    ~Interface() {}\n  };\n\n  //! \\brief Informs Run() whether to handle a single request-reply transaction\n  //!     or to run in a loop.\n  enum Persistent {\n    //! \\brief Handle a single request-reply transaction and then return.\n    kOneShot = false,\n\n    //! \\brief Run in a loop, potentially handling multiple request-reply\n    //!     transactions.\n    kPersistent,\n  };\n\n  //! \\brief Determines how to handle the reception of messages larger than the\n  //!     size of the buffer allocated to store them.\n  enum ReceiveLarge {\n    //! \\brief Return `MACH_RCV_TOO_LARGE` upon receipt of a large message.\n    //!\n    //! This mimics the default behavior of `mach_msg_server()` when `options`\n    //! does not contain `MACH_RCV_LARGE`.\n    kReceiveLargeError = 0,\n\n    //! \\brief Ignore large messages, and attempt to receive the next queued\n    //!     message upon encountering one.\n    //!\n    //! When a large message is encountered, a warning will be logged.\n    //!\n    //! `mach_msg()` will be called to receive the next message after a large\n    //! one even when accompanied by a #Persistent value of #kOneShot.\n    kReceiveLargeIgnore,\n\n    //! \\brief Allocate an appropriately-sized buffer upon encountering a large\n    //!     message. The buffer will be used to receive the message. This\n    //!\n    //! This mimics the behavior of `mach_msg_server()` when `options` contains\n    //! `MACH_RCV_LARGE`.\n    kReceiveLargeResize,\n  };\n\n  MachMessageServer() = delete;\n  MachMessageServer(const MachMessageServer&) = delete;\n  MachMessageServer& operator=(const MachMessageServer&) = delete;\n\n  //! \\brief Runs a Mach message server to handle a Mach RPC request for MIG\n  //!     servers.\n  //!\n  //! This function listens for a request message and passes it to a callback\n  //! interface. A reponse is collected from that interface, and is sent back as\n  //! a reply.\n  //!\n  //! This function is similar to `mach_msg_server()` and\n  //! `mach_msg_server_once()`.\n  //!\n  //! \\param[in] interface The MachMessageServerInterface that is responsible\n  //!     for handling the message. Interface::MachMessageServerRequestSize() is\n  //!     used as the receive size for the request message, and\n  //!     Interface::MachMessageServerReplySize() is used as the\n  //!     maximum size of the reply message. If \\a options contains\n  //!     `MACH_RCV_LARGE`, this function will retry a receive operation that\n  //!     returns `MACH_RCV_TOO_LARGE` with an appropriately-sized buffer.\n  //!     MachMessageServerInterface::MachMessageServerFunction() is called to\n  //!     handle the request and populate the reply.\n  //! \\param[in] receive_port The port on which to receive the request message.\n  //! \\param[in] options Options suitable for mach_msg. For the defaults, use\n  //!     `MACH_MSG_OPTION_NONE`. `MACH_RCV_LARGE` when specified here is\n  //!     ignored. Set \\a receive_large to #kReceiveLargeResize instead.\n  //! \\param[in] persistent Chooses between one-shot and persistent operation.\n  //! \\param[in] receive_large Determines the behavior upon encountering a\n  //!     message larger than the receive buffer’s size.\n  //! \\param[in] timeout_ms The maximum duration that this entire function will\n  //!     run, in milliseconds. This may be #kMachMessageTimeoutNonblocking or\n  //!     #kMachMessageTimeoutWaitIndefinitely. When \\a persistent is\n  //!     #kPersistent, the timeout applies to the overall duration of this\n  //!     function, not to any individual `mach_msg()` call.\n  //!\n  //! \\return On success, `MACH_MSG_SUCCESS` (when \\a persistent is #kOneShot)\n  //!     or `MACH_RCV_TIMED_OUT` (when \\a persistent is #kOneShot and \\a\n  //!     timeout_ms is not #kMachMessageTimeoutWaitIndefinitely). This function\n  //!     has no successful return value when \\a persistent is #kPersistent and\n  //!     \\a timeout_ms is #kMachMessageTimeoutWaitIndefinitely. On failure,\n  //!     returns a value identifying the nature of the error. A request\n  //!     received with a reply port that is (or becomes) a dead name before the\n  //!     reply is sent will result in `MACH_SEND_INVALID_DEST` as a return\n  //!     value, which may or may not be considered an error from the caller’s\n  //!     perspective.\n  static mach_msg_return_t Run(Interface* interface,\n                               mach_port_t receive_port,\n                               mach_msg_options_t options,\n                               Persistent persistent,\n                               ReceiveLarge receive_large,\n                               mach_msg_timeout_t timeout_ms);\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_MACH_MESSAGE_SERVER_H_\n"
  },
  {
    "path": "util/mach/mach_message_server_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/mach_message_server.h\"\n\n#include <mach/mach.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <set>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"test/mac/mach_multiprocess.h\"\n#include \"util/file/file_io.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass TestMachMessageServer : public MachMessageServer::Interface,\n                              public MachMultiprocess {\n public:\n  struct Options {\n    // The type of reply port that the client should put in its request message.\n    enum ReplyPortType {\n      // The normal reply port is the client’s local port, to which it holds\n      // a receive right. This allows the server to respond directly to the\n      // client. The client will expect a reply.\n      kReplyPortNormal,\n\n      // Use MACH_PORT_NULL as the reply port, which the server should detect\n      // avoid attempting to send a message to, and return success. The client\n      // will not expect a reply.\n      kReplyPortNull,\n\n      // Make the server see the reply port as a dead name by setting the reply\n      // port to a receive right and then destroying that right before the\n      // server processes the request. The server should return\n      // MACH_SEND_INVALID_DEST, and the client will not expect a reply.\n      kReplyPortDead,\n    };\n\n    Options()\n        : expect_server_interface_method_called(true),\n          parent_wait_for_child_pipe(false),\n          server_options(MACH_MSG_OPTION_NONE),\n          server_persistent(MachMessageServer::kOneShot),\n          server_receive_large(MachMessageServer::kReceiveLargeError),\n          server_timeout_ms(kMachMessageTimeoutWaitIndefinitely),\n          server_mig_retcode(KERN_SUCCESS),\n          server_destroy_complex(true),\n          expect_server_destroyed_complex(true),\n          expect_server_result(KERN_SUCCESS),\n          expect_server_transaction_count(1),\n          child_wait_for_parent_pipe_early(false),\n          client_send_request_count(1),\n          client_send_complex(false),\n          client_send_large(false),\n          client_reply_port_type(kReplyPortNormal),\n          client_expect_reply(true),\n          child_send_all_requests_before_receiving_any_replies(false),\n          child_wait_for_parent_pipe_late(false) {\n    }\n\n    // true if MachMessageServerFunction() is expected to be called.\n    bool expect_server_interface_method_called;\n\n    // true if the parent should wait for the child to write a byte to the pipe\n    // as a signal that the child is ready for the parent to begin its side of\n    // the test. This is used for nonblocking tests, which require that there\n    // be something in the server’s queue before attempting a nonblocking\n    // receive if the receive is to be successful.\n    bool parent_wait_for_child_pipe;\n\n    // Options to pass to MachMessageServer::Run() as the |options| parameter.\n    mach_msg_options_t server_options;\n\n    // Whether the server should run in one-shot or persistent mode.\n    MachMessageServer::Persistent server_persistent;\n\n    // The strategy for handling large messages.\n    MachMessageServer::ReceiveLarge server_receive_large;\n\n    // The server’s timeout in milliseconds, or kMachMessageTimeoutNonblocking\n    // or kMachMessageTimeoutWaitIndefinitely.\n    mach_msg_timeout_t server_timeout_ms;\n\n    // The return code that the server returns to the client via the\n    // mig_reply_error_t::RetCode field. A client would normally see this as\n    // a Mach RPC return value.\n    kern_return_t server_mig_retcode;\n\n    // The value that the server function should set its destroy_complex_request\n    // parameter to. This is true if resources sent in complex request messages\n    // should be destroyed, and false if they should not be destroyed, assuming\n    // that the server function indicates success.\n    bool server_destroy_complex;\n\n    // Whether to expect the server to destroy a complex message. Even if\n    // server_destroy_complex is false, a complex message will be destroyed if\n    // the MIG return code was unsuccessful.\n    bool expect_server_destroyed_complex;\n\n    // The expected return value from MachMessageServer::Run().\n    kern_return_t expect_server_result;\n\n    // The number of transactions that the server is expected to handle.\n    size_t expect_server_transaction_count;\n\n    // true if the child should wait for the parent to signal that it’s ready\n    // for the child to begin sending requests via the pipe. This is done if the\n    // parent needs to perform operations on its receive port before the child\n    // should be permitted to send anything to it. Currently, this is used to\n    // allow the parent to ensure that the receive port’s queue length is high\n    // enough before the child begins attempting to fill it.\n    bool child_wait_for_parent_pipe_early;\n\n    // The number of requests that the client should send to the server.\n    size_t client_send_request_count;\n\n    // true if the client should send a complex message, one that carries a port\n    // descriptor in its body. Normally false.\n    bool client_send_complex;\n\n    // true if the client should send a larger message than the server has\n    // allocated space to receive. The server’s response is directed by\n    // server_receive_large.\n    bool client_send_large;\n\n    // The type of reply port that the client should provide in its request’s\n    // mach_msg_header_t::msgh_local_port, which will appear to the server as\n    // mach_msg_header_t::msgh_remote_port.\n    ReplyPortType client_reply_port_type;\n\n    // true if the client should wait for a reply from the server. For\n    // non-normal reply ports or requests which the server responds to with no\n    // reply (MIG_NO_REPLY), the server will either not send a reply or not\n    // succeed in sending a reply, and the child process should not wait for\n    // one.\n    bool client_expect_reply;\n\n    // true if the client should send all requests before attempting to receive\n    // any replies from the server. This is used for the persistent nonblocking\n    // test, which requires the client to fill the server’s queue before the\n    // server can attempt processing it.\n    bool child_send_all_requests_before_receiving_any_replies;\n\n    // true if the child should wait to receive a byte from the parent before\n    // exiting. This can be used to keep a receive right in the child alive\n    // until the parent has a chance to verify that it’s holding a send right.\n    // Otherwise, the right might appear in the parent as a dead name if the\n    // child exited before the parent had a chance to examine it. This would be\n    // a race.\n    bool child_wait_for_parent_pipe_late;\n  };\n\n  explicit TestMachMessageServer(const Options& options)\n      : MachMessageServer::Interface(),\n        MachMultiprocess(),\n        options_(options),\n        child_complex_message_port_(),\n        parent_complex_message_port_(MACH_PORT_NULL) {\n  }\n\n  TestMachMessageServer(const TestMachMessageServer&) = delete;\n  TestMachMessageServer& operator=(const TestMachMessageServer&) = delete;\n\n  // Runs the test.\n  void Test() {\n    EXPECT_EQ(replies_, requests_);\n    uint32_t start = requests_;\n\n    Run();\n\n    EXPECT_EQ(replies_, requests_);\n    EXPECT_EQ(requests_ - start, options_.expect_server_transaction_count);\n  }\n\n  // MachMessageServerInterface:\n\n  virtual bool MachMessageServerFunction(\n      const mach_msg_header_t* in,\n      mach_msg_header_t* out,\n      bool* destroy_complex_request) override {\n    *destroy_complex_request = options_.server_destroy_complex;\n\n    EXPECT_TRUE(options_.expect_server_interface_method_called);\n    if (!options_.expect_server_interface_method_called) {\n      return false;\n    }\n\n    struct ReceiveRequestMessage : public RequestMessage {\n      mach_msg_trailer_t trailer;\n    };\n\n    struct ReceiveLargeRequestMessage : public LargeRequestMessage {\n      mach_msg_trailer_t trailer;\n    };\n\n    const ReceiveRequestMessage* request =\n        reinterpret_cast<const ReceiveRequestMessage*>(in);\n    const mach_msg_bits_t expect_msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) |\n        (options_.client_send_complex ? MACH_MSGH_BITS_COMPLEX : 0);\n    EXPECT_EQ(request->header.msgh_bits, expect_msgh_bits);\n    EXPECT_EQ(request->header.msgh_size,\n              options_.client_send_large ? sizeof(LargeRequestMessage)\n                                         : sizeof(RequestMessage));\n    if (options_.client_reply_port_type == Options::kReplyPortNormal) {\n      EXPECT_EQ(request->header.msgh_remote_port, RemotePort());\n    }\n    EXPECT_EQ(request->header.msgh_local_port, LocalPort());\n    EXPECT_EQ(request->header.msgh_id, kRequestMessageID);\n    if (options_.client_send_complex) {\n      EXPECT_EQ(request->body.msgh_descriptor_count, 1u);\n      EXPECT_NE(request->port_descriptor.name, kMachPortNull);\n      parent_complex_message_port_ = request->port_descriptor.name;\n      EXPECT_EQ(request->port_descriptor.disposition,\n                implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_MOVE_SEND));\n      EXPECT_EQ(\n          request->port_descriptor.type,\n          implicit_cast<mach_msg_descriptor_type_t>(MACH_MSG_PORT_DESCRIPTOR));\n    } else {\n      EXPECT_EQ(request->body.msgh_descriptor_count, 0u);\n      EXPECT_EQ(request->port_descriptor.name, kMachPortNull);\n      EXPECT_EQ(request->port_descriptor.disposition, 0u);\n      EXPECT_EQ(request->port_descriptor.type, 0u);\n    }\n    EXPECT_EQ(memcmp(&request->ndr, &NDR_record, sizeof(NDR_record)), 0);\n    EXPECT_EQ(request->number, requests_);\n\n    // Look for the trailer in the right spot, depending on whether the request\n    // message was a RequestMessage or a LargeRequestMessage.\n    const mach_msg_trailer_t* trailer;\n    if (options_.client_send_large) {\n      const ReceiveLargeRequestMessage* large_request =\n          reinterpret_cast<const ReceiveLargeRequestMessage*>(request);\n      for (size_t index = 0; index < sizeof(large_request->data); ++index) {\n        EXPECT_EQ(large_request->data[index], '!');\n      }\n      trailer = &large_request->trailer;\n    } else {\n      trailer = &request->trailer;\n    }\n\n    EXPECT_EQ(\n        trailer->msgh_trailer_type,\n        implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0));\n    EXPECT_EQ(trailer->msgh_trailer_size, MACH_MSG_TRAILER_MINIMUM_SIZE);\n\n    ++requests_;\n\n    ReplyMessage* reply = reinterpret_cast<ReplyMessage*>(out);\n    reply->Head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);\n    reply->Head.msgh_size = sizeof(*reply);\n    reply->Head.msgh_remote_port = request->header.msgh_remote_port;\n    reply->Head.msgh_local_port = MACH_PORT_NULL;\n    reply->Head.msgh_id = kReplyMessageID;\n    reply->NDR = NDR_record;\n    reply->RetCode = options_.server_mig_retcode;\n    reply->number = replies_++;\n\n    return true;\n  }\n\n  std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {\n    static constexpr mach_msg_id_t request_ids[] = {kRequestMessageID};\n    return std::set<mach_msg_id_t>(&request_ids[0],\n                                   &request_ids[std::size(request_ids)]);\n  }\n\n  mach_msg_size_t MachMessageServerRequestSize() override {\n    return sizeof(RequestMessage);\n  }\n\n  mach_msg_size_t MachMessageServerReplySize() override {\n    return sizeof(ReplyMessage);\n  }\n\n private:\n  struct RequestMessage : public mach_msg_base_t {\n    // If body.msgh_descriptor_count is 0, port_descriptor will still be\n    // present, but it will be zeroed out. It wouldn’t normally be present in a\n    // message froma MIG-generated interface, but it’s harmless and simpler to\n    // leave it here and just treat it as more data.\n    mach_msg_port_descriptor_t port_descriptor;\n\n    NDR_record_t ndr;\n    uint32_t number;\n  };\n\n  // LargeRequestMessage is larger enough than a regular RequestMessage to\n  // ensure that whatever buffer was allocated to receive a RequestMessage is\n  // not large enough to receive a LargeRequestMessage.\n  struct LargeRequestMessage : public RequestMessage {\n    uint8_t data[4 * PAGE_MAX_SIZE];\n  };\n\n  struct ReplyMessage : public mig_reply_error_t {\n    uint32_t number;\n  };\n\n  // MachMultiprocess:\n\n  void MachMultiprocessParent() override {\n    mach_port_t local_port = LocalPort();\n\n    kern_return_t kr;\n    if (options_.child_send_all_requests_before_receiving_any_replies) {\n      // On OS X 10.10, the queue limit of a new Mach port seems to be 2 by\n      // default, which is below the value of MACH_PORT_QLIMIT_DEFAULT. Set the\n      // port’s queue limit explicitly here.\n      mach_port_limits limits = {};\n      limits.mpl_qlimit = MACH_PORT_QLIMIT_DEFAULT;\n      kr = mach_port_set_attributes(mach_task_self(),\n                                    local_port,\n                                    MACH_PORT_LIMITS_INFO,\n                                    reinterpret_cast<mach_port_info_t>(&limits),\n                                    MACH_PORT_LIMITS_INFO_COUNT);\n      ASSERT_EQ(kr, KERN_SUCCESS)\n          << MachErrorMessage(kr, \"mach_port_set_attributes\");\n    }\n\n    if (options_.child_wait_for_parent_pipe_early) {\n      // Tell the child to begin sending messages.\n      char c = '\\0';\n      CheckedWriteFile(WritePipeHandle(), &c, 1);\n    }\n\n    if (options_.parent_wait_for_child_pipe) {\n      // Wait until the child is done sending what it’s going to send.\n      char c;\n      CheckedReadFileExactly(ReadPipeHandle(), &c, 1);\n      EXPECT_EQ(c, '\\0');\n    }\n\n    ASSERT_EQ((kr = MachMessageServer::Run(this,\n                                           local_port,\n                                           options_.server_options,\n                                           options_.server_persistent,\n                                           options_.server_receive_large,\n                                           options_.server_timeout_ms)),\n              options_.expect_server_result)\n        << MachErrorMessage(kr, \"MachMessageServer\");\n\n    if (options_.client_send_complex) {\n      EXPECT_NE(parent_complex_message_port_, kMachPortNull);\n      mach_port_type_t type;\n\n      if (!options_.expect_server_destroyed_complex) {\n        // MachMessageServer should not have destroyed the resources sent in the\n        // complex request message.\n        kr = mach_port_type(\n            mach_task_self(), parent_complex_message_port_, &type);\n        EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"mach_port_type\");\n        EXPECT_EQ(type, MACH_PORT_TYPE_SEND);\n\n        // Destroy the resources here.\n        kr = mach_port_deallocate(mach_task_self(),\n                                  parent_complex_message_port_);\n        EXPECT_EQ(kr, KERN_SUCCESS)\n            << MachErrorMessage(kr, \"mach_port_deallocate\");\n      }\n\n      // The kernel won’t have reused the same name for another Mach port in\n      // this task so soon. It’s possible that something else in this task could\n      // have reused the name, but it’s unlikely for that to have happened in\n      // this test environment.\n      kr =\n          mach_port_type(mach_task_self(), parent_complex_message_port_, &type);\n      EXPECT_EQ(kr, KERN_INVALID_NAME)\n          << MachErrorMessage(kr, \"mach_port_type\");\n    }\n\n    if (options_.child_wait_for_parent_pipe_late) {\n      // Let the child know it’s safe to exit.\n      char c = '\\0';\n      CheckedWriteFile(WritePipeHandle(), &c, 1);\n    }\n  }\n\n  void MachMultiprocessChild() override {\n    if (options_.child_wait_for_parent_pipe_early) {\n      // Wait until the parent is done setting things up on its end.\n      char c;\n      CheckedReadFileExactly(ReadPipeHandle(), &c, 1);\n      EXPECT_EQ(c, '\\0');\n    }\n\n    for (size_t index = 0;\n         index < options_.client_send_request_count;\n         ++index) {\n      if (options_.child_send_all_requests_before_receiving_any_replies) {\n        // For this test, all of the messages need to go into the queue before\n        // the parent is allowed to start processing them. Don’t attempt to\n        // process replies before all of the requests are sent, because the\n        // server won’t have sent any replies until all of the requests are in\n        // its queue.\n        ASSERT_NO_FATAL_FAILURE(ChildSendRequest());\n      } else {\n        ASSERT_NO_FATAL_FAILURE(ChildSendRequestAndWaitForReply());\n      }\n    }\n\n    if (options_.parent_wait_for_child_pipe &&\n        options_.child_send_all_requests_before_receiving_any_replies) {\n      // Now that all of the requests have been sent, let the parent know that\n      // it’s safe to begin processing them, and then wait for the replies.\n      ASSERT_NO_FATAL_FAILURE(ChildNotifyParentViaPipe());\n\n      for (size_t index = 0;\n           index < options_.client_send_request_count;\n           ++index) {\n        ASSERT_NO_FATAL_FAILURE(ChildWaitForReply());\n      }\n    }\n\n    if (options_.child_wait_for_parent_pipe_late) {\n      char c;\n      CheckedReadFileExactly(ReadPipeHandle(), &c, 1);\n      ASSERT_EQ(c, '\\0');\n    }\n  }\n\n  // In the child process, sends a request message to the server.\n  void ChildSendRequest() {\n    // local_receive_port_owner will the receive right that is created in this\n    // scope and intended to be destroyed when leaving this scope, after it has\n    // been carried in a Mach message.\n    base::apple::ScopedMachReceiveRight local_receive_port_owner;\n\n    // A LargeRequestMessage is always allocated, but the message that will be\n    // sent will be a normal RequestMessage due to the msgh_size field\n    // indicating the size of the smaller base structure unless\n    // options_.client_send_large is true.\n    LargeRequestMessage request = {};\n\n    request.header.msgh_bits =\n        MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |\n        (options_.client_send_complex ? MACH_MSGH_BITS_COMPLEX : 0);\n    request.header.msgh_size = options_.client_send_large ?\n        sizeof(LargeRequestMessage) : sizeof(RequestMessage);\n    request.header.msgh_remote_port = RemotePort();\n    kern_return_t kr;\n    switch (options_.client_reply_port_type) {\n      case Options::kReplyPortNormal:\n        request.header.msgh_local_port = LocalPort();\n        break;\n      case Options::kReplyPortNull:\n        request.header.msgh_local_port = MACH_PORT_NULL;\n        break;\n      case Options::kReplyPortDead: {\n        // Use a newly-allocated receive right that will be destroyed when this\n        // method returns. A send right will be made from this receive right and\n        // carried in the request message to the server. By the time the server\n        // looks at the right, it will have become a dead name.\n        local_receive_port_owner.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n        ASSERT_TRUE(local_receive_port_owner.is_valid());\n        request.header.msgh_local_port = local_receive_port_owner.get();\n        break;\n      }\n    }\n    request.header.msgh_id = kRequestMessageID;\n    if (options_.client_send_complex) {\n      // Allocate a new receive right in this process and make a send right that\n      // will appear in the parent process. This is used to test that the server\n      // properly handles ownership of resources received in complex messages.\n      request.body.msgh_descriptor_count = 1;\n      child_complex_message_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n      ASSERT_TRUE(child_complex_message_port_.is_valid());\n      request.port_descriptor.name = child_complex_message_port_.get();\n      request.port_descriptor.disposition = MACH_MSG_TYPE_MAKE_SEND;\n      request.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;\n    } else {\n      request.body.msgh_descriptor_count = 0;\n      request.port_descriptor.name = MACH_PORT_NULL;\n      request.port_descriptor.disposition = 0;\n      request.port_descriptor.type = 0;\n    }\n    request.ndr = NDR_record;\n    request.number = requests_++;\n\n    if (options_.client_send_large) {\n      memset(request.data, '!', sizeof(request.data));\n    }\n\n    kr = mach_msg(&request.header,\n                  MACH_SEND_MSG | MACH_SEND_TIMEOUT,\n                  request.header.msgh_size,\n                  0,\n                  MACH_PORT_NULL,\n                  MACH_MSG_TIMEOUT_NONE,\n                  MACH_PORT_NULL);\n    ASSERT_EQ(kr, MACH_MSG_SUCCESS) << MachErrorMessage(kr, \"mach_msg\");\n  }\n\n  // In the child process, waits for a reply message from the server.\n  void ChildWaitForReply() {\n    if (!options_.client_expect_reply) {\n      // The client shouldn’t expect a reply when it didn’t send a good reply\n      // port with its request, or when testing the server behaving in a way\n      // that doesn’t send replies.\n      return;\n    }\n\n    struct ReceiveReplyMessage : public ReplyMessage {\n      mach_msg_trailer_t trailer;\n    };\n\n    ReceiveReplyMessage reply = {};\n    kern_return_t kr = mach_msg(&reply.Head,\n                                MACH_RCV_MSG,\n                                0,\n                                sizeof(reply),\n                                LocalPort(),\n                                MACH_MSG_TIMEOUT_NONE,\n                                MACH_PORT_NULL);\n    ASSERT_EQ(kr, MACH_MSG_SUCCESS) << MachErrorMessage(kr, \"mach_msg\");\n\n    ASSERT_EQ(reply.Head.msgh_bits,\n              implicit_cast<mach_msg_bits_t>(\n                  MACH_MSGH_BITS(0, MACH_MSG_TYPE_MOVE_SEND)));\n    ASSERT_EQ(reply.Head.msgh_size, sizeof(ReplyMessage));\n    ASSERT_EQ(reply.Head.msgh_remote_port, kMachPortNull);\n    ASSERT_EQ(reply.Head.msgh_local_port, LocalPort());\n    ASSERT_EQ(reply.Head.msgh_id, kReplyMessageID);\n    ASSERT_EQ(memcmp(&reply.NDR, &NDR_record, sizeof(NDR_record)), 0);\n    ASSERT_EQ(reply.RetCode, options_.server_mig_retcode);\n    ASSERT_EQ(reply.number, replies_);\n    ASSERT_EQ(\n        reply.trailer.msgh_trailer_type,\n        implicit_cast<mach_msg_trailer_type_t>(MACH_MSG_TRAILER_FORMAT_0));\n    ASSERT_EQ(reply.trailer.msgh_trailer_size, MACH_MSG_TRAILER_MINIMUM_SIZE);\n\n    ++replies_;\n  }\n\n  // For test types where the child needs to notify the server in the parent\n  // that the child is ready, this method will send a byte via the POSIX pipe.\n  // The parent will be waiting in a read() on this pipe, and will proceed to\n  // running MachMessageServer() once it’s received.\n  void ChildNotifyParentViaPipe() {\n    char c = '\\0';\n    CheckedWriteFile(WritePipeHandle(), &c, 1);\n  }\n\n  // In the child process, sends a request message to the server and then\n  // receives a reply message.\n  void ChildSendRequestAndWaitForReply() {\n    ASSERT_NO_FATAL_FAILURE(ChildSendRequest());\n\n    if (options_.parent_wait_for_child_pipe &&\n        !options_.child_send_all_requests_before_receiving_any_replies) {\n      // The parent is waiting to read a byte to indicate that the message has\n      // been placed in the queue.\n      ASSERT_NO_FATAL_FAILURE(ChildNotifyParentViaPipe());\n    }\n\n    ASSERT_NO_FATAL_FAILURE(ChildWaitForReply());\n  }\n\n  const Options& options_;\n\n  // A receive right allocated in the child process. A send right will be\n  // created from this right and sent to the parent parent process in the\n  // request message.\n  base::apple::ScopedMachReceiveRight child_complex_message_port_;\n\n  // The send right received in the parent process. This right is stored in a\n  // member variable to test that resources carried in complex messages are\n  // properly destroyed in the server when expected.\n  mach_port_t parent_complex_message_port_;\n\n  static uint32_t requests_;\n  static uint32_t replies_;\n\n  static constexpr mach_msg_id_t kRequestMessageID = 16237;\n  static constexpr mach_msg_id_t kReplyMessageID = kRequestMessageID + 100;\n};\n\nuint32_t TestMachMessageServer::requests_;\nuint32_t TestMachMessageServer::replies_;\nconstexpr mach_msg_id_t TestMachMessageServer::kRequestMessageID;\nconstexpr mach_msg_id_t TestMachMessageServer::kReplyMessageID;\n\nTEST(MachMessageServer, Basic) {\n  // The client sends one message to the server, which will wait indefinitely in\n  // blocking mode for it.\n  TestMachMessageServer::Options options;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, NonblockingNoMessage) {\n  // The server waits in nonblocking mode and the client sends nothing, so the\n  // server should return immediately without processing any message.\n  TestMachMessageServer::Options options;\n  options.expect_server_interface_method_called = false;\n  options.server_timeout_ms = kMachMessageTimeoutNonblocking;\n  options.expect_server_result = MACH_RCV_TIMED_OUT;\n  options.expect_server_transaction_count = 0;\n  options.client_send_request_count = 0;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, TimeoutNoMessage) {\n  // The server waits in blocking mode for one message, but with a timeout. The\n  // client sends no message, so the server returns after the timeout.\n  TestMachMessageServer::Options options;\n  options.expect_server_interface_method_called = false;\n  options.server_timeout_ms = 10;\n  options.expect_server_result = MACH_RCV_TIMED_OUT;\n  options.expect_server_transaction_count = 0;\n  options.client_send_request_count = 0;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, Nonblocking) {\n  // The client sends one message to the server and then signals the server that\n  // it’s safe to start waiting for it in nonblocking mode. The message is in\n  // the server’s queue, so it’s able to receive it when it begins listening in\n  // nonblocking mode.\n  TestMachMessageServer::Options options;\n  options.parent_wait_for_child_pipe = true;\n  options.server_timeout_ms = kMachMessageTimeoutNonblocking;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, Timeout) {\n  // The client sends one message to the server, which will wait in blocking\n  // mode for it up to a specific timeout.\n  TestMachMessageServer::Options options;\n  options.server_timeout_ms = 10;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, PersistentTenMessages) {\n  // The server waits for as many messages as it can receive in blocking mode\n  // with a timeout. The client sends several messages, and the server processes\n  // them all.\n  TestMachMessageServer::Options options;\n  options.server_persistent = MachMessageServer::kPersistent;\n  options.server_timeout_ms = 10;\n  options.expect_server_result = MACH_RCV_TIMED_OUT;\n  options.expect_server_transaction_count = 10;\n  options.client_send_request_count = 10;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, PersistentNonblockingFourMessages) {\n  // The client sends several messages to the server and then signals the server\n  // that it’s safe to start waiting for them in nonblocking mode. The server\n  // then listens for them in nonblocking persistent mode, and receives all of\n  // them because they’ve been queued up. The client doesn’t wait for the\n  // replies until after it’s put all of its requests into the server’s queue.\n  //\n  // This test is sensitive to the length of the IPC queue limit. Mach ports\n  // normally have a queue length limit of MACH_PORT_QLIMIT_DEFAULT (which is\n  // MACH_PORT_QLIMIT_BASIC, or 5). The number of messages sent for this test\n  // must be below this, because the server does not begin dequeueing request\n  // messages until the client has finished sending them.\n  //\n  // The queue limit on new ports has been seen to be below\n  // MACH_PORT_QLIMIT_DEFAULT, so it will explicitly be set by\n  // mach_port_set_attributes() for this test. This needs to happen before the\n  // child is allowed to begin sending messages, so\n  // child_wait_for_parent_pipe_early is used to make the child wait until the\n  // parent is ready.\n  constexpr size_t kTransactionCount = 4;\n  static_assert(kTransactionCount <= MACH_PORT_QLIMIT_DEFAULT,\n                \"must not exceed queue limit\");\n\n  TestMachMessageServer::Options options;\n  options.parent_wait_for_child_pipe = true;\n  options.server_persistent = MachMessageServer::kPersistent;\n  options.server_timeout_ms = kMachMessageTimeoutNonblocking;\n  options.expect_server_result = MACH_RCV_TIMED_OUT;\n  options.expect_server_transaction_count = kTransactionCount;\n  options.child_wait_for_parent_pipe_early = true;\n  options.client_send_request_count = kTransactionCount;\n  options.child_send_all_requests_before_receiving_any_replies = true;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ReturnCodeInvalidArgument) {\n  // This tests that the mig_reply_error_t::RetCode field is properly returned\n  // to the client.\n  TestMachMessageServer::Options options;\n  options.server_mig_retcode = KERN_INVALID_ARGUMENT;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ReturnCodeNoReply) {\n  // This tests that when mig_reply_error_t::RetCode is set to MIG_NO_REPLY, no\n  // response is sent to the client.\n  TestMachMessageServer::Options options;\n  options.server_mig_retcode = MIG_NO_REPLY;\n  options.client_expect_reply = false;\n  options.child_wait_for_parent_pipe_late = true;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ReplyPortNull) {\n  // The client sets its reply port to MACH_PORT_NULL. The server should see\n  // this and avoid sending a message to the null port. No reply message is\n  // sent and the server returns success.\n  TestMachMessageServer::Options options;\n  options.client_reply_port_type =\n      TestMachMessageServer::Options::kReplyPortNull;\n  options.client_expect_reply = false;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ReplyPortDead) {\n  // The client allocates a new port and uses it as the reply port in its\n  // request message, and then deallocates its receive right to that port. It\n  // then signals the server to process the request message. The server’s view\n  // of the port is that it is a dead name. The server function will return\n  // MACH_SEND_INVALID_DEST because it’s not possible to send a message to a\n  // dead name.\n  TestMachMessageServer::Options options;\n  options.parent_wait_for_child_pipe = true;\n  options.expect_server_result = MACH_SEND_INVALID_DEST;\n  options.client_reply_port_type =\n      TestMachMessageServer::Options::kReplyPortDead;\n  options.client_expect_reply = false;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, Complex) {\n  // The client allocates a new receive right and sends a complex request\n  // message to the server with a send right made out of this receive right. The\n  // server receives this message and is instructed to destroy the send right\n  // when it is done handling the request-reply transaction. The former send\n  // right is verified to be invalid after the server runs. This test ensures\n  // that resources transferred to a server process temporarily aren’t leaked.\n  TestMachMessageServer::Options options;\n  options.client_send_complex = true;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ComplexNotDestroyed) {\n  // As in MachMessageServer.Complex, but the server is instructed not to\n  // destroy the send right. After the server runs, the send right is verified\n  // to continue to exist in the server task. The client process is then\n  // signalled by pipe that it’s safe to exit so that the send right in the\n  // server task doesn’t prematurely become a dead name. This test ensures that\n  // rights that are expected to be retained in the server task are properly\n  // retained.\n  TestMachMessageServer::Options options;\n  options.server_destroy_complex = false;\n  options.expect_server_destroyed_complex = false;\n  options.client_send_complex = true;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ComplexDestroyedInvalidArgument) {\n  // As in MachMessageServer.ComplexNotDestroyed, but the server does not return\n  // a successful code via MIG. The server is expected to destroy resources in\n  // this case, because server_destroy_complex = false is only honored when a\n  // MIG request is handled successfully or with no reply.\n  TestMachMessageServer::Options options;\n  options.server_mig_retcode = KERN_INVALID_TASK;\n  options.server_destroy_complex = false;\n  options.client_send_complex = true;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ComplexNotDestroyedNoReply) {\n  // As in MachMessageServer.ComplexNotDestroyed, but the server does not send\n  // a reply message and is expected to retain the send right in the server\n  // task.\n  TestMachMessageServer::Options options;\n  options.server_mig_retcode = MIG_NO_REPLY;\n  options.server_destroy_complex = false;\n  options.expect_server_destroyed_complex = false;\n  options.client_send_complex = true;\n  options.client_expect_reply = false;\n  options.child_wait_for_parent_pipe_late = true;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ReceiveLargeError) {\n  // The client sends a request to the server that is larger than the server is\n  // expecting. server_receive_large is kReceiveLargeError, so the request is\n  // destroyed and the server returns a MACH_RCV_TOO_LARGE error. The client\n  // does not receive a reply.\n  TestMachMessageServer::Options options;\n  options.expect_server_result = MACH_RCV_TOO_LARGE;\n  options.expect_server_transaction_count = 0;\n  options.client_send_large = true;\n  options.client_expect_reply = false;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ReceiveLargeRetry) {\n  // The client sends a request to the server that is larger than the server is\n  // initially expecting. server_receive_large is kReceiveLargeResize, so a new\n  // buffer is allocated to receive the message. The server receives the large\n  // request message, processes it, and returns a reply to the client.\n  TestMachMessageServer::Options options;\n  options.server_receive_large = MachMessageServer::kReceiveLargeResize;\n  options.client_send_large = true;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\nTEST(MachMessageServer, ReceiveLargeIgnore) {\n  // The client sends a request to the server that is larger than the server is\n  // expecting. server_receive_large is kReceiveLargeIgnore, so the request is\n  // destroyed but the server does not consider this an error. The server is\n  // running in blocking mode with a timeout, and continues to wait for a\n  // message until it times out. The client does not receive a reply.\n  TestMachMessageServer::Options options;\n  options.server_receive_large = MachMessageServer::kReceiveLargeIgnore;\n  options.server_timeout_ms = 10;\n  options.expect_server_result = MACH_RCV_TIMED_OUT;\n  options.expect_server_transaction_count = 0;\n  options.client_send_large = true;\n  options.client_expect_reply = false;\n  TestMachMessageServer test_mach_message_server(options);\n  test_mach_message_server.Test();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/mach_message_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/mach_message.h\"\n\n#include <unistd.h>\n\n#include <tuple>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(MachMessage, MachMessageDeadlineFromTimeout) {\n  MachMessageDeadline deadline_0 =\n      MachMessageDeadlineFromTimeout(kMachMessageTimeoutNonblocking);\n  EXPECT_EQ(deadline_0, kMachMessageDeadlineNonblocking);\n\n  deadline_0 =\n      MachMessageDeadlineFromTimeout(kMachMessageTimeoutWaitIndefinitely);\n  EXPECT_EQ(deadline_0, kMachMessageDeadlineWaitIndefinitely);\n\n  deadline_0 = MachMessageDeadlineFromTimeout(1);\n  MachMessageDeadline deadline_1 = MachMessageDeadlineFromTimeout(100);\n\n  EXPECT_NE(deadline_0, kMachMessageDeadlineNonblocking);\n  EXPECT_NE(deadline_0, kMachMessageDeadlineWaitIndefinitely);\n  EXPECT_NE(deadline_1, kMachMessageDeadlineNonblocking);\n  EXPECT_NE(deadline_1, kMachMessageDeadlineWaitIndefinitely);\n  EXPECT_GE(deadline_1, deadline_0);\n}\n\nTEST(MachMessage, PrepareMIGReplyFromRequest_SetMIGReplyError) {\n  mach_msg_header_t request;\n  request.msgh_bits =\n      MACH_MSGH_BITS_COMPLEX |\n      MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, MACH_MSG_TYPE_PORT_SEND);\n  request.msgh_size = 64;\n  request.msgh_remote_port = 0x01234567;\n  request.msgh_local_port = 0x89abcdef;\n  request.msgh_reserved = 0xa5a5a5a5;\n  request.msgh_id = 1011;\n\n  mig_reply_error_t reply;\n\n  // PrepareMIGReplyFromRequest() doesn’t touch this field.\n  reply.RetCode = MIG_TYPE_ERROR;\n\n  PrepareMIGReplyFromRequest(&request, &reply.Head);\n\n  EXPECT_EQ(reply.Head.msgh_bits,\n            implicit_cast<mach_msg_bits_t>(\n                MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0)));\n  EXPECT_EQ(reply.Head.msgh_size, sizeof(reply));\n  EXPECT_EQ(reply.Head.msgh_remote_port, request.msgh_remote_port);\n  EXPECT_EQ(reply.Head.msgh_local_port, kMachPortNull);\n  EXPECT_EQ(reply.Head.msgh_reserved, 0u);\n  EXPECT_EQ(reply.Head.msgh_id, 1111);\n  EXPECT_EQ(reply.NDR.mig_vers, NDR_record.mig_vers);\n  EXPECT_EQ(reply.NDR.if_vers, NDR_record.if_vers);\n  EXPECT_EQ(reply.NDR.reserved1, NDR_record.reserved1);\n  EXPECT_EQ(reply.NDR.mig_encoding, NDR_record.mig_encoding);\n  EXPECT_EQ(reply.NDR.int_rep, NDR_record.int_rep);\n  EXPECT_EQ(reply.NDR.char_rep, NDR_record.char_rep);\n  EXPECT_EQ(reply.NDR.float_rep, NDR_record.float_rep);\n  EXPECT_EQ(reply.NDR.reserved2, NDR_record.reserved2);\n  EXPECT_EQ(reply.RetCode, MIG_TYPE_ERROR);\n\n  SetMIGReplyError(&reply.Head, MIG_BAD_ID);\n\n  EXPECT_EQ(reply.RetCode, MIG_BAD_ID);\n}\n\nTEST(MachMessage, MachMessageTrailerFromHeader) {\n  mach_msg_empty_t empty;\n  empty.send.header.msgh_size = sizeof(mach_msg_empty_send_t);\n  EXPECT_EQ(MachMessageTrailerFromHeader(&empty.rcv.header),\n            &empty.rcv.trailer);\n\n  struct TestSendMessage : public mach_msg_header_t {\n    uint8_t data[126];\n  };\n  struct TestReceiveMessage : public TestSendMessage {\n    mach_msg_trailer_t trailer;\n  };\n  union TestMessage {\n    TestSendMessage send;\n    TestReceiveMessage receive;\n  };\n\n  TestMessage test;\n  test.send.msgh_size = sizeof(TestSendMessage);\n  EXPECT_EQ(MachMessageTrailerFromHeader(&test.receive), &test.receive.trailer);\n}\n\nTEST(MachMessage, MachMessageDestroyReceivedPort) {\n  mach_port_t port = NewMachPort(MACH_PORT_RIGHT_RECEIVE);\n  ASSERT_NE(port, kMachPortNull);\n  EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_RECEIVE));\n\n  base::apple::ScopedMachReceiveRight receive(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  mach_msg_type_name_t right_type;\n  kern_return_t kr = mach_port_extract_right(mach_task_self(),\n                                             receive.get(),\n                                             MACH_MSG_TYPE_MAKE_SEND,\n                                             &port,\n                                             &right_type);\n  ASSERT_EQ(kr, KERN_SUCCESS)\n      << MachErrorMessage(kr, \"mach_port_extract_right\");\n  ASSERT_EQ(port, receive);\n  ASSERT_EQ(right_type,\n            implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND));\n  EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND));\n\n  kr = mach_port_extract_right(mach_task_self(),\n                               receive.get(),\n                               MACH_MSG_TYPE_MAKE_SEND_ONCE,\n                               &port,\n                               &right_type);\n  ASSERT_EQ(kr, KERN_SUCCESS)\n      << MachErrorMessage(kr, \"mach_port_extract_right\");\n  ASSERT_NE(port, kMachPortNull);\n  EXPECT_NE(port, receive);\n  ASSERT_EQ(right_type,\n            implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE));\n  EXPECT_TRUE(\n      MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND_ONCE));\n\n  kr = mach_port_extract_right(mach_task_self(),\n                               receive.get(),\n                               MACH_MSG_TYPE_MAKE_SEND,\n                               &port,\n                               &right_type);\n  ASSERT_EQ(kr, KERN_SUCCESS)\n      << MachErrorMessage(kr, \"mach_port_extract_right\");\n  ASSERT_EQ(port, receive);\n  ASSERT_EQ(right_type,\n            implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND));\n  EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_RECEIVE));\n  std::ignore = receive.release();\n  EXPECT_TRUE(MachMessageDestroyReceivedPort(port, MACH_MSG_TYPE_PORT_SEND));\n}\n\n#if BUILDFLAG(IS_MAC)\n\nTEST(MachMessage, AuditPIDFromMachMessageTrailer) {\n  base::apple::ScopedMachReceiveRight port(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_NE(port, kMachPortNull);\n\n  mach_msg_empty_send_t send = {};\n  send.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0);\n  send.header.msgh_size = sizeof(send);\n  send.header.msgh_remote_port = port.get();\n  mach_msg_return_t mr =\n      MachMessageWithDeadline(&send.header,\n                              MACH_SEND_MSG,\n                              0,\n                              MACH_PORT_NULL,\n                              kMachMessageDeadlineNonblocking,\n                              MACH_PORT_NULL,\n                              false);\n  ASSERT_EQ(mr, MACH_MSG_SUCCESS)\n      << MachErrorMessage(mr, \"MachMessageWithDeadline send\");\n\n  struct EmptyReceiveMessageWithAuditTrailer : public mach_msg_empty_send_t {\n    union {\n      mach_msg_trailer_t trailer;\n      mach_msg_audit_trailer_t audit_trailer;\n    };\n  };\n\n  EmptyReceiveMessageWithAuditTrailer receive;\n  mr = MachMessageWithDeadline(&receive.header,\n                               MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,\n                               sizeof(receive),\n                               port.get(),\n                               kMachMessageDeadlineNonblocking,\n                               MACH_PORT_NULL,\n                               false);\n  ASSERT_EQ(mr, MACH_MSG_SUCCESS)\n      << MachErrorMessage(mr, \"MachMessageWithDeadline receive\");\n\n  EXPECT_EQ(AuditPIDFromMachMessageTrailer(&receive.trailer), getpid());\n}\n\n#endif  // BUILDFLAG(IS_MAC)\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/mig.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2019 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport sys\nimport tempfile\n\nimport mig_fix\nimport mig_gen\n\n\ndef _try_remove(*paths):\n    for path in paths:\n        try:\n            os.remove(path)\n        except OSError:\n            pass\n\n\ndef _generate_and_fix(user_c,\n                      server_c,\n                      user_h,\n                      server_h,\n                      defs,\n                      sdk=None,\n                      clang_path=None,\n                      mig_path=None,\n                      migcom_path=None,\n                      arch=None,\n                      mig_args=None):\n    interface = mig_gen.MigInterface(user_c, server_c, user_h, server_h)\n    mig_gen.generate_interface(defs,\n                               interface,\n                               sdk=sdk,\n                               clang_path=clang_path,\n                               mig_path=mig_path,\n                               migcom_path=migcom_path,\n                               arch=arch,\n                               mig_args=mig_args)\n    mig_fix.fix_interface(interface)\n\n\ndef _wrap_arch_guards(file, arch):\n    contents = '#if defined(__%s__)\\n' % arch\n    with open(file, 'r') as f:\n        contents += f.read()\n    contents += '\\n#endif  /* __%s__ */\\n' % arch\n    return contents\n\n\ndef _write_file(path, data):\n    with open(path, 'w') as file:\n        file.write(data)\n\n\ndef main(args):\n    parsed = mig_gen.parse_args(args, multiple_arch=True)\n\n    _try_remove(parsed.user_c, parsed.server_c, parsed.user_h, parsed.server_h)\n\n    if len(parsed.arch) <= 1:\n        _generate_and_fix(\n            user_c=parsed.user_c,\n            server_c=parsed.server_c,\n            user_h=parsed.user_h,\n            server_h=parsed.server_h,\n            defs=parsed.defs,\n            sdk=parsed.sdk,\n            clang_path=parsed.clang_path,\n            mig_path=parsed.mig_path,\n            migcom_path=parsed.migcom_path,\n            arch=parsed.arch[0] if len(parsed.arch) >= 1 else None,\n            mig_args=parsed.mig_args)\n        return 0\n\n    # Run mig once per architecture, and smush everything together, wrapped in\n    # in architecture-specific #if guards.\n\n    user_c_data = ''\n    server_c_data = ''\n    user_h_data = ''\n    server_h_data = ''\n\n    for arch in parsed.arch:\n        with tempfile.TemporaryDirectory(prefix=os.path.basename(sys.argv[0]) +\n                                         '_') as temp_dir:\n            user_c = os.path.join(temp_dir, os.path.basename(parsed.user_c))\n            server_c = os.path.join(temp_dir, os.path.basename(parsed.server_c))\n            user_h = os.path.join(temp_dir, os.path.basename(parsed.user_h))\n            server_h = os.path.join(temp_dir, os.path.basename(parsed.server_h))\n            _generate_and_fix(user_c=user_c,\n                              server_c=server_c,\n                              user_h=user_h,\n                              server_h=server_h,\n                              defs=parsed.defs,\n                              sdk=parsed.sdk,\n                              clang_path=parsed.clang_path,\n                              mig_path=parsed.mig_path,\n                              migcom_path=parsed.migcom_path,\n                              arch=arch,\n                              mig_args=parsed.mig_args)\n\n            user_c_data += _wrap_arch_guards(user_c, arch)\n            server_c_data += _wrap_arch_guards(server_c, arch)\n            user_h_data += _wrap_arch_guards(user_h, arch)\n            server_h_data += _wrap_arch_guards(server_h, arch)\n\n    _write_file(parsed.user_c, user_c_data)\n    _write_file(parsed.server_c, server_c_data)\n    _write_file(parsed.user_h, user_h_data)\n    _write_file(parsed.server_h, server_h_data)\n\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv[1:]))\n"
  },
  {
    "path": "util/mach/mig_fix.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2019 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport os\nimport re\nimport sys\n\nfrom mig_gen import MigInterface\n\n\ndef _make_generated_comments_deterministic(contents):\n    \"\"\"Replaces generated code comments with determenistic ones.\n\n    This is what is generated by mig (only in .c files):\n    /*\n     * IDENTIFICATION:\n     * stub generated Mon Jan 17 15:28:03 2022\n     * with a MiG generated by bootstrap_cmds-122\n     * OPTIONS:\n     */\n\n    We look for two specific lines and replace them like so:\n    /*\n     * IDENTIFICATION:\n     * stub generated <suppressed by mig_fix.py>\n     * with a MiG generated by <suppressed by mig_fix.py>\n     * OPTIONS:\n     */\n    \"\"\"\n\n    return re.sub(r'^( \\* (?:stub generated|with a MiG generated by) ).+$',\n                  r'\\1<suppressed by mig_fix.py>',\n                  contents,\n                  count=2,\n                  flags=re.MULTILINE)\n\n\ndef _fix_user_implementation(implementation, fixed_implementation, header,\n                             fixed_header):\n    \"\"\"Rewrites a MIG-generated user implementation (.c) file.\n\n    Rewrites the file at |implementation| by adding “__attribute__((unused))” to\n    the definition of any structure typedefed as “__Reply” by searching for the\n    pattern unique to those structure definitions. These structures are in fact\n    unused in the user implementation file, and this will trigger a\n    -Wunused-local-typedefs warning in gcc unless removed or marked with the\n    “unused” attribute. Also changes header references to point to the new\n    header filename, if changed. Replaces generated code comments with\n    deterministic ones.\n\n    If |fixed_implementation| is None, overwrites the original; otherwise, puts\n    the result in the file at |fixed_implementation|.\n    \"\"\"\n\n    file = open(implementation, 'r+' if fixed_implementation is None else 'r')\n    contents = file.read()\n\n    pattern = re.compile('^(\\t} __Reply);$', flags=re.MULTILINE)\n    contents = pattern.sub(r'\\1 __attribute__((unused));', contents)\n\n    if fixed_header is not None:\n        contents = contents.replace(\n            '#include \"%s\"' % os.path.basename(header),\n            '#include \"%s\"' % os.path.basename(fixed_header))\n\n    # Replace generated code comments with determenistic ones.\n    contents = _make_generated_comments_deterministic(contents)\n\n    if fixed_implementation is None:\n        file.seek(0)\n        file.truncate()\n    else:\n        file.close()\n        file = open(fixed_implementation, 'w')\n    file.write(contents)\n    file.close()\n\n\ndef _fix_server_implementation(implementation, fixed_implementation, header,\n                               fixed_header):\n    \"\"\"Rewrites a MIG-generated server implementation (.c) file.\n\n    Rewrites the file at |implementation| by replacing “mig_internal” with\n    “mig_external” on functions that begin with “__MIG_check__”. This makes\n    these functions available to other callers outside this file from a linkage\n    perspective. It then returns, as a list of lines, declarations that can be\n    added to a header file, so that other files that include that header file\n    will have access to these declarations from a compilation perspective. Also\n    changes header references to point to the new header filename, if changed.\n    Replaces generated code comments with deterministic ones.\n\n    If |fixed_implementation| is None or not provided, overwrites the original;\n    otherwise, puts the result in the file at |fixed_implementation|.\n    \"\"\"\n\n    file = open(implementation, 'r+' if fixed_implementation is None else 'r')\n    contents = file.read()\n\n    # Find interesting declarations.\n    declaration_pattern = re.compile(\n        '^mig_internal (kern_return_t __MIG_check__.*)$', flags=re.MULTILINE)\n    declarations = declaration_pattern.findall(contents)\n\n    # Remove “__attribute__((__unused__))” from the declarations, and call them\n    # “mig_external” or “extern” depending on whether “mig_external” is defined.\n    attribute_pattern = re.compile(r'__attribute__\\(\\(__unused__\\)\\) ')\n    declarations = [\n        '''\\\n#ifdef mig_external\nmig_external\n#else\nextern\n#endif\n''' + attribute_pattern.sub('', x) + ';\\n' for x in declarations\n    ]\n\n    # Rewrite the declarations in this file as “mig_external”.\n    contents = declaration_pattern.sub(r'mig_external \\1', contents)\n\n    # Crashpad never implements the mach_msg_server() MIG callouts. To avoid\n    # needing to provide stub implementations, set KERN_FAILURE as the RetCode\n    # and abort().\n    routine_callout_pattern = re.compile(\n        r'OutP->RetCode = (([a-zA-Z0-9_]+)\\(.+\\));')\n    routine_callouts = routine_callout_pattern.findall(contents)\n    for routine in routine_callouts:\n        contents = contents.replace(routine[0], 'KERN_FAILURE; abort()')\n\n    # Include the header for abort().\n    contents = '#include <stdlib.h>\\n' + contents\n\n    if fixed_header is not None:\n        contents = contents.replace(\n            '#include \"%s\"' % os.path.basename(header),\n            '#include \"%s\"' % os.path.basename(fixed_header))\n\n    # Replace generated code comments with determenistic ones.\n    contents = _make_generated_comments_deterministic(contents)\n\n    if fixed_implementation is None:\n        file.seek(0)\n        file.truncate()\n    else:\n        file.close()\n        file = open(fixed_implementation, 'w')\n    file.write(contents)\n    file.close()\n    return declarations\n\n\ndef _fix_header(header, fixed_header, declarations=[]):\n    \"\"\"Rewrites a MIG-generated header (.h) file.\n\n    Rewrites the file at |header| by placing it inside an “extern \"C\"” block, so\n    that it declares things properly when included by a C++ compilation unit.\n    |declarations| can be a list of additional declarations to place inside the\n    “extern \"C\"” block after the original contents of |header|.\n\n    If |fixed_header| is None or not provided, overwrites the original;\n    otherwise, puts the result in the file at |fixed_header|.\n    \"\"\"\n\n    file = open(header, 'r+' if fixed_header is None else 'r')\n    contents = file.read()\n    declarations_text = ''.join(declarations)\n    contents = '''\\\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n%s\n%s\n#ifdef __cplusplus\n}\n#endif\n''' % (contents, declarations_text)\n\n    if fixed_header is None:\n        file.seek(0)\n        file.truncate()\n    else:\n        file.close()\n        file = open(fixed_header, 'w')\n    file.write(contents)\n    file.close()\n\n\ndef fix_interface(interface, fixed_interface=None):\n    if fixed_interface is None:\n        fixed_interface = MigInterface(None, None, None, None)\n\n    _fix_user_implementation(interface.user_c, fixed_interface.user_c,\n                             interface.user_h, fixed_interface.user_h)\n    server_declarations = _fix_server_implementation(interface.server_c,\n                                                     fixed_interface.server_c,\n                                                     interface.server_h,\n                                                     fixed_interface.server_h)\n    _fix_header(interface.user_h, fixed_interface.user_h)\n    _fix_header(interface.server_h, fixed_interface.server_h,\n                server_declarations)\n\n\ndef main(args):\n    parser = argparse.ArgumentParser()\n    parser.add_argument('user_c')\n    parser.add_argument('--fixed_user_c', default=None)\n    parser.add_argument('server_c')\n    parser.add_argument('--fixed_server_c', default=None)\n    parser.add_argument('user_h')\n    parser.add_argument('--fixed_user_h', default=None)\n    parser.add_argument('server_h')\n    parser.add_argument('--fixed_server_h', default=None)\n    parsed = parser.parse_args(args)\n\n    interface = MigInterface(parsed.user_c, parsed.server_c, parsed.user_h,\n                             parsed.server_h)\n    fixed_interface = MigInterface(parsed.fixed_user_c, parsed.fixed_server_c,\n                                   parsed.fixed_user_h, parsed.fixed_server_h)\n    fix_interface(interface, fixed_interface)\n\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv[1:]))\n"
  },
  {
    "path": "util/mach/mig_gen.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright 2019 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport collections\nimport os\nimport subprocess\nimport sys\n\nMigInterface = collections.namedtuple(\n    'MigInterface', ['user_c', 'server_c', 'user_h', 'server_h'])\n\n\ndef generate_interface(defs,\n                       interface,\n                       sdk=None,\n                       clang_path=None,\n                       mig_path=None,\n                       migcom_path=None,\n                       arch=None,\n                       mig_args=None):\n    if mig_path is None:\n        mig_path = 'mig'\n\n    # yapf: disable\n    command = [\n        mig_path,\n        '-user', interface.user_c,\n        '-server', interface.server_c,\n        '-header', interface.user_h,\n        '-sheader', interface.server_h,\n    ]\n    # yapf: enable\n\n    if clang_path is not None:\n        os.environ['MIGCC'] = clang_path\n    if migcom_path is not None:\n        os.environ['MIGCOM'] = migcom_path\n    if arch is not None:\n        command.extend(['-arch', arch])\n    if sdk is not None:\n        command.extend(['-isysroot', sdk])\n    if mig_args:\n        command.extend(mig_args)\n    command.append(defs)\n    subprocess.check_call(command)\n\n\ndef parse_args(args, multiple_arch=False):\n    parser = argparse.ArgumentParser(\n        description=\"A utility for Mach Interface Generator (MIG) compilation.\",\n        usage=\n        \"%(prog)s [OPTIONS] <defs> <user_c> <server_c> <user_h> <server_h> -- [mig arguments]\"\n    )\n    parser.add_argument('--clang-path', help='Path to clang')\n    parser.add_argument('--mig-path', help='Path to mig')\n    parser.add_argument('--migcom-path', help='Path to migcom')\n    if not multiple_arch:\n        parser.add_argument('--arch', help='Target architecture')\n    else:\n        parser.add_argument(\n            '--arch',\n            default=[],\n            action='append',\n            help='Target architecture (may appear multiple times)')\n    parser.add_argument('--sdk', help='Path to SDK')\n    parser.add_argument('defs')\n    parser.add_argument('user_c')\n    parser.add_argument('server_c')\n    parser.add_argument('user_h')\n    parser.add_argument('server_h')\n    parser.add_argument('mig_args', nargs=\"*\")\n    return parser.parse_args(args)\n\n\ndef main(args):\n    parsed = parse_args(args)\n    interface = MigInterface(parsed.user_c, parsed.server_c, parsed.user_h,\n                             parsed.server_h)\n    generate_interface(parsed.defs,\n                       interface,\n                       sdk=parsed.sdk,\n                       clang_path=parsed.clang_path,\n                       mig_path=parsed.mig_path,\n                       migcom_path=parsed.migcom_path,\n                       arch=parsed.arch,\n                       mig_args=parsed.mig_args)\n\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv[1:]))\n"
  },
  {
    "path": "util/mach/notify_server.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/notify_server.h\"\n\n#include <iterator>\n\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/notifyServer.h\"\n\nnamespace {\n\n// The MIG-generated __MIG_check__Request__*() functions are not declared as\n// accepting const data, but they could have been because they in fact do not\n// modify the data. These wrapper functions are provided to bridge the const gap\n// between the code in this file, which is const-correct and treats request\n// message data as const, and the generated functions.\n\nkern_return_t MIGCheckRequestMachNotifyPortDeleted(\n    const __Request__mach_notify_port_deleted_t* in_request) {\n  using Request = __Request__mach_notify_port_deleted_t;\n  return __MIG_check__Request__mach_notify_port_deleted_t(\n      const_cast<Request*>(in_request));\n}\n\nkern_return_t MIGCheckRequestMachNotifyPortDestroyed(\n    const __Request__mach_notify_port_destroyed_t* in_request) {\n  using Request = __Request__mach_notify_port_destroyed_t;\n  return __MIG_check__Request__mach_notify_port_destroyed_t(\n      const_cast<Request*>(in_request));\n}\n\nkern_return_t MIGCheckRequestMachNotifyNoSenders(\n    const __Request__mach_notify_no_senders_t* in_request) {\n  using Request = __Request__mach_notify_no_senders_t;\n  return __MIG_check__Request__mach_notify_no_senders_t(\n      const_cast<Request*>(in_request));\n}\n\nkern_return_t MIGCheckRequestMachNotifySendOnce(\n    const __Request__mach_notify_send_once_t* in_request) {\n  using Request = __Request__mach_notify_send_once_t;\n  return __MIG_check__Request__mach_notify_send_once_t(\n      const_cast<Request*>(in_request));\n}\n\nkern_return_t MIGCheckRequestMachNotifyDeadName(\n    const __Request__mach_notify_dead_name_t* in_request) {\n  using Request = __Request__mach_notify_dead_name_t;\n  return __MIG_check__Request__mach_notify_dead_name_t(\n      const_cast<Request*>(in_request));\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nkern_return_t NotifyServer::DefaultInterface::DoMachNotifyPortDeleted(\n    notify_port_t notify,\n    mach_port_name_t name,\n    const mach_msg_trailer_t* trailer) {\n  return MIG_BAD_ID;\n}\n\nkern_return_t NotifyServer::DefaultInterface::DoMachNotifyPortDestroyed(\n    notify_port_t notify,\n    mach_port_t rights,\n    const mach_msg_trailer_t* trailer,\n    bool* destroy_request) {\n  *destroy_request = true;\n  return MIG_BAD_ID;\n}\n\nkern_return_t NotifyServer::DefaultInterface::DoMachNotifyNoSenders(\n    notify_port_t notify,\n    mach_port_mscount_t mscount,\n    const mach_msg_trailer_t* trailer) {\n  return MIG_BAD_ID;\n}\n\nkern_return_t NotifyServer::DefaultInterface::DoMachNotifySendOnce(\n    notify_port_t notify,\n    const mach_msg_trailer_t* trailer) {\n  return MIG_BAD_ID;\n}\n\nkern_return_t NotifyServer::DefaultInterface::DoMachNotifyDeadName(\n    notify_port_t notify,\n    mach_port_name_t name,\n    const mach_msg_trailer_t* trailer) {\n  return MIG_BAD_ID;\n}\n\nNotifyServer::NotifyServer(NotifyServer::Interface* interface)\n    : MachMessageServer::Interface(),\n      interface_(interface) {\n}\n\nbool NotifyServer::MachMessageServerFunction(\n    const mach_msg_header_t* in_header,\n    mach_msg_header_t* out_header,\n    bool* destroy_complex_request) {\n  PrepareMIGReplyFromRequest(in_header, out_header);\n\n  const mach_msg_trailer_t* in_trailer =\n      MachMessageTrailerFromHeader(in_header);\n\n  switch (in_header->msgh_id) {\n    case MACH_NOTIFY_PORT_DELETED: {\n      // mach_notify_port_deleted(), do_mach_notify_port_deleted().\n      using Request = __Request__mach_notify_port_deleted_t;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n      kern_return_t kr = MIGCheckRequestMachNotifyPortDeleted(in_request);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = __Reply__mach_notify_port_deleted_t;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->RetCode =\n          interface_->DoMachNotifyPortDeleted(in_header->msgh_local_port,\n                                              in_request->name,\n                                              in_trailer);\n      return true;\n    }\n\n    case MACH_NOTIFY_PORT_DESTROYED: {\n      // mach_notify_port_destroyed(), do_mach_notify_port_destroyed().\n      using Request = __Request__mach_notify_port_destroyed_t;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n      kern_return_t kr = MIGCheckRequestMachNotifyPortDestroyed(in_request);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = __Reply__mach_notify_port_destroyed_t;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->RetCode =\n          interface_->DoMachNotifyPortDestroyed(in_header->msgh_local_port,\n                                                in_request->rights.name,\n                                                in_trailer,\n                                                destroy_complex_request);\n      return true;\n    }\n\n    case MACH_NOTIFY_NO_SENDERS: {\n      // mach_notify_no_senders(), do_mach_notify_no_senders().\n      using Request = __Request__mach_notify_no_senders_t;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n      kern_return_t kr = MIGCheckRequestMachNotifyNoSenders(in_request);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = __Reply__mach_notify_no_senders_t;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->RetCode =\n          interface_->DoMachNotifyNoSenders(in_header->msgh_local_port,\n                                            in_request->mscount,\n                                            in_trailer);\n      return true;\n    }\n\n    case MACH_NOTIFY_SEND_ONCE: {\n      // mach_notify_send_once(), do_mach_notify_send_once().\n      using Request = __Request__mach_notify_send_once_t;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n      kern_return_t kr = MIGCheckRequestMachNotifySendOnce(in_request);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = __Reply__mach_notify_send_once_t;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->RetCode =\n          interface_->DoMachNotifySendOnce(in_header->msgh_local_port,\n                                           in_trailer);\n      return true;\n    }\n\n    case MACH_NOTIFY_DEAD_NAME: {\n      // mach_notify_dead_name(), do_mach_notify_dead_name().\n      using Request = __Request__mach_notify_dead_name_t;\n      const Request* in_request = reinterpret_cast<const Request*>(in_header);\n      kern_return_t kr = MIGCheckRequestMachNotifyDeadName(in_request);\n      if (kr != MACH_MSG_SUCCESS) {\n        SetMIGReplyError(out_header, kr);\n        return true;\n      }\n\n      using Reply = __Reply__mach_notify_dead_name_t;\n      Reply* out_reply = reinterpret_cast<Reply*>(out_header);\n      out_reply->RetCode =\n          interface_->DoMachNotifyDeadName(in_header->msgh_local_port,\n                                           in_request->name,\n                                           in_trailer);\n      return true;\n    }\n\n    default: {\n      SetMIGReplyError(out_header, MIG_BAD_ID);\n      return false;\n    }\n  }\n}\n\nstd::set<mach_msg_id_t> NotifyServer::MachMessageServerRequestIDs() {\n  static constexpr mach_msg_id_t request_ids[] = {\n      MACH_NOTIFY_PORT_DELETED,\n      MACH_NOTIFY_PORT_DESTROYED,\n      MACH_NOTIFY_NO_SENDERS,\n      MACH_NOTIFY_SEND_ONCE,\n      MACH_NOTIFY_DEAD_NAME,\n  };\n  return std::set<mach_msg_id_t>(&request_ids[0],\n                                 &request_ids[std::size(request_ids)]);\n}\n\nmach_msg_size_t NotifyServer::MachMessageServerRequestSize() {\n  return sizeof(__RequestUnion__do_notify_subsystem);\n}\n\nmach_msg_size_t NotifyServer::MachMessageServerReplySize() {\n  return sizeof(__ReplyUnion__do_notify_subsystem);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/notify_server.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_\n#define CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_\n\n#include <mach/mach.h>\n\n#include <set>\n\n#include \"util/mach/mach_message_server.h\"\n\nnamespace crashpad {\n\n//! \\brief A server interface for the `notify` Mach subsystem.\n//!\n//! The <a\n//! href=\"https://lists.apple.com/archives/darwin-development/2001/Sep/msg00451.html\">mach\n//! port notifications</a> thread on the <a\n//! href=\"https://lists.apple.com/archives/darwin-development/\">darwin-development</a>\n//! mailing list (now known as <a\n//! href=\"https://lists.apple.com/mailman/listinfo/darwin-dev\">darwin-dev</a>)\n//! is good background for the various notification types.\nclass NotifyServer : public MachMessageServer::Interface {\n public:\n  //! \\brief An interface that the different request messages that are a part of\n  //!     the `notify` Mach subsystem can be dispatched to.\n  //!\n  //! Default implementations of all methods are available in the\n  //! DefaultInterface class.\n  class Interface {\n   public:\n    //! \\brief Handles port-deleted notifications sent by\n    //!     `mach_notify_port_deleted()`.\n    //!\n    //! A port-deleted notification is generated when a port with a dead-name\n    //! notification request is destroyed and the port name becomes available\n    //! for reuse.\n    //!\n    //! This behaves equivalently to a `do_mach_notify_port_deleted()` function\n    //! used with `notify_server()`.\n    //!\n    //! \\param[in] notify The Mach port that the notification was sent to.\n    //! \\param[in] name The name that formerly referenced the deleted port. When\n    //!     this method is called, \\a name no longer corresponds to the port\n    //!     that has been deleted, and may be reused for another purpose.\n    //! \\param[in] trailer The trailer received with the notification message.\n    virtual kern_return_t DoMachNotifyPortDeleted(\n        notify_port_t notify,\n        mach_port_name_t name,\n        const mach_msg_trailer_t* trailer) = 0;\n\n    //! \\brief Handles port-destroyed notifications sent by\n    //!     `mach_notify_port_destroyed()`.\n    //!\n    //! A port-destroyed notification is generated when a receive right with a\n    //! port-destroyed notification request is destroyed. Rather than destroying\n    //! the receive right, it is transferred via this notification’s \\a rights\n    //! parameter.\n    //!\n    //! This behaves equivalently to a `do_mach_notify_port_destroyed()`\n    //! function used with `notify_server()`.\n    //!\n    //! \\param[in] notify The Mach port that the notification was sent to.\n    //! \\param[in] rights A receive right for the port that would have been\n    //!     destroyed. The callee takes ownership of this port, however, if the\n    //!     callee does not wish to take ownership, it may set \\a\n    //!     destroy_request to `true`.\n    //! \\param[in] trailer The trailer received with the notification message.\n    //! \\param[out] destroy_request `true` if the request message is to be\n    //!     destroyed even when this method returns success. See\n    //!     MachMessageServer::Interface.\n    virtual kern_return_t DoMachNotifyPortDestroyed(\n        notify_port_t notify,\n        mach_port_t rights,\n        const mach_msg_trailer_t* trailer,\n        bool* destroy_request) = 0;\n\n    //! \\brief Handles no-senders notifications sent by\n    //!     `mach_notify_no_senders()`.\n    //!\n    //! A no-senders notification is generated when a receive right with a\n    //! no-senders notification request loses its last corresponding send right.\n    //!\n    //! This behaves equivalently to a `do_mach_notify_no_senders()` function\n    //! used with `notify_server()`.\n    //!\n    //! \\param[in] notify The Mach port that the notification was sent to.\n    //! \\param[in] mscount The value of the sender-less port’s make-send count\n    //!     at the time the notification was generated.\n    //! \\param[in] trailer The trailer received with the notification message.\n    virtual kern_return_t DoMachNotifyNoSenders(\n        notify_port_t notify,\n        mach_port_mscount_t mscount,\n        const mach_msg_trailer_t* trailer) = 0;\n\n    //! \\brief Handles send-once notifications sent by\n    //!     `mach_notify_send_once()`.\n    //!\n    //! A send-once notification is generated when a send-once right is\n    //! destroyed without being used.\n    //!\n    //! This behaves equivalently to a `do_mach_notify_send_once()` function\n    //! used with `notify_server()`.\n    //!\n    //! \\param[in] notify The Mach port that the notification was sent to.\n    //! \\param[in] trailer The trailer received with the notification message.\n    //!\n    //! \\note Unlike the other notifications in the `notify` subsystem,\n    //!     send-once notifications are not generated as a result of a\n    //!     notification request, but are generated any time a send-once right\n    //!     is destroyed rather than being used. The notification is sent via\n    //!     the send-once right to its receiver. These notifications are more\n    //!     useful for clients, not servers. Send-once notifications are\n    //!     normally handled by MIG-generated client routines, which make\n    //!     send-once rights for their reply ports and interpret send-once\n    //!     notifications as a signal that there will be no reply. Although not\n    //!     expected to be primarily useful for servers, this method is provided\n    //!     because send-once notifications are defined as a part of the\n    //!     `notify` subsystem.\n    virtual kern_return_t DoMachNotifySendOnce(\n        notify_port_t notify,\n        const mach_msg_trailer_t* trailer) = 0;\n\n    //! \\brief Handles dead-name notifications sent by\n    //!     `mach_notify_dead_name()`.\n    //!\n    //! A dead-name notification is generated when a port with a dead-name\n    //! notification request is destroyed and the right becomes a dead name.\n    //!\n    //! This behaves equivalently to a `do_mach_notify_dead_name()` function\n    //! used with `notify_server()`.\n    //!\n    //! \\param[in] notify The Mach port that the notification was sent to.\n    //! \\param[in] name The dead name. Although this is transferred as a\n    //!     `mach_port_name_t` and not a `mach_port_t`, the callee assumes an\n    //!     additional reference to this port when this method is called. See\n    //!     the note below.\n    //! \\param[in] trailer The trailer received with the notification message.\n    //!\n    //! \\note When a dead-name notification is generated, the user reference\n    //!     count of the dead name is incremented. A send right with one\n    //!     reference that becomes a dead name will have one dead-name\n    //!     reference, and the dead-name notification will add another dead-name\n    //!     reference, for a total of 2. DoMachNotifyDeadName() implementations\n    //!     must take care to deallocate this extra reference. There is no \\a\n    //!     destroy_request parameter to simplify this operation because\n    //!     dead-name notifications carry a port name only (\\a name is of type\n    //!     `mach_port_name_t`) without transferring port rights, and are thus\n    //!     not complex Mach messages.\n    virtual kern_return_t DoMachNotifyDeadName(\n        notify_port_t notify,\n        mach_port_name_t name,\n        const mach_msg_trailer_t* trailer) = 0;\n\n   protected:\n    ~Interface() {}\n  };\n\n  //! \\brief A concrete implementation of Interface that provides a default\n  //!     behavior for all `notify` routines.\n  //!\n  //! The Mach `notify` subsystem contains a collection of unrelated routines,\n  //! and a single server would rarely need to implement all of them. To make it\n  //! easier to use NotifyServer, a server can inherit from DefaultInterface\n  //! instead of Interface. Unless overridden, each routine in DefaultInterface\n  //! returns `MIG_BAD_ID` to indicate to the caller that the `notify` message\n  //! was unexpected and not processed.\n  class DefaultInterface : public Interface {\n   public:\n    DefaultInterface(const DefaultInterface&) = delete;\n    DefaultInterface& operator=(const DefaultInterface&) = delete;\n\n    // Interface:\n\n    kern_return_t DoMachNotifyPortDeleted(\n        notify_port_t notify,\n        mach_port_name_t name,\n        const mach_msg_trailer_t* trailer) override;\n\n    kern_return_t DoMachNotifyPortDestroyed(\n        notify_port_t notify,\n        mach_port_t rights,\n        const mach_msg_trailer_t* trailer,\n        bool* destroy_request) override;\n\n    kern_return_t DoMachNotifyNoSenders(\n        notify_port_t notify,\n        mach_port_mscount_t mscount,\n        const mach_msg_trailer_t* trailer) override;\n\n    kern_return_t DoMachNotifySendOnce(\n        notify_port_t notify,\n        const mach_msg_trailer_t* trailer) override;\n\n    kern_return_t DoMachNotifyDeadName(\n        notify_port_t notify,\n        mach_port_name_t name,\n        const mach_msg_trailer_t* trailer) override;\n\n   protected:\n    DefaultInterface() : Interface() {}\n    ~DefaultInterface() {}\n  };\n\n  //! \\brief Constructs an object of this class.\n  //!\n  //! \\param[in] interface The interface to dispatch requests to. Weak.\n  explicit NotifyServer(Interface* interface);\n\n  NotifyServer(const NotifyServer&) = delete;\n  NotifyServer& operator=(const NotifyServer&) = delete;\n\n  // MachMessageServer::Interface:\n\n  bool MachMessageServerFunction(const mach_msg_header_t* in_header,\n                                 mach_msg_header_t* out_header,\n                                 bool* destroy_complex_request) override;\n\n  std::set<mach_msg_id_t> MachMessageServerRequestIDs() override;\n\n  mach_msg_size_t MachMessageServerRequestSize() override;\n  mach_msg_size_t MachMessageServerReplySize() override;\n\n private:\n  Interface* interface_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_NOTIFY_SERVER_H_\n"
  },
  {
    "path": "util/mach/notify_server_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/notify_server.h\"\n\n#include <stddef.h>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/compiler_specific.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/mach/mach_message.h\"\n#include \"util/mach/mach_message_server.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing testing::AllOf;\nusing testing::DoAll;\nusing testing::Eq;\nusing testing::Pointee;\nusing testing::ResultOf;\nusing testing::Return;\nusing testing::SetArgPointee;\nusing testing::StrictMock;\nusing testing::WithArg;\n\n//! \\brief Adds a send right to an existing receive right.\n//!\n//! \\param[in] receive_right The receive right to add a send right to.\n//!\n//! \\return The send right, which will have the same name as the receive right.\n//!     On failure, `MACH_PORT_NULL` with a Google Test failure added.\nmach_port_t SendRightFromReceiveRight(mach_port_t receive_right) {\n  kern_return_t kr = mach_port_insert_right(\n      mach_task_self(), receive_right, receive_right, MACH_MSG_TYPE_MAKE_SEND);\n  if (kr != KERN_SUCCESS) {\n    EXPECT_EQ(kr, KERN_SUCCESS)\n        << MachErrorMessage(kr, \"mach_port_insert_right\");\n    return MACH_PORT_NULL;\n  }\n\n  return receive_right;\n}\n\n//! \\brief Extracts a send-once right from a receive right.\n//!\n//! \\param[in] receive_right The receive right to make a send-once right from.\n//!\n//! \\return The send-once right. On failure, `MACH_PORT_NULL` with a Google Test\n//!     failure added.\nmach_port_t SendOnceRightFromReceiveRight(mach_port_t receive_right) {\n  mach_port_t send_once_right;\n  mach_msg_type_name_t acquired_type;\n  kern_return_t kr = mach_port_extract_right(mach_task_self(),\n                                             receive_right,\n                                             MACH_MSG_TYPE_MAKE_SEND_ONCE,\n                                             &send_once_right,\n                                             &acquired_type);\n  if (kr != KERN_SUCCESS) {\n    EXPECT_EQ(kr, KERN_SUCCESS)\n        << MachErrorMessage(kr, \"mach_port_extract_right\");\n    return MACH_PORT_NULL;\n  }\n\n  EXPECT_EQ(acquired_type,\n            implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE));\n\n  return send_once_right;\n}\n\n//! \\brief Deallocates a Mach port by calling `mach_port_deallocate()`.\n//!\n//! This function exists to adapt `mach_port_deallocate()` to a function that\n//! accepts a single argument and has no return value. It can be used with the\n//! testing::Invoke() Google Mock action.\n//!\n//! On failure, a Google Test failure will be added.\nvoid MachPortDeallocate(mach_port_t port) {\n  kern_return_t kr = mach_port_deallocate(mach_task_self(), port);\n  EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"mach_port_deallocate\");\n}\n\n//! \\brief Determines whether a specific right is held for a Mach port.\n//!\n//! \\param[in] port The port to check for a right.\n//! \\param[in] right The right to check for.\n//!\n//! \\return `true` if \\a port has \\a right, `false` otherwise. On faliure,\n//!     `false` with a Google Test failure added.\nbool IsRight(mach_port_t port, mach_port_type_t right) {\n  mach_port_type_t type;\n  kern_return_t kr = mach_port_type(mach_task_self(), port, &type);\n  if (kr != KERN_SUCCESS) {\n    EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"mach_port_type\");\n    return false;\n  }\n\n  return type & right;\n}\n\n//! \\brief Determines whether a receive right is held for a Mach port.\n//!\n//! This is a special single-argument form of IsRight() for ease of use in a\n//! Google Mock matcher.\n//!\n//! \\param[in] port The port to check for a receive right.\n//!\n//! \\return `true` if a receive right is held, `false` otherwise. On faliure,\n//!     `false` with a Google Test failure added.\nbool IsReceiveRight(mach_port_t port) {\n  return IsRight(port, MACH_PORT_TYPE_RECEIVE);\n}\n\n//! \\brief Returns the user reference count for port rights.\n//!\n//! \\param[in] port The port whose user reference count should be returned.\n//! \\param[in] right The port right to return the user reference count for.\n//!\n//! \\return The user reference count for the specified port and right. On\n//!     failure, `-1` with a Google Test failure added.\nmach_port_urefs_t RightRefCount(mach_port_t port, mach_port_right_t right) {\n  mach_port_urefs_t refs;\n  kern_return_t kr = mach_port_get_refs(mach_task_self(), port, right, &refs);\n  if (kr != KERN_SUCCESS) {\n    EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"mach_port_get_refs\");\n    return -1;\n  }\n\n  return refs;\n}\n\n//! \\brief Returns the user reference count for a port’s dead-name rights.\n//!\n//! This is a special single-argument form of RightRefCount() for ease of use in\n//! a Google Mock matcher.\n//!\n//! \\param[in] port The port whose dead-name user reference count should be\n//!     returned.\n//!\n//! \\return The user reference count for the port’s dead-name rights. On\n//!     failure, `-1` with a Google Test failure added.\nmach_port_urefs_t DeadNameRightRefCount(mach_port_t port) {\n  return RightRefCount(port, MACH_PORT_RIGHT_DEAD_NAME);\n}\n\nclass NotifyServerTestBase : public testing::Test,\n                             public NotifyServer::Interface {\n public:\n  NotifyServerTestBase(const NotifyServerTestBase&) = delete;\n  NotifyServerTestBase& operator=(const NotifyServerTestBase&) = delete;\n\n  // NotifyServer::Interface:\n\n  MOCK_METHOD(kern_return_t,\n              DoMachNotifyPortDeleted,\n              (notify_port_t notify,\n               mach_port_name_t name,\n               const mach_msg_trailer_t* trailer),\n              (override));\n\n  MOCK_METHOD(kern_return_t,\n              DoMachNotifyPortDestroyed,\n              (notify_port_t notify,\n               mach_port_t rights,\n               const mach_msg_trailer_t* trailer,\n               bool* destroy_request),\n              (override));\n\n  MOCK_METHOD(kern_return_t,\n              DoMachNotifyNoSenders,\n              (notify_port_t notify,\n               mach_port_mscount_t mscount,\n               const mach_msg_trailer_t* trailer),\n              (override));\n\n  MOCK_METHOD(kern_return_t,\n              DoMachNotifySendOnce,\n              (notify_port_t notify, const mach_msg_trailer_t* trailer),\n              (override));\n\n  MOCK_METHOD(kern_return_t,\n              DoMachNotifyDeadName,\n              (notify_port_t notify,\n               mach_port_name_t name,\n               const mach_msg_trailer_t* trailer),\n              (override));\n\n protected:\n  NotifyServerTestBase() : testing::Test(), NotifyServer::Interface() {}\n\n  ~NotifyServerTestBase() override {}\n\n  //! \\brief Requests a Mach port notification.\n  //!\n  //! \\a name, \\a variant, and \\a sync are passed as-is to\n  //! `mach_port_request_notification()`. The notification will be sent to a\n  //! send-once right made from ServerPort(). Any previous send right for the\n  //! notification will be deallocated.\n  //!\n  //! \\return `true` on success, `false` on failure with a Google Test failure\n  //!     added.\n  bool RequestMachPortNotification(mach_port_t name,\n                                   mach_msg_id_t variant,\n                                   mach_port_mscount_t sync) {\n    mach_port_t previous;\n    kern_return_t kr =\n        mach_port_request_notification(mach_task_self(),\n                                       name,\n                                       variant,\n                                       sync,\n                                       ServerPort(),\n                                       MACH_MSG_TYPE_MAKE_SEND_ONCE,\n                                       &previous);\n    if (kr != KERN_SUCCESS) {\n      EXPECT_EQ(kr, KERN_SUCCESS)\n          << MachErrorMessage(kr, \"mach_port_request_notification\");\n      return false;\n    }\n\n    base::apple::ScopedMachSendRight previous_owner(previous);\n    EXPECT_EQ(previous, kMachPortNull);\n\n    return true;\n  }\n\n  //! \\brief Runs a NotifyServer Mach message server.\n  //!\n  //! The server will listen on ServerPort() in persistent nonblocking mode, and\n  //! dispatch received messages to the appropriate NotifyServer::Interface\n  //! method. Google Mock expectations check that the proper method, if any, is\n  //! called exactly once, and that no undesired methods are called.\n  //!\n  //! MachMessageServer::Run() is expected to return `MACH_RCV_TIMED_OUT`,\n  //! because it runs in persistent nonblocking mode. If it returns anything\n  //! else, a Google Test assertion is added.\n  void RunServer() {\n    NotifyServer notify_server(this);\n    mach_msg_return_t mr =\n        MachMessageServer::Run(&notify_server,\n                               ServerPort(),\n                               kMachMessageReceiveAuditTrailer,\n                               MachMessageServer::kPersistent,\n                               MachMessageServer::kReceiveLargeError,\n                               kMachMessageTimeoutNonblocking);\n    ASSERT_EQ(mr, MACH_RCV_TIMED_OUT)\n        << MachErrorMessage(mr, \"MachMessageServer::Run\");\n  }\n\n  //! \\brief Returns the receive right to be used for the server.\n  //!\n  //! This receive right is created lazily on a per-test basis. It is destroyed\n  //! by TearDown() at the conclusion of each test.\n  //!\n  //! \\return The server port receive right, creating it if one has not yet been\n  //!     established for the current test. On failure, returns `MACH_PORT_NULL`\n  //!     with a Google Test failure added.\n  mach_port_t ServerPort() {\n    if (!server_port_.is_valid()) {\n      server_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n      EXPECT_TRUE(server_port_.is_valid());\n    }\n\n    return server_port_.get();\n  }\n\n  // testing::Test:\n  void TearDown() override { server_port_.reset(); }\n\n private:\n  base::apple::ScopedMachReceiveRight server_port_;\n};\n\nusing NotifyServerTest = StrictMock<NotifyServerTestBase>;\n\nTEST_F(NotifyServerTest, Basic) {\n  NotifyServer server(this);\n\n  std::set<mach_msg_id_t> expect_request_ids;\n  expect_request_ids.insert(MACH_NOTIFY_PORT_DELETED);\n  expect_request_ids.insert(MACH_NOTIFY_PORT_DESTROYED);\n  expect_request_ids.insert(MACH_NOTIFY_NO_SENDERS);\n  expect_request_ids.insert(MACH_NOTIFY_SEND_ONCE);\n  expect_request_ids.insert(MACH_NOTIFY_DEAD_NAME);\n  EXPECT_EQ(server.MachMessageServerRequestIDs(), expect_request_ids);\n\n  // The port-destroyed notification is the largest request message in the\n  // subsystem. <mach/notify.h> defines the same structure, but with a basic\n  // trailer, so use offsetof to get the size of the basic structure without any\n  // trailer.\n  EXPECT_EQ(server.MachMessageServerRequestSize(),\n            offsetof(mach_port_destroyed_notification_t, trailer));\n\n  mig_reply_error_t reply;\n  EXPECT_EQ(server.MachMessageServerReplySize(), sizeof(reply));\n}\n\n// When no notifications are requested, nothing should happen.\nTEST_F(NotifyServerTest, NoNotification) {\n  RunServer();\n}\n\n// When a send-once right with a dead-name notification request is deallocated,\n// a port-deleted notification should be generated.\nTEST_F(NotifyServerTest, MachNotifyPortDeleted) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  base::apple::ScopedMachSendRight send_once_right(\n      SendOnceRightFromReceiveRight(receive_right.get()));\n  ASSERT_TRUE(send_once_right.is_valid());\n\n  ASSERT_TRUE(RequestMachPortNotification(\n      send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));\n\n  EXPECT_CALL(\n      *this,\n      DoMachNotifyPortDeleted(ServerPort(),\n                              send_once_right.get(),\n                              ResultOf(AuditPIDFromMachMessageTrailer, 0)))\n      .WillOnce(Return(MIG_NO_REPLY))\n      .RetiresOnSaturation();\n\n  send_once_right.reset();\n\n  RunServer();\n}\n\n// When a receive right with a port-destroyed notification request is destroyed,\n// a port-destroyed notification should be generated.\nTEST_F(NotifyServerTest, MachNotifyPortDestroyed) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  ASSERT_TRUE(RequestMachPortNotification(\n      receive_right.get(), MACH_NOTIFY_PORT_DESTROYED, 0));\n\n  EXPECT_CALL(\n      *this,\n      DoMachNotifyPortDestroyed(ServerPort(),\n                                ResultOf(IsReceiveRight, true),\n                                ResultOf(AuditPIDFromMachMessageTrailer, 0),\n                                Pointee(Eq(false))))\n      .WillOnce(DoAll(SetArgPointee<3>(true), Return(MIG_NO_REPLY)))\n      .RetiresOnSaturation();\n\n  receive_right.reset();\n\n  RunServer();\n}\n\n// When a receive right with a port-destroyed notification request is not\n// destroyed, no port-destroyed notification should be generated.\nTEST_F(NotifyServerTest, MachNotifyPortDestroyed_NoNotification) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  ASSERT_TRUE(RequestMachPortNotification(\n      receive_right.get(), MACH_NOTIFY_PORT_DESTROYED, 0));\n\n  RunServer();\n}\n\n// When a no-senders notification request is registered for a receive right with\n// no senders, a no-senders notification should be generated.\nTEST_F(NotifyServerTest, MachNotifyNoSenders_NoSendRight) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  ASSERT_TRUE(RequestMachPortNotification(\n      receive_right.get(), MACH_NOTIFY_NO_SENDERS, 0));\n\n  EXPECT_CALL(*this,\n              DoMachNotifyNoSenders(\n                  ServerPort(), 0, ResultOf(AuditPIDFromMachMessageTrailer, 0)))\n      .WillOnce(Return(MIG_NO_REPLY))\n      .RetiresOnSaturation();\n\n  RunServer();\n}\n\n// When the last send right corresponding to a receive right with a no-senders\n// notification request is deallocated, a no-senders notification should be\n// generated.\nTEST_F(NotifyServerTest, MachNotifyNoSenders_SendRightDeallocated) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  base::apple::ScopedMachSendRight send_right(\n      SendRightFromReceiveRight(receive_right.get()));\n  ASSERT_TRUE(send_right.is_valid());\n\n  ASSERT_TRUE(RequestMachPortNotification(\n      receive_right.get(), MACH_NOTIFY_NO_SENDERS, 1));\n\n  EXPECT_CALL(*this,\n              DoMachNotifyNoSenders(\n                  ServerPort(), 1, ResultOf(AuditPIDFromMachMessageTrailer, 0)))\n      .WillOnce(Return(MIG_NO_REPLY))\n      .RetiresOnSaturation();\n\n  send_right.reset();\n\n  RunServer();\n}\n\n// When the a receive right with a no-senders notification request never loses\n// all senders, no no-senders notification should be generated.\nTEST_F(NotifyServerTest, MachNotifyNoSenders_NoNotification) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  base::apple::ScopedMachSendRight send_right_0(\n      SendRightFromReceiveRight(receive_right.get()));\n  ASSERT_TRUE(send_right_0.is_valid());\n\n  base::apple::ScopedMachSendRight send_right_1(\n      SendRightFromReceiveRight(receive_right.get()));\n  ASSERT_TRUE(send_right_1.is_valid());\n\n  ASSERT_TRUE(RequestMachPortNotification(\n      receive_right.get(), MACH_NOTIFY_NO_SENDERS, 1));\n\n  send_right_1.reset();\n\n  RunServer();\n\n  EXPECT_EQ(RightRefCount(receive_right.get(), MACH_PORT_RIGHT_RECEIVE), 1u);\n  EXPECT_EQ(RightRefCount(receive_right.get(), MACH_PORT_RIGHT_SEND), 1u);\n}\n\n// When a send-once right is deallocated without being used, a send-once\n// notification notification should be sent via the send-once right.\nTEST_F(NotifyServerTest, MachNotifySendOnce_ExplicitDeallocation) {\n  base::apple::ScopedMachSendRight send_once_right(\n      SendOnceRightFromReceiveRight(ServerPort()));\n  ASSERT_TRUE(send_once_right.is_valid());\n\n  EXPECT_CALL(*this,\n              DoMachNotifySendOnce(ServerPort(),\n                                   ResultOf(AuditPIDFromMachMessageTrailer, 0)))\n      .WillOnce(Return(MIG_NO_REPLY))\n      .RetiresOnSaturation();\n\n  send_once_right.reset();\n\n  RunServer();\n}\n\n// When a send-once right is sent to a receiver that never dequeues the message,\n// the send-once right is destroyed, and a send-once notification should appear\n// on the reply port.\nTEST_F(NotifyServerTest, MachNotifySendOnce_ImplicitDeallocation) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  mach_msg_empty_send_t message = {};\n  message.header.msgh_bits =\n      MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);\n  message.header.msgh_size = sizeof(message);\n  message.header.msgh_remote_port = receive_right.get();\n  message.header.msgh_local_port = ServerPort();\n  mach_msg_return_t mr = mach_msg(&message.header,\n                                  MACH_SEND_MSG | MACH_SEND_TIMEOUT,\n                                  message.header.msgh_size,\n                                  0,\n                                  MACH_PORT_NULL,\n                                  0,\n                                  MACH_PORT_NULL);\n  ASSERT_EQ(mr, MACH_MSG_SUCCESS) << MachErrorMessage(mr, \"mach_msg\");\n\n  EXPECT_CALL(*this,\n              DoMachNotifySendOnce(ServerPort(),\n                                   ResultOf(AuditPIDFromMachMessageTrailer, 0)))\n      .WillOnce(Return(MIG_NO_REPLY))\n      .RetiresOnSaturation();\n\n  receive_right.reset();\n\n  RunServer();\n}\n\n// When the receive right corresponding to a send-once right with a dead-name\n// notification request is destroyed, a dead-name notification should be\n// generated.\nTEST_F(NotifyServerTest, MachNotifyDeadName) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  base::apple::ScopedMachSendRight send_once_right(\n      SendOnceRightFromReceiveRight(receive_right.get()));\n  ASSERT_TRUE(send_once_right.is_valid());\n\n  ASSERT_TRUE(RequestMachPortNotification(\n      send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));\n\n  // send_once_right becomes a dead name with the send-once right’s original\n  // user reference count of 1, but the dead-name notification increments the\n  // dead-name reference count, so it becomes 2. Take care to deallocate that\n  // reference. The original reference is managed by send_once_right_owner.\n  EXPECT_CALL(*this,\n              DoMachNotifyDeadName(ServerPort(),\n                                   AllOf(send_once_right.get(),\n                                         ResultOf(DeadNameRightRefCount, 2)),\n                                   ResultOf(AuditPIDFromMachMessageTrailer, 0)))\n      .WillOnce(\n          DoAll(WithArg<1>(MachPortDeallocate), Return(MIG_NO_REPLY)))\n      .RetiresOnSaturation();\n\n  receive_right.reset();\n\n  RunServer();\n\n  EXPECT_TRUE(IsRight(send_once_right.get(), MACH_PORT_TYPE_DEAD_NAME));\n\n  EXPECT_EQ(RightRefCount(send_once_right.get(), MACH_PORT_RIGHT_SEND_ONCE),\n            0u);\n  EXPECT_EQ(DeadNameRightRefCount(send_once_right.get()), 1u);\n}\n\n// When the receive right corresponding to a send-once right with a dead-name\n// notification request is not destroyed, no dead-name notification should be\n// generated.\nTEST_F(NotifyServerTest, MachNotifyDeadName_NoNotification) {\n  base::apple::ScopedMachReceiveRight receive_right(\n      NewMachPort(MACH_PORT_RIGHT_RECEIVE));\n  ASSERT_TRUE(receive_right.is_valid());\n\n  base::apple::ScopedMachSendRight send_once_right(\n      SendOnceRightFromReceiveRight(receive_right.get()));\n  ASSERT_TRUE(send_once_right.is_valid());\n\n  ASSERT_TRUE(RequestMachPortNotification(\n      send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));\n\n  RunServer();\n\n  EXPECT_FALSE(IsRight(send_once_right.get(), MACH_PORT_TYPE_DEAD_NAME));\n\n  EXPECT_EQ(RightRefCount(send_once_right.get(), MACH_PORT_RIGHT_SEND_ONCE),\n            1u);\n  EXPECT_EQ(DeadNameRightRefCount(send_once_right.get()), 0u);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/scoped_task_suspend.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/scoped_task_suspend.h\"\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nScopedTaskSuspend::ScopedTaskSuspend(task_t task) : task_(task) {\n  DCHECK_NE(task_, mach_task_self());\n\n  kern_return_t kr = task_suspend(task_);\n  if (kr != KERN_SUCCESS) {\n    task_ = TASK_NULL;\n    MACH_LOG(ERROR, kr) << \"task_suspend\";\n  }\n}\n\nScopedTaskSuspend::~ScopedTaskSuspend() {\n  if (task_ != TASK_NULL) {\n    kern_return_t kr = task_resume(task_);\n    MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << \"task_resume\";\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/scoped_task_suspend.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_\n#define CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_\n\n#include <mach/mach.h>\n\n\nnamespace crashpad {\n\n//! \\brief Manages the suspension of another task.\n//!\n//! While an object of this class exists, the other task will be suspended. Once\n//! the object is destroyed, the other task will become eligible for resumption.\n//! Note that suspensions are counted, and the task will not actually resume\n//! unless its suspend count drops to 0.\n//!\n//! Callers should not attempt to suspend the current task (`mach_task_self()`).\nclass ScopedTaskSuspend {\n public:\n  explicit ScopedTaskSuspend(task_t task);\n\n  ScopedTaskSuspend(const ScopedTaskSuspend&) = delete;\n  ScopedTaskSuspend& operator=(const ScopedTaskSuspend&) = delete;\n\n  ~ScopedTaskSuspend();\n\n private:\n  task_t task_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_\n"
  },
  {
    "path": "util/mach/scoped_task_suspend_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/scoped_task_suspend.h\"\n\n#include <mach/mach.h>\n\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"test/mac/mach_multiprocess.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nint SuspendCount(task_t task) {\n  // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO.\n  // TASK_BASIC_INFO_64 is equivalent and works on earlier systems.\n  task_basic_info_64 task_basic_info;\n  mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT;\n  kern_return_t kr = task_info(task,\n                               TASK_BASIC_INFO_64,\n                               reinterpret_cast<task_info_t>(&task_basic_info),\n                               &task_basic_info_count);\n  if (kr != KERN_SUCCESS) {\n    ADD_FAILURE() << MachErrorMessage(kr, \"task_info\");\n    return -1;\n  }\n\n  return task_basic_info.suspend_count;\n}\n\nclass ScopedTaskSuspendTest final : public MachMultiprocess {\n public:\n  ScopedTaskSuspendTest() : MachMultiprocess() {}\n\n  ScopedTaskSuspendTest(const ScopedTaskSuspendTest&) = delete;\n  ScopedTaskSuspendTest& operator=(const ScopedTaskSuspendTest&) = delete;\n\n  ~ScopedTaskSuspendTest() {}\n\n private:\n  // MachMultiprocess:\n\n  void MachMultiprocessParent() override {\n    task_t child_task = ChildTask();\n\n    EXPECT_EQ(SuspendCount(child_task), 0);\n\n    {\n      ScopedTaskSuspend suspend(child_task);\n      EXPECT_EQ(SuspendCount(child_task), 1);\n\n      {\n        ScopedTaskSuspend suspend_again(child_task);\n        EXPECT_EQ(SuspendCount(child_task), 2);\n      }\n\n      EXPECT_EQ(SuspendCount(child_task), 1);\n    }\n\n    EXPECT_EQ(SuspendCount(child_task), 0);\n  }\n\n  void MachMultiprocessChild() override {\n  }\n};\n\nTEST(ScopedTaskSuspend, ScopedTaskSuspend) {\n  ScopedTaskSuspendTest scoped_task_suspend_test;\n  scoped_task_suspend_test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/symbolic_constants_mach.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/symbolic_constants_mach.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <string_view>\n\n#include \"base/strings/stringprintf.h\"\n#include \"util/mach/exception_behaviors.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n\nnamespace {\n\nconstexpr const char* kExceptionNames[] = {\n    nullptr,\n\n    // sed -Ene 's/^#define[[:space:]]EXC_([[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/    \"\\1\",/p'\n    //     /usr/include/mach/exception_types.h\n    \"BAD_ACCESS\",\n    \"BAD_INSTRUCTION\",\n    \"ARITHMETIC\",\n    \"EMULATION\",\n    \"SOFTWARE\",\n    \"BREAKPOINT\",\n    \"SYSCALL\",\n    \"MACH_SYSCALL\",\n    \"RPC_ALERT\",\n    \"CRASH\",\n    \"RESOURCE\",\n    \"GUARD\",\n    \"CORPSE_NOTIFY\",\n};\nstatic_assert(std::size(kExceptionNames) == EXC_TYPES_COUNT,\n              \"kExceptionNames length\");\n\nconstexpr char kExcPrefix[] = \"EXC_\";\nconstexpr char kExcMaskPrefix[] = \"EXC_MASK_\";\n\nconstexpr const char* kBehaviorNames[] = {\n    nullptr,\n\n    // sed -Ene 's/^# define[[:space:]]EXCEPTION_([[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/    \"\\1\",/p'\n    //     /usr/include/mach/exception_types.h\n    \"DEFAULT\",\n    \"STATE\",\n    \"STATE_IDENTITY\",\n};\n\nconstexpr char kBehaviorPrefix[] = \"EXCEPTION_\";\nconstexpr char kMachExceptionCodesFull[] = \"MACH_EXCEPTION_CODES\";\nconstexpr char kMachExceptionCodesShort[] = \"MACH\";\n\nconstexpr const char* kFlavorNames[] = {\n    \"THREAD_STATE_FLAVOR_LIST\",\n\n#if defined(__i386__) || defined(__x86_64__)\n    // sed -Ene 's/^#define ((x86|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/    \"\\1\",/p'\n    //     /usr/include/mach/i386/thread_status.h\n    // and then fix up by adding x86_SAVED_STATE32 and x86_SAVED_STATE64.\n    \"x86_THREAD_STATE32\",\n    \"x86_FLOAT_STATE32\",\n    \"x86_EXCEPTION_STATE32\",\n    \"x86_THREAD_STATE64\",\n    \"x86_FLOAT_STATE64\",\n    \"x86_EXCEPTION_STATE64\",\n    \"x86_THREAD_STATE\",\n    \"x86_FLOAT_STATE\",\n    \"x86_EXCEPTION_STATE\",\n    \"x86_DEBUG_STATE32\",\n    \"x86_DEBUG_STATE64\",\n    \"x86_DEBUG_STATE\",\n    \"THREAD_STATE_NONE\",\n    \"x86_SAVED_STATE32\",\n    \"x86_SAVED_STATE64\",\n    \"x86_AVX_STATE32\",\n    \"x86_AVX_STATE64\",\n    \"x86_AVX_STATE\",\n#elif defined(__ppc__) || defined(__ppc64__)\n    // sed -Ene 's/^#define ((PPC|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/    \"\\1\",/p'\n    //     usr/include/mach/ppc/thread_status.h\n    // (Mac OS X 10.6 SDK)\n    \"PPC_THREAD_STATE\",\n    \"PPC_FLOAT_STATE\",\n    \"PPC_EXCEPTION_STATE\",\n    \"PPC_VECTOR_STATE\",\n    \"PPC_THREAD_STATE64\",\n    \"PPC_EXCEPTION_STATE64\",\n    \"THREAD_STATE_NONE\",\n#elif defined(__arm__) || defined(__aarch64__)\n    // sed -Ene 's/^#define ((ARM|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/    \"\\1\",/p'\n    //     usr/include/mach/arm/thread_status.h\n    // (iOS 7 SDK)\n    // and then fix up by making the list sparse as appropriate.\n    \"ARM_THREAD_STATE\",\n    \"ARM_VFP_STATE\",\n    \"ARM_EXCEPTION_STATE\",\n    \"ARM_DEBUG_STATE\",\n    \"THREAD_STATE_NONE\",\n    \"ARM_THREAD_STATE64\",\n    \"ARM_EXCEPTION_STATE64\",\n    nullptr,\n    \"ARM_THREAD_STATE32\",\n    nullptr,\n    nullptr,\n    nullptr,\n    nullptr,\n    \"ARM_DEBUG_STATE32\",\n    \"ARM_DEBUG_STATE64\",\n    \"ARM_NEON_STATE\",\n    \"ARM_NEON_STATE64\",\n#endif\n};\n\n// Certain generic flavors have high constants not contiguous with the flavors\n// above. List them separately alongside their constants.\nconstexpr struct {\n  thread_state_flavor_t flavor;\n  const char* name;\n} kGenericFlavorNames[] = {\n    {THREAD_STATE_FLAVOR_LIST_NEW, \"THREAD_STATE_FLAVOR_LIST_NEW\"},\n    {THREAD_STATE_FLAVOR_LIST_10_9, \"THREAD_STATE_FLAVOR_LIST_10_9\"},\n};\n\n// Returns the short name for a flavor name, given its full flavor name.\nstd::string ThreadStateFlavorFullToShort(std::string_view flavor) {\n  // For generic flavors like THREAD_STATE_NONE and THREAD_STATE_FLAVOR_LIST_*.\n  static constexpr char kThreadState[] = \"THREAD_STATE_\";\n  size_t prefix_len = strlen(kThreadState);\n  const char* flavor_data = flavor.data();\n  size_t flavor_len = flavor.size();\n  if (flavor_len >= prefix_len &&\n      strncmp(flavor_data, kThreadState, prefix_len) == 0) {\n    return std::string(flavor_data + prefix_len, flavor_len - prefix_len);\n  }\n\n  // For architecture-specific flavors.\n#if defined(__i386__) || defined(__x86_64__)\n  static constexpr char kArchPrefix[] = \"x86_\";\n#elif defined(__ppc__) || defined(__ppc64__)\n  static constexpr char kArchPrefix[] = \"PPC_\";\n#elif defined(__arm__) || defined(__aarch64__)\n  static constexpr char kArchPrefix[] = \"ARM_\";\n#endif\n  prefix_len = strlen(kArchPrefix);\n  if (flavor_len >= prefix_len &&\n      strncmp(flavor_data, kArchPrefix, prefix_len) == 0) {\n    // Shorten the suffix by removing _STATE. If the suffix contains a\n    // significant designation like 32 or 64, keep it, so that a full name like\n    // x86_THREAD_STATE64 becomes a short name like THREAD64.\n    static constexpr struct {\n      const char* orig;\n      const char* repl;\n    } kStateSuffixes[] = {\n        {\"_STATE\", \"\"},\n        {\"_STATE32\", \"32\"},\n        {\"_STATE64\", \"64\"},\n    };\n    for (size_t suffix_index = 0; suffix_index < std::size(kStateSuffixes);\n         ++suffix_index) {\n      const char* suffix = kStateSuffixes[suffix_index].orig;\n      size_t suffix_len = strlen(suffix);\n      if (flavor_len >= suffix_len &&\n          strncmp(flavor_data + flavor_len - suffix_len, suffix, suffix_len) ==\n              0) {\n        std::string s(flavor_data + prefix_len,\n                      flavor_len - prefix_len - suffix_len);\n        return s.append(kStateSuffixes[suffix_index].repl);\n      }\n    }\n  }\n\n  return std::string(flavor_data, flavor_len);\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nstd::string ExceptionToString(exception_type_t exception,\n                              SymbolicConstantToStringOptions options) {\n  const char* exception_name =\n      implicit_cast<size_t>(exception) < std::size(kExceptionNames)\n          ? kExceptionNames[exception]\n          : nullptr;\n  if (!exception_name) {\n    if (options & kUnknownIsNumeric) {\n      return base::StringPrintf(\"%d\", exception);\n    }\n    return std::string();\n  }\n\n  if (options & kUseShortName) {\n    return std::string(exception_name);\n  }\n  return base::StringPrintf(\"%s%s\", kExcPrefix, exception_name);\n}\n\nbool StringToException(std::string_view string,\n                       StringToSymbolicConstantOptions options,\n                       exception_type_t* exception) {\n  if ((options & kAllowFullName) || (options & kAllowShortName)) {\n    bool can_match_full =\n        (options & kAllowFullName) &&\n        string.substr(0, strlen(kExcPrefix)).compare(kExcPrefix) == 0;\n    std::string_view short_string =\n        can_match_full ? string.substr(strlen(kExcPrefix)) : string;\n    for (exception_type_t index = 0;\n         index < implicit_cast<exception_type_t>(std::size(kExceptionNames));\n         ++index) {\n      const char* exception_name = kExceptionNames[index];\n      if (!exception_name) {\n        continue;\n      }\n      if (can_match_full && short_string.compare(exception_name) == 0) {\n        *exception = index;\n        return true;\n      }\n      if ((options & kAllowShortName) && string.compare(exception_name) == 0) {\n        *exception = index;\n        return true;\n      }\n    }\n  }\n\n  if (options & kAllowNumber) {\n    return StringToNumber(std::string(string.data(), string.length()),\n                          reinterpret_cast<unsigned int*>(exception));\n  }\n\n  return false;\n}\n\nstd::string ExceptionMaskToString(exception_mask_t exception_mask,\n                                  SymbolicConstantToStringOptions options) {\n  exception_mask_t local_exception_mask = exception_mask;\n  std::string mask_string;\n  bool has_forbidden_or = false;\n  for (size_t exception = 0; exception < std::size(kExceptionNames);\n       ++exception) {\n    const char* exception_name = kExceptionNames[exception];\n    exception_mask_t exception_mask_value = 1 << exception;\n    if (exception_name && (local_exception_mask & exception_mask_value)) {\n      if (!mask_string.empty()) {\n        if (!(options & kUseOr)) {\n          has_forbidden_or = true;\n          break;\n        }\n        mask_string.append(\"|\");\n      }\n      if (!(options & kUseShortName)) {\n        mask_string.append(kExcMaskPrefix);\n      }\n      mask_string.append(exception_name);\n      local_exception_mask &= ~exception_mask_value;\n    }\n  }\n\n  if (has_forbidden_or) {\n    local_exception_mask = exception_mask;\n    mask_string.clear();\n  }\n\n  // Deal with any remainder.\n  if (local_exception_mask) {\n    if (!(options & kUnknownIsNumeric)) {\n      return std::string();\n    }\n    if (!mask_string.empty()) {\n      mask_string.append(\"|\");\n    }\n    mask_string.append(base::StringPrintf(\"%#x\", local_exception_mask));\n  }\n\n  return mask_string;\n}\n\nbool StringToExceptionMask(std::string_view string,\n                           StringToSymbolicConstantOptions options,\n                           exception_mask_t* exception_mask) {\n  if (options & kAllowOr) {\n    options &= ~kAllowOr;\n    exception_mask_t build_mask = 0;\n    size_t pos = -1;\n    do {\n      ++pos;\n      const size_t start = pos;\n      pos = string.find('|', pos);\n      std::string_view substring = (pos == std::string_view::npos)\n                                       ? string.substr(start)\n                                       : string.substr(start, pos - start);\n      exception_mask_t temp_mask;\n      if (!StringToExceptionMask(substring, options, &temp_mask)) {\n        return false;\n      }\n      build_mask |= temp_mask;\n    } while (pos != std::string_view::npos);\n\n    *exception_mask = build_mask;\n    return true;\n  }\n\n  if ((options & kAllowFullName) || (options & kAllowShortName)) {\n    bool can_match_full =\n        (options & kAllowFullName) &&\n        string.substr(0, strlen(kExcMaskPrefix)).compare(kExcMaskPrefix) == 0;\n    std::string_view short_string =\n        can_match_full ? string.substr(strlen(kExcMaskPrefix)) : string;\n    for (exception_type_t index = 0;\n         index < implicit_cast<exception_type_t>(std::size(kExceptionNames));\n         ++index) {\n      const char* exception_name = kExceptionNames[index];\n      if (!exception_name) {\n        continue;\n      }\n      if (can_match_full && short_string.compare(exception_name) == 0) {\n        *exception_mask = 1 << index;\n        return true;\n      }\n      if ((options & kAllowShortName) && string.compare(exception_name) == 0) {\n        *exception_mask = 1 << index;\n        return true;\n      }\n    }\n\n    // EXC_MASK_ALL is a special case: it is not in kExceptionNames as it exists\n    // only as a mask value.\n    static constexpr char kExcMaskAll[] = \"ALL\";\n    if ((can_match_full && short_string.compare(kExcMaskAll) == 0) ||\n        ((options & kAllowShortName) && string.compare(kExcMaskAll) == 0)) {\n      *exception_mask = ExcMaskAll();\n      return true;\n    }\n  }\n\n  if (options & kAllowNumber) {\n    return StringToNumber(std::string(string.data(), string.length()),\n                          reinterpret_cast<unsigned int*>(exception_mask));\n  }\n\n  return false;\n}\n\nstd::string ExceptionBehaviorToString(exception_behavior_t behavior,\n                                      SymbolicConstantToStringOptions options) {\n  const exception_behavior_t basic_behavior = ExceptionBehaviorBasic(behavior);\n\n  const char* behavior_name =\n      implicit_cast<size_t>(basic_behavior) < std::size(kBehaviorNames)\n          ? kBehaviorNames[basic_behavior]\n          : nullptr;\n  if (!behavior_name) {\n    if (options & kUnknownIsNumeric) {\n      return base::StringPrintf(\"%#x\", behavior);\n    }\n    return std::string();\n  }\n\n  std::string behavior_string;\n  if (options & kUseShortName) {\n    behavior_string.assign(behavior_name);\n  } else {\n    behavior_string.assign(base::StringPrintf(\n        \"%s%s\", kBehaviorPrefix, behavior_name));\n  }\n\n  if (ExceptionBehaviorHasMachExceptionCodes(behavior)) {\n    behavior_string.append(\"|\");\n    behavior_string.append((options & kUseShortName) ? kMachExceptionCodesShort\n                                                     : kMachExceptionCodesFull);\n  }\n\n  return behavior_string;\n}\n\nbool StringToExceptionBehavior(std::string_view string,\n                               StringToSymbolicConstantOptions options,\n                               exception_behavior_t* behavior) {\n  std::string_view sp = string;\n  exception_behavior_t build_behavior = 0;\n  size_t pos = sp.find('|', 0);\n  if (pos != std::string_view::npos) {\n    std::string_view left = sp.substr(0, pos);\n    std::string_view right = sp.substr(pos + 1, sp.length() - pos - 1);\n    if (options & kAllowFullName) {\n      if (left.compare(kMachExceptionCodesFull) == 0) {\n        build_behavior |= MACH_EXCEPTION_CODES;\n        sp = right;\n      } else if (right.compare(kMachExceptionCodesFull) == 0) {\n        build_behavior |= MACH_EXCEPTION_CODES;\n        sp = left;\n      }\n    }\n    if (!(build_behavior & MACH_EXCEPTION_CODES) &&\n        (options & kAllowShortName)) {\n      if (left.compare(kMachExceptionCodesShort) == 0) {\n        build_behavior |= MACH_EXCEPTION_CODES;\n        sp = right;\n      } else if (right.compare(kMachExceptionCodesShort) == 0) {\n        build_behavior |= MACH_EXCEPTION_CODES;\n        sp = left;\n      }\n    }\n    if (!(build_behavior & MACH_EXCEPTION_CODES)) {\n      return false;\n    }\n  }\n\n  if ((options & kAllowFullName) || (options & kAllowShortName)) {\n    bool can_match_full =\n        (options & kAllowFullName) &&\n        sp.substr(0, strlen(kBehaviorPrefix)).compare(kBehaviorPrefix) == 0;\n    std::string_view short_string =\n        can_match_full ? sp.substr(strlen(kBehaviorPrefix)) : sp;\n    for (exception_behavior_t index = 0;\n         index < implicit_cast<exception_behavior_t>(std::size(kBehaviorNames));\n         ++index) {\n      const char* behavior_name = kBehaviorNames[index];\n      if (!behavior_name) {\n        continue;\n      }\n      if (can_match_full && short_string.compare(behavior_name) == 0) {\n        build_behavior |= index;\n        *behavior = build_behavior;\n        return true;\n      }\n      if ((options & kAllowShortName) && sp.compare(behavior_name) == 0) {\n        build_behavior |= index;\n        *behavior = build_behavior;\n        return true;\n      }\n    }\n  }\n\n  if (options & kAllowNumber) {\n    exception_behavior_t temp_behavior;\n    if (!StringToNumber(std::string(sp.data(), sp.length()),\n                        reinterpret_cast<unsigned int*>(&temp_behavior))) {\n      return false;\n    }\n    build_behavior |= temp_behavior;\n    *behavior = build_behavior;\n    return true;\n  }\n\n  return false;\n}\n\nstd::string ThreadStateFlavorToString(thread_state_flavor_t flavor,\n                                      SymbolicConstantToStringOptions options) {\n  const char* flavor_name =\n      implicit_cast<size_t>(flavor) < std::size(kFlavorNames)\n          ? kFlavorNames[flavor]\n          : nullptr;\n\n  if (!flavor_name) {\n    for (size_t generic_flavor_index = 0;\n         generic_flavor_index < std::size(kGenericFlavorNames);\n         ++generic_flavor_index) {\n      if (flavor == kGenericFlavorNames[generic_flavor_index].flavor) {\n        flavor_name = kGenericFlavorNames[generic_flavor_index].name;\n        break;\n      }\n    }\n  }\n\n  if (!flavor_name) {\n    if (options & kUnknownIsNumeric) {\n      return base::StringPrintf(\"%d\", flavor);\n    }\n    return std::string();\n  }\n\n  if (options & kUseShortName) {\n    return ThreadStateFlavorFullToShort(flavor_name);\n  }\n  return std::string(flavor_name);\n}\n\nbool StringToThreadStateFlavor(std::string_view string,\n                               StringToSymbolicConstantOptions options,\n                               thread_state_flavor_t* flavor) {\n  if ((options & kAllowFullName) || (options & kAllowShortName)) {\n    for (thread_state_flavor_t index = 0;\n         index < implicit_cast<thread_state_flavor_t>(std::size(kFlavorNames));\n         ++index) {\n      const char* flavor_name = kFlavorNames[index];\n      if (!flavor_name) {\n        continue;\n      }\n      if ((options & kAllowFullName) && string.compare(flavor_name) == 0) {\n        *flavor = index;\n        return true;\n      }\n      if (options & kAllowShortName) {\n        std::string short_name = ThreadStateFlavorFullToShort(flavor_name);\n        if (string.compare(short_name) == 0) {\n          *flavor = index;\n          return true;\n        }\n      }\n    }\n\n    for (size_t generic_flavor_index = 0;\n         generic_flavor_index < std::size(kGenericFlavorNames);\n         ++generic_flavor_index) {\n      const char* flavor_name = kGenericFlavorNames[generic_flavor_index].name;\n      thread_state_flavor_t flavor_number =\n          kGenericFlavorNames[generic_flavor_index].flavor;\n      if ((options & kAllowFullName) && string.compare(flavor_name) == 0) {\n        *flavor = flavor_number;\n        return true;\n      }\n      if (options & kAllowShortName) {\n        std::string short_name = ThreadStateFlavorFullToShort(flavor_name);\n        if (string.compare(short_name) == 0) {\n          *flavor = flavor_number;\n          return true;\n        }\n      }\n    }\n  }\n\n  if (options & kAllowNumber) {\n    return StringToNumber(std::string(string.data(), string.length()),\n                          reinterpret_cast<unsigned int*>(flavor));\n  }\n\n  return false;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/symbolic_constants_mach.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_\n#define CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_\n\n#include <mach/mach.h>\n\n#include <string>\n#include <string_view>\n\n#include \"util/misc/symbolic_constants_common.h\"\n\nnamespace crashpad {\n\n//! \\brief Converts a Mach exception value to a textual representation.\n//!\n//! \\param[in] exception The Mach exception value to convert.\n//! \\param[in] options Options affecting the conversion. ::kUseOr is ignored.\n//!     For ::kUnknownIsNumeric, the format is `\"%d\"`.\n//!\n//! \\return The converted string.\nstd::string ExceptionToString(exception_type_t exception,\n                              SymbolicConstantToStringOptions options);\n\n//! \\brief Converts a string to its corresponding Mach exception value.\n//!\n//! \\param[in] string The string to convert.\n//! \\param[in] options Options affecting the conversion. ::kAllowOr is ignored.\n//! \\param[out] exception The converted Mach exception value.\n//!\n//! \\return `true` on success, `false` if \\a string could not be converted as\n//!     requested.\nbool StringToException(std::string_view string,\n                       StringToSymbolicConstantOptions options,\n                       exception_type_t* exception);\n\n//! \\brief Converts a Mach exception mask value to a textual representation.\n//!\n//! \\param[in] exception_mask The Mach exception mask value to convert.\n//! \\param[in] options Options affecting the conversion. ::kUseOr is honored.\n//!     For ::kUnknownIsNumeric, the format is `\"%#x\"`.\n//!\n//! \\return The converted string.\nstd::string ExceptionMaskToString(exception_mask_t exception_mask,\n                                  SymbolicConstantToStringOptions options);\n\n//! \\brief Converts a string to its corresponding Mach exception mask value.\n//!\n//! \\param[in] string The string to convert.\n//! \\param[in] options Options affecting the conversion. ::kAllowOr is honored.\n//! \\param[out] exception_mask The converted Mach exception mask value.\n//!\n//! \\return `true` on success, `false` if \\a string could not be converted as\n//!     requested.\nbool StringToExceptionMask(std::string_view string,\n                           StringToSymbolicConstantOptions options,\n                           exception_mask_t* exception_mask);\n\n//! \\brief Converts a Mach exception behavior value to a textual representation.\n//!\n//! \\param[in] behavior The Mach exception behavior value to convert.\n//! \\param[in] options Options affecting the conversion. ::kUseOr is ignored.\n//!     `MACH_EXCEPTION_CODES` can always be ORed in, but no other values can be\n//!     ORed with each other. For ::kUnknownIsNumeric, the format is `\"%#x\"`.\n//!\n//! \\return The converted string.\nstd::string ExceptionBehaviorToString(exception_behavior_t behavior,\n                                      SymbolicConstantToStringOptions options);\n\n//! \\brief Converts a string to its corresponding Mach exception behavior value.\n//!\n//! \\param[in] string The string to convert.\n//! \\param[in] options Options affecting the conversion. ::kAllowOr is ignored.\n//!     `MACH_EXCEPTION_CODES` can always be ORed in, but no other values can be\n//!     ORed with each other.\n//! \\param[out] behavior The converted Mach exception behavior value.\n//!\n//! \\return `true` on success, `false` if \\a string could not be converted as\n//!     requested.\nbool StringToExceptionBehavior(std::string_view string,\n                               StringToSymbolicConstantOptions options,\n                               exception_behavior_t* behavior);\n\n//! \\brief Converts a thread state flavor value to a textual representation.\n//!\n//! \\param[in] flavor The thread state flavor value to convert.\n//! \\param[in] options Options affecting the conversion. ::kUseOr is ignored.\n//!     For ::kUnknownIsNumeric, the format is `\"%d\"`.\n//!\n//! \\return The converted string.\nstd::string ThreadStateFlavorToString(thread_state_flavor_t flavor,\n                                      SymbolicConstantToStringOptions options);\n\n//! \\brief Converts a string to its corresponding thread state flavor value.\n//!\n//! \\param[in] string The string to convert.\n//! \\param[in] options Options affecting the conversion. ::kAllowOr is ignored.\n//! \\param[out] flavor The converted thread state flavor value.\n//!\n//! \\return `true` on success, `false` if \\a string could not be converted as\n//!     requested.\nbool StringToThreadStateFlavor(std::string_view string,\n                               StringToSymbolicConstantOptions options,\n                               thread_state_flavor_t* flavor);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_SYMBOLIC_CONSTANTS_MACH_H_\n"
  },
  {
    "path": "util/mach/symbolic_constants_mach_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/symbolic_constants_mach.h\"\n\n#include <mach/mach.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <string_view>\n\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"util/mach/mach_extensions.h\"\n#include \"util/misc/implicit_cast.h\"\n\n#define NUL_TEST_DATA(string) \\\n  { string, std::size(string) - 1 }\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Options to use for normal tests, those that don’t require kAllowOr.\nconstexpr StringToSymbolicConstantOptions kNormalOptions[] = {\n    0,\n    kAllowFullName,\n    kAllowShortName,\n    kAllowFullName | kAllowShortName,\n    kAllowNumber,\n    kAllowFullName | kAllowNumber,\n    kAllowShortName | kAllowNumber,\n    kAllowFullName | kAllowShortName | kAllowNumber,\n};\n\n// If |expect| is nullptr, the conversion is expected to fail. If |expect| is\n// empty, the conversion is expected to succeed, but the precise returned string\n// value is unknown. Otherwise, the conversion is expected to succeed, and\n// |expect| contains the precise expected string value to be returned. If\n// |expect| contains the substring \"0x1\", the conversion is expected only to\n// succeed when kUnknownIsNumeric is set.\n//\n// Only set kUseFullName or kUseShortName when calling this. Other options are\n// exercised directly by this function.\ntemplate <typename Traits>\nvoid TestSomethingToStringOnce(typename Traits::ValueType value,\n                               const char* expect,\n                               SymbolicConstantToStringOptions options) {\n  std::string actual =\n      Traits::SomethingToString(value, options | kUnknownIsEmpty | kUseOr);\n  std::string actual_numeric =\n      Traits::SomethingToString(value, options | kUnknownIsNumeric | kUseOr);\n  if (expect) {\n    if (expect[0] == '\\0') {\n      EXPECT_FALSE(actual.empty()) << Traits::kValueName << \" \" << value;\n    } else if (strstr(expect, \"0x1\")) {\n      EXPECT_TRUE(actual.empty()) << Traits::kValueName << \" \" << value\n                                  << \", actual \" << actual;\n      actual.assign(expect);\n    } else {\n      EXPECT_EQ(actual, expect) << Traits::kValueName << \" \" << value;\n    }\n    EXPECT_EQ(actual_numeric, actual) << Traits::kValueName << \" \" << value;\n  } else {\n    EXPECT_TRUE(actual.empty()) << Traits::kValueName << \" \" << value\n                                << \", actual \" << actual;\n    EXPECT_FALSE(actual_numeric.empty()) << Traits::kValueName << \" \" << value\n                                         << \", actual_numeric \"\n                                         << actual_numeric;\n  }\n}\n\ntemplate <typename Traits>\nvoid TestSomethingToString(typename Traits::ValueType value,\n                           const char* expect_full,\n                           const char* expect_short) {\n  {\n    SCOPED_TRACE(\"full_name\");\n    TestSomethingToStringOnce<Traits>(value, expect_full, kUseFullName);\n  }\n\n  {\n    SCOPED_TRACE(\"short_name\");\n    TestSomethingToStringOnce<Traits>(value, expect_short, kUseShortName);\n  }\n}\n\ntemplate <typename Traits>\nvoid TestStringToSomething(std::string_view string,\n                           StringToSymbolicConstantOptions options,\n                           bool expect_result,\n                           typename Traits::ValueType expect_value) {\n  typename Traits::ValueType actual_value;\n  bool actual_result =\n      Traits::StringToSomething(string, options, &actual_value);\n  if (expect_result) {\n    EXPECT_TRUE(actual_result) << \"string \" << string << \", options \" << options\n                               << \", \" << Traits::kValueName << \" \"\n                               << expect_value;\n    if (actual_result) {\n      EXPECT_EQ(actual_value, expect_value) << \"string \" << string\n                                            << \", options \" << options;\n    }\n  } else {\n    EXPECT_FALSE(actual_result) << \"string \" << string << \", options \"\n                                << options << \", \" << Traits::kValueName << \" \"\n                                << actual_value;\n  }\n}\n\nconstexpr struct {\n  exception_type_t exception;\n  const char* full_name;\n  const char* short_name;\n} kExceptionTestData[] = {\n    {EXC_BAD_ACCESS, \"EXC_BAD_ACCESS\", \"BAD_ACCESS\"},\n    {EXC_BAD_INSTRUCTION, \"EXC_BAD_INSTRUCTION\", \"BAD_INSTRUCTION\"},\n    {EXC_ARITHMETIC, \"EXC_ARITHMETIC\", \"ARITHMETIC\"},\n    {EXC_EMULATION, \"EXC_EMULATION\", \"EMULATION\"},\n    {EXC_SOFTWARE, \"EXC_SOFTWARE\", \"SOFTWARE\"},\n    {EXC_MACH_SYSCALL, \"EXC_MACH_SYSCALL\", \"MACH_SYSCALL\"},\n    {EXC_RPC_ALERT, \"EXC_RPC_ALERT\", \"RPC_ALERT\"},\n    {EXC_CRASH, \"EXC_CRASH\", \"CRASH\"},\n    {EXC_RESOURCE, \"EXC_RESOURCE\", \"RESOURCE\"},\n    {EXC_GUARD, \"EXC_GUARD\", \"GUARD\"},\n};\n\nstruct ConvertExceptionTraits {\n  using ValueType = exception_type_t;\n  static std::string SomethingToString(\n      ValueType value,\n      SymbolicConstantToStringOptions options) {\n    return ExceptionToString(value, options);\n  }\n  static bool StringToSomething(std::string_view string,\n                                StringToSymbolicConstantOptions options,\n                                ValueType* value) {\n    return StringToException(string, options, value);\n  }\n  static constexpr char kValueName[] = \"exception\";\n};\nconstexpr char ConvertExceptionTraits::kValueName[];\n\nvoid TestExceptionToString(exception_type_t value,\n                           const char* expect_full,\n                           const char* expect_short) {\n  return TestSomethingToString<ConvertExceptionTraits>(\n      value, expect_full, expect_short);\n}\n\nTEST(SymbolicConstantsMach, ExceptionToString) {\n  for (size_t index = 0; index < std::size(kExceptionTestData); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n    TestExceptionToString(kExceptionTestData[index].exception,\n                          kExceptionTestData[index].full_name,\n                          kExceptionTestData[index].short_name);\n  }\n\n  for (exception_type_t exception = 0;\n       exception < EXC_TYPES_COUNT + 8;\n       ++exception) {\n    SCOPED_TRACE(base::StringPrintf(\"exception %d\", exception));\n    if (exception > 0 && exception < EXC_TYPES_COUNT) {\n      TestExceptionToString(exception, \"\", \"\");\n    } else {\n      TestExceptionToString(exception, nullptr, nullptr);\n    }\n  }\n}\n\nvoid TestStringToException(std::string_view string,\n                           StringToSymbolicConstantOptions options,\n                           bool expect_result,\n                           exception_type_t expect_value) {\n  return TestStringToSomething<ConvertExceptionTraits>(\n      string, options, expect_result, expect_value);\n}\n\nTEST(SymbolicConstantsMach, StringToException) {\n  for (size_t option_index = 0; option_index < std::size(kNormalOptions);\n       ++option_index) {\n    SCOPED_TRACE(base::StringPrintf(\"option_index %zu\", option_index));\n    StringToSymbolicConstantOptions options = kNormalOptions[option_index];\n    for (size_t index = 0; index < std::size(kExceptionTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      exception_type_t exception = kExceptionTestData[index].exception;\n      {\n        SCOPED_TRACE(\"full_name\");\n        TestStringToException(kExceptionTestData[index].full_name,\n                              options,\n                              options & kAllowFullName,\n                              exception);\n      }\n      {\n        SCOPED_TRACE(\"short_name\");\n        TestStringToException(kExceptionTestData[index].short_name,\n                              options,\n                              options & kAllowShortName,\n                              exception);\n      }\n      {\n        SCOPED_TRACE(\"number\");\n        std::string number_string = base::StringPrintf(\"%d\", exception);\n        TestStringToException(\n            number_string, options, options & kAllowNumber, exception);\n      }\n    }\n\n    static constexpr const char* kNegativeTestData[] = {\n        \"EXC_CRASH \",\n        \" EXC_BAD_INSTRUCTION\",\n        \"CRASH \",\n        \" BAD_INSTRUCTION\",\n        \"EXC_EXC_BAD_ACCESS\",\n        \"EXC_SOFTWARES\",\n        \"SOFTWARES\",\n        \"EXC_JUNK\",\n        \"random\",\n        \"\",\n    };\n\n    for (size_t index = 0; index < std::size(kNegativeTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      TestStringToException(kNegativeTestData[index], options, false, 0);\n    }\n\n    static constexpr struct {\n      const char* string;\n      size_t length;\n    } kNULTestData[] = {\n        NUL_TEST_DATA(\"\\0EXC_ARITHMETIC\"),\n        NUL_TEST_DATA(\"EXC_\\0ARITHMETIC\"),\n        NUL_TEST_DATA(\"EXC_ARITH\\0METIC\"),\n        NUL_TEST_DATA(\"EXC_ARITHMETIC\\0\"),\n        NUL_TEST_DATA(\"\\0ARITHMETIC\"),\n        NUL_TEST_DATA(\"ARITH\\0METIC\"),\n        NUL_TEST_DATA(\"ARITHMETIC\\0\"),\n        NUL_TEST_DATA(\"\\0003\"),\n        NUL_TEST_DATA(\"3\\0\"),\n        NUL_TEST_DATA(\"1\\0002\"),\n    };\n\n    for (size_t index = 0; index < std::size(kNULTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      std::string_view string(kNULTestData[index].string,\n                              kNULTestData[index].length);\n      TestStringToException(string, options, false, 0);\n    }\n  }\n\n  // Ensure that a NUL is not required at the end of the string.\n  {\n    SCOPED_TRACE(\"trailing_NUL_full\");\n    TestStringToException(std::string_view(\"EXC_BREAKPOINTED\", 14),\n                          kAllowFullName,\n                          true,\n                          EXC_BREAKPOINT);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_short\");\n    TestStringToException(std::string_view(\"BREAKPOINTED\", 10),\n                          kAllowShortName,\n                          true,\n                          EXC_BREAKPOINT);\n  }\n}\n\nconstexpr struct {\n  exception_mask_t exception_mask;\n  const char* full_name;\n  const char* short_name;\n} kExceptionMaskTestData[] = {\n    {EXC_MASK_BAD_ACCESS, \"EXC_MASK_BAD_ACCESS\", \"BAD_ACCESS\"},\n    {EXC_MASK_BAD_INSTRUCTION, \"EXC_MASK_BAD_INSTRUCTION\", \"BAD_INSTRUCTION\"},\n    {EXC_MASK_ARITHMETIC, \"EXC_MASK_ARITHMETIC\", \"ARITHMETIC\"},\n    {EXC_MASK_EMULATION, \"EXC_MASK_EMULATION\", \"EMULATION\"},\n    {EXC_MASK_SOFTWARE, \"EXC_MASK_SOFTWARE\", \"SOFTWARE\"},\n    {EXC_MASK_MACH_SYSCALL, \"EXC_MASK_MACH_SYSCALL\", \"MACH_SYSCALL\"},\n    {EXC_MASK_RPC_ALERT, \"EXC_MASK_RPC_ALERT\", \"RPC_ALERT\"},\n    {EXC_MASK_CRASH, \"EXC_MASK_CRASH\", \"CRASH\"},\n    {EXC_MASK_RESOURCE, \"EXC_MASK_RESOURCE\", \"RESOURCE\"},\n    {EXC_MASK_GUARD, \"EXC_MASK_GUARD\", \"GUARD\"},\n    {0x1, \"0x1\", \"0x1\"},\n    {EXC_MASK_CRASH | 0x1, \"EXC_MASK_CRASH|0x1\", \"CRASH|0x1\"},\n    {EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC |\n         EXC_MASK_EMULATION |\n         EXC_MASK_SOFTWARE |\n         EXC_MASK_BREAKPOINT |\n         EXC_MASK_SYSCALL |\n         EXC_MASK_MACH_SYSCALL |\n         EXC_MASK_RPC_ALERT,\n     \"EXC_MASK_BAD_ACCESS|EXC_MASK_BAD_INSTRUCTION|EXC_MASK_ARITHMETIC|\"\n     \"EXC_MASK_EMULATION|EXC_MASK_SOFTWARE|EXC_MASK_BREAKPOINT|\"\n     \"EXC_MASK_SYSCALL|EXC_MASK_MACH_SYSCALL|EXC_MASK_RPC_ALERT\",\n     \"BAD_ACCESS|BAD_INSTRUCTION|ARITHMETIC|EMULATION|SOFTWARE|BREAKPOINT|\"\n     \"SYSCALL|MACH_SYSCALL|RPC_ALERT\"},\n    {EXC_MASK_RESOURCE | EXC_MASK_GUARD,\n     \"EXC_MASK_RESOURCE|EXC_MASK_GUARD\",\n     \"RESOURCE|GUARD\"},\n};\n\nstruct ConvertExceptionMaskTraits {\n  using ValueType = exception_mask_t;\n  static std::string SomethingToString(\n      ValueType value,\n      SymbolicConstantToStringOptions options) {\n    return ExceptionMaskToString(value, options);\n  }\n  static bool StringToSomething(std::string_view string,\n                                StringToSymbolicConstantOptions options,\n                                ValueType* value) {\n    return StringToExceptionMask(string, options, value);\n  }\n  static constexpr char kValueName[] = \"exception_mask\";\n};\nconstexpr char ConvertExceptionMaskTraits::kValueName[];\n\nvoid TestExceptionMaskToString(exception_mask_t value,\n                               const char* expect_full,\n                               const char* expect_short) {\n  return TestSomethingToString<ConvertExceptionMaskTraits>(\n      value, expect_full, expect_short);\n}\n\nTEST(SymbolicConstantsMach, ExceptionMaskToString) {\n  for (size_t index = 0; index < std::size(kExceptionMaskTestData); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n    TestExceptionMaskToString(kExceptionMaskTestData[index].exception_mask,\n                              kExceptionMaskTestData[index].full_name,\n                              kExceptionMaskTestData[index].short_name);\n  }\n\n  // Test kUseOr handling.\n  EXPECT_TRUE(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,\n                                    kUseFullName).empty());\n  EXPECT_TRUE(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,\n                                    kUseShortName).empty());\n  EXPECT_EQ(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,\n                                  kUseFullName | kUnknownIsNumeric),\n            \"0x1400\");\n  EXPECT_EQ(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,\n                                  kUseShortName | kUnknownIsNumeric),\n            \"0x1400\");\n  EXPECT_EQ(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,\n                                  kUseFullName | kUseOr),\n            \"EXC_MASK_CRASH|EXC_MASK_GUARD\");\n  EXPECT_EQ(ExceptionMaskToString(EXC_MASK_CRASH | EXC_MASK_GUARD,\n                                  kUseShortName | kUseOr),\n            \"CRASH|GUARD\");\n}\n\nvoid TestStringToExceptionMask(std::string_view string,\n                               StringToSymbolicConstantOptions options,\n                               bool expect_result,\n                               exception_mask_t expect_value) {\n  return TestStringToSomething<ConvertExceptionMaskTraits>(\n      string, options, expect_result, expect_value);\n}\n\nTEST(SymbolicConstantsMach, StringToExceptionMask) {\n  // Don’t use kNormalOptions, because kAllowOr needs to be tested.\n  static constexpr StringToSymbolicConstantOptions kOptions[] = {\n      0,\n      kAllowFullName,\n      kAllowShortName,\n      kAllowFullName | kAllowShortName,\n      kAllowNumber,\n      kAllowFullName | kAllowNumber,\n      kAllowShortName | kAllowNumber,\n      kAllowFullName | kAllowShortName | kAllowNumber,\n      kAllowOr,\n      kAllowFullName | kAllowOr,\n      kAllowShortName | kAllowOr,\n      kAllowFullName | kAllowShortName | kAllowOr,\n      kAllowNumber | kAllowOr,\n      kAllowFullName | kAllowNumber | kAllowOr,\n      kAllowShortName | kAllowNumber | kAllowOr,\n      kAllowFullName | kAllowShortName | kAllowNumber | kAllowOr,\n  };\n\n  for (size_t option_index = 0; option_index < std::size(kOptions);\n       ++option_index) {\n    SCOPED_TRACE(base::StringPrintf(\"option_index %zu\", option_index));\n    StringToSymbolicConstantOptions options = kOptions[option_index];\n    for (size_t index = 0; index < std::size(kExceptionMaskTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      exception_mask_t exception_mask =\n          kExceptionMaskTestData[index].exception_mask;\n      {\n        SCOPED_TRACE(\"full_name\");\n        std::string_view full_name(kExceptionMaskTestData[index].full_name);\n        bool has_number = full_name.find(\"0x\", 0) != std::string_view::npos;\n        bool has_or = full_name.find('|', 0) != std::string_view::npos;\n        bool allowed_characteristics =\n            (has_number ? (options & kAllowNumber) : true) &&\n            (has_or ? (options & kAllowOr) : true);\n        bool is_number = full_name.compare(\"0x1\") == 0;\n        bool expect_valid =\n            ((options & kAllowFullName) && allowed_characteristics) ||\n            ((options & kAllowNumber) && is_number);\n        TestStringToExceptionMask(\n            full_name, options, expect_valid, exception_mask);\n      }\n      {\n        SCOPED_TRACE(\"short_name\");\n        std::string_view short_name(kExceptionMaskTestData[index].short_name);\n        bool has_number = short_name.find(\"0x\", 0) != std::string_view::npos;\n        bool has_or = short_name.find('|', 0) != std::string_view::npos;\n        bool allowed_characteristics =\n            (has_number ? (options & kAllowNumber) : true) &&\n            (has_or ? (options & kAllowOr) : true);\n        bool is_number = short_name.compare(\"0x1\") == 0;\n        bool expect_valid =\n            ((options & kAllowShortName) && allowed_characteristics) ||\n            ((options & kAllowNumber) && is_number);\n        TestStringToExceptionMask(\n            short_name, options, expect_valid, exception_mask);\n      }\n    }\n\n    static constexpr const char* kNegativeTestData[] = {\n        \"EXC_MASK_CRASH \",\n        \" EXC_MASK_BAD_INSTRUCTION\",\n        \"EXC_MASK_EXC_MASK_BAD_ACCESS\",\n        \"EXC_MASK_SOFTWARES\",\n        \"EXC_MASK_JUNK\",\n        \"EXC_GUARD\",\n        \"EXC_ARITHMETIC|EXC_FAKE\",\n        \"ARITHMETIC|FAKE\",\n        \"FAKE|ARITHMETIC\",\n        \"EXC_FAKE|EXC_ARITHMETIC\",\n        \"random\",\n        \"\",\n    };\n\n    for (size_t index = 0; index < std::size(kNegativeTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      TestStringToExceptionMask(kNegativeTestData[index], options, false, 0);\n    }\n\n    static constexpr struct {\n      const char* string;\n      size_t length;\n    } kNULTestData[] = {\n        NUL_TEST_DATA(\"\\0EXC_MASK_ARITHMETIC\"),\n        NUL_TEST_DATA(\"EXC_\\0MASK_ARITHMETIC\"),\n        NUL_TEST_DATA(\"EXC_MASK_\\0ARITHMETIC\"),\n        NUL_TEST_DATA(\"EXC_MASK_ARITH\\0METIC\"),\n        NUL_TEST_DATA(\"EXC_MASK_ARITHMETIC\\0\"),\n        NUL_TEST_DATA(\"\\0ARITHMETIC\"),\n        NUL_TEST_DATA(\"ARITH\\0METIC\"),\n        NUL_TEST_DATA(\"ARITHMETIC\\0\"),\n        NUL_TEST_DATA(\"\\0003\"),\n        NUL_TEST_DATA(\"3\\0\"),\n        NUL_TEST_DATA(\"1\\0002\"),\n        NUL_TEST_DATA(\"EXC_MASK_ARITHMETIC\\0|EXC_MASK_EMULATION\"),\n        NUL_TEST_DATA(\"EXC_MASK_ARITHMETIC|\\0EXC_MASK_EMULATION\"),\n        NUL_TEST_DATA(\"ARITHMETIC\\0|EMULATION\"),\n        NUL_TEST_DATA(\"ARITHMETIC|\\0EMULATION\"),\n    };\n\n    for (size_t index = 0; index < std::size(kNULTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      std::string_view string(kNULTestData[index].string,\n                              kNULTestData[index].length);\n      TestStringToExceptionMask(string, options, false, 0);\n    }\n  }\n\n  static const struct {\n    const char* string;\n    StringToSymbolicConstantOptions options;\n    exception_mask_t mask;\n  } kNonCanonicalTestData[] = {\n      {\"EXC_MASK_ALL\", kAllowFullName, ExcMaskAll()},\n      {\"ALL\", kAllowShortName, ExcMaskAll()},\n      {\"EXC_MASK_ALL|EXC_MASK_CRASH\",\n       kAllowFullName | kAllowOr,\n       ExcMaskAll() | EXC_MASK_CRASH},\n      {\"ALL|CRASH\",\n       kAllowShortName | kAllowOr,\n       ExcMaskAll() | EXC_MASK_CRASH},\n      {\"EXC_MASK_BAD_INSTRUCTION|EXC_MASK_BAD_ACCESS\",\n       kAllowFullName | kAllowOr,\n       EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION},\n      {\"EMULATION|ARITHMETIC\",\n       kAllowShortName | kAllowOr,\n       EXC_MASK_ARITHMETIC | EXC_MASK_EMULATION},\n      {\"EXC_MASK_SOFTWARE|BREAKPOINT\",\n       kAllowFullName | kAllowShortName | kAllowOr,\n       EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT},\n      {\"SYSCALL|0x100\",\n       kAllowShortName | kAllowNumber | kAllowOr,\n       EXC_MASK_SYSCALL | 0x100},\n    };\n\n  for (size_t index = 0; index < std::size(kNonCanonicalTestData); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n    TestStringToExceptionMask(kNonCanonicalTestData[index].string,\n                              kNonCanonicalTestData[index].options,\n                              true,\n                              kNonCanonicalTestData[index].mask);\n  }\n\n  // Ensure that a NUL is not required at the end of the string.\n  {\n    SCOPED_TRACE(\"trailing_NUL_full\");\n    TestStringToExceptionMask(std::string_view(\"EXC_MASK_BREAKPOINTED\", 19),\n                              kAllowFullName,\n                              true,\n                              EXC_MASK_BREAKPOINT);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_short\");\n    TestStringToExceptionMask(std::string_view(\"BREAKPOINTED\", 10),\n                              kAllowShortName,\n                              true,\n                              EXC_MASK_BREAKPOINT);\n  }\n}\n\nconstexpr struct {\n  exception_behavior_t behavior;\n  const char* full_name;\n  const char* short_name;\n} kExceptionBehaviorTestData[] = {\n    {EXCEPTION_DEFAULT, \"EXCEPTION_DEFAULT\", \"DEFAULT\"},\n    {EXCEPTION_STATE, \"EXCEPTION_STATE\", \"STATE\"},\n    {EXCEPTION_STATE_IDENTITY, \"EXCEPTION_STATE_IDENTITY\", \"STATE_IDENTITY\"},\n    {implicit_cast<exception_behavior_t>(EXCEPTION_DEFAULT |\n                                         MACH_EXCEPTION_CODES),\n     \"EXCEPTION_DEFAULT|MACH_EXCEPTION_CODES\",\n     \"DEFAULT|MACH\"},\n    {implicit_cast<exception_behavior_t>(EXCEPTION_STATE |\n                                         MACH_EXCEPTION_CODES),\n     \"EXCEPTION_STATE|MACH_EXCEPTION_CODES\",\n     \"STATE|MACH\"},\n    {implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY |\n                                         MACH_EXCEPTION_CODES),\n     \"EXCEPTION_STATE_IDENTITY|MACH_EXCEPTION_CODES\",\n     \"STATE_IDENTITY|MACH\"},\n};\n\nstruct ConvertExceptionBehaviorTraits {\n  using ValueType = exception_behavior_t;\n  static std::string SomethingToString(\n      ValueType value,\n      SymbolicConstantToStringOptions options) {\n    return ExceptionBehaviorToString(value, options);\n  }\n  static bool StringToSomething(std::string_view string,\n                                StringToSymbolicConstantOptions options,\n                                ValueType* value) {\n    return StringToExceptionBehavior(string, options, value);\n  }\n  static constexpr char kValueName[] = \"behavior\";\n};\nconstexpr char ConvertExceptionBehaviorTraits::kValueName[];\n\nvoid TestExceptionBehaviorToString(exception_behavior_t value,\n                                   const char* expect_full,\n                                   const char* expect_short) {\n  return TestSomethingToString<ConvertExceptionBehaviorTraits>(\n      value, expect_full, expect_short);\n}\n\nTEST(SymbolicConstantsMach, ExceptionBehaviorToString) {\n  for (size_t index = 0; index < std::size(kExceptionBehaviorTestData);\n       ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n    TestExceptionBehaviorToString(kExceptionBehaviorTestData[index].behavior,\n                                  kExceptionBehaviorTestData[index].full_name,\n                                  kExceptionBehaviorTestData[index].short_name);\n  }\n\n  for (exception_behavior_t behavior = 0; behavior < 8; ++behavior) {\n    SCOPED_TRACE(base::StringPrintf(\"behavior %d\", behavior));\n    exception_behavior_t behavior_mach = behavior | MACH_EXCEPTION_CODES;\n    if (behavior > 0 && behavior <= EXCEPTION_STATE_IDENTITY) {\n      TestExceptionBehaviorToString(behavior, \"\", \"\");\n      TestExceptionBehaviorToString(behavior_mach, \"\", \"\");\n    } else {\n      TestExceptionBehaviorToString(behavior, nullptr, nullptr);\n      TestExceptionBehaviorToString(behavior_mach, nullptr, nullptr);\n    }\n  }\n}\n\nvoid TestStringToExceptionBehavior(std::string_view string,\n                                   StringToSymbolicConstantOptions options,\n                                   bool expect_result,\n                                   exception_behavior_t expect_value) {\n  return TestStringToSomething<ConvertExceptionBehaviorTraits>(\n      string, options, expect_result, expect_value);\n}\n\nTEST(SymbolicConstantsMach, StringToExceptionBehavior) {\n  for (size_t option_index = 0; option_index < std::size(kNormalOptions);\n       ++option_index) {\n    SCOPED_TRACE(base::StringPrintf(\"option_index %zu\", option_index));\n    StringToSymbolicConstantOptions options = kNormalOptions[option_index];\n    for (size_t index = 0; index < std::size(kExceptionBehaviorTestData);\n         ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      exception_behavior_t behavior =\n          kExceptionBehaviorTestData[index].behavior;\n      {\n        SCOPED_TRACE(\"full_name\");\n        TestStringToExceptionBehavior(\n            kExceptionBehaviorTestData[index].full_name,\n            options,\n            options & kAllowFullName,\n            behavior);\n      }\n      {\n        SCOPED_TRACE(\"short_name\");\n        TestStringToExceptionBehavior(\n            kExceptionBehaviorTestData[index].short_name,\n            options,\n            options & kAllowShortName,\n            behavior);\n      }\n      {\n        SCOPED_TRACE(\"number\");\n        std::string number_string = base::StringPrintf(\"0x%x\", behavior);\n        TestStringToExceptionBehavior(\n            number_string, options, options & kAllowNumber, behavior);\n      }\n    }\n\n    static constexpr const char* kNegativeTestData[] = {\n        \"EXCEPTION_DEFAULT \",\n        \" EXCEPTION_STATE\",\n        \"EXCEPTION_EXCEPTION_STATE_IDENTITY\",\n        \"EXCEPTION_DEFAULTS\",\n        \"EXCEPTION_JUNK\",\n        \"random\",\n        \"MACH_EXCEPTION_CODES\",\n        \"MACH\",\n        \"MACH_EXCEPTION_CODES|MACH_EXCEPTION_CODES\",\n        \"MACH_EXCEPTION_CODES|EXCEPTION_NONEXISTENT\",\n        \"MACH|junk\",\n        \"EXCEPTION_DEFAULT|EXCEPTION_STATE\",\n        \"1|2\",\n        \"\",\n    };\n\n    for (size_t index = 0; index < std::size(kNegativeTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      TestStringToExceptionBehavior(\n          kNegativeTestData[index], options, false, 0);\n    }\n\n    static constexpr struct {\n      const char* string;\n      size_t length;\n    } kNULTestData[] = {\n        NUL_TEST_DATA(\"\\0EXCEPTION_STATE_IDENTITY\"),\n        NUL_TEST_DATA(\"EXCEPTION_\\0STATE_IDENTITY\"),\n        NUL_TEST_DATA(\"EXCEPTION_STATE\\0_IDENTITY\"),\n        NUL_TEST_DATA(\"EXCEPTION_STATE_IDENTITY\\0\"),\n        NUL_TEST_DATA(\"\\0STATE_IDENTITY\"),\n        NUL_TEST_DATA(\"STATE\\0_IDENTITY\"),\n        NUL_TEST_DATA(\"STATE_IDENTITY\\0\"),\n        NUL_TEST_DATA(\"\\0003\"),\n        NUL_TEST_DATA(\"3\\0\"),\n        NUL_TEST_DATA(\"0x8000000\\0001\"),\n        NUL_TEST_DATA(\"EXCEPTION_STATE_IDENTITY\\0|MACH_EXCEPTION_CODES\"),\n        NUL_TEST_DATA(\"EXCEPTION_STATE_IDENTITY|\\0MACH_EXCEPTION_CODES\"),\n        NUL_TEST_DATA(\"STATE_IDENTITY\\0|MACH\"),\n        NUL_TEST_DATA(\"STATE_IDENTITY|\\0MACH\"),\n    };\n\n    for (size_t index = 0; index < std::size(kNULTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      std::string_view string(kNULTestData[index].string,\n                              kNULTestData[index].length);\n      TestStringToExceptionBehavior(string, options, false, 0);\n    }\n  }\n\n  static constexpr struct {\n    const char* string;\n    StringToSymbolicConstantOptions options;\n    exception_behavior_t behavior;\n  } kNonCanonicalTestData[] = {\n      {\"MACH_EXCEPTION_CODES|EXCEPTION_STATE_IDENTITY\",\n       kAllowFullName,\n       implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY |\n                                           MACH_EXCEPTION_CODES)},\n      {\"MACH|STATE_IDENTITY\",\n       kAllowShortName,\n       implicit_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY |\n                                           MACH_EXCEPTION_CODES)},\n      {\"MACH_EXCEPTION_CODES|STATE\",\n       kAllowFullName | kAllowShortName,\n       implicit_cast<exception_behavior_t>(EXCEPTION_STATE |\n                                           MACH_EXCEPTION_CODES)},\n      {\"MACH|EXCEPTION_STATE\",\n       kAllowFullName | kAllowShortName,\n       implicit_cast<exception_behavior_t>(EXCEPTION_STATE |\n                                           MACH_EXCEPTION_CODES)},\n      {\"3|MACH_EXCEPTION_CODES\",\n       kAllowFullName | kAllowNumber,\n       implicit_cast<exception_behavior_t>(MACH_EXCEPTION_CODES | 3)},\n      {\"MACH|0x2\",\n       kAllowShortName | kAllowNumber,\n       implicit_cast<exception_behavior_t>(MACH_EXCEPTION_CODES | 0x2)},\n  };\n\n  for (size_t index = 0; index < std::size(kNonCanonicalTestData); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n    TestStringToExceptionBehavior(kNonCanonicalTestData[index].string,\n                                  kNonCanonicalTestData[index].options,\n                                  true,\n                                  kNonCanonicalTestData[index].behavior);\n  }\n\n  // Ensure that a NUL is not required at the end of the string.\n  {\n    SCOPED_TRACE(\"trailing_NUL_full\");\n    TestStringToExceptionBehavior(std::string_view(\"EXCEPTION_DEFAULTS\", 17),\n                                  kAllowFullName,\n                                  true,\n                                  EXCEPTION_DEFAULT);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_short\");\n    TestStringToExceptionBehavior(std::string_view(\"DEFAULTS\", 7),\n                                  kAllowShortName,\n                                  true,\n                                  EXCEPTION_DEFAULT);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_full_mach\");\n    std::string_view string(\"EXCEPTION_DEFAULT|MACH_EXCEPTION_CODESS\", 38);\n    TestStringToExceptionBehavior(string,\n                                  kAllowFullName | kAllowOr,\n                                  true,\n                                  EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_short_mach\");\n    TestStringToExceptionBehavior(std::string_view(\"DEFAULT|MACH_\", 12),\n                                  kAllowShortName | kAllowOr,\n                                  true,\n                                  EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES);\n  }\n}\n\nconstexpr struct {\n  thread_state_flavor_t flavor;\n  const char* full_name;\n  const char* short_name;\n} kThreadStateFlavorTestData[] = {\n    {THREAD_STATE_NONE, \"THREAD_STATE_NONE\", \"NONE\"},\n    {THREAD_STATE_FLAVOR_LIST, \"THREAD_STATE_FLAVOR_LIST\", \"FLAVOR_LIST\"},\n    {THREAD_STATE_FLAVOR_LIST_NEW,\n     \"THREAD_STATE_FLAVOR_LIST_NEW\",\n     \"FLAVOR_LIST_NEW\"},\n    {THREAD_STATE_FLAVOR_LIST_10_9,\n     \"THREAD_STATE_FLAVOR_LIST_10_9\",\n     \"FLAVOR_LIST_10_9\"},\n#if defined(__i386__) || defined(__x86_64__)\n    {x86_THREAD_STATE32, \"x86_THREAD_STATE32\", \"THREAD32\"},\n    {x86_FLOAT_STATE32, \"x86_FLOAT_STATE32\", \"FLOAT32\"},\n    {x86_EXCEPTION_STATE32, \"x86_EXCEPTION_STATE32\", \"EXCEPTION32\"},\n    {x86_THREAD_STATE64, \"x86_THREAD_STATE64\", \"THREAD64\"},\n    {x86_FLOAT_STATE64, \"x86_FLOAT_STATE64\", \"FLOAT64\"},\n    {x86_EXCEPTION_STATE64, \"x86_EXCEPTION_STATE64\", \"EXCEPTION64\"},\n    {x86_THREAD_STATE, \"x86_THREAD_STATE\", \"THREAD\"},\n    {x86_FLOAT_STATE, \"x86_FLOAT_STATE\", \"FLOAT\"},\n    {x86_EXCEPTION_STATE, \"x86_EXCEPTION_STATE\", \"EXCEPTION\"},\n    {x86_DEBUG_STATE32, \"x86_DEBUG_STATE32\", \"DEBUG32\"},\n    {x86_DEBUG_STATE64, \"x86_DEBUG_STATE64\", \"DEBUG64\"},\n    {x86_DEBUG_STATE, \"x86_DEBUG_STATE\", \"DEBUG\"},\n    {14, \"x86_SAVED_STATE32\", \"SAVED32\"},\n    {15, \"x86_SAVED_STATE64\", \"SAVED64\"},\n    {x86_AVX_STATE32, \"x86_AVX_STATE32\", \"AVX32\"},\n    {x86_AVX_STATE64, \"x86_AVX_STATE64\", \"AVX64\"},\n    {x86_AVX_STATE, \"x86_AVX_STATE\", \"AVX\"},\n#elif defined(__ppc__) || defined(__ppc64__)\n    {PPC_THREAD_STATE, \"PPC_THREAD_STATE\", \"THREAD\"},\n    {PPC_FLOAT_STATE, \"PPC_FLOAT_STATE\", \"FLOAT\"},\n    {PPC_EXCEPTION_STATE, \"PPC_EXCEPTION_STATE\", \"EXCEPTION\"},\n    {PPC_VECTOR_STATE, \"PPC_VECTOR_STATE\", \"VECTOR\"},\n    {PPC_THREAD_STATE64, \"PPC_THREAD_STATE64\", \"THREAD64\"},\n    {PPC_EXCEPTION_STATE64, \"PPC_EXCEPTION_STATE64\", \"EXCEPTION64\"},\n#elif defined(__arm__) || defined(__aarch64__)\n    {ARM_THREAD_STATE, \"ARM_THREAD_STATE\", \"THREAD\"},\n    {ARM_VFP_STATE, \"ARM_VFP_STATE\", \"VFP\"},\n    {ARM_EXCEPTION_STATE, \"ARM_EXCEPTION_STATE\", \"EXCEPTION\"},\n    {ARM_DEBUG_STATE, \"ARM_DEBUG_STATE\", \"DEBUG\"},\n    {ARM_THREAD_STATE64, \"ARM_THREAD_STATE64\", \"THREAD64\"},\n    {ARM_EXCEPTION_STATE64, \"ARM_EXCEPTION_STATE64\", \"EXCEPTION64\"},\n    {ARM_THREAD_STATE32, \"ARM_THREAD_STATE32\", \"THREAD32\"},\n    {ARM_DEBUG_STATE32, \"ARM_DEBUG_STATE32\", \"DEBUG32\"},\n    {ARM_DEBUG_STATE64, \"ARM_DEBUG_STATE64\", \"DEBUG64\"},\n    {ARM_NEON_STATE, \"ARM_NEON_STATE\", \"NEON\"},\n    {ARM_NEON_STATE64, \"ARM_NEON_STATE64\", \"NEON64\"},\n#endif\n};\n\nstruct ConvertThreadStateFlavorTraits {\n  using ValueType = thread_state_flavor_t;\n  static std::string SomethingToString(\n      ValueType value,\n      SymbolicConstantToStringOptions options) {\n    return ThreadStateFlavorToString(value, options);\n  }\n  static bool StringToSomething(std::string_view string,\n                                StringToSymbolicConstantOptions options,\n                                ValueType* value) {\n    return StringToThreadStateFlavor(string, options, value);\n  }\n  static constexpr char kValueName[] = \"flavor\";\n};\nconstexpr char ConvertThreadStateFlavorTraits::kValueName[];\n\nvoid TestThreadStateFlavorToString(exception_type_t value,\n                                   const char* expect_full,\n                                   const char* expect_short) {\n  return TestSomethingToString<ConvertThreadStateFlavorTraits>(\n      value, expect_full, expect_short);\n}\n\nTEST(SymbolicConstantsMach, ThreadStateFlavorToString) {\n  for (size_t index = 0; index < std::size(kThreadStateFlavorTestData);\n       ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n    TestThreadStateFlavorToString(kThreadStateFlavorTestData[index].flavor,\n                                  kThreadStateFlavorTestData[index].full_name,\n                                  kThreadStateFlavorTestData[index].short_name);\n  }\n\n  for (thread_state_flavor_t flavor = 0; flavor < 136; ++flavor) {\n    SCOPED_TRACE(base::StringPrintf(\"flavor %d\", flavor));\n\n    // Flavor numbers appear to be assigned somewhat haphazardly, especially on\n    // certain architectures. The conditional should match flavors that\n    // ThreadStateFlavorToString() knows how to convert.\n    if (\n#if defined(__i386__) || defined(__x86_64__)\n        flavor <= x86_AVX_STATE\n#elif defined(__ppc__) || defined(__ppc64__)\n        flavor <= THREAD_STATE_NONE\n#elif defined(__arm__) || defined(__aarch64__)\n        (flavor <= ARM_EXCEPTION_STATE64 || flavor == ARM_THREAD_STATE32 ||\n         (flavor >= ARM_DEBUG_STATE32 && flavor <= ARM_NEON_STATE64))\n#endif\n        ||\n        flavor == THREAD_STATE_FLAVOR_LIST_NEW ||\n        flavor == THREAD_STATE_FLAVOR_LIST_10_9) {\n      TestThreadStateFlavorToString(flavor, \"\", \"\");\n    } else {\n      TestThreadStateFlavorToString(flavor, nullptr, nullptr);\n    }\n  }\n}\n\nvoid TestStringToThreadStateFlavor(std::string_view string,\n                                   StringToSymbolicConstantOptions options,\n                                   bool expect_result,\n                                   thread_state_flavor_t expect_value) {\n  return TestStringToSomething<ConvertThreadStateFlavorTraits>(\n      string, options, expect_result, expect_value);\n}\n\nTEST(SymbolicConstantsMach, StringToThreadStateFlavor) {\n  for (size_t option_index = 0; option_index < std::size(kNormalOptions);\n       ++option_index) {\n    SCOPED_TRACE(base::StringPrintf(\"option_index %zu\", option_index));\n    StringToSymbolicConstantOptions options = kNormalOptions[option_index];\n    for (size_t index = 0; index < std::size(kThreadStateFlavorTestData);\n         ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      thread_state_flavor_t flavor = kThreadStateFlavorTestData[index].flavor;\n      {\n        SCOPED_TRACE(\"full_name\");\n        TestStringToThreadStateFlavor(\n            kThreadStateFlavorTestData[index].full_name,\n            options,\n            options & kAllowFullName,\n            flavor);\n      }\n      {\n        SCOPED_TRACE(\"short_name\");\n        TestStringToThreadStateFlavor(\n            kThreadStateFlavorTestData[index].short_name,\n            options,\n            options & kAllowShortName,\n            flavor);\n      }\n      {\n        SCOPED_TRACE(\"number\");\n        std::string number_string = base::StringPrintf(\"%d\", flavor);\n        TestStringToThreadStateFlavor(\n            number_string, options, options & kAllowNumber, flavor);\n      }\n    }\n\n    static constexpr const char* kNegativeTestData[] = {\n      \"THREAD_STATE_NONE \",\n      \" THREAD_STATE_NONE\",\n      \"NONE \",\n      \" NONE\",\n      \"THREAD_STATE_THREAD_STATE_NONE\",\n      \"THREAD_STATE_NONE_AT_ALL\",\n      \"NONE_AT_ALL\",\n      \"THREAD_STATE_JUNK\",\n      \"JUNK\",\n      \"random\",\n      \" THREAD64\",\n      \"THREAD64 \",\n      \"THREAD642\",\n      \"\",\n#if defined(__i386__) || defined(__x86_64__)\n      \" x86_THREAD_STATE64\",\n      \"x86_THREAD_STATE64 \",\n      \"x86_THREAD_STATE642\",\n      \"x86_JUNK\",\n      \"x86_JUNK_STATE32\",\n      \"PPC_THREAD_STATE\",\n      \"ARM_THREAD_STATE\",\n#elif defined(__ppc__) || defined(__ppc64__)\n      \" PPC_THREAD_STATE64\",\n      \"PPC_THREAD_STATE64 \",\n      \"PPC_THREAD_STATE642\",\n      \"PPC_JUNK\",\n      \"PPC_JUNK_STATE32\",\n      \"x86_THREAD_STATE\",\n      \"ARM_THREAD_STATE\",\n#elif defined(__arm__) || defined(__aarch64__)\n      \" ARM_THREAD_STATE64\",\n      \"ARM_THREAD_STATE64 \",\n      \"ARM_THREAD_STATE642\",\n      \"ARM_JUNK\",\n      \"ARM_JUNK_STATE32\",\n      \"x86_THREAD_STATE\",\n      \"PPC_THREAD_STATE\",\n#endif\n    };\n\n    for (size_t index = 0; index < std::size(kNegativeTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      TestStringToThreadStateFlavor(\n          kNegativeTestData[index], options, false, 0);\n    }\n\n    static constexpr struct {\n      const char* string;\n      size_t length;\n    } kNULTestData[] = {\n        NUL_TEST_DATA(\"\\0THREAD_STATE_NONE\"),\n        NUL_TEST_DATA(\"THREAD_\\0STATE_NONE\"),\n        NUL_TEST_DATA(\"THREAD_STATE_\\0NONE\"),\n        NUL_TEST_DATA(\"THREAD_STATE_NO\\0NE\"),\n        NUL_TEST_DATA(\"THREAD_STATE_NONE\\0\"),\n        NUL_TEST_DATA(\"\\0NONE\"),\n        NUL_TEST_DATA(\"NO\\0NE\"),\n        NUL_TEST_DATA(\"NONE\\0\"),\n        NUL_TEST_DATA(\"\\0THREAD_STATE_FLAVOR_LIST_NEW\"),\n        NUL_TEST_DATA(\"THREAD_STATE_\\0FLAVOR_LIST_NEW\"),\n        NUL_TEST_DATA(\"THREAD_STATE_FLAVOR_LIST\\0_NEW\"),\n        NUL_TEST_DATA(\"THREAD_STATE_FLAVOR_LIST_NEW\\0\"),\n        NUL_TEST_DATA(\"\\0FLAVOR_LIST_NEW\"),\n        NUL_TEST_DATA(\"FLAVOR_LIST\\0_NEW\"),\n        NUL_TEST_DATA(\"FLAVOR_LIST_NEW\\0\"),\n        NUL_TEST_DATA(\"\\0THREAD\"),\n        NUL_TEST_DATA(\"THR\\0EAD\"),\n        NUL_TEST_DATA(\"THREAD\\0\"),\n        NUL_TEST_DATA(\"\\0THREAD64\"),\n        NUL_TEST_DATA(\"THR\\0EAD64\"),\n        NUL_TEST_DATA(\"THREAD\\064\"),\n        NUL_TEST_DATA(\"THREAD64\\0\"),\n        NUL_TEST_DATA(\"\\0002\"),\n        NUL_TEST_DATA(\"2\\0\"),\n        NUL_TEST_DATA(\"1\\0002\"),\n#if defined(__i386__) || defined(__x86_64__)\n        NUL_TEST_DATA(\"\\0x86_THREAD_STATE64\"),\n        NUL_TEST_DATA(\"x86\\0_THREAD_STATE64\"),\n        NUL_TEST_DATA(\"x86_\\0THREAD_STATE64\"),\n        NUL_TEST_DATA(\"x86_THR\\0EAD_STATE64\"),\n        NUL_TEST_DATA(\"x86_THREAD\\0_STATE64\"),\n        NUL_TEST_DATA(\"x86_THREAD_\\0STATE64\"),\n        NUL_TEST_DATA(\"x86_THREAD_STA\\0TE64\"),\n        NUL_TEST_DATA(\"x86_THREAD_STATE\\00064\"),\n        NUL_TEST_DATA(\"x86_THREAD_STATE64\\0\"),\n#elif defined(__ppc__) || defined(__ppc64__)\n        NUL_TEST_DATA(\"\\0PPC_THREAD_STATE64\"),\n        NUL_TEST_DATA(\"PPC\\0_THREAD_STATE64\"),\n        NUL_TEST_DATA(\"PPC_\\0THREAD_STATE64\"),\n        NUL_TEST_DATA(\"PPC_THR\\0EAD_STATE64\"),\n        NUL_TEST_DATA(\"PPC_THREAD\\0_STATE64\"),\n        NUL_TEST_DATA(\"PPC_THREAD_\\0STATE64\"),\n        NUL_TEST_DATA(\"PPC_THREAD_STA\\0TE64\"),\n        NUL_TEST_DATA(\"PPC_THREAD_STATE\\00064\"),\n#elif defined(__arm__) || defined(__aarch64__)\n        NUL_TEST_DATA(\"\\0ARM_THREAD_STATE64\"),\n        NUL_TEST_DATA(\"ARM\\0_THREAD_STATE64\"),\n        NUL_TEST_DATA(\"ARM_\\0THREAD_STATE64\"),\n        NUL_TEST_DATA(\"ARM_THR\\0EAD_STATE64\"),\n        NUL_TEST_DATA(\"ARM_THREAD\\0_STATE64\"),\n        NUL_TEST_DATA(\"ARM_THREAD_\\0STATE64\"),\n        NUL_TEST_DATA(\"ARM_THREAD_STA\\0TE64\"),\n        NUL_TEST_DATA(\"ARM_THREAD_STATE\\00064\"),\n#endif\n    };\n\n    for (size_t index = 0; index < std::size(kNULTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      std::string_view string(kNULTestData[index].string,\n                              kNULTestData[index].length);\n      TestStringToThreadStateFlavor(string, options, false, 0);\n    }\n  }\n\n  // Ensure that a NUL is not required at the end of the string.\n  {\n    SCOPED_TRACE(\"trailing_NUL_full\");\n    TestStringToThreadStateFlavor(std::string_view(\"THREAD_STATE_NONER\", 17),\n                                  kAllowFullName,\n                                  true,\n                                  THREAD_STATE_NONE);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_short\");\n    TestStringToThreadStateFlavor(\n        std::string_view(\"NONER\", 4), kAllowShortName, true, THREAD_STATE_NONE);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_full_new\");\n    std::string_view string(\"THREAD_STATE_FLAVOR_LIST_NEWS\", 28);\n    TestStringToThreadStateFlavor(\n        string, kAllowFullName, true, THREAD_STATE_FLAVOR_LIST_NEW);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_short_new\");\n    TestStringToThreadStateFlavor(std::string_view(\"FLAVOR_LIST_NEWS\", 15),\n                                  kAllowShortName,\n                                  true,\n                                  THREAD_STATE_FLAVOR_LIST_NEW);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/task_for_pid.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/mach/task_for_pid.h\"\n\n#include <sys/sysctl.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <iterator>\n#include <set>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/apple/scoped_mach_port.h\"\n#include \"util/posix/process_info.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n//! \\brief Determines whether the groups that \\a process_reader belongs to are\n//!     a subset of the groups that the current process belongs to.\n//!\n//! This function is similar to 10.9.5\n//! `xnu-2422.115.4/bsd/kern/kern_credential.c` `kauth_cred_gid_subset()`.\nbool TaskForPIDGroupCheck(const ProcessInfo& process_info) {\n  std::set<gid_t> groups = process_info.AllGroups();\n\n  ProcessInfo process_info_self;\n  if (!process_info_self.InitializeWithPid(getpid())) {\n    return false;\n  }\n\n  std::set<gid_t> groups_self = process_info_self.AllGroups();\n\n  // difference will only contain elements of groups not present in groups_self.\n  // It will not contain elements of groups_self not present in groups. (That\n  // would be std::set_symmetric_difference.)\n  std::set<gid_t> difference;\n  std::set_difference(groups.begin(),\n                      groups.end(),\n                      groups_self.begin(),\n                      groups_self.end(),\n                      std::inserter(difference, difference.begin()));\n  if (!difference.empty()) {\n    LOG(ERROR) << \"permission denied (gid)\";\n    return false;\n  }\n\n  return true;\n}\n\n//! \\brief Determines whether the current process should have permission to\n//!     access the specified task port.\n//!\n//! This function is similar to 10.9.5\n//! `xnu-2422.115.4/bsd/vm/vm_unix.c` `task_for_pid_posix_check()`.\n//!\n//! This function accepts a `task_t` argument instead of a `pid_t` argument,\n//! implying that the task send right must be retrieved before it can be\n//! checked. This is done because a `pid_t` argument may refer to a different\n//! task in between the time that access is checked and its corresponding\n//! `task_t` is obtained by `task_for_pid()`. When `task_for_pid()` is called\n//! first, any operations requiring the process ID will call `pid_for_task()`\n//! and be guaranteed to use the process ID corresponding to the correct task,\n//! or to fail if that task is no longer running. If the task dies and the PID\n//! is recycled, it is still possible to look up the wrong PID, but falsely\n//! granting task access based on the new process’ characteristics is harmless\n//! because the task will be a dead name at that point.\nbool TaskForPIDCheck(task_t task) {\n  // If the effective user ID is not 0, then this code is not running as root at\n  // all, and the kernel’s own checks are sufficient to determine access. The\n  // point of this function is to simulate the kernel’s own checks when the\n  // effective user ID is 0 but the real user ID is anything else.\n  if (geteuid() != 0) {\n    return true;\n  }\n\n  // If the real user ID is 0, then this code is not running setuid root, it’s\n  // genuinely running as root, and it should be allowed maximum access.\n  uid_t uid = getuid();\n  if (uid == 0) {\n    return true;\n  }\n\n  // task_for_pid_posix_check() would permit access to the running process’ own\n  // task here, and would then check the kern.tfp.policy sysctl. If set to\n  // KERN_TFP_POLICY_DENY, it would deny access.\n  //\n  // This behavior is not duplicated here because the point of this function is\n  // to permit task_for_pid() access for setuid root programs. It is assumed\n  // that a setuid root program ought to be able to overcome any policy set in\n  // kern.tfp.policy.\n  //\n  // Access to the running process’ own task is not granted outright and is\n  // instead subjected to the same user/group ID checks as any other process.\n  // This has the effect of denying access to the running process’ own task when\n  // it is setuid root. This is intentional, because it prevents the same sort\n  // of cross-privilege disclosure discussed below at the DidChangePriveleges()\n  // check. The running process can still access its own task port via\n  // mach_task_self(), but a non-root user cannot coerce a setuid root tool to\n  // operate on itself by specifying its own process ID to this TaskForPID()\n  // interface.\n\n  ProcessInfo process_info;\n  if (!process_info.InitializeWithTask(task)) {\n    return false;\n  }\n\n  // The target process’ real user ID, effective user ID, and saved set-user ID\n  // must match this process’ own real user ID. task_for_pid_posix_check()\n  // checks against the current process’ effective user ID, but for the purposes\n  // of this function, when running setuid root, the real user ID is the correct\n  // choice.\n  if (process_info.RealUserID() != uid ||\n      process_info.EffectiveUserID() != uid ||\n      process_info.SavedUserID() != uid) {\n    LOG(ERROR) << \"permission denied (uid)\";\n    return false;\n  }\n\n  // The target process must not have changed privileges. The rationale for this\n  // check is explained in 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c\n  // issetugid(): processes that have changed privileges may have loaded data\n  // using different credentials than they are currently operating with, and\n  // allowing other processes access to this data based solely on a check of the\n  // current credentials could violate confidentiality.\n  if (process_info.DidChangePrivileges()) {\n    LOG(ERROR) << \"permission denied (P_SUGID)\";\n    return false;\n  }\n\n  return TaskForPIDGroupCheck(process_info);\n}\n\n}  // namespace\n\ntask_t TaskForPID(pid_t pid) {\n  task_t task;\n  kern_return_t kr = task_for_pid(mach_task_self(), pid, &task);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << \"task_for_pid\";\n    return TASK_NULL;\n  }\n\n  base::apple::ScopedMachSendRight task_owner(task);\n\n  if (!TaskForPIDCheck(task)) {\n    return TASK_NULL;\n  }\n\n  return task_owner.release();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/mach/task_for_pid.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_\n#define CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_\n\n#include <mach/mach.h>\n#include <sys/types.h>\n\nnamespace crashpad {\n\n//! \\brief Wraps `task_for_pid()`.\n//!\n//! This function exists to support `task_for_pid()` access checks in a setuid\n//! environment. Normally, `task_for_pid()` can only return an arbitrary task’s\n//! port when running as root or when taskgated(8) approves. When not running as\n//! root, a series of access checks are perfomed to ensure that the running\n//! process has permission to obtain the other process’ task port.\n//!\n//! It is possible to make an executable setuid root to give it broader\n//! `task_for_pid()` access by bypassing taskgated(8) checks, but this also has\n//! the effect of bypassing the access checks, allowing any process’ task port\n//! to be obtained. In most situations, these access checks are desirable to\n//! prevent security and privacy breaches.\n//!\n//! When running as setuid root, this function wraps `task_for_pid()`,\n//! reimplementing those access checks. A process whose effective user ID is 0\n//! and whose real user ID is nonzero is understood to be running setuid root.\n//! In this case, the requested task’s real, effective, and saved set-user IDs\n//! must all equal the running process’ real user ID, the requested task must\n//! not have changed privileges, and the requested task’s set of all group IDs\n//! (including its real, effective, and saved set-group IDs and supplementary\n//! group list) must be a subset of the running process’ set of all group IDs.\n//! These access checks mimic those that the kernel performs.\n//!\n//! When not running as setuid root, `task_for_pid()` is called directly,\n//! without imposing any additional checks beyond what the kernel does.\n//!\n//! \\param[in] pid The process ID of the task whose task port is desired.\n//!\n//! \\return A send right to the task port if it could be obtained, or\n//!     `TASK_NULL` otherwise, with an error message logged. If a send right is\n//!     returned, the caller takes ownership of it.\ntask_t TaskForPID(pid_t pid);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MACH_TASK_FOR_PID_H_\n"
  },
  {
    "path": "util/misc/address_sanitizer.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_ADDRESS_SANITIZER_H_\n#define CRASHPAD_UTIL_MISC_ADDRESS_SANITIZER_H_\n\n#include \"base/compiler_specific.h\"\n#include \"build/build_config.h\"\n\n#if !defined(ADDRESS_SANITIZER)\n#if HAS_FEATURE(address_sanitizer) || \\\n    (defined(COMPILER_GCC) && defined(__SANITIZE_ADDRESS__))\n#define ADDRESS_SANITIZER 1\n#endif\n#endif  // !defined(ADDRESS_SANITIZER)\n\n#endif  // CRASHPAD_UTIL_MISC_ADDRESS_SANITIZER_H_\n"
  },
  {
    "path": "util/misc/address_types.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_ADDRESS_TYPES_H_\n#define CRASHPAD_UTIL_MISC_ADDRESS_TYPES_H_\n\n#include <stdint.h>\n\n#include <type_traits>\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <mach/mach_types.h>\n#elif BUILDFLAG(IS_WIN)\n#include \"util/win/address_types.h\"\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include \"util/linux/address_types.h\"\n#elif BUILDFLAG(IS_FUCHSIA)\n#include <zircon/types.h>\n#else\n#error \"Unhandled OS type\"\n#endif\n\nnamespace crashpad {\n\n#if DOXYGEN\n\n//! \\brief Type used to represent an address in a process, potentially across\n//!     bitness.\nusing VMAddress = uint64_t;\n\n//! \\brief Type used to represent the size of a memory range (with a\n//!     VMAddress), potentially across bitness.\nusing VMSize = uint64_t;\n\n#elif BUILDFLAG(IS_APPLE)\n\nusing VMAddress = mach_vm_address_t;\nusing VMSize = mach_vm_size_t;\n\n#elif BUILDFLAG(IS_WIN)\n\nusing VMAddress = WinVMAddress;\nusing VMSize = WinVMSize;\n\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n\nusing VMAddress = LinuxVMAddress;\nusing VMSize = LinuxVMSize;\n\n#elif BUILDFLAG(IS_FUCHSIA)\n\nusing VMAddress = zx_vaddr_t;\nusing VMSize = size_t;\n\n#endif\n\n//! \\brief Type used to represent an offset from a VMAddress, potentially\n//!     across bitness.\nusing VMOffset = std::make_signed<VMSize>::type;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_ADDRESS_TYPES_H_\n"
  },
  {
    "path": "util/misc/arm64_pac_bti.S",
    "content": "// Copyright 2021 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_ARM64_PAC_BTI_S\n#define CRASHPAD_UTIL_MISC_ARM64_PAC_BTI_S\n\n/* Support macros for the Armv8.5-A Branch Target Identification and\n * Armv8.3-A Pointer Authentication features which require emitting\n * a .note.gnu.property section with the appropriate\n * architecture-dependent feature bits set.\n * Read more: \"ELF for the Arm® 64-bit Architecture\"\n */\n#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT == 1)\n#define GNU_PROPERTY_AARCH64_BTI 1  // Has BTI\n#define CRASHPAD_AARCH64_VALID_JUMP_CALL_TARGET bti jc\n#define CRASHPAD_AARCH64_VALID_CALL_TARGET      bti c\n#define CRASHPAD_AARCH64_VALID_JUMP_TARGET      bti j\n#else\n#define GNU_PROPERTY_AARCH64_BTI 0  // No BTI\n#define CRASHPAD_AARCH64_VALID_JUMP_CALL_TARGET\n#define CRASHPAD_AARCH64_VALID_CALL_TARGET\n#define CRASHPAD_AARCH64_VALID_JUMP_TARGET\n#endif\n\n#if defined(__ARM_FEATURE_PAC_DEFAULT)\n#if ((__ARM_FEATURE_PAC_DEFAULT & ((1<<0)|(1<<1))) == 0)\n#error Pointer authentication defines no valid key!\n#endif\n#define GNU_PROPERTY_AARCH64_PAC 1 // Has PAC\n#else\n#define GNU_PROPERTY_AARCH64_PAC 0 // No PAC\n#endif\n\n/**\n * Emit a proper .note.gnu.property section in case of PAC or BTI being enabled.\n */\n#if (GNU_PROPERTY_AARCH64_BTI != 0 || GNU_PROPERTY_AARCH64_PAC != 0)\n  .pushsection .note.gnu.property, \"a\"\n  .balign 4\n  .long 0x4  /* size of field \"GNU\" */\n  .long 0x10 /* note descriptor size */\n  .long 0x5  /* type of note descriptor: NT_GNU_PROPERTY_TYPE_0 */\n  .asciz \"GNU\"\n  .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */\n  .long 0x4\n  .long ((GNU_PROPERTY_AARCH64_BTI<<0)|(GNU_PROPERTY_AARCH64_PAC<<1))\n  .long 0x0\n  .popsection\n#endif\n\n#undef GNU_PROPERTY_AARCH64_BTI\n#undef GNU_PROPERTY_AARCH64_PAC\n\n#endif /* CRASHPAD_UTIL_MISC_ARM64_PAC_BTI_S */\n"
  },
  {
    "path": "util/misc/arraysize.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_ARRAYSIZE_H_\n#define CRASHPAD_UTIL_MISC_ARRAYSIZE_H_\n\n#include <sys/types.h>  // For size_t.\n\n#include <type_traits>\n\n//! \\file\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief A helper to implement ArraySize.\ntemplate <typename ArrayType>\nconstexpr size_t ArraySizeHelper() noexcept {\n  return std::extent<typename std::remove_reference<ArrayType>::type>::value;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n\n//! \\brief A way of computing an array’s size.\n//!\n//! Use this only where `std::size()` won’t work, such as in constant\n//! expressions (including `static_assert` expressions) that consider the\n//! sizes of non-static data members.\n#define ArraySize(array) crashpad::internal::ArraySizeHelper<decltype(array)>()\n\n#endif  // CRASHPAD_UTIL_MISC_ARRAYSIZE_H_\n"
  },
  {
    "path": "util/misc/arraysize_test.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/arraysize.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ArraySize, ArraySize) {\n  [[maybe_unused]] char c1[1];\n  static_assert(ArraySize(c1) == 1, \"c1\");\n\n  [[maybe_unused]] char c2[2];\n  static_assert(ArraySize(c2) == 2, \"c2\");\n\n  [[maybe_unused]] char c4[4];\n  static_assert(ArraySize(c4) == 4, \"c4\");\n\n  [[maybe_unused]] int i1[1];\n  static_assert(ArraySize(i1) == 1, \"i1\");\n\n  [[maybe_unused]] int i2[2];\n  static_assert(ArraySize(i2) == 2, \"i2\");\n\n  [[maybe_unused]] int i4[4];\n  static_assert(ArraySize(i4) == 4, \"i4\");\n\n  [[maybe_unused]] long l8[8];\n  static_assert(ArraySize(l8) == 8, \"l8\");\n\n  [[maybe_unused]] int l9[9];\n  static_assert(ArraySize(l9) == 9, \"l9\");\n\n  struct S {\n    char c;\n    int i;\n    long l;\n    bool b;\n  };\n\n  [[maybe_unused]] S s1[1];\n  static_assert(ArraySize(s1) == 1, \"s1\");\n\n  [[maybe_unused]] S s10[10];\n  static_assert(ArraySize(s10) == 10, \"s10\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/as_underlying_type.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_AS_UNDERLYING_TYPE_H_\n#define CRASHPAD_UTIL_MISC_AS_UNDERLYING_TYPE_H_\n\n#include <type_traits>\n\nnamespace crashpad {\n\n//! \\brief Casts a value to its underlying type.\n//!\n//! \\param[in] from The value to be casted.\n//! \\return \\a from casted to its underlying type.\ntemplate <typename From>\nconstexpr typename std::underlying_type<From>::type AsUnderlyingType(\n    From from) {\n  return static_cast<typename std::underlying_type<From>::type>(from);\n}\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_AS_UNDERLYING_TYPE_H_\n"
  },
  {
    "path": "util/misc/capture_context.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_CAPTURE_CONTEXT_H_\n#define CRASHPAD_UTIL_MISC_CAPTURE_CONTEXT_H_\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <mach/mach.h>\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include <ucontext.h>\n#endif  // BUILDFLAG(IS_APPLE)\n\nnamespace crashpad {\n\n#if BUILDFLAG(IS_APPLE)\n#if defined(ARCH_CPU_X86_FAMILY)\nusing NativeCPUContext = x86_thread_state;\n#elif defined(ARCH_CPU_ARM64)\nusing NativeCPUContext = arm_unified_thread_state;\n#endif\n#elif BUILDFLAG(IS_WIN)\nusing NativeCPUContext = CONTEXT;\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\nusing NativeCPUContext = ucontext_t;\n#endif  // BUILDFLAG(IS_APPLE)\n\n//! \\brief Saves the CPU context.\n//!\n//! The CPU context will be captured as accurately and completely as possible,\n//! containing an atomic snapshot at the point of this function’s return. This\n//! function does not modify any registers.\n//!\n//! This function is a replacement for `RtlCaptureContext()` and `getcontext()`\n//! which contain bugs and/or limitations.\n//!\n//! On 32-bit x86, `RtlCaptureContext()` requires that `ebp` be used as a frame\n//! pointer, and returns `ebp`, `esp`, and `eip` out of sync with the other\n//! registers. Both the 32-bit x86 and 64-bit x86_64 versions of\n//! `RtlCaptureContext()` capture only the state of the integer registers,\n//! ignoring floating-point and vector state.\n//!\n//! CaptureContext isn't used on Fuchsia, nor does a concept of `ucontext_t`\n//! exist on Fuchsia.\n//!\n//! \\param[out] cpu_context The structure to store the context in.\n//!\n//! \\note The ABI may require that this function's argument is passed by\n//!     register, preventing this fuction from saving the original value of that\n//!     register. This occurs in the following circumstances:\n//!\n//!     OS                  | Architecture | Register\n//!     --------------------|--------------|---------\n//!     Win                 | x86_64       | `%%rcx`\n//!     macOS/Linux         | x86_64       | `%%rdi`\n//!     Linux               | ARM/ARM64    | `r0`/`x0`\n//!     Linux               | MIPS/MIPS64  | `$a0`\n//!     Linux               | RISCV64      | `a0`\n//!\n//!     Additionally, the value `LR` on ARM/ARM64 will be the return address of\n//!     this function.\n//!\n//!     If the value of these register prior to calling this function are needed\n//!     they must be obtained separately prior to calling this function. For\n//!     example:\n//!     \\code\n//!       uint64_t rdi;\n//!       asm(\"movq %%rdi, %0\" : \"=m\"(rdi));\n//!     \\endcode\nvoid CaptureContext(NativeCPUContext* cpu_context);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_CAPTURE_CONTEXT_H_\n"
  },
  {
    "path": "util/misc/capture_context_linux.S",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/arm64_pac_bti.S\"\n\n// namespace crashpad {\n// void CaptureContext(ucontext_t* context);\n// }  // namespace crashpad\n\n// The type name for a ucontext_t varies by libc implementation and version.\n// Bionic and glibc 2.25 typedef ucontext_t from struct ucontext. glibc 2.26+\n// typedef ucontext_t from struct ucontext_t. Alias the symbol names to maintain\n// compatibility with both possibilities.\n#define CAPTURECONTEXT_SYMBOL _ZN8crashpad14CaptureContextEP10ucontext_t\n#define CAPTURECONTEXT_SYMBOL2 _ZN8crashpad14CaptureContextEP8ucontext\n\n  .text\n  .globl CAPTURECONTEXT_SYMBOL\n  .globl CAPTURECONTEXT_SYMBOL2\n#if defined(__i386__) || defined(__x86_64__)\n  .balign 16, 0x90\n#elif defined(__arm__) || defined(__aarch64__)\n  .balign 4, 0x0\n  .type CAPTURECONTEXT_SYMBOL, %function\n  .type CAPTURECONTEXT_SYMBOL2, %function\n#elif defined(__mips__)\n  .balign 4, 0x0\n#elif defined(__riscv)\n  .balign 4, 0x0\n#endif\n\nCAPTURECONTEXT_SYMBOL:\nCAPTURECONTEXT_SYMBOL2:\n  CRASHPAD_AARCH64_VALID_CALL_TARGET\n\n#if defined(__i386__)\n\n  .cfi_startproc\n\n  pushl %ebp\n  .cfi_def_cfa_offset 8\n  .cfi_offset %ebp, -8\n  movl %esp, %ebp\n  .cfi_def_cfa_register %ebp\n\n  // Note that 16-byte stack alignment is not maintained because this function\n  // does not call out to any other.\n\n  // pushfl first, because some instructions (but probably none used here)\n  // affect %eflags. %eflags will be in -4(%ebp).\n  pushfl\n\n  // Save the original value of %eax, and use %eax to hold the ucontext_t*\n  // argument. The original value of %eax will be in -8(%ebp).\n  pushl %eax\n  movl 8(%ebp), %eax\n\n  // Save the original value of %ecx, and use %ecx as a scratch register.\n  pushl %ecx\n\n  // The segment registers are 16 bits wide, but mcontext_t declares them\n  // as unsigned 32-bit values, so zero the top half.\n  xorl %ecx, %ecx\n  movw %gs, %cx\n  movl %ecx, 0x14(%eax)  // context->uc_mcontext.xgs\n  movw %fs, %cx\n  movl %ecx, 0x18(%eax)  // context->uc_mcontext.xfs\n  movw %es, %cx\n  movl %ecx, 0x1c(%eax)  // context->uc_mcontext.xes\n  movw %ds, %cx\n  movl %ecx, 0x20(%eax)  // context->uc_mcontext.xds\n\n  // General-purpose registers whose values haven’t changed can be captured\n  // directly.\n  movl %edi, 0x24(%eax)  // context->uc_mcontext.edi\n  movl %esi, 0x28(%eax)  // context->uc_mcontext.esi\n\n  // The original %ebp was saved on the stack in this function’s prologue.\n  movl (%ebp), %ecx\n  movl %ecx, 0x2c(%eax)  // context->uc_mcontext.ebp\n\n  // %esp was saved in %ebp in this function’s prologue, but the caller’s %esp\n  // is 8 more than this value: 4 for the original %ebp saved on the stack in\n  // this function’s prologue, and 4 for the return address saved on the stack\n  // by the call instruction that reached this function.\n  leal 8(%ebp), %ecx\n  movl %ecx, 0x30(%eax)  // context->uc_mcontext.esp\n\n  // More general-purpose registers\n  movl %ebx, 0x34(%eax)  // context->uc_mcontext.ebx\n  movl %edx, 0x38(%eax)  // context->uc_mcontext.edx\n\n  // The original %ecx was saved on the stack above.\n  movl -12(%ebp), %ecx\n  movl %ecx, 0x3c(%eax)  // context->uc_mcontext.ecx\n\n  // The original %eax was saved on the stack above.\n  movl -8(%ebp), %ecx\n  movl %ecx, 0x40(%eax)  // context->uc_mcontext.eax\n\n  // trapno and err are unused so zero them out.\n  xorl %ecx, %ecx\n  movl %ecx, 0x44(%eax)  // context->uc_mcontext.trapno\n  movl %ecx, 0x48(%eax)  // context->uc_mcontext.err\n\n  // %eip can’t be accessed directly, but the return address saved on the stack\n  // by the call instruction that reached this function can be used.\n  movl 4(%ebp), %ecx\n  movl %ecx, 0x4c(%eax)  // context->uc_mcontext.eip\n\n  // More segment registers\n  xorl %ecx, %ecx\n  movw %cs, %cx\n  movl %ecx, 0x50(%eax)  // context->uc_mcontext.xcs\n\n  // The original %eflags was saved on the stack above.\n  movl -4(%ebp), %ecx\n  movl %ecx, 0x54(%eax)  // context->uc_mcontext.eflags\n\n  // uesp is unused so zero it out.\n  xorl %ecx, %ecx\n  movl %ecx, 0x58(%eax)  // context->uc_mcontext.uesp\n\n  // The last segment register.\n  movw %ss, %cx\n  movl %ecx, 0x5c(%eax)  // context->uc_mcontext.xss\n\n  // TODO(jperaza): save floating-point registers.\n  xorl %ecx, %ecx\n  movl %ecx, 0x60(%eax)  // context->uc_mcontext.fpregs\n\n  // Clean up by restoring clobbered registers, even those considered volatile\n  // by the ABI, so that the captured context represents the state at this\n  // function’s exit.\n  popl %ecx\n  popl %eax\n  popfl\n\n  popl %ebp\n\n  ret\n\n  .cfi_endproc\n\n#elif defined(__x86_64__)\n\n  .cfi_startproc\n\n  pushq %rbp\n  .cfi_def_cfa_offset 16\n  .cfi_offset %rbp, -16\n  movq %rsp, %rbp\n  .cfi_def_cfa_register %rbp\n\n  // Note that 16-byte stack alignment is not maintained because this function\n  // does not call out to any other.\n\n  // pushfq first, because some instructions (but probably none used here)\n  // affect %rflags. %rflags will be in -8(%rbp).\n  pushfq\n\n  // General-purpose registers whose values haven’t changed can be captured\n  // directly.\n  movq %r8, 0x28(%rdi)  // context->uc_mcontext.r8\n  movq %r9, 0x30(%rdi)  // context->uc_mcontext.r9\n  movq %r10, 0x38(%rdi)  // context->uc_mcontext.r10\n  movq %r11, 0x40(%rdi)  // context->uc_mcontext.r11\n  movq %r12, 0x48(%rdi)  // context->uc_mcontext.r12\n  movq %r13, 0x50(%rdi)  // context->uc_mcontext.r13\n  movq %r14, 0x58(%rdi)  // context->uc_mcontext.r14\n  movq %r15, 0x60(%rdi)  // context->uc_mcontext.r15\n\n  // Because of the calling convention, there’s no way to recover the value of\n  // the caller’s %rdi as it existed prior to calling this function. This\n  // function captures a snapshot of the register state at its return, which\n  // involves %rdi containing a pointer to its first argument. Callers that\n  // require the value of %rdi prior to calling this function should obtain it\n  // separately. For example:\n  //   uint64_t rdi;\n  //   asm(\"movq %%rdi, %0\" : \"=m\"(rdi));\n  movq %rdi, 0x68(%rdi)  // context->uc_mcontext.rdi\n\n  movq %rsi, 0x70(%rdi)  // context->uc_mcontext.rsi\n\n  // Use %r8 as a scratch register now that it has been saved.\n  // The original %rbp was saved on the stack in this function’s prologue.\n  movq (%rbp), %r8\n  movq %r8, 0x78(%rdi)  // context->uc_mcontext.rbp\n\n  // Save the remaining general-purpose registers.\n  movq %rbx, 0x80(%rdi)  // context->uc_mcontext.rbx\n  movq %rdx, 0x88(%rdi)  // context->uc_mcontext.rdx\n  movq %rax, 0x90(%rdi)  // context->uc_mcontext.rax\n  movq %rcx, 0x98(%rdi)  // context->uc_mcontext.rcx\n\n  // %rsp was saved in %rbp in this function’s prologue, but the caller’s %rsp\n  // is 16 more than this value: 8 for the original %rbp saved on the stack in\n  // this function’s prologue, and 8 for the return address saved on the stack\n  // by the call instruction that reached this function.\n  leaq 16(%rbp), %r8\n  movq %r8, 0xa0(%rdi)  // context->uc_mcontext.rsp\n\n  // %rip can’t be accessed directly, but the return address saved on the stack\n  // by the call instruction that reached this function can be used.\n  movq 8(%rbp), %r8\n  movq %r8, 0xa8(%rdi)  // context->uc_mcontext.rip\n\n  // The original %rflags was saved on the stack above.\n  movq -8(%rbp), %r8\n  movq %r8, 0xb0(%rdi)  // context->uc_mcontext.eflags\n\n  // Save the segment registers\n  movw %cs, 0xb8(%rdi)  // context->uc_mcontext.cs\n  movw %gs, 0xba(%rdi)  // context->uc_mcontext.gs\n  movw %fs, 0xbc(%rdi)  // context->uc_mcontext.fs\n\n  xorw %ax, %ax\n  movw %ax, 0xbe(%rdi)  // context->uc_mcontext.padding\n\n  // Zero out the remainder of the unused pseudo-registers\n  xorq %r8, %r8\n  movq %r8, 0xc0(%rdi)  // context->uc_mcontext.err\n  movq %r8, 0xc8(%rdi)  // context->uc_mcontext.trapno\n  movq %r8, 0xd0(%rdi)  // context->uc_mcontext.oldmask\n  movq %r8, 0xd8(%rdi)  // context->uc_mcontext.cr2\n\n  // TODO(jperaza): save floating-point registers.\n  movq %r8, 0xe0(%rdi)  // context->uc_mcontext.fpregs\n\n  // Clean up by restoring clobbered registers, even those considered volatile\n  // by the ABI, so that the captured context represents the state at this\n  // function’s exit.\n  movq 0x90(%rdi), %rax\n  movq 0x28(%rdi), %r8\n\n  popfq\n\n  popq %rbp\n\n  ret\n\n  .cfi_endproc\n\n#elif defined(__arm__)\n\n  // The original r0 can't be recovered.\n  str r0, [r0, #0x20]\n\n  // Now advance r0 to point to the register array.\n  add r0, r0, #0x24\n\n  // Save registers r1-r12 at context->uc_mcontext.regs[i].\n  stm r0, {r1-r12}\n\n  // Restore r0.\n  sub r0, r0, #0x24\n\n  // Save SP/r13.\n  str SP, [r0, #0x54]  // context->uc_mcontext.sp\n\n  // The original LR can't be recovered.\n  str LR, [r0, #0x58]  // context->uc_mcontext.lr\n\n  // The link register holds the return address for this function.\n  str LR, [r0, #0x5c]  // context->uc_mcontext.pc\n\n  // Use r1 as a scratch register.\n\n  // CPSR is a deprecated synonym for APSR.\n  mrs r1, APSR\n  str r1, [r0, #0x60]  // context->uc_mcontext.cpsr\n\n  // Zero out unused fields.\n  mov r1, #0x0\n  str r1, [r0, #0x14]  // context->uc_mcontext.trap_no\n  str r1, [r0, #0x18]  // context->uc_mcontext.error_code\n  str r1, [r0, #0x1c]  // context->uc_mcontext.oldmask\n  str r1, [r0, #0x64]  // context->uc_mcontext.fault_address\n\n  // Restore r1.\n  ldr r1, [r0, #0x24]\n\n  // TODO(https://crashpad.chromium.org/bug/300): save floating-point registers.\n\n  mov PC, LR\n\n#elif defined(__aarch64__)\n\n  // Zero out fault_address, which is unused.\n  str xzr, [x0, #0xb0]  // context->uc_mcontext.fault_address\n\n  // Save general purpose registers in context->uc_mcontext.regs[i].\n  // The original x0 can't be recovered.\n  stp x0, x1, [x0, #0xb8]\n  stp x2, x3, [x0, #0xc8]\n  stp x4, x5, [x0, #0xd8]\n  stp x6, x7, [x0, #0xe8]\n  stp x8, x9, [x0, #0xf8]\n  stp x10, x11, [x0, #0x108]\n  stp x12, x13, [x0, #0x118]\n  stp x14, x15, [x0, #0x128]\n  stp x16, x17, [x0, #0x138]\n  stp x18, x19, [x0, #0x148]\n  stp x20, x21, [x0, #0x158]\n  stp x22, x23, [x0, #0x168]\n  stp x24, x25, [x0, #0x178]\n  stp x26, x27, [x0, #0x188]\n  stp x28, x29, [x0, #0x198]\n\n  // The original LR can't be recovered, therefore no need to sign x30 with PAC.\n  str x30, [x0, #0x1a8]\n\n  // Use x1 as a scratch register.\n  mov x1, SP\n  str x1, [x0, #0x1b0] // context->uc_mcontext.sp\n\n  // The link register holds the return address for this function and won't be\n  // recovered, therefore no need to sign x30 with PAC.\n  str x30, [x0, #0x1b8]  // context->uc_mcontext.pc\n\n  // pstate should hold SPSR but NZCV are the only bits we know about.\n  mrs x1, NZCV\n  str x1, [x0, #0x1c0]  // context->uc_mcontext.pstate\n\n  // Restore x1 from the saved context.\n  ldr x1, [x0, #0xc0]\n\n  // TODO(https://crashpad.chromium.org/bug/300): save floating-point registers.\n\n  ret\n#elif defined(__mips__)\n  .set noat\n\n#if _MIPS_SIM == _ABIO32\n#define STORE sw\n#define MCONTEXT_FPREG_SIZE 4\n#define MCONTEXT_PC_OFFSET 32\n#else\n#define STORE sd\n#define MCONTEXT_FPREG_SIZE 8\n#define MCONTEXT_PC_OFFSET 616\n#endif\n\n#define MCONTEXT_REG_SIZE 8\n#define MCONTEXT_GREGS_OFFSET 40\n#define MCONTEXT_FPREGS_OFFSET 296\n\n  // Value of register 0 is always 0.\n  // Registers 26 and 27 are reserved for kernel, and shouldn't be used.\n  STORE $1, (1 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $2, (2 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $3, (3 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $4, (4 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $5, (5 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $6, (6 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $7, (7 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $8, (8 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $9, (9 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $10, (10 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $11, (11 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $12, (12 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $13, (13 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $14, (14 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $15, (15 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $16, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $17, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $18, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $19, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $20, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $21, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $22, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $23, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)($a0)\n  STORE $31, (MCONTEXT_PC_OFFSET)($a0)\n\n#ifdef __mips_hard_float\n  s.d $f0, (0 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f2, (2 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f4, (4 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f6, (6 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f8, (8 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f10, (10 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f12, (12 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f14, (14 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f16, (16 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f18, (18 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f20, (20 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f22, (22 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f24, (24 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f26, (26 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f28, (28 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f30, (30 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n#if _MIPS_SIM != _ABIO32\n  s.d $f1, (1 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f3, (3 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f5, (5 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f7, (7 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f9, (9 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f11, (11 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f13, (13 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f15, (15 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f17, (17 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f19, (19 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f21, (21 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f23, (23 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f25, (25 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f27, (27 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f29, (29 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n  s.d $f31, (31 * MCONTEXT_FPREG_SIZE + MCONTEXT_FPREGS_OFFSET)($a0)\n#endif  // _MIPS_SIM != _ABIO32\n#endif  // __mips_hard_float\n\n  jr $ra\n\n  .set at\n\n#elif defined(__riscv)\n\n  #define MCONTEXT_GREGS_OFFSET 176\n\n  // x1/ra is the return address. Store it as the pc.\n  // The original x10/a0 can't be recovered.\n  sd x1, (0 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x1, (1 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x2, (2 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x3, (3 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x4, (4 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x5, (5 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x6, (6 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x7, (7 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x8, (8 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x9, (9 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x10, (10 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x11, (11 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x12, (12 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x13, (13 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x14, (14 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x15, (15 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x16, (16 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x17, (17 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x18, (18 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x19, (19 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x20, (20 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x21, (21 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x22, (22 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x23, (23 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x24, (24 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x25, (25 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x26, (26 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x27, (27 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x28, (28 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x29, (29 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x30, (30 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n  sd x31, (31 * 8 + MCONTEXT_GREGS_OFFSET)(a0)\n\n  #define MCONTEXT_FPREGS_OFFSET MCONTEXT_GREGS_OFFSET + 32*8\n\n  // Use x31/t6 as scratch register.\n  frcsr x31\n  sw x31, (32 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n\n  fsd f0, (0 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f1, (1 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f2, (2 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f3, (3 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f4, (4 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f5, (5 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f6, (6 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f7, (7 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f8, (8 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f9, (9 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f10, (10 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f11, (11 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f12, (12 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f13, (13 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f14, (14 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f15, (15 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f16, (16 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f17, (17 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f18, (18 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f19, (19 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f20, (20 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f21, (21 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f22, (22 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f23, (23 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f24, (24 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f25, (25 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f26, (26 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f27, (27 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f28, (28 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f29, (29 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f30, (30 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n  fsd f31, (31 * 8 + MCONTEXT_FPREGS_OFFSET)(a0)\n\n  ret\n\n#endif  // __i386__\n"
  },
  {
    "path": "util/misc/capture_context_mac.S",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)\n\n#if defined(__i386__) || defined(__x86_64__)\n// namespace crashpad {\n// void CaptureContext(x86_thread_state_t* x86_thread_state);\n// }  // namespace crashpad\n#define CAPTURECONTEXT_SYMBOL __ZN8crashpad14CaptureContextEP16x86_thread_state\n#elif defined(__aarch64__)\n// namespace crashpad {\n// void CaptureContext(arm_unified_thread_state_t* arm_unified_thread_state);\n// }  // namespace crashpad\n#define CAPTURECONTEXT_SYMBOL \\\n    __ZN8crashpad14CaptureContextEP24arm_unified_thread_state\n#endif\n\n  .section __TEXT,__text,regular,pure_instructions\n  .private_extern CAPTURECONTEXT_SYMBOL\n  .globl CAPTURECONTEXT_SYMBOL\n#if defined(__i386__) || defined(__x86_64__)\n  .p2align 4, 0x90\n#elif defined(__aarch64__)\n  .p2align 2\n#endif\nCAPTURECONTEXT_SYMBOL:\n\n#if defined(__i386__)\n\n  .cfi_startproc\n\n  pushl %ebp\n  .cfi_def_cfa_offset 8\n  .cfi_offset %ebp, -8\n  movl %esp, %ebp\n  .cfi_def_cfa_register %ebp\n\n  // Note that 16-byte stack alignment is not maintained because this function\n  // does not call out to any other.\n\n  // pushfl first, because some instructions (but probably none used here)\n  // affect %eflags. %eflags will be in -4(%ebp).\n  pushfl\n\n  // Save the original value of %eax, and use %eax to hold the x86_thread_state*\n  // argument. The original value of %eax will be in -8(%ebp).\n  pushl %eax\n  movl 8(%ebp), %eax\n\n  // Initialize the header identifying the x86_thread_state_t structure as\n  // carrying an x86_thread_state32_t (flavor x86_THREAD_STATE32) of size\n  // x86_THREAD_STATE32_COUNT 32-bit values.\n  movl $1, (%eax)  // x86_thread_state->tsh.flavor\n  movl $16, 4(%eax)  // x86_thread_state->tsh.count\n\n  // General-purpose registers whose values haven’t changed can be captured\n  // directly.\n  movl %ebx, 12(%eax)  // x86_thread_state->uts.ts32.__ebx\n  movl %ecx, 16(%eax)  // x86_thread_state->uts.ts32.__ecx\n  movl %edx, 20(%eax)  // x86_thread_state->uts.ts32.__edx\n  movl %edi, 24(%eax)  // x86_thread_state->uts.ts32.__edi\n  movl %esi, 28(%eax)  // x86_thread_state->uts.ts32.__esi\n\n  // Now that the original value of %edx has been saved, it can be repurposed to\n  // hold other registers’ values.\n\n  // The original %eax was saved on the stack above.\n  movl -8(%ebp), %edx\n  movl %edx, 8(%eax)  // x86_thread_state->uts.ts32.__eax\n\n  // The original %ebp was saved on the stack in this function’s prologue.\n  movl (%ebp), %edx\n  movl %edx, 32(%eax)  // x86_thread_state->uts.ts32.__ebp\n\n  // %esp was saved in %ebp in this function’s prologue, but the caller’s %esp\n  // is 8 more than this value: 4 for the original %ebp saved on the stack in\n  // this function’s prologue, and 4 for the return address saved on the stack\n  // by the call instruction that reached this function.\n  leal 8(%ebp), %edx\n  movl %edx, 36(%eax)  // x86_thread_state->uts.ts32.__esp\n\n  // The original %eflags was saved on the stack above.\n  movl -4(%ebp), %edx\n  movl %edx, 44(%eax)  // x86_thread_state->uts.ts32.__eflags\n\n  // %eip can’t be accessed directly, but the return address saved on the stack\n  // by the call instruction that reached this function can be used.\n  movl 4(%ebp), %edx\n  movl %edx, 48(%eax)  // x86_thread_state->uts.ts32.__eip\n\n  // The segment registers are 16 bits wide, but x86_thread_state declares them\n  // as unsigned 32-bit values, so zero the top half.\n  xorl %edx, %edx\n  movw %ss, %dx\n  movl %edx, 40(%eax)  // x86_thread_state->uts.ts32.__ss\n  movw %cs, %dx\n  movl %edx, 52(%eax)  // x86_thread_state->uts.ts32.__cs\n  movw %ds, %dx\n  movl %edx, 56(%eax)  // x86_thread_state->uts.ts32.__ds\n  movw %es, %dx\n  movl %edx, 60(%eax)  // x86_thread_state->uts.ts32.__es\n  movw %fs, %dx\n  movl %edx, 64(%eax)  // x86_thread_state->uts.ts32.__fs\n  movw %gs, %dx\n  movl %edx, 68(%eax)  // x86_thread_state->uts.ts32.__gs\n\n  // Clean up by restoring clobbered registers, even those considered volatile\n  // by the ABI, so that the captured context represents the state at this\n  // function’s exit.\n  movl 20(%eax), %edx  // x86_thread_state->uts.ts32.__edx\n  popl %eax\n  popfl\n\n  popl %ebp\n\n  ret\n\n  .cfi_endproc\n\n#elif defined(__x86_64__)\n\n  .cfi_startproc\n\n  pushq %rbp\n  .cfi_def_cfa_offset 16\n  .cfi_offset %rbp, -16\n  movq %rsp, %rbp\n  .cfi_def_cfa_register %rbp\n\n  // Note that 16-byte stack alignment is not maintained because this function\n  // does not call out to any other.\n\n  // pushfq first, because some instructions (but probably none used here)\n  // affect %rflags. %rflags will be in -8(%rbp).\n  pushfq\n\n  // Initialize the header identifying the x86_thread_state_t structure as\n  // carrying an x86_thread_state64_t (flavor x86_THREAD_STATE64) of size\n  // x86_THREAD_STATE64_COUNT 32-bit values.\n  movl $4, (%rdi)  // x86_thread_state->tsh.flavor\n  movl $42, 4(%rdi)  // x86_thread_state->tsh.count\n\n  // General-purpose registers whose values haven’t changed can be captured\n  // directly.\n  movq %rax, 8(%rdi)  // x86_thread_state->uts.ts64.__rax\n  movq %rbx, 16(%rdi)  // x86_thread_state->uts.ts64.__rbx\n  movq %rcx, 24(%rdi)  // x86_thread_state->uts.ts64.__rcx\n  movq %rdx, 32(%rdi)  // x86_thread_state->uts.ts64.__rdx\n  movq %rsi, 48(%rdi)  // x86_thread_state->uts.ts64.__rsi\n  movq %r8, 72(%rdi)  // x86_thread_state->uts.ts64.__r8\n  movq %r9, 80(%rdi)  // x86_thread_state->uts.ts64.__r9\n  movq %r10, 88(%rdi)  // x86_thread_state->uts.ts64.__r10\n  movq %r11, 96(%rdi)  // x86_thread_state->uts.ts64.__r11\n  movq %r12, 104(%rdi)  // x86_thread_state->uts.ts64.__r12\n  movq %r13, 112(%rdi)  // x86_thread_state->uts.ts64.__r13\n  movq %r14, 120(%rdi)  // x86_thread_state->uts.ts64.__r14\n  movq %r15, 128(%rdi)  // x86_thread_state->uts.ts64.__r15\n\n  // Because of the calling convention, there’s no way to recover the value of\n  // the caller’s %rdi as it existed prior to calling this function. This\n  // function captures a snapshot of the register state at its return, which\n  // involves %rdi containing a pointer to its first argument. Callers that\n  // require the value of %rdi prior to calling this function should obtain it\n  // separately. For example:\n  //   uint64_t rdi;\n  //   asm(\"movq %%rdi, %0\" : \"=m\"(rdi));\n  movq %rdi, 40(%rdi)  // x86_thread_state->uts.ts64.__rdi\n\n  // Now that the original value of %rax has been saved, it can be repurposed to\n  // hold other registers’ values.\n\n  // The original %rbp was saved on the stack in this function’s prologue.\n  movq (%rbp), %rax\n  movq %rax, 56(%rdi)  // x86_thread_state->uts.ts64.__rbp\n\n  // %rsp was saved in %rbp in this function’s prologue, but the caller’s %rsp\n  // is 16 more than this value: 8 for the original %rbp saved on the stack in\n  // this function’s prologue, and 8 for the return address saved on the stack\n  // by the call instruction that reached this function.\n  leaq 16(%rbp), %rax\n  movq %rax, 64(%rdi)  // x86_thread_state->uts.ts64.__rsp\n\n  // %rip can’t be accessed directly, but the return address saved on the stack\n  // by the call instruction that reached this function can be used.\n  movq 8(%rbp), %rax\n  movq %rax, 136(%rdi)  // x86_thread_state->uts.ts64.__rip\n\n  // The original %rflags was saved on the stack above.\n  movq -8(%rbp), %rax\n  movq %rax, 144(%rdi)  // x86_thread_state->uts.ts64.__rflags\n\n  // The segment registers are 16 bits wide, but x86_thread_state declares them\n  // as unsigned 64-bit values, so zero the top portion.\n  xorq %rax, %rax\n  movw %cs, %ax\n  movq %rax, 152(%rdi)  // x86_thread_state->uts.ts64.__cs\n  movw %fs, %ax\n  movq %rax, 160(%rdi)  // x86_thread_state->uts.ts64.__fs\n  movw %gs, %ax\n  movq %rax, 168(%rdi)  // x86_thread_state->uts.ts64.__gs\n\n  // Clean up by restoring clobbered registers, even those considered volatile\n  // by the ABI, so that the captured context represents the state at this\n  // function’s exit.\n  movq 8(%rdi), %rax\n  popfq\n\n  popq %rbp\n\n  ret\n\n  .cfi_endproc\n\n#elif defined(__aarch64__)\n\n  .cfi_startproc\n\n  // Save general-purpose registers in arm_unified_thread_state->ts_64.__x[0].\n  // The original x0 can't be recovered.\n  stp x0, x1, [x0, #0x8]\n  stp x2, x3, [x0, #0x18]\n  stp x4, x5, [x0, #0x28]\n  stp x6, x7, [x0, #0x38]\n  stp x8, x9, [x0, #0x48]\n  stp x10, x11, [x0, #0x58]\n  stp x12, x13, [x0, #0x68]\n  stp x14, x15, [x0, #0x78]\n  stp x16, x17, [x0, #0x88]\n  stp x18, x19, [x0, #0x98]\n  stp x20, x21, [x0, #0xa8]\n  stp x22, x23, [x0, #0xb8]\n  stp x24, x25, [x0, #0xc8]\n  stp x26, x27, [x0, #0xd8]\n\n  // Save the last general-purpose register (x28) and the frame pointer (x29).\n  stp x28, x29, [x0, #0xe8]  // __x[28] and __fp\n\n  // Save the link register (x30) and the stack pointer (using x1 as a scratch\n  // register)\n  mov x1, sp\n  stp x30, x1, [x0, #0xf8]  // __lr and __sp\n\n  // The link register (x30) holds the return address for this function.\n  // __cpsr should hold current program status register, but nzcv are the only\n  // bits we know about (saved using x1 as a scratch register). The 64-bit x1\n  // covers both the 32-bit __cpsr (which receives the nzcv bits) and __pad\n  // (which will be zeroed).\n  mrs x1, nzcv\n  stp x30, x1, [x0, #0x108]  // __pc and __cpsr and __pad\n\n  // Initialize the header identifying the arm_unified_thread_state structure as\n  // carrying an arm_thread_state64_t (flavor ARM_THREAD_STATE64) of size\n  // ARM_THREAD_STATE64_COUNT 32-bit values.\n  mov x1, #6\n  movk x1, #68, lsl #32\n  str x1, [x0, #0x0]  // arm_thread_state->ash.flavor and count\n\n  // Restore x1 from the saved context.\n  ldr x1, [x0, #0x10]\n\n  // TODO(justincohen): Consider saving floating-point registers into\n  // arm_neon_state64_t as second parameter, or as a a second function call\n  // after all of the general-purpose state is captured, or as a new struct that\n  // has both arm_unified_state_t and arm_neon_state64_t members. That may be\n  // better than a second parameter (which occupies another register) and better\n  // than a second function call.\n\n  ret\n\n  .cfi_endproc\n\n#endif\n\n.subsections_via_symbols\n\n#endif\n"
  },
  {
    "path": "util/misc/capture_context_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/capture_context.h\"\n\n#include <stdint.h>\n\n#include <algorithm>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"util/misc/address_sanitizer.h\"\n#include \"util/misc/capture_context_test_util.h\"\n#include \"util/misc/memory_sanitizer.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n#if defined(MEMORY_SANITIZER)\n// CaptureContext() calls inline assembly and is incompatible with MSan.\n__attribute__((no_sanitize(\"memory\")))\n#endif  // defined(MEMORY_SANITIZER)\n\nvoid TestCaptureContext() {\n  NativeCPUContext context_1;\n  CaptureContext(&context_1);\n\n  {\n    SCOPED_TRACE(\"context_1\");\n    ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_1));\n  }\n\n  // The program counter reference value is this function’s address. The\n  // captured program counter should be slightly greater than or equal to the\n  // reference program counter.\n  uintptr_t pc = ProgramCounterFromContext(context_1);\n\n#if !defined(ADDRESS_SANITIZER) && !defined(ARCH_CPU_MIPS_FAMILY) && \\\n    !defined(MEMORY_SANITIZER)\n  // Sanitizers can cause enough code bloat that the “nearby” check would\n  // likely fail.\n  const uintptr_t kReferencePC =\n      reinterpret_cast<uintptr_t>(TestCaptureContext);\n  EXPECT_PRED2([](uintptr_t actual,\n                  uintptr_t reference) { return actual - reference < 128u; },\n               pc,\n               kReferencePC);\n#endif\n\n  const uintptr_t sp = StackPointerFromContext(context_1);\n\n  // Declare context_2 here because all local variables need to be declared\n  // before computing the stack pointer reference value, so that the reference\n  // value can be the lowest value possible.\n  NativeCPUContext context_2;\n\n  // AddressSanitizer with use-after-return detection causes stack variables to\n  // be allocated on the heap.\n#if !defined(ADDRESS_SANITIZER)\n  // The stack pointer reference value is the lowest address of a local variable\n  // in this function. The captured program counter will be slightly less than\n  // or equal to the reference stack pointer.\n  const uintptr_t kReferenceSP =\n      std::min(std::min(reinterpret_cast<uintptr_t>(&context_1),\n                        reinterpret_cast<uintptr_t>(&context_2)),\n               std::min(reinterpret_cast<uintptr_t>(&pc),\n                        reinterpret_cast<uintptr_t>(&sp)));\n  EXPECT_PRED2([](uintptr_t actual,\n                  uintptr_t reference) { return reference - actual < 768u; },\n               sp,\n               kReferenceSP);\n#endif  // !defined(ADDRESS_SANITIZER)\n\n  // Capture the context again, expecting that the stack pointer stays the same\n  // and the program counter increases. Strictly speaking, there’s no guarantee\n  // that these conditions will hold, although they do for known compilers even\n  // under typical optimization.\n  CaptureContext(&context_2);\n\n  {\n    SCOPED_TRACE(\"context_2\");\n    ASSERT_NO_FATAL_FAILURE(SanityCheckContext(context_2));\n  }\n\n  EXPECT_EQ(StackPointerFromContext(context_2), sp);\n  EXPECT_GT(ProgramCounterFromContext(context_2), pc);\n}\n\nTEST(CaptureContext, CaptureContext) {\n  ASSERT_NO_FATAL_FAILURE(TestCaptureContext());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/capture_context_test_util.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/capture_context.h\"\n\n#include <stdint.h>\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief Sanity check conditions that should be true for any NativeCPUContext\n//!     produced by CaptureContext().\n//!\n//! If the context structure has fields that tell whether it’s valid, such as\n//! magic numbers or size fields, sanity-checks those fields for validity with\n//! fatal Google Test assertions. For other fields, where it’s possible to\n//! reason about their validity based solely on their contents, sanity-checks\n//! via nonfatal Google Test assertions.\n//!\n//! \\param[in] context The context to check.\nvoid SanityCheckContext(const NativeCPUContext& context);\n\n//! \\brief Return the value of the program counter from a NativeCPUContext.\nuintptr_t ProgramCounterFromContext(const NativeCPUContext& context);\n\n//! \\brief Return the value of the stack pointer from a NativeCPUContext.\nuintptr_t StackPointerFromContext(const NativeCPUContext& context);\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/capture_context_test_util_linux.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/capture_context_test_util.h\"\n\n#include \"gtest/gtest.h\"\n#include \"util/misc/from_pointer_cast.h\"\n\nnamespace crashpad {\nnamespace test {\n\nvoid SanityCheckContext(const NativeCPUContext& context) {\n#if defined(ARCH_CPU_X86)\n  // TODO(jperaza): fpregs is nullptr until CaptureContext() supports capturing\n  // floating point context.\n  EXPECT_EQ(context.uc_mcontext.fpregs, nullptr);\n#elif defined(ARCH_CPU_X86_64)\n  EXPECT_EQ(context.uc_mcontext.gregs[REG_RDI],\n            FromPointerCast<intptr_t>(&context));\n  EXPECT_EQ(context.uc_mcontext.fpregs, nullptr);\n#elif defined(ARCH_CPU_ARMEL)\n  EXPECT_EQ(context.uc_mcontext.arm_r0, FromPointerCast<uintptr_t>(&context));\n#elif defined(ARCH_CPU_ARM64)\n  EXPECT_EQ(context.uc_mcontext.regs[0], FromPointerCast<uintptr_t>(&context));\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  EXPECT_EQ(context.uc_mcontext.gregs[4], FromPointerCast<uintptr_t>(&context));\n#elif defined(ARCH_CPU_RISCV64)\n  EXPECT_EQ(context.uc_mcontext.__gregs[10],\n            FromPointerCast<uintptr_t>(&context));\n#endif\n}\n\nuintptr_t ProgramCounterFromContext(const NativeCPUContext& context) {\n#if defined(ARCH_CPU_X86)\n  return context.uc_mcontext.gregs[REG_EIP];\n#elif defined(ARCH_CPU_X86_64)\n  return context.uc_mcontext.gregs[REG_RIP];\n#elif defined(ARCH_CPU_ARMEL)\n  return context.uc_mcontext.arm_pc;\n#elif defined(ARCH_CPU_ARM64)\n  return context.uc_mcontext.pc;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  return context.uc_mcontext.pc;\n#elif defined(ARCH_CPU_RISCV64)\n  return context.uc_mcontext.__gregs[0];\n#endif\n}\n\nuintptr_t StackPointerFromContext(const NativeCPUContext& context) {\n#if defined(ARCH_CPU_X86)\n  return context.uc_mcontext.gregs[REG_ESP];\n#elif defined(ARCH_CPU_X86_64)\n  return context.uc_mcontext.gregs[REG_RSP];\n#elif defined(ARCH_CPU_ARMEL)\n  return context.uc_mcontext.arm_sp;\n#elif defined(ARCH_CPU_ARM64)\n  return context.uc_mcontext.sp;\n#elif defined(ARCH_CPU_MIPS_FAMILY)\n  return context.uc_mcontext.gregs[29];\n#elif defined(ARCH_CPU_RISCV64)\n  return context.uc_mcontext.__gregs[2];\n#endif\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/capture_context_test_util_mac.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/capture_context_test_util.h\"\n\n#include \"gtest/gtest.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\n\nvoid SanityCheckContext(const NativeCPUContext& context) {\n#if defined(ARCH_CPU_X86)\n  ASSERT_EQ(implicit_cast<thread_state_flavor_t>(context.tsh.flavor),\n            implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE32));\n  ASSERT_EQ(implicit_cast<uint32_t>(context.tsh.count),\n            implicit_cast<uint32_t>(x86_THREAD_STATE32_COUNT));\n#elif defined(ARCH_CPU_X86_64)\n  ASSERT_EQ(implicit_cast<thread_state_flavor_t>(context.tsh.flavor),\n            implicit_cast<thread_state_flavor_t>(x86_THREAD_STATE64));\n  ASSERT_EQ(implicit_cast<uint32_t>(context.tsh.count),\n            implicit_cast<uint32_t>(x86_THREAD_STATE64_COUNT));\n#elif defined(ARCH_CPU_ARM64)\n  ASSERT_EQ(implicit_cast<thread_state_flavor_t>(context.ash.flavor),\n            implicit_cast<thread_state_flavor_t>(ARM_THREAD_STATE64));\n  ASSERT_EQ(implicit_cast<uint32_t>(context.ash.count),\n            implicit_cast<uint32_t>(ARM_THREAD_STATE64_COUNT));\n#endif\n\n#if defined(ARCH_CPU_X86_FAMILY)\n// The segment registers are only capable of storing 16-bit quantities, but\n// the context structure provides native integer-width fields for them. Ensure\n// that the high bits are all clear.\n//\n// Many bit positions in the flags register are reserved and will always read\n// a known value. Most reserved bits are always 0, but bit 1 is always 1.\n// Check that the reserved bits are all set to their expected values. Note\n// that the set of reserved bits may be relaxed over time with newer CPUs, and\n// that this test may need to be changed to reflect these developments. The\n// current set of reserved bits are 1, 3, 5, 15, and 22 and higher. See Intel\n// Software Developer’s Manual, Volume 1: Basic Architecture (253665-051),\n// 3.4.3 “EFLAGS Register”, and AMD Architecture Programmer’s Manual, Volume\n// 2: System Programming (24593-3.24), 3.1.6 “RFLAGS Register”.\n#if defined(ARCH_CPU_X86)\n  EXPECT_EQ(context.uts.ts32.__cs & ~0xffff, 0u);\n  EXPECT_EQ(context.uts.ts32.__ds & ~0xffff, 0u);\n  EXPECT_EQ(context.uts.ts32.__es & ~0xffff, 0u);\n  EXPECT_EQ(context.uts.ts32.__fs & ~0xffff, 0u);\n  EXPECT_EQ(context.uts.ts32.__gs & ~0xffff, 0u);\n  EXPECT_EQ(context.uts.ts32.__ss & ~0xffff, 0u);\n  EXPECT_EQ(context.uts.ts32.__eflags & 0xffc0802a, 2u);\n#elif defined(ARCH_CPU_X86_64)\n  EXPECT_EQ(context.uts.ts64.__cs & ~UINT64_C(0xffff), 0u);\n  EXPECT_EQ(context.uts.ts64.__fs & ~UINT64_C(0xffff), 0u);\n  EXPECT_EQ(context.uts.ts64.__gs & ~UINT64_C(0xffff), 0u);\n  EXPECT_EQ(context.uts.ts64.__rflags & UINT64_C(0xffffffffffc0802a), 2u);\n#endif\n\n#elif defined(ARCH_CPU_ARM64)\n  // Check that the bits other than nzcv in __cpsr read 0.\n  EXPECT_EQ(context.ts_64.__cpsr & 0x0fffffff, 0u);\n\n  // Check that __pad is entirely zeroed out.\n  EXPECT_EQ(context.ts_64.__pad, 0u);\n\n  // Because ARM ret doesn’t change %lr, and because CaptureContext captures the\n  // the state at CaptureContext’s return to its caller, check for equivalence\n  // of __lr and __pc.\n  EXPECT_EQ(context.ts_64.__lr, context.ts_64.__pc);\n#endif\n}\n\nuintptr_t ProgramCounterFromContext(const NativeCPUContext& context) {\n#if defined(ARCH_CPU_X86)\n  return context.uts.ts32.__eip;\n#elif defined(ARCH_CPU_X86_64)\n  return context.uts.ts64.__rip;\n#elif defined(ARCH_CPU_ARM64)\n  return context.ts_64.__pc;\n#endif\n}\n\nuintptr_t StackPointerFromContext(const NativeCPUContext& context) {\n#if defined(ARCH_CPU_X86)\n  return context.uts.ts32.__esp;\n#elif defined(ARCH_CPU_X86_64)\n  return context.uts.ts64.__rsp;\n#elif defined(ARCH_CPU_ARM64)\n  return context.ts_64.__sp;\n#endif\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/capture_context_test_util_win.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/capture_context_test_util.h\"\n\n#include <iterator>\n\n#include \"gtest/gtest.h\"\n#include \"util/win/context_wrappers.h\"\n\nnamespace crashpad {\nnamespace test {\n\nvoid SanityCheckContext(const NativeCPUContext& context) {\n#if defined(ARCH_CPU_X86)\n  constexpr uint32_t must_have = CONTEXT_i386 | CONTEXT_CONTROL |\n                                 CONTEXT_INTEGER | CONTEXT_SEGMENTS |\n                                 CONTEXT_FLOATING_POINT;\n  ASSERT_EQ(context.ContextFlags & must_have, must_have);\n  constexpr uint32_t may_have = CONTEXT_EXTENDED_REGISTERS;\n  ASSERT_EQ(context.ContextFlags & ~(must_have | may_have), 0u);\n#elif defined(ARCH_CPU_X86_64)\n  ASSERT_EQ(\n      context.ContextFlags,\n      static_cast<DWORD>(CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER |\n                         CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT));\n#endif\n\n#if defined(ARCH_CPU_X86_FAMILY)\n  // Many bit positions in the flags register are reserved and will always read\n  // a known value. Most reserved bits are always 0, but bit 1 is always 1.\n  // Check that the reserved bits are all set to their expected values. Note\n  // that the set of reserved bits may be relaxed over time with newer CPUs, and\n  // that this test may need to be changed to reflect these developments. The\n  // current set of reserved bits are 1, 3, 5, 15, and 22 and higher. See Intel\n  // Software Developer’s Manual, Volume 1: Basic Architecture (253665-055),\n  // 3.4.3 “EFLAGS Register”, and AMD Architecture Programmer’s Manual, Volume\n  // 2: System Programming (24593-3.25), 3.1.6 “RFLAGS Register”.\n  EXPECT_EQ(context.EFlags & 0xffc0802a, 2u);\n\n  // CaptureContext() doesn’t capture debug registers, so make sure they read 0.\n  EXPECT_EQ(context.Dr0, 0u);\n  EXPECT_EQ(context.Dr1, 0u);\n  EXPECT_EQ(context.Dr2, 0u);\n  EXPECT_EQ(context.Dr3, 0u);\n  EXPECT_EQ(context.Dr6, 0u);\n  EXPECT_EQ(context.Dr7, 0u);\n#endif\n\n#if defined(ARCH_CPU_X86)\n  // fxsave doesn’t write these bytes.\n  for (size_t i = 464; i < std::size(context.ExtendedRegisters); ++i) {\n    SCOPED_TRACE(i);\n    EXPECT_EQ(context.ExtendedRegisters[i], 0);\n  }\n#elif defined(ARCH_CPU_X86_64)\n  // mxcsr shows up twice in the context structure. Make sure the values are\n  // identical.\n  EXPECT_EQ(context.FltSave.MxCsr, context.MxCsr);\n\n  // fxsave doesn’t write these bytes.\n  for (size_t i = 0; i < std::size(context.FltSave.Reserved4); ++i) {\n    SCOPED_TRACE(i);\n    EXPECT_EQ(context.FltSave.Reserved4[i], 0);\n  }\n\n  // CaptureContext() doesn’t use these fields.\n  EXPECT_EQ(context.P1Home, 0u);\n  EXPECT_EQ(context.P2Home, 0u);\n  EXPECT_EQ(context.P3Home, 0u);\n  EXPECT_EQ(context.P4Home, 0u);\n  EXPECT_EQ(context.P5Home, 0u);\n  EXPECT_EQ(context.P6Home, 0u);\n  for (size_t i = 0; i < std::size(context.VectorRegister); ++i) {\n    SCOPED_TRACE(i);\n    EXPECT_EQ(context.VectorRegister[i].Low, 0u);\n    EXPECT_EQ(context.VectorRegister[i].High, 0u);\n  }\n  EXPECT_EQ(context.VectorControl, 0u);\n  EXPECT_EQ(context.DebugControl, 0u);\n  EXPECT_EQ(context.LastBranchToRip, 0u);\n  EXPECT_EQ(context.LastBranchFromRip, 0u);\n  EXPECT_EQ(context.LastExceptionToRip, 0u);\n  EXPECT_EQ(context.LastExceptionFromRip, 0u);\n#endif\n}\n\nuintptr_t ProgramCounterFromContext(const NativeCPUContext& context) {\n  return reinterpret_cast<uintptr_t>(ProgramCounterFromCONTEXT(&context));\n}\n\nuintptr_t StackPointerFromContext(const NativeCPUContext& context) {\n#if defined(ARCH_CPU_X86)\n  return context.Esp;\n#elif defined(ARCH_CPU_X86_64)\n  return context.Rsp;\n#elif defined(ARCH_CPU_ARM64)\n  return context.Sp;\n#endif\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/capture_context_win.asm",
    "content": "; Copyright 2015 The Crashpad Authors\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\n; Detect ml64 assembling for x86_64 by checking for rax.\nifdef rax\n_M_X64 equ 1\nelse\n_M_IX86 equ 1\nendif\n\nifdef _M_IX86\n.586\n.xmm\n.model flat\nendif\n\n; The CONTEXT structure definitions that follow are based on those in <winnt.h>.\n; Field names are prefixed (as in c_Rax) to avoid colliding with the predefined\n; register names (such as Rax).\n\nifdef _M_IX86\n\nCONTEXT_i386 equ 10000h\nCONTEXT_CONTROL equ CONTEXT_i386 or 1h\nCONTEXT_INTEGER equ CONTEXT_i386 or 2h\nCONTEXT_SEGMENTS equ CONTEXT_i386 or 4h\nCONTEXT_FLOATING_POINT equ CONTEXT_i386 or 8h\nCONTEXT_DEBUG_REGISTERS equ CONTEXT_i386 or 10h\nCONTEXT_EXTENDED_REGISTERS equ CONTEXT_i386 or 20h\nCONTEXT_XSTATE equ CONTEXT_i386 or 40h\n\nMAXIMUM_SUPPORTED_EXTENSION equ 512\n\nCONTEXT struct\n  c_ContextFlags dword ?\n\n  c_Dr0 dword ?\n  c_Dr1 dword ?\n  c_Dr2 dword ?\n  c_Dr3 dword ?\n  c_Dr6 dword ?\n  c_Dr7 dword ?\n\n  struct c_FloatSave\n    f_ControlWord dword ?\n    f_StatusWord dword ?\n    f_TagWord dword ?\n    f_ErrorOffset dword ?\n    f_ErrorSelector dword ?\n    f_DataOffset dword ?\n    f_DataSelector dword ?\n    f_RegisterArea byte 80 dup(?)\n\n    union\n      f_Spare0 dword ?  ; As in FLOATING_SAVE_AREA.\n      f_Cr0NpxState dword ?  ; As in WOW64_FLOATING_SAVE_AREA.\n    ends\n  ends\n\n  c_SegGs dword ?\n  c_SegFs dword ?\n  c_SegEs dword ?\n  c_SegDs dword ?\n\n  c_Edi dword ?\n  c_Esi dword ?\n  c_Ebx dword ?\n  c_Edx dword ?\n  c_Ecx dword ?\n  c_Eax dword ?\n\n  c_Ebp dword ?\n\n  c_Eip dword ?\n  c_SegCs dword ?\n\n  c_EFlags dword ?\n\n  c_Esp dword ?\n  c_SegSs dword ?\n\n  c_ExtendedRegisters byte MAXIMUM_SUPPORTED_EXTENSION dup(?)\nCONTEXT ends\n\nelseifdef _M_X64\n\nM128A struct 16\n  m_Low qword ?\n  m_High qword ?\nM128A ends\n\nCONTEXT_AMD64 equ 100000h\nCONTEXT_CONTROL equ CONTEXT_AMD64 or 1h\nCONTEXT_INTEGER equ CONTEXT_AMD64 or 2h\nCONTEXT_SEGMENTS equ CONTEXT_AMD64 or 4h\nCONTEXT_FLOATING_POINT equ CONTEXT_AMD64 or 8h\nCONTEXT_DEBUG_REGISTERS equ CONTEXT_AMD64 or 10h\nCONTEXT_XSTATE equ CONTEXT_AMD64 or 40h\n\nCONTEXT struct 16\n  c_P1Home qword ?\n  c_P2Home qword ?\n  c_P3Home qword ?\n  c_P4Home qword ?\n  c_P5Home qword ?\n  c_P6Home qword ?\n\n  c_ContextFlags dword ?\n  c_MxCsr dword ?\n\n  c_SegCs word ?\n  c_SegDs word ?\n  c_SegEs word ?\n  c_SegFs word ?\n  c_SegGs word ?\n  c_SegSs word ?\n\n  c_EFlags dword ?\n\n  c_Dr0 qword ?\n  c_Dr1 qword ?\n  c_Dr2 qword ?\n  c_Dr3 qword ?\n  c_Dr6 qword ?\n  c_Dr7 qword ?\n\n  c_Rax qword ?\n  c_Rcx qword ?\n  c_Rdx qword ?\n  c_Rbx qword ?\n  c_Rsp qword ?\n  c_Rbp qword ?\n  c_Rsi qword ?\n  c_Rdi qword ?\n  c_R8 qword ?\n  c_R9 qword ?\n  c_R10 qword ?\n  c_R11 qword ?\n  c_R12 qword ?\n  c_R13 qword ?\n  c_R14 qword ?\n  c_R15 qword ?\n\n  c_Rip qword ?\n\n  union\n    struct c_FltSave\n      f_ControlWord word ?\n      f_StatusWord word ?\n      f_TagWord byte ?\n      f_Reserved1 byte ?\n      f_ErrorOpcode word ?\n      f_ErrorOffset dword ?\n      f_ErrorSelector word ?\n      f_Reserved2 word ?\n      f_DataOffset dword ?\n      f_DataSelector word ?\n      f_Reserved3 word ?\n      f_MxCsr dword ?\n      f_MxCsr_Mask dword ?\n      f_FloatRegisters M128A 8 dup(<?>)\n      f_XmmRegisters M128A 16 dup(<?>)\n      f_Reserved4 byte 96 dup(?)\n    ends\n    struct\n      fx_Header M128A 2 dup(<?>)\n      fx_Legacy M128A 8 dup(<?>)\n      fx_Xmm0 M128A <?>\n      fx_Xmm1 M128A <?>\n      fx_Xmm2 M128A <?>\n      fx_Xmm3 M128A <?>\n      fx_Xmm4 M128A <?>\n      fx_Xmm5 M128A <?>\n      fx_Xmm6 M128A <?>\n      fx_Xmm7 M128A <?>\n      fx_Xmm8 M128A <?>\n      fx_Xmm9 M128A <?>\n      fx_Xmm10 M128A <?>\n      fx_Xmm11 M128A <?>\n      fx_Xmm12 M128A <?>\n      fx_Xmm13 M128A <?>\n      fx_Xmm14 M128A <?>\n      fx_Xmm15 M128A <?>\n    ends\n  ends\n\n  c_VectorRegister M128A 26 dup(<?>)\n  c_VectorControl qword ?\n\n  c_DebugControl qword ?\n  c_LastBranchToRip qword ?\n  c_LastBranchFromRip qword ?\n  c_LastExceptionToRip qword ?\n  c_LastExceptionFromRip qword ?\nCONTEXT ends\n\nendif\n\n; namespace crashpad {\n; void CaptureContext(CONTEXT* context);\n; }  // namespace crashpad\nifdef _M_IX86\nCAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPAU_CONTEXT@@@Z\nelseifdef _M_X64\nCAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z\nendif\n\n_TEXT segment\npublic CAPTURECONTEXT_SYMBOL\n\nifdef _M_IX86\n\nCAPTURECONTEXT_SYMBOL proc\n\n  push ebp\n  mov ebp, esp\n\n  ; pushfd first, because some instructions affect eflags. eflags will be in\n  ; [ebp-4].\n  pushfd\n\n  ; Save the original value of ebx, and use ebx to hold the CONTEXT* argument.\n  ; The original value of ebx will be in [ebp-8].\n  push ebx\n  mov ebx, [ebp+8]\n\n  ; General-purpose registers whose values haven’t changed can be captured\n  ; directly.\n  mov [ebx.CONTEXT].c_Edi, edi\n  mov [ebx.CONTEXT].c_Esi, esi\n  mov [ebx.CONTEXT].c_Edx, edx\n  mov [ebx.CONTEXT].c_Ecx, ecx\n  mov [ebx.CONTEXT].c_Eax, eax\n\n  ; Now that the original value of edx has been saved, it can be repurposed to\n  ; hold other registers’ values.\n\n  ; The original ebx was saved on the stack above.\n  mov edx, dword ptr [ebp-8]\n  mov [ebx.CONTEXT].c_Ebx, edx\n\n  ; The original ebp was saved on the stack in this function’s prologue.\n  mov edx, dword ptr [ebp]\n  mov [ebx.CONTEXT].c_Ebp, edx\n\n  ; eip can’t be accessed directly, but the return address saved on the stack\n  ; by the call instruction that reached this function can be used.\n  mov edx, dword ptr [ebp+4]\n  mov [ebx.CONTEXT].c_Eip, edx\n\n  ; The original eflags was saved on the stack above.\n  mov edx, dword ptr [ebp-4]\n  mov [ebx.CONTEXT].c_EFlags, edx\n\n  ; esp was saved in ebp in this function’s prologue, but the caller’s esp is 8\n  ; more than this value: 4 for the original ebp saved on the stack in this\n  ; function’s prologue, and 4 for the return address saved on the stack by the\n  ; call instruction that reached this function.\n  lea edx, [ebp+8]\n  mov [ebx.CONTEXT].c_Esp, edx\n\n  ; The segment registers are 16 bits wide, but CONTEXT declares them as\n  ; unsigned 32-bit values, so zero the top half.\n  xor edx, edx\n  mov dx, gs\n  mov [ebx.CONTEXT].c_SegGs, edx\n  mov dx, fs\n  mov [ebx.CONTEXT].c_SegFs, edx\n  mov dx, es\n  mov [ebx.CONTEXT].c_SegEs, edx\n  mov dx, ds\n  mov [ebx.CONTEXT].c_SegDs, edx\n  mov dx, cs\n  mov [ebx.CONTEXT].c_SegCs, edx\n  mov dx, ss\n  mov [ebx.CONTEXT].c_SegSs, edx\n\n  ; Prepare for the string move that will populate the ExtendedRegisters area,\n  ; or the string store that will zero it.\n  cld\n\n  ; Use cpuid 1 to check whether fxsave is supported. If it is, perform it\n  ; before fnsave because fxsave is a less-destructive operation.\n  mov esi, ebx\n  mov eax, 1\n  cpuid\n  mov ebx, esi\n\n  test edx, 01000000  ; FXSR\n  jnz $FXSave\n\n  ; fxsave is not supported. Set ContextFlags to not include\n  ; CONTEXT_EXTENDED_REGISTERS, and zero the ExtendedRegisters area.\n  mov [ebx.CONTEXT].c_ContextFlags, CONTEXT_i386 or \\\n                                    CONTEXT_CONTROL or \\\n                                    CONTEXT_INTEGER or \\\n                                    CONTEXT_SEGMENTS or \\\n                                    CONTEXT_FLOATING_POINT\n  lea edi, [ebx.CONTEXT].c_ExtendedRegisters\n  xor eax, eax\n  mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword)  ; 128\n  rep stosd\n  jmp $FXSaveDone\n\n$FXSave:\n  ; fxsave is supported. Set ContextFlags to include CONTEXT_EXTENDED_REGISTERS.\n  mov [ebx.CONTEXT].c_ContextFlags, CONTEXT_i386 or \\\n                                    CONTEXT_CONTROL or \\\n                                    CONTEXT_INTEGER or \\\n                                    CONTEXT_SEGMENTS or \\\n                                    CONTEXT_FLOATING_POINT or \\\n                                    CONTEXT_EXTENDED_REGISTERS\n\n  ; fxsave requires a 16 byte-aligned destination memory area. Nothing\n  ; guarantees the alignment of a CONTEXT structure, so create a temporary\n  ; aligned fxsave destination on the stack.\n  and esp, 0fffffff0h\n  sub esp, MAXIMUM_SUPPORTED_EXTENSION\n\n  ; Zero out the temporary fxsave area before performing the fxsave. Some of the\n  ; fxsave area may not be written by fxsave, and some is definitely not written\n  ; by fxsave.\n  mov edi, esp\n  xor eax, eax\n  mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword)  ; 128\n  rep stosd\n\n  fxsave [esp]\n\n  ; Copy the temporary fxsave area into the CONTEXT structure.\n  lea edi, [ebx.CONTEXT].c_ExtendedRegisters\n  mov esi, esp\n  mov ecx, MAXIMUM_SUPPORTED_EXTENSION / sizeof(dword)  ; 128\n  rep movsd\n\n  ; Free the stack space used for the temporary fxsave area.\n  lea esp, [ebp-8]\n\n  ; TODO(mark): AVX/xsave support. https://crashpad.chromium.org/bug/58\n\n$FXSaveDone:\n  ; fnsave reinitializes the FPU with an implicit finit operation, so use frstor\n  ; to restore the original state.\n  fnsave [ebx.CONTEXT].c_FloatSave\n  frstor [ebx.CONTEXT].c_FloatSave\n\n  ; cr0 is inaccessible from user code, and this field would not be used anyway.\n  mov [ebx.CONTEXT].c_FloatSave.f_Cr0NpxState, 0\n\n  ; The debug registers can’t be read from user code, so zero them out in the\n  ; CONTEXT structure. context->ContextFlags doesn’t indicate that they are\n  ; present.\n  mov [ebx.CONTEXT].c_Dr0, 0\n  mov [ebx.CONTEXT].c_Dr1, 0\n  mov [ebx.CONTEXT].c_Dr2, 0\n  mov [ebx.CONTEXT].c_Dr3, 0\n  mov [ebx.CONTEXT].c_Dr6, 0\n  mov [ebx.CONTEXT].c_Dr7, 0\n\n  ; Clean up by restoring clobbered registers, even those considered volatile\n  ; by the ABI, so that the captured context represents the state at this\n  ; function’s exit.\n  mov edi, [ebx.CONTEXT].c_Edi\n  mov esi, [ebx.CONTEXT].c_Esi\n  mov edx, [ebx.CONTEXT].c_Edx\n  mov ecx, [ebx.CONTEXT].c_Ecx\n  mov eax, [ebx.CONTEXT].c_Eax\n  pop ebx\n  popfd\n\n  pop ebp\n\n  ret\n\nCAPTURECONTEXT_SYMBOL endp\n\nelseifdef _M_X64\n\nCAPTURECONTEXT_SYMBOL proc frame\n\n  push rbp\n  .pushreg rbp\n  mov rbp, rsp\n  .setframe rbp, 0\n\n  ; Note that 16-byte stack alignment is not maintained because this function\n  ; does not call out to any other.\n\n  ; pushfq first, because some instructions affect rflags. rflags will be in\n  ; [rbp-8].\n  pushfq\n  .allocstack 8\n  .endprolog\n\n  mov [rcx.CONTEXT].c_ContextFlags, CONTEXT_AMD64 or \\\n                                    CONTEXT_CONTROL or \\\n                                    CONTEXT_INTEGER or \\\n                                    CONTEXT_SEGMENTS or \\\n                                    CONTEXT_FLOATING_POINT\n\n  ; General-purpose registers whose values haven’t changed can be captured\n  ; directly.\n  mov [rcx.CONTEXT].c_Rax, rax\n  mov [rcx.CONTEXT].c_Rdx, rdx\n  mov [rcx.CONTEXT].c_Rbx, rbx\n  mov [rcx.CONTEXT].c_Rsi, rsi\n  mov [rcx.CONTEXT].c_Rdi, rdi\n  mov [rcx.CONTEXT].c_R8, r8\n  mov [rcx.CONTEXT].c_R9, r9\n  mov [rcx.CONTEXT].c_R10, r10\n  mov [rcx.CONTEXT].c_R11, r11\n  mov [rcx.CONTEXT].c_R12, r12\n  mov [rcx.CONTEXT].c_R13, r13\n  mov [rcx.CONTEXT].c_R14, r14\n  mov [rcx.CONTEXT].c_R15, r15\n\n  ; Because of the calling convention, there’s no way to recover the value of\n  ; the caller’s rcx as it existed prior to calling this function. This\n  ; function captures a snapshot of the register state at its return, which\n  ; involves rcx containing a pointer to its first argument.\n  mov [rcx.CONTEXT].c_Rcx, rcx\n\n  ; Now that the original value of rax has been saved, it can be repurposed to\n  ; hold other registers’ values.\n\n  ; Save mxcsr. This is duplicated in context->FltSave.MxCsr, saved by fxsave\n  ; below.\n  stmxcsr [rcx.CONTEXT].c_MxCsr\n\n  ; Segment registers.\n  mov [rcx.CONTEXT].c_SegCs, cs\n  mov [rcx.CONTEXT].c_SegDs, ds\n  mov [rcx.CONTEXT].c_SegEs, es\n  mov [rcx.CONTEXT].c_SegFs, fs\n  mov [rcx.CONTEXT].c_SegGs, gs\n  mov [rcx.CONTEXT].c_SegSs, ss\n\n  ; The original rflags was saved on the stack above. Note that the CONTEXT\n  ; structure only stores eflags, the low 32 bits. The high 32 bits in rflags\n  ; are reserved.\n  mov rax, qword ptr [rbp-8]\n  mov [rcx.CONTEXT].c_EFlags, eax\n\n  ; rsp was saved in rbp in this function’s prologue, but the caller’s rsp is\n  ; 16 more than this value: 8 for the original rbp saved on the stack in this\n  ; function’s prologue, and 8 for the return address saved on the stack by the\n  ; call instruction that reached this function.\n  lea rax, [rbp+16]\n  mov [rcx.CONTEXT].c_Rsp, rax\n\n  ; The original rbp was saved on the stack in this function’s prologue.\n  mov rax, qword ptr [rbp]\n  mov [rcx.CONTEXT].c_Rbp, rax\n\n  ; rip can’t be accessed directly, but the return address saved on the stack by\n  ; the call instruction that reached this function can be used.\n  mov rax, qword ptr [rbp+8]\n  mov [rcx.CONTEXT].c_Rip, rax\n\n  ; Zero out the fxsave area before performing the fxsave. Some of the fxsave\n  ; area may not be written by fxsave, and some is definitely not written by\n  ; fxsave. This also zeroes out the rest of the CONTEXT structure to its end,\n  ; including the unused VectorRegister and VectorControl fields, and the debug\n  ; control register fields.\n  mov rbx, rcx\n  cld\n  lea rdi, [rcx.CONTEXT].c_FltSave\n  xor rax, rax\n  mov rcx, (sizeof(CONTEXT) - CONTEXT.c_FltSave) / sizeof(qword)  ; 122\n  rep stosq\n  mov rcx, rbx\n\n  ; Save the floating point (including SSE) state. The CONTEXT structure is\n  ; declared as 16-byte-aligned, which is correct for this operation.\n  fxsave [rcx.CONTEXT].c_FltSave\n\n  ; TODO(mark): AVX/xsave support. https://crashpad.chromium.org/bug/58\n\n  ; The register parameter home address fields aren’t used, so zero them out.\n  mov [rcx.CONTEXT].c_P1Home, 0\n  mov [rcx.CONTEXT].c_P2Home, 0\n  mov [rcx.CONTEXT].c_P3Home, 0\n  mov [rcx.CONTEXT].c_P4Home, 0\n  mov [rcx.CONTEXT].c_P5Home, 0\n  mov [rcx.CONTEXT].c_P6Home, 0\n\n  ; The debug registers can’t be read from user code, so zero them out in the\n  ; CONTEXT structure. context->ContextFlags doesn’t indicate that they are\n  ; present.\n  mov [rcx.CONTEXT].c_Dr0, 0\n  mov [rcx.CONTEXT].c_Dr1, 0\n  mov [rcx.CONTEXT].c_Dr2, 0\n  mov [rcx.CONTEXT].c_Dr3, 0\n  mov [rcx.CONTEXT].c_Dr6, 0\n  mov [rcx.CONTEXT].c_Dr7, 0\n\n  ; Clean up by restoring clobbered registers, even those considered volatile by\n  ; the ABI, so that the captured context represents the state at this\n  ; function’s exit.\n  mov rax, [rcx.CONTEXT].c_Rax\n  mov rbx, [rcx.CONTEXT].c_Rbx\n  mov rdi, [rcx.CONTEXT].c_Rdi\n  popfq\n\n  pop rbp\n\n  ret\n\nCAPTURECONTEXT_SYMBOL endp\n\nendif\n\n_TEXT ends\nend\n"
  },
  {
    "path": "util/misc/capture_context_win_arm64.asm",
    "content": "; Copyright 2019 The Crashpad Authors\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\n  EXPORT |?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z|\n  AREA |.text|, CODE\n|?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z| PROC\n  ; Save general purpose registers in context.regs[i].\n  ; The original x0 can't be recovered.\n  stp x0, x1, [x0, #0x008]\n  stp x2, x3, [x0, #0x018]\n  stp x4, x5, [x0, #0x028]\n  stp x6, x7, [x0, #0x038]\n  stp x8, x9, [x0, #0x048]\n  stp x10, x11, [x0, #0x058]\n  stp x12, x13, [x0, #0x068]\n  stp x14, x15, [x0, #0x078]\n  stp x16, x17, [x0, #0x088]\n  stp x18, x19, [x0, #0x098]\n  stp x20, x21, [x0, #0x0a8]\n  stp x22, x23, [x0, #0x0b8]\n  stp x24, x25, [x0, #0x0c8]\n  stp x26, x27, [x0, #0x0d8]\n  stp x28, x29, [x0, #0x0e8]\n\n  ; The original LR can't be recovered.\n  str LR, [x0, #0x0f8]\n\n  ; Use x1 as a scratch register.\n  mov x1, SP\n  str x1, [x0, #0x100] ; context.sp\n\n  ; The link register holds the return address for this function.\n  str LR, [x0, #0x108]  ; context.pc\n\n  ; pstate should hold SPSR but NZCV are the only bits we know about.\n  mrs x1, NZCV\n\n  ; Enable Control flags, such as CONTEXT_ARM64, CONTEXT_CONTROL,\n  ; CONTEXT_INTEGER\n  ldr w1, =0x00400003\n\n  ; Set ControlFlags /0x000/ and pstate /0x004/ at the same time.\n  str x1, [x0, #0x000]\n\n  ; Restore x1 from the saved context.\n  ldr x1, [x0, #0x010]\n\n  ; TODO(https://crashpad.chromium.org/bug/300): save floating-point registers\n\n  ret\n  ENDP\n\n  END\n"
  },
  {
    "path": "util/misc/clock.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_CLOCK_H_\n#define CRASHPAD_UTIL_MISC_CLOCK_H_\n\n#include <stdint.h>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n//! \\brief Returns the value of the system’s monotonic clock.\n//!\n//! The monotonic clock is a tick counter whose epoch is unspecified. It is a\n//! monotonically-increasing clock that cannot be set, and never jumps backwards\n//! on a running system. The monotonic clock may stop while the system is\n//! sleeping, and it may be reset when the system starts up. This clock is\n//! suitable for computing durations of events. Subject to the underlying\n//! clock’s resolution, successive calls to this function will result in a\n//! series of increasing values.\n//!\n//! \\return The value of the system’s monotonic clock, in nanoseconds.\nuint64_t ClockMonotonicNanoseconds();\n\n//! \\brief Sleeps for the specified duration.\n//!\n//! \\param[in] nanoseconds The number of nanoseconds to sleep. The actual sleep\n//!     may be slightly longer due to latencies and timer resolution.\n//!\n//! On POSIX, this function is resilient against the underlying `nanosleep()`\n//! system call being interrupted by a signal.\nvoid SleepNanoseconds(uint64_t nanoseconds);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_CLOCK_H_\n"
  },
  {
    "path": "util/misc/clock_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/clock.h\"\n\n#include <time.h>\n\nnamespace crashpad {\n\nuint64_t ClockMonotonicNanoseconds() {\n  return clock_gettime_nsec_np(CLOCK_UPTIME_RAW);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/clock_posix.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/clock.h\"\n\n#include <time.h>\n\n#include <ostream>\n\n#include \"base/check.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n\nnamespace {\n\nconstexpr uint64_t kNanosecondsPerSecond = 1E9;\n\n}  // namespace\n\nnamespace crashpad {\n\n#if !BUILDFLAG(IS_APPLE)\n\nuint64_t ClockMonotonicNanoseconds() {\n  timespec now;\n  int rv = clock_gettime(CLOCK_MONOTONIC, &now);\n  DPCHECK(rv == 0) << \"clock_gettime\";\n\n  return now.tv_sec * kNanosecondsPerSecond + now.tv_nsec;\n}\n\n#endif\n\nvoid SleepNanoseconds(uint64_t nanoseconds) {\n  timespec sleep_time;\n  sleep_time.tv_sec = nanoseconds / kNanosecondsPerSecond;\n  sleep_time.tv_nsec = nanoseconds % kNanosecondsPerSecond;\n  int rv = HANDLE_EINTR(nanosleep(&sleep_time, &sleep_time));\n  DPCHECK(rv == 0) << \"nanosleep\";\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/clock_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/clock.h\"\n\n#include <sys/types.h>\n\n#include <algorithm>\n#include <iterator>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Clock, ClockMonotonicNanoseconds) {\n  uint64_t start = ClockMonotonicNanoseconds();\n  EXPECT_GT(start, 0u);\n\n  uint64_t now = start;\n  for (size_t iteration = 0; iteration < 10; ++iteration) {\n    uint64_t last = now;\n    now = ClockMonotonicNanoseconds();\n\n    // Use EXPECT_GE instead of EXPECT_GT, because there are no guarantees about\n    // the clock’s resolution.\n    EXPECT_GE(now, last);\n  }\n\n#if !BUILDFLAG(IS_WIN)  // No SleepNanoseconds implemented on Windows.\n  // SleepNanoseconds() should sleep for at least the value of the clock’s\n  // resolution, so the clock’s value should definitely increase after a sleep.\n  // EXPECT_GT can be used instead of EXPECT_GE after the sleep.\n  SleepNanoseconds(1);\n  now = ClockMonotonicNanoseconds();\n  EXPECT_GT(now, start);\n#endif  // BUILDFLAG(IS_WIN)\n}\n\n#if !BUILDFLAG(IS_WIN)  // No SleepNanoseconds implemented on Windows.\n\nvoid TestSleepNanoseconds(uint64_t nanoseconds) {\n  uint64_t start = ClockMonotonicNanoseconds();\n\n  SleepNanoseconds(nanoseconds);\n\n  uint64_t end = ClockMonotonicNanoseconds();\n  uint64_t diff = end - start;\n\n  // |nanoseconds| is the lower bound for the actual amount of time spent\n  // sleeping.\n  EXPECT_GE(diff, nanoseconds);\n\n  // It’s difficult to set an upper bound for the time spent sleeping, and\n  // attempting to do so results in a flaky test.\n}\n\nTEST(Clock, SleepNanoseconds) {\n  static constexpr uint64_t kTestData[] = {\n      0,\n      1,\n      static_cast<uint64_t>(1E3),  // 1 microsecond\n      static_cast<uint64_t>(1E4),  // 10 microseconds\n      static_cast<uint64_t>(1E5),  // 100 microseconds\n      static_cast<uint64_t>(1E6),  // 1 millisecond\n      static_cast<uint64_t>(1E7),  // 10 milliseconds\n      static_cast<uint64_t>(2E7),  // 20 milliseconds\n      static_cast<uint64_t>(5E7),  // 50 milliseconds\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const uint64_t nanoseconds = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %zu, nanoseconds %\" PRIu64, index, nanoseconds));\n\n    TestSleepNanoseconds(nanoseconds);\n  }\n}\n\n#endif  // BUILDFLAG(IS_WIN)\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/clock_win.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/clock.h\"\n\n#include <windows.h>\n#include <sys/types.h>\n\nnamespace {\n\nLARGE_INTEGER QpcFrequencyInternal() {\n  LARGE_INTEGER frequency;\n  QueryPerformanceFrequency(&frequency);\n  return frequency;\n}\n\nint64_t QpcFrequency() {\n  static LARGE_INTEGER frequency = QpcFrequencyInternal();\n  return frequency.QuadPart;\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nuint64_t ClockMonotonicNanoseconds() {\n  LARGE_INTEGER time;\n  QueryPerformanceCounter(&time);\n  int64_t frequency = QpcFrequency();\n  int64_t whole_seconds = time.QuadPart / frequency;\n  int64_t leftover_ticks = time.QuadPart % frequency;\n  constexpr int64_t kNanosecondsPerSecond = static_cast<const int64_t>(1E9);\n  return (whole_seconds * kNanosecondsPerSecond) +\n         ((leftover_ticks * kNanosecondsPerSecond) / frequency);\n}\n\nvoid SleepNanoseconds(uint64_t nanoseconds) {\n  // This is both inaccurate (will be way too long for short sleeps) and\n  // incorrect (can sleep for less than requested). But it's what's available\n  // without implementing a busy loop.\n  constexpr uint64_t kNanosecondsPerMillisecond = static_cast<uint64_t>(1E6);\n  Sleep(static_cast<DWORD>(nanoseconds / kNanosecondsPerMillisecond));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/elf_note_types.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_ELF_NOTE_TYPES_H_\n#define CRASHPAD_UTIL_MISC_ELF_NOTE_TYPES_H_\n\n// This header defines types of ELF \"notes\" that are embedded sections. These\n// can be read by ElfImageReader in the snapshot library, and are created in\n// client modules. All notes used by Crashpad use the name \"Crashpad\" and one of\n// the types defined here. Note that this file is #included into .S files, so\n// must be relatively plain (no C++ features).\n\n#define CRASHPAD_ELF_NOTE_NAME \"Crashpad\"\n\n// Used by ElfImageReader for testing purposes.\n#define CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST 1\n\n// Used by the client library to stash a pointer to the CrashpadInfo structure\n// for retrieval by the module snapshot. 'OFNI' == 0x4f464e49 which appears as\n// \"INFO\" in readelf -x.\n#define CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO 0x4f464e49\n\n#endif  // CRASHPAD_UTIL_MISC_ELF_NOTE_TYPES_H_\n"
  },
  {
    "path": "util/misc/from_pointer_cast.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_FROM_POINTER_CAST_H_\n#define CRASHPAD_UTIL_MISC_FROM_POINTER_CAST_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"base/numerics/safe_conversions.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n#if DOXYGEN\n\n//! \\brief Casts from a pointer type to an integer.\n//!\n//! Compared to `reinterpret_cast<>()`, FromPointerCast<>() defines whether a\n//! pointer type is sign-extended or zero-extended. Casts to signed integral\n//! types are sign-extended. Casts to unsigned integral types are zero-extended.\n//!\n//! Use FromPointerCast<>() instead of `reinterpret_cast<>()` when casting a\n//! pointer to an integral type that may not be the same width as a pointer.\n//! There is no need to prefer FromPointerCast<>() when casting to an integral\n//! type that’s definitely the same width as a pointer, such as `uintptr_t` and\n//! `intptr_t`.\ntemplate <typename To, typename From>\nFromPointerCast(From from) {\n  return reinterpret_cast<To>(from);\n}\n\n#else  // DOXYGEN\n\n// Cast std::nullptr_t to any type.\n//\n// In C++14, the nullptr_t check could use std::is_null_pointer<From>::value\n// instead of the is_same<remove_cv<From>::type, nullptr_t>::type construct.\ntemplate <typename To, typename From>\ntypename std::enable_if<\n    std::is_same<typename std::remove_cv<From>::type, std::nullptr_t>::value,\n    To>::type\nFromPointerCast(From) {\n  return To();\n}\n\n// FromPointerCast<>() with a function pointer “From” type raises\n// -Wnoexcept-type in GCC 7.2 if the function pointer type has a throw() or\n// noexcept specification. This is the case for all standard C library functions\n// provided by glibc. Various tests make use of FromPointerCast<>() with\n// pointers to standard C library functions.\n//\n// Clang has the similar -Wc++1z-compat-mangling, which is not triggered by this\n// pattern.\n#if defined(COMPILER_GCC) && !defined(__clang__) && \\\n    (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2))\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wnoexcept-type\"\n#endif\n\n// Cast a pointer to any other pointer type.\ntemplate <typename To, typename From>\ntypename std::enable_if<std::is_pointer<From>::value &&\n                            std::is_pointer<To>::value,\n                        To>::type\nFromPointerCast(From from) {\n  return reinterpret_cast<To>(from);\n}\n\n// Cast a pointer to an integral type. Sign-extend when casting to a signed\n// type, zero-extend when casting to an unsigned type.\ntemplate <typename To, typename From>\ntypename std::enable_if<std::is_pointer<From>::value &&\n                            std::is_integral<To>::value,\n                        To>::type\nFromPointerCast(From from) {\n  const auto intermediate =\n      reinterpret_cast<typename std::conditional<std::is_signed<To>::value,\n                                                 intptr_t,\n                                                 uintptr_t>::type>(from);\n\n  if (sizeof(To) >= sizeof(From)) {\n    // If the destination integral type is at least as wide as the source\n    // pointer type, use static_cast<>() and just return it.\n    return static_cast<To>(intermediate);\n  }\n\n  // If the destination integral type is narrower than the source pointer type,\n  // use checked_cast<>().\n  return base::checked_cast<To>(intermediate);\n}\n\n#if defined(COMPILER_GCC) && !defined(__clang__) && \\\n    (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2))\n#pragma GCC diagnostic pop\n#endif\n\n#endif  // DOXYGEN\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_FROM_POINTER_CAST_H_\n"
  },
  {
    "path": "util/misc/from_pointer_cast_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/from_pointer_cast.h\"\n\n#include <stdlib.h>\n#include <sys/types.h>\n\n#include <limits>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nstruct SomeType {};\n\ntemplate <typename T>\nclass FromPointerCastTest : public testing::Test {};\n\nusing FromPointerCastTestTypes = testing::Types<void*,\n                                                const void*,\n                                                volatile void*,\n                                                const volatile void*,\n                                                SomeType*,\n                                                const SomeType*,\n                                                volatile SomeType*,\n                                                const volatile SomeType*>;\n\nTYPED_TEST_SUITE(FromPointerCastTest, FromPointerCastTestTypes);\n\nTYPED_TEST(FromPointerCastTest, ToSigned) {\n  EXPECT_EQ(FromPointerCast<int64_t>(nullptr), 0);\n  EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(1)), 1);\n  EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(-1)), -1);\n  EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(\n                std::numeric_limits<uintptr_t>::max())),\n            static_cast<intptr_t>(std::numeric_limits<uintptr_t>::max()));\n  EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(\n                std::numeric_limits<intptr_t>::min())),\n            std::numeric_limits<intptr_t>::min());\n  EXPECT_EQ(FromPointerCast<int64_t>(reinterpret_cast<TypeParam>(\n                std::numeric_limits<intptr_t>::max())),\n            std::numeric_limits<intptr_t>::max());\n}\n\nTYPED_TEST(FromPointerCastTest, ToUnsigned) {\n  EXPECT_EQ(FromPointerCast<uint64_t>(nullptr), 0u);\n  EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(1)), 1u);\n  EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(-1)),\n            static_cast<uintptr_t>(-1));\n  EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(\n                std::numeric_limits<uintptr_t>::max())),\n            std::numeric_limits<uintptr_t>::max());\n  EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(\n                std::numeric_limits<intptr_t>::min())),\n            static_cast<uintptr_t>(std::numeric_limits<intptr_t>::min()));\n  EXPECT_EQ(FromPointerCast<uint64_t>(reinterpret_cast<TypeParam>(\n                std::numeric_limits<intptr_t>::max())),\n            static_cast<uintptr_t>(std::numeric_limits<intptr_t>::max()));\n}\n\n// MatchCV<YourType, CVQualifiedType>::Type adapts YourType to match the\n// const/void qualification of CVQualifiedType.\ntemplate <typename Base, typename MatchTo>\nstruct MatchCV {\n private:\n  using NonCVBase = typename std::remove_cv<Base>::type;\n\n public:\n  using Type = typename std::conditional<\n      std::is_const<MatchTo>::value,\n      typename std::conditional<std::is_volatile<MatchTo>::value,\n                                const volatile NonCVBase,\n                                const NonCVBase>::type,\n      typename std::conditional<std::is_volatile<MatchTo>::value,\n                                volatile NonCVBase,\n                                NonCVBase>::type>::type;\n};\n\n#if defined(COMPILER_MSVC) && _MSC_VER < 1910\n// Google Test under MSVS 2015 (MSC 19.0) doesn’t handle EXPECT_EQ(a, b) when a\n// or b is a pointer to a volatile type, because it can’t figure out how to\n// print them.\ntemplate <typename T>\ntypename std::remove_volatile<typename std::remove_pointer<T>::type>::type*\nMaybeRemoveVolatile(const T& value) {\n  return const_cast<typename std::remove_volatile<\n      typename std::remove_pointer<T>::type>::type*>(value);\n}\n#else  // COMPILER_MSVC && _MSC_VER < 1910\n// This isn’t a problem in MSVS 2017 (MSC 19.1) or with other compilers.\ntemplate <typename T>\nT MaybeRemoveVolatile(const T& value) {\n  return value;\n}\n#endif  // COMPILER_MSVC && _MSC_VER < 1910\n\nTYPED_TEST(FromPointerCastTest, ToPointer) {\n  using CVSomeType =\n      typename MatchCV<SomeType,\n                       typename std::remove_pointer<TypeParam>::type>::Type;\n\n  EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<CVSomeType*>(nullptr)),\n            MaybeRemoveVolatile(static_cast<CVSomeType*>(nullptr)));\n  EXPECT_EQ(MaybeRemoveVolatile(\n                FromPointerCast<CVSomeType*>(reinterpret_cast<TypeParam>(1))),\n            MaybeRemoveVolatile(reinterpret_cast<CVSomeType*>(1)));\n  EXPECT_EQ(MaybeRemoveVolatile(\n                FromPointerCast<CVSomeType*>(reinterpret_cast<TypeParam>(-1))),\n            MaybeRemoveVolatile(reinterpret_cast<CVSomeType*>(-1)));\n  EXPECT_EQ(\n      MaybeRemoveVolatile(FromPointerCast<CVSomeType*>(\n          reinterpret_cast<TypeParam>(std::numeric_limits<uintptr_t>::max()))),\n      MaybeRemoveVolatile(reinterpret_cast<CVSomeType*>(\n          std::numeric_limits<uintptr_t>::max())));\n  EXPECT_EQ(\n      MaybeRemoveVolatile(FromPointerCast<CVSomeType*>(\n          reinterpret_cast<TypeParam>(std::numeric_limits<intptr_t>::min()))),\n      MaybeRemoveVolatile(\n          reinterpret_cast<CVSomeType*>(std::numeric_limits<intptr_t>::min())));\n  EXPECT_EQ(\n      MaybeRemoveVolatile(FromPointerCast<CVSomeType*>(\n          reinterpret_cast<TypeParam>(std::numeric_limits<intptr_t>::max()))),\n      MaybeRemoveVolatile(\n          reinterpret_cast<CVSomeType*>(std::numeric_limits<intptr_t>::max())));\n}\n\nTEST(FromPointerCast, FromFunctionPointer) {\n  // These casts should work with or without the & in &malloc.\n\n  EXPECT_NE(FromPointerCast<int64_t>(malloc), 0);\n  EXPECT_NE(FromPointerCast<int64_t>(&malloc), 0);\n\n  EXPECT_NE(FromPointerCast<uint64_t>(malloc), 0u);\n  EXPECT_NE(FromPointerCast<uint64_t>(&malloc), 0u);\n\n  EXPECT_EQ(FromPointerCast<void*>(malloc), reinterpret_cast<void*>(malloc));\n  EXPECT_EQ(FromPointerCast<void*>(&malloc), reinterpret_cast<void*>(malloc));\n  EXPECT_EQ(FromPointerCast<const void*>(malloc),\n            reinterpret_cast<const void*>(malloc));\n  EXPECT_EQ(FromPointerCast<const void*>(&malloc),\n            reinterpret_cast<const void*>(malloc));\n  EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<volatile void*>(malloc)),\n            MaybeRemoveVolatile(reinterpret_cast<volatile void*>(malloc)));\n  EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<volatile void*>(&malloc)),\n            MaybeRemoveVolatile(reinterpret_cast<volatile void*>(malloc)));\n  EXPECT_EQ(\n      MaybeRemoveVolatile(FromPointerCast<const volatile void*>(malloc)),\n      MaybeRemoveVolatile(reinterpret_cast<const volatile void*>(malloc)));\n  EXPECT_EQ(\n      MaybeRemoveVolatile(FromPointerCast<const volatile void*>(&malloc)),\n      MaybeRemoveVolatile(reinterpret_cast<const volatile void*>(malloc)));\n\n  EXPECT_EQ(FromPointerCast<SomeType*>(malloc),\n            reinterpret_cast<SomeType*>(malloc));\n  EXPECT_EQ(FromPointerCast<SomeType*>(&malloc),\n            reinterpret_cast<SomeType*>(malloc));\n  EXPECT_EQ(FromPointerCast<const SomeType*>(malloc),\n            reinterpret_cast<const SomeType*>(malloc));\n  EXPECT_EQ(FromPointerCast<const SomeType*>(&malloc),\n            reinterpret_cast<const SomeType*>(malloc));\n  EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<volatile SomeType*>(malloc)),\n            MaybeRemoveVolatile(reinterpret_cast<volatile SomeType*>(malloc)));\n  EXPECT_EQ(MaybeRemoveVolatile(FromPointerCast<volatile SomeType*>(&malloc)),\n            MaybeRemoveVolatile(reinterpret_cast<volatile SomeType*>(malloc)));\n  EXPECT_EQ(\n      MaybeRemoveVolatile(FromPointerCast<const volatile SomeType*>(malloc)),\n      MaybeRemoveVolatile(reinterpret_cast<const volatile SomeType*>(malloc)));\n  EXPECT_EQ(\n      MaybeRemoveVolatile(FromPointerCast<const volatile SomeType*>(&malloc)),\n      MaybeRemoveVolatile(reinterpret_cast<const volatile SomeType*>(malloc)));\n}\n\nTEST(FromPointerCast, ToNarrowInteger) {\n  EXPECT_EQ(FromPointerCast<int>(nullptr), 0);\n  EXPECT_EQ(FromPointerCast<int>(reinterpret_cast<void*>(1)), 1);\n  EXPECT_EQ(FromPointerCast<int>(reinterpret_cast<void*>(-1)), -1);\n  EXPECT_EQ(FromPointerCast<int>(reinterpret_cast<void*>(\n                static_cast<intptr_t>(std::numeric_limits<int>::max()))),\n            std::numeric_limits<int>::max());\n  EXPECT_EQ(FromPointerCast<int>(reinterpret_cast<void*>(\n                static_cast<intptr_t>(std::numeric_limits<int>::min()))),\n            std::numeric_limits<int>::min());\n\n  EXPECT_EQ(FromPointerCast<unsigned int>(nullptr), 0u);\n  EXPECT_EQ(FromPointerCast<unsigned int>(reinterpret_cast<void*>(1)), 1u);\n  EXPECT_EQ(\n      FromPointerCast<unsigned int>(reinterpret_cast<void*>(\n          static_cast<uintptr_t>(std::numeric_limits<unsigned int>::max()))),\n      std::numeric_limits<unsigned int>::max());\n  EXPECT_EQ(FromPointerCast<unsigned int>(reinterpret_cast<void*>(\n                static_cast<uintptr_t>(std::numeric_limits<int>::max()))),\n            static_cast<unsigned int>(std::numeric_limits<int>::max()));\n\n  // int and unsigned int may not be narrower than a pointer, so also test short\n  // and unsigned short.\n\n  EXPECT_EQ(FromPointerCast<short>(nullptr), 0);\n  EXPECT_EQ(FromPointerCast<short>(reinterpret_cast<void*>(1)), 1);\n  EXPECT_EQ(FromPointerCast<short>(reinterpret_cast<void*>(-1)), -1);\n  EXPECT_EQ(FromPointerCast<short>(reinterpret_cast<void*>(\n                static_cast<intptr_t>(std::numeric_limits<short>::max()))),\n            std::numeric_limits<short>::max());\n  EXPECT_EQ(FromPointerCast<short>(reinterpret_cast<void*>(\n                static_cast<intptr_t>(std::numeric_limits<short>::min()))),\n            std::numeric_limits<short>::min());\n\n  EXPECT_EQ(FromPointerCast<unsigned short>(nullptr), 0u);\n  EXPECT_EQ(FromPointerCast<unsigned short>(reinterpret_cast<void*>(1)), 1u);\n  EXPECT_EQ(\n      FromPointerCast<unsigned short>(reinterpret_cast<void*>(\n          static_cast<uintptr_t>(std::numeric_limits<unsigned short>::max()))),\n      std::numeric_limits<unsigned short>::max());\n  EXPECT_EQ(FromPointerCast<unsigned short>(reinterpret_cast<void*>(\n                static_cast<uintptr_t>(std::numeric_limits<short>::max()))),\n            static_cast<unsigned short>(std::numeric_limits<short>::max()));\n}\n\nTEST(FromPointerCastDeathTest, ToNarrowInteger) {\n  if (sizeof(int) < sizeof(void*)) {\n    EXPECT_DEATH_CHECK(\n        FromPointerCast<int>(reinterpret_cast<void*>(static_cast<uintptr_t>(\n            std::numeric_limits<unsigned int>::max() + 1ull))),\n        \"\");\n    EXPECT_DEATH_CHECK(\n        FromPointerCast<unsigned int>(\n            reinterpret_cast<void*>(static_cast<uintptr_t>(\n                std::numeric_limits<unsigned int>::max() + 1ull))),\n        \"\");\n  }\n\n  // int and unsigned int may not be narrower than a pointer, so also test short\n  // and unsigned short.\n\n  EXPECT_DEATH_CHECK(\n      FromPointerCast<short>(reinterpret_cast<void*>(static_cast<uintptr_t>(\n          std::numeric_limits<unsigned short>::max() + 1u))),\n      \"\");\n  EXPECT_DEATH_CHECK(FromPointerCast<unsigned short>(\n                         reinterpret_cast<void*>(static_cast<uintptr_t>(\n                             std::numeric_limits<unsigned short>::max() + 1u))),\n                     \"\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/implicit_cast.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_IMPLICIT_CAST_H_\n#define CRASHPAD_UTIL_MISC_IMPLICIT_CAST_H_\n\nnamespace crashpad {\n\n// Use implicit_cast as a safe version of static_cast or const_cast\n// for upcasting in the type hierarchy (i.e. casting a pointer to Foo\n// to a pointer to SuperclassOfFoo or casting a pointer to Foo to\n// a const pointer to Foo).\n// When you use implicit_cast, the compiler checks that the cast is safe.\n// Such explicit implicit_casts are necessary in surprisingly many\n// situations where C++ demands an exact type match instead of an\n// argument type convertible to a target type.\n//\n// The From type can be inferred, so the preferred syntax for using\n// implicit_cast is the same as for static_cast etc.:\n//\n//   implicit_cast<ToType>(expr)\n//\n// implicit_cast would have been part of the C++ standard library,\n// but the proposal was submitted too late.  It will probably make\n// its way into the language in the future.\ntemplate <typename To, typename From>\nconstexpr To implicit_cast(From const& f) {\n  return f;\n}\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_IMPLICIT_CAST_H_\n"
  },
  {
    "path": "util/misc/initialization_state.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_\n#define CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_\n\n#include <stdint.h>\n\n\nnamespace crashpad {\n\n//! \\brief Tracks whether data are initialized.\n//!\n//! Objects of this type track whether the data they’re guarding are\n//! initialized. The three possible states are uninitialized (the initial\n//! state), initializing, and valid. As the guarded data are initialized, an\n//! InitializationState object will normally transition through these three\n//! states. A fourth state corresponds to the destruction of objects of this\n//! type, making it less likely that a use-after-free of an InitializationState\n//! object will appear in the valid state.\n//!\n//! If the only purpose for tracking the initialization state of guarded data is\n//! to DCHECK when the object is in an unexpected state, use\n//! InitializationStateDcheck instead.\nclass InitializationState {\n public:\n  //! \\brief The object’s state.\n  enum State : uint8_t {\n    //! \\brief The object has not yet been initialized.\n    kStateUninitialized = 0,\n\n    //! \\brief The object is being initialized.\n    //!\n    //! This state protects against attempted reinitializaton of\n    //! partially-initialized objects whose initial initialization attempt\n    //! failed. This state is to be used while objects are initializing, but are\n    //! not yet fully initialized.\n    kStateInvalid,\n\n    //! \\brief The object has been initialized.\n    kStateValid,\n\n    //! \\brief The object has been destroyed.\n    kStateDestroyed,\n  };\n\n  InitializationState() : state_(kStateUninitialized) {}\n  explicit InitializationState(State state) : state_(state) {}\n\n  InitializationState(const InitializationState&) = delete;\n  InitializationState& operator=(const InitializationState&) = delete;\n\n  ~InitializationState() { state_ = kStateDestroyed; }\n\n  //! \\brief Returns `true` if the object’s state is #kStateUninitialized and it\n  //!     is safe to begin initializing it.\n  bool is_uninitialized() const { return state_ == kStateUninitialized; }\n\n  //! \\brief Sets the object’s state to #kStateInvalid, marking initialization\n  //!     as being in process.\n  void set_invalid() { state_ = kStateInvalid; }\n\n  //! \\brief Sets the object’s state to #kStateValid, marking it initialized.\n  void set_valid() { state_ = kStateValid; }\n\n  //! \\brief Returns `true` if the the object’s state is #kStateValid and it has\n  //!     been fully initialized and may be used.\n  bool is_valid() const { return state_ == kStateValid; }\n\n protected:\n  //! \\brief Returns the object’s state.\n  //!\n  //! Consumers of this class should use an is_state_*() method instead.\n  State state() const { return state_; }\n\n  //! \\brief Sets the object’s state.\n  //!\n  //! Consumers of this class should use a set_state_*() method instead.\n  void set_state(State state) { state_ = state; }\n\n private:\n  // state_ is volatile to ensure that it’ll be set by the destructor when it\n  // runs. Otherwise, optimizations might prevent it from ever being set to\n  // kStateDestroyed, limiting this class’ ability to catch use-after-free\n  // errors.\n  volatile State state_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_H_\n"
  },
  {
    "path": "util/misc/initialization_state_dcheck.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/initialization_state_dcheck.h\"\n\nnamespace crashpad {\n\n#if DCHECK_IS_ON()\n\nInitializationStateDcheck::State InitializationStateDcheck::SetInitializing() {\n  State old_state = state();\n  if (old_state == kStateUninitialized) {\n    set_invalid();\n  }\n\n  return old_state;\n}\n\nInitializationStateDcheck::State InitializationStateDcheck::SetValid() {\n  State old_state = state();\n  if (old_state == kStateInvalid) {\n    set_valid();\n  }\n\n  return old_state;\n}\n\n#endif\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/initialization_state_dcheck.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_\n#define CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_\n\n//! \\file\n\n#include <tuple>\n\n#include \"base/check_op.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/initialization_state.h\"\n\nnamespace crashpad {\n\n#if DCHECK_IS_ON() || DOXYGEN\n\n//! \\brief Tracks whether data are initialized, triggering a DCHECK assertion\n//!     on an invalid data access.\n//!\n//! Put an InitializationStateDcheck member into a class to help DCHECK that\n//! it’s in the right states at the right times. This is useful for classes with\n//! Initialize() methods. The chief advantage of InitializationStateDcheck over\n//! having a member variable to track state is that when the only use of the\n//! variable is to DCHECK, it wastes space (in memory and executable code) in\n//! non-DCHECK builds unless the code is also peppered with ugly `#%ifdef`s.\n//!\n//! This implementation concentrates the ugly `#%ifdef`s in one location.\n//!\n//! Usage:\n//!\n//! \\code\n//!   class Class {\n//!    public:\n//!     Class() : initialized_() {}\n//!\n//!     void Initialize() {\n//!       INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n//!       // Perform initialization.\n//!       INITIALIZATION_STATE_SET_VALID(initialized_);\n//!     }\n//!\n//!     void DoSomething() {\n//!       INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n//!       // Do something.\n//!     }\n//!\n//!    private:\n//!     InitializationStateDcheck initialized_;\n//!   };\n//! \\endcode\nclass InitializationStateDcheck : public InitializationState {\n public:\n  InitializationStateDcheck() : InitializationState() {}\n\n  InitializationStateDcheck(const InitializationStateDcheck&) = delete;\n  InitializationStateDcheck& operator=(const InitializationStateDcheck&) =\n      delete;\n\n  //! \\brief Returns the object’s state.\n  //!\n  //! Consumers of this class should not call this method. Use the\n  //! INITIALIZATION_STATE_SET_INITIALIZING(), INITIALIZATION_STATE_SET_VALID(),\n  //! and INITIALIZATION_STATE_DCHECK_VALID() macros instead.\n  //\n  // The superclass’ state() accessor is protected, but it needs to be exposed\n  // to consumers of this class for the macros below to work properly. The\n  // macros prefer access to the unerlying state value over a simple boolean\n  // because with access to the state value, DCHECK_EQ can be used, which, when\n  // tripped, prints both the expected and observed values. This can aid\n  // troubleshooting.\n  State state() const { return InitializationState::state(); }\n\n  //! \\brief Marks an uninitialized object as initializing.\n  //!\n  //! If the object is in the #kStateUninitialized state, changes its state to\n  //! #kStateInvalid (initializing) and returns the previous\n  //! (#kStateUninitialized) state. Otherwise, returns the object’s current\n  //! state.\n  //!\n  //! Consumers of this class should not call this method. Use the\n  //! INITIALIZATION_STATE_SET_INITIALIZING() macro instead.\n  State SetInitializing();\n\n  //! \\brief Marks an initializing object as valid.\n  //!\n  //! If the object is in the #kStateInvalid (initializing) state, changes its\n  //! state to #kStateValid and returns the previous (#kStateInvalid) state.\n  //! Otherwise, returns the object’s current state.\n  //!\n  //! Consumers of this class should not call this method. Use the\n  //! INITIALIZATION_STATE_SET_VALID() macro instead.\n  State SetValid();\n};\n\n// Using macros enables the non-DCHECK no-op implementation below to be more\n// compact and less intrusive. These are macros instead of methods that call\n// DCHECK to enable the DCHECK failure message to point to the correct file and\n// line number, and to allow additional messages to be streamed on failure with\n// the << operator.\n\n//! \\brief Checks that a crashpad::InitializationStateDcheck object is in the\n//!     crashpad::InitializationState::kStateUninitialized state, and changes\n//!     its state to initializing\n//!     (crashpad::InitializationState::kStateInvalid).\n//!\n//! If the object is not in the correct state, a DCHECK assertion is triggered\n//! and the object’s state remains unchanged.\n//!\n//! \\param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck\n//!     object.\n//!\n//! \\sa crashpad::InitializationStateDcheck\n#define INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck) \\\n  DCHECK_EQ((initialization_state_dcheck).SetInitializing(),               \\\n            (initialization_state_dcheck).kStateUninitialized)\n\n//! \\brief Checks that a crashpad::InitializationStateDcheck object is in the\n//!     initializing (crashpad::InitializationState::kStateInvalid) state, and\n//!     changes its state to crashpad::InitializationState::kStateValid.\n//!\n//! If the object is not in the correct state, a DCHECK assertion is triggered\n//! and the object’s state remains unchanged.\n//!\n//! \\param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck\n//!     object.\n//!\n//! \\sa crashpad::InitializationStateDcheck\n#define INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck) \\\n  DCHECK_EQ((initialization_state_dcheck).SetValid(),               \\\n            (initialization_state_dcheck).kStateInvalid)\n\n//! \\brief Checks that a crashpad::InitializationStateDcheck object is in the\n//!     crashpad::InitializationState::kStateValid state.\n//!\n//! If the object is not in the correct state, a DCHECK assertion is triggered.\n//!\n//! \\param[in] initialization_state_dcheck A crashpad::InitializationStateDcheck\n//!     object.\n//!\n//! \\sa crashpad::InitializationStateDcheck\n#define INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck) \\\n  DCHECK_EQ((initialization_state_dcheck).state(),                     \\\n            (initialization_state_dcheck).kStateValid)\n\n#else\n\n#if defined(COMPILER_MSVC)\n// bool[0] (below) is not accepted by MSVC.\nstruct InitializationStateDcheck {\n};\n#else\n// Since this is to be used as a DCHECK (for debugging), it should be\n// non-intrusive in non-DCHECK (non-debug, release) builds. An empty struct\n// would still have a nonzero size (rationale:\n// http://www.stroustrup.com/bs_faq2.html#sizeof-empty). Zero-length arrays are\n// technically invalid according to the standard, but clang and g++ accept them\n// without complaint even with warnings turned up. They take up no space at all,\n// and they can be “initialized” with the same () syntax used to initialize\n// objects of the DCHECK_IS_ON() InitializationStateDcheck class above.\nusing InitializationStateDcheck = bool[0];\n#endif  // COMPILER_MSVC\n\n// Avoid triggering warnings by repurposing these macros when DCHECKs are\n// disabled.\n#define INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck) \\\n  do {                                                                     \\\n    std::ignore = initialization_state_dcheck;                             \\\n  } while (false)\n#define INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck) \\\n  do {                                                              \\\n    std::ignore = initialization_state_dcheck;                      \\\n  } while (false)\n#define INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck) \\\n  do {                                                                 \\\n    std::ignore = initialization_state_dcheck;                         \\\n  } while (false)\n\n#endif\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_INITIALIZATION_INITIALIZATION_STATE_DCHECK_H_\n"
  },
  {
    "path": "util/misc/initialization_state_dcheck_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/initialization_state_dcheck.h\"\n\n#include <stdlib.h>\n\n#include <memory>\n\n#include \"base/check_op.h\"\n#include \"base/memory/free_deleter.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(InitializationStateDcheck, InitializationStateDcheck) {\n  InitializationStateDcheck initialization_state_dcheck;\n  INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);\n  INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);\n  INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck);\n}\n\n#if DCHECK_IS_ON()\n\n// InitializationStateDcheck only DCHECKs, so the death tests can only run\n// when DCHECKs are enabled.\n\nTEST(InitializationStateDcheckDeathTest, Uninitialized_NotInvalid) {\n  // This tests that an attempt to set an uninitialized object as valid without\n  // transitioning through the initializing (invalid) state fails.\n  InitializationStateDcheck initialization_state_dcheck;\n  ASSERT_DEATH_CHECK(\n      INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck),\n      \"kStateInvalid\");\n}\n\nTEST(InitializationStateDcheckDeathTest, Uninitialized_NotValid) {\n  // This tests that an attempt to use an uninitialized object as though it\n  // were valid fails.\n  InitializationStateDcheck initialization_state_dcheck;\n  ASSERT_DEATH_CHECK(\n      INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck),\n      \"kStateValid\");\n}\n\nTEST(InitializationStateDcheckDeathTest, Invalid_NotUninitialized) {\n  // This tests that an attempt to begin initializing an object on which\n  // initialization was already attempted fails.\n  InitializationStateDcheck initialization_state_dcheck;\n  INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);\n  ASSERT_DEATH_CHECK(\n      INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck),\n      \"kStateUninitialized\");\n}\n\nTEST(InitializationStateDcheckDeathTest, Invalid_NotValid) {\n  // This tests that an attempt to use an initializing object as though it\n  // were valid fails.\n  InitializationStateDcheck initialization_state_dcheck;\n  INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);\n  ASSERT_DEATH_CHECK(\n      INITIALIZATION_STATE_DCHECK_VALID(initialization_state_dcheck),\n      \"kStateValid\");\n}\n\nTEST(InitializationStateDcheckDeathTest, Valid_NotUninitialized) {\n  // This tests that an attempt to begin initializing an object that has already\n  // been initialized fails.\n  InitializationStateDcheck initialization_state_dcheck;\n  INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);\n  INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);\n  ASSERT_DEATH_CHECK(\n      INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck),\n      \"kStateUninitialized\");\n}\n\nTEST(InitializationStateDcheckDeathTest, Valid_NotInvalid) {\n  // This tests that an attempt to set a valid object as valid a second time\n  // fails.\n  InitializationStateDcheck initialization_state_dcheck;\n  INITIALIZATION_STATE_SET_INITIALIZING(initialization_state_dcheck);\n  INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck);\n  ASSERT_DEATH_CHECK(\n      INITIALIZATION_STATE_SET_VALID(initialization_state_dcheck),\n      \"kStateInvalid\");\n}\n\nTEST(InitializationStateDcheckDeathTest, Destroyed_NotUninitialized) {\n  // This tests that an attempt to reinitialize a destroyed object fails. See\n  // the InitializationState.InitializationState test for an explanation of this\n  // use-after-free test.\n  std::unique_ptr<InitializationStateDcheck, base::FreeDeleter>\n  initialization_state_dcheck_buffer(static_cast<InitializationStateDcheck*>(\n      malloc(sizeof(InitializationStateDcheck))));\n\n  InitializationStateDcheck* initialization_state_dcheck =\n      new (initialization_state_dcheck_buffer.get())\n          InitializationStateDcheck();\n\n  INITIALIZATION_STATE_SET_INITIALIZING(*initialization_state_dcheck);\n  INITIALIZATION_STATE_SET_VALID(*initialization_state_dcheck);\n  INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck);\n\n  initialization_state_dcheck->~InitializationStateDcheck();\n\n  ASSERT_DEATH_CHECK(\n      INITIALIZATION_STATE_SET_INITIALIZING(*initialization_state_dcheck),\n      \"kStateUninitialized\");\n}\n\nTEST(InitializationStateDcheckDeathTest, Destroyed_NotValid) {\n  // This tests that an attempt to use a destroyed object fails. See the\n  // InitializationState.InitializationState test for an explanation of this\n  // use-after-free test.\n  std::unique_ptr<InitializationStateDcheck, base::FreeDeleter>\n  initialization_state_dcheck_buffer(static_cast<InitializationStateDcheck*>(\n      malloc(sizeof(InitializationStateDcheck))));\n\n  InitializationStateDcheck* initialization_state_dcheck =\n      new (initialization_state_dcheck_buffer.get())\n          InitializationStateDcheck();\n\n  INITIALIZATION_STATE_SET_INITIALIZING(*initialization_state_dcheck);\n  INITIALIZATION_STATE_SET_VALID(*initialization_state_dcheck);\n  INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck);\n\n  initialization_state_dcheck->~InitializationStateDcheck();\n\n  ASSERT_DEATH_CHECK(\n      INITIALIZATION_STATE_DCHECK_VALID(*initialization_state_dcheck),\n      \"kStateValid\");\n}\n\n#endif\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/initialization_state_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/initialization_state.h\"\n\n#include <stdlib.h>\n\n#include <memory>\n\n#include \"base/compiler_specific.h\"\n#include \"base/memory/free_deleter.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(InitializationState, InitializationState) {\n  // Use placement new so that the buffer used to host the object remains live\n  // even after the object is destroyed.\n  std::unique_ptr<InitializationState, base::FreeDeleter>\n  initialization_state_buffer(\n      static_cast<InitializationState*>(malloc(sizeof(InitializationState))));\n\n  InitializationState* initialization_state =\n      new (initialization_state_buffer.get()) InitializationState();\n\n  EXPECT_TRUE(initialization_state->is_uninitialized());\n  EXPECT_FALSE(initialization_state->is_valid());\n\n  initialization_state->set_invalid();\n\n  EXPECT_FALSE(initialization_state->is_uninitialized());\n  EXPECT_FALSE(initialization_state->is_valid());\n\n  initialization_state->set_valid();\n\n  EXPECT_FALSE(initialization_state->is_uninitialized());\n  EXPECT_TRUE(initialization_state->is_valid());\n\n  initialization_state->~InitializationState();\n\n  // initialization_state points to something that no longer exists. This\n  // portion of the test is intended to check that after an InitializationState\n  // object is destroyed, it will not be considered valid on a use-after-free,\n  // assuming that nothing else was written to its former home in memory.\n  //\n  // Because initialization_state was constructed via placement new into a\n  // buffer that’s still valid and its destructor was called directly, this\n  // approximates use-after-free without risking that the memory formerly used\n  // for the InitializationState object has been repurposed.\n\n  // (Though this is still UB and MSan does not like this)\n  MSAN_UNPOISON(initialization_state, sizeof(*initialization_state));\n\n  EXPECT_FALSE(initialization_state->is_uninitialized());\n  EXPECT_FALSE(initialization_state->is_valid());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/lexing.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/lexing.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n#include <string.h>\n\n#include <limits>\n#include <string_view>\n\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/string_util.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n#define MAKE_ADAPTER(type, function)                                \\\n  bool ConvertStringToNumber(std::string_view input, type* value) { \\\n    return function(input, value);                                  \\\n  }\nMAKE_ADAPTER(int, base::StringToInt)\nMAKE_ADAPTER(unsigned int, base::StringToUint)\nMAKE_ADAPTER(int64_t, base::StringToInt64)\nMAKE_ADAPTER(uint64_t, base::StringToUint64)\n#undef MAKE_ADAPTER\n\n}  // namespace\n\nbool AdvancePastPrefix(const char** input, const char* pattern) {\n  size_t length = strlen(pattern);\n  if (strncmp(*input, pattern, length) == 0) {\n    *input += length;\n    return true;\n  }\n  return false;\n}\n\ntemplate <typename T>\nbool AdvancePastNumber(const char** input, T* value) {\n  size_t length = 0;\n  if (std::numeric_limits<T>::is_signed && **input == '-') {\n    ++length;\n  }\n  while (base::IsAsciiDigit((*input)[length])) {\n    ++length;\n  }\n  bool success = ConvertStringToNumber(std::string_view(*input, length), value);\n  if (success) {\n    *input += length;\n    return true;\n  }\n  return false;\n}\n\ntemplate bool AdvancePastNumber(const char** input, int* value);\ntemplate bool AdvancePastNumber(const char** input, unsigned int* value);\ntemplate bool AdvancePastNumber(const char** input, int64_t* value);\ntemplate bool AdvancePastNumber(const char** input, uint64_t* value);\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/lexing.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_LEXING_H_\n#define CRASHPAD_UTIL_MISC_LEXING_H_\n\nnamespace crashpad {\n\n//! \\brief Match a pattern at the start of a char string.\n//!\n//! \\param[in,out] input A pointer to the char string to match against. \\a input\n//!     is advanced past the matched pattern if it is found.\n//! \\param[in] pattern The pattern to match at the start of \\a input.\n//! \\return `true` if the pattern is matched exactly and \\a input is advanced,\n//!     otherwise `false`.\nbool AdvancePastPrefix(const char** input, const char* pattern);\n\n//! \\brief Convert a prefix of a char string to a numeric value.\n//!\n//! Valid values are positive or negative decimal numbers, matching the regular\n//! expression \"-?\\d+\", and within the limits of T.\n//!\n//! \\param[in,out] input A pointer to the char string to match against. \\a input\n//!     is advanced past the number if one is found.\n//! \\param[out] value The converted number, if one is found.\n//! \\return `true` if a number is found at the start of \\a input and \\a input is\n//!     advanced, otherwise `false`.\ntemplate <typename T>\nbool AdvancePastNumber(const char** input, T* value);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_LEXING_H\n"
  },
  {
    "path": "util/misc/memory_sanitizer.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_MEMORY_SANITIZER_H_\n#define CRASHPAD_UTIL_MISC_MEMORY_SANITIZER_H_\n\n#include \"base/compiler_specific.h\"\n#include \"build/build_config.h\"\n\n#if !defined(MEMORY_SANITIZER)\n#if HAS_FEATURE(memory_sanitizer)\n#define MEMORY_SANITIZER 1\n#endif  // HAS_FEATURE(memory_sanitizer)\n#endif  // !defined(MEMORY_SANITIZER)\n\n#endif  // CRASHPAD_UTIL_MISC_MEMORY_SANITIZER_H_\n"
  },
  {
    "path": "util/misc/metrics.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/metrics.h\"\n\n#include \"base/metrics/histogram_functions.h\"\n#include \"base/metrics/histogram_macros.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#define METRICS_OS_NAME \"Mac\"\n#elif BUILDFLAG(IS_WIN)\n#define METRICS_OS_NAME \"Win\"\n#elif BUILDFLAG(IS_ANDROID)\n#define METRICS_OS_NAME \"Android\"\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#define METRICS_OS_NAME \"Linux\"\n#elif BUILDFLAG(IS_FUCHSIA)\n#define METRICS_OS_NAME \"Fuchsia\"\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\n//! \\brief Metrics values used to track the start and completion of a crash\n//!     handling. These are used as metrics values directly, so\n//!     enumeration values so new values should always be added at the end,\n//!     before kMaxValue.\nenum class ExceptionProcessingState {\n  //! \\brief Logged when exception processing is started.\n  kStarted = 0,\n\n  //! \\brief Logged when exception processing completes.\n  kFinished = 1,\n\n  //! \\brief An invalid value.\n  kMaxValue,\n};\n\nvoid ExceptionProcessing(ExceptionProcessingState state) {\n  UMA_HISTOGRAM_ENUMERATION(\"Crashpad.ExceptionEncountered\",\n                            state,\n                            ExceptionProcessingState::kMaxValue);\n}\n\n}  // namespace\n\n// static\nvoid Metrics::CrashReportPending(PendingReportReason reason) {\n  UMA_HISTOGRAM_ENUMERATION(\n      \"Crashpad.CrashReportPending\", reason, PendingReportReason::kMaxValue);\n}\n\n// static\nvoid Metrics::CrashReportSize(FileOffset size) {\n  UMA_HISTOGRAM_CUSTOM_COUNTS(\"Crashpad.CrashReportSize\",\n                              base::saturated_cast<uint32_t>(size),\n                              0,\n                              20 * 1024 * 1024,\n                              50);\n}\n\n// static\nvoid Metrics::CrashUploadAttempted(bool successful) {\n  UMA_HISTOGRAM_BOOLEAN(\"Crashpad.CrashUpload.AttemptSuccessful\", successful);\n}\n\n#if BUILDFLAG(IS_APPLE)\n// static\nvoid Metrics::CrashUploadErrorCode(int error_code) {\n  base::UmaHistogramSparse(\"Crashpad.CrashUpload.ErrorCode\", error_code);\n}\n#endif\n\n// static\nvoid Metrics::CrashUploadSkipped(CrashSkippedReason reason) {\n  UMA_HISTOGRAM_ENUMERATION(\n      \"Crashpad.CrashUpload.Skipped\", reason, CrashSkippedReason::kMaxValue);\n}\n\n// static\nvoid Metrics::ExceptionCaptureResult(CaptureResult result) {\n  ExceptionProcessing(ExceptionProcessingState::kFinished);\n  UMA_HISTOGRAM_ENUMERATION(\n      \"Crashpad.ExceptionCaptureResult\", result, CaptureResult::kMaxValue);\n}\n\n// static\nvoid Metrics::ExceptionCode(uint32_t exception_code) {\n  base::UmaHistogramSparse(\"Crashpad.ExceptionCode.\" METRICS_OS_NAME,\n                           exception_code);\n}\n\n// static\nvoid Metrics::ExceptionEncountered() {\n  ExceptionProcessing(ExceptionProcessingState::kStarted);\n}\n\n// static\nvoid Metrics::HandlerLifetimeMilestone(LifetimeMilestone milestone) {\n  UMA_HISTOGRAM_ENUMERATION(\"Crashpad.HandlerLifetimeMilestone\",\n                            milestone,\n                            LifetimeMilestone::kMaxValue);\n}\n\n// static\nvoid Metrics::HandlerCrashed(uint32_t exception_code) {\n  base::UmaHistogramSparse(\n      \"Crashpad.HandlerCrash.ExceptionCode.\" METRICS_OS_NAME, exception_code);\n}\n\n#if BUILDFLAG(IS_IOS)\n// static\nvoid Metrics::MissingIntermediateDumpKey(\n    const internal::IntermediateDumpKey& key) {\n  base::UmaHistogramSparse(\"Crashpad.IntermediateDump.Reader.MissingKey\",\n                           static_cast<uint16_t>(key));\n}\n\n// static\nvoid Metrics::InvalidIntermediateDumpKeySize(\n    const internal::IntermediateDumpKey& key) {\n  base::UmaHistogramSparse(\"Crashpad.IntermediateDump.Reader.InvalidKeySize\",\n                           static_cast<uint16_t>(key));\n}\n#endif\n\n// static\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/metrics.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_METRICS_H_\n#define CRASHPAD_UTIL_MISC_METRICS_H_\n\n#include <inttypes.h>\n\n#include \"build/build_config.h\"\n#include \"util/file/file_io.h\"\n\n#if BUILDFLAG(IS_IOS)\n#include \"util/ios/ios_intermediate_dump_format.h\"\n#endif\n\nnamespace crashpad {\n\n//! \\brief Container class to hold shared UMA metrics integration points.\n//!\n//! Each static function in this class will call a `UMA_*` from\n//! `base/metrics/histogram_macros.h`. When building Crashpad standalone,\n//! (against mini_chromium), these macros do nothing. When built against\n//! Chromium's base, they allow integration with its metrics system.\nclass Metrics {\n public:\n  Metrics() = delete;\n  Metrics(const Metrics&) = delete;\n  Metrics& operator=(const Metrics&) = delete;\n\n  //! \\brief Values for CrashReportPending().\n  //!\n  //! \\note These are used as metrics enumeration values, so new values should\n  //!     always be added at the end, before PendingReportReason::kMaxValue.\n  enum class PendingReportReason : int32_t {\n    //! \\brief A report was newly created and is ready for upload.\n    kNewlyCreated = 0,\n\n    //! \\brief The user manually requested the report be uploaded.\n    kUserInitiated = 1,\n\n    //! \\brief The number of values in this enumeration; not a valid value.\n    kMaxValue\n  };\n\n  //! \\brief Reports when a crash upload has entered the pending state.\n  static void CrashReportPending(PendingReportReason reason);\n\n  //! \\brief Reports the size of a crash report file in bytes. Should be called\n  //!     when a new report is written to disk.\n  static void CrashReportSize(FileOffset size);\n\n  //! \\brief Reports on a crash upload attempt, and if it succeeded.\n  static void CrashUploadAttempted(bool successful);\n\n#if BUILDFLAG(IS_APPLE) || DOXYGEN\n  //! \\brief Records error codes from\n  //!     `+[NSURLConnection sendSynchronousRequest:returningResponse:error:]`.\n  static void CrashUploadErrorCode(int error_code);\n#endif\n\n  //! \\brief Values for CrashUploadSkipped().\n  //!\n  //! \\note These are used as metrics enumeration values, so new values should\n  //!     always be added at the end, before CrashSkippedReason::kMaxValue.\n  enum class CrashSkippedReason : int32_t {\n    //! \\brief Crash uploading is disabled.\n    kUploadsDisabled = 0,\n\n    //! \\brief There was another upload too recently, so this one was throttled.\n    kUploadThrottled = 1,\n\n    //! \\brief The report had an unexpected timestamp.\n    kUnexpectedTime = 2,\n\n    //! \\brief The database reported an error, likely due to a filesystem\n    //!     problem.\n    kDatabaseError = 3,\n\n    //! \\brief The upload of the crash failed during communication with the\n    //!     server.\n    kUploadFailed = 4,\n\n    //! \\brief There was an error between accessing the report from the database\n    //!     and uploading it to the crash server.\n    kPrepareForUploadFailed = 5,\n\n    //! \\brief The upload of the crash failed during communication with the\n    //!     server, but the upload can be retried later.\n    kUploadFailedButCanRetry = 6,\n\n    //! \\brief The number of values in this enumeration; not a valid value.\n    kMaxValue\n  };\n\n  //! \\brief Reports when a report is moved to the completed state in the\n  //!     database, without the report being uploadad.\n  static void CrashUploadSkipped(CrashSkippedReason reason);\n\n  //! \\brief The result of capturing an exception.\n  //!\n  //! \\note These are used as metrics enumeration values, so new values should\n  //!     always be added at the end, before CaptureResult::kMaxValue.\n  enum class CaptureResult : int32_t {\n    //! \\brief The exception capture succeeded normally.\n    kSuccess = 0,\n\n    //! \\brief Unexpected exception behavior.\n    //!\n    //! This value is only used on macOS.\n    kUnexpectedExceptionBehavior = 1,\n\n    //! \\brief Failed due to attempt to suspend self.\n    //!\n    //! This value is only used on macOS.\n    kFailedDueToSuspendSelf = 2,\n\n    //! \\brief The process snapshot could not be captured.\n    kSnapshotFailed = 3,\n\n    //! \\brief The exception could not be initialized.\n    kExceptionInitializationFailed = 4,\n\n    //! \\brief The attempt to prepare a new crash report in the crash database\n    //!     failed.\n    kPrepareNewCrashReportFailed = 5,\n\n    //! \\brief Writing the minidump to disk failed.\n    kMinidumpWriteFailed = 6,\n\n    //! \\brief There was a database error in attempt to complete the report.\n    kFinishedWritingCrashReportFailed = 7,\n\n    //! \\brief An attempt to directly `ptrace` the target failed.\n    //!\n    //! This value is only used on Linux/Android.\n    kDirectPtraceFailed = 8,\n\n    //! \\brief An attempt to `ptrace` via a PtraceBroker failed.\n    //!\n    //! This value is only used on Linux/Android.\n    kBrokeredPtraceFailed = 9,\n\n    //! \\brief Sanitization was requested but could not be initialized.\n    kSanitizationInitializationFailed = 10,\n\n    //! \\brief Sanitization caused this crash dump to be skipped.\n    kSkippedDueToSanitization = 11,\n\n    //! \\brief Failure to open a memfd caused this crash dump to be skipped.\n    kOpenMemfdFailed = 12,\n\n    //! \\brief The number of values in this enumeration; not a valid value.\n    kMaxValue\n  };\n\n  //! \\brief Reports on the outcome of capturing a report in the exception\n  //!     handler. Should be called on all capture completion paths.\n  static void ExceptionCaptureResult(CaptureResult result);\n\n  //! \\brief The exception code for an exception was retrieved.\n  //!\n  //! These values are OS-specific, and correspond to\n  //! MINIDUMP_EXCEPTION::ExceptionCode.\n  static void ExceptionCode(uint32_t exception_code);\n\n  //! \\brief The exception handler server started capturing an exception.\n  static void ExceptionEncountered();\n\n  //! \\brief An important event in a handler process’ lifetime.\n  //!\n  //! \\note These are used as metrics enumeration values, so new values should\n  //!     always be added at the end, before LifetimeMilestone::kMaxValue.\n  enum class LifetimeMilestone : int32_t {\n    //! \\brief The handler process started.\n    kStarted = 0,\n\n    //! \\brief The handler process exited normally and cleanly.\n    kExitedNormally,\n\n    //! \\brief The handler process exited early, but was successful in\n    //!     performing some non-default action on user request.\n    kExitedEarly,\n\n    //! \\brief The handler process exited with a failure code.\n    kFailed,\n\n    //! \\brief The handler process was forcibly terminated.\n    kTerminated,\n\n    //! \\brief The handler process crashed.\n    kCrashed,\n\n    //! \\brief The number of values in this enumeration; not a valid value.\n    kMaxValue\n  };\n\n  //! \\brief Records a handler start/exit/crash event.\n  static void HandlerLifetimeMilestone(LifetimeMilestone milestone);\n\n  //! \\brief The handler process crashed with the given exception code.\n  //!\n  //! This is currently only reported on Windows.\n  static void HandlerCrashed(uint32_t exception_code);\n\n#if BUILDFLAG(IS_IOS) || DOXYGEN\n  //! \\brief Records a missing key from an intermediate dump.\n  static void MissingIntermediateDumpKey(\n      const internal::IntermediateDumpKey& key);\n\n  //! \\brief Records a key with an invalid key size from an intermediate dump.\n  static void InvalidIntermediateDumpKeySize(\n      const internal::IntermediateDumpKey& key);\n#endif\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_METRICS_H_\n"
  },
  {
    "path": "util/misc/no_cfi_icall.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_NO_CFI_ICALL_H_\n#define CRASHPAD_UTIL_MISC_NO_CFI_ICALL_H_\n\n#include <type_traits>\n#include <utility>\n\n#include \"base/compiler_specific.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif  // BUILDFLAG(IS_WIN)\n\nnamespace crashpad {\n\nnamespace {\n\ntemplate <typename Functor>\nstruct FunctorTraits;\n\ntemplate <typename R, typename... Args>\nstruct FunctorTraits<R (*)(Args...) noexcept> {\n  template <typename Function, typename... RunArgs>\n  DISABLE_CFI_ICALL static R Invoke(Function&& function, RunArgs&&... args) {\n    return std::forward<Function>(function)(std::forward<RunArgs>(args)...);\n  }\n};\n\ntemplate <typename R, typename... Args>\nstruct FunctorTraits<R (*)(Args..., ...) noexcept> {\n  template <typename Function, typename... RunArgs>\n  DISABLE_CFI_ICALL static R Invoke(Function&& function, RunArgs&&... args) {\n    return std::forward<Function>(function)(std::forward<RunArgs>(args)...);\n  }\n};\n\n#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86)\ntemplate <typename R, typename... Args>\nstruct FunctorTraits<R(__stdcall*)(Args...) noexcept> {\n  template <typename... RunArgs>\n  DISABLE_CFI_ICALL static R Invoke(R(__stdcall* function)(Args...),\n                                    RunArgs&&... args) {\n    return function(std::forward<RunArgs>(args)...);\n  }\n};\n#endif  // BUILDFLAG(IS_WIN) && ARCH_CPU_X86\n\n#if __cplusplus >= 201703L\n// These specializations match functions which are not explicitly declared\n// noexcept. They must only be present at C++17 when noexcept is part of a\n// function's type. If they are present earlier, they redefine the\n// specializations above.\ntemplate <typename R, typename... Args>\nstruct FunctorTraits<R (*)(Args...)> {\n  template <typename Function, typename... RunArgs>\n  DISABLE_CFI_ICALL static R Invoke(Function&& function, RunArgs&&... args) {\n    return std::forward<Function>(function)(std::forward<RunArgs>(args)...);\n  }\n};\n\ntemplate <typename R, typename... Args>\nstruct FunctorTraits<R (*)(Args..., ...)> {\n  template <typename Function, typename... RunArgs>\n  DISABLE_CFI_ICALL static R Invoke(Function&& function, RunArgs&&... args) {\n    return std::forward<Function>(function)(std::forward<RunArgs>(args)...);\n  }\n};\n\n#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86)\ntemplate <typename R, typename... Args>\nstruct FunctorTraits<R(__stdcall*)(Args...)> {\n  template <typename... RunArgs>\n  DISABLE_CFI_ICALL static R Invoke(R(__stdcall* function)(Args...),\n                                    RunArgs&&... args) {\n    return function(std::forward<RunArgs>(args)...);\n  }\n};\n#endif  // BUILDFLAG(IS_WIN) && ARCH_CPU_X86\n\n#endif  // __cplusplus >= 201703L\n\n}  // namespace\n\n//! \\brief Disables cfi-icall for calls made through a function pointer.\n//!\n//! Clang provides several Control-Flow-Integrity (CFI) sanitizers, among them,\n//! cfi-icall, which attempts to verify that the dynamic type of a function\n//! matches the static type of the function pointer used to call it.\n//!\n//! https://clang.llvm.org/docs/ControlFlowIntegrity.html#indirect-function-call-checking\n//!\n//! However, cfi-icall does not have enough information to check indirect calls\n//! to functions in other modules, such as through the pointers returned by\n//! `dlsym()`. In these cases, CFI aborts the program upon executing the\n//! indirect call.\n//!\n//! This class encapsulates cross-DSO function pointers to disable cfi-icall\n//! precisely when calling these pointers.\ntemplate <typename Functor>\nclass NoCfiIcall {\n public:\n  //! \\brief Constructs this object.\n  //!\n  //! \\param function A pointer to the function to be called.\n  explicit NoCfiIcall(Functor function) : function_(function) {}\n\n  //! \\see NoCfiIcall\n  NoCfiIcall() : function_(static_cast<Functor>(nullptr)) {}\n\n  //! \\see NoCfiIcall\n  template <typename PointerType,\n            typename = std::enable_if_t<\n                std::is_same<typename std::remove_cv<PointerType>::type,\n                             void*>::value>>\n  explicit NoCfiIcall(PointerType function)\n      : function_(reinterpret_cast<Functor>(function)) {}\n\n#if BUILDFLAG(IS_WIN)\n  //! \\see NoCfiIcall\n  template <typename = std::enable_if_t<\n                !std::is_same<typename std::remove_cv<Functor>::type,\n                              FARPROC>::value>>\n  explicit NoCfiIcall(FARPROC function)\n      : function_(reinterpret_cast<Functor>(function)) {}\n#endif  // BUILDFLAG(IS_WIN)\n\n  ~NoCfiIcall() = default;\n\n  //! \\brief Updates the pointer to the function to be called.\n  //!\n  //! \\param function A pointer to the function to be called.\n  void SetPointer(Functor function) { function_ = function; }\n\n  //! \\see SetPointer\n  template <typename PointerType,\n            typename = std::enable_if_t<\n                std::is_same<typename std::remove_cv<PointerType>::type,\n                             void*>::value>>\n  void SetPointer(PointerType function) {\n    function_ = reinterpret_cast<Functor>(function);\n  }\n\n  //! \\brief Calls the function without sanitization by cfi-icall.\n  template <typename... RunArgs>\n  decltype(auto) operator()(RunArgs&&... args) const {\n    return FunctorTraits<Functor>::Invoke(function_,\n                                          std::forward<RunArgs>(args)...);\n  }\n\n  //! \\brief Returns `true` if not `nullptr`.\n  operator bool() const { return function_ != nullptr; }\n\n private:\n  Functor function_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_NO_CFI_ICALL_H_\n"
  },
  {
    "path": "util/misc/no_cfi_icall_test.cc",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/no_cfi_icall.h\"\n\n#include <stdio.h>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n\n#include \"util/win/get_function.h\"\n#else\n#include <dlfcn.h>\n#include <sys/types.h>\n#include <unistd.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(NoCfiIcall, NullptrIsFalse) {\n  NoCfiIcall<void (*)(void) noexcept> call(nullptr);\n  ASSERT_FALSE(call);\n}\n\nint TestFunc() noexcept {\n  return 42;\n}\n\nTEST(NoCfiIcall, SameDSOICall) {\n  NoCfiIcall<decltype(TestFunc)*> call(&TestFunc);\n  ASSERT_TRUE(call);\n  ASSERT_EQ(call(), 42);\n}\n\nTEST(NoCfiIcall, CrossDSOICall) {\n#if BUILDFLAG(IS_WIN)\n  static const NoCfiIcall<decltype(GetCurrentProcessId)*> call(\n      GET_FUNCTION_REQUIRED(L\"kernel32.dll\", GetCurrentProcessId));\n  ASSERT_TRUE(call);\n  EXPECT_EQ(call(), GetCurrentProcessId());\n#else\n  static const NoCfiIcall<decltype(getpid)*> call(dlsym(RTLD_NEXT, \"getpid\"));\n  ASSERT_TRUE(call);\n  EXPECT_EQ(call(), getpid());\n#endif\n}\n\nTEST(NoCfiIcall, Args) {\n#if !BUILDFLAG(IS_WIN)\n  static const NoCfiIcall<decltype(snprintf)*> call(\n      dlsym(RTLD_NEXT, \"snprintf\"));\n  ASSERT_TRUE(call);\n\n  char buf[1024];\n\n  // Regular args.\n  memset(buf, 0, sizeof(buf));\n  ASSERT_GT(call(buf, sizeof(buf), \"Hello!\"), 0);\n  EXPECT_STREQ(buf, \"Hello!\");\n\n  // Variadic args.\n  memset(buf, 0, sizeof(buf));\n  ASSERT_GT(call(buf, sizeof(buf), \"%s, %s!\", \"Hello\", \"World\"), 0);\n  EXPECT_STREQ(buf, \"Hello, World!\");\n#endif\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/paths.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PATHS_H_\n#define CRASHPAD_UTIL_PATHS_H_\n\n#include \"base/files/file_path.h\"\n\nnamespace crashpad {\n\n//! \\brief Functions to obtain paths.\nclass Paths {\n public:\n  Paths() = delete;\n  Paths(const Paths&) = delete;\n  Paths& operator=(const Paths&) = delete;\n\n  //! \\brief Obtains the pathname of the currently-running executable.\n  //!\n  //! \\param[out] path The pathname of the currently-running executable.\n  //!\n  //! \\return `true` on success. `false` on failure, with a message logged.\n  //!\n  //! \\note In test code, use test::TestPaths::Executable() instead.\n  static bool Executable(base::FilePath* path);\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_TEST_PATHS_H_\n"
  },
  {
    "path": "util/misc/paths_fuchsia.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/paths.h\"\n\n#include <sys/stat.h>\n#include <zircon/process.h>\n#include <zircon/syscalls.h>\n\n\nnamespace crashpad {\n\n// static\nbool Paths::Executable(base::FilePath* path) {\n  // Assume the environment has been set up following\n  // https://fuchsia.googlesource.com/docs/+/master/the-book/namespaces.md#typical-directory-structure\n  // .\n  *path = base::FilePath(\"/pkg/bin/app\");\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/paths_linux.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/paths.h\"\n\n#include <limits.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <string>\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\n// static\nbool Paths::Executable(base::FilePath* path) {\n  // Linux does not provide a straightforward way to size the buffer before\n  // calling readlink(). Normally, the st_size field returned by lstat() could\n  // be used, but this is usually zero for things in /proc.\n  //\n  // The /proc filesystem does not provide any way to read “exe” links for\n  // pathnames longer than a page. See linux-4.9.20/fs/proc/base.c\n  // do_proc_readlink(), which allocates a single page to receive the path\n  // string. Coincidentally, the page size and PATH_MAX are normally the same\n  // value, although neither is strictly a limit on the length of a pathname.\n  //\n  // On Android, the smaller of the page size and PATH_MAX actually does serve\n  // as an effective limit on the length of an executable’s pathname. See\n  // Android 7.1.1 bionic/linker/linker.cpp get_executable_path(), which aborts\n  // via __libc_fatal() if the “exe” link can’t be read into a PATH_MAX-sized\n  // buffer.\n  std::string exe_path(std::max(getpagesize(), PATH_MAX),\n                       std::string::value_type());\n  ssize_t exe_path_len =\n      readlink(\"/proc/self/exe\", &exe_path[0], exe_path.size());\n  if (exe_path_len < 0) {\n    PLOG(ERROR) << \"readlink\";\n    return false;\n  } else if (static_cast<size_t>(exe_path_len) >= exe_path.size()) {\n    LOG(ERROR) << \"readlink\";\n    return false;\n  }\n\n  exe_path.resize(exe_path_len);\n  *path = base::FilePath(exe_path);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/paths_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/paths.h\"\n\n#include <mach-o/dyld.h>\n#include <stdint.h>\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\n// static\nbool Paths::Executable(base::FilePath* path) {\n  uint32_t executable_length = 0;\n  _NSGetExecutablePath(nullptr, &executable_length);\n  if (executable_length <= 1) {\n    LOG(ERROR) << \"_NSGetExecutablePath\";\n    return false;\n  }\n\n  std::string executable_path(executable_length - 1, std::string::value_type());\n  int rv = _NSGetExecutablePath(&executable_path[0], &executable_length);\n  if (rv != 0) {\n    LOG(ERROR) << \"_NSGetExecutablePath\";\n    return false;\n  }\n\n  *path = base::FilePath(executable_path);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/paths_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/paths.h\"\n\n#include \"base/files/file_path.h\"\n#include \"gtest/gtest.h\"\n#include \"test/test_paths.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Paths, Executable) {\n  base::FilePath executable_path;\n  ASSERT_TRUE(Paths::Executable(&executable_path));\n  const base::FilePath executable_name(executable_path.BaseName());\n  const base::FilePath expected_name(TestPaths::ExpectedExecutableBasename(\n      FILE_PATH_LITERAL(\"crashpad_util_test\")));\n\n  EXPECT_EQ(executable_name.value(), expected_name.value());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/paths_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/paths.h\"\n\n#include <windows.h>\n\n#include <iterator>\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\n// static\nbool Paths::Executable(base::FilePath* path) {\n  wchar_t executable_path[_MAX_PATH];\n  unsigned int len = GetModuleFileName(\n      nullptr, executable_path, static_cast<DWORD>(std::size(executable_path)));\n  if (len == 0) {\n    PLOG(ERROR) << \"GetModuleFileName\";\n    return false;\n  } else if (len >= std::size(executable_path)) {\n    LOG(ERROR) << \"GetModuleFileName\";\n    return false;\n  }\n\n  *path = base::FilePath(executable_path);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/pdb_structures.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/pdb_structures.h\"\n\nnamespace crashpad {\n\nconst uint32_t CodeViewRecordPDB20::kSignature;\nconst uint32_t CodeViewRecordPDB70::kSignature;\nconst uint32_t CodeViewRecordBuildID::kSignature;\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/pdb_structures.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_\n#define CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_\n\n#include <stdint.h>\n\n#include \"util/misc/uuid.h\"\n\nnamespace crashpad {\n\n//! \\brief A CodeView record linking to a `.pdb` 2.0 file.\n//!\n//! This format provides an indirect link to debugging data by referencing an\n//! external `.pdb` file by its name, timestamp, and age. This structure may be\n//! pointed to by MINIDUMP_MODULE::CvRecord. It has been superseded by\n//! CodeViewRecordPDB70.\n//!\n//! For more information about this structure and format, see <a\n//! href=\"http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles\">Matching\n//! Debug Information</a>, PDB Files, and <i>Undocumented Windows 2000\n//! Secrets</i>, Windows 2000 Debugging Support/Microsoft Symbol File\n//! Internals/CodeView Subsections.\n//!\n//! \\sa IMAGE_DEBUG_MISC\nstruct CodeViewRecordPDB20 {\n  //! \\brief The magic number identifying this structure version, stored in\n  //!     #signature.\n  //!\n  //! In a hex dump, this will appear as “NB10” when produced by a little-endian\n  //! machine.\n  static const uint32_t kSignature = '01BN';\n\n  //! \\brief The magic number identifying this structure version, the value of\n  //!     #kSignature.\n  uint32_t signature;\n\n  //! \\brief The offset to CodeView data.\n  //!\n  //! In this structure, this field always has the value `0` because no CodeView\n  //! data is present, there is only a link to CodeView data stored in an\n  //! external file.\n  uint32_t offset;\n\n  //! \\brief The time that the `.pdb` file was created, in `time_t` format, the\n  //!     number of seconds since the POSIX epoch.\n  uint32_t timestamp;\n\n  //! \\brief The revision of the `.pdb` file.\n  //!\n  //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb`\n  //! file is created, it has age `1`, and subsequent updates increase this\n  //! value.\n  uint32_t age;\n\n  //! \\brief The path or file name of the `.pdb` file associated with the\n  //!     module.\n  //!\n  //! This is a NUL-terminated string. On Windows, it will be encoded in the\n  //! code page of the system that linked the module. On other operating\n  //! systems, UTF-8 may be used.\n  uint8_t pdb_name[1];\n};\n\n//! \\brief A CodeView record linking to a `.pdb` 7.0 file.\n//!\n//! This format provides an indirect link to debugging data by referencing an\n//! external `.pdb` file by its name, %UUID, and age. This structure may be\n//! pointed to by MINIDUMP_MODULE::CvRecord.\n//!\n//! For more information about this structure and format, see <a\n//! href=\"http://www.debuginfo.com/articles/debuginfomatch.html#pdbfiles\">Matching\n//! Debug Information</a>, PDB Files.\n//!\n//! \\sa CodeViewRecordPDB20\n//! \\sa IMAGE_DEBUG_MISC\nstruct CodeViewRecordPDB70 {\n  // UUID has a constructor, which makes it non-POD, which makes this structure\n  // non-POD. In order for the default constructor to zero-initialize other\n  // members, an explicit constructor must be provided.\n  CodeViewRecordPDB70() : signature(), uuid(), age(), pdb_name() {}\n\n  //! \\brief The magic number identifying this structure version, stored in\n  //!     #signature.\n  //!\n  //! In a hex dump, this will appear as “RSDS” when produced by a little-endian\n  //! machine.\n  static const uint32_t kSignature = 'SDSR';\n\n  //! \\brief The magic number identifying this structure version, the value of\n  //!     #kSignature.\n  uint32_t signature;\n\n  //! \\brief The `.pdb` file’s unique identifier.\n  UUID uuid;\n\n  //! \\brief The revision of the `.pdb` file.\n  //!\n  //! A `.pdb` file’s age indicates incremental changes to it. When a `.pdb`\n  //! file is created, it has age `1`, and subsequent updates increase this\n  //! value.\n  uint32_t age;\n\n  //! \\brief The path or file name of the `.pdb` file associated with the\n  //!     module.\n  //!\n  //! This is a NUL-terminated string. On Windows, it will be encoded in the\n  //! code page of the system that linked the module. On other operating\n  //! systems, UTF-8 may be used.\n  uint8_t pdb_name[1];\n};\n\n//! \\brief A CodeView record containing an ELF build-id.\n//!\n//! This identifier comes from the ELF section `NT_GNU_BUILD_ID`.\nstruct CodeViewRecordBuildID {\n  //! \\brief The magic number identifying this structure version, stored in\n  //!     #signature.\n  //!\n  //! In a hex dump, this will appear as “LEpB” when produced by a little-endian\n  //! machine.\n  static const uint32_t kSignature = 'BpEL';\n\n  //! \\brief The magic number identifying this structure version, the value of\n  //!     #kSignature.\n  uint32_t signature;\n\n  //! \\brief The build ID for this object.\n  //!\n  //! This usually comes from `NT_GNU_BUILD_ID` on ELF objects.\n  uint8_t build_id[1];\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_PDB_STRUCTURES_H_\n"
  },
  {
    "path": "util/misc/random_string.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/random_string.h\"\n\n#include \"base/rand_util.h\"\n\nnamespace crashpad {\n\nstd::string RandomString() {\n  std::string random_string;\n  for (int index = 0; index < 16; ++index) {\n    random_string.append(1, static_cast<char>(base::RandInt('A', 'Z')));\n  }\n  return random_string;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/random_string.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_RANDOM_STRING_H_\n#define CRASHPAD_UTIL_MISC_RANDOM_STRING_H_\n\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief Returns a random string.\n//!\n//! The string consists of 16 uppercase characters chosen at random. The\n//! returned string has over 75 bits of randomness (26<sup>16</sup> &gt;\n//! 2<sup>75</sup>).\nstd::string RandomString();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_RANDOM_STRING_H_\n"
  },
  {
    "path": "util/misc/random_string_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/random_string.h\"\n\n#include <sys/types.h>\n\n#include <iterator>\n#include <set>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(RandomString, RandomString) {\n  // Explicitly list the allowed characters, rather than relying on a range.\n  // This prevents the test from having any dependency on the character set, so\n  // that the implementation is free to assume all uppercase letters are\n  // contiguous as in ASCII.\n  const std::string allowed_characters(\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\");\n\n  size_t character_counts[26] = {};\n  ASSERT_EQ(allowed_characters.size(), std::size(character_counts));\n\n  std::set<std::string> strings;\n\n  for (size_t i = 0; i < 256; ++i) {\n    const std::string random_string = RandomString();\n    EXPECT_EQ(random_string.size(), 16u);\n\n    // Make sure that the string is unique. It is possible, but extremely\n    // unlikely, for there to be collisions.\n    auto result = strings.insert(random_string);\n    EXPECT_TRUE(result.second) << random_string;\n\n    for (char c : random_string) {\n      size_t character_index = allowed_characters.find(c);\n\n      // Make sure that no unexpected characters appear.\n      EXPECT_NE(character_index, std::string::npos) << c;\n\n      if (character_index != std::string::npos) {\n        ++character_counts[character_index];\n      }\n    }\n  }\n\n  // Make sure every character appears at least once. It is possible, but\n  // extremely unlikely, for a character to not appear at all.\n  for (size_t character_index = 0;\n       character_index < std::size(character_counts);\n       ++character_index) {\n    EXPECT_GT(character_counts[character_index], 0u)\n        << allowed_characters[character_index];\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/range_set.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/range_set.h\"\n\n#include <algorithm>\n\nnamespace crashpad {\n\nRangeSet::RangeSet() = default;\n\nRangeSet::~RangeSet() = default;\n\nvoid RangeSet::Insert(VMAddress base, VMSize size) {\n  if (!size) {\n    return;\n  }\n\n  VMAddress last = base + size - 1;\n\n  auto overlapping_range = ranges_.lower_bound(base);\n#define OVERLAPPING_RANGES_BASE overlapping_range->second\n#define OVERLAPPING_RANGES_LAST overlapping_range->first\n  while (overlapping_range != ranges_.end() &&\n         OVERLAPPING_RANGES_BASE <= last) {\n    base = std::min(base, OVERLAPPING_RANGES_BASE);\n    last = std::max(last, OVERLAPPING_RANGES_LAST);\n    auto tmp = overlapping_range;\n    ++overlapping_range;\n    ranges_.erase(tmp);\n  }\n#undef OVERLAPPING_RANGES_BASE\n#undef OVERLAPPING_RANGES_LAST\n\n  ranges_[last] = base;\n}\n\nbool RangeSet::Contains(VMAddress address) const {\n  auto range_above_address = ranges_.lower_bound(address);\n  return range_above_address != ranges_.end() &&\n         range_above_address->second <= address;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/range_set.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_RANGE_SET_H_\n#define CRASHPAD_UTIL_MISC_RANGE_SET_H_\n\n#include <map>\n\n#include \"util/misc/address_types.h\"\n\nnamespace crashpad {\n\n//! \\brief A set of VMAddress ranges.\nclass RangeSet {\n public:\n  RangeSet();\n\n  RangeSet(const RangeSet&) = delete;\n  RangeSet& operator=(const RangeSet&) = delete;\n\n  ~RangeSet();\n\n  //! \\brief Inserts a range into the set.\n  //!\n  //! \\param[in] base The low address of the range.\n  //! \\param[in] size The size of the range.\n  void Insert(VMAddress base, VMSize size);\n\n  //! \\brief Returns `true` if \\a address falls within a range in this set.\n  bool Contains(VMAddress address) const;\n\n private:\n  // Keys are the highest address in the range. Values are the base address of\n  // the range. Overlapping ranges are merged on insertion. Adjacent ranges may\n  // be merged.\n  std::map<VMAddress, VMAddress> ranges_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_RANGE_SET_H_\n"
  },
  {
    "path": "util/misc/range_set_test.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/range_set.h\"\n\n#include <sys/types.h>\n\n#include <memory>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/from_pointer_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid ExpectRangeIsContained(const RangeSet& ranges,\n                            VMAddress base,\n                            VMSize size) {\n  for (VMAddress addr = base; addr < base + size; ++addr) {\n    SCOPED_TRACE(base::StringPrintf(\"0x%\" PRIx64 \" in range 0x%\" PRIx64\n                                    \":0x%\" PRIx64,\n                                    addr,\n                                    base,\n                                    base + size));\n    EXPECT_TRUE(ranges.Contains(addr));\n  }\n}\n\nTEST(RangeSet, Basic) {\n  RangeSet ranges;\n  auto base = FromPointerCast<VMAddress>(&ranges);\n  VMSize size = sizeof(ranges);\n  ranges.Insert(base, size);\n  ExpectRangeIsContained(ranges, base, size);\n  EXPECT_FALSE(ranges.Contains(base - 1));\n  EXPECT_FALSE(ranges.Contains(base + size));\n}\n\nTEST(RangeSet, ZeroSizedRange) {\n  RangeSet ranges;\n  auto addr = FromPointerCast<VMAddress>(&ranges);\n  ranges.Insert(addr, 0);\n  EXPECT_FALSE(ranges.Contains(addr));\n}\n\nTEST(RangeSet, DuplicateRanges) {\n  RangeSet ranges;\n  auto base = FromPointerCast<VMAddress>(&ranges);\n  VMSize size = sizeof(ranges);\n  ranges.Insert(base, size);\n  ranges.Insert(base, size);\n  ExpectRangeIsContained(ranges, base, size);\n}\n\nTEST(RangeSet, OverlappingRanges) {\n  RangeSet ranges;\n  ranges.Insert(37, 16);\n  ranges.Insert(9, 9);\n  ranges.Insert(17, 42);\n\n  EXPECT_TRUE(ranges.Contains(9));\n  EXPECT_TRUE(ranges.Contains(17));\n  EXPECT_TRUE(ranges.Contains(36));\n  EXPECT_TRUE(ranges.Contains(37));\n  EXPECT_TRUE(ranges.Contains(52));\n  EXPECT_TRUE(ranges.Contains(58));\n}\n\nTEST(RangeSet, SubRangeInLargeRange) {\n  constexpr size_t kBufferSize = 2 << 22;\n  auto buf = std::make_unique<char[]>(kBufferSize);\n\n  RangeSet ranges;\n  auto addr = FromPointerCast<VMAddress>(buf.get());\n\n  ranges.Insert(addr, kBufferSize);\n  EXPECT_TRUE(ranges.Contains(addr));\n  EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1));\n\n  ranges.Insert(addr, kBufferSize / 2);\n  EXPECT_TRUE(ranges.Contains(addr));\n  EXPECT_TRUE(ranges.Contains(addr + kBufferSize / 2 - 1));\n  EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1));\n}\n\nTEST(RangeSet, LargeOverlappingRanges) {\n  constexpr size_t kBufferSize = 2 << 23;\n  auto buf = std::make_unique<char[]>(kBufferSize);\n\n  RangeSet ranges;\n  auto addr = FromPointerCast<VMAddress>(buf.get());\n\n  ranges.Insert(addr, 3 * kBufferSize / 4);\n  ranges.Insert(addr + kBufferSize / 4, 3 * kBufferSize / 4);\n  EXPECT_TRUE(ranges.Contains(addr));\n  EXPECT_TRUE(ranges.Contains(addr + kBufferSize - 1));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/reinterpret_bytes.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/reinterpret_bytes.h\"\n\n#include <string.h>\n\n#include <algorithm>\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nbool ReinterpretBytesImpl(const char* data,\n                          size_t data_size,\n                          char* dest,\n                          size_t dest_size) {\n  // Verify that any unused bytes from data are zero.\n  // The unused bytes are at the start of the data buffer for big-endian and the\n  // end of the buffer for little-endian.\n  if (dest_size < data_size) {\n    auto extra_bytes = data;\n#if defined(ARCH_CPU_LITTLE_ENDIAN)\n    extra_bytes += dest_size;\n#endif  // ARCH_CPU_LITTLE_ENDIAN\n\n    uint64_t zero = 0;\n    size_t extra_bytes_size = data_size - dest_size;\n    while (extra_bytes_size > 0) {\n      size_t to_check = std::min(extra_bytes_size, sizeof(zero));\n      if (memcmp(extra_bytes, &zero, to_check) != 0) {\n        LOG(ERROR) << \"information loss\";\n        return false;\n      }\n      extra_bytes += to_check;\n      extra_bytes_size -= to_check;\n    }\n  }\n\n  // Zero out the destination, in case it is larger than data.\n  memset(dest, 0, dest_size);\n\n#if defined(ARCH_CPU_LITTLE_ENDIAN)\n  // Copy a prefix of data to a prefix of dest for little-endian\n  memcpy(dest, data, std::min(dest_size, data_size));\n#else\n  // or the suffix of data to the suffix of dest for big-endian\n  if (data_size >= dest_size) {\n    memcpy(dest, data + data_size - dest_size, dest_size);\n  } else {\n    memcpy(dest + dest_size - data_size, data, data_size);\n  }\n#endif  // ARCH_CPU_LITTLE_ENDIAN\n  return true;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/reinterpret_bytes.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_REINTERPRET_BYTES_H_\n#define CRASHPAD_UTIL_MISC_REINTERPRET_BYTES_H_\n\n#include <stddef.h>\n\nnamespace crashpad {\n\nnamespace internal {\n\nbool ReinterpretBytesImpl(const char* from,\n                          size_t from_size,\n                          char* to,\n                          size_t to_size);\n\n}  // namespace internal\n\n//! \\brief Copies the bytes of \\a from to \\a to.\n//!\n//! This function is similar to `bit_cast`, except that it can operate on\n//! differently sized types.\n//!\n//! \\return `true` if the copy is possible without information loss, otherwise\n//!     `false` with a message logged.\ntemplate <typename From, typename To>\nbool ReinterpretBytes(const From& from, To* to) {\n  return internal::ReinterpretBytesImpl(reinterpret_cast<const char*>(&from),\n                                        sizeof(From),\n                                        reinterpret_cast<char*>(to),\n                                        sizeof(To));\n}\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_REINTERPRET_BYTES_H_\n"
  },
  {
    "path": "util/misc/reinterpret_bytes_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/reinterpret_bytes.h\"\n\n#include <stdint.h>\n\n#include <limits>\n\n#include \"base/bit_cast.h\"\n#include \"gtest/gtest.h\"\n#include \"util/numeric/int128.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\ntemplate <typename From, typename To>\nvoid ExpectReinterpret(From from, To* to, To expected) {\n  ASSERT_TRUE(ReinterpretBytes(from, to));\n  EXPECT_EQ(*to, expected);\n}\n\ntemplate <typename From, typename To>\nvoid ExpectUnsignedEqual(From from, To* to) {\n  To expected = static_cast<To>(from);\n  ExpectReinterpret(from, to, expected);\n}\n\nTEST(ReinterpretBytes, ToUnsigned) {\n  uint64_t from64, to64;\n  uint32_t from32, to32;\n\n  from32 = 0;\n  ExpectUnsignedEqual(from32, &to32);\n  ExpectUnsignedEqual(from32, &to64);\n\n  from32 = std::numeric_limits<uint32_t>::max();\n  ExpectUnsignedEqual(from32, &to32);\n  ExpectUnsignedEqual(from32, &to64);\n\n  from64 = 0;\n  ExpectUnsignedEqual(from64, &to32);\n  ExpectUnsignedEqual(from64, &to64);\n\n  from64 = std::numeric_limits<uint64_t>::max();\n  ExpectUnsignedEqual(from64, &to64);\n  EXPECT_FALSE(ReinterpretBytes(from64, &to32));\n\n  uint8_t to8 = std::numeric_limits<uint8_t>::max();\n  uint128_struct from128;\n  from128.lo = to8;\n  from128.hi = 0;\n  ExpectReinterpret(from128, &to8, to8);\n}\n\nTEST(ReinterpretBytes, ToSigned) {\n  uint64_t from64;\n  int64_t to64;\n  int32_t to32;\n\n  from64 = 0;\n  ExpectReinterpret(from64, &to32, static_cast<int32_t>(0));\n  ExpectReinterpret(from64, &to64, static_cast<int64_t>(0));\n\n  to32 = -1;\n  from64 = base::bit_cast<uint32_t>(to32);\n  ExpectReinterpret(from64, &to32, to32);\n\n  to32 = std::numeric_limits<int32_t>::max();\n  from64 = base::bit_cast<uint32_t>(to32);\n  ExpectReinterpret(from64, &to32, to32);\n\n  to32 = std::numeric_limits<int32_t>::min();\n  from64 = base::bit_cast<uint32_t>(to32);\n  ExpectReinterpret(from64, &to32, to32);\n\n  to64 = -1;\n  from64 = base::bit_cast<uint64_t>(to64);\n  ExpectReinterpret(from64, &to64, to64);\n\n  to64 = std::numeric_limits<int64_t>::max();\n  from64 = base::bit_cast<uint64_t>(to64);\n  ExpectReinterpret(from64, &to64, to64);\n\n  to64 = std::numeric_limits<int64_t>::min();\n  from64 = base::bit_cast<uint64_t>(to64);\n  ExpectReinterpret(from64, &to64, to64);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/scoped_forbid_return.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/scoped_forbid_return.h\"\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nScopedForbidReturn::~ScopedForbidReturn() {\n  if (armed_) {\n    LOG(FATAL) << \"attempt to exit scope forbidden\";\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/scoped_forbid_return.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_\n#define CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_\n\n\nnamespace crashpad {\n\n//! \\brief Asserts that a scope must not be exited while unsafe.\n//!\n//! An object of this class has two states: armed and disarmed. A disarmed\n//! object is a harmless no-op. An armed object will abort execution upon\n//! destruction. Newly-constructed objects are armed by default.\n//!\n//! These objects may be used to assert that a scope not be exited while it is\n//! unsafe to do so. If it ever becomes safe to leave such a scope, an object\n//! can be disarmed.\nclass ScopedForbidReturn {\n public:\n  ScopedForbidReturn() : armed_(true) {}\n\n  ScopedForbidReturn(const ScopedForbidReturn&) = delete;\n  ScopedForbidReturn& operator=(const ScopedForbidReturn&) = delete;\n\n  ~ScopedForbidReturn();\n\n  //! \\brief Arms the object so that it will abort execution when destroyed.\n  //!\n  //! The most recent call to Arm() or Disarm() sets the state of the object.\n  void Arm() { armed_ = true; }\n\n  //! \\brief Arms the object so that it will abort execution when destroyed.\n  //!\n  //! The most recent call to Arm() or Disarm() sets the state of the object.\n  void Disarm() { armed_ = false; }\n\n private:\n  bool armed_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_SCOPED_FORBID_RETURN_H_\n"
  },
  {
    "path": "util/misc/scoped_forbid_return_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/scoped_forbid_return.h\"\n\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nenum ForbidReturnType {\n  kForbidReturnDefault = 0,\n  kForbidReturnArmed,\n  kForbidReturnDisarmed,\n};\n\nvoid ScopedForbidReturnHelper(ForbidReturnType type) {\n  ScopedForbidReturn forbid_return;\n\n  switch (type) {\n    case kForbidReturnDefault:\n      break;\n    case kForbidReturnArmed:\n      forbid_return.Arm();\n      break;\n    case kForbidReturnDisarmed:\n      forbid_return.Disarm();\n      break;\n  }\n}\n\n// kForbiddenMessage may appear to be unused if ASSERT_DEATH_CHECK() throws it\n// away.\n[[maybe_unused]] constexpr char kForbiddenMessage[] =\n    \"attempt to exit scope forbidden\";\n\nTEST(ScopedForbidReturnDeathTest, Default) {\n  ASSERT_DEATH_CHECK(ScopedForbidReturnHelper(kForbidReturnDefault),\n                     kForbiddenMessage);\n}\n\nTEST(ScopedForbidReturnDeathTest, Armed) {\n  ASSERT_DEATH_CHECK(ScopedForbidReturnHelper(kForbidReturnArmed),\n                     kForbiddenMessage);\n}\n\nTEST(ScopedForbidReturn, Disarmed) {\n  ScopedForbidReturnHelper(kForbidReturnDisarmed);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/symbolic_constants_common.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_\n#define CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_\n\n//! \\file\n//!\n//! \\anchor symbolic_constant_terminology\n//! Symbolic constant terminology\n//! =============================\n//! <dl>\n//!   <dt>Family</dt>\n//!   <dd>A group of related symbolic constants. Typically, within a single\n//!       family, one function will be used to transform a numeric value to a\n//!       string equivalent, and another will perform the inverse operation.\n//!       Families include POSIX signals and Mach exception masks.</dd>\n//!   <dt>Full name</dt>\n//!   <dd>The normal symbolic name used for a constant. For example, in the\n//!       family of POSIX signals, the strings `\"SIGHUP\"` and `\"SIGSEGV\"` are\n//!       full names.</dd>\n//!   <dt>Short name</dt>\n//!   <dd>An abbreviated form of symbolic name used for a constant. Short names\n//!       vary between families, but are commonly constructed by removing a\n//!       common prefix from full names. For example, in the family of POSIX\n//!       signals, the prefix is `SIG`, and short names include `\"HUP\"` and\n//!       `\"SEGV\"`.</dd>\n//!   <dt>Numeric string</dt>\n//!   <dd>A string that does not contain a full or short name, but contains a\n//!       numeric value that can be interpreted as a symbolic constant. For\n//!       example, in the family of POSIX signals, `SIGKILL` generally has value\n//!       `9`, so the numeric string `\"9\"` would be interpreted equivalently to\n//!       `\"SIGKILL\"`.</dd>\n//! </dl>\n\nnamespace crashpad {\n\n//! \\brief Options for various `*ToString` functions in `symbolic_constants_*`\n//!     files.\n//!\n//! \\sa \\ref symbolic_constant_terminology \"Symbolic constant terminology\"\nenum SymbolicConstantToStringOptionBits {\n  //! \\brief Return the full name for a given constant.\n  //!\n  //! \\attention API consumers should provide this value when desired, but\n  //!     should provide only one of kUseFullName and ::kUseShortName. Because\n  //!     kUseFullName is valueless, implementers should check for the absence\n  //!     of ::kUseShortName instead.\n  kUseFullName = 0 << 0,\n\n  //! \\brief Return the short name for a given constant.\n  kUseShortName = 1 << 0,\n\n  //! \\brief If no symbolic name is known for a given constant, return an empty\n  //!     string.\n  //!\n  //! \\attention API consumers should provide this value when desired, but\n  //!     should provide only one of kUnknownIsEmpty and ::kUnknownIsNumeric.\n  //!     Because kUnknownIsEmpty is valueless, implementers should check for\n  //!     the absence of ::kUnknownIsNumeric instead.\n  kUnknownIsEmpty = 0 << 1,\n\n  //! \\brief If no symbolic name is known for a given constant, return a numeric\n  //!     string.\n  //!\n  //! The numeric format used will vary by family, but will be appropriate to\n  //! the family. Families whose values are typically constructed as bitfields\n  //! will generally use a hexadecimal format, and other families will generally\n  //! use a signed or unsigned decimal format.\n  kUnknownIsNumeric = 1 << 1,\n\n  //! \\brief Use `|` to combine values in a bitfield.\n  //!\n  //! For families whose values may be constructed as bitfields, allow\n  //! conversion to strings containing multiple individual components treated as\n  //! being combined by a bitwise “or” operation. An example family of constants\n  //! that behaves this way is the suite of Mach exception masks. For constants\n  //! that are not constructed as bitfields, or constants that are only\n  //! partially constructed as bitfields, this option has no effect.\n  kUseOr = 1 << 2,\n};\n\n//! \\brief A bitfield containing values of #SymbolicConstantToStringOptionBits.\nusing SymbolicConstantToStringOptions = unsigned int;\n\n//! \\brief Options for various `StringTo*` functions in `symbolic_constants_*`\n//!     files.\n//!\n//! Not every `StringTo*` function will implement each of these options. See\n//! function-specific documentation for details.\n//!\n//! \\sa \\ref symbolic_constant_terminology \"Symbolic constant terminology\"\nenum StringToSymbolicConstantOptionBits {\n  //! \\brief Allow conversion from a string containing a symbolic constant by\n  //!     its full name.\n  kAllowFullName = 1 << 0,\n\n  //! \\brief Allow conversion from a string containing a symbolic constant by\n  //!     its short name.\n  kAllowShortName = 1 << 1,\n\n  //! \\brief Allow conversion from a numeric string.\n  kAllowNumber = 1 << 2,\n\n  //! \\brief Allow `|` to combine values in a bitfield.\n  //!\n  //! For families whose values may be constructed as bitfields, allow\n  //! conversion of strings containing multiple individual components treated as\n  //! being combined by a bitwise “or” operation. An example family of constants\n  //! that behaves this way is the suite of Mach exception masks. For constants\n  //! that are not constructed as bitfields, or constants that are only\n  //! partially constructed as bitfields, this option has no effect.\n  kAllowOr = 1 << 3,\n};\n\n//! \\brief A bitfield containing values of #StringToSymbolicConstantOptionBits.\nusing StringToSymbolicConstantOptions = unsigned int;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_SYMBOLIC_CONSTANTS_COMMON_H_\n"
  },
  {
    "path": "util/misc/time.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/time.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nvoid AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result) {\n  result->tv_sec = ts1.tv_sec + ts2.tv_sec;\n  result->tv_nsec = ts1.tv_nsec + ts2.tv_nsec;\n  if (result->tv_nsec >= long{kNanosecondsPerSecond}) {\n    ++result->tv_sec;\n    result->tv_nsec -= kNanosecondsPerSecond;\n  }\n}\n\nvoid SubtractTimespec(const timespec& t1,\n                      const timespec& t2,\n                      timespec* result) {\n  result->tv_sec = t1.tv_sec - t2.tv_sec;\n  result->tv_nsec = t1.tv_nsec - t2.tv_nsec;\n  if (result->tv_nsec < 0) {\n    result->tv_sec -= 1;\n    result->tv_nsec += kNanosecondsPerSecond;\n  }\n}\n\nbool TimespecToTimeval(const timespec& ts, timeval* tv) {\n  tv->tv_usec = ts.tv_nsec / 1000;\n\n  // timespec::tv_sec and timeval::tv_sec should generally both be of type\n  // time_t, however, on Windows, timeval::tv_sec is declared as a long, which\n  // may be smaller than a time_t.\n  return AssignIfInRange(&tv->tv_sec, ts.tv_sec);\n}\n\nvoid TimevalToTimespec(const timeval& tv, timespec* ts) {\n  ts->tv_sec = tv.tv_sec;\n  ts->tv_nsec = tv.tv_usec * 1000;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/time.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_TIME_H_\n#define CRASHPAD_UTIL_MISC_TIME_H_\n\n#include <stdint.h>\n#include <sys/time.h>\n#include <time.h>\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif\n\nnamespace crashpad {\n\nconstexpr uint64_t kNanosecondsPerSecond = static_cast<uint64_t>(1E9);\n\n//! \\brief Add `timespec` \\a ts1 and \\a ts2 and return the result in \\a result.\nvoid AddTimespec(const timespec& ts1, const timespec& ts2, timespec* result);\n\n//! \\brief Subtract `timespec` \\a ts2 from \\a ts1 and return the result in \\a\n//!     result.\nvoid SubtractTimespec(const timespec& ts1,\n                      const timespec& ts2,\n                      timespec* result);\n\n//! \\brief Convert the timespec \\a ts to a timeval \\a tv.\n//! \\return `true` if the assignment is possible without truncation.\nbool TimespecToTimeval(const timespec& ts, timeval* tv);\n\n//! \\brief Convert the timeval \\a tv to a timespec \\a ts.\nvoid TimevalToTimespec(const timeval& tv, timespec* ts);\n\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n\n//! \\brief Convert a `timespec` to a Windows `FILETIME`, converting from POSIX\n//!     epoch to Windows epoch.\nFILETIME TimespecToFiletimeEpoch(const timespec& ts);\n\n//! \\brief Convert a Windows `FILETIME` to `timespec`, converting from Windows\n//!     epoch to POSIX epoch.\ntimespec FiletimeToTimespecEpoch(const FILETIME& filetime);\n\n//! \\brief Convert Windows `FILETIME` to `timeval`, converting from Windows\n//!     epoch to POSIX epoch.\ntimeval FiletimeToTimevalEpoch(const FILETIME& filetime);\n\n//! \\brief Convert Windows `FILETIME` to `timeval`, treating the values as\n//!     an interval of elapsed time.\ntimeval FiletimeToTimevalInterval(const FILETIME& filetime);\n\n//! \\brief Similar to POSIX `gettimeofday()`, gets the current system time in\n//!     UTC.\nvoid GetTimeOfDay(timeval* tv);\n\n#endif  // BUILDFLAG(IS_WIN)\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \\\n    DOXYGEN\n//! \\brief Get the kernel boot time. Subsequent calls to this function may\n//!     return different results due to the system clock being changed or\n//!     imprecision in measuring the boot time.\n//! \\return `true` on success. Otherwise, `false` with a message logged.\nbool GetBootTime(timespec* ts);\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID) || DOXYGEN\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_TIME_H_\n"
  },
  {
    "path": "util/misc/time_linux.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/time.h\"\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nbool GetBootTime(timespec* boot_time) {\n  timespec uptime;\n  if (clock_gettime(CLOCK_BOOTTIME, &uptime) != 0) {\n    PLOG(ERROR) << \"clock_gettime\";\n    return false;\n  }\n\n  timespec current_time;\n  if (clock_gettime(CLOCK_REALTIME, &current_time) != 0) {\n    PLOG(ERROR) << \"clock_gettime\";\n    return false;\n  }\n\n  SubtractTimespec(current_time, uptime, boot_time);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/time_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/time.h\"\n\n#include <limits>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Time, TimespecArithmetic) {\n  timespec ts1, ts2, result;\n  ts1.tv_sec = ts2.tv_sec = 1;\n  ts1.tv_nsec = ts2.tv_nsec = kNanosecondsPerSecond / 2;\n  AddTimespec(ts1, ts2, &result);\n  EXPECT_EQ(result.tv_sec, 3);\n  EXPECT_EQ(result.tv_nsec, 0);\n\n  ts1.tv_sec = 2;\n  ts1.tv_nsec = 0;\n  ts2.tv_sec = 1;\n  ts2.tv_nsec = 1;\n  SubtractTimespec(ts1, ts2, &result);\n  EXPECT_EQ(result.tv_sec, 0);\n  EXPECT_EQ(result.tv_nsec, long{kNanosecondsPerSecond - 1});\n}\n\nTEST(Time, TimeConversions) {\n  // On July 30th, 2014 at 9:15 PM GMT+0, the Crashpad git repository was born.\n  // (nanoseconds are approximate)\n  constexpr timespec kCrashpadBirthdate = {\n    /* .tv_sec= */ 1406754914,\n    /* .tv_nsec= */ 32487\n  };\n\n  timeval timeval_birthdate;\n  ASSERT_TRUE(TimespecToTimeval(kCrashpadBirthdate, &timeval_birthdate));\n  EXPECT_EQ(timeval_birthdate.tv_sec, kCrashpadBirthdate.tv_sec);\n  EXPECT_EQ(timeval_birthdate.tv_usec, kCrashpadBirthdate.tv_nsec / 1000);\n\n  timespec timespec_birthdate;\n  TimevalToTimespec(timeval_birthdate, &timespec_birthdate);\n  EXPECT_EQ(timespec_birthdate.tv_sec, kCrashpadBirthdate.tv_sec);\n  EXPECT_EQ(timespec_birthdate.tv_nsec,\n            kCrashpadBirthdate.tv_nsec - (kCrashpadBirthdate.tv_nsec % 1000));\n\n  constexpr timespec kEndOfTime = {\n    /* .tv_sec= */ std::numeric_limits<decltype(timespec::tv_sec)>::max(),\n    /* .tv_nsec= */ 0\n  };\n\n  timeval end_of_timeval;\n  if (std::numeric_limits<decltype(timespec::tv_sec)>::max() >\n      std::numeric_limits<decltype(timeval::tv_sec)>::max()) {\n    EXPECT_FALSE(TimespecToTimeval(kEndOfTime, &end_of_timeval));\n  } else {\n    EXPECT_TRUE(TimespecToTimeval(kEndOfTime, &end_of_timeval));\n  }\n\n#if BUILDFLAG(IS_WIN)\n  constexpr uint64_t kBirthdateFiletimeIntervals = 130512285140000324;\n  FILETIME filetime_birthdate;\n  filetime_birthdate.dwLowDateTime = 0xffffffff & kBirthdateFiletimeIntervals;\n  filetime_birthdate.dwHighDateTime = kBirthdateFiletimeIntervals >> 32;\n\n  FILETIME filetime = TimespecToFiletimeEpoch(kCrashpadBirthdate);\n  EXPECT_EQ(filetime.dwLowDateTime, filetime_birthdate.dwLowDateTime);\n  EXPECT_EQ(filetime.dwHighDateTime, filetime_birthdate.dwHighDateTime);\n\n  timespec_birthdate = FiletimeToTimespecEpoch(filetime_birthdate);\n  EXPECT_EQ(timespec_birthdate.tv_sec, kCrashpadBirthdate.tv_sec);\n  EXPECT_EQ(timespec_birthdate.tv_nsec,\n            kCrashpadBirthdate.tv_nsec - kCrashpadBirthdate.tv_nsec % 100);\n\n  timeval_birthdate = FiletimeToTimevalEpoch(filetime_birthdate);\n  EXPECT_EQ(timeval_birthdate.tv_sec, kCrashpadBirthdate.tv_sec);\n  EXPECT_EQ(timeval_birthdate.tv_usec, kCrashpadBirthdate.tv_nsec / 1000);\n\n  FILETIME elapsed_filetime;\n  elapsed_filetime.dwLowDateTime = 0;\n  elapsed_filetime.dwHighDateTime = 0;\n  timeval elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime);\n  EXPECT_EQ(elapsed_timeval.tv_sec, 0);\n  EXPECT_EQ(elapsed_timeval.tv_usec, 0);\n\n  elapsed_filetime.dwLowDateTime = 9;\n  elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime);\n  EXPECT_EQ(elapsed_timeval.tv_sec, 0);\n  EXPECT_EQ(elapsed_timeval.tv_usec, 0);\n\n  elapsed_filetime.dwLowDateTime = 10;\n  elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime);\n  EXPECT_EQ(elapsed_timeval.tv_sec, 0);\n  EXPECT_EQ(elapsed_timeval.tv_usec, 1);\n\n  elapsed_filetime.dwHighDateTime = 1;\n  elapsed_filetime.dwLowDateTime = 0;\n  elapsed_timeval = FiletimeToTimevalInterval(elapsed_filetime);\n  EXPECT_EQ(elapsed_timeval.tv_sec, 429);\n  EXPECT_EQ(elapsed_timeval.tv_usec, 496729);\n#endif  // BUILDFLAG(IS_WIN)\n}\n\n#if BUILDFLAG(IS_WIN)\n\nTEST(Time, GetTimeOfDay) {\n  timeval t;\n  GetTimeOfDay(&t);\n  time_t approx_now = time(nullptr);\n  EXPECT_GE(approx_now, t.tv_sec);\n  EXPECT_LT(approx_now - 100, t.tv_sec);\n}\n\n#endif  // BUILDFLAG(IS_WIN)\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/time_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/time.h\"\n\n#include <stdint.h>\n\n#include \"base/check_op.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nconstexpr uint64_t kMicrosecondsPerSecond = static_cast<uint64_t>(1E6);\nconstexpr uint64_t kNanosecondsPerFiletimeInterval = static_cast<uint64_t>(100);\nconstexpr uint64_t kFiletimeIntervalsPerSecond =\n    kNanosecondsPerSecond / kNanosecondsPerFiletimeInterval;\nconstexpr uint64_t kFiletimeIntervalsPerMicrosecond =\n    kFiletimeIntervalsPerSecond / kMicrosecondsPerSecond;\n\n// Windows epoch is 1601-01-01, and FILETIME ticks are 100 nanoseconds.\n// 1601 to 1970 is 369 years + 89 leap days = 134774 days * 86400 seconds per\n// day. It's not entirely clear, but it appears that these are solar seconds,\n// not SI seconds, so there are no leap seconds to be considered.\nconstexpr uint64_t kNumSecondsFrom1601To1970 = (369 * 365 + 89) * 86400ULL;\n\nuint64_t FiletimeToMicroseconds(const FILETIME& filetime) {\n  uint64_t t = (static_cast<uint64_t>(filetime.dwHighDateTime) << 32) |\n               filetime.dwLowDateTime;\n  return t / kFiletimeIntervalsPerMicrosecond;\n}\n\ntimeval MicrosecondsToTimeval(uint64_t microseconds) {\n  timeval tv;\n  tv.tv_sec = static_cast<long>(microseconds / kMicrosecondsPerSecond);\n  tv.tv_usec = static_cast<long>(microseconds % kMicrosecondsPerSecond);\n  return tv;\n}\n\n}  // namespace\n\nFILETIME TimespecToFiletimeEpoch(const timespec& ts) {\n  uint64_t intervals =\n      (kNumSecondsFrom1601To1970 + ts.tv_sec) * kFiletimeIntervalsPerSecond +\n      ts.tv_nsec / kNanosecondsPerFiletimeInterval;\n  FILETIME filetime;\n  filetime.dwLowDateTime = intervals & 0xffffffff;\n  filetime.dwHighDateTime = intervals >> 32;\n  return filetime;\n}\n\ntimespec FiletimeToTimespecEpoch(const FILETIME& filetime) {\n  uint64_t intervals =\n      (uint64_t{filetime.dwHighDateTime} << 32) | filetime.dwLowDateTime;\n  timespec result;\n  result.tv_sec =\n      (intervals / kFiletimeIntervalsPerSecond) - kNumSecondsFrom1601To1970;\n  result.tv_nsec =\n      static_cast<long>(intervals % kFiletimeIntervalsPerSecond) *\n      kNanosecondsPerFiletimeInterval;\n  return result;\n}\n\ntimeval FiletimeToTimevalEpoch(const FILETIME& filetime) {\n  uint64_t microseconds = FiletimeToMicroseconds(filetime);\n\n  DCHECK_GE(microseconds, kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond);\n  microseconds -= kNumSecondsFrom1601To1970 * kMicrosecondsPerSecond;\n  return MicrosecondsToTimeval(microseconds);\n}\n\ntimeval FiletimeToTimevalInterval(const FILETIME& filetime) {\n  return MicrosecondsToTimeval(FiletimeToMicroseconds(filetime));\n}\n\nvoid GetTimeOfDay(timeval* tv) {\n  FILETIME filetime;\n  GetSystemTimeAsFileTime(&filetime);\n  *tv = FiletimeToTimevalEpoch(filetime);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/tri_state.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_TRI_STATE_H_\n#define CRASHPAD_UTIL_MISC_TRI_STATE_H_\n\n#include <stdint.h>\n\nnamespace crashpad {\n\n//! \\brief A tri-state value that can be unset, on, or off.\nenum class TriState : uint8_t {\n  //! \\brief The value has not explicitly been set.\n  //!\n  //! To allow a zero-initialized value to have this behavior, this must have\n  //! the value `0`.\n  kUnset = 0,\n\n  //! \\brief The value has explicitly been set to on, or a behavior has\n  //!     explicitly been enabled.\n  kEnabled,\n\n  //! \\brief The value has explicitly been set to off, or a behavior has\n  //!     explicitly been disabled.\n  kDisabled,\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_TRI_STATE_H_\n"
  },
  {
    "path": "util/misc/uuid.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#if !defined(__STDC_FORMAT_MACROS)\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include \"util/misc/uuid.h\"\n\n#include <inttypes.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <string.h>\n\n#include <algorithm>\n#include <string_view>\n#include <type_traits>\n\n#include \"base/containers/span.h\"\n#include \"base/numerics/byte_conversions.h\"\n#include \"base/rand_util.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <uuid/uuid.h>\n#endif  // BUILDFLAG(IS_APPLE)\n\nnamespace crashpad {\n\nstatic_assert(sizeof(UUID) == 16, \"UUID must be 16 bytes\");\nstatic_assert(std::is_standard_layout<UUID>::value,\n              \"UUID must be a standard-layout type\");\nstatic_assert(std::is_trivial<UUID>::value, \"UUID must be a trivial type\");\n\nbool UUID::operator==(const UUID& that) const {\n  return memcmp(this, &that, sizeof(*this)) == 0;\n}\n\nbool UUID::operator<(const UUID& that) const {\n  return memcmp(this, &that, sizeof(*this)) < 0;\n}\n\nvoid UUID::InitializeToZero() {\n  memset(this, 0, sizeof(*this));\n}\n\nvoid UUID::InitializeFromBytes(const uint8_t* bytes_ptr) {\n  // TODO(crbug.com/40284755): This span construction is unsound. The caller\n  // should provide a span instead of an unbounded pointer.\n  base::span bytes(bytes_ptr, base::fixed_extent<sizeof(UUID)>());\n  data_1 = base::U32FromBigEndian(bytes.subspan<0u, 4u>());\n  data_2 = base::U16FromBigEndian(bytes.subspan<4u, 2u>());\n  data_3 = base::U16FromBigEndian(bytes.subspan<6u, 2u>());\n  std::ranges::copy(bytes.subspan<8, 2>(), data_4);\n  std::ranges::copy(bytes.subspan<10, 6>(), data_5);\n}\n\nbool UUID::InitializeFromString(std::string_view string) {\n  if (string.length() != 36)\n    return false;\n\n  UUID temp;\n  static constexpr char kScanFormat[] =\n      \"%08\" SCNx32 \"-%04\" SCNx16 \"-%04\" SCNx16\n      \"-%02\" SCNx8 \"%02\" SCNx8\n      \"-%02\" SCNx8 \"%02\" SCNx8 \"%02\" SCNx8 \"%02\" SCNx8 \"%02\" SCNx8 \"%02\" SCNx8;\n  int rv = sscanf(string.data(),\n                  kScanFormat,\n                  &temp.data_1,\n                  &temp.data_2,\n                  &temp.data_3,\n                  &temp.data_4[0],\n                  &temp.data_4[1],\n                  &temp.data_5[0],\n                  &temp.data_5[1],\n                  &temp.data_5[2],\n                  &temp.data_5[3],\n                  &temp.data_5[4],\n                  &temp.data_5[5]);\n  if (rv != 11)\n    return false;\n\n  *this = temp;\n  return true;\n}\n\n#if BUILDFLAG(IS_WIN)\nbool UUID::InitializeFromString(std::wstring_view string) {\n  return InitializeFromString(base::WideToUTF8(string));\n}\n#endif\n\nbool UUID::InitializeWithNew() {\n#if BUILDFLAG(IS_APPLE)\n  uuid_t uuid;\n  uuid_generate(uuid);\n  InitializeFromBytes(uuid);\n  return true;\n#elif BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \\\n    BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)\n  // Linux, Android, and Fuchsia do not provide a UUID generator in a\n  // widely-available system library. On Linux and Android, uuid_generate()\n  // from libuuid is not available everywhere.\n  // On Windows, do not use UuidCreate() to avoid a dependency on rpcrt4, so\n  // that this function is usable early in DllMain().\n  base::RandBytes(base::byte_span_from_ref(*this));\n\n  // Set six bits per RFC 4122 §4.4 to identify this as a pseudo-random UUID.\n  data_3 = (4 << 12) | (data_3 & 0x0fff);  // §4.1.3\n  data_4[0] = 0x80 | (data_4[0] & 0x3f);  // §4.1.1\n\n  return true;\n#else\n#error Port.\n#endif  // BUILDFLAG(IS_APPLE)\n}\n\n#if BUILDFLAG(IS_WIN)\nvoid UUID::InitializeFromSystemUUID(const ::UUID* system_uuid) {\n  static_assert(sizeof(::UUID) == sizeof(UUID),\n                \"unexpected system uuid size\");\n  static_assert(offsetof(::UUID, Data1) == offsetof(UUID, data_1),\n                \"unexpected system uuid layout\");\n  memcpy(this, system_uuid, sizeof(*this));\n}\n#endif  // BUILDFLAG(IS_WIN)\n\nstd::string UUID::ToString() const {\n  return base::StringPrintf(\"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\",\n                            data_1,\n                            data_2,\n                            data_3,\n                            data_4[0],\n                            data_4[1],\n                            data_5[0],\n                            data_5[1],\n                            data_5[2],\n                            data_5[3],\n                            data_5[4],\n                            data_5[5]);\n}\n\n#if BUILDFLAG(IS_WIN)\nstd::wstring UUID::ToWString() const {\n  return base::UTF8ToWide(ToString());\n}\n#endif  // BUILDFLAG(IS_WIN)\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/uuid.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_UUID_H_\n#define CRASHPAD_UTIL_MISC_UUID_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <string_view>\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <rpc.h>\n#endif\n\nnamespace crashpad {\n\n//! \\brief A universally unique identifier (%UUID).\n//!\n//! An alternate term for %UUID is “globally unique identifier” (GUID), used\n//! primarily by Microsoft.\n//!\n//! A %UUID is a unique 128-bit number specified by RFC 4122.\n//!\n//! This is a POD structure.\nstruct UUID {\n  bool operator==(const UUID& that) const;\n  bool operator!=(const UUID& that) const { return !operator==(that); }\n  bool operator<(const UUID& that) const;\n\n  //! \\brief Initializes the %UUID to zero.\n  void InitializeToZero();\n\n  //! \\brief Initializes the %UUID from a sequence of bytes.\n  //!\n  //! \\a bytes is taken as a %UUID laid out in big-endian format in memory. On\n  //! little-endian machines, appropriate byte-swapping will be performed to\n  //! initialize an object’s data members.\n  //!\n  //! \\param[in] bytes A buffer of exactly 16 bytes that will be assigned to the\n  //!     %UUID.\n  void InitializeFromBytes(const uint8_t* bytes);\n\n  //! \\brief Initializes the %UUID from a RFC 4122 §3 formatted string.\n  //!\n  //! \\param[in] string A string of the form\n  //!     `\"00112233-4455-6677-8899-aabbccddeeff\"`.\n  //!\n  //! \\return `true` if the string was formatted correctly and the object has\n  //!     been initialized with the data. `false` if the string could not be\n  //!     parsed, with the object state untouched.\n  bool InitializeFromString(std::string_view string);\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n  bool InitializeFromString(std::wstring_view string);\n#endif  // BUILDFLAG(IS_WIN)\n\n  //! \\brief Initializes the %UUID using a standard system facility to generate\n  //!     the value.\n  //!\n  //! \\return `true` if the %UUID was initialized correctly, `false` otherwise\n  //!     with a message logged.\n  bool InitializeWithNew();\n\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n  //! \\brief Initializes the %UUID from a system `UUID` or `GUID` structure.\n  //!\n  //! \\param[in] system_uuid A system `UUID` or `GUID` structure.\n  void InitializeFromSystemUUID(const ::UUID* system_uuid);\n#endif  // BUILDFLAG(IS_WIN)\n\n  //! \\brief Formats the %UUID per RFC 4122 §3.\n  //!\n  //! \\return A string of the form `\"00112233-4455-6677-8899-aabbccddeeff\"`.\n  std::string ToString() const;\n\n#if BUILDFLAG(IS_WIN) || DOXYGEN\n  //! \\brief The same as ToString, but returned as a wstring.\n  std::wstring ToWString() const;\n#endif  // BUILDFLAG(IS_WIN)\n\n  // These fields are laid out according to RFC 4122 §4.1.2.\n  uint32_t data_1;\n  uint16_t data_2;\n  uint16_t data_3;\n  uint8_t data_4[2];\n  uint8_t data_5[6];\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_UUID_H_\n"
  },
  {
    "path": "util/misc/uuid_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/uuid.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <string>\n#include <string_view>\n\n#include \"base/format_macros.h\"\n#include \"base/scoped_generic.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(UUID, UUID) {\n  UUID uuid_zero;\n  uuid_zero.InitializeToZero();\n  EXPECT_EQ(uuid_zero.data_1, 0u);\n  EXPECT_EQ(uuid_zero.data_2, 0u);\n  EXPECT_EQ(uuid_zero.data_3, 0u);\n  EXPECT_EQ(uuid_zero.data_4[0], 0u);\n  EXPECT_EQ(uuid_zero.data_4[1], 0u);\n  EXPECT_EQ(uuid_zero.data_5[0], 0u);\n  EXPECT_EQ(uuid_zero.data_5[1], 0u);\n  EXPECT_EQ(uuid_zero.data_5[2], 0u);\n  EXPECT_EQ(uuid_zero.data_5[3], 0u);\n  EXPECT_EQ(uuid_zero.data_5[4], 0u);\n  EXPECT_EQ(uuid_zero.data_5[5], 0u);\n  EXPECT_EQ(uuid_zero.ToString(), \"00000000-0000-0000-0000-000000000000\");\n\n  static constexpr uint8_t kBytes[16] = {0x00,\n                                         0x01,\n                                         0x02,\n                                         0x03,\n                                         0x04,\n                                         0x05,\n                                         0x06,\n                                         0x07,\n                                         0x08,\n                                         0x09,\n                                         0x0a,\n                                         0x0b,\n                                         0x0c,\n                                         0x0d,\n                                         0x0e,\n                                         0x0f};\n  UUID uuid;\n  uuid.InitializeFromBytes(kBytes);\n  EXPECT_EQ(uuid.data_1, 0x00010203u);\n  EXPECT_EQ(uuid.data_2, 0x0405u);\n  EXPECT_EQ(uuid.data_3, 0x0607u);\n  EXPECT_EQ(uuid.data_4[0], 0x08u);\n  EXPECT_EQ(uuid.data_4[1], 0x09u);\n  EXPECT_EQ(uuid.data_5[0], 0x0au);\n  EXPECT_EQ(uuid.data_5[1], 0x0bu);\n  EXPECT_EQ(uuid.data_5[2], 0x0cu);\n  EXPECT_EQ(uuid.data_5[3], 0x0du);\n  EXPECT_EQ(uuid.data_5[4], 0x0eu);\n  EXPECT_EQ(uuid.data_5[5], 0x0fu);\n  EXPECT_EQ(uuid.ToString(), \"00010203-0405-0607-0809-0a0b0c0d0e0f\");\n\n  // Test both operator== and operator!=.\n  EXPECT_FALSE(uuid == uuid_zero);\n  EXPECT_NE(uuid, uuid_zero);\n\n  UUID uuid_2;\n  uuid_2.InitializeFromBytes(kBytes);\n  EXPECT_EQ(uuid_2, uuid);\n  EXPECT_FALSE(uuid != uuid_2);\n\n  // Test operator< operator\n  UUID uuid_3{};\n  UUID uuid_4;\n  uuid_4.InitializeFromString(\"11111111-1111-1111-1111-111111111111\");\n  UUID uuid_5;\n  uuid_5.InitializeFromString(\"22222222-2222-2222-2222-222222222222\");\n\n  EXPECT_LT(uuid_3, uuid_4);\n  EXPECT_LT(uuid_3, uuid_5);\n  EXPECT_LT(uuid_4, uuid_5);\n\n  // Make sure that operator== and operator!= check the entire UUID.\n  ++uuid.data_1;\n  EXPECT_NE(uuid, uuid_2);\n  EXPECT_LT(uuid_2, uuid);\n  --uuid.data_1;\n  ++uuid.data_2;\n  EXPECT_NE(uuid, uuid_2);\n  EXPECT_LT(uuid_2, uuid);\n  --uuid.data_2;\n  ++uuid.data_3;\n  EXPECT_NE(uuid, uuid_2);\n  EXPECT_LT(uuid_2, uuid);\n  --uuid.data_3;\n  for (size_t index = 0; index < std::size(uuid.data_4); ++index) {\n    ++uuid.data_4[index];\n    EXPECT_NE(uuid, uuid_2);\n    EXPECT_LT(uuid_2, uuid);\n    --uuid.data_4[index];\n  }\n  for (size_t index = 0; index < std::size(uuid.data_5); ++index) {\n    ++uuid.data_5[index];\n    EXPECT_NE(uuid, uuid_2);\n    EXPECT_LT(uuid_2, uuid);\n    --uuid.data_5[index];\n  }\n\n  // Make sure that the UUIDs are equal again, otherwise the test above may not\n  // have been valid.\n  EXPECT_EQ(uuid_2, uuid);\n\n  static constexpr uint8_t kMoreBytes[16] = {0xff,\n                                             0xee,\n                                             0xdd,\n                                             0xcc,\n                                             0xbb,\n                                             0xaa,\n                                             0x99,\n                                             0x88,\n                                             0x77,\n                                             0x66,\n                                             0x55,\n                                             0x44,\n                                             0x33,\n                                             0x22,\n                                             0x11,\n                                             0x00};\n  uuid.InitializeFromBytes(kMoreBytes);\n  EXPECT_EQ(uuid.data_1, 0xffeeddccu);\n  EXPECT_EQ(uuid.data_2, 0xbbaau);\n  EXPECT_EQ(uuid.data_3, 0x9988u);\n  EXPECT_EQ(uuid.data_4[0], 0x77u);\n  EXPECT_EQ(uuid.data_4[1], 0x66u);\n  EXPECT_EQ(uuid.data_5[0], 0x55u);\n  EXPECT_EQ(uuid.data_5[1], 0x44u);\n  EXPECT_EQ(uuid.data_5[2], 0x33u);\n  EXPECT_EQ(uuid.data_5[3], 0x22u);\n  EXPECT_EQ(uuid.data_5[4], 0x11u);\n  EXPECT_EQ(uuid.data_5[5], 0x00u);\n  EXPECT_EQ(uuid.ToString(), \"ffeeddcc-bbaa-9988-7766-554433221100\");\n\n  EXPECT_NE(uuid, uuid_2);\n  EXPECT_NE(uuid, uuid_zero);\n\n  // Test that UUID is standard layout.\n  memset(&uuid, 0x45, 16);\n  EXPECT_EQ(uuid.data_1, 0x45454545u);\n  EXPECT_EQ(uuid.data_2, 0x4545u);\n  EXPECT_EQ(uuid.data_3, 0x4545u);\n  EXPECT_EQ(uuid.data_4[0], 0x45u);\n  EXPECT_EQ(uuid.data_4[1], 0x45u);\n  EXPECT_EQ(uuid.data_5[0], 0x45u);\n  EXPECT_EQ(uuid.data_5[1], 0x45u);\n  EXPECT_EQ(uuid.data_5[2], 0x45u);\n  EXPECT_EQ(uuid.data_5[3], 0x45u);\n  EXPECT_EQ(uuid.data_5[4], 0x45u);\n  EXPECT_EQ(uuid.data_5[5], 0x45u);\n  EXPECT_EQ(uuid.ToString(), \"45454545-4545-4545-4545-454545454545\");\n\n  UUID initialized_generated;\n  initialized_generated.InitializeWithNew();\n  EXPECT_NE(initialized_generated, uuid_zero);\n}\n\nTEST(UUID, FromString) {\n  static constexpr struct TestCase {\n    const char* uuid_string;\n    bool success;\n  } kCases[] = {\n    // Valid:\n    {\"c6849cb5-fe14-4a79-8978-9ae6034c521d\", true},\n    {\"00000000-0000-0000-0000-000000000000\", true},\n    {\"ffffffff-ffff-ffff-ffff-ffffffffffff\", true},\n    // Outside HEX range:\n    {\"7318z10b-c453-4cef-9dc8-015655cb4bbc\", false},\n    {\"7318a10b-c453-4cef-9dz8-015655cb4bbc\", false},\n    // Incomplete:\n    {\"15655cb4-\", false},\n    {\"7318f10b-c453-4cef-9dc8-015655cb4bb\", false},\n    {\"318f10b-c453-4cef-9dc8-015655cb4bb2\", false},\n    {\"7318f10b-c453-4ef-9dc8-015655cb4bb2\", false},\n    {\"\", false},\n    {\"abcd\", false},\n    // Trailing data:\n    {\"6d247a34-53d5-40ec-a90d-d8dea9e94cc01\", false}\n  };\n\n  UUID uuid_zero;\n  uuid_zero.InitializeToZero();\n  const std::string empty_uuid = uuid_zero.ToString();\n\n  for (size_t index = 0; index < std::size(kCases); ++index) {\n    const TestCase& test_case = kCases[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %\" PRIuS \": %s\", index, test_case.uuid_string));\n\n    UUID uuid;\n    uuid.InitializeToZero();\n    EXPECT_EQ(uuid.InitializeFromString(test_case.uuid_string),\n              test_case.success);\n    if (test_case.success) {\n      EXPECT_EQ(uuid.ToString(), test_case.uuid_string);\n    } else {\n      EXPECT_EQ(uuid.ToString(), empty_uuid);\n    }\n  }\n\n  // Test for case insensitivty.\n  UUID uuid;\n  uuid.InitializeFromString(\"F32E5BDC-2681-4C73-A4E6-911FFD89B846\");\n  EXPECT_EQ(uuid.ToString(), \"f32e5bdc-2681-4c73-a4e6-911ffd89b846\");\n\n  // Mixed case.\n  uuid.InitializeFromString(\"5762C15D-50b5-4171-a2e9-7429C9EC6CAB\");\n  EXPECT_EQ(uuid.ToString(), \"5762c15d-50b5-4171-a2e9-7429c9ec6cab\");\n\n#if BUILDFLAG(IS_WIN)\n  // Test accepting a std::u16string_view via L\"\" literals on Windows.\n  EXPECT_TRUE(\n      uuid.InitializeFromString(L\"F32E5BDC-2681-4C73-A4E6-444FFD44B444\"));\n  EXPECT_EQ(uuid.ToString(), \"f32e5bdc-2681-4c73-a4e6-444ffd44b444\");\n\n  EXPECT_TRUE(\n      uuid.InitializeFromString(L\"5762C15D-50b5-4171-a2e9-5555C5EC5CAB\"));\n  EXPECT_EQ(uuid.ToString(), \"5762c15d-50b5-4171-a2e9-5555c5ec5cab\");\n#endif  // BUILDFLAG(IS_WIN)\n}\n\n#if BUILDFLAG(IS_WIN)\n\nTEST(UUID, FromSystem) {\n  ::GUID system_uuid;\n  ASSERT_EQ(UuidCreate(&system_uuid), RPC_S_OK);\n\n  UUID uuid;\n  uuid.InitializeFromSystemUUID(&system_uuid);\n\n  RPC_WSTR system_string;\n  ASSERT_EQ(UuidToString(&system_uuid, &system_string), RPC_S_OK);\n\n  struct ScopedRpcStringFreeTraits {\n    static RPC_WSTR* InvalidValue() { return nullptr; }\n    static void Free(RPC_WSTR* rpc_string) {\n      EXPECT_EQ(RpcStringFree(rpc_string), RPC_S_OK);\n    }\n  };\n  using ScopedRpcString =\n      base::ScopedGeneric<RPC_WSTR*, ScopedRpcStringFreeTraits>;\n  ScopedRpcString scoped_system_string(&system_string);\n\n  EXPECT_EQ(uuid.ToWString(), reinterpret_cast<wchar_t*>(system_string));\n}\n\n#endif  // BUILDFLAG(IS_WIN)\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/zlib.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/misc/zlib.h\"\n\n#include <ostream>\n\n#include \"base/check.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"third_party/zlib/zlib_crashpad.h\"\n\nnamespace crashpad {\n\nint ZlibWindowBitsWithGzipWrapper(int window_bits) {\n  // See the documentation for deflateInit2() and inflateInit2() in <zlib.h>. 0\n  // is only valid during decompression.\n\n  DCHECK(window_bits == 0 || (window_bits >= 8 && window_bits <= 15))\n      << window_bits;\n\n  return 16 + window_bits;\n}\n\nstd::string ZlibErrorString(int zr) {\n  return base::StringPrintf(\"%s (%d)\", zError(zr), zr);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/misc/zlib.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_MISC_ZLIB_H_\n#define CRASHPAD_UTIL_MISC_ZLIB_H_\n\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief Obtain a \\a window_bits parameter to pass to `deflateInit2()` or\n//!     `inflateInit2()` that specifies a `gzip` wrapper instead of the default\n//!     zlib wrapper.\n//!\n//! \\param[in] window_bits A \\a window_bits value that only specifies the base-2\n//!     logarithm of the deflate sliding window size.\n//!\n//! \\return \\a window_bits adjusted to specify a `gzip` wrapper, to be passed to\n//!     `deflateInit2()` or `inflateInit2()`.\nint ZlibWindowBitsWithGzipWrapper(int window_bits);\n\n//! \\brief Formats a string for an error received from the zlib library.\n//!\n//! \\param[in] zr A zlib result code, such as `Z_STREAM_ERROR`.\n//!\n//! \\return A formatted string.\nstd::string ZlibErrorString(int zr);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_MISC_ZLIB_H_\n"
  },
  {
    "path": "util/net/generate_test_server_key.py",
    "content": "#!/usr/bin/env python\n\n# Copyright 2018 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport os\nimport subprocess\nimport sys\n\ntestdata = os.path.join(os.path.dirname(__file__), 'testdata')\nkey = os.path.join(testdata, 'crashpad_util_test_key.pem')\ncert = os.path.join(testdata, 'crashpad_util_test_cert.pem')\n\nwith open(cert, 'w') as cert_file, open(key, 'w') as key_file:\n    MESSAGE = ('DO NOT EDIT: This file was auto-generated by ' + __file__ +\n               '\\n\\n')\n    cert_file.write(MESSAGE)\n    key_file.write(MESSAGE)\n\n    proc = subprocess.Popen([\n        'openssl', 'req', '-x509', '-nodes', '-subj', '/CN=localhost', '-days',\n        '3650', '-newkey', 'rsa:2048', '-keyout', '-'\n    ],\n                            stderr=open(os.devnull, 'w'),\n                            stdout=subprocess.PIPE)\n\n    contents = proc.communicate()[0]\n    dest = sys.stderr\n    for line in contents.splitlines(True):\n        if line.startswith(\"-----BEGIN PRIVATE KEY-----\"):\n            dest = key_file\n        elif line.startswith(\"-----BEGIN CERTIFICATE-----\"):\n            dest = cert_file\n        elif line.startswith(\"-----END\"):\n            dest.write(line)\n            dest = sys.stderr\n            continue\n\n        dest.write(line)\n"
  },
  {
    "path": "util/net/http_body.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_body.h\"\n\n#include <string.h>\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/check.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\n\nStringHTTPBodyStream::StringHTTPBodyStream(const std::string& string)\n    : HTTPBodyStream(), string_(string), bytes_read_() {\n}\n\nStringHTTPBodyStream::~StringHTTPBodyStream() {\n}\n\nFileOperationResult StringHTTPBodyStream::GetBytesBuffer(uint8_t* buffer,\n                                                         size_t max_len) {\n  size_t num_bytes_remaining = string_.length() - bytes_read_;\n  if (num_bytes_remaining == 0) {\n    return num_bytes_remaining;\n  }\n\n  size_t num_bytes_returned = std::min(\n      std::min(num_bytes_remaining, max_len),\n      implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max()));\n  memcpy(buffer, &string_[bytes_read_], num_bytes_returned);\n  bytes_read_ += num_bytes_returned;\n  return num_bytes_returned;\n}\n\nFileReaderHTTPBodyStream::FileReaderHTTPBodyStream(FileReaderInterface* reader)\n    : HTTPBodyStream(), reader_(reader), reached_eof_(false) {\n  DCHECK(reader_);\n}\n\nFileReaderHTTPBodyStream::~FileReaderHTTPBodyStream() {}\n\nFileOperationResult FileReaderHTTPBodyStream::GetBytesBuffer(uint8_t* buffer,\n                                                             size_t max_len) {\n  if (reached_eof_) {\n    return 0;\n  }\n\n  FileOperationResult rv = reader_->Read(buffer, max_len);\n  if (rv == 0) {\n    reached_eof_ = true;\n  }\n  return rv;\n}\n\nCompositeHTTPBodyStream::CompositeHTTPBodyStream(\n    const CompositeHTTPBodyStream::PartsList& parts)\n    : HTTPBodyStream(), parts_(parts), current_part_(parts_.begin()) {\n}\n\nCompositeHTTPBodyStream::~CompositeHTTPBodyStream() {\n  for (auto& item : parts_)\n    delete item;\n}\n\nFileOperationResult CompositeHTTPBodyStream::GetBytesBuffer(uint8_t* buffer,\n                                                            size_t buffer_len) {\n  FileOperationResult max_len = std::min(\n      buffer_len,\n      implicit_cast<size_t>(std::numeric_limits<FileOperationResult>::max()));\n  FileOperationResult bytes_copied = 0;\n  while (bytes_copied < max_len && current_part_ != parts_.end()) {\n    FileOperationResult this_read =\n        (*current_part_)\n            ->GetBytesBuffer(buffer + bytes_copied, max_len - bytes_copied);\n\n    if (this_read == 0) {\n      // If the current part has returned 0 indicating EOF, advance the current\n      // part and try again.\n      ++current_part_;\n    } else if (this_read < 0) {\n      return this_read;\n    }\n    bytes_copied += this_read;\n  }\n\n  return bytes_copied;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_body.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NET_HTTP_BODY_H_\n#define CRASHPAD_UTIL_NET_HTTP_BODY_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <string>\n#include <vector>\n\n#include \"util/file/file_io.h\"\n#include \"util/file/file_reader.h\"\n\nnamespace crashpad {\n\n//! \\brief An interface to a stream that can be used for an HTTP request body.\nclass HTTPBodyStream {\n public:\n  virtual ~HTTPBodyStream() {}\n\n  //! \\brief Copies up to \\a max_len bytes into the user-supplied buffer.\n  //!\n  //! \\param[out] buffer A user-supplied buffer into which this method will copy\n  //!      bytes from the stream.\n  //! \\param[in] max_len The length (or size) of \\a buffer. At most this many\n  //!     bytes will be copied.\n  //!\n  //! \\return On success, a positive number indicating the number of bytes\n  //!     actually copied to \\a buffer. On failure, a negative number. When\n  //!     the stream has no more data, returns `0`.\n  virtual FileOperationResult GetBytesBuffer(uint8_t* buffer,\n                                             size_t max_len) = 0;\n\n protected:\n  HTTPBodyStream() {}\n};\n\n//! \\brief An implementation of HTTPBodyStream that turns a fixed string into\n//!     a stream.\nclass StringHTTPBodyStream : public HTTPBodyStream {\n public:\n  //! \\brief Creates a stream with the specified string.\n  //!\n  //! \\param[in] string The string to turn into a stream.\n  explicit StringHTTPBodyStream(const std::string& string);\n\n  StringHTTPBodyStream(const StringHTTPBodyStream&) = delete;\n  StringHTTPBodyStream& operator=(const StringHTTPBodyStream&) = delete;\n\n  ~StringHTTPBodyStream() override;\n\n  // HTTPBodyStream:\n  FileOperationResult GetBytesBuffer(uint8_t* buffer, size_t max_len) override;\n\n private:\n  std::string string_;\n  size_t bytes_read_;\n};\n\n//! \\brief An implementation of HTTPBodyStream that reads from a\n//!     FileReaderInterface and provides its contents for an HTTP body.\nclass FileReaderHTTPBodyStream : public HTTPBodyStream {\n public:\n  //! \\brief Creates a stream for reading from a FileReaderInterface.\n  //!\n  //! \\param[in] reader A FileReaderInterface from which this HTTPBodyStream\n  //!     will read.\n  explicit FileReaderHTTPBodyStream(FileReaderInterface* reader);\n\n  FileReaderHTTPBodyStream(const FileReaderHTTPBodyStream&) = delete;\n  FileReaderHTTPBodyStream& operator=(const FileReaderHTTPBodyStream&) = delete;\n\n  ~FileReaderHTTPBodyStream() override;\n\n  // HTTPBodyStream:\n  FileOperationResult GetBytesBuffer(uint8_t* buffer, size_t max_len) override;\n\n private:\n  FileReaderInterface* reader_;  // weak\n  bool reached_eof_;\n};\n\n//! \\brief An implementation of HTTPBodyStream that combines an array of\n//!     several other HTTPBodyStream objects into a single, unified stream.\nclass CompositeHTTPBodyStream : public HTTPBodyStream {\n public:\n  using PartsList = std::vector<HTTPBodyStream*>;\n\n  //! \\brief Creates a stream from an array of other stream parts.\n  //!\n  //! \\param[in] parts A vector of HTTPBodyStream objects, of which this object\n  //!     takes ownership, that will be represented as a single unified stream.\n  //!     Callers should not mutate the stream objects after passing them to\n  //!     an instance of this class.\n  explicit CompositeHTTPBodyStream(const PartsList& parts);\n\n  CompositeHTTPBodyStream(const CompositeHTTPBodyStream&) = delete;\n  CompositeHTTPBodyStream& operator=(const CompositeHTTPBodyStream&) = delete;\n\n  ~CompositeHTTPBodyStream() override;\n\n  // HTTPBodyStream:\n  FileOperationResult GetBytesBuffer(uint8_t* buffer, size_t max_len) override;\n\n private:\n  PartsList parts_;\n  PartsList::iterator current_part_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NET_HTTP_BODY_H_\n"
  },
  {
    "path": "util/net/http_body_gzip.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_body_gzip.h\"\n\n#include <utility>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"third_party/zlib/zlib_crashpad.h\"\n#include \"util/misc/zlib.h\"\n\nnamespace crashpad {\n\nGzipHTTPBodyStream::GzipHTTPBodyStream(std::unique_ptr<HTTPBodyStream> source)\n    : input_(),\n      source_(std::move(source)),\n      z_stream_(new z_stream()),\n      state_(State::kUninitialized) {}\n\nGzipHTTPBodyStream::~GzipHTTPBodyStream() {\n  DCHECK(state_ == State::kUninitialized ||\n         state_ == State::kFinished ||\n         state_ == State::kError);\n}\n\nFileOperationResult GzipHTTPBodyStream::GetBytesBuffer(uint8_t* buffer,\n                                                       size_t max_len) {\n  if (state_ == State::kError) {\n    return -1;\n  }\n\n  if (state_ == State::kFinished) {\n    return 0;\n  }\n\n  if (state_ == State::kUninitialized) {\n    z_stream_->zalloc = Z_NULL;\n    z_stream_->zfree = Z_NULL;\n    z_stream_->opaque = Z_NULL;\n\n    // The default values for zlib’s internal MAX_WBITS and DEF_MEM_LEVEL. These\n    // are the values that deflateInit() would use, but they’re not exported\n    // from zlib. deflateInit2() is used instead of deflateInit() to get the\n    // gzip wrapper.\n    constexpr int kZlibMaxWindowBits = 15;\n    constexpr int kZlibDefaultMemoryLevel = 8;\n\n    int zr = deflateInit2(z_stream_.get(),\n                          Z_DEFAULT_COMPRESSION,\n                          Z_DEFLATED,\n                          ZlibWindowBitsWithGzipWrapper(kZlibMaxWindowBits),\n                          kZlibDefaultMemoryLevel,\n                          Z_DEFAULT_STRATEGY);\n    if (zr != Z_OK) {\n      LOG(ERROR) << \"deflateInit2: \" << ZlibErrorString(zr);\n      state_ = State::kError;\n      return -1;\n    }\n\n    state_ = State::kOperating;\n  }\n\n  z_stream_->next_out = buffer;\n  z_stream_->avail_out = base::saturated_cast<uInt>(max_len);\n\n  while (state_ != State::kFinished && z_stream_->avail_out > 0) {\n    if (state_ != State::kInputEOF && z_stream_->avail_in == 0) {\n      FileOperationResult input_bytes =\n          source_->GetBytesBuffer(input_, sizeof(input_));\n      if (input_bytes == -1) {\n        Done(State::kError);\n        return -1;\n      }\n\n      if (input_bytes == 0) {\n        state_ = State::kInputEOF;\n      }\n\n      z_stream_->next_in = input_;\n      z_stream_->avail_in = base::checked_cast<uInt>(input_bytes);\n    }\n\n    int zr = deflate(z_stream_.get(),\n                     state_ == State::kInputEOF ? Z_FINISH : Z_NO_FLUSH);\n    if (state_ == State::kInputEOF && zr == Z_STREAM_END) {\n      Done(State::kFinished);\n      if (state_ == State::kError) {\n        return -1;\n      }\n    } else if (zr != Z_OK) {\n      LOG(ERROR) << \"deflate: \" << ZlibErrorString(zr);\n      Done(State::kError);\n      return -1;\n    }\n  }\n\n  DCHECK_LE(z_stream_->avail_out, max_len);\n  return max_len - z_stream_->avail_out;\n}\n\nvoid GzipHTTPBodyStream::Done(State state) {\n  DCHECK(state_ == State::kOperating || state_ == State::kInputEOF) << state_;\n  DCHECK(state == State::kFinished || state == State::kError) << state;\n\n  int zr = deflateEnd(z_stream_.get());\n  if (zr != Z_OK) {\n    LOG(ERROR) << \"deflateEnd: \" << ZlibErrorString(zr);\n    state_ = State::kError;\n  } else {\n    state_ = state;\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_body_gzip.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NET_HTTP_BODY_GZIP_H_\n#define CRASHPAD_UTIL_NET_HTTP_BODY_GZIP_H_\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <memory>\n\n#include \"util/file/file_io.h\"\n#include \"util/net/http_body.h\"\n\nextern \"C\" {\ntypedef struct z_stream_s z_stream;\n}  // extern \"C\"\n\nnamespace crashpad {\n\n//! \\brief An implementation of HTTPBodyStream that `gzip`-compresses another\n//!     HTTPBodyStream.\nclass GzipHTTPBodyStream : public HTTPBodyStream {\n public:\n  explicit GzipHTTPBodyStream(std::unique_ptr<HTTPBodyStream> source);\n\n  GzipHTTPBodyStream(const GzipHTTPBodyStream&) = delete;\n  GzipHTTPBodyStream& operator=(const GzipHTTPBodyStream&) = delete;\n\n  ~GzipHTTPBodyStream() override;\n\n  // HTTPBodyStream:\n  FileOperationResult GetBytesBuffer(uint8_t* buffer, size_t max_len) override;\n\n private:\n  enum State : int {\n    kUninitialized,\n    kOperating,\n    kInputEOF,\n    kFinished,\n    kError,\n  };\n\n  // Calls deflateEnd() and transitions state_ to state. If deflateEnd() fails,\n  // logs a message and transitions state_ to State::kError.\n  void Done(State state);\n\n  uint8_t input_[4096];\n  std::unique_ptr<HTTPBodyStream> source_;\n  std::unique_ptr<z_stream> z_stream_;\n  State state_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NET_HTTP_BODY_GZIP_H_\n"
  },
  {
    "path": "util/net/http_body_gzip_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_body_gzip.h\"\n\n#include <string.h>\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/rand_util.h\"\n#include \"gtest/gtest.h\"\n#include \"third_party/zlib/zlib_crashpad.h\"\n#include \"util/misc/zlib.h\"\n#include \"util/net/http_body.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass ScopedZlibInflateStream {\n public:\n  explicit ScopedZlibInflateStream(z_stream* zlib) : zlib_(zlib) {}\n\n  ScopedZlibInflateStream(const ScopedZlibInflateStream&) = delete;\n  ScopedZlibInflateStream& operator=(const ScopedZlibInflateStream&) = delete;\n\n  ~ScopedZlibInflateStream() {\n    int zr = inflateEnd(zlib_);\n    EXPECT_EQ(zr, Z_OK) << \"inflateEnd: \" << ZlibErrorString(zr);\n  }\n\n private:\n  z_stream* zlib_;  // weak\n};\n\nvoid GzipInflate(const std::string& compressed,\n                 std::string* decompressed,\n                 size_t buf_size) {\n  decompressed->clear();\n\n  // There’s got to be at least a small buffer.\n  auto buf = base::HeapArray<uint8_t>::Uninit(\n      std::max(buf_size, static_cast<size_t>(1)));\n  z_stream zlib = {};\n  zlib.zalloc = Z_NULL;\n  zlib.zfree = Z_NULL;\n  zlib.opaque = Z_NULL;\n  zlib.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(&compressed[0]));\n  zlib.avail_in = base::checked_cast<uInt>(compressed.size());\n  zlib.next_out = buf.data();\n  zlib.avail_out = base::checked_cast<uInt>(buf.size());\n\n  int zr = inflateInit2(&zlib, ZlibWindowBitsWithGzipWrapper(0));\n  ASSERT_EQ(zr, Z_OK) << \"inflateInit2: \" << ZlibErrorString(zr);\n  ScopedZlibInflateStream zlib_inflate(&zlib);\n\n  zr = inflate(&zlib, Z_FINISH);\n  ASSERT_EQ(zr, Z_STREAM_END) << \"inflate: \" << ZlibErrorString(zr);\n\n  ASSERT_LE(zlib.avail_out, buf.size());\n  decompressed->assign(reinterpret_cast<char*>(buf.data()),\n                       buf.size() - zlib.avail_out);\n}\n\nvoid TestGzipDeflateInflate(const std::string& string) {\n  std::unique_ptr<HTTPBodyStream> string_stream(\n      new StringHTTPBodyStream(string));\n  GzipHTTPBodyStream gzip_stream(std::move(string_stream));\n\n  // The minimum size of a gzip wrapper per RFC 1952: a 10-byte header and an\n  // 8-byte trailer.\n  constexpr size_t kGzipHeaderSize = 18;\n\n  // Per https://zlib.net/zlib_tech.html, in the worst case, zlib will store\n  // uncompressed data as-is, at an overhead of 5 bytes per 16384-byte block.\n  // Zero-length input will “compress” to a 2-byte zlib stream. Add the overhead\n  // of the gzip wrapper, assuming no optional fields are present.\n  size_t buf_size =\n      string.size() + kGzipHeaderSize +\n      (string.empty() ? 2 : (((string.size() + 16383) / 16384) * 5));\n  auto buf = base::HeapArray<uint8_t>::Uninit(buf_size);\n  FileOperationResult compressed_bytes =\n      gzip_stream.GetBytesBuffer(buf.data(), buf.size());\n  ASSERT_NE(compressed_bytes, -1);\n  ASSERT_LE(static_cast<size_t>(compressed_bytes), buf.size());\n\n  // Make sure that the stream is really at EOF.\n  uint8_t eof_buf[16];\n  ASSERT_EQ(gzip_stream.GetBytesBuffer(eof_buf, sizeof(eof_buf)), 0);\n\n  std::string compressed(reinterpret_cast<char*>(buf.data()), compressed_bytes);\n\n  ASSERT_GE(compressed.size(), kGzipHeaderSize);\n  EXPECT_EQ(compressed[0], '\\37');\n  EXPECT_EQ(compressed[1], '\\213');\n  EXPECT_EQ(compressed[2], Z_DEFLATED);\n\n  std::string decompressed;\n  ASSERT_NO_FATAL_FAILURE(\n      GzipInflate(compressed, &decompressed, string.size()));\n\n  EXPECT_EQ(decompressed, string);\n\n  // In block mode, compression should be identical.\n  string_stream.reset(new StringHTTPBodyStream(string));\n  GzipHTTPBodyStream block_gzip_stream(std::move(string_stream));\n  uint8_t block_buf[4096];\n  std::string block_compressed;\n  FileOperationResult block_compressed_bytes;\n  while ((block_compressed_bytes = block_gzip_stream.GetBytesBuffer(\n              block_buf, sizeof(block_buf))) > 0) {\n    block_compressed.append(reinterpret_cast<char*>(block_buf),\n                            block_compressed_bytes);\n  }\n  ASSERT_EQ(block_compressed_bytes, 0);\n  EXPECT_EQ(block_compressed, compressed);\n}\n\nstd::string MakeString(size_t size) {\n  std::string string;\n  for (size_t i = 0; i < size; ++i) {\n    string.append(1, static_cast<char>((i % 256) ^ ((i >> 8) % 256)));\n  }\n  return string;\n}\n\nconstexpr size_t kFourKBytes = 4096;\nconstexpr size_t kManyBytes = 375017;\n\nTEST(GzipHTTPBodyStream, Empty) {\n  TestGzipDeflateInflate(std::string());\n}\n\nTEST(GzipHTTPBodyStream, OneByte) {\n  TestGzipDeflateInflate(std::string(\"Z\"));\n}\n\nTEST(GzipHTTPBodyStream, FourKBytes_NUL) {\n  TestGzipDeflateInflate(std::string(kFourKBytes, '\\0'));\n}\n\nTEST(GzipHTTPBodyStream, ManyBytes_NUL) {\n  TestGzipDeflateInflate(std::string(kManyBytes, '\\0'));\n}\n\nTEST(GzipHTTPBodyStream, FourKBytes_Deterministic) {\n  TestGzipDeflateInflate(MakeString(kFourKBytes));\n}\n\nTEST(GzipHTTPBodyStream, ManyBytes_Deterministic) {\n  TestGzipDeflateInflate(MakeString(kManyBytes));\n}\n\nTEST(GzipHTTPBodyStream, FourKBytes_Random) {\n  TestGzipDeflateInflate(base::RandBytesAsString(kFourKBytes));\n}\n\nTEST(GzipHTTPBodyStream, ManyBytes_Random) {\n  TestGzipDeflateInflate(base::RandBytesAsString(kManyBytes));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_body_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_body.h\"\n\n#include <string.h>\n\n#include \"gtest/gtest.h\"\n#include \"test/test_paths.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/net/http_body_test_util.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid ExpectBufferSet(const uint8_t* actual,\n                     uint8_t expected_byte,\n                     size_t num_expected_bytes) {\n  for (size_t i = 0; i < num_expected_bytes; ++i) {\n    EXPECT_EQ(actual[i], expected_byte) << i;\n  }\n}\n\nTEST(StringHTTPBodyStream, EmptyString) {\n  uint8_t buf[32];\n  memset(buf, '!', sizeof(buf));\n\n  std::string empty_string;\n  StringHTTPBodyStream stream(empty_string);\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n  ExpectBufferSet(buf, '!', sizeof(buf));\n}\n\nTEST(StringHTTPBodyStream, SmallString) {\n  uint8_t buf[32];\n  memset(buf, '!', sizeof(buf));\n\n  std::string string(\"Hello, world\");\n  StringHTTPBodyStream stream(string);\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)),\n            implicit_cast<FileOperationResult>(string.length()));\n\n  std::string actual(reinterpret_cast<const char*>(buf), string.length());\n  EXPECT_EQ(actual, string);\n  ExpectBufferSet(buf + string.length(), '!', sizeof(buf) - string.length());\n\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n}\n\nTEST(StringHTTPBodyStream, MultipleReads) {\n  uint8_t buf[2];\n  memset(buf, '!', sizeof(buf));\n\n  {\n    std::string string(\"test\");\n    SCOPED_TRACE(\"aligned buffer boundary\");\n\n    StringHTTPBodyStream stream(string);\n    EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 2);\n    EXPECT_EQ(buf[0], 't');\n    EXPECT_EQ(buf[1], 'e');\n    EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 2);\n    EXPECT_EQ(buf[0], 's');\n    EXPECT_EQ(buf[1], 't');\n    EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n    EXPECT_EQ(buf[0], 's');\n    EXPECT_EQ(buf[1], 't');\n  }\n\n  {\n    std::string string(\"abc\");\n    SCOPED_TRACE(\"unaligned buffer boundary\");\n\n    StringHTTPBodyStream stream(string);\n    EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 2);\n    EXPECT_EQ(buf[0], 'a');\n    EXPECT_EQ(buf[1], 'b');\n    EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 1);\n    EXPECT_EQ(buf[0], 'c');\n    EXPECT_EQ(buf[1], 'b');  // Unmodified from last read.\n    EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n    EXPECT_EQ(buf[0], 'c');\n    EXPECT_EQ(buf[1], 'b');\n  }\n}\n\nTEST(FileReaderHTTPBodyStream, ReadASCIIFile) {\n  base::FilePath path = TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"util/net/testdata/ascii_http_body.txt\"));\n\n  FileReader reader;\n  ASSERT_TRUE(reader.Open(path));\n  FileReaderHTTPBodyStream stream(&reader);\n  std::string contents = ReadStreamToString(&stream, 32);\n  EXPECT_EQ(contents, \"This is a test.\\n\");\n\n  // Make sure that the file is not read again after it has been read to\n  // completion.\n  uint8_t buf[8];\n  memset(buf, '!', sizeof(buf));\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n  ExpectBufferSet(buf, '!', sizeof(buf));\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n  ExpectBufferSet(buf, '!', sizeof(buf));\n}\n\nTEST(FileReaderHTTPBodyStream, ReadBinaryFile) {\n  // HEX contents of file: |FEEDFACE A11A15|.\n  base::FilePath path = TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"util/net/testdata/binary_http_body.dat\"));\n  // This buffer size was chosen so that reading the file takes multiple reads.\n  uint8_t buf[4];\n\n  FileReader reader;\n  ASSERT_TRUE(reader.Open(path));\n  FileReaderHTTPBodyStream stream(&reader);\n\n  memset(buf, '!', sizeof(buf));\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 4);\n  EXPECT_EQ(buf[0], 0xfe);\n  EXPECT_EQ(buf[1], 0xed);\n  EXPECT_EQ(buf[2], 0xfa);\n  EXPECT_EQ(buf[3], 0xce);\n\n  memset(buf, '!', sizeof(buf));\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 3);\n  EXPECT_EQ(buf[0], 0xa1);\n  EXPECT_EQ(buf[1], 0x1a);\n  EXPECT_EQ(buf[2], 0x15);\n  EXPECT_EQ(buf[3], '!');\n\n  memset(buf, '!', sizeof(buf));\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n  ExpectBufferSet(buf, '!', sizeof(buf));\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n  ExpectBufferSet(buf, '!', sizeof(buf));\n}\n\nTEST(CompositeHTTPBodyStream, TwoEmptyStrings) {\n  std::vector<HTTPBodyStream*> parts;\n  parts.push_back(new StringHTTPBodyStream(std::string()));\n  parts.push_back(new StringHTTPBodyStream(std::string()));\n\n  CompositeHTTPBodyStream stream(parts);\n\n  uint8_t buf[5];\n  memset(buf, '!', sizeof(buf));\n  EXPECT_EQ(stream.GetBytesBuffer(buf, sizeof(buf)), 0);\n  ExpectBufferSet(buf, '!', sizeof(buf));\n}\n\nclass CompositeHTTPBodyStreamBufferSize\n    : public testing::TestWithParam<size_t> {\n};\n\nTEST_P(CompositeHTTPBodyStreamBufferSize, ThreeStringParts) {\n  std::string string1(\"crashpad\");\n  std::string string2(\"test\");\n  std::string string3(\"foobar\");\n  const size_t all_strings_length =\n      string1.length() + string2.length() + string3.length();\n  std::string buf(all_strings_length + 3, '!');\n\n  std::vector<HTTPBodyStream*> parts;\n  parts.push_back(new StringHTTPBodyStream(string1));\n  parts.push_back(new StringHTTPBodyStream(string2));\n  parts.push_back(new StringHTTPBodyStream(string3));\n\n  CompositeHTTPBodyStream stream(parts);\n\n  std::string actual_string = ReadStreamToString(&stream, GetParam());\n  EXPECT_EQ(actual_string, string1 + string2 + string3);\n\n  ExpectBufferSet(reinterpret_cast<uint8_t*>(&buf[all_strings_length]), '!', 3);\n}\n\nTEST_P(CompositeHTTPBodyStreamBufferSize, StringsAndFile) {\n  std::string string1(\"Hello! \");\n  std::string string2(\" Goodbye :)\");\n\n  std::vector<HTTPBodyStream*> parts;\n  parts.push_back(new StringHTTPBodyStream(string1));\n  base::FilePath path = TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"util/net/testdata/ascii_http_body.txt\"));\n\n  FileReader reader;\n  ASSERT_TRUE(reader.Open(path));\n  parts.push_back(new FileReaderHTTPBodyStream(&reader));\n  parts.push_back(new StringHTTPBodyStream(string2));\n\n  CompositeHTTPBodyStream stream(parts);\n\n  std::string expected_string = string1 + \"This is a test.\\n\" + string2;\n  std::string actual_string = ReadStreamToString(&stream, GetParam());\n  EXPECT_EQ(actual_string, expected_string);\n}\n\nINSTANTIATE_TEST_SUITE_P(VariableBufferSize,\n                         CompositeHTTPBodyStreamBufferSize,\n                         testing::Values(1, 2, 9, 16, 31, 128, 1024));\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_body_test_util.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_body_test_util.h\"\n\n#include <stdint.h>\n\n#include \"base/containers/heap_array.h\"\n#include \"gtest/gtest.h\"\n#include \"util/file/file_io.h\"\n#include \"util/net/http_body.h\"\n\nnamespace crashpad {\nnamespace test {\n\nstd::string ReadStreamToString(HTTPBodyStream* stream) {\n  return ReadStreamToString(stream, 32);\n}\n\nstd::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size) {\n  auto buf = base::HeapArray<uint8_t>::Uninit(buffer_size);\n  std::string result;\n\n  FileOperationResult bytes_read;\n  while ((bytes_read = stream->GetBytesBuffer(buf.data(), buf.size())) != 0) {\n    if (bytes_read < 0) {\n      ADD_FAILURE() << \"Failed to read from stream: \" << bytes_read;\n      return std::string();\n    }\n\n    result.append(reinterpret_cast<char*>(buf.data()), bytes_read);\n  }\n\n  return result;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_body_test_util.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_\n#define CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_\n\n#include <sys/types.h>\n\n#include <string>\n\nnamespace crashpad {\n\nclass HTTPBodyStream;\n\nnamespace test {\n\n//! \\brief Reads a HTTPBodyStream to a string. If an error occurs, adds a\n//!     test failure and returns an empty string.\n//!\n//! \\param[in] stream The stream from which to read.\n//!\n//! \\return The contents of the stream, or an empty string on failure.\nstd::string ReadStreamToString(HTTPBodyStream* stream);\n\n//! \\brief Reads a HTTPBodyStream to a string. If an error occurs, adds a\n//!     test failure and returns an empty string.\n//!\n//! \\param[in] stream The stream from which to read.\n//! \\param[in] buffer_size The size of the buffer to use when reading from the\n//!     stream.\n//!\n//! \\return The contents of the stream, or an empty string on failure.\nstd::string ReadStreamToString(HTTPBodyStream* stream, size_t buffer_size);\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NET_HTTP_BODY_TEST_UTIL_H_\n"
  },
  {
    "path": "util/net/http_headers.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NET_HTTP_HEADERS_H_\n#define CRASHPAD_UTIL_NET_HTTP_HEADERS_H_\n\n#include <map>\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief A map of HTTP header fields to their values.\nusing HTTPHeaders = std::map<std::string, std::string>;\n\n//! \\brief The header name `\"Content-Type\"`.\nconstexpr char kContentType[] = \"Content-Type\";\n\n//! \\brief The header name `\"Content-Length\"`.\nconstexpr char kContentLength[] = \"Content-Length\";\n\n//! \\brief The header name `\"Content-Encoding\"`.\nconstexpr char kContentEncoding[] = \"Content-Encoding\";\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NET_HTTP_HEADERS_H_\n"
  },
  {
    "path": "util/net/http_multipart_builder.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_multipart_builder.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <utility>\n#include <vector>\n\n#include \"base/check.h\"\n#include \"base/rand_util.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"util/net/http_body.h\"\n#include \"util/net/http_body_gzip.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nconstexpr char kCRLF[] = \"\\r\\n\";\n\nconstexpr char kBoundaryCRLF[] = \"\\r\\n\\r\\n\";\n\n// Generates a random string suitable for use as a multipart boundary.\nstd::string GenerateBoundaryString() {\n  // RFC 2046 §5.1.1 says that the boundary string may be 1 to 70 characters\n  // long, choosing from the set of alphanumeric characters along with\n  // characters from the set “'()+_,-./:=? ”, and not ending in a space.\n  // However, some servers have been observed as dealing poorly with certain\n  // nonalphanumeric characters. See\n  // blink/Source/platform/network/FormDataEncoder.cpp\n  // blink::FormDataEncoder::GenerateUniqueBoundaryString().\n  //\n  // This implementation produces a 56-character string with over 190 bits of\n  // randomness (62^32 > 2^190).\n  std::string boundary_string = \"---MultipartBoundary-\";\n  for (int index = 0; index < 32; ++index) {\n    static constexpr char kCharacters[] =\n        \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n    int random_value =\n        base::RandInt(0, static_cast<int>(strlen(kCharacters)) - 1);\n    boundary_string += kCharacters[random_value];\n  }\n  boundary_string += \"---\";\n  return boundary_string;\n}\n\n// Escapes the specified name to be suitable for the name field of a\n// form-data part.\nstd::string EncodeMIMEField(const std::string& name) {\n  // This URL-escapes the quote character and newline characters, per Blink. See\n  // blink/Source/platform/network/FormDataEncoder.cpp\n  // blink::AppendQuotedString(). %-encoding is endorsed by RFC 7578 §2, with\n  // approval for otherwise unencoded UTF-8 given by RFC 7578 §5.1. Blink does\n  // not escape the '%' character, but it seems appropriate to do so in order to\n  // be able to decode the string properly.\n  std::string encoded;\n  for (char character : name) {\n    switch (character) {\n      case '\\r':\n      case '\\n':\n      case '\"':\n      case '%':\n        encoded += base::StringPrintf(\"%%%02x\", character);\n        break;\n      default:\n        encoded += character;\n        break;\n    }\n  }\n\n  return encoded;\n}\n\n// Returns a string, formatted with a multipart boundary and a field name,\n// after which the contents of the part at |name| can be appended.\nstd::string GetFormDataBoundary(const std::string& boundary,\n                                const std::string& name) {\n  return base::StringPrintf(\n      \"--%s%sContent-Disposition: form-data; name=\\\"%s\\\"\",\n      boundary.c_str(),\n      kCRLF,\n      EncodeMIMEField(name).c_str());\n}\n\nvoid AssertSafeMIMEType(const std::string& string) {\n  for (size_t i = 0; i < string.length(); ++i) {\n    char c = string[i];\n    CHECK((c >= 'a' && c <= 'z') ||\n          (c >= 'A' && c <= 'Z') ||\n          (c >= '0' && c <= '9') ||\n          c == '/' ||\n          c == '.' ||\n          c == '_' ||\n          c == '+' ||\n          c == '-');\n  }\n}\n\n}  // namespace\n\nHTTPMultipartBuilder::HTTPMultipartBuilder()\n    : boundary_(GenerateBoundaryString()),\n      form_data_(),\n      file_attachments_(),\n      gzip_enabled_(false) {}\n\nHTTPMultipartBuilder::~HTTPMultipartBuilder() {\n}\n\nvoid HTTPMultipartBuilder::SetGzipEnabled(bool gzip_enabled) {\n  gzip_enabled_ = gzip_enabled;\n}\n\nvoid HTTPMultipartBuilder::SetFormData(const std::string& key,\n                                       const std::string& value) {\n  EraseKey(key);\n  form_data_[key] = value;\n}\n\nvoid HTTPMultipartBuilder::SetFileAttachment(\n    const std::string& key,\n    const std::string& upload_file_name,\n    FileReaderInterface* reader,\n    const std::string& content_type) {\n  EraseKey(upload_file_name);\n\n  FileAttachment attachment;\n  attachment.filename = EncodeMIMEField(upload_file_name);\n  attachment.reader = reader;\n\n  if (content_type.empty()) {\n    attachment.content_type = \"application/octet-stream\";\n  } else {\n    AssertSafeMIMEType(content_type);\n    attachment.content_type = content_type;\n  }\n\n  file_attachments_[key] = attachment;\n}\n\nstd::unique_ptr<HTTPBodyStream> HTTPMultipartBuilder::GetBodyStream() {\n  // The objects inserted into this vector will be owned by the returned\n  // CompositeHTTPBodyStream. Take care to not early-return without deleting\n  // this memory.\n  std::vector<HTTPBodyStream*> streams;\n\n  for (const auto& pair : form_data_) {\n    std::string field = GetFormDataBoundary(boundary_, pair.first);\n    field += kBoundaryCRLF;\n    field += pair.second;\n    field += kCRLF;\n    streams.push_back(new StringHTTPBodyStream(field));\n  }\n\n  for (const auto& pair : file_attachments_) {\n    const FileAttachment& attachment = pair.second;\n    std::string header = GetFormDataBoundary(boundary_, pair.first);\n    header += base::StringPrintf(\"; filename=\\\"%s\\\"%s\",\n        attachment.filename.c_str(), kCRLF);\n    header += base::StringPrintf(\"Content-Type: %s%s\",\n        attachment.content_type.c_str(), kBoundaryCRLF);\n\n    streams.push_back(new StringHTTPBodyStream(header));\n    streams.push_back(new FileReaderHTTPBodyStream(attachment.reader));\n    streams.push_back(new StringHTTPBodyStream(kCRLF));\n  }\n\n  streams.push_back(\n      new StringHTTPBodyStream(\"--\"  + boundary_ + \"--\" + kCRLF));\n\n  auto composite =\n      std::unique_ptr<HTTPBodyStream>(new CompositeHTTPBodyStream(streams));\n  if (gzip_enabled_) {\n    return std::unique_ptr<HTTPBodyStream>(\n        new GzipHTTPBodyStream(std::move(composite)));\n  }\n  return composite;\n}\n\nvoid HTTPMultipartBuilder::PopulateContentHeaders(\n    HTTPHeaders* http_headers) const {\n  std::string content_type =\n      base::StringPrintf(\"multipart/form-data; boundary=%s\", boundary_.c_str());\n  (*http_headers)[kContentType] = content_type;\n\n  if (gzip_enabled_) {\n    (*http_headers)[kContentEncoding] = \"gzip\";\n  }\n}\n\nvoid HTTPMultipartBuilder::EraseKey(const std::string& key) {\n  auto data_it = form_data_.find(key);\n  if (data_it != form_data_.end())\n    form_data_.erase(data_it);\n\n  auto file_it = file_attachments_.find(key);\n  if (file_it != file_attachments_.end())\n    file_attachments_.erase(file_it);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_multipart_builder.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_\n#define CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_\n\n#include <map>\n#include <memory>\n#include <string>\n\n#include \"util/file/file_reader.h\"\n#include \"util/net/http_headers.h\"\n\nnamespace crashpad {\n\nclass HTTPBodyStream;\n\n//! \\brief This class is used to build a MIME multipart message, conforming to\n//!     RFC 2046, for use as a HTTP request body.\nclass HTTPMultipartBuilder {\n public:\n  HTTPMultipartBuilder();\n\n  HTTPMultipartBuilder(const HTTPMultipartBuilder&) = delete;\n  HTTPMultipartBuilder& operator=(const HTTPMultipartBuilder&) = delete;\n\n  ~HTTPMultipartBuilder();\n\n  //! \\brief Enables or disables `gzip` compression.\n  //!\n  //! \\param[in] gzip_enabled Whether to enable or disable `gzip` compression.\n  //!\n  //! When `gzip` compression is enabled, the body stream returned by\n  //! GetBodyStream() will be `gzip`-compressed, and the content headers set by\n  //! PopulateContentHeaders() will contain `Content-Encoding: gzip`.\n  void SetGzipEnabled(bool gzip_enabled);\n\n  //! \\brief Sets a `Content-Disposition: form-data` key-value pair.\n  //!\n  //! \\param[in] key The key of the form data, specified as the `name` in the\n  //!     multipart message. Any data previously set on this class with this\n  //!     key will be overwritten.\n  //! \\param[in] value The value to set at the \\a key.\n  void SetFormData(const std::string& key, const std::string& value);\n\n  //! \\brief Specifies the contents read from \\a reader to be uploaded as\n  //!     multipart data, available at `name` of \\a upload_file_name.\n  //!\n  //! \\param[in] key The key of the form data, specified as the `name` in the\n  //!     multipart message. Any data previously set on this class with this\n  //!     key will be overwritten.\n  //! \\param[in] upload_file_name The `filename` to specify for this multipart\n  //!     data attachment.\n  //! \\param[in] reader A FileReaderInterface from which to read the content to\n  //!     upload.\n  //! \\param[in] content_type The `Content-Type` to specify for the attachment.\n  //!     If this is empty, `\"application/octet-stream\"` will be used.\n  void SetFileAttachment(const std::string& key,\n                         const std::string& upload_file_name,\n                         FileReaderInterface* reader,\n                         const std::string& content_type);\n\n  //! \\brief Generates the HTTPBodyStream for the data currently supplied to\n  //!     the builder.\n  //!\n  //! \\return A caller-owned HTTPBodyStream object.\n  std::unique_ptr<HTTPBodyStream> GetBodyStream();\n\n  //! \\brief Adds the appropriate content headers to \\a http_headers.\n  //!\n  //! Any headers that this method adds will replace existing headers by the\n  //! same name in \\a http_headers.\n  void PopulateContentHeaders(HTTPHeaders* http_headers) const;\n\n private:\n  struct FileAttachment {\n    std::string filename;\n    std::string content_type;\n    FileReaderInterface* reader;\n  };\n\n  // Removes elements from both data maps at the specified |key|, to ensure\n  // uniqueness across the entire HTTP body.\n  void EraseKey(const std::string& key);\n\n  std::string boundary_;\n  std::map<std::string, std::string> form_data_;\n  std::map<std::string, FileAttachment> file_attachments_;\n  bool gzip_enabled_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NET_HTTP_MULTIPART_BUILDER_H_\n"
  },
  {
    "path": "util/net/http_multipart_builder_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_multipart_builder.h\"\n\n#include <sys/types.h>\n\n#include <vector>\n\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n#include \"test/test_paths.h\"\n#include \"util/net/http_body.h\"\n#include \"util/net/http_body_test_util.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nstd::vector<std::string> SplitCRLF(const std::string& string) {\n  std::vector<std::string> lines;\n  size_t last_line = 0;\n  for (size_t i = 0; i < string.length(); ++i) {\n    if (string[i] == '\\r' && i+1 < string.length() && string[i+1] == '\\n') {\n      lines.push_back(string.substr(last_line, i - last_line));\n      last_line = i + 2;\n      ++i;\n    }\n  }\n  // Append any remainder.\n  if (last_line < string.length()) {\n    lines.push_back(string.substr(last_line));\n  }\n  return lines;\n}\n\n// In the tests below, the form data pairs don’t appear in the order they were\n// added. The current implementation uses a std::map which sorts keys, so the\n// entires appear in alphabetical order. However, this is an implementation\n// detail, and it’s OK if the writer stops sorting in this order. Testing for\n// a specific order is just the easiest way to write this test while the writer\n// will output things in a known order.\n\nTEST(HTTPMultipartBuilder, ThreeStringFields) {\n  HTTPMultipartBuilder builder;\n\n  static constexpr char kKey1[] = \"key1\";\n  static constexpr char kValue1[] = \"test\";\n  builder.SetFormData(kKey1, kValue1);\n\n  static constexpr char kKey2[] = \"key2\";\n  static constexpr char kValue2[] = \"This is another test.\";\n  builder.SetFormData(kKey2, kValue2);\n\n  static constexpr char kKey3[] = \"key-three\";\n  static constexpr char kValue3[] = \"More tests\";\n  builder.SetFormData(kKey3, kValue3);\n\n  std::unique_ptr<HTTPBodyStream> body(builder.GetBodyStream());\n  ASSERT_TRUE(body.get());\n  std::string contents = ReadStreamToString(body.get());\n  auto lines = SplitCRLF(contents);\n  ASSERT_EQ(lines.size(), 13u);\n  auto lines_it = lines.begin();\n\n  // The first line is the boundary. All subsequent boundaries must match this.\n  const std::string& boundary = *lines_it++;\n  EXPECT_GE(boundary.length(), 1u);\n  EXPECT_LE(boundary.length(), 70u);\n\n  EXPECT_EQ(*lines_it++, \"Content-Disposition: form-data; name=\\\"key-three\\\"\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kValue3);\n\n  EXPECT_EQ(*lines_it++, boundary);\n  EXPECT_EQ(*lines_it++, \"Content-Disposition: form-data; name=\\\"key1\\\"\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kValue1);\n\n  EXPECT_EQ(*lines_it++, boundary);\n  EXPECT_EQ(*lines_it++, \"Content-Disposition: form-data; name=\\\"key2\\\"\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kValue2);\n\n  EXPECT_EQ(*lines_it++, boundary + \"--\");\n\n  EXPECT_EQ(lines_it, lines.end());\n}\n\nTEST(HTTPMultipartBuilder, ThreeFileAttachments) {\n  HTTPMultipartBuilder builder;\n  base::FilePath ascii_http_body_path = TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"util/net/testdata/ascii_http_body.txt\"));\n\n  FileReader reader1;\n  ASSERT_TRUE(reader1.Open(ascii_http_body_path));\n  builder.SetFileAttachment(\"first\", \"minidump.dmp\", &reader1, \"\");\n\n  FileReader reader2;\n  ASSERT_TRUE(reader2.Open(ascii_http_body_path));\n  builder.SetFileAttachment(\"second\", \"minidump.dmp\", &reader2, \"text/plain\");\n\n  FileReader reader3;\n  ASSERT_TRUE(reader3.Open(ascii_http_body_path));\n  builder.SetFileAttachment(\n      \"\\\"third 50% silly\\\"\", \"test%foo.txt\", &reader3, \"text/plain\");\n\n  static constexpr char kFileContents[] = \"This is a test.\\n\";\n\n  std::unique_ptr<HTTPBodyStream> body(builder.GetBodyStream());\n  ASSERT_TRUE(body.get());\n  std::string contents = ReadStreamToString(body.get());\n  auto lines = SplitCRLF(contents);\n  ASSERT_EQ(lines.size(), 16u);\n  auto lines_it = lines.begin();\n\n  const std::string& boundary = *lines_it++;\n  EXPECT_GE(boundary.length(), 1u);\n  EXPECT_LE(boundary.length(), 70u);\n\n  EXPECT_EQ(*lines_it++,\n            \"Content-Disposition: form-data; \"\n            \"name=\\\"%22third 50%25 silly%22\\\"; filename=\\\"test%25foo.txt\\\"\");\n  EXPECT_EQ(*lines_it++, \"Content-Type: text/plain\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kFileContents);\n\n  EXPECT_EQ(*lines_it++, boundary);\n  EXPECT_EQ(*lines_it++,\n            \"Content-Disposition: form-data; \"\n            \"name=\\\"first\\\"; filename=\\\"minidump.dmp\\\"\");\n  EXPECT_EQ(*lines_it++, \"Content-Type: application/octet-stream\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kFileContents);\n\n  EXPECT_EQ(*lines_it++, boundary);\n  EXPECT_EQ(*lines_it++,\n            \"Content-Disposition: form-data; \"\n            \"name=\\\"second\\\"; filename=\\\"minidump.dmp\\\"\");\n  EXPECT_EQ(*lines_it++, \"Content-Type: text/plain\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kFileContents);\n\n  EXPECT_EQ(*lines_it++, boundary + \"--\");\n\n  EXPECT_EQ(lines_it, lines.end());\n}\n\nTEST(HTTPMultipartBuilder, OverwriteFormDataWithEscapedKey) {\n  HTTPMultipartBuilder builder;\n  static constexpr char kKey[] = \"a 100% \\\"silly\\\"\\r\\ntest\";\n  builder.SetFormData(kKey, \"some dummy value\");\n  builder.SetFormData(kKey, \"overwrite\");\n  std::unique_ptr<HTTPBodyStream> body(builder.GetBodyStream());\n  ASSERT_TRUE(body.get());\n  std::string contents = ReadStreamToString(body.get());\n  auto lines = SplitCRLF(contents);\n  ASSERT_EQ(lines.size(), 5u);\n  auto lines_it = lines.begin();\n\n  const std::string& boundary = *lines_it++;\n  EXPECT_GE(boundary.length(), 1u);\n  EXPECT_LE(boundary.length(), 70u);\n\n  EXPECT_EQ(*lines_it++,\n            \"Content-Disposition: form-data; name=\\\"a 100%25 \"\n            \"%22silly%22%0d%0atest\\\"\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, \"overwrite\");\n  EXPECT_EQ(*lines_it++, boundary + \"--\");\n  EXPECT_EQ(lines_it, lines.end());\n}\n\nTEST(HTTPMultipartBuilder, OverwriteFileAttachment) {\n  HTTPMultipartBuilder builder;\n  static constexpr char kValue[] = \"1 2 3 test\";\n  builder.SetFormData(\"a key\", kValue);\n  base::FilePath testdata_path =\n      TestPaths::TestDataRoot().Append(FILE_PATH_LITERAL(\"util/net/testdata\"));\n\n  FileReader reader1;\n  ASSERT_TRUE(reader1.Open(\n      testdata_path.Append(FILE_PATH_LITERAL(\"binary_http_body.dat\"))));\n  builder.SetFileAttachment(\"minidump\", \"minidump.dmp\", &reader1, \"\");\n\n  FileReader reader2;\n  ASSERT_TRUE(reader2.Open(\n      testdata_path.Append(FILE_PATH_LITERAL(\"binary_http_body.dat\"))));\n  builder.SetFileAttachment(\"minidump2\", \"minidump.dmp\", &reader2, \"\");\n\n  FileReader reader3;\n  ASSERT_TRUE(reader3.Open(\n      testdata_path.Append(FILE_PATH_LITERAL(\"ascii_http_body.txt\"))));\n  builder.SetFileAttachment(\"minidump\", \"minidump.dmp\", &reader3, \"text/plain\");\n\n  std::unique_ptr<HTTPBodyStream> body(builder.GetBodyStream());\n  ASSERT_TRUE(body.get());\n  std::string contents = ReadStreamToString(body.get());\n  auto lines = SplitCRLF(contents);\n  ASSERT_EQ(lines.size(), 15u);\n  auto lines_it = lines.begin();\n\n  const std::string& boundary = *lines_it++;\n  EXPECT_GE(boundary.length(), 1u);\n  EXPECT_LE(boundary.length(), 70u);\n\n  EXPECT_EQ(*lines_it++, \"Content-Disposition: form-data; name=\\\"a key\\\"\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kValue);\n\n  EXPECT_EQ(*lines_it++, boundary);\n  EXPECT_EQ(*lines_it++,\n            \"Content-Disposition: form-data; \"\n            \"name=\\\"minidump\\\"; filename=\\\"minidump.dmp\\\"\");\n  EXPECT_EQ(*lines_it++, \"Content-Type: text/plain\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, \"This is a test.\\n\");\n\n  EXPECT_EQ(*lines_it++, boundary);\n  EXPECT_EQ(*lines_it++,\n            \"Content-Disposition: form-data; \"\n            \"name=\\\"minidump2\\\"; filename=\\\"minidump.dmp\\\"\");\n  EXPECT_EQ(*lines_it++, \"Content-Type: application/octet-stream\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, \"\\xFE\\xED\\xFA\\xCE\\xA1\\x1A\\x15\");\n\n  EXPECT_EQ(*lines_it++, boundary + \"--\");\n\n  EXPECT_EQ(lines_it, lines.end());\n}\n\nTEST(HTTPMultipartBuilder, SharedFormDataAndAttachmentKeyNamespace) {\n  HTTPMultipartBuilder builder;\n  static constexpr char kValue1[] = \"11111\";\n  builder.SetFormData(\"one\", kValue1);\n  base::FilePath ascii_http_body_path = TestPaths::TestDataRoot().Append(\n      FILE_PATH_LITERAL(\"util/net/testdata/ascii_http_body.txt\"));\n\n  FileReader reader;\n  ASSERT_TRUE(reader.Open(ascii_http_body_path));\n  builder.SetFileAttachment(\"minidump\", \"minidump.dmp\", &reader, \"\");\n  static constexpr char kValue2[] = \"this is not a file\";\n  builder.SetFormData(\"minidump\", kValue2);\n\n  std::unique_ptr<HTTPBodyStream> body(builder.GetBodyStream());\n  ASSERT_TRUE(body.get());\n  std::string contents = ReadStreamToString(body.get());\n  auto lines = SplitCRLF(contents);\n  ASSERT_EQ(lines.size(), 9u);\n  auto lines_it = lines.begin();\n\n  const std::string& boundary = *lines_it++;\n  EXPECT_GE(boundary.length(), 1u);\n  EXPECT_LE(boundary.length(), 70u);\n\n  EXPECT_EQ(*lines_it++, \"Content-Disposition: form-data; name=\\\"minidump\\\"\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kValue2);\n\n  EXPECT_EQ(*lines_it++, boundary);\n  EXPECT_EQ(*lines_it++, \"Content-Disposition: form-data; name=\\\"one\\\"\");\n  EXPECT_EQ(*lines_it++, \"\");\n  EXPECT_EQ(*lines_it++, kValue1);\n\n  EXPECT_EQ(*lines_it++, boundary + \"--\");\n\n  EXPECT_EQ(lines_it, lines.end());\n}\n\nTEST(HTTPMultipartBuilderDeathTest, AssertUnsafeMIMEType) {\n  HTTPMultipartBuilder builder;\n  FileReader reader;\n  // Invalid and potentially dangerous:\n  ASSERT_DEATH_CHECK(builder.SetFileAttachment(\"\", \"\", &reader, \"\\r\\n\"), \"\");\n  ASSERT_DEATH_CHECK(builder.SetFileAttachment(\"\", \"\", &reader, \"\\\"\"), \"\");\n  ASSERT_DEATH_CHECK(builder.SetFileAttachment(\"\", \"\", &reader, \"\\x12\"), \"\");\n  ASSERT_DEATH_CHECK(builder.SetFileAttachment(\"\", \"\", &reader, \"<>\"), \"\");\n  // Invalid but safe:\n  builder.SetFileAttachment(\"\", \"\", &reader, \"0/totally/-invalid.pdf\");\n  // Valid and safe:\n  builder.SetFileAttachment(\"\", \"\", &reader, \"application/xml+xhtml\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_transport.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_transport.h\"\n\n#include <utility>\n\n#include \"util/net/http_body.h\"\n\nnamespace crashpad {\n\nHTTPTransport::HTTPTransport()\n    : url_(),\n      method_(\"POST\"),\n      headers_(),\n      body_stream_(),\n      timeout_(15.0) {\n}\n\nHTTPTransport::~HTTPTransport() {\n}\n\nvoid HTTPTransport::SetURL(const std::string& url) {\n  url_ = url;\n}\n\nvoid HTTPTransport::SetMethod(const std::string& method) {\n  method_ = method;\n}\n\nvoid HTTPTransport::SetHeader(const std::string& header,\n                              const std::string& value) {\n  headers_[header] = value;\n}\n\nvoid HTTPTransport::SetBodyStream(std::unique_ptr<HTTPBodyStream> stream) {\n  body_stream_ = std::move(stream);\n}\n\nvoid HTTPTransport::SetTimeout(double timeout) {\n  timeout_ = timeout;\n}\n\nvoid HTTPTransport::SetRootCACertificatePath(const base::FilePath& cert) {\n  root_ca_certificate_path_ = cert;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_transport.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_\n#define CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_\n\n#include <memory>\n#include <string>\n\n#include \"base/files/file_path.h\"\n#include \"util/net/http_headers.h\"\n\nnamespace crashpad {\n\nclass HTTPBodyStream;\n\n//! \\brief HTTPTransport executes a HTTP request using the specified URL, HTTP\n//!     method, headers, and body. This class can only issue a synchronous\n//!     HTTP request.\n//!\n//! This class cannot be instantiated directly. A concrete subclass must be\n//! instantiated instead, which provides an implementation to execute the\n//! request that is appropriate for the host operating system.\nclass HTTPTransport {\n public:\n  HTTPTransport(const HTTPTransport&) = delete;\n  HTTPTransport& operator=(const HTTPTransport&) = delete;\n\n  virtual ~HTTPTransport();\n\n  //! \\brief Instantiates a concrete HTTPTransport class for the current\n  //!     operating system.\n  //!\n  //! \\return A new caller-owned HTTPTransport object.\n  static std::unique_ptr<HTTPTransport> Create();\n\n  //! \\brief Sets URL to which the request will be made.\n  //!\n  //! \\param[in] url The request URL.\n  void SetURL(const std::string& url);\n\n  //! \\brief Sets the HTTP method to execute. E.g., GET, POST, etc. The default\n  //!     method is `\"POST\"`.\n  //!\n  //! \\param[in] http_method The HTTP method.\n  void SetMethod(const std::string& http_method);\n\n  //! \\brief Sets a HTTP header-value pair.\n  //!\n  //! \\param[in] header The HTTP header name. Any previous value set at this\n  //!     name will be overwritten.\n  //! \\param[in] value The value to set for the header.\n  void SetHeader(const std::string& header, const std::string& value);\n\n  //! \\brief Sets the stream object from which to generate the HTTP body.\n  //!\n  //! \\param[in] stream A HTTPBodyStream, of which this class will take\n  //!     ownership.\n  void SetBodyStream(std::unique_ptr<HTTPBodyStream> stream);\n\n  //! \\brief Sets the timeout for the HTTP request. The default is 15 seconds.\n  //!\n  //! \\param[in] timeout The request timeout, in seconds.\n  void SetTimeout(double timeout);\n\n  //! \\brief Sets a certificate file to be used in lieu of the system CA cert\n  //!     bundle.\n  //!\n  //! This is exposed primarily for testing with a self-signed certificate, and\n  //! it isn't necessary to set it in normal use.\n  //!\n  //! \\param[in] cert The filename of a file in PEM format containing the CA\n  //!     cert to be used for TLS connections.\n  void SetRootCACertificatePath(const base::FilePath& cert);\n\n  //! \\brief Performs the HTTP request with the configured parameters and waits\n  //!     for the execution to complete.\n  //!\n  //! \\param[out] response_body On success, this will be set to the HTTP\n  //!     response body. This parameter is optional and may be set to `nullptr`\n  //!     if the response body is not required.\n  //!\n  //! \\return Whether or not the request was successful, defined as returning\n  //!     a HTTP status code in the range 200-203 (inclusive).\n  virtual bool ExecuteSynchronously(std::string* response_body) = 0;\n\n protected:\n  HTTPTransport();\n\n  const std::string& url() const { return url_; }\n  const std::string& method() const { return method_; }\n  const HTTPHeaders& headers() const { return headers_; }\n  HTTPBodyStream* body_stream() const { return body_stream_.get(); }\n  double timeout() const { return timeout_; }\n  const base::FilePath& root_ca_certificate_path() const {\n    return root_ca_certificate_path_;\n  }\n\n private:\n  std::string url_;\n  std::string method_;\n  base::FilePath root_ca_certificate_path_;\n  HTTPHeaders headers_;\n  std::unique_ptr<HTTPBodyStream> body_stream_;\n  double timeout_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NET_HTTP_TRANSPORT_H_\n"
  },
  {
    "path": "util/net/http_transport_libcurl.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_transport.h\"\n\n#include <curl/curl.h>\n#include <dlfcn.h>\n#include <string.h>\n#include <sys/utsname.h>\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_math.h\"\n#include \"base/scoped_generic.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"package.h\"\n#include \"util/misc/no_cfi_icall.h\"\n#include \"util/net/http_body.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n// Crashpad depends on libcurl via dlopen() in order to maximize its tolerance\n// of various libcurl flavors and installation configurations. This class serves\n// as a linkage table for libcurl procedures.\nclass Libcurl {\n public:\n  Libcurl(const Libcurl&) = delete;\n  Libcurl& operator=(const Libcurl&) = delete;\n\n  static bool Initialized() {\n    static bool initialized = Get()->Initialize();\n    return initialized;\n  }\n\n  static void CurlEasyCleanup(CURL* curl) {\n    return Get()->curl_easy_cleanup_(curl);\n  }\n\n  static CURL* CurlEasyInit() { return Get()->curl_easy_init_(); }\n\n  static CURLcode CurlEasyPerform(CURL* curl) {\n    return Get()->curl_easy_perform_(curl);\n  }\n\n  static const char* CurlEasyStrError(CURLcode code) {\n    return Get()->curl_easy_strerror_(code);\n  }\n\n  template <typename Pointer>\n  static CURLcode CurlEasyGetInfo(CURL* curl, CURLINFO info, Pointer out) {\n    return Get()->curl_easy_getinfo_(curl, info, out);\n  }\n\n  template <typename Pointer>\n  static CURLcode CurlEasySetOpt(CURL* curl, CURLoption option, Pointer param) {\n    return Get()->curl_easy_setopt_(curl, option, param);\n  }\n\n  static CURLcode CurlGlobalInit(long flags) {\n    return Get()->curl_global_init_(flags);\n  }\n\n  static void CurlSlistFreeAll(struct curl_slist* slist) {\n    return Get()->curl_slist_free_all_(slist);\n  }\n\n  static struct curl_slist* CurlSlistAppend(struct curl_slist* slist,\n                                            const char* data) {\n    return Get()->curl_slist_append_(slist, data);\n  }\n\n  static char* CurlVersion() { return Get()->curl_version_(); }\n\n private:\n  Libcurl() = default;\n  ~Libcurl() = delete;\n\n  static Libcurl* Get() {\n    static Libcurl* instance = new Libcurl();\n    return instance;\n  }\n\n  bool Initialize() {\n    void* libcurl = []() {\n      std::vector<std::string> errors;\n      for (const auto& lib : {\n               \"libcurl.so\",\n               \"libcurl-gnutls.so.4\",\n               \"libcurl-nss.so.4\",\n               \"libcurl.so.4\",\n           }) {\n        void* libcurl = dlopen(lib, RTLD_LAZY | RTLD_LOCAL);\n        if (libcurl) {\n          return libcurl;\n        }\n        errors.emplace_back(dlerror());\n      }\n      for (const auto& message : errors) {\n        LOG(ERROR) << \"dlopen:\" << message;\n      }\n      return static_cast<void*>(nullptr);\n    }();\n    if (!libcurl) {\n      return false;\n    }\n\n#define LINK_OR_RETURN_FALSE(symbol)               \\\n  do {                                             \\\n    symbol##_.SetPointer(dlsym(libcurl, #symbol)); \\\n    if (!symbol##_) {                              \\\n      LOG(ERROR) << \"dlsym:\" << dlerror();         \\\n      return false;                                \\\n    }                                              \\\n  } while (0);\n\n    LINK_OR_RETURN_FALSE(curl_easy_cleanup);\n    LINK_OR_RETURN_FALSE(curl_easy_init);\n    LINK_OR_RETURN_FALSE(curl_easy_perform);\n    LINK_OR_RETURN_FALSE(curl_easy_strerror);\n    LINK_OR_RETURN_FALSE(curl_easy_getinfo);\n    LINK_OR_RETURN_FALSE(curl_easy_setopt);\n    LINK_OR_RETURN_FALSE(curl_global_init);\n    LINK_OR_RETURN_FALSE(curl_slist_free_all);\n    LINK_OR_RETURN_FALSE(curl_slist_append);\n    LINK_OR_RETURN_FALSE(curl_version);\n\n#undef LINK_OR_RETURN_FALSE\n\n    return true;\n  }\n\n  NoCfiIcall<decltype(curl_easy_cleanup)*> curl_easy_cleanup_;\n  NoCfiIcall<decltype(curl_easy_init)*> curl_easy_init_;\n  NoCfiIcall<decltype(curl_easy_perform)*> curl_easy_perform_;\n  NoCfiIcall<decltype(curl_easy_strerror)*> curl_easy_strerror_;\n  NoCfiIcall<decltype(curl_easy_getinfo)*> curl_easy_getinfo_;\n  NoCfiIcall<decltype(curl_easy_setopt)*> curl_easy_setopt_;\n  NoCfiIcall<decltype(curl_global_init)*> curl_global_init_;\n  NoCfiIcall<decltype(curl_slist_free_all)*> curl_slist_free_all_;\n  NoCfiIcall<decltype(curl_slist_append)*> curl_slist_append_;\n  NoCfiIcall<decltype(curl_version)*> curl_version_;\n};\n\nstd::string UserAgent() {\n  std::string user_agent = base::StringPrintf(\n      \"%s/%s %s\", PACKAGE_NAME, PACKAGE_VERSION, Libcurl::CurlVersion());\n\n  utsname os;\n  if (uname(&os) != 0) {\n    PLOG(WARNING) << \"uname\";\n  } else {\n    // Match the architecture name that would be used by the kernel, so that the\n    // strcmp() below can omit the kernel’s architecture name if it’s the same\n    // as the user process’ architecture. On Linux, these names are normally\n    // defined in each architecture’s Makefile as UTS_MACHINE, but can be\n    // overridden in architecture-specific configuration as COMPAT_UTS_MACHINE.\n    // See linux-4.9.17/arch/*/Makefile and\n    // linux-4.9.17/arch/*/include/asm/compat.h. In turn, on some systems, these\n    // names are further overridden or refined in early kernel startup code by\n    // modifying the string returned by linux-4.9.17/include/linux/utsname.h\n    // init_utsname() as noted.\n#if defined(ARCH_CPU_X86)\n    // linux-4.9.17/arch/x86/kernel/cpu/bugs.c check_bugs() sets the first digit\n    // to 4, 5, or 6, but no higher.\n#if defined(__i686__)\n    static constexpr char arch[] = \"i686\";\n#elif defined(__i586__)\n    static constexpr char arch[] = \"i586\";\n#elif defined(__i486__)\n    static constexpr char arch[] = \"i486\";\n#else\n    static constexpr char arch[] = \"i386\";\n#endif\n#elif defined(ARCH_CPU_X86_64)\n    static constexpr char arch[] = \"x86_64\";\n#elif defined(ARCH_CPU_ARMEL)\n    // linux-4.9.17/arch/arm/kernel/setup.c setup_processor() bases the string\n    // on the ARM processor name and a character identifying little- or\n    // big-endian. The processor name comes from a definition in\n    // arch/arm/mm/proc-*.S.\n#if defined(__ARM_ARCH_4T__)\n    static constexpr char arch[] =\n        \"armv4t\"\n#elif defined(__ARM_ARCH_5TEJ__)\n    static constexpr char arch[] =\n        \"armv5tej\"\n#elif defined(__ARM_ARCH_5TE__)\n    static constexpr char arch[] =\n        \"armv5te\"\n#elif defined(__ARM_ARCH_5T__)\n    static constexpr char arch[] =\n        \"armv5t\"\n#elif defined(__ARM_ARCH_7M__)\n    static constexpr char arch[] =\n        \"armv7m\"\n#else\n    // Most ARM architectures fall into here, including all profile variants of\n    // armv6, armv7, armv8, with one exception, armv7m, handled above.\n    // xstr(__ARM_ARCH) will be the architecture revision number, such as 6, 7,\n    // or 8.\n#define xstr(s) str(s)\n#define str(s) #s\n    static constexpr char arch[] =\n        \"armv\" xstr(__ARM_ARCH)\n#undef str\n#undef xstr\n#endif\n#if defined(ARCH_CPU_LITTLE_ENDIAN)\n        \"l\";\n#elif defined(ARCH_CPU_BIG_ENDIAN)\n        \"b\";\n#endif\n#elif defined(ARCH_CPU_ARM64)\n    // ARM64 uses aarch64 or aarch64_be as directed by ELF_PLATFORM. See\n    // linux-4.9.17/arch/arm64/kernel/setup.c setup_arch().\n#if defined(ARCH_CPU_LITTLE_ENDIAN)\n    static constexpr char arch[] = \"aarch64\";\n#elif defined(ARCH_CPU_BIG_ENDIAN)\n    static constexpr char arch[] = \"aarch64_be\";\n#endif\n#elif defined (ARCH_CPU_RISCV64)\n    static constexpr char arch[] = \"riscv64\";\n#else\n#error Port\n#endif\n\n    user_agent.append(\n        base::StringPrintf(\" %s/%s (%s\", os.sysname, os.release, arch));\n    if (strcmp(arch, os.machine) != 0) {\n      user_agent.append(base::StringPrintf(\"; %s\", os.machine));\n    }\n    user_agent.append(1, ')');\n  }\n\n  return user_agent;\n}\n\nstd::string CurlErrorMessage(CURLcode curl_err, const std::string& base) {\n  return base::StringPrintf(\"%s: %s (%d)\",\n                            base.c_str(),\n                            Libcurl::CurlEasyStrError(curl_err),\n                            curl_err);\n}\n\nstruct ScopedCURLTraits {\n  static CURL* InvalidValue() { return nullptr; }\n  static void Free(CURL* curl) {\n    if (curl) {\n      Libcurl::CurlEasyCleanup(curl);\n    }\n  }\n};\nusing ScopedCURL = base::ScopedGeneric<CURL*, ScopedCURLTraits>;\n\nclass CurlSList {\n public:\n  CurlSList() : list_(nullptr) {}\n\n  CurlSList(const CurlSList&) = delete;\n  CurlSList& operator=(const CurlSList&) = delete;\n\n  ~CurlSList() {\n    if (list_) {\n      Libcurl::CurlSlistFreeAll(list_);\n    }\n  }\n\n  curl_slist* get() const { return list_; }\n\n  bool Append(const char* data) {\n    curl_slist* list = Libcurl::CurlSlistAppend(list_, data);\n    if (!list_) {\n      list_ = list;\n    }\n    return list != nullptr;\n  }\n\n private:\n  curl_slist* list_;\n};\n\nclass ScopedClearString {\n public:\n  explicit ScopedClearString(std::string* string) : string_(string) {}\n\n  ScopedClearString(const ScopedClearString&) = delete;\n  ScopedClearString& operator=(const ScopedClearString&) = delete;\n\n  ~ScopedClearString() {\n    if (string_) {\n      string_->clear();\n    }\n  }\n\n  void Disarm() { string_ = nullptr; }\n\n private:\n  std::string* string_;\n};\n\nclass HTTPTransportLibcurl final : public HTTPTransport {\n public:\n  HTTPTransportLibcurl();\n\n  HTTPTransportLibcurl(const HTTPTransportLibcurl&) = delete;\n  HTTPTransportLibcurl& operator=(const HTTPTransportLibcurl&) = delete;\n\n  ~HTTPTransportLibcurl() override;\n\n  // HTTPTransport:\n  bool ExecuteSynchronously(std::string* response_body) override;\n\n private:\n  static size_t ReadRequestBody(char* buffer,\n                                size_t size,\n                                size_t nitems,\n                                void* userdata);\n  static size_t WriteResponseBody(char* buffer,\n                                  size_t size,\n                                  size_t nitems,\n                                  void* userdata);\n};\n\nHTTPTransportLibcurl::HTTPTransportLibcurl() : HTTPTransport() {}\n\nHTTPTransportLibcurl::~HTTPTransportLibcurl() {}\n\nbool HTTPTransportLibcurl::ExecuteSynchronously(std::string* response_body) {\n  DCHECK(body_stream());\n\n  response_body->clear();\n\n  // curl_easy_init() will do this on the first call if it hasn’t been done yet,\n  // but not in a thread-safe way as is done here.\n  static CURLcode curl_global_init_err = []() {\n    return Libcurl::CurlGlobalInit(CURL_GLOBAL_DEFAULT);\n  }();\n  if (curl_global_init_err != CURLE_OK) {\n    LOG(ERROR) << CurlErrorMessage(curl_global_init_err, \"curl_global_init\");\n    return false;\n  }\n\n  CurlSList curl_headers;\n  ScopedCURL curl(Libcurl::CurlEasyInit());\n  if (!curl.get()) {\n    LOG(ERROR) << \"curl_easy_init\";\n    return false;\n  }\n\n// These macros wrap the repetitive “try something, log an error and return\n// false on failure” pattern. Macros are convenient because the log messages\n// will point to the correct line number, which can help pinpoint a problem when\n// there are as many calls to these functions as there are here.\n#define TRY_CURL_EASY_SETOPT(curl, option, parameter)               \\\n  do {                                                              \\\n    CURLcode curl_err =                                             \\\n        Libcurl::CurlEasySetOpt((curl), (option), (parameter));     \\\n    if (curl_err != CURLE_OK) {                                     \\\n      LOG(ERROR) << CurlErrorMessage(curl_err, \"curl_easy_setopt\"); \\\n      return false;                                                 \\\n    }                                                               \\\n  } while (false)\n#define TRY_CURL_SLIST_APPEND(slist, data) \\\n  do {                                     \\\n    if (!(slist).Append(data)) {           \\\n      LOG(ERROR) << \"curl_slist_append\";   \\\n      return false;                        \\\n    }                                      \\\n  } while (false)\n\n  TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_USERAGENT, UserAgent().c_str());\n\n  // Accept and automatically decode any encoding that libcurl understands.\n  TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_ACCEPT_ENCODING, \"\");\n\n  TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_URL, url().c_str());\n\n  if (!root_ca_certificate_path().empty()) {\n    TRY_CURL_EASY_SETOPT(\n        curl.get(), CURLOPT_CAINFO, root_ca_certificate_path().value().c_str());\n  }\n\n  constexpr int kMillisecondsPerSecond = 1E3;\n  TRY_CURL_EASY_SETOPT(curl.get(),\n                       CURLOPT_TIMEOUT_MS,\n                       static_cast<long>(timeout() * kMillisecondsPerSecond));\n\n  // If the request body size is known ahead of time, a Content-Length header\n  // field will be present. Store that to use as CURLOPT_POSTFIELDSIZE_LARGE,\n  // which will both set the Content-Length field in the request header and\n  // inform libcurl of the request body size. Otherwise, use Transfer-Encoding:\n  // chunked, which does not require advance knowledge of the request body size.\n  bool chunked = true;\n  size_t content_length;\n  for (const auto& pair : headers()) {\n    if (pair.first == kContentLength) {\n      chunked = !base::StringToSizeT(pair.second, &content_length);\n      DCHECK(!chunked);\n    } else {\n      TRY_CURL_SLIST_APPEND(curl_headers,\n                            (pair.first + \": \" + pair.second).c_str());\n    }\n  }\n\n  if (method() == \"POST\") {\n    TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_POST, 1l);\n\n    // By default when sending a POST request, libcurl includes an “Expect:\n    // 100-continue” header field. Althogh this header is specified in HTTP/1.1\n    // (RFC 2616 §8.2.3, RFC 7231 §5.1.1), even collection servers that claim to\n    // speak HTTP/1.1 may not respond to it. When sending this header field,\n    // libcurl will wait for one second for the server to respond with a “100\n    // Continue” status before continuing to transmit the request body. This\n    // delay is avoided by telling libcurl not to send this header field at all.\n    // The drawback is that certain HTTP error statuses may not be received\n    // until after substantial amounts of data have been sent to the server.\n    TRY_CURL_SLIST_APPEND(curl_headers, \"Expect:\");\n\n    if (chunked) {\n      TRY_CURL_SLIST_APPEND(curl_headers, \"Transfer-Encoding: chunked\");\n    } else {\n      curl_off_t content_length_curl;\n      if (!AssignIfInRange(&content_length_curl, content_length)) {\n        LOG(ERROR) << base::StringPrintf(\"Content-Length %zu too large\",\n                                         content_length);\n        return false;\n      }\n      TRY_CURL_EASY_SETOPT(\n          curl.get(), CURLOPT_POSTFIELDSIZE_LARGE, content_length_curl);\n    }\n  } else if (method() != \"GET\") {\n    // Untested.\n    TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_CUSTOMREQUEST, method().c_str());\n  }\n\n  TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_HTTPHEADER, curl_headers.get());\n\n  TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_READFUNCTION, ReadRequestBody);\n  TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_READDATA, this);\n  TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEFUNCTION, WriteResponseBody);\n  TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEDATA, response_body);\n\n#undef TRY_CURL_EASY_SETOPT\n#undef TRY_CURL_SLIST_APPEND\n\n  // If a partial response body is received and then a failure occurs, ensure\n  // that response_body is cleared.\n  ScopedClearString clear_response_body(response_body);\n\n  // Do it.\n  CURLcode curl_err = Libcurl::CurlEasyPerform(curl.get());\n  if (curl_err != CURLE_OK) {\n    LOG(ERROR) << CurlErrorMessage(curl_err, \"curl_easy_perform\");\n    return false;\n  }\n\n  long status;\n  curl_err =\n      Libcurl::CurlEasyGetInfo(curl.get(), CURLINFO_RESPONSE_CODE, &status);\n  if (curl_err != CURLE_OK) {\n    LOG(ERROR) << CurlErrorMessage(curl_err, \"curl_easy_getinfo\");\n    return false;\n  }\n\n  if (status != 200) {\n    LOG(ERROR) << base::StringPrintf(\"HTTP status %ld\", status);\n    return false;\n  }\n\n  // The response body is complete. Don’t clear it.\n  clear_response_body.Disarm();\n\n  return true;\n}\n\n// static\nsize_t HTTPTransportLibcurl::ReadRequestBody(char* buffer,\n                                             size_t size,\n                                             size_t nitems,\n                                             void* userdata) {\n  HTTPTransportLibcurl* self =\n      reinterpret_cast<HTTPTransportLibcurl*>(userdata);\n\n  // This libcurl callback mimics the silly stdio-style fread() interface: size\n  // and nitems have been separated and must be multiplied.\n  base::CheckedNumeric<size_t> checked_len = base::CheckMul(size, nitems);\n  size_t len = checked_len.ValueOrDefault(std::numeric_limits<size_t>::max());\n\n  // Limit the read to what can be expressed in a FileOperationResult.\n  len = std::min(\n      len,\n      static_cast<size_t>(std::numeric_limits<FileOperationResult>::max()));\n\n  FileOperationResult bytes_read = self->body_stream()->GetBytesBuffer(\n      reinterpret_cast<uint8_t*>(buffer), len);\n  if (bytes_read < 0) {\n    return CURL_READFUNC_ABORT;\n  }\n\n  return bytes_read;\n}\n\n// static\nsize_t HTTPTransportLibcurl::WriteResponseBody(char* buffer,\n                                               size_t size,\n                                               size_t nitems,\n                                               void* userdata) {\n#if defined(MEMORY_SANITIZER)\n  // Work around an MSAN false-positive in passing `userdata`.\n  __msan_unpoison(&userdata, sizeof(userdata));\n#endif\n  std::string* response_body = reinterpret_cast<std::string*>(userdata);\n\n  // This libcurl callback mimics the silly stdio-style fread() interface: size\n  // and nitems have been separated and must be multiplied.\n  base::CheckedNumeric<size_t> checked_len = base::CheckMul(size, nitems);\n  size_t len = checked_len.ValueOrDefault(std::numeric_limits<size_t>::max());\n\n  response_body->append(buffer, len);\n  return len;\n}\n\n}  // namespace\n\n// static\nstd::unique_ptr<HTTPTransport> HTTPTransport::Create() {\n  return std::unique_ptr<HTTPTransport>(\n      Libcurl::Initialized() ? new HTTPTransportLibcurl() : nullptr);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_transport_mac.mm",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_transport.h\"\n\n#import <Foundation/Foundation.h>\n#include <sys/utsname.h>\n\n#include \"base/apple/bridging.h\"\n#include \"base/apple/foundation_util.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/sys_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"package.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/misc/metrics.h\"\n#include \"util/net/http_body.h\"\n\n// An implementation of NSInputStream that reads from a\n// crashpad::HTTPBodyStream.\n@interface CrashpadHTTPBodyStreamTransport : NSInputStream {\n @private\n  NSStreamStatus _streamStatus;\n  id<NSStreamDelegate> __strong _delegate;\n  crashpad::HTTPBodyStream* _bodyStream;  // weak\n}\n- (instancetype)initWithBodyStream:(crashpad::HTTPBodyStream*)bodyStream;\n@end\n\n@implementation CrashpadHTTPBodyStreamTransport\n\n- (instancetype)initWithBodyStream:(crashpad::HTTPBodyStream*)bodyStream {\n  if ((self = [super init])) {\n    _streamStatus = NSStreamStatusNotOpen;\n    _bodyStream = bodyStream;\n  }\n  return self;\n}\n\n// NSInputStream:\n\n- (BOOL)hasBytesAvailable {\n  // Per Apple's documentation: \"May also return YES if a read must be attempted\n  // in order to determine the availability of bytes.\"\n  switch (_streamStatus) {\n    case NSStreamStatusAtEnd:\n    case NSStreamStatusClosed:\n    case NSStreamStatusError:\n      return NO;\n    default:\n      return YES;\n  }\n}\n\n- (NSInteger)read:(uint8_t*)buffer maxLength:(NSUInteger)maxLen {\n  _streamStatus = NSStreamStatusReading;\n\n  crashpad::FileOperationResult rv =\n      _bodyStream->GetBytesBuffer(buffer, maxLen);\n\n  if (rv == 0)\n    _streamStatus = NSStreamStatusAtEnd;\n  else if (rv < 0)\n    _streamStatus = NSStreamStatusError;\n  else\n    _streamStatus = NSStreamStatusOpen;\n\n  return rv;\n}\n\n- (BOOL)getBuffer:(uint8_t**)buffer length:(NSUInteger*)length {\n  return NO;\n}\n\n// NSStream:\n\n- (void)scheduleInRunLoop:(NSRunLoop*)runLoop forMode:(NSString*)mode {\n}\n\n- (void)removeFromRunLoop:(NSRunLoop*)runLoop forMode:(NSString*)mode {\n}\n\n- (void)open {\n  _streamStatus = NSStreamStatusOpen;\n}\n\n- (void)close {\n  _streamStatus = NSStreamStatusClosed;\n}\n\n- (NSStreamStatus)streamStatus {\n  return _streamStatus;\n}\n\n- (id<NSStreamDelegate>)delegate {\n  return _delegate;\n}\n\n- (void)setDelegate:(id)delegate {\n  _delegate = delegate;\n}\n\n- (id)propertyForKey:(NSStreamPropertyKey)key {\n  return nil;\n}\n\n- (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key {\n  return NO;\n}\n\n@end\n\nnamespace crashpad {\n\nnamespace {\n\nNSString* AppendEscapedFormat(NSString* base,\n                              NSString* format,\n                              NSString* data) {\n  return [base stringByAppendingFormat:\n                   format,\n                   [data stringByAddingPercentEncodingWithAllowedCharacters:\n                             [[NSCharacterSet\n                                 characterSetWithCharactersInString:\n                                     @\"()<>@,;:\\\\\\\"/[]?={} \\t\"] invertedSet]]];\n}\n\n// This builds the same User-Agent string that CFNetwork would build internally,\n// but it uses PACKAGE_NAME and PACKAGE_VERSION in place of values obtained from\n// the main bundle’s Info.plist.\nNSString* UserAgentString() {\n  NSString* user_agent = [NSString string];\n\n  // CFNetwork would use the main bundle’s CFBundleName, or the main\n  // executable’s filename if none.\n  user_agent = AppendEscapedFormat(user_agent, @\"%@\", @PACKAGE_NAME);\n\n  // CFNetwork would use the main bundle’s CFBundleVersion, or the string\n  // “(unknown version)” if none.\n  user_agent = AppendEscapedFormat(user_agent, @\"/%@\", @PACKAGE_VERSION);\n\n  // Expected to be CFNetwork.\n  NSBundle* nsurl_bundle = [NSBundle bundleForClass:[NSURLRequest class]];\n  NSString* bundle_name = base::apple::ObjCCast<NSString>([nsurl_bundle\n      objectForInfoDictionaryKey:base::apple::CFToNSPtrCast(kCFBundleNameKey)]);\n  if (bundle_name) {\n    user_agent = AppendEscapedFormat(user_agent, @\" %@\", bundle_name);\n\n    NSString* bundle_version = base::apple::ObjCCast<NSString>(\n        [nsurl_bundle objectForInfoDictionaryKey:base::apple::CFToNSPtrCast(\n                                                     kCFBundleVersionKey)]);\n    if (bundle_version) {\n      user_agent = AppendEscapedFormat(user_agent, @\"/%@\", bundle_version);\n    }\n  }\n\n  utsname os;\n  if (uname(&os) != 0) {\n    PLOG(WARNING) << \"uname\";\n  } else {\n    user_agent = AppendEscapedFormat(user_agent, @\" %@\", @(os.sysname));\n    user_agent = AppendEscapedFormat(user_agent, @\"/%@\", @(os.release));\n\n    // CFNetwork just uses the equivalent of os.machine to obtain the native\n    // (kernel) architecture. Here, give the process’ architecture as well as\n    // the native architecture. Use the same strings that the kernel would, so\n    // that they can be de-duplicated.\n#if defined(ARCH_CPU_X86)\n    NSString* arch = @\"i386\";\n#elif defined(ARCH_CPU_X86_64)\n    NSString* arch = @\"x86_64\";\n#elif defined(ARCH_CPU_ARM64)\n    NSString* arch = @\"arm64\";\n#else\n#error Port\n#endif\n    user_agent = AppendEscapedFormat(user_agent, @\" (%@\", arch);\n\n    NSString* machine = @(os.machine);\n    if (![machine isEqualToString:arch]) {\n      user_agent = AppendEscapedFormat(user_agent, @\"; %@\", machine);\n    }\n\n    user_agent = [user_agent stringByAppendingString:@\")\"];\n  }\n\n  return user_agent;\n}\n\nclass HTTPTransportMac final : public HTTPTransport {\n public:\n  HTTPTransportMac();\n\n  HTTPTransportMac(const HTTPTransportMac&) = delete;\n  HTTPTransportMac& operator=(const HTTPTransportMac&) = delete;\n\n  ~HTTPTransportMac() override;\n\n  bool ExecuteSynchronously(std::string* response_body) override;\n};\n\nHTTPTransportMac::HTTPTransportMac() : HTTPTransport() {\n}\n\nHTTPTransportMac::~HTTPTransportMac() {\n}\n\nbool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) {\n  DCHECK(body_stream());\n\n  @autoreleasepool {\n    NSString* url_ns_string = base::SysUTF8ToNSString(url());\n    NSURL* url = [NSURL URLWithString:url_ns_string];\n    NSMutableURLRequest* request =\n        [NSMutableURLRequest requestWithURL:url\n                                cachePolicy:NSURLRequestUseProtocolCachePolicy\n                            timeoutInterval:timeout()];\n    [request setHTTPMethod:base::SysUTF8ToNSString(method())];\n\n    // If left to its own devices, CFNetwork would build a user-agent string\n    // based on keys in the main bundle’s Info.plist, giving ugly results if\n    // there is no Info.plist. Provide a User-Agent string similar to the one\n    // that CFNetwork would use, but with appropriate values in place of the\n    // Info.plist-derived strings.\n    [request setValue:UserAgentString() forHTTPHeaderField:@\"User-Agent\"];\n\n    for (const auto& pair : headers()) {\n      [request setValue:base::SysUTF8ToNSString(pair.second)\n          forHTTPHeaderField:base::SysUTF8ToNSString(pair.first)];\n    }\n\n    NSInputStream* input_stream = [[CrashpadHTTPBodyStreamTransport alloc]\n        initWithBodyStream:body_stream()];\n    [request setHTTPBodyStream:input_stream];\n\n    NSURLResponse* response = nil;\n    NSError* error = nil;\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n    // Deprecated in OS X 10.11. The suggested replacement, NSURLSession, is\n    // only available on 10.9 and later, and this needs to run on earlier\n    // releases.\n    NSData* body = [NSURLConnection sendSynchronousRequest:request\n                                         returningResponse:&response\n                                                     error:&error];\n#pragma clang diagnostic pop\n\n    if (error) {\n      Metrics::CrashUploadErrorCode(error.code);\n      LOG(ERROR) << [[error localizedDescription] UTF8String] << \" (\"\n                 << [[error domain] UTF8String] << \" \" << [error code] << \")\";\n      return false;\n    }\n    if (!response) {\n      LOG(ERROR) << \"no response\";\n      return false;\n    }\n    NSHTTPURLResponse* http_response =\n        base::apple::ObjCCast<NSHTTPURLResponse>(response);\n    if (!http_response) {\n      LOG(ERROR) << \"no http_response\";\n      return false;\n    }\n    NSInteger http_status = [http_response statusCode];\n    if (http_status < 200 || http_status > 203) {\n      LOG(ERROR) << base::StringPrintf(\"HTTP status %ld\",\n                                       implicit_cast<long>(http_status));\n      return false;\n    }\n\n    if (response_body) {\n      response_body->assign(static_cast<const char*>([body bytes]),\n                            [body length]);\n    }\n\n    return true;\n  }\n}\n\n}  // namespace\n\n// static\nstd::unique_ptr<HTTPTransport> HTTPTransport::Create() {\n  return std::unique_ptr<HTTPTransport>(new HTTPTransportMac());\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_transport_socket.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <fcntl.h>\n#include <netdb.h>\n#include <poll.h>\n#include <string.h>\n#include <sys/socket.h>\n\n#include <iterator>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/scoped_generic.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"util/file/file_io.h\"\n#include \"util/net/http_body.h\"\n#include \"util/net/http_transport.h\"\n#include \"util/net/url.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n#include \"util/string/split_string.h\"\n\n#if defined(CRASHPAD_USE_BORINGSSL)\n#include <openssl/ssl.h>\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\nconstexpr const char kCRLFTerminator[] = \"\\r\\n\";\n\nclass HTTPTransportSocket final : public HTTPTransport {\n public:\n  HTTPTransportSocket() = default;\n\n  HTTPTransportSocket(const HTTPTransportSocket&) = delete;\n  HTTPTransportSocket& operator=(const HTTPTransportSocket&) = delete;\n\n  ~HTTPTransportSocket() override = default;\n\n  bool ExecuteSynchronously(std::string* response_body) override;\n};\n\nstruct ScopedAddrinfoTraits {\n  static addrinfo* InvalidValue() { return nullptr; }\n  static void Free(addrinfo* ai) { freeaddrinfo(ai); }\n};\nusing ScopedAddrinfo = base::ScopedGeneric<addrinfo*, ScopedAddrinfoTraits>;\n\nclass Stream {\n public:\n  virtual ~Stream() = default;\n\n  virtual bool LoggingWrite(const void* data, size_t size) = 0;\n  virtual bool LoggingRead(void* data, size_t size) = 0;\n  virtual bool LoggingReadToEOF(std::string* contents) = 0;\n};\n\nclass FdStream : public Stream {\n public:\n  explicit FdStream(int fd) : fd_(fd) { CHECK(fd_ >= 0); }\n\n  FdStream(const FdStream&) = delete;\n  FdStream& operator=(const FdStream&) = delete;\n\n  bool LoggingWrite(const void* data, size_t size) override {\n    return LoggingWriteFile(fd_, data, size);\n  }\n\n  bool LoggingRead(void* data, size_t size) override {\n    return LoggingReadFileExactly(fd_, data, size);\n  }\n\n  bool LoggingReadToEOF(std::string* result) override {\n    return crashpad::LoggingReadToEOF(fd_, result);\n  }\n\n private:\n  int fd_;\n};\n\n#if defined(CRASHPAD_USE_BORINGSSL)\nclass SSLStream : public Stream {\n public:\n  SSLStream() = default;\n\n  SSLStream(const SSLStream&) = delete;\n  SSLStream& operator=(const SSLStream&) = delete;\n\n  bool Initialize(const base::FilePath& root_cert_path,\n                  int sock,\n                  const std::string& hostname) {\n    SSL_library_init();\n\n    ctx_.reset(SSL_CTX_new(TLS_method()));\n    if (!ctx_.is_valid()) {\n      LOG(ERROR) << \"SSL_CTX_new\";\n      return false;\n    }\n\n    if (SSL_CTX_set_min_proto_version(ctx_.get(), TLS1_2_VERSION) <= 0) {\n      LOG(ERROR) << \"SSL_CTX_set_min_proto_version\";\n      return false;\n    }\n\n    SSL_CTX_set_verify(ctx_.get(), SSL_VERIFY_PEER, nullptr);\n    SSL_CTX_set_verify_depth(ctx_.get(), 5);\n\n    if (!root_cert_path.empty()) {\n      if (SSL_CTX_load_verify_locations(\n              ctx_.get(), root_cert_path.value().c_str(), nullptr) <= 0) {\n        LOG(ERROR) << \"SSL_CTX_load_verify_locations\";\n        return false;\n      }\n    } else {\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n      if (SSL_CTX_load_verify_locations(\n              ctx_.get(), nullptr, \"/etc/ssl/certs\") <= 0) {\n        LOG(ERROR) << \"SSL_CTX_load_verify_locations\";\n        return false;\n      }\n#elif BUILDFLAG(IS_FUCHSIA)\n      if (SSL_CTX_load_verify_locations(\n              ctx_.get(), \"/config/ssl/cert.pem\", nullptr) <= 0) {\n        LOG(ERROR) << \"SSL_CTX_load_verify_locations\";\n        return false;\n      }\n#else\n#error cert store location\n#endif\n    }\n\n    ssl_.reset(SSL_new(ctx_.get()));\n    if (!ssl_.is_valid()) {\n      LOG(ERROR) << \"SSL_new\";\n      return false;\n    }\n\n    BIO* bio = BIO_new_socket(sock, BIO_NOCLOSE);\n    if (!bio) {\n      LOG(ERROR) << \"BIO_new_socket\";\n      return false;\n    }\n\n    // SSL_set_bio() takes ownership of |bio|.\n    SSL_set_bio(ssl_.get(), bio, bio);\n\n    if (SSL_set_tlsext_host_name(ssl_.get(), hostname.c_str()) == 0) {\n      LOG(ERROR) << \"SSL_set_tlsext_host_name\";\n      return false;\n    }\n\n    if (SSL_connect(ssl_.get()) <= 0) {\n      LOG(ERROR) << \"SSL_connect\";\n      return false;\n    }\n\n    return true;\n  }\n\n  bool LoggingWrite(const void* data, size_t size) override {\n    return SSL_write(ssl_.get(), data, size) != 0;\n  }\n\n  bool LoggingRead(void* data, size_t size) override {\n    return SSL_read(ssl_.get(), data, size) != 0;\n  }\n\n  bool LoggingReadToEOF(std::string* contents) override {\n    contents->clear();\n    char buffer[4096];\n    FileOperationResult rv;\n    while ((rv = SSL_read(ssl_.get(), buffer, sizeof(buffer))) > 0) {\n      DCHECK_LE(static_cast<size_t>(rv), sizeof(buffer));\n      contents->append(buffer, rv);\n    }\n    if (rv < 0) {\n      LOG(ERROR) << \"SSL_read\";\n      contents->clear();\n      return false;\n    }\n    return true;\n  }\n\n private:\n  struct ScopedSSLCTXTraits {\n    static SSL_CTX* InvalidValue() { return nullptr; }\n    static void Free(SSL_CTX* ctx) { SSL_CTX_free(ctx); }\n  };\n  using ScopedSSLCTX = base::ScopedGeneric<SSL_CTX*, ScopedSSLCTXTraits>;\n\n  struct ScopedSSLTraits {\n    static SSL* InvalidValue() { return nullptr; }\n    static void Free(SSL* ssl) {\n      SSL_shutdown(ssl);\n      SSL_free(ssl);\n    }\n  };\n  using ScopedSSL = base::ScopedGeneric<SSL*, ScopedSSLTraits>;\n\n  ScopedSSLCTX ctx_;\n  ScopedSSL ssl_;\n};\n#endif\n\nbool WaitUntilSocketIsReady(int sock) {\n  pollfd pollfds;\n  pollfds.fd = sock;\n  pollfds.events = POLLIN | POLLPRI | POLLOUT;\n  constexpr int kTimeoutMS = 1000;\n  int ret = HANDLE_EINTR(poll(&pollfds, 1, kTimeoutMS));\n  if (ret < 0) {\n    PLOG(ERROR) << \"poll\";\n    return false;\n  } else if (ret == 1) {\n    if (pollfds.revents & POLLERR) {\n      int err;\n      socklen_t err_len = sizeof(err);\n      if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &err_len) != 0) {\n        PLOG(ERROR) << \"getsockopt\";\n      } else {\n        errno = err;\n        PLOG(ERROR) << \"POLLERR\";\n      }\n      return false;\n    }\n    if (pollfds.revents & POLLHUP) {\n      return false;\n    }\n    return (pollfds.revents & POLLIN) != 0 || (pollfds.revents & POLLOUT) != 0;\n  }\n\n  // Timeout.\n  return false;\n}\n\nclass ScopedSetNonblocking {\n public:\n  explicit ScopedSetNonblocking(int sock) : sock_(sock) {\n    int flags = fcntl(sock, F_GETFL, 0);\n    if (flags < 0) {\n      PLOG(ERROR) << \"fcntl\";\n      sock_ = -1;\n      return;\n    }\n\n    if (fcntl(sock_, F_SETFL, flags | O_NONBLOCK) < 0) {\n      PLOG(ERROR) << \"fcntl\";\n      sock_ = -1;\n    }\n  }\n\n  ScopedSetNonblocking(const ScopedSetNonblocking&) = delete;\n  ScopedSetNonblocking& operator=(const ScopedSetNonblocking&) = delete;\n\n  ~ScopedSetNonblocking() {\n    if (sock_ >= 0) {\n      int flags = fcntl(sock_, F_GETFL, 0);\n      if (flags < 0) {\n        PLOG(ERROR) << \"fcntl\";\n        return;\n      }\n\n      if (fcntl(sock_, F_SETFL, flags & (~O_NONBLOCK)) < 0) {\n        PLOG(ERROR) << \"fcntl\";\n      }\n    }\n  }\n\n private:\n  int sock_;\n};\n\nbase::ScopedFD CreateSocket(const std::string& hostname,\n                            const std::string& port) {\n  addrinfo hints = {};\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_protocol = 0;\n  hints.ai_flags = 0;\n\n  addrinfo* addrinfo_raw;\n  if (getaddrinfo(hostname.c_str(), port.c_str(), &hints, &addrinfo_raw) < 0) {\n    PLOG(ERROR) << \"getaddrinfo\";\n    return base::ScopedFD();\n  }\n  ScopedAddrinfo addrinfo(addrinfo_raw);\n\n  for (const auto* ap = addrinfo.get(); ap; ap = ap->ai_next) {\n    base::ScopedFD result(\n        socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol));\n    if (!result.is_valid()) {\n      continue;\n    }\n\n    {\n      // Set socket to non-blocking to avoid hanging for a long time if the\n      // network is down.\n      ScopedSetNonblocking nonblocking(result.get());\n\n      if (HANDLE_EINTR(connect(result.get(), ap->ai_addr, ap->ai_addrlen)) <\n          0) {\n        if (errno != EINPROGRESS) {\n          PLOG(ERROR) << \"connect\";\n        } else if (WaitUntilSocketIsReady(result.get())) {\n          return result;\n        }\n        return base::ScopedFD();\n      }\n\n      return result;\n    }\n  }\n\n  return base::ScopedFD();\n}\n\nbool WriteRequest(Stream* stream,\n                  const std::string& method,\n                  const std::string& resource,\n                  const HTTPHeaders& headers,\n                  HTTPBodyStream* body_stream) {\n  std::string request_line = base::StringPrintf(\n      \"%s %s HTTP/1.0\\r\\n\", method.c_str(), resource.c_str());\n  if (!stream->LoggingWrite(request_line.data(), request_line.size()))\n    return false;\n\n  // Write headers, and determine if Content-Length has been specified.\n  bool chunked = true;\n  size_t content_length = 0;\n  for (const auto& header : headers) {\n    std::string header_str = base::StringPrintf(\n        \"%s: %s\\r\\n\", header.first.c_str(), header.second.c_str());\n    if (header.first == kContentLength) {\n      chunked = !base::StringToSizeT(header.second, &content_length);\n      DCHECK(!chunked);\n    }\n\n    if (!stream->LoggingWrite(header_str.data(), header_str.size()))\n      return false;\n  }\n\n  // If no Content-Length, then encode as chunked, so add that header too.\n  if (chunked) {\n    static constexpr const char kTransferEncodingChunked[] =\n        \"Transfer-Encoding: chunked\\r\\n\";\n    if (!stream->LoggingWrite(kTransferEncodingChunked,\n                              strlen(kTransferEncodingChunked))) {\n      return false;\n    }\n  }\n\n  if (!stream->LoggingWrite(kCRLFTerminator, strlen(kCRLFTerminator))) {\n    return false;\n  }\n\n  FileOperationResult data_bytes;\n  do {\n    constexpr size_t kCRLFSize = std::size(kCRLFTerminator) - 1;\n    struct __attribute__((packed)) {\n      char size[8];\n      char crlf[2];\n      uint8_t data[32 * 1024 + kCRLFSize];\n    } buf;\n    static_assert(\n        sizeof(buf) == sizeof(buf.size) + sizeof(buf.crlf) + sizeof(buf.data),\n        \"buf should not have padding\");\n\n    // Read a block of data.\n    data_bytes =\n        body_stream->GetBytesBuffer(buf.data, sizeof(buf.data) - kCRLFSize);\n    if (data_bytes == -1) {\n      return false;\n    }\n    DCHECK_GE(data_bytes, 0);\n    DCHECK_LE(static_cast<size_t>(data_bytes), sizeof(buf.data) - kCRLFSize);\n\n    void* write_start;\n    size_t write_size;\n\n    if (chunked) {\n      // Chunked encoding uses the entirety of buf. buf.size is presented in\n      // hexadecimal without any leading \"0x\". The terminating CR and LF will be\n      // placed immediately following the used portion of buf.data, even if\n      // buf.data is not full.\n\n      char tmp[9];\n      int rv = snprintf(tmp,\n                        sizeof(tmp),\n                        \"%08x\",\n                        base::checked_cast<unsigned int>(data_bytes));\n      DCHECK_EQ(static_cast<size_t>(rv), sizeof(buf.size));\n      strncpy(buf.size, tmp, sizeof(buf.size));\n      DCHECK_NE(buf.size[sizeof(buf.size) - 1], '\\0');\n\n      memcpy(&buf.crlf[0], kCRLFTerminator, kCRLFSize);\n      memcpy(&buf.data[data_bytes], kCRLFTerminator, kCRLFSize);\n\n      // Skip leading zeroes in the chunk size.\n      size_t size_len;\n      for (size_len = sizeof(buf.size); size_len > 1; --size_len) {\n        if (buf.size[sizeof(buf.size) - size_len] != '0') {\n          break;\n        }\n      }\n\n      write_start = static_cast<char*>(buf.crlf) - size_len;\n      write_size = size_len + sizeof(buf.crlf) + data_bytes + kCRLFSize;\n    } else {\n      // When not using chunked encoding, only use buf.data.\n      write_start = buf.data;\n      write_size = data_bytes;\n    }\n\n    // write_size will be 0 at EOF in non-chunked mode. Skip the write in that\n    // case. In contrast, at EOF in chunked mode, a zero-length chunk must be\n    // sent to signal EOF. This will happen when processing the EOF indicated by\n    // a 0 return from body_stream()->GetBytesBuffer() above.\n    if (write_size != 0) {\n      if (!stream->LoggingWrite(write_start, write_size))\n        return false;\n    }\n  } while (data_bytes > 0);\n\n  return true;\n}\n\nbool ReadLine(Stream* stream, std::string* line) {\n  line->clear();\n  for (;;) {\n    char byte;\n    if (!stream->LoggingRead(&byte, 1)) {\n      return false;\n    }\n\n    line->append(&byte, 1);\n    if (byte == '\\n')\n      return true;\n  }\n}\n\nbool StartsWith(const std::string& str, const char* with, size_t len) {\n  return str.compare(0, len, with) == 0;\n}\n\nbool ReadResponseLine(Stream* stream) {\n  std::string response_line;\n  if (!ReadLine(stream, &response_line)) {\n    LOG(ERROR) << \"ReadLine\";\n    return false;\n  }\n  static constexpr const char kHttp10[] = \"HTTP/1.0 \";\n  static constexpr const char kHttp11[] = \"HTTP/1.1 \";\n  if (!(StartsWith(response_line, kHttp10, strlen(kHttp10)) ||\n        StartsWith(response_line, kHttp11, strlen(kHttp11))) ||\n      response_line.size() < strlen(kHttp10) + 3 ||\n      response_line.at(strlen(kHttp10) + 3) != ' ') {\n    return false;\n  }\n  unsigned int http_status = 0;\n  return base::StringToUint(response_line.substr(strlen(kHttp10), 3),\n                            &http_status) &&\n         http_status >= 200 && http_status <= 203;\n}\n\nbool ReadResponseHeaders(Stream* stream, HTTPHeaders* headers) {\n  for (;;) {\n    std::string line;\n    if (!ReadLine(stream, &line)) {\n      return false;\n    }\n\n    if (line == kCRLFTerminator) {\n      return true;\n    }\n\n    std::string left, right;\n    if (!SplitStringFirst(line, ':', &left, &right)) {\n      LOG(ERROR) << \"SplitStringFirst\";\n      return false;\n    }\n    DCHECK_EQ(right[right.size() - 1], '\\n');\n    DCHECK_EQ(right[right.size() - 2], '\\r');\n    DCHECK_EQ(right[0], ' ');\n    DCHECK_NE(right[1], ' ');\n    right = right.substr(1, right.size() - 3);\n    (*headers)[left] = right;\n  }\n}\n\nbool ReadContentChunked(Stream* stream, std::string* body) {\n  // TODO(scottmg): https://crashpad.chromium.org/bug/196.\n  LOG(ERROR) << \"TODO(scottmg): chunked response read\";\n  return false;\n}\n\nbool ReadResponse(Stream* stream, std::string* response_body) {\n  response_body->clear();\n\n  if (!ReadResponseLine(stream)) {\n    return false;\n  }\n\n  HTTPHeaders response_headers;\n  if (!ReadResponseHeaders(stream, &response_headers)) {\n    return false;\n  }\n\n  auto it = response_headers.find(\"Content-Length\");\n  size_t len = 0;\n  if (it != response_headers.end()) {\n    if (!base::StringToSizeT(it->second, &len)) {\n      LOG(ERROR) << \"invalid Content-Length\";\n      return false;\n    }\n  }\n\n  if (len) {\n    response_body->resize(len, 0);\n    return stream->LoggingRead(&(*response_body)[0], len);\n  }\n\n  it = response_headers.find(\"Transfer-Encoding\");\n  bool chunked = false;\n  if (it != response_headers.end() && it->second == \"chunked\") {\n    chunked = true;\n  }\n\n  return chunked ? ReadContentChunked(stream, response_body)\n                 : stream->LoggingReadToEOF(response_body);\n}\n\nbool HTTPTransportSocket::ExecuteSynchronously(std::string* response_body) {\n  std::string scheme, hostname, port, resource;\n  if (!CrackURL(url(), &scheme, &hostname, &port, &resource)) {\n    return false;\n  }\n\n#if !defined(CRASHPAD_USE_BORINGSSL)\n  CHECK(scheme == \"http\") << \"Got \" << scheme << \" for scheme in '\" << url()\n                          << \"'\";\n#endif\n\n  base::ScopedFD sock(CreateSocket(hostname, port));\n  if (!sock.is_valid()) {\n    return false;\n  }\n\n#if defined(CRASHPAD_USE_BORINGSSL)\n  std::unique_ptr<Stream> stream;\n  if (scheme == \"https\") {\n    auto ssl_stream = std::make_unique<SSLStream>();\n    if (!ssl_stream->Initialize(\n            root_ca_certificate_path(), sock.get(), hostname)) {\n      LOG(ERROR) << \"SSLStream Initialize\";\n      return false;\n    }\n    stream = std::move(ssl_stream);\n  } else {\n    stream = std::make_unique<FdStream>(sock.get());\n  }\n#else  // CRASHPAD_USE_BORINGSSL\n  std::unique_ptr<Stream> stream(std::make_unique<FdStream>(sock.get()));\n#endif  // CRASHPAD_USE_BORINGSSL\n\n  if (!WriteRequest(\n          stream.get(), method(), resource, headers(), body_stream())) {\n    return false;\n  }\n\n  if (!ReadResponse(stream.get(), response_body)) {\n    return false;\n  }\n\n  return true;\n}\n\n}  // namespace\n\n// static\nstd::unique_ptr<HTTPTransport> HTTPTransport::Create() {\n  return std::unique_ptr<HTTPTransportSocket>(new HTTPTransportSocket);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_transport_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_transport.h\"\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"base/files/file_path.h\"\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"test/test_paths.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/random_string.h\"\n#include \"util/net/http_body.h\"\n#include \"util/net/http_headers.h\"\n#include \"util/net/http_multipart_builder.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n#if BUILDFLAG(IS_WIN)\nstd::string ToUTF8IfWin(const std::wstring& x) {\n  return base::WideToUTF8(x);\n}\n#else\nstd::string ToUTF8IfWin(const std::string& x) {\n  return x;\n}\n#endif\n\nclass HTTPTransportTestFixture : public MultiprocessExec {\n public:\n  using RequestValidator = void (*)(HTTPTransportTestFixture*,\n                                    const std::string&);\n\n  HTTPTransportTestFixture(const std::string& scheme,\n                           const HTTPHeaders& headers,\n                           std::unique_ptr<HTTPBodyStream> body_stream,\n                           uint16_t http_response_code,\n                           RequestValidator request_validator)\n      : MultiprocessExec(),\n        headers_(headers),\n        body_stream_(std::move(body_stream)),\n        response_code_(http_response_code),\n        request_validator_(request_validator),\n        cert_(),\n        scheme_and_host_() {\n    base::FilePath server_path = TestPaths::Executable().DirName().Append(\n        FILE_PATH_LITERAL(\"http_transport_test_server\")\n#if BUILDFLAG(IS_WIN)\n            FILE_PATH_LITERAL(\".exe\")\n#endif\n    );\n\n    if (scheme == \"http\") {\n      scheme_and_host_ = \"http://localhost\";\n      SetChildCommand(server_path, nullptr);\n    } else {\n      std::vector<std::string> args;\n      cert_ = TestPaths::TestDataRoot().Append(\n          FILE_PATH_LITERAL(\"util/net/testdata/crashpad_util_test_cert.pem\"));\n      args.push_back(ToUTF8IfWin(cert_.value()));\n      args.emplace_back(\n          ToUTF8IfWin(TestPaths::TestDataRoot()\n                          .Append(FILE_PATH_LITERAL(\n                              \"util/net/testdata/crashpad_util_test_key.pem\"))\n                          .value()));\n      SetChildCommand(server_path, &args);\n      scheme_and_host_ = \"https://localhost\";\n    }\n  }\n\n  const HTTPHeaders& headers() { return headers_; }\n\n private:\n  void MultiprocessParent() override {\n    // Use Logging*File() instead of Checked*File() so that the test can fail\n    // gracefully with a Google Test assertion if the child does not execute\n    // properly.\n\n    // The child will write the HTTP server port number as a packed unsigned\n    // short to stdout.\n    uint16_t port;\n    ASSERT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &port, sizeof(port)));\n\n    // Then the parent will tell the web server what response code to send\n    // for the HTTP request.\n    ASSERT_TRUE(LoggingWriteFile(\n        WritePipeHandle(), &response_code_, sizeof(response_code_)));\n\n    // The parent will also tell the web server what response body to send back.\n    // The web server will only send the response body if the response code is\n    // 200.\n    const std::string random_string = RandomString();\n\n    ASSERT_TRUE(LoggingWriteFile(\n        WritePipeHandle(), random_string.c_str(), random_string.size()));\n\n    // Now execute the HTTP request.\n    std::unique_ptr<HTTPTransport> transport(HTTPTransport::Create());\n    transport->SetMethod(\"POST\");\n\n    if (!cert_.empty()) {\n      transport->SetRootCACertificatePath(cert_);\n    }\n    transport->SetURL(\n        base::StringPrintf(\"%s:%d/upload\", scheme_and_host_.c_str(), port));\n    for (const auto& pair : headers_) {\n      transport->SetHeader(pair.first, pair.second);\n    }\n    transport->SetBodyStream(std::move(body_stream_));\n\n    std::string response_body;\n    bool success = transport->ExecuteSynchronously(&response_body);\n    if (response_code_ >= 200 && response_code_ <= 203) {\n      EXPECT_TRUE(success);\n      std::string expect_response_body = random_string + \"\\r\\n\";\n      EXPECT_EQ(response_body, expect_response_body);\n    } else {\n      EXPECT_FALSE(success);\n      EXPECT_TRUE(response_body.empty());\n    }\n\n    // Read until the child's stdout closes.\n    std::string request;\n    char buf[32];\n    FileOperationResult bytes_read;\n    while ((bytes_read = ReadFile(ReadPipeHandle(), buf, sizeof(buf))) != 0) {\n      ASSERT_GE(bytes_read, 0);\n      request.append(buf, bytes_read);\n    }\n\n    if (request_validator_)\n      request_validator_(this, request);\n  }\n\n  HTTPHeaders headers_;\n  std::unique_ptr<HTTPBodyStream> body_stream_;\n  uint16_t response_code_;\n  RequestValidator request_validator_;\n  base::FilePath cert_;\n  std::string scheme_and_host_;\n};\n\nconstexpr char kMultipartFormData[] = \"multipart/form-data\";\n\nvoid GetHeaderField(const std::string& request,\n                    const std::string& header,\n                    std::string* value) {\n  size_t index = request.find(header);\n  ASSERT_NE(index, std::string::npos);\n  // Since the header is never the first line of the request, it should always\n  // be preceded by a CRLF.\n  EXPECT_EQ(request[index - 1], '\\n');\n  EXPECT_EQ(request[index - 2], '\\r');\n\n  index += header.length();\n  EXPECT_EQ(request[index++], ':');\n  // Per RFC 7230 §3.2, there can be one or more spaces or horizontal tabs.\n  // For testing purposes, just assume one space.\n  EXPECT_EQ(request[index++], ' ');\n\n  size_t header_end = request.find('\\r', index);\n  ASSERT_NE(header_end, std::string::npos);\n\n  *value = request.substr(index, header_end - index);\n}\n\nvoid GetMultipartBoundary(const std::string& request,\n                          std::string* multipart_boundary) {\n  std::string content_type;\n  GetHeaderField(request, kContentType, &content_type);\n\n  ASSERT_GE(content_type.length(), strlen(kMultipartFormData));\n  size_t index = strlen(kMultipartFormData);\n  EXPECT_EQ(content_type.substr(0, index), kMultipartFormData);\n\n  EXPECT_EQ(content_type[index++], ';');\n\n  size_t boundary_begin = content_type.find('=', index);\n  ASSERT_NE(boundary_begin, std::string::npos);\n  EXPECT_EQ(content_type[boundary_begin++], '=');\n  if (multipart_boundary) {\n    *multipart_boundary = content_type.substr(boundary_begin);\n  }\n}\n\nconstexpr char kBoundaryEq[] = \"boundary=\";\n\nvoid ValidFormData(HTTPTransportTestFixture* fixture,\n                   const std::string& request) {\n  std::string actual_boundary;\n  GetMultipartBoundary(request, &actual_boundary);\n\n  const auto& content_type = fixture->headers().find(kContentType);\n  ASSERT_NE(content_type, fixture->headers().end());\n\n  size_t boundary = content_type->second.find(kBoundaryEq);\n  ASSERT_NE(boundary, std::string::npos);\n  std::string expected_boundary =\n      content_type->second.substr(boundary + strlen(kBoundaryEq));\n  EXPECT_EQ(actual_boundary, expected_boundary);\n\n  size_t body_start = request.find(\"\\r\\n\\r\\n\");\n  ASSERT_NE(body_start, std::string::npos);\n  body_start += 4;\n\n  std::string expected = \"--\" + expected_boundary + \"\\r\\n\";\n  expected += \"Content-Disposition: form-data; name=\\\"key1\\\"\\r\\n\\r\\n\";\n  expected += \"test\\r\\n\";\n  ASSERT_LT(body_start + expected.length(), request.length());\n  EXPECT_EQ(request.substr(body_start, expected.length()), expected);\n\n  body_start += expected.length();\n\n  expected = \"--\" + expected_boundary + \"\\r\\n\";\n  expected += \"Content-Disposition: form-data; name=\\\"key2\\\"\\r\\n\\r\\n\";\n  expected += \"--abcdefg123\\r\\n\";\n  expected += \"--\" + expected_boundary + \"--\\r\\n\";\n  ASSERT_EQ(request.length(), body_start + expected.length());\n  EXPECT_EQ(request.substr(body_start), expected);\n}\n\nclass HTTPTransport : public testing::TestWithParam<std::string> {};\n\nTEST_P(HTTPTransport, ValidFormData) {\n  HTTPMultipartBuilder builder;\n  builder.SetFormData(\"key1\", \"test\");\n  builder.SetFormData(\"key2\", \"--abcdefg123\");\n\n  HTTPHeaders headers;\n  builder.PopulateContentHeaders(&headers);\n\n  HTTPTransportTestFixture test(\n      GetParam(), headers, builder.GetBodyStream(), 200, &ValidFormData);\n  test.Run();\n}\n\nTEST_P(HTTPTransport, ValidFormData_Gzip) {\n  HTTPMultipartBuilder builder;\n  builder.SetGzipEnabled(true);\n  builder.SetFormData(\"key1\", \"test\");\n  builder.SetFormData(\"key2\", \"--abcdefg123\");\n\n  HTTPHeaders headers;\n  builder.PopulateContentHeaders(&headers);\n\n  HTTPTransportTestFixture test(\n      GetParam(), headers, builder.GetBodyStream(), 200, &ValidFormData);\n  test.Run();\n}\n\nconstexpr char kTextPlain[] = \"text/plain\";\n\nvoid ErrorResponse(HTTPTransportTestFixture* fixture,\n                   const std::string& request) {\n  std::string content_type;\n  GetHeaderField(request, kContentType, &content_type);\n  EXPECT_EQ(content_type, kTextPlain);\n}\n\nTEST_P(HTTPTransport, ErrorResponse) {\n  HTTPMultipartBuilder builder;\n  HTTPHeaders headers;\n  headers[kContentType] = kTextPlain;\n  HTTPTransportTestFixture test(\n      GetParam(), headers, builder.GetBodyStream(), 404, &ErrorResponse);\n  test.Run();\n}\n\nconstexpr char kTextBody[] = \"hello world\";\n\nvoid UnchunkedPlainText(HTTPTransportTestFixture* fixture,\n                        const std::string& request) {\n  std::string header_value;\n  GetHeaderField(request, kContentType, &header_value);\n  EXPECT_EQ(header_value, kTextPlain);\n\n  GetHeaderField(request, kContentLength, &header_value);\n  const auto& content_length = fixture->headers().find(kContentLength);\n  ASSERT_NE(content_length, fixture->headers().end());\n  EXPECT_EQ(header_value, content_length->second);\n\n  size_t body_start = request.rfind(\"\\r\\n\");\n  ASSERT_NE(body_start, std::string::npos);\n\n  EXPECT_EQ(request.substr(body_start + 2), kTextBody);\n}\n\nTEST_P(HTTPTransport, UnchunkedPlainText) {\n  std::unique_ptr<HTTPBodyStream> body_stream(\n      new StringHTTPBodyStream(kTextBody));\n\n  HTTPHeaders headers;\n  headers[kContentType] = kTextPlain;\n  headers[kContentLength] = base::StringPrintf(\"%\" PRIuS, strlen(kTextBody));\n\n  HTTPTransportTestFixture test(\n      GetParam(), headers, std::move(body_stream), 200, &UnchunkedPlainText);\n  test.Run();\n}\n\nvoid RunUpload33k(const std::string& scheme, bool has_content_length) {\n  // On macOS, NSMutableURLRequest winds up calling into a CFReadStream’s Read()\n  // callback with a 32kB buffer. Make sure that it’s able to get everything\n  // when enough is available to fill this buffer, requiring more than one\n  // Read().\n\n  std::string request_string(33 * 1024, 'a');\n  std::unique_ptr<HTTPBodyStream> body_stream(\n      new StringHTTPBodyStream(request_string));\n\n  HTTPHeaders headers;\n  headers[kContentType] = \"application/octet-stream\";\n  if (has_content_length) {\n    headers[kContentLength] =\n        base::StringPrintf(\"%\" PRIuS, request_string.size());\n  }\n  HTTPTransportTestFixture test(\n      scheme,\n      headers,\n      std::move(body_stream),\n      200,\n      [](HTTPTransportTestFixture* fixture, const std::string& request) {\n        size_t body_start = request.rfind(\"\\r\\n\");\n        EXPECT_EQ(request.size() - body_start, 33 * 1024u + 2);\n      });\n  test.Run();\n}\n\nTEST_P(HTTPTransport, Upload33k) {\n  RunUpload33k(GetParam(), true);\n}\n\nTEST_P(HTTPTransport, Upload33k_LengthUnknown) {\n  // The same as Upload33k, but without declaring Content-Length ahead of time.\n  RunUpload33k(GetParam(), false);\n}\n\n// This should be on for Fuchsia, but DX-382. Debug and re-enabled.\n#if defined(CRASHPAD_USE_BORINGSSL) && !BUILDFLAG(IS_FUCHSIA)\n// The test server requires BoringSSL or OpenSSL, so https in tests can only be\n// enabled where that's readily available. Additionally on Linux, the bots fail\n// lacking libcrypto.so.1.1, so disabled there for now. On Mac, they could also\n// likely be enabled relatively easily, if HTTPTransportMac learned to respect\n// the user-supplied cert.\n//\n// If tests with boringssl are failing because of expired certificates, try\n// re-running generate_test_server_key.py.\nINSTANTIATE_TEST_SUITE_P(HTTPTransport,\n                         HTTPTransport,\n                         testing::Values(\"http\", \"https\"),\n                         [](const testing::TestParamInfo<std::string>& info) {\n                           return info.param;\n                         });\n#else\nINSTANTIATE_TEST_SUITE_P(HTTPTransport,\n                         HTTPTransport,\n                         testing::Values(\"http\"),\n                         [](const testing::TestParamInfo<std::string>& info) {\n                           return info.param;\n                         });\n#endif\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/http_transport_test_server.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// A one-shot testing webserver.\n//\n// When invoked, this server will write a short integer to stdout, indicating on\n// which port the server is listening. It will then read one integer from stdin,\n// indicating the response code to be sent in response to a request. It also\n// reads 16 characters from stdin, which, after having \"\\r\\n\" appended, will\n// form the response body in a successful response (one with code 200). The\n// server will process one HTTP request, deliver the prearranged response to the\n// client, and write the entire request to stdout. It will then terminate.\n\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"tools/tool_support.h\"\n#include \"util/file/file_io.h\"\n\n#if COMPILER_MSVC\n#pragma warning(push)\n#pragma warning(disable: 4244 4245 4267 4702)\n#endif\n\n#if defined(CRASHPAD_USE_BORINGSSL)\n#define CPPHTTPLIB_OPENSSL_SUPPORT\n#endif\n\n#define CPPHTTPLIB_ZLIB_SUPPORT\n#include \"third_party/cpp-httplib/cpp-httplib/httplib.h\"\n\n#if COMPILER_MSVC\n#pragma warning(pop)\n#endif\n\nnamespace crashpad {\nnamespace {\n\nint HttpTransportTestServerMain(int argc, char* argv[]) {\n  std::unique_ptr<httplib::Server> server;\n  if (argc == 1) {\n    server.reset(new httplib::Server);\n#if defined(CRASHPAD_USE_BORINGSSL)\n  } else if (argc == 3) {\n    server.reset(new httplib::SSLServer(argv[1], argv[2]));\n#endif\n  } else {\n    LOG(ERROR) << \"usage: http_transport_test_server [cert.pem key.pem]\";\n    return 1;\n  }\n\n\n  if (!server->is_valid()) {\n    LOG(ERROR) << \"server creation failed\";\n    return 1;\n  }\n\n  server->set_keep_alive_max_count(1);\n\n  uint16_t response_code;\n  char response[16];\n\n  std::string to_stdout;\n\n  server->Post(\"/upload\",\n               [&response, &response_code, &server, &to_stdout](\n                   const httplib::Request& req, httplib::Response& res) {\n                 res.status = response_code;\n                 if (response_code == 200) {\n                   res.set_content(std::string(response, 16) + \"\\r\\n\",\n                                   \"text/plain\");\n                 } else {\n                   res.set_content(\"error\", \"text/plain\");\n                 }\n\n                 to_stdout += \"POST /upload HTTP/1.0\\r\\n\";\n                 for (const auto& h : req.headers) {\n                   to_stdout += base::StringPrintf(\n                       \"%s: %s\\r\\n\", h.first.c_str(), h.second.c_str());\n                 }\n                 to_stdout += \"\\r\\n\";\n                 to_stdout += req.body;\n                 if (req.is_multipart_form_data()) {\n                   std::string boundary;\n                   httplib::detail::parse_multipart_boundary(\n                       req.get_header_value(\"Content-Type\"), boundary);\n                   for (const auto& part : req.form.fields) {\n                     to_stdout += \"--\" + boundary + \"\\r\\n\";\n                     to_stdout += \"Content-Disposition: form-data; name=\\\"\" +\n                                  part.first + \"\\\"\\r\\n\\r\\n\";\n                     to_stdout += part.second.content + \"\\r\\n\";\n                   }\n                   to_stdout += \"--\" + boundary + \"--\\r\\n\";\n                 }\n\n                 server->stop();\n               });\n\n  uint16_t port =\n      base::checked_cast<uint16_t>(server->bind_to_any_port(\"localhost\"));\n\n  CheckedWriteFile(\n      StdioFileHandle(StdioStream::kStandardOutput), &port, sizeof(port));\n\n  CheckedReadFileExactly(StdioFileHandle(StdioStream::kStandardInput),\n                         &response_code,\n                         sizeof(response_code));\n\n  CheckedReadFileExactly(StdioFileHandle(StdioStream::kStandardInput),\n                         &response,\n                         sizeof(response));\n\n  server->listen_after_bind();\n\n  LoggingWriteFile(StdioFileHandle(StdioStream::kStandardOutput),\n                   to_stdout.data(),\n                   to_stdout.size());\n\n  return 0;\n}\n\n}  // namespace\n}  // namespace crashpad\n\n#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)\nint main(int argc, char* argv[]) {\n  return crashpad::HttpTransportTestServerMain(argc, argv);\n}\n#elif BUILDFLAG(IS_WIN)\nint wmain(int argc, wchar_t* argv[]) {\n  return crashpad::ToolSupport::Wmain(\n      argc, argv, crashpad::HttpTransportTestServerMain);\n}\n#endif  // BUILDFLAG(IS_POSIX)\n"
  },
  {
    "path": "util/net/http_transport_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/http_transport.h\"\n\n#include <windows.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/types.h>\n#include <wchar.h>\n#include <winhttp.h>\n\n#include <iterator>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/scoped_generic.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"package.h\"\n#include \"util/file/file_io.h\"\n#include \"util/net/http_body.h\"\n#include \"util/numeric/safe_assignment.h\"\n#include \"util/win/module_version.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nconstexpr wchar_t kWinHttpDll[] = L\"winhttp.dll\";\n\nstd::string UserAgent() {\n  std::string user_agent =\n      base::StringPrintf(\"%s/%s WinHTTP\", PACKAGE_NAME, PACKAGE_VERSION);\n\n  VS_FIXEDFILEINFO version;\n  if (GetModuleVersionAndType(base::FilePath(kWinHttpDll), &version)) {\n    user_agent.append(base::StringPrintf(\"/%lu.%lu.%lu.%lu\",\n                                         version.dwFileVersionMS >> 16,\n                                         version.dwFileVersionMS & 0xffff,\n                                         version.dwFileVersionLS >> 16,\n                                         version.dwFileVersionLS & 0xffff));\n  }\n\n  if (GetModuleVersionAndType(base::FilePath(L\"kernel32.dll\"), &version) &&\n      (version.dwFileOS & VOS_NT_WINDOWS32) == VOS_NT_WINDOWS32) {\n    user_agent.append(base::StringPrintf(\" Windows_NT/%lu.%lu.%lu.%lu (\",\n                                         version.dwFileVersionMS >> 16,\n                                         version.dwFileVersionMS & 0xffff,\n                                         version.dwFileVersionLS >> 16,\n                                         version.dwFileVersionLS & 0xffff));\n#if defined(ARCH_CPU_X86)\n    user_agent.append(\"x86\");\n#elif defined(ARCH_CPU_X86_64)\n    user_agent.append(\"x64\");\n#elif defined(ARCH_CPU_ARM64)\n    user_agent.append(\"arm64\");\n#else\n#error Port\n#endif\n\n    BOOL is_wow64;\n    if (!IsWow64Process(GetCurrentProcess(), &is_wow64)) {\n      PLOG(WARNING) << \"IsWow64Process\";\n    } else if (is_wow64) {\n      user_agent.append(\"; WoW64\");\n    }\n    user_agent.append(1, ')');\n  }\n\n  return user_agent;\n}\n\n// PLOG doesn't work for messages from WinHTTP, so we need to use\n// FORMAT_MESSAGE_FROM_HMODULE + the dll name manually here.\nstd::string WinHttpMessage(const char* extra) {\n  DWORD error_code = GetLastError();\n  char msgbuf[256];\n  DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |\n                FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE;\n  DWORD len = FormatMessageA(flags,\n                             GetModuleHandle(kWinHttpDll),\n                             error_code,\n                             0,\n                             msgbuf,\n                             static_cast<DWORD>(std::size(msgbuf)),\n                             nullptr);\n  if (!len) {\n    return base::StringPrintf(\"%s: error 0x%lx while retrieving error 0x%lx\",\n                              extra,\n                              GetLastError(),\n                              error_code);\n  }\n\n  // Most system messages end in a space. Remove the space if it’s there,\n  // because the StringPrintf() below includes one.\n  if (len >= 1 && msgbuf[len - 1] == ' ') {\n    msgbuf[len - 1] = '\\0';\n  }\n  return base::StringPrintf(\"%s: %s (0x%lx)\", extra, msgbuf, error_code);\n}\n\nstruct ScopedHINTERNETTraits {\n  static HINTERNET InvalidValue() {\n    return nullptr;\n  }\n  static void Free(HINTERNET handle) {\n    if (handle) {\n      if (!WinHttpCloseHandle(handle)) {\n        LOG(ERROR) << WinHttpMessage(\"WinHttpCloseHandle\");\n      }\n    }\n  }\n};\n\nusing ScopedHINTERNET = base::ScopedGeneric<HINTERNET, ScopedHINTERNETTraits>;\n\nclass HTTPTransportWin final : public HTTPTransport {\n public:\n  HTTPTransportWin();\n\n  HTTPTransportWin(const HTTPTransportWin&) = delete;\n  HTTPTransportWin& operator=(const HTTPTransportWin&) = delete;\n\n  ~HTTPTransportWin() override;\n\n  bool ExecuteSynchronously(std::string* response_body) override;\n};\n\nHTTPTransportWin::HTTPTransportWin() : HTTPTransport() {\n}\n\nHTTPTransportWin::~HTTPTransportWin() {\n}\n\nbool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) {\n  ScopedHINTERNET session(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(),\n                                      WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,\n                                      WINHTTP_NO_PROXY_NAME,\n                                      WINHTTP_NO_PROXY_BYPASS,\n                                      0));\n  if (!session.get()) {\n    LOG(ERROR) << WinHttpMessage(\"WinHttpOpen\");\n    return false;\n  }\n\n  int timeout_in_ms = static_cast<int>(timeout() * 1000);\n  if (!WinHttpSetTimeouts(session.get(),\n                          timeout_in_ms,\n                          timeout_in_ms,\n                          timeout_in_ms,\n                          timeout_in_ms)) {\n    LOG(ERROR) << WinHttpMessage(\"WinHttpSetTimeouts\");\n    return false;\n  }\n\n  URL_COMPONENTS url_components = {0};\n  url_components.dwStructSize = sizeof(URL_COMPONENTS);\n  url_components.dwHostNameLength = 1;\n  url_components.dwUrlPathLength = 1;\n  url_components.dwExtraInfoLength = 1;\n  std::wstring url_wide(base::UTF8ToWide(url()));\n  // dwFlags = ICU_REJECT_USERPWD fails on XP.\n  if (!WinHttpCrackUrl(\n          url_wide.c_str(), 0, 0, &url_components)) {\n    LOG(ERROR) << WinHttpMessage(\"WinHttpCrackUrl\");\n    return false;\n  }\n  DCHECK(url_components.nScheme == INTERNET_SCHEME_HTTP ||\n         url_components.nScheme == INTERNET_SCHEME_HTTPS);\n  std::wstring host_name(url_components.lpszHostName,\n                         url_components.dwHostNameLength);\n  std::wstring url_path(url_components.lpszUrlPath,\n                        url_components.dwUrlPathLength);\n  std::wstring extra_info(url_components.lpszExtraInfo,\n                          url_components.dwExtraInfoLength);\n\n  // Use url_path, and get the query parameter from extra_info, up to the first\n  // #, if any. See RFC 7230 §5.3.1 and RFC 3986 §3.4. Beware that when this is\n  // used to POST data, the query parameters generally belong in the request\n  // body and not in the URL request target. It’s legal for them to be in both\n  // places, but the interpretation is subject to whatever the client and server\n  // agree on. This honors whatever was passed in, matching other platforms, but\n  // you’ve been warned!\n  std::wstring request_target(\n      url_path.append(extra_info.substr(0, extra_info.find(L'#'))));\n\n  ScopedHINTERNET connect(WinHttpConnect(\n      session.get(), host_name.c_str(), url_components.nPort, 0));\n  if (!connect.get()) {\n    LOG(ERROR) << WinHttpMessage(\"WinHttpConnect\");\n    return false;\n  }\n\n  ScopedHINTERNET request(WinHttpOpenRequest(\n      connect.get(),\n      base::UTF8ToWide(method()).c_str(),\n      request_target.c_str(),\n      nullptr,\n      WINHTTP_NO_REFERER,\n      WINHTTP_DEFAULT_ACCEPT_TYPES,\n      url_components.nScheme == INTERNET_SCHEME_HTTPS ? WINHTTP_FLAG_SECURE\n                                                      : 0));\n  if (!request.get()) {\n    LOG(ERROR) << WinHttpMessage(\"WinHttpOpenRequest\");\n    return false;\n  }\n\n  // Add headers to the request.\n  //\n  // If Content-Length is not provided, implement chunked mode per RFC 7230\n  // §4.1.\n  //\n  // Note that chunked mode can only be used on Vista and later. Otherwise,\n  // WinHttpSendRequest() requires a real value for dwTotalLength, used for the\n  // Content-Length header. Determining that in the absence of a provided\n  // Content-Length would require reading the entire request body before calling\n  // WinHttpSendRequest().\n  bool chunked = true;\n  size_t content_length = 0;\n  for (const auto& pair : headers()) {\n    if (pair.first == kContentLength) {\n      chunked = !base::StringToSizeT(pair.second, &content_length);\n      DCHECK(!chunked);\n    } else {\n      std::wstring header_string = base::UTF8ToWide(pair.first) + L\": \" +\n                                   base::UTF8ToWide(pair.second) + L\"\\r\\n\";\n      if (!WinHttpAddRequestHeaders(\n              request.get(),\n              header_string.c_str(),\n              base::checked_cast<DWORD>(header_string.size()),\n              WINHTTP_ADDREQ_FLAG_ADD)) {\n        LOG(ERROR) << WinHttpMessage(\"WinHttpAddRequestHeaders\");\n        return false;\n      }\n    }\n  }\n\n  DWORD content_length_dword;\n  if (chunked) {\n    static constexpr wchar_t kTransferEncodingHeader[] =\n        L\"Transfer-Encoding: chunked\\r\\n\";\n    if (!WinHttpAddRequestHeaders(\n            request.get(),\n            kTransferEncodingHeader,\n            base::checked_cast<DWORD>(wcslen(kTransferEncodingHeader)),\n            WINHTTP_ADDREQ_FLAG_ADD)) {\n      LOG(ERROR) << WinHttpMessage(\"WinHttpAddRequestHeaders\");\n      return false;\n    }\n\n    content_length_dword = WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH;\n  } else if (!AssignIfInRange(&content_length_dword, content_length)) {\n    content_length_dword = WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH;\n  }\n\n  if (!WinHttpSendRequest(request.get(),\n                          WINHTTP_NO_ADDITIONAL_HEADERS,\n                          0,\n                          WINHTTP_NO_REQUEST_DATA,\n                          0,\n                          content_length_dword,\n                          0)) {\n    LOG(ERROR) << WinHttpMessage(\"WinHttpSendRequest\");\n    return false;\n  }\n\n  size_t total_written = 0;\n  FileOperationResult data_bytes;\n  do {\n    struct {\n      char size[8];\n      char crlf0[2];\n      uint8_t data[32 * 1024];\n      char crlf1[2];\n    } buf;\n    static_assert(sizeof(buf) == sizeof(buf.size) +\n                                 sizeof(buf.crlf0) +\n                                 sizeof(buf.data) +\n                                 sizeof(buf.crlf1),\n                  \"buf should not have padding\");\n\n    // Read a block of data.\n    data_bytes = body_stream()->GetBytesBuffer(buf.data, sizeof(buf.data));\n    if (data_bytes == -1) {\n      return false;\n    }\n    DCHECK_GE(data_bytes, 0);\n    DCHECK_LE(static_cast<size_t>(data_bytes), sizeof(buf.data));\n\n    void* write_start;\n    DWORD write_size;\n\n    if (chunked) {\n      // Chunked encoding uses the entirety of buf. buf.size is presented in\n      // hexadecimal without any leading \"0x\". The terminating CR and LF will be\n      // placed immediately following the used portion of buf.data, even if\n      // buf.data is not full, and not necessarily in buf.crlf1.\n\n      unsigned int data_bytes_ui = base::checked_cast<unsigned int>(data_bytes);\n\n      // snprintf() would NUL-terminate, but _snprintf() won’t.\n      int rv = _snprintf(buf.size, sizeof(buf.size), \"%08x\", data_bytes_ui);\n      DCHECK_GE(rv, 0);\n      DCHECK_EQ(static_cast<size_t>(rv), sizeof(buf.size));\n      DCHECK_NE(buf.size[sizeof(buf.size) - 1], '\\0');\n\n      buf.crlf0[0] = '\\r';\n      buf.crlf0[1] = '\\n';\n      buf.data[data_bytes] = '\\r';\n      buf.data[data_bytes + 1] = '\\n';\n\n      // Skip leading zeroes in the chunk size.\n      unsigned int size_len;\n      for (size_len = sizeof(buf.size); size_len > 1; --size_len) {\n        if (buf.size[sizeof(buf.size) - size_len] != '0') {\n          break;\n        }\n      }\n\n      write_start = buf.crlf0 - size_len;\n      write_size = base::checked_cast<DWORD>(size_len + sizeof(buf.crlf0) +\n                                             data_bytes + sizeof(buf.crlf1));\n    } else {\n      // When not using chunked encoding, only use buf.data.\n      write_start = buf.data;\n      write_size = base::checked_cast<DWORD>(data_bytes);\n    }\n\n    // write_size will be 0 at EOF in non-chunked mode. Skip the write in that\n    // case. In contrast, at EOF in chunked mode, a zero-length chunk must be\n    // sent to signal EOF. This will happen when processing the EOF indicated by\n    // a 0 return from body_stream()->GetBytesBuffer() above.\n    if (write_size != 0) {\n      DWORD written;\n      if (!WinHttpWriteData(request.get(), write_start, write_size, &written)) {\n        LOG(ERROR) << WinHttpMessage(\"WinHttpWriteData\");\n        return false;\n      }\n\n      DCHECK_EQ(written, write_size);\n      total_written += written;\n    }\n  } while (data_bytes > 0);\n\n  if (!chunked) {\n    DCHECK_EQ(total_written, content_length);\n  }\n\n  if (!WinHttpReceiveResponse(request.get(), nullptr)) {\n    LOG(ERROR) << WinHttpMessage(\"WinHttpReceiveResponse\");\n    return false;\n  }\n\n  DWORD status_code = 0;\n  DWORD sizeof_status_code = sizeof(status_code);\n\n  if (!WinHttpQueryHeaders(\n          request.get(),\n          WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,\n          WINHTTP_HEADER_NAME_BY_INDEX,\n          &status_code,\n          &sizeof_status_code,\n          WINHTTP_NO_HEADER_INDEX)) {\n    LOG(ERROR) << WinHttpMessage(\"WinHttpQueryHeaders\");\n    return false;\n  }\n\n  if (status_code < 200 || status_code > 203) {\n    LOG(ERROR) << base::StringPrintf(\"HTTP status %lu\", status_code);\n    return false;\n  }\n\n  if (response_body) {\n    response_body->clear();\n\n    // There isn’t any reason to call WinHttpQueryDataAvailable(), because it\n    // returns the number of bytes available to be read without blocking at the\n    // time of the call, not the number of bytes until end-of-file. This method,\n    // which executes synchronously, is only concerned with reading until EOF.\n    DWORD bytes_read = 0;\n    do {\n      char read_buffer[4096];\n      if (!WinHttpReadData(\n              request.get(), read_buffer, sizeof(read_buffer), &bytes_read)) {\n        LOG(ERROR) << WinHttpMessage(\"WinHttpReadData\");\n        return false;\n      }\n\n      response_body->append(read_buffer, bytes_read);\n    } while (bytes_read > 0);\n  }\n\n  return true;\n}\n\n}  // namespace\n\n// static\nstd::unique_ptr<HTTPTransport> HTTPTransport::Create() {\n  return std::unique_ptr<HTTPTransportWin>(new HTTPTransportWin);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/testdata/ascii_http_body.txt",
    "content": "This is a test.\n"
  },
  {
    "path": "util/net/testdata/crashpad_util_test_cert.pem",
    "content": "DO NOT EDIT: This file was auto-generated by ./util/net/generate_test_server_key.py\n\n-----BEGIN CERTIFICATE-----\nMIIDCDCCAfCgAwIBAgITF9h5BaPfzje590HrE1UF+f9PVDANBgkqhkiG9w0BAQsF\nADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTkxMDMwMjIyNDE4WhcNMjkxMDI3\nMjIyNDE4WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUA\nA4IBDwAwggEKAoIBAQCrT0iXfixTPH6nBxgvCgiKkSTeVTSvfhcoUYA7yPZj/1XH\nVYfECoGyvFN4/5g/GL4c721XZmI92Twg2Oa3HksRY8VlNXGNwqbt+wftSLOz+dOE\nbsJ3+NsBtXuaIphFPA9u0h+yrplBtM/dxLTW5v8e3OuEMBXa+94dp4y78u21UkzM\nuWB66tduT7/f0S9P68XhksYwwwvCn5lu258wHKNM5mb4CTwOcUgyyprlS2FS3Fsx\npIsg9wDfQy5rHINxcZGZZ8Ht4KOJ7egwhfiP4XEiLK6+5DZoOQtrkEUFfLL4i5sx\n+eOeD12GP+Dxs6KHYoejGftNx/7U0E2GVMWwWzstAgMBAAGjUzBRMB0GA1UdDgQW\nBBT8y2bnyty/YWcvzlE9JIAPG7/q4DAfBgNVHSMEGDAWgBT8y2bnyty/YWcvzlE9\nJIAPG7/q4DAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCF+R21\nCd1fgITfhYJE7JAV3jLfact5h/2MP5UwgtODfZt2Idwxs8qqwPB0g7WQqEjrPfTv\n6y90AEwr2L8e+wXGnTKtWelCqzztFGov4Hj+rd158eOmEo0IPGrx8dwqLQbw91uU\ncq4onf5iuHkOt99TmIqVYn2zaOHbOF2YYyU052X+9XE/Z5fhibX5THfEG3w0+XM6\naKEGIM/MxfVaLA8yDXFxhDXHBrH+QAAXTQaC8exnp+po/psyJARD0sM509MeTdAv\npo9JyIzesNAsLW4I7kL8I5i8e+WN79lX2jgwaWMxPmHadYN3mtoltpmFM40oFN6q\n8wjuU07eN/G79c3B\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "util/net/testdata/crashpad_util_test_key.pem",
    "content": "DO NOT EDIT: This file was auto-generated by ./util/net/generate_test_server_key.py\n\n-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrT0iXfixTPH6n\nBxgvCgiKkSTeVTSvfhcoUYA7yPZj/1XHVYfECoGyvFN4/5g/GL4c721XZmI92Twg\n2Oa3HksRY8VlNXGNwqbt+wftSLOz+dOEbsJ3+NsBtXuaIphFPA9u0h+yrplBtM/d\nxLTW5v8e3OuEMBXa+94dp4y78u21UkzMuWB66tduT7/f0S9P68XhksYwwwvCn5lu\n258wHKNM5mb4CTwOcUgyyprlS2FS3FsxpIsg9wDfQy5rHINxcZGZZ8Ht4KOJ7egw\nhfiP4XEiLK6+5DZoOQtrkEUFfLL4i5sx+eOeD12GP+Dxs6KHYoejGftNx/7U0E2G\nVMWwWzstAgMBAAECggEANEgJDnrqSijfOlh27/wu6SMszlHQt3JS3PIqoZROZexK\nIICg45qVRJgnHXlb3H3Pn4MOqqrLdraynA+2MdKj9FWvq5io5CuwyFZhb/BNL7Mh\n83veC8E+DYJ2i27da9vNlfO4ys5wZVYqTjM3QZLT73Zaxkfqk59khUZaNA4Kr9hD\nEBs9gaDMATyIAN5eAbYTVxgobZ+7OjCNmQ46x2KOg6Wwg/ZQEXtqFoDB3aCNVfTz\nOxlLb5XWF9PvqW0p1wFpF0XEDipkVtjorjp3mHXRuq0gS9Gev1ChQej1yVqMee9J\njnVK0k7qZw4WLStxBjHqWlBnYLcXLb4f2c6cssA6YQKBgQDfqtYFjU3HCeoRaAoP\nMWTSW2PX8fitnp0O4e1yHymDgtirepvPL8pa7rTBkHuR6lfXNjY0qD0NPsbr81nG\nkNv/btLOK1YPw6f468S6DMsOzGPIWWE+jkhF/1iUTMUQLvOM3lCLEi0ayZ26yx1T\nF2wO2u1kGc1eJ8wn6TccsYiguQKBgQDEEt7eGQMkxLFV4N5e5iunHBvQg+EgfLZZ\nbnuftwruqafmCht/BZgmKQD+e6B6h3nC/iaKET101zpWL95x7Ayd1+mg6Rmdzl87\nctftScrNLYUTl36BuhP82Y9HvLgf20xrEwBGivc0QtqhMAz/DJoknEM/29LmWEsZ\nVwvWQxhsFQKBgQCrOcJMT8d6Fznsh2QkC2EutK3ztBb2+xUrPoQjOH30YqfyZpN/\nAgv8nv8bq7sdknQamjLXDvBmAmgQW6SfoWf53OJe2Mgym0stAXkCIScWNhwxVVNf\nq1bi1z79kOPPptHmRo8MWCbVegFY7YOOh8C+gpT3a9VPPlJJP31kZvi8aQKBgDcD\nZGzEb9FdLrR9x2axBgZ5KIS0u/G1jCRDj4Qcg4C7MVSl+VkGZM4wKws7/KbkZBGF\n5aJPfALQcJnGDI/CPzf6YJ65SGqygJ3ZdyQo1DIFV5VLqD8Vyo3jLQRfuvmVOjfA\nuQ8R5pJPP7CCHuNg0c772RKNxvrCQy/08GlJogyRAoGAK6A/8r/iDRJipJSOZdCl\nMreiJvjFOQrORhy+TeDGn02EHK0lijzj1I7SlgfUDLWVnQpBlk12wV6aHGUj2NlG\nCtk15MFs6gggPV1Fmt6PeviE1e9E4+SDWX35Jhe8JDIqxdgZ0HW/S9Nl4Xt3owhw\n/DSneMQd7X0Tdq7lesJjd3o=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "util/net/tls.gni",
    "content": "# Copyright 2018 The Crashpad Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport(\"../../build/crashpad_buildconfig.gni\")\n\ndeclare_args() {\n  if (crashpad_is_fuchsia || crashpad_is_android) {\n    crashpad_http_transport_impl = \"socket\"\n  } else if (crashpad_is_linux) {\n    crashpad_http_transport_impl = \"libcurl\"\n  } else {\n    crashpad_http_transport_impl = \"\"\n  }\n\n  # TODO(scottmg): https://crbug.com/crashpad/266 fuchsia:DX-690: BoringSSL\n  # was removed from the Fuchsia SDK. Re-enable it when we have a way to acquire\n  # a BoringSSL lib again.\n  crashpad_use_boringssl_for_http_transport_socket =\n      crashpad_is_in_fuchsia || (crashpad_is_linux && crashpad_is_in_chromium)\n}\n"
  },
  {
    "path": "util/net/url.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/url.h\"\n\n#include <string.h>\n\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n\nnamespace crashpad {\n\nstd::string URLEncode(const std::string& url) {\n  const char kSafeCharacters[] = \"-_.~\";\n  std::string encoded;\n  encoded.reserve(url.length());\n\n  for (unsigned char character : url) {\n    if (((character >= 'A') && (character <= 'Z')) ||\n        ((character >= 'a') && (character <= 'z')) ||\n        ((character >= '0') && (character <= '9')) ||\n        (strchr(kSafeCharacters, character) != nullptr)) {\n      // Copy unreserved character.\n      encoded += character;\n    } else {\n      // Escape character.\n      encoded += base::StringPrintf(\"%%%02X\", character);\n    }\n  }\n\n  return encoded;\n}\n\nbool CrackURL(const std::string& url,\n              std::string* scheme,\n              std::string* host,\n              std::string* port,\n              std::string* rest) {\n  std::string result_scheme;\n  std::string result_port;\n\n  size_t host_start;\n  static constexpr const char kHttp[] = \"http://\";\n  static constexpr const char kHttps[] = \"https://\";\n  if (url.compare(0, strlen(kHttp), kHttp) == 0) {\n    result_scheme = \"http\";\n    result_port = \"80\";\n    host_start = strlen(kHttp);\n  } else if (url.compare(0, strlen(kHttps), kHttps) == 0) {\n    result_scheme = \"https\";\n    result_port = \"443\";\n    host_start = strlen(kHttps);\n  } else {\n    LOG(ERROR) << \"expecting http or https\";\n    return false;\n  }\n\n  // Find the start of the resource.\n  size_t resource_start = url.find('/', host_start);\n  if (resource_start == std::string::npos) {\n    LOG(ERROR) << \"no resource component\";\n    return false;\n  }\n\n  scheme->swap(result_scheme);\n  port->swap(result_port);\n  std::string host_and_possible_port =\n      url.substr(host_start, resource_start - host_start);\n  size_t colon = host_and_possible_port.find(':');\n  if (colon == std::string::npos) {\n    *host = host_and_possible_port;\n  } else {\n    *host = host_and_possible_port.substr(0, colon);\n    *port = host_and_possible_port.substr(colon + 1);\n  }\n\n  *rest = url.substr(resource_start);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/net/url.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NET_URL_H_\n#define CRASHPAD_UTIL_NET_URL_H_\n\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief Performs percent-encoding (URL encoding) on the input string,\n//!     following RFC 3986 paragraph 2.\n//!\n//! \\param[in] url The string to be encoded.\n//! \\return The encoded string.\nstd::string URLEncode(const std::string& url);\n\n//! \\brief Crack a URL into component parts.\n//!\n//! This is not a general function, and works only on the limited style of URLs\n//! that are expected to be used by HTTPTransport::SetURL().\n//!\n//! \\param[in] url The URL to crack.\n//! \\param[out] scheme The request scheme, either http or https.\n//! \\param[out] host The hostname.\n//! \\param[out] port The port.\n//! \\param[out] rest The remainder of the URL (both resource and URL params).\n//! \\return `true` on success in which case all output parameters will be filled\n//!     out, or `false` on failure, in which case the output parameters will be\n//!     unmodified and an error will be logged.\nbool CrackURL(const std::string& url,\n              std::string* scheme,\n              std::string* host,\n              std::string* port,\n              std::string* rest);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NET_URL_H_\n"
  },
  {
    "path": "util/net/url_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/net/url.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(URLEncode, Empty) {\n  EXPECT_EQ(URLEncode(\"\"), \"\");\n}\n\nTEST(URLEncode, ReservedCharacters) {\n  EXPECT_EQ(URLEncode(\" !#$&'()*+,/:;=?@[]\"),\n            \"%20%21%23%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D\");\n}\n\nTEST(URLEncode, UnreservedCharacters) {\n  EXPECT_EQ(URLEncode(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"),\n            \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\");\n  EXPECT_EQ(URLEncode(\"0123456789-_.~\"), \"0123456789-_.~\");\n}\n\nTEST(URLEncode, SimpleAddress) {\n  EXPECT_EQ(\n      URLEncode(\"http://some.address.com/page.html?arg1=value&arg2=value\"),\n      \"http%3A%2F%2Fsome.address.com%2Fpage.html%3Farg1%3Dvalue%26arg2%\"\n      \"3Dvalue\");\n}\n\nTEST(CrackURL, Unsupported) {\n  std::string scheme, host, port, rest;\n\n  // Not HTTP.\n  EXPECT_FALSE(CrackURL(\"file://stuff/things\", &scheme, &host, &port, &rest));\n\n  // No resource.\n  EXPECT_FALSE(CrackURL(\"file://stuff\", &scheme, &host, &port, &rest));\n  EXPECT_FALSE(CrackURL(\"http://stuff\", &scheme, &host, &port, &rest));\n  EXPECT_FALSE(CrackURL(\"https://stuff\", &scheme, &host, &port, &rest));\n}\n\nTEST(CrackURL, UnsupportedDoesNotModifiedOutArgs) {\n  std::string scheme, host, port, rest;\n\n  scheme = \"scheme\";\n  host = \"host\";\n  port = \"port\";\n  rest = \"rest\";\n\n  // Bad scheme.\n  EXPECT_FALSE(CrackURL(\"file://stuff/things\", &scheme, &host, &port, &rest));\n  EXPECT_EQ(scheme, \"scheme\");\n  EXPECT_EQ(host, \"host\");\n  EXPECT_EQ(port, \"port\");\n  EXPECT_EQ(rest, \"rest\");\n\n  scheme = \"scheme\";\n  host = \"host\";\n  port = \"port\";\n  rest = \"rest\";\n\n  // No resource.\n  EXPECT_FALSE(CrackURL(\"http://stuff\", &scheme, &host, &port, &rest));\n  EXPECT_EQ(scheme, \"scheme\");\n  EXPECT_EQ(host, \"host\");\n  EXPECT_EQ(port, \"port\");\n  EXPECT_EQ(rest, \"rest\");\n}\n\nTEST(CrackURL, BasicWithDefaultPort) {\n  std::string scheme, host, port, rest;\n\n  ASSERT_TRUE(CrackURL(\"http://stuff/things\", &scheme, &host, &port, &rest));\n  EXPECT_EQ(scheme, \"http\");\n  EXPECT_EQ(host, \"stuff\");\n  EXPECT_EQ(port, \"80\");\n  EXPECT_EQ(rest, \"/things\");\n\n  ASSERT_TRUE(CrackURL(\"https://stuff/things\", &scheme, &host, &port, &rest));\n  EXPECT_EQ(scheme, \"https\");\n  EXPECT_EQ(host, \"stuff\");\n  EXPECT_EQ(port, \"443\");\n  EXPECT_EQ(rest, \"/things\");\n}\n\nTEST(CrackURL, BasicWithExplicitPort) {\n  std::string scheme, host, port, rest;\n\n  ASSERT_TRUE(\n      CrackURL(\"http://stuff:999/things\", &scheme, &host, &port, &rest));\n  EXPECT_EQ(scheme, \"http\");\n  EXPECT_EQ(host, \"stuff\");\n  EXPECT_EQ(port, \"999\");\n  EXPECT_EQ(rest, \"/things\");\n\n  ASSERT_TRUE(\n      CrackURL(\"https://stuff:1010/things\", &scheme, &host, &port, &rest));\n  EXPECT_EQ(scheme, \"https\");\n  EXPECT_EQ(host, \"stuff\");\n  EXPECT_EQ(port, \"1010\");\n  EXPECT_EQ(rest, \"/things\");\n}\n\nTEST(CrackURL, WithURLParams) {\n  std::string scheme, host, port, rest;\n\n  ASSERT_TRUE(CrackURL(\n      \"http://stuff:999/things?blah=stuff:3\", &scheme, &host, &port, &rest));\n  EXPECT_EQ(scheme, \"http\");\n  EXPECT_EQ(host, \"stuff\");\n  EXPECT_EQ(port, \"999\");\n  EXPECT_EQ(rest, \"/things?blah=stuff:3\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/numeric/checked_address_range.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/numeric/checked_address_range.h\"\n\n#include \"base/check_op.h\"\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <mach/mach.h>\n#elif BUILDFLAG(IS_WIN)\n#include \"util/win/address_types.h\"\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include \"util/linux/address_types.h\"\n#elif BUILDFLAG(IS_FUCHSIA)\n#include <zircon/types.h>\n#endif  // BUILDFLAG(IS_APPLE)\n\nnamespace crashpad {\nnamespace internal {\n\ntemplate <class ValueType, class SizeType>\nCheckedAddressRangeGeneric<ValueType, SizeType>::CheckedAddressRangeGeneric()\n    : range_32_(0, 0),\n#if defined(COMPILER_MSVC)\n      range_64_(0, 0),\n#endif  // COMPILER_MSVC\n      is_64_bit_(false),\n      range_ok_(true) {\n}\n\ntemplate <class ValueType, class SizeType>\nCheckedAddressRangeGeneric<ValueType, SizeType>::CheckedAddressRangeGeneric(\n    bool is_64_bit,\n    ValueType base,\n    SizeType size)\n#if defined(COMPILER_MSVC)\n    : range_32_(0, 0),\n      range_64_(0, 0)\n#endif  // COMPILER_MSVC\n{\n  SetRange(is_64_bit, base, size);\n}\n\ntemplate <class ValueType, class SizeType>\nValueType CheckedAddressRangeGeneric<ValueType, SizeType>::Base() const {\n  return is_64_bit_ ? range_64_.base() : range_32_.base();\n}\n\ntemplate <class ValueType, class SizeType>\nSizeType CheckedAddressRangeGeneric<ValueType, SizeType>::Size() const {\n  return is_64_bit_ ? range_64_.size() : range_32_.size();\n}\n\ntemplate <class ValueType, class SizeType>\nValueType CheckedAddressRangeGeneric<ValueType, SizeType>::End() const {\n  return is_64_bit_ ? range_64_.end() : range_32_.end();\n}\n\ntemplate <class ValueType, class SizeType>\nbool CheckedAddressRangeGeneric<ValueType, SizeType>::IsValid() const {\n  return range_ok_ && (is_64_bit_ ? range_64_.IsValid() : range_32_.IsValid());\n}\n\ntemplate <class ValueType, class SizeType>\nvoid CheckedAddressRangeGeneric<ValueType, SizeType>::SetRange(bool is_64_bit,\n                                                               ValueType base,\n                                                               SizeType size) {\n  is_64_bit_ = is_64_bit;\n  if (is_64_bit_) {\n    range_64_.SetRange(base, size);\n    range_ok_ = true;\n  } else {\n    range_32_.SetRange(static_cast<uint32_t>(base),\n                       static_cast<uint32_t>(size));\n    range_ok_ = base::IsValueInRangeForNumericType<uint32_t>(base) &&\n                base::IsValueInRangeForNumericType<uint32_t>(size);\n  }\n}\n\ntemplate <class ValueType, class SizeType>\nbool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsValue(\n    ValueType value) const {\n  DCHECK(range_ok_);\n\n  if (is_64_bit_) {\n    return range_64_.ContainsValue(value);\n  }\n\n  if (!base::IsValueInRangeForNumericType<uint32_t>(value)) {\n    return false;\n  }\n\n  return range_32_.ContainsValue(static_cast<uint32_t>(value));\n}\n\ntemplate <class ValueType, class SizeType>\nbool CheckedAddressRangeGeneric<ValueType, SizeType>::ContainsRange(\n    const CheckedAddressRangeGeneric& that) const {\n  DCHECK_EQ(is_64_bit_, that.is_64_bit_);\n  DCHECK(range_ok_);\n  DCHECK(that.range_ok_);\n\n  return is_64_bit_ ? range_64_.ContainsRange(that.range_64_)\n                    : range_32_.ContainsRange(that.range_32_);\n}\n\ntemplate <class ValueType, class SizeType>\nstd::string CheckedAddressRangeGeneric<ValueType, SizeType>::AsString() const {\n  return base::StringPrintf(\"0x%\" PRIx64 \" + 0x%\" PRIx64 \" (%s)\",\n                            uint64_t{Base()},\n                            uint64_t{Size()},\n                            Is64Bit() ? \"64\" : \"32\");\n}\n\n// Explicit instantiations for the cases we use.\n#if BUILDFLAG(IS_APPLE)\ntemplate class CheckedAddressRangeGeneric<mach_vm_address_t, mach_vm_size_t>;\n#elif BUILDFLAG(IS_WIN)\ntemplate class CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>;\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\ntemplate class CheckedAddressRangeGeneric<LinuxVMAddress, LinuxVMSize>;\n#elif BUILDFLAG(IS_FUCHSIA)\ntemplate class CheckedAddressRangeGeneric<zx_vaddr_t, size_t>;\n#endif  // BUILDFLAG(IS_APPLE)\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/numeric/checked_address_range.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_\n#define CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_\n\n#include <stdint.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"util/numeric/checked_range.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Ensures that a range, composed of a base and a size, does not\n//!     overflow the pointer type of the process it describes a range in.\n//!\n//! This class checks bases of type `ValueType` and sizes of type `SizeType`\n//! against a process whose pointer type is either 32 or 64 bits wide.\n//!\n//! Aside from varying the overall range on the basis of a process’ pointer type\n//! width, this class functions very similarly to CheckedRange.\n//!\n//! \\sa CheckedMachAddressRange\ntemplate <class ValueType, class SizeType>\nclass CheckedAddressRangeGeneric {\n public:\n  //! \\brief Initializes a default range.\n  //!\n  //! The default range has base 0, size 0, and appears to be from a 32-bit\n  //! process.\n  CheckedAddressRangeGeneric();\n\n  //! \\brief Initializes a range.\n  //!\n  //! See SetRange().\n  CheckedAddressRangeGeneric(bool is_64_bit, ValueType base, SizeType size);\n\n  //! \\brief Sets a range’s fields.\n  //!\n  //! \\param[in] is_64_bit `true` if \\a base and \\a size refer to addresses in a\n  //!     64-bit process; `false` if they refer to addresses in a 32-bit\n  //!     process.\n  //! \\param[in] base The range’s base address.\n  //! \\param[in] size The range’s size.\n  void SetRange(bool is_64_bit, ValueType base, SizeType size);\n\n  //! \\brief The range’s base address.\n  ValueType Base() const;\n\n  //! \\brief The range’s size.\n  SizeType Size() const;\n\n  //! \\brief The range’s end address (its base address plus its size).\n  ValueType End() const;\n\n  //! \\brief Returns the validity of the address range.\n  //!\n  //! \\return `true` if the address range is valid, `false` otherwise.\n  //!\n  //! An address range is valid if its size can be converted to the address\n  //! range’s data type without data loss, and if its end (base plus size) can\n  //! be computed without overflowing its data type.\n  bool IsValid() const;\n\n  //! \\brief Returns whether this range refers to a 64-bit process.\n  bool Is64Bit() const { return is_64_bit_; }\n\n  //! \\brief Returns whether the address range contains another address.\n  //!\n  //! \\param[in] value The (possibly) contained address.\n  //!\n  //! \\return `true` if the address range contains \\a value, `false` otherwise.\n  //!\n  //! An address range contains a value if the value is greater than or equal to\n  //! its base address, and less than its end address (base address plus size).\n  //!\n  //! This method must only be called if IsValid() would return `true`.\n  bool ContainsValue(const ValueType value) const;\n\n  //! \\brief Returns whether the address range contains another address range.\n  //!\n  //! \\param[in] that The (possibly) contained address range.\n  //!\n  //! \\return `true` if `this` address range, the containing address range,\n  //!     contains \\a that, the contained address range. `false` otherwise.\n  //!\n  //! An address range contains another address range when the contained address\n  //! range’s base is greater than or equal to the containing address range’s\n  //! base, and the contained address range’s end is less than or equal to the\n  //! containing address range’s end.\n  //!\n  //! This method should only be called on two CheckedAddressRangeGeneric\n  //! objects representing address ranges in the same process.\n  //!\n  //! This method must only be called if IsValid() would return `true` for both\n  //! CheckedAddressRangeGeneric objects involved.\n  bool ContainsRange(const CheckedAddressRangeGeneric& that) const;\n\n  //! \\brief Returns a string describing the address range.\n  //!\n  //! The string will be formatted as `\"0x123 + 0x45 (64)\"`, where the\n  //! individual components are the address, size, and bitness.\n  std::string AsString() const;\n\n private:\n#if defined(COMPILER_MSVC)\n  // MSVC cannot handle a union containing CheckedRange (with constructor, etc.)\n  // currently.\n  CheckedRange<uint32_t> range_32_;\n  CheckedRange<uint64_t> range_64_;\n#else\n  // The field of the union that is expressed is determined by is_64_bit_.\n  union {\n    CheckedRange<uint32_t> range_32_;\n    CheckedRange<uint64_t> range_64_;\n  };\n#endif\n\n  // Determines which field of the union is expressed.\n  bool is_64_bit_;\n\n  // Whether the base and size were valid for their data type when set. This is\n  // always true when is_64_bit_ is true because the underlying data types are\n  // 64 bits wide and there is no possibility for range and size to overflow.\n  // When is_64_bit_ is false, range_ok_ will be false if SetRange() was passed\n  // a base or size that overflowed the underlying 32-bit data type. This field\n  // is necessary because the interface exposes the address and size types\n  // uniformly, but these types are too wide for the underlying pointer and size\n  // types in 32-bit processes.\n  bool range_ok_;\n};\n\n}  // namespace internal\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NUMERIC_CHECKED_ADDRESS_RANGE_H_\n"
  },
  {
    "path": "util/numeric/checked_address_range_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/numeric/checked_address_range.h\"\n\n#include <sys/types.h>\n\n#include <iterator>\n#include <limits>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nusing CheckedAddressRange =\n    internal::CheckedAddressRangeGeneric<uint64_t, uint64_t>;\n\nenum Validity {\n  kInvalid = false,\n  kValid,\n  kValid64Invalid32,\n};\n\nbool ExpectationForValidity32(Validity validity) {\n  return validity == kValid;\n}\n\nbool ExpectationForValidity64(Validity validity) {\n  return validity == kValid || validity == kValid64Invalid32;\n}\n\nTEST(CheckedAddressRange, IsValid) {\n  static constexpr struct {\n    uint64_t base;\n    uint64_t size;\n    Validity validity;\n  } kTestData[] = {\n      {0, 0, kValid},\n      {0, 1, kValid},\n      {0, 2, kValid},\n      {0, 0x7fffffff, kValid},\n      {0, 0x80000000, kValid},\n      {0, 0xfffffffe, kValid},\n      {0, 0xffffffff, kValid},\n      {0, 0xffffffffffffffff, kValid64Invalid32},\n      {1, 0, kValid},\n      {1, 1, kValid},\n      {1, 2, kValid},\n      {1, 0x7fffffff, kValid},\n      {1, 0x80000000, kValid},\n      {1, 0xfffffffe, kValid},\n      {1, 0xffffffff, kValid64Invalid32},\n      {1, 0xfffffffffffffffe, kValid64Invalid32},\n      {1, 0xffffffffffffffff, kInvalid},\n      {0x7fffffff, 0, kValid},\n      {0x7fffffff, 1, kValid},\n      {0x7fffffff, 2, kValid},\n      {0x7fffffff, 0x7fffffff, kValid},\n      {0x7fffffff, 0x80000000, kValid},\n      {0x7fffffff, 0xfffffffe, kValid64Invalid32},\n      {0x7fffffff, 0xffffffff, kValid64Invalid32},\n      {0x80000000, 0, kValid},\n      {0x80000000, 1, kValid},\n      {0x80000000, 2, kValid},\n      {0x80000000, 0x7fffffff, kValid},\n      {0x80000000, 0x80000000, kValid64Invalid32},\n      {0x80000000, 0xfffffffe, kValid64Invalid32},\n      {0x80000000, 0xffffffff, kValid64Invalid32},\n      {0xfffffffe, 0, kValid},\n      {0xfffffffe, 1, kValid},\n      {0xfffffffe, 2, kValid64Invalid32},\n      {0xfffffffe, 0x7fffffff, kValid64Invalid32},\n      {0xfffffffe, 0x80000000, kValid64Invalid32},\n      {0xfffffffe, 0xfffffffe, kValid64Invalid32},\n      {0xfffffffe, 0xffffffff, kValid64Invalid32},\n      {0xffffffff, 0, kValid},\n      {0xffffffff, 1, kValid64Invalid32},\n      {0xffffffff, 2, kValid64Invalid32},\n      {0xffffffff, 0x7fffffff, kValid64Invalid32},\n      {0xffffffff, 0x80000000, kValid64Invalid32},\n      {0xffffffff, 0xfffffffe, kValid64Invalid32},\n      {0xffffffff, 0xffffffff, kValid64Invalid32},\n      {0x7fffffffffffffff, 0, kValid64Invalid32},\n      {0x7fffffffffffffff, 1, kValid64Invalid32},\n      {0x7fffffffffffffff, 2, kValid64Invalid32},\n      {0x7fffffffffffffff, 0x7fffffffffffffff, kValid64Invalid32},\n      {0x7fffffffffffffff, 0x8000000000000000, kValid64Invalid32},\n      {0x7fffffffffffffff, 0x8000000000000001, kInvalid},\n      {0x7fffffffffffffff, 0xfffffffffffffffe, kInvalid},\n      {0x7fffffffffffffff, 0xffffffffffffffff, kInvalid},\n      {0x8000000000000000, 0, kValid64Invalid32},\n      {0x8000000000000000, 1, kValid64Invalid32},\n      {0x8000000000000000, 2, kValid64Invalid32},\n      {0x8000000000000000, 0x7fffffffffffffff, kValid64Invalid32},\n      {0x8000000000000000, 0x8000000000000000, kInvalid},\n      {0x8000000000000000, 0x8000000000000001, kInvalid},\n      {0x8000000000000000, 0xfffffffffffffffe, kInvalid},\n      {0x8000000000000000, 0xffffffffffffffff, kInvalid},\n      {0xfffffffffffffffe, 0, kValid64Invalid32},\n      {0xfffffffffffffffe, 1, kValid64Invalid32},\n      {0xfffffffffffffffe, 2, kInvalid},\n      {0xffffffffffffffff, 0, kValid64Invalid32},\n      {0xffffffffffffffff, 1, kInvalid},\n  };\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS\n                                    \", base 0x%\" PRIx64 \", size 0x%\" PRIx64,\n                                    index,\n                                    testcase.base,\n                                    testcase.size));\n\n    CheckedAddressRange range_32(false, testcase.base, testcase.size);\n    EXPECT_EQ(range_32.IsValid(), ExpectationForValidity32(testcase.validity));\n\n    CheckedAddressRange range_64(true, testcase.base, testcase.size);\n    EXPECT_EQ(range_64.IsValid(), ExpectationForValidity64(testcase.validity));\n  }\n}\n\nTEST(CheckedAddressRange, ContainsValue) {\n  static constexpr struct {\n    uint64_t value;\n    bool expectation;\n  } kTestData[] = {\n      {0, false},\n      {1, false},\n      {0x1fff, false},\n      {0x2000, true},\n      {0x2001, true},\n      {0x2ffe, true},\n      {0x2fff, true},\n      {0x3000, false},\n      {0x3001, false},\n      {0x7fffffff, false},\n      {0x80000000, false},\n      {0x80000001, false},\n      {0x80001fff, false},\n      {0x80002000, false},\n      {0x80002001, false},\n      {0x80002ffe, false},\n      {0x80002fff, false},\n      {0x80003000, false},\n      {0x80003001, false},\n      {0xffffcfff, false},\n      {0xffffdfff, false},\n      {0xffffefff, false},\n      {0xffffffff, false},\n      {0x100000000, false},\n      {0xffffffffffffffff, false},\n  };\n\n  CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);\n  ASSERT_TRUE(parent_range_32.IsValid());\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %\" PRIuS \", value 0x%\" PRIx64, index, testcase.value));\n\n    EXPECT_EQ(parent_range_32.ContainsValue(testcase.value),\n              testcase.expectation);\n  }\n\n  CheckedAddressRange parent_range_64(true, 0x100000000, 0x1000);\n  ASSERT_TRUE(parent_range_64.IsValid());\n  EXPECT_FALSE(parent_range_64.ContainsValue(0xffffffff));\n  EXPECT_TRUE(parent_range_64.ContainsValue(0x100000000));\n  EXPECT_TRUE(parent_range_64.ContainsValue(0x100000001));\n  EXPECT_TRUE(parent_range_64.ContainsValue(0x100000fff));\n  EXPECT_FALSE(parent_range_64.ContainsValue(0x100001000));\n}\n\nTEST(CheckedAddressRange, ContainsRange) {\n  static constexpr struct {\n    uint64_t base;\n    uint64_t size;\n    bool expectation;\n  } kTestData[] = {\n      {0, 0, false},\n      {0, 1, false},\n      {0x2000, 0x1000, true},\n      {0, 0x2000, false},\n      {0x3000, 0x1000, false},\n      {0x1800, 0x1000, false},\n      {0x2800, 0x1000, false},\n      {0x2000, 0x800, true},\n      {0x2800, 0x800, true},\n      {0x2400, 0x800, true},\n      {0x2800, 0, true},\n      {0x2000, 0xffffdfff, false},\n      {0x2800, 0xffffd7ff, false},\n      {0x3000, 0xffffcfff, false},\n      {0xfffffffe, 1, false},\n      {0xffffffff, 0, false},\n      {0x1fff, 0, false},\n      {0x2000, 0, true},\n      {0x2001, 0, true},\n      {0x2fff, 0, true},\n      {0x3000, 0, true},\n      {0x3001, 0, false},\n      {0x1fff, 1, false},\n      {0x2000, 1, true},\n      {0x2001, 1, true},\n      {0x2fff, 1, true},\n      {0x3000, 1, false},\n      {0x3001, 1, false},\n  };\n\n  CheckedAddressRange parent_range_32(false, 0x2000, 0x1000);\n  ASSERT_TRUE(parent_range_32.IsValid());\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS\n                                    \", base 0x%\" PRIx64 \", size 0x%\" PRIx64,\n                                    index,\n                                    testcase.base,\n                                    testcase.size));\n\n    CheckedAddressRange child_range_32(false, testcase.base, testcase.size);\n    ASSERT_TRUE(child_range_32.IsValid());\n    EXPECT_EQ(parent_range_32.ContainsRange(child_range_32),\n              testcase.expectation);\n  }\n\n  CheckedAddressRange parent_range_64(true, 0x100000000, 0x1000);\n  ASSERT_TRUE(parent_range_64.IsValid());\n\n  CheckedAddressRange child_range_64(true, 0xffffffff, 2);\n  EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));\n\n  child_range_64.SetRange(true, 0x100000000, 2);\n  EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));\n\n  child_range_64.SetRange(true, 0x100000ffe, 2);\n  EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));\n\n  child_range_64.SetRange(true, 0x100000fff, 2);\n  EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/numeric/checked_range.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_\n#define CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_\n\n#include <limits>\n#include <tuple>\n\n#include \"base/check.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/numerics/safe_math.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\n\n//! \\brief Ensures that a range, composed of a base and size, does not overflow\n//!     its data type.\ntemplate <typename ValueType, typename SizeType = ValueType>\nclass CheckedRange {\n public:\n  CheckedRange(ValueType base, SizeType size) {\n    static_assert(!std::numeric_limits<SizeType>::is_signed,\n                  \"SizeType must be unsigned\");\n    SetRange(base, size);\n  }\n\n  //! \\brief Sets the range’s base and size to \\a base and \\a size,\n  //!     respectively.\n  void SetRange(ValueType base, SizeType size) {\n    base_ = base;\n    size_ = size;\n  }\n\n  //! \\brief The range’s base.\n  ValueType base() const { return base_; }\n\n  //! \\brief The range’s size.\n  SizeType size() const { return size_; }\n\n  //! \\brief The range’s end (its base plus its size).\n  ValueType end() const { return base_ + size_; }\n\n  //! \\brief Returns the validity of the range.\n  //!\n  //! \\return `true` if the range is valid, `false` otherwise.\n  //!\n  //! A range is valid if its size can be converted to the range’s data type\n  //! without data loss, and if its end (base plus size) can be computed without\n  //! overflowing its data type.\n  bool IsValid() const {\n    if (!base::IsValueInRangeForNumericType<ValueType, SizeType>(size_)) {\n      return false;\n    }\n    base::CheckedNumeric<ValueType> checked_end(base_);\n    checked_end += implicit_cast<ValueType>(size_);\n    return checked_end.IsValid();\n  }\n\n  //! \\brief Returns whether the range contains another value.\n  //!\n  //! \\param[in] value The (possibly) contained value.\n  //!\n  //! \\return `true` if the range contains \\a value, `false` otherwise.\n  //!\n  //! A range contains a value if the value is greater than or equal to its\n  //! base, and less than its end (base plus size).\n  //!\n  //! This method must only be called if IsValid() would return `true`.\n  bool ContainsValue(ValueType value) const {\n    DCHECK(IsValid());\n\n    return value >= base() && value < end();\n  }\n\n  //! \\brief Returns whether the range contains another range.\n  //!\n  //! \\param[in] that The (possibly) contained range.\n  //!\n  //! \\return `true` if `this` range, the containing range, contains \\a that,\n  //!     the contained range. `false` otherwise.\n  //!\n  //! A range contains another range when the contained range’s base is greater\n  //! than or equal to the containing range’s base, and the contained range’s\n  //! end is less than or equal to the containing range’s end.\n  //!\n  //! This method must only be called if IsValid() would return `true` for both\n  //! CheckedRange objects involved.\n  bool ContainsRange(const CheckedRange<ValueType, SizeType>& that) const {\n    DCHECK(IsValid());\n    DCHECK(that.IsValid());\n\n    return that.base() >= base() && that.end() <= end();\n  }\n\n  //! \\brief Returns whether the range overlaps another range.\n  //!\n  //! \\param[in] that The (possibly) overlapping range.\n  //!\n  //! \\return `true` if `this` range, the first range, overlaps \\a that,\n  //!     the provided range. `false` otherwise.\n  //!\n  //! Ranges are considered to be closed-open [base, end) for this test. Zero\n  //! length ranges are never considered to overlap another range.\n  //!\n  //! This method must only be called if IsValid() would return `true` for both\n  //! CheckedRange objects involved.\n  bool OverlapsRange(const CheckedRange<ValueType, SizeType>& that) const {\n    DCHECK(IsValid());\n    DCHECK(that.IsValid());\n\n    if (size() == 0 || that.size() == 0)\n      return false;\n\n    return base() < that.end() && that.base() < end();\n  }\n\n  bool operator<(const CheckedRange& other) const {\n    return std::tie(base_, size_) < std::tie(other.base_, other.size_);\n  }\n\n private:\n  ValueType base_;\n  SizeType size_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NUMERIC_CHECKED_RANGE_H_\n"
  },
  {
    "path": "util/numeric/checked_range_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/numeric/checked_range.h\"\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <limits>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(CheckedRange, IsValid) {\n  static constexpr struct {\n    uint32_t base;\n    uint32_t size;\n    bool valid;\n  } kUnsignedTestData[] = {\n      {0, 0, true},\n      {0, 1, true},\n      {0, 2, true},\n      {0, 0x7fffffff, true},\n      {0, 0x80000000, true},\n      {0, 0xfffffffe, true},\n      {0, 0xffffffff, true},\n      {1, 0, true},\n      {1, 1, true},\n      {1, 2, true},\n      {1, 0x7fffffff, true},\n      {1, 0x80000000, true},\n      {1, 0xfffffffe, true},\n      {1, 0xffffffff, false},\n      {0x7fffffff, 0, true},\n      {0x7fffffff, 1, true},\n      {0x7fffffff, 2, true},\n      {0x7fffffff, 0x7fffffff, true},\n      {0x7fffffff, 0x80000000, true},\n      {0x7fffffff, 0xfffffffe, false},\n      {0x7fffffff, 0xffffffff, false},\n      {0x80000000, 0, true},\n      {0x80000000, 1, true},\n      {0x80000000, 2, true},\n      {0x80000000, 0x7fffffff, true},\n      {0x80000000, 0x80000000, false},\n      {0x80000000, 0xfffffffe, false},\n      {0x80000000, 0xffffffff, false},\n      {0xfffffffe, 0, true},\n      {0xfffffffe, 1, true},\n      {0xfffffffe, 2, false},\n      {0xfffffffe, 0x7fffffff, false},\n      {0xfffffffe, 0x80000000, false},\n      {0xfffffffe, 0xfffffffe, false},\n      {0xfffffffe, 0xffffffff, false},\n      {0xffffffff, 0, true},\n      {0xffffffff, 1, false},\n      {0xffffffff, 2, false},\n      {0xffffffff, 0x7fffffff, false},\n      {0xffffffff, 0x80000000, false},\n      {0xffffffff, 0xfffffffe, false},\n      {0xffffffff, 0xffffffff, false},\n  };\n\n  for (size_t index = 0; index < std::size(kUnsignedTestData); ++index) {\n    const auto& testcase = kUnsignedTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"unsigned index %\" PRIuS\n                                    \", base 0x%x, size 0x%x\",\n                                    index,\n                                    testcase.base,\n                                    testcase.size));\n\n    CheckedRange<uint32_t> range(testcase.base, testcase.size);\n    EXPECT_EQ(range.IsValid(), testcase.valid);\n  }\n\n  const int32_t kMinInt32 = std::numeric_limits<int32_t>::min();\n  static constexpr struct {\n    int32_t base;\n    uint32_t size;\n    bool valid;\n  } kSignedTestData[] = {\n      {0, 0, true},\n      {0, 1, true},\n      {0, 2, true},\n      {0, 0x7fffffff, true},\n      {0, 0x80000000, false},\n      {0, 0xfffffffe, false},\n      {0, 0xffffffff, false},\n      {1, 0, true},\n      {1, 1, true},\n      {1, 2, true},\n      {1, 0x7fffffff, false},\n      {1, 0x80000000, false},\n      {1, 0xfffffffe, false},\n      {1, 0xffffffff, false},\n      {0x7fffffff, 0, true},\n      {0x7fffffff, 1, false},\n      {0x7fffffff, 2, false},\n      {0x7fffffff, 0x7fffffff, false},\n      {0x7fffffff, 0x80000000, false},\n      {0x7fffffff, 0xfffffffe, false},\n      {0x7fffffff, 0xffffffff, false},\n      {kMinInt32, 0, true},\n      {kMinInt32, 1, true},\n      {kMinInt32, 2, true},\n      {kMinInt32, 0x7fffffff, true},\n      {kMinInt32, 0x80000000, false},\n      {kMinInt32, 0xfffffffe, false},\n      {kMinInt32, 0xffffffff, false},\n      {-2, 0, true},\n      {-2, 1, true},\n      {-2, 2, true},\n      {-2, 0x7fffffff, true},\n      {-2, 0x80000000, false},\n      {-2, 0xfffffffe, false},\n      {-2, 0xffffffff, false},\n      {-1, 0, true},\n      {-1, 1, true},\n      {-1, 2, true},\n      {-1, 0x7fffffff, true},\n      {-1, 0x80000000, false},\n      {-1, 0xfffffffe, false},\n      {-1, 0xffffffff, false},\n  };\n\n  for (size_t index = 0; index < std::size(kSignedTestData); ++index) {\n    const auto& testcase = kSignedTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"signed index %\" PRIuS\n                                    \", base 0x%x, size 0x%x\",\n                                    index,\n                                    testcase.base,\n                                    testcase.size));\n\n    CheckedRange<int32_t, uint32_t> range(testcase.base, testcase.size);\n    EXPECT_EQ(range.IsValid(), testcase.valid);\n  }\n}\n\nTEST(CheckedRange, ContainsValue) {\n  static constexpr struct {\n    uint32_t value;\n    bool contains;\n  } kTestData[] = {\n      {0, false},\n      {1, false},\n      {0x1fff, false},\n      {0x2000, true},\n      {0x2001, true},\n      {0x2ffe, true},\n      {0x2fff, true},\n      {0x3000, false},\n      {0x3001, false},\n      {0x7fffffff, false},\n      {0x80000000, false},\n      {0x80000001, false},\n      {0x80001fff, false},\n      {0x80002000, false},\n      {0x80002001, false},\n      {0x80002ffe, false},\n      {0x80002fff, false},\n      {0x80003000, false},\n      {0x80003001, false},\n      {0xffffcfff, false},\n      {0xffffdfff, false},\n      {0xffffefff, false},\n      {0xffffffff, false},\n  };\n\n  CheckedRange<uint32_t> parent_range(0x2000, 0x1000);\n  ASSERT_TRUE(parent_range.IsValid());\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %\" PRIuS \", value 0x%x\", index, testcase.value));\n\n    EXPECT_EQ(parent_range.ContainsValue(testcase.value), testcase.contains);\n  }\n}\n\nTEST(CheckedRange, ContainsRange) {\n  static constexpr struct {\n    uint32_t base;\n    uint32_t size;\n    bool contains;\n  } kTestData[] = {\n      {0, 0, false},\n      {0, 1, false},\n      {0x2000, 0x1000, true},\n      {0, 0x2000, false},\n      {0x3000, 0x1000, false},\n      {0x1800, 0x1000, false},\n      {0x2800, 0x1000, false},\n      {0x2000, 0x800, true},\n      {0x2800, 0x800, true},\n      {0x2400, 0x800, true},\n      {0x2800, 0, true},\n      {0x2000, 0xffffdfff, false},\n      {0x2800, 0xffffd7ff, false},\n      {0x3000, 0xffffcfff, false},\n      {0xfffffffe, 1, false},\n      {0xffffffff, 0, false},\n      {0x1fff, 0, false},\n      {0x2000, 0, true},\n      {0x2001, 0, true},\n      {0x2fff, 0, true},\n      {0x3000, 0, true},\n      {0x3001, 0, false},\n      {0x1fff, 1, false},\n      {0x2000, 1, true},\n      {0x2001, 1, true},\n      {0x2fff, 1, true},\n      {0x3000, 1, false},\n      {0x3001, 1, false},\n  };\n\n  CheckedRange<uint32_t> parent_range(0x2000, 0x1000);\n  ASSERT_TRUE(parent_range.IsValid());\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS \", base 0x%x, size 0x%x\",\n                                    index,\n                                    testcase.base,\n                                    testcase.size));\n\n    CheckedRange<uint32_t> child_range(testcase.base, testcase.size);\n    ASSERT_TRUE(child_range.IsValid());\n    EXPECT_EQ(parent_range.ContainsRange(child_range), testcase.contains);\n  }\n}\n\nTEST(CheckedRange, OverlapsRange) {\n  static constexpr struct {\n    uint32_t base;\n    uint32_t size;\n    bool overlaps;\n  } kTestData[] = {\n      {0, 0, false},\n      {0, 1, false},\n      {0x2000, 0x1000, true},\n      {0, 0x2000, false},\n      {0x3000, 0x1000, false},\n      {0x1800, 0x1000, true},\n      {0x1800, 0x2000, true},\n      {0x2800, 0x1000, true},\n      {0x2000, 0x800, true},\n      {0x2800, 0x800, true},\n      {0x2400, 0x800, true},\n      {0x2800, 0, false},\n      {0x2000, 0xffffdfff, true},\n      {0x2800, 0xffffd7ff, true},\n      {0x3000, 0xffffcfff, false},\n      {0xfffffffe, 1, false},\n      {0xffffffff, 0, false},\n      {0x1fff, 0, false},\n      {0x2000, 0, false},\n      {0x2001, 0, false},\n      {0x2fff, 0, false},\n      {0x3000, 0, false},\n      {0x3001, 0, false},\n      {0x1fff, 1, false},\n      {0x2000, 1, true},\n      {0x2001, 1, true},\n      {0x2fff, 1, true},\n      {0x3000, 1, false},\n      {0x3001, 1, false},\n  };\n\n  CheckedRange<uint32_t> first_range(0x2000, 0x1000);\n  ASSERT_TRUE(first_range.IsValid());\n\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto& testcase = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\"index %\" PRIuS \", base 0x%x, size 0x%x\",\n                                    index,\n                                    testcase.base,\n                                    testcase.size));\n\n    CheckedRange<uint32_t> second_range(testcase.base, testcase.size);\n    ASSERT_TRUE(second_range.IsValid());\n    EXPECT_EQ(first_range.OverlapsRange(second_range), testcase.overlaps);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/numeric/checked_vm_address_range.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NUMERIC_CHECKED_VM_ADDRESS_RANGE_H_\n#define CRASHPAD_UTIL_NUMERIC_CHECKED_VM_ADDRESS_RANGE_H_\n\n#include \"util/misc/address_types.h\"\n#include \"util/numeric/checked_address_range.h\"\n\nnamespace crashpad {\n\n//! \\brief Ensures that a range, composed of a base and a size, does not\n//!     overflow the pointer type of the process it describes a range in.\n//!\n//! This class checks bases of type #VMAddress and sizes of type #VMSize against\n//! a process whose pointer type is either 32 or 64 bits wide.\n//!\n//! Aside from varying the overall range on the basis of a process’ pointer type\n//! width, this class functions very similarly to CheckedRange.\nusing CheckedVMAddressRange =\n    internal::CheckedAddressRangeGeneric<VMAddress, VMSize>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NUMERIC_CHECKED_VM_ADDRESS_RANGE_H_\n"
  },
  {
    "path": "util/numeric/in_range_cast.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_\n#define CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_\n\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n\nnamespace crashpad {\n\n//! \\brief Casts to a different type if it can be done without data loss,\n//!     logging a warning message and returing a default value otherwise.\n//!\n//! \\param[in] source The value to convert and return.\n//! \\param[in] default_value The default value to return, in the event that \\a\n//!     source cannot be represented in the destination type.\n//!\n//! \\return \\a source if it can be represented in the destination type,\n//!     otherwise \\a default_value.\ntemplate <typename Destination, typename Source>\nDestination InRangeCast(Source source, Destination default_value) {\n  if (base::IsValueInRangeForNumericType<Destination>(source)) {\n    return static_cast<Destination>(source);\n  }\n\n  LOG(WARNING) << \"value \" << source << \" out of range\";\n  return static_cast<Destination>(default_value);\n}\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NUMERIC_IN_RANGE_CAST_H_\n"
  },
  {
    "path": "util/numeric/in_range_cast_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/numeric/in_range_cast.h\"\n\n#include <stdint.h>\n\n#include <limits>\n\n#include \"gtest/gtest.h\"\n#include \"util/misc/implicit_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr int32_t kInt32Min = std::numeric_limits<int32_t>::min();\nconstexpr int64_t kInt64Min = std::numeric_limits<int64_t>::min();\n\nTEST(InRangeCast, Uint32) {\n  EXPECT_EQ(InRangeCast<uint32_t>(0, 1), 0u);\n  EXPECT_EQ(InRangeCast<uint32_t>(1, 1), 1u);\n  EXPECT_EQ(InRangeCast<uint32_t>(2, 1), 2u);\n  EXPECT_EQ(InRangeCast<uint32_t>(-1, 0), 0u);\n  EXPECT_EQ(InRangeCast<uint32_t>(-1, 1), 1u);\n  EXPECT_EQ(InRangeCast<uint32_t>(-1, 2), 2u);\n  EXPECT_EQ(InRangeCast<uint32_t>(0xffffffffu, 1), 0xffffffffu);\n  EXPECT_EQ(InRangeCast<uint32_t>(UINT64_C(0xffffffff), 1), 0xffffffffu);\n  EXPECT_EQ(InRangeCast<uint32_t>(UINT64_C(0x100000000), 1), 1u);\n  EXPECT_EQ(InRangeCast<uint32_t>(UINT64_C(0x100000001), 1), 1u);\n  EXPECT_EQ(InRangeCast<uint32_t>(kInt32Min, 1), 1u);\n  EXPECT_EQ(InRangeCast<uint32_t>(kInt64Min, 1), 1u);\n  EXPECT_EQ(InRangeCast<uint32_t>(-1, 0xffffffffu), 0xffffffffu);\n}\n\nTEST(InRangeCast, Int32) {\n  EXPECT_EQ(InRangeCast<int32_t>(0, 1), 0);\n  EXPECT_EQ(InRangeCast<int32_t>(1, 1), 1);\n  EXPECT_EQ(InRangeCast<int32_t>(2, 1), 2);\n  EXPECT_EQ(InRangeCast<int32_t>(-1, 1), -1);\n  EXPECT_EQ(InRangeCast<int32_t>(0x7fffffff, 1), 0x7fffffff);\n  EXPECT_EQ(InRangeCast<int32_t>(0x7fffffffu, 1), 0x7fffffff);\n  EXPECT_EQ(InRangeCast<int32_t>(0x80000000u, 1), 1);\n  EXPECT_EQ(InRangeCast<int32_t>(0xffffffffu, 1), 1);\n  EXPECT_EQ(InRangeCast<int32_t>(INT64_C(0x80000000), 1), 1);\n  EXPECT_EQ(InRangeCast<int32_t>(INT64_C(0xffffffff), 1), 1);\n  EXPECT_EQ(InRangeCast<int32_t>(INT64_C(0x100000000), 1), 1);\n  EXPECT_EQ(InRangeCast<int32_t>(kInt32Min, 1), kInt32Min);\n  EXPECT_EQ(InRangeCast<int32_t>(implicit_cast<int64_t>(kInt32Min), 1),\n            kInt32Min);\n  EXPECT_EQ(InRangeCast<int32_t>(implicit_cast<int64_t>(kInt32Min) - 1, 1), 1);\n  EXPECT_EQ(InRangeCast<int32_t>(kInt64Min, 1), 1);\n  EXPECT_EQ(InRangeCast<int32_t>(0xffffffffu, 0), 0);\n  EXPECT_EQ(InRangeCast<int32_t>(0xffffffffu, -1), -1);\n  EXPECT_EQ(InRangeCast<int32_t>(0xffffffffu, kInt32Min), kInt32Min);\n  EXPECT_EQ(InRangeCast<int32_t>(0xffffffffu, 0x7fffffff), 0x7fffffff);\n}\n\nTEST(InRangeCast, Uint64) {\n  EXPECT_EQ(InRangeCast<uint64_t>(0, 1), 0u);\n  EXPECT_EQ(InRangeCast<uint64_t>(1, 1), 1u);\n  EXPECT_EQ(InRangeCast<uint64_t>(2, 1), 2u);\n  EXPECT_EQ(InRangeCast<uint64_t>(-1, 0), 0u);\n  EXPECT_EQ(InRangeCast<uint64_t>(-1, 1), 1u);\n  EXPECT_EQ(InRangeCast<uint64_t>(-1, 2), 2u);\n  EXPECT_EQ(InRangeCast<uint64_t>(0xffffffffu, 1), 0xffffffffu);\n  EXPECT_EQ(InRangeCast<uint64_t>(UINT64_C(0xffffffff), 1), 0xffffffffu);\n  EXPECT_EQ(InRangeCast<uint64_t>(UINT64_C(0x100000000), 1),\n            UINT64_C(0x100000000));\n  EXPECT_EQ(InRangeCast<uint64_t>(UINT64_C(0x100000001), 1),\n            UINT64_C(0x100000001));\n  EXPECT_EQ(InRangeCast<uint64_t>(kInt32Min, 1), 1u);\n  EXPECT_EQ(InRangeCast<uint64_t>(INT64_C(-1), 1), 1u);\n  EXPECT_EQ(InRangeCast<uint64_t>(kInt64Min, 1), 1u);\n  EXPECT_EQ(InRangeCast<uint64_t>(-1, UINT64_C(0xffffffffffffffff)),\n            UINT64_C(0xffffffffffffffff));\n}\n\nTEST(InRangeCast, Int64) {\n  EXPECT_EQ(InRangeCast<int64_t>(0, 1), 0);\n  EXPECT_EQ(InRangeCast<int64_t>(1, 1), 1);\n  EXPECT_EQ(InRangeCast<int64_t>(2, 1), 2);\n  EXPECT_EQ(InRangeCast<int64_t>(-1, 1), -1);\n  EXPECT_EQ(InRangeCast<int64_t>(0x7fffffff, 1), 0x7fffffff);\n  EXPECT_EQ(InRangeCast<int64_t>(0x7fffffffu, 1), 0x7fffffff);\n  EXPECT_EQ(InRangeCast<int64_t>(0x80000000u, 1), INT64_C(0x80000000));\n  EXPECT_EQ(InRangeCast<int64_t>(0xffffffffu, 1), INT64_C(0xffffffff));\n  EXPECT_EQ(InRangeCast<int64_t>(INT64_C(0x80000000), 1), INT64_C(0x80000000));\n  EXPECT_EQ(InRangeCast<int64_t>(INT64_C(0xffffffff), 1), INT64_C(0xffffffff));\n  EXPECT_EQ(InRangeCast<int64_t>(INT64_C(0x100000000), 1),\n            INT64_C(0x100000000));\n  EXPECT_EQ(InRangeCast<int64_t>(INT64_C(0x7fffffffffffffff), 1),\n            INT64_C(0x7fffffffffffffff));\n  EXPECT_EQ(InRangeCast<int64_t>(UINT64_C(0x7fffffffffffffff), 1),\n            INT64_C(0x7fffffffffffffff));\n  EXPECT_EQ(InRangeCast<int64_t>(UINT64_C(0x8000000000000000), 1), 1);\n  EXPECT_EQ(InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), 1), 1);\n  EXPECT_EQ(InRangeCast<int64_t>(kInt32Min, 1), kInt32Min);\n  EXPECT_EQ(InRangeCast<int64_t>(implicit_cast<int64_t>(kInt32Min), 1),\n            kInt32Min);\n  EXPECT_EQ(InRangeCast<int64_t>(kInt64Min, 1), kInt64Min);\n  EXPECT_EQ(InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), 0), 0);\n  EXPECT_EQ(InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), -1), -1);\n  EXPECT_EQ(InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff), kInt64Min),\n            kInt64Min);\n  EXPECT_EQ(InRangeCast<int64_t>(UINT64_C(0xffffffffffffffff),\n                                 INT64_C(0x7fffffffffffffff)),\n            INT64_C(0x7fffffffffffffff));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/numeric/int128.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NUMERIC_INT128_H_\n#define CRASHPAD_UTIL_NUMERIC_INT128_H_\n\n#include <stdint.h>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n//! \\brief Stores a 128-bit quantity.\n//!\n//! This structure is organized so that 128-bit quantities are laid out in\n//! memory according to the system’s natural byte order. If a system offers a\n//! native 128-bit type, it should be possible to bit_cast<> between that type\n//! and this one.\n//!\n//! This structure is designed to have the same layout, although not the same\n//! field names, as the Windows SDK’s `M128A` type from `<winnt.h>`. It is\n//! provided here instead of in `compat` because it is useful outside of the\n//! scope of data structures defined by the Windows SDK.\nstruct uint128_struct {\n#if defined(ARCH_CPU_LITTLE_ENDIAN) || DOXYGEN\n  //! \\brief The low 64 bits of the 128-bit quantity.\n  uint64_t lo;\n\n  //! \\brief The high 64 bits of the 128-bit quantity.\n  uint64_t hi;\n#else\n  uint64_t hi;\n  uint64_t lo;\n#endif\n};\n\nstatic_assert(sizeof(uint128_struct) == 16, \"uint128 must be 16 bytes\");\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NUMERIC_INT128_H_\n"
  },
  {
    "path": "util/numeric/int128_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/numeric/int128.h\"\n\n#include \"base/bit_cast.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Int128, UInt128) {\n#if defined(ARCH_CPU_LITTLE_ENDIAN)\n  static constexpr uint8_t kBytes[] =\n      {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};\n#else\n  static constexpr uint8_t kBytes[] =\n      {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};\n#endif\n\n  uint128_struct uint128;\n  static_assert(sizeof(uint128) == sizeof(kBytes), \"sizes must be equal\");\n\n  uint128 = base::bit_cast<uint128_struct>(kBytes);\n\n  EXPECT_EQ(uint128.lo, 0x0706050403020100u);\n  EXPECT_EQ(uint128.hi, 0x0f0e0d0c0b0a0908u);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/numeric/safe_assignment.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_\n#define CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_\n\n#include \"base/numerics/safe_conversions.h\"\n\nnamespace crashpad {\n\n//! \\brief Performs an assignment if it can be done safely, and signals if it\n//!     cannot be done safely.\n//!\n//! \\param[out] destination A pointer to the variable to be assigned to.\n//! \\param[in] source The value to assign.\n//!\n//! \\return `true` if \\a source is in the range supported by the type of \\a\n//!     *destination, with the assignment to \\a *destination having been\n//!     performed. `false` if the assignment cannot be completed safely because\n//!     \\a source is outside of this range.\ntemplate <typename Destination, typename Source>\nbool AssignIfInRange(Destination* destination, Source source) {\n  if (!base::IsValueInRangeForNumericType<Destination>(source)) {\n    return false;\n  }\n\n  *destination = static_cast<Destination>(source);\n  return true;\n}\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_NUMERIC_SAFE_ASSIGNMENT_H_\n"
  },
  {
    "path": "util/posix/close_multiple.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/close_multiple.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <iterator>\n\n#include \"base/check_op.h\"\n#include \"base/files/scoped_file.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/strings/string_number_conversions.h\"\n#include \"build/build_config.h\"\n#include \"util/file/directory_reader.h\"\n#include \"util/misc/implicit_cast.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <sys/sysctl.h>\n#endif\n\n// Everything in this file is expected to execute between fork() and exec(),\n// so everything called here must be acceptable in this context. However,\n// logging code that is not expected to execute under normal circumstances is\n// currently permitted.\n\nnamespace crashpad {\nnamespace {\n\n// This function attempts to close |fd| or mark it as close-on-exec. On systems\n// where close-on-exec is attempted, a failure to mark it close-on-exec will be\n// followed by an attempt to close it. |ebadf_ok| should be set to |true| if\n// the caller is attempting to close the file descriptor “blind,” that is,\n// without knowledge that it is or is not a valid file descriptor.\nvoid CloseNowOrOnExec(int fd, bool ebadf_ok) {\n  int rv;\n\n#if BUILDFLAG(IS_APPLE)\n  // Try to set close-on-exec, to avoid attempting to close a guarded FD with\n  // a close guard set.\n  rv = fcntl(fd, F_SETFD, FD_CLOEXEC);\n  if (rv != -1 || (ebadf_ok && errno == EBADF)) {\n    return;\n  }\n  PLOG(WARNING) << \"fcntl\";\n#endif\n\n  rv = IGNORE_EINTR(close(fd));\n  if (rv != 0 && !(ebadf_ok && errno == EBADF)) {\n    PLOG(WARNING) << \"close\";\n  }\n}\n\n// This function implements CloseMultipleNowOrOnExec() using an operating\n// system-specific FD directory to determine which file descriptors are open.\n// This is an advantage over looping over all possible file descriptors, because\n// no attempt needs to be made to close file descriptors that are not open.\nbool CloseMultipleNowOrOnExecUsingFDDir(int min_fd, int preserve_fd) {\n#if BUILDFLAG(IS_APPLE)\n  static constexpr char kFDDir[] = \"/dev/fd\";\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  static constexpr char kFDDir[] = \"/proc/self/fd\";\n#endif\n\n  DirectoryReader reader;\n  if (!reader.Open(base::FilePath(kFDDir))) {\n    return false;\n  }\n  int directory_fd = reader.DirectoryFD();\n  DCHECK_GE(directory_fd, 0);\n\n  base::FilePath entry_fd_path;\n  DirectoryReader::Result result;\n  while ((result = reader.NextFile(&entry_fd_path)) ==\n         DirectoryReader::Result::kSuccess) {\n    int entry_fd;\n    if (!base::StringToInt(entry_fd_path.value(), &entry_fd)) {\n      return false;\n    }\n\n    if (entry_fd >= min_fd && entry_fd != preserve_fd &&\n        entry_fd != directory_fd) {\n      CloseNowOrOnExec(entry_fd, false);\n    }\n  }\n  if (result == DirectoryReader::Result::kError) {\n    return false;\n  }\n\n  return true;\n}\n\n}  // namespace\n\nvoid CloseMultipleNowOrOnExec(int fd, int preserve_fd) {\n  if (CloseMultipleNowOrOnExecUsingFDDir(fd, preserve_fd)) {\n    return;\n  }\n\n  // Fallback: close every file descriptor starting at |fd| and ending at the\n  // system’s file descriptor limit. Check a few values and use the highest as\n  // the limit, because these may be based on the file descriptor limit set by\n  // setrlimit(), and higher-numbered file descriptors may have been opened\n  // prior to the limit being lowered. On both macOS and Linux glibc, both\n  // sysconf() and getdtablesize() return the current RLIMIT_NOFILE value, not\n  // the maximum possible file descriptor. For macOS, see 10.11.5\n  // Libc-1082.50.1/gen/FreeBSD/sysconf.c sysconf() and 10.11.6\n  // xnu-3248.60.10/bsd/kern/kern_descrip.c getdtablesize(). For Linux glibc,\n  // see glibc-2.24/sysdeps/posix/sysconf.c __sysconf() and\n  // glibc-2.24/sysdeps/posix/getdtsz.c __getdtablesize(). For Android, see\n  // 7.0.0 bionic/libc/bionic/sysconf.cpp sysconf() and\n  // bionic/libc/bionic/ndk_cruft.cpp getdtablesize().\n  int max_fd = implicit_cast<int>(sysconf(_SC_OPEN_MAX));\n\n#if !BUILDFLAG(IS_ANDROID)\n  // getdtablesize() was removed effective Android 5.0.0 (API 21). Since it\n  // returns the same thing as the sysconf() above, just skip it. See\n  // https://android.googlesource.com/platform/bionic/+/462abab12b074c62c0999859e65d5a32ebb41951.\n  max_fd = std::max(max_fd, getdtablesize());\n#endif\n\n#if !(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \\\n      BUILDFLAG(IS_ANDROID)) ||                        \\\n    defined(OPEN_MAX)\n  // Linux does not provide OPEN_MAX. See\n  // https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/include/linux/limits.h?id=77293034696e3e0b6c8b8fc1f96be091104b3d2b.\n  max_fd = std::max(max_fd, OPEN_MAX);\n#endif\n\n  // Consult a sysctl to determine the system-wide limit on the maximum number\n  // of open files per process. Note that it is possible to change this limit\n  // while the system is running, but it’s still a better upper bound than the\n  // current RLIMIT_NOFILE value.\n\n#if BUILDFLAG(IS_APPLE)\n  // See 10.11.6 xnu-3248.60.10/bsd/kern/kern_resource.c maxfilesperproc,\n  // referenced by dosetrlimit().\n  int oid[] = {CTL_KERN, KERN_MAXFILESPERPROC};\n  int maxfilesperproc;\n  size_t maxfilesperproc_size = sizeof(maxfilesperproc);\n  if (sysctl(oid,\n             std::size(oid),\n             &maxfilesperproc,\n             &maxfilesperproc_size,\n             nullptr,\n             0) == 0) {\n    max_fd = std::max(max_fd, maxfilesperproc);\n  } else {\n    PLOG(WARNING) << \"sysctl\";\n  }\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  // See linux-4.4.27/fs/file.c sysctl_nr_open, referenced by kernel/sys.c\n  // do_prlimit() and kernel/sysctl.c fs_table. Inability to open this file is\n  // not considered an error, because /proc may not be available or usable.\n  {\n    base::ScopedFILE nr_open_file(fopen(\"/proc/sys/fs/nr_open\", \"re\"));\n    if (nr_open_file.get() != nullptr) {\n      int nr_open;\n      if (fscanf(nr_open_file.get(), \"%d\\n\", &nr_open) == 1 &&\n          feof(nr_open_file.get())) {\n        max_fd = std::max(max_fd, nr_open);\n      } else {\n        LOG(WARNING) << \"/proc/sys/fs/nr_open format error\";\n      }\n    }\n  }\n#endif\n\n  for (int entry_fd = fd; entry_fd < max_fd; ++entry_fd) {\n    if (entry_fd != preserve_fd) {\n      CloseNowOrOnExec(entry_fd, true);\n    }\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/close_multiple.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_\n#define CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_\n\nnamespace crashpad {\n\n//! \\brief Close multiple file descriptors or mark them close-on-exec.\n//!\n//! This is similar to the BSD/Solaris-style `closefrom()` routine, which closes\n//! all open file descriptors equal to or higher than its \\a fd argument. This\n//! function must not be called while other threads are active. It is intended\n//! to be used in a child process created by `fork()`, prior to calling an\n//! `exec()`-family function. This guarantees that a (possibly untrustworthy)\n//! child process does not inherit file descriptors that it has no need for.\n//!\n//! Unlike the BSD function, this function may not close file descriptors\n//! immediately, but may instead mark them as close-on-exec. The actual behavior\n//! chosen is specific to the operating system. On macOS, file descriptors are\n//! marked close-on-exec instead of being closed outright in order to avoid\n//! raising `EXC_GUARD` exceptions for guarded file descriptors that are\n//! protected against `close()`.\n//!\n//! \\param[in] fd The lowest file descriptor to close or set as close-on-exec.\n//! \\param[in] preserve_fd A file descriptor to preserve and not close (or set\n//!     as close-on-exec), even if it is open and its value is greater than \\a\n//!     fd. To not preserve any file descriptor, pass `-1` for this parameter.\nvoid CloseMultipleNowOrOnExec(int fd, int preserve_fd);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_CLOSE_MULTIPLE_H_\n"
  },
  {
    "path": "util/posix/close_stdio.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/close_stdio.h\"\n\n#include <fcntl.h>\n#include <paths.h>\n#include <unistd.h>\n\n#include <tuple>\n\n#include \"base/check.h\"\n#include \"base/files/scoped_file.h\"\n#include \"base/posix/eintr_wrapper.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nvoid CloseStdioStream(int desired_fd, int oflag) {\n  base::ScopedFD fd(\n      HANDLE_EINTR(open(_PATH_DEVNULL, oflag | O_NOCTTY | O_CLOEXEC)));\n  if (fd == desired_fd) {\n    // Weird, but play along.\n    std::ignore = fd.release();\n  } else {\n    PCHECK(fd.get() >= 0) << \"open\";\n    PCHECK(HANDLE_EINTR(dup2(fd.get(), desired_fd)) != -1) << \"dup2\";\n    fd.reset();\n  }\n}\n\n}  // namespace\n\nvoid CloseStdinAndStdout() {\n  // Open /dev/null for stdin and stdout separately, so that it can be opened\n  // with the correct mode each time. This ensures that attempts to write to\n  // stdin or read from stdout fail with EBADF.\n  CloseStdioStream(STDIN_FILENO, O_RDONLY);\n  CloseStdioStream(STDOUT_FILENO, O_WRONLY);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/close_stdio.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_\n#define CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_\n\nnamespace crashpad {\n\n//! \\brief Closes `stdin` and `stdout` by opening `/dev/null` over them.\n//!\n//! It is normally inadvisable to `close()` the three standard input/output\n//! streams, because they occupy special file descriptors. Closing them outright\n//! could result in their file descriptors being reused. This causes problems\n//! for library code (including the standard library) that expects these file\n//! descriptors to have special meaning.\n//!\n//! This function discards the standard input and standard output streams by\n//! opening `/dev/null` and assigning it to their file descriptors, closing\n//! whatever had been at those file descriptors previously.\n//!\n//! `stderr`, the standard error stream, is not closed. It is often useful to\n//! retain the ability to send diagnostic messages to the standard error stream.\n//!\n//! \\note This function can only maintain its guarantees in a single-threaded\n//!     process, or in situations where the caller has control of all threads in\n//!     the process.\nvoid CloseStdinAndStdout();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_CLOSE_STDIO_H_\n"
  },
  {
    "path": "util/posix/drop_privileges.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <unistd.h>\n\n#include <ostream>\n\n#include \"base/check_op.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\nvoid DropPrivileges() {\n  gid_t gid = getgid();\n  uid_t uid = getuid();\n\n#if BUILDFLAG(IS_APPLE)\n  // Based on the POSIX.1-2008 2013 edition documentation for setreuid() and\n  // setregid(), setreuid() and setregid() alone should be sufficient to drop\n  // privileges. The standard specifies that the saved ID should be set to the\n  // effective ID whenever the real ID is not -1, or whenever the effective ID\n  // is set not equal to the real ID. This code never specifies -1, so the\n  // setreuid() and setregid() alone should work according to the standard.\n  //\n  // In practice, on older versions of macOS, setuid() and setgid() (or\n  // seteuid() and setegid()) must be called first. Otherwise, setreuid() and\n  // setregid() do not alter the saved IDs, leaving open the possibility for\n  // future privilege escalation.\n  //\n  // The problem exists in 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c\n  // setreuid(). Based on its comments, it purports to set the svuid to the new\n  // euid when the old svuid doesn’t match one of the new ruid and euid. This\n  // isn’t how POSIX.1-2008 says it should behave, but it should work for this\n  // function’s purposes. In reality, setreuid() doesn’t even do this: it sets\n  // the svuid to the old euid, which does not drop privileges when the old euid\n  // is different from the desired euid. The workaround of calling setuid() or\n  // seteuid() before setreuid() works because it sets the euid so that by the\n  // time setreuid() runs, the old euid is actually the value that ought to be\n  // set as the svuid. setregid() is similar. This bug was reported as radar\n  // 18987552, fixed in 10.10.3 and security updates to 10.9.5 and 10.8.5.\n  //\n  // setuid() and setgid() alone will only set the saved IDs when running as\n  // root. When running a setuid non-root or setgid program, they do not alter\n  // the saved ID, and do not effect a permanent privilege drop.\n  gid_t egid = getegid();\n  PCHECK(setgid(gid) == 0) << \"setgid\";\n  PCHECK(setregid(gid, gid) == 0) << \"setregid\";\n\n  uid_t euid = geteuid();\n  PCHECK(setuid(uid) == 0) << \"setuid\";\n  PCHECK(setreuid(uid, uid) == 0) << \"setreuid\";\n\n  if (uid != 0) {\n    // Because the setXid()+setreXid() interface to change IDs is fragile,\n    // ensure that privileges cannot be regained. This can only be done if the\n    // real user ID (and now the effective user ID as well) is not root, because\n    // root always has permission to change identity.\n    if (euid != uid) {\n      CHECK_EQ(seteuid(euid), -1);\n    }\n    if (egid != gid) {\n      CHECK_EQ(setegid(egid), -1);\n    }\n  }\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  PCHECK(setresgid(gid, gid, gid) == 0) << \"setresgid\";\n  PCHECK(setresuid(uid, uid, uid) == 0) << \"setresuid\";\n\n  // Don’t check to see if privileges can be regained on Linux, because on\n  // Linux, it’s not as simple as ensuring that this can’t be done if non-root.\n  // Instead, the ability to change user and group IDs are controlled by the\n  // CAP_SETUID and CAP_SETGID capabilities, which may be granted to non-root\n  // processes. Since the setresXid() interface is well-defined, it shouldn’t be\n  // necessary to perform any additional checking anyway.\n  //\n  // TODO(mark): Drop CAP_SETUID and CAP_SETGID if present and non-root?\n#else\n#error Port this function to your system.\n#endif\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/drop_privileges.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_\n#define CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_\n\nnamespace crashpad {\n\n//! \\brief Permanently drops privileges conferred by being a setuid or setgid\n//!     executable.\n//!\n//! The effective user ID and saved set-user ID are set to the real user ID,\n//! negating any effects of being a setuid executable. The effective group ID\n//! and saved set-group ID are set to the real group ID, negating any effects of\n//! being a setgid executable. Because the saved set-user ID and saved set-group\n//! ID are reset, there is no way to restore the prior privileges, and the drop\n//! is permanent.\n//!\n//! This function drops privileges correctly when running setuid root and in\n//! other circumstances, including when running setuid non-root. If the program\n//! is not a setuid or setgid executable, this function has no effect.\n//!\n//! No changes are made to the supplementary group list, which is normally not\n//! altered for setuid or setgid executables.\nvoid DropPrivileges();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_DROP_PRIVILEGES_H_\n"
  },
  {
    "path": "util/posix/process_info.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_\n#define CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_\n\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"build/build_config.h\"\n#include \"util/misc/initialization_state.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <mach/mach.h>\n#include <sys/sysctl.h>\n#endif\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include \"util/linux/ptrace_connection.h\"\n#endif\n\nnamespace crashpad {\n\nclass ProcessInfo {\n public:\n  ProcessInfo();\n\n  ProcessInfo(const ProcessInfo&) = delete;\n  ProcessInfo& operator=(const ProcessInfo&) = delete;\n\n  ~ProcessInfo();\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \\\n    DOXYGEN\n  //! \\brief Initializes this object with information about the process whose ID\n  //!     is \\a pid using a PtraceConnection \\a connection.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class. This method may only be called once.\n  //!\n  //! It is unspecified whether the information that an object of this class\n  //! returns is loaded at the time Initialize() is called or subsequently, and\n  //! whether this information is cached in the object or not.\n  //!\n  //! \\param[in] connection A connection to the remote process.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool InitializeWithPtrace(PtraceConnection* connection);\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID) || DOXYGEN\n\n#if BUILDFLAG(IS_APPLE) || DOXYGEN\n  //! \\brief Initializes this object with information about the process whose ID\n  //!     is \\a pid.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class. This method may only be called once.\n  //!\n  //! It is unspecified whether the information that an object of this class\n  //! returns is loaded at the time Initialize() is called or subsequently, and\n  //! whether this information is cached in the object or not.\n  //!\n  //! \\param[in] pid The process ID to obtain information for.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool InitializeWithPid(pid_t pid);\n\n  //! \\brief Initializes this object with information about a process based on\n  //!     its Mach task.\n  //!\n  //! This method serves as a stand-in for InitializeWithPid() and may be called\n  //! in its place with the same restrictions and considerations.\n  //!\n  //! \\param[in] task The Mach task to obtain information for.\n  //!\n  //! \\return `true` on success, `false` on failure with an message logged.\n  bool InitializeWithTask(task_t task);\n#endif\n\n  //! \\return The target task’s process ID.\n  pid_t ProcessID() const;\n\n  //! \\return The target task’s parent process ID.\n  pid_t ParentProcessID() const;\n\n  //! \\return The target process’ real user ID as would be returned to it by\n  //!     `getuid()`.\n  uid_t RealUserID() const;\n\n  //! \\return The target process’ effective user ID as would be returned to it\n  //!     by `geteuid()`.\n  uid_t EffectiveUserID() const;\n\n  //! \\return The target process’ saved set-user ID.\n  uid_t SavedUserID() const;\n\n  //! \\return the target process’ real group ID as would be returned to it by\n  //!     `getgid()`.\n  gid_t RealGroupID() const;\n\n  //! \\return the target process’ effective group ID as would be returned to it\n  //!     by `getegid()`.\n  gid_t EffectiveGroupID() const;\n\n  //! \\return The target process’ saved set-group ID.\n  gid_t SavedGroupID() const;\n\n  //! \\return the target process’ supplementary group list as would be returned\n  //!     to it by `getgroups()`.\n  std::set<gid_t> SupplementaryGroups() const;\n\n  //! \\return All groups that the target process claims membership in, including\n  //!     RealGroupID(), EffectiveGroupID(), SavedGroupID(), and\n  //!     SupplementaryGroups().\n  std::set<gid_t> AllGroups() const;\n\n  //! \\brief Determines whether the target process has changed privileges.\n  //!\n  //! A process is considered to have changed privileges if it has changed its\n  //! real, effective, or saved set-user or group IDs with the `setuid()`,\n  //! `seteuid()`, `setreuid()`, `setgid()`, `setegid()`, or `setregid()` system\n  //! calls since its most recent `execve()`, or if its privileges changed at\n  //! `execve()` as a result of executing a setuid or setgid executable.\n  bool DidChangePrivileges() const;\n\n  //! \\brief Determines the target process’ bitness.\n  //!\n  //! \\return `true` if the target task is a 64-bit process.\n  bool Is64Bit() const;\n\n  //! \\brief Determines the target process’ start time.\n  //!\n  //! \\param[out] start_time The time that the process started.\n  //!\n  //! \\return `true` on success, with \\a start_time set. Otherwise, `false` with\n  //!     a message logged.\n  bool StartTime(timeval* start_time) const;\n\n  //! \\brief Obtains the arguments used to launch a process.\n  //!\n  //! Whether it is possible to obtain this information for a process with\n  //! different privileges than the running program is system-dependent.\n  //!\n  //! \\param[out] argv The process’ arguments as passed to its `main()` function\n  //!     as the \\a argv parameter, possibly modified by the process.\n  //!\n  //! \\return `true` on success, with \\a argv populated appropriately.\n  //!     Otherwise, `false` with a message logged.\n  //!\n  //! \\note This function may spuriously return `false` when used to examine a\n  //!     process that it is calling `exec()`. If examining such a process, call\n  //!     this function in a retry loop with a small (100ns) delay to avoid an\n  //!     erroneous assumption that \\a pid is not running.\n  bool Arguments(std::vector<std::string>* argv) const;\n\n private:\n#if BUILDFLAG(IS_APPLE)\n  kinfo_proc kern_proc_info_;\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  // Some members are marked mutable so that they can be lazily initialized by\n  // const methods. These are always InitializationState-protected so that\n  // multiple successive calls will always produce the same return value and out\n  // parameters. This is necessary for intergration with the Snapshot interface.\n  // See https://crashpad.chromium.org/bug/9.\n  PtraceConnection* connection_;\n  std::set<gid_t> supplementary_groups_;\n  mutable timeval start_time_;\n  pid_t pid_;\n  pid_t ppid_;\n  uid_t uid_;\n  uid_t euid_;\n  uid_t suid_;\n  gid_t gid_;\n  gid_t egid_;\n  gid_t sgid_;\n  bool is_64_bit_;\n  mutable InitializationState start_time_initialized_;\n#endif\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_PROCESS_INFO_H_\n"
  },
  {
    "path": "util/posix/process_info_linux.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/process_info.h\"\n\n#include <stdio.h>\n\n#include \"base/check.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"util/file/delimited_file_reader.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/string_file.h\"\n#include \"util/linux/proc_stat_reader.h\"\n#include \"util/misc/lexing.h\"\n#include \"util/misc/time.h\"\n\nnamespace crashpad {\n\nProcessInfo::ProcessInfo()\n    : connection_(),\n      supplementary_groups_(),\n      start_time_(),\n      pid_(-1),\n      ppid_(-1),\n      uid_(-1),\n      euid_(-1),\n      suid_(-1),\n      gid_(-1),\n      egid_(-1),\n      sgid_(-1),\n      start_time_initialized_(),\n      initialized_() {}\n\nProcessInfo::~ProcessInfo() {}\n\nbool ProcessInfo::InitializeWithPtrace(PtraceConnection* connection) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  DCHECK(connection);\n\n  connection_ = connection;\n  pid_ = connection->GetProcessID();\n  is_64_bit_ = connection->Is64Bit();\n\n  {\n    char path[32];\n    snprintf(path, sizeof(path), \"/proc/%d/status\", pid_);\n    std::string contents;\n    if (!connection->ReadFileContents(base::FilePath(path), &contents)) {\n      return false;\n    }\n    StringFile status_file;\n    status_file.SetString(contents);\n\n    DelimitedFileReader status_file_line_reader(&status_file);\n\n    bool have_ppid = false;\n    bool have_uids = false;\n    bool have_gids = false;\n    bool have_groups = false;\n    std::string line;\n    DelimitedFileReader::Result result;\n    while ((result = status_file_line_reader.GetLine(&line)) ==\n           DelimitedFileReader::Result::kSuccess) {\n      if (line.back() != '\\n') {\n        LOG(ERROR) << \"format error: unterminated line at EOF\";\n        return false;\n      }\n\n      bool understood_line = false;\n      const char* line_c = line.c_str();\n      if (AdvancePastPrefix(&line_c, \"PPid:\\t\")) {\n        if (have_ppid) {\n          LOG(ERROR) << \"format error: multiple PPid lines\";\n          return false;\n        }\n        have_ppid = AdvancePastNumber(&line_c, &ppid_);\n        if (!have_ppid) {\n          LOG(ERROR) << \"format error: unrecognized PPid format\";\n          return false;\n        }\n        understood_line = true;\n      } else if (AdvancePastPrefix(&line_c, \"Uid:\\t\")) {\n        if (have_uids) {\n          LOG(ERROR) << \"format error: multiple Uid lines\";\n          return false;\n        }\n        uid_t fsuid;\n        have_uids = AdvancePastNumber(&line_c, &uid_) &&\n                    AdvancePastPrefix(&line_c, \"\\t\") &&\n                    AdvancePastNumber(&line_c, &euid_) &&\n                    AdvancePastPrefix(&line_c, \"\\t\") &&\n                    AdvancePastNumber(&line_c, &suid_) &&\n                    AdvancePastPrefix(&line_c, \"\\t\") &&\n                    AdvancePastNumber(&line_c, &fsuid);\n        if (!have_uids) {\n          LOG(ERROR) << \"format error: unrecognized Uid format\";\n          return false;\n        }\n        understood_line = true;\n      } else if (AdvancePastPrefix(&line_c, \"Gid:\\t\")) {\n        if (have_gids) {\n          LOG(ERROR) << \"format error: multiple Gid lines\";\n          return false;\n        }\n        gid_t fsgid;\n        have_gids = AdvancePastNumber(&line_c, &gid_) &&\n                    AdvancePastPrefix(&line_c, \"\\t\") &&\n                    AdvancePastNumber(&line_c, &egid_) &&\n                    AdvancePastPrefix(&line_c, \"\\t\") &&\n                    AdvancePastNumber(&line_c, &sgid_) &&\n                    AdvancePastPrefix(&line_c, \"\\t\") &&\n                    AdvancePastNumber(&line_c, &fsgid);\n        if (!have_gids) {\n          LOG(ERROR) << \"format error: unrecognized Gid format\";\n          return false;\n        }\n        understood_line = true;\n      } else if (AdvancePastPrefix(&line_c, \"Groups:\\t\")) {\n        if (have_groups) {\n          LOG(ERROR) << \"format error: multiple Groups lines\";\n          return false;\n        }\n        if (!AdvancePastPrefix(&line_c, \" \")) {\n          // In Linux 4.10, even an empty Groups: line has a trailing space.\n          gid_t group;\n          while (AdvancePastNumber(&line_c, &group)) {\n            supplementary_groups_.insert(group);\n            if (!AdvancePastPrefix(&line_c, \" \")) {\n              LOG(ERROR) << \"format error: unrecognized Groups format\";\n              return false;\n            }\n          }\n        }\n        have_groups = true;\n        understood_line = true;\n      }\n\n      if (understood_line && line_c != &line.back()) {\n        LOG(ERROR) << \"format error: unconsumed trailing data\";\n        return false;\n      }\n    }\n    if (result != DelimitedFileReader::Result::kEndOfFile) {\n      return false;\n    }\n    if (!have_ppid || !have_uids || !have_gids || !have_groups) {\n      LOG(ERROR) << \"format error: missing fields\";\n      return false;\n    }\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\npid_t ProcessInfo::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return pid_;\n}\n\npid_t ProcessInfo::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return ppid_;\n}\n\nuid_t ProcessInfo::RealUserID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return uid_;\n}\n\nuid_t ProcessInfo::EffectiveUserID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return euid_;\n}\n\nuid_t ProcessInfo::SavedUserID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return suid_;\n}\n\ngid_t ProcessInfo::RealGroupID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return gid_;\n}\n\ngid_t ProcessInfo::EffectiveGroupID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return egid_;\n}\n\ngid_t ProcessInfo::SavedGroupID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return sgid_;\n}\n\nstd::set<gid_t> ProcessInfo::SupplementaryGroups() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return supplementary_groups_;\n}\n\nstd::set<gid_t> ProcessInfo::AllGroups() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::set<gid_t> all_groups = SupplementaryGroups();\n  all_groups.insert(RealGroupID());\n  all_groups.insert(EffectiveGroupID());\n  all_groups.insert(SavedGroupID());\n  return all_groups;\n}\n\nbool ProcessInfo::DidChangePrivileges() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  // TODO(jperaza): Is this possible to determine?\n  return false;\n}\n\nbool ProcessInfo::Is64Bit() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return is_64_bit_;\n}\n\nbool ProcessInfo::StartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (start_time_initialized_.is_uninitialized()) {\n    start_time_initialized_.set_invalid();\n    ProcStatReader reader;\n    if (!reader.Initialize(connection_, pid_)) {\n      return false;\n    }\n    timespec boot_time_ts;\n    if (!GetBootTime(&boot_time_ts)) {\n      return false;\n    }\n    timeval boot_time;\n    TimespecToTimeval(boot_time_ts, &boot_time);\n    if (!reader.StartTime(boot_time, &start_time_)) {\n      return false;\n    }\n    start_time_initialized_.set_valid();\n  }\n\n  if (!start_time_initialized_.is_valid()) {\n    return false;\n  }\n\n  *start_time = start_time_;\n  return true;\n}\n\nbool ProcessInfo::Arguments(std::vector<std::string>* argv) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  char path[32];\n  snprintf(path, sizeof(path), \"/proc/%d/cmdline\", pid_);\n  std::string contents;\n  if (!connection_->ReadFileContents(base::FilePath(path), &contents)) {\n    return false;\n  }\n  StringFile cmdline_file;\n  cmdline_file.SetString(contents);\n\n  DelimitedFileReader cmdline_file_field_reader(&cmdline_file);\n\n  std::vector<std::string> local_argv;\n  std::string argument;\n  DelimitedFileReader::Result result;\n  while ((result = cmdline_file_field_reader.GetDelim('\\0', &argument)) ==\n         DelimitedFileReader::Result::kSuccess) {\n    if (argument.back() != '\\0') {\n      LOG(ERROR) << \"format error\";\n      return false;\n    }\n    argument.pop_back();\n    local_argv.push_back(argument);\n  }\n  if (result != DelimitedFileReader::Result::kEndOfFile) {\n    return false;\n  }\n\n  argv->swap(local_argv);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/process_info_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/process_info.h\"\n\n#include <string.h>\n\n#include <iterator>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nProcessInfo::ProcessInfo() : kern_proc_info_(), initialized_() {\n}\n\nProcessInfo::~ProcessInfo() {\n}\n\nbool ProcessInfo::InitializeWithPid(pid_t pid) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};\n  size_t len = sizeof(kern_proc_info_);\n  if (sysctl(mib, std::size(mib), &kern_proc_info_, &len, nullptr, 0) != 0) {\n    PLOG(ERROR) << \"sysctl for pid \" << pid;\n    return false;\n  }\n\n  // This sysctl does not return an error if the pid was not found. 10.9.5\n  // xnu-2422.115.4/bsd/kern/kern_sysctl.c sysctl_prochandle() calls\n  // xnu-2422.115.4/bsd/kern/kern_proc.c proc_iterate(), which provides no\n  // indication of whether anything was done. To catch this, check that the PID\n  // has changed from the 0 value it was given when initialized by the\n  // constructor.\n  if (kern_proc_info_.kp_proc.p_pid == 0) {\n    LOG(WARNING) << \"pid \" << pid << \" not found\";\n    return false;\n  }\n\n  DCHECK_EQ(kern_proc_info_.kp_proc.p_pid, pid);\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessInfo::InitializeWithTask(task_t task) {\n  pid_t pid;\n  kern_return_t kr = pid_for_task(task, &pid);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(ERROR, kr) << \"pid_for_task\";\n    return false;\n  }\n\n  return InitializeWithPid(pid);\n}\n\npid_t ProcessInfo::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_proc.p_pid;\n}\n\npid_t ProcessInfo::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_eproc.e_ppid;\n}\n\nuid_t ProcessInfo::RealUserID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_eproc.e_pcred.p_ruid;\n}\n\nuid_t ProcessInfo::EffectiveUserID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_eproc.e_ucred.cr_uid;\n}\n\nuid_t ProcessInfo::SavedUserID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_eproc.e_pcred.p_svuid;\n}\n\ngid_t ProcessInfo::RealGroupID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_eproc.e_pcred.p_rgid;\n}\n\ngid_t ProcessInfo::EffectiveGroupID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_eproc.e_ucred.cr_gid;\n}\n\ngid_t ProcessInfo::SavedGroupID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_eproc.e_pcred.p_svgid;\n}\n\nstd::set<gid_t> ProcessInfo::SupplementaryGroups() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  const short ngroups = kern_proc_info_.kp_eproc.e_ucred.cr_ngroups;\n  DCHECK_GE(ngroups, 0);\n  DCHECK_LE(static_cast<size_t>(ngroups),\n            std::size(kern_proc_info_.kp_eproc.e_ucred.cr_groups));\n\n  const gid_t* groups = kern_proc_info_.kp_eproc.e_ucred.cr_groups;\n  return std::set<gid_t>(&groups[0], &groups[ngroups]);\n}\n\nstd::set<gid_t> ProcessInfo::AllGroups() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  std::set<gid_t> all_groups = SupplementaryGroups();\n  all_groups.insert(RealGroupID());\n  all_groups.insert(EffectiveGroupID());\n  all_groups.insert(SavedGroupID());\n  return all_groups;\n}\n\nbool ProcessInfo::DidChangePrivileges() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_proc.p_flag & P_SUGID;\n}\n\nbool ProcessInfo::Is64Bit() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return kern_proc_info_.kp_proc.p_flag & P_LP64;\n}\n\nbool ProcessInfo::StartTime(timeval* start_time) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *start_time = kern_proc_info_.kp_proc.p_starttime;\n  return true;\n}\n\nbool ProcessInfo::Arguments(std::vector<std::string>* argv) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  // The format of KERN_PROCARGS2 is explained in 10.9.2 adv_cmds-153/ps/print.c\n  // getproclline(). It is an int (argc) followed by the executable’s string\n  // area. The string area consists of NUL-terminated strings, beginning with\n  // the executable path, and then starting on an aligned boundary, all of the\n  // elements of argv, envp, and applev.\n\n  // It is possible for a process to exec() in between the two sysctl() calls\n  // below. If that happens, and the string area of the new program is larger\n  // than that of the old one, args_size_estimate will be too small. To detect\n  // this situation, the second sysctl() attempts to fetch args_size_estimate +\n  // 1 bytes, expecting to only receive args_size_estimate. If it gets the extra\n  // byte, it indicates that the string area has grown, and the sysctl() pair\n  // will be retried a limited number of times.\n\n  size_t args_size_estimate;\n  size_t args_size;\n  std::string args;\n  int tries = 3;\n  const pid_t pid = ProcessID();\n  do {\n    int mib[] = {CTL_KERN, KERN_PROCARGS2, pid};\n    int rv =\n        sysctl(mib, std::size(mib), nullptr, &args_size_estimate, nullptr, 0);\n    if (rv != 0) {\n      PLOG(ERROR) << \"sysctl (size) for pid \" << pid;\n      return false;\n    }\n\n    // TODO(https://crashpad.chromium.org/bug/355): This was increased from + 1\n    // to + 32 to work around a new bug in macOS 11.0db6 20A5364e that has\n    // broken {CTL_KERN, KERN_PROCARGS2} such that it will not work properly\n    // unless provided with a buffer at least 17 bytes larger than indicated in\n    // args_size_estimate. If this bug is fixed prior to the 11.0 release,\n    // remove the workaround and go back to + 1. (A positive offset is needed\n    // for the reasons described above.)\n    args_size = args_size_estimate + 32;\n    args.resize(args_size);\n    rv = sysctl(mib, std::size(mib), &args[0], &args_size, nullptr, 0);\n    if (rv != 0) {\n      PLOG(ERROR) << \"sysctl (data) for pid \" << pid;\n      return false;\n    }\n  } while (args_size == args_size_estimate + 1 && tries--);\n\n  if (args_size == args_size_estimate + 1) {\n    LOG(ERROR) << \"unexpected args_size\";\n    return false;\n  }\n\n  // KERN_PROCARGS2 needs to at least contain argc.\n  if (args_size < sizeof(int)) {\n    LOG(ERROR) << \"tiny args_size\";\n    return false;\n  }\n  args.resize(args_size);\n\n  // Get argc.\n  int argc;\n  memcpy(&argc, &args[0], sizeof(argc));\n\n  // Find the end of the executable path.\n  size_t start_pos = sizeof(argc);\n  size_t nul_pos = args.find('\\0', start_pos);\n  if (nul_pos == std::string::npos) {\n    LOG(ERROR) << \"unterminated executable path\";\n    return false;\n  }\n\n  // Find the beginning of the string area.\n  start_pos = args.find_first_not_of('\\0', nul_pos);\n  if (start_pos == std::string::npos) {\n    LOG(ERROR) << \"no string area\";\n    return false;\n  }\n\n  std::vector<std::string> local_argv;\n  while (argc-- && nul_pos != std::string::npos) {\n    nul_pos = args.find('\\0', start_pos);\n    local_argv.push_back(args.substr(start_pos, nul_pos - start_pos));\n    start_pos = nul_pos + 1;\n  }\n\n  if (argc >= 0) {\n    // Not every argument was recovered.\n    LOG(ERROR) << \"did not recover all arguments\";\n    return false;\n  }\n\n  argv->swap(local_argv);\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/process_info_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/process_info.h\"\n\n#include <sys/utsname.h>\n#include <time.h>\n\n#include <algorithm>\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"base/strings/string_number_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/main_arguments.h\"\n#include \"test/multiprocess.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/string/split_string.h\"\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include \"util/linux/direct_ptrace_connection.h\"\n#include \"test/linux/fake_ptrace_connection.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nvoid TestProcessSelfOrClone(const ProcessInfo& process_info) {\n  // There’s no system call to obtain the saved set-user ID or saved set-group\n  // ID in an easy way. Normally, they are the same as the effective user ID and\n  // effective group ID, so just check against those.\n  EXPECT_EQ(process_info.RealUserID(), getuid());\n  const uid_t euid = geteuid();\n  EXPECT_EQ(process_info.EffectiveUserID(), euid);\n  EXPECT_EQ(process_info.SavedUserID(), euid);\n\n  const gid_t gid = getgid();\n  EXPECT_EQ(process_info.RealGroupID(), gid);\n  const gid_t egid = getegid();\n  EXPECT_EQ(process_info.EffectiveGroupID(), egid);\n  EXPECT_EQ(process_info.SavedGroupID(), egid);\n\n  // Test SupplementaryGroups().\n  int group_count = getgroups(0, nullptr);\n  ASSERT_GE(group_count, 0) << ErrnoMessage(\"getgroups\");\n\n  std::vector<gid_t> group_vector(group_count);\n  if (group_count > 0) {\n    group_count = getgroups(group_vector.size(), &group_vector[0]);\n    ASSERT_GE(group_count, 0) << ErrnoMessage(\"getgroups\");\n    ASSERT_EQ(implicit_cast<size_t>(group_count), group_vector.size());\n  }\n\n  std::set<gid_t> group_set(group_vector.begin(), group_vector.end());\n  EXPECT_EQ(process_info.SupplementaryGroups(), group_set);\n\n  // Test AllGroups(), which is SupplementaryGroups() plus the real, effective,\n  // and saved set-group IDs. The effective and saved set-group IDs are expected\n  // to be identical (see above).\n  group_set.insert(gid);\n  group_set.insert(egid);\n\n  EXPECT_EQ(process_info.AllGroups(), group_set);\n\n  // The test executable isn’t expected to change privileges.\n  EXPECT_FALSE(process_info.DidChangePrivileges());\n\n#if defined(ARCH_CPU_64_BITS)\n  EXPECT_TRUE(process_info.Is64Bit());\n#else\n  EXPECT_FALSE(process_info.Is64Bit());\n#endif\n\n  // Test StartTime(). This program must have started at some time in the past.\n  timeval start_time;\n  ASSERT_TRUE(process_info.StartTime(&start_time));\n  EXPECT_FALSE(start_time.tv_sec == 0 && start_time.tv_usec == 0);\n  time_t now;\n  time(&now);\n  EXPECT_LE(start_time.tv_sec, now);\n\n  const std::vector<std::string>& expect_argv = GetMainArguments();\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  // Prior to Linux 4.2, the kernel only allowed reading a single page from\n  // /proc/<pid>/cmdline, causing any further arguments to be truncated. Disable\n  // testing arguments in this case.\n  // TODO(jperaza): The main arguments are stored on the main thread's stack\n  // (and so should be included in dumps automatically), and\n  // ProcessInfo::Arguments() might be updated to read the arguments directly,\n  // rather than via procfs on older kernels.\n  utsname uts;\n  ASSERT_EQ(uname(&uts), 0) << ErrnoMessage(\"uname\");\n  std::vector<std::string> parts = SplitString(uts.release, '.');\n  ASSERT_GE(parts.size(), 2u);\n\n  int major, minor;\n  ASSERT_TRUE(base::StringToInt(parts[0], &major));\n  ASSERT_TRUE(base::StringToInt(parts[1], &minor));\n\n  size_t argv_size = 0;\n  for (const auto& arg : expect_argv) {\n    argv_size += arg.size() + 1;\n  }\n\n  if ((major < 4 || (major == 4 && minor < 2)) &&\n      argv_size > static_cast<size_t>(getpagesize())) {\n    return;\n  }\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n  std::vector<std::string> argv;\n  ASSERT_TRUE(process_info.Arguments(&argv));\n\n  // expect_argv always contains the initial view of the arguments at the time\n  // the program was invoked. argv may contain this view, or it may contain the\n  // current view of arguments after Google Test argv processing. argv may be a\n  // subset of expect_argv.\n  //\n  // Google Test argv processing always leaves argv[0] intact, so this can be\n  // checked directly.\n  ASSERT_FALSE(expect_argv.empty());\n  ASSERT_FALSE(argv.empty());\n  EXPECT_EQ(argv[0], expect_argv[0]);\n\n  EXPECT_LE(argv.size(), expect_argv.size());\n\n  // Everything else in argv should have a match in expect_argv too, but things\n  // may have moved around.\n  for (size_t arg_index = 1; arg_index < argv.size(); ++arg_index) {\n    const std::string& arg = argv[arg_index];\n    SCOPED_TRACE(\n        base::StringPrintf(\"arg_index %zu, arg %s\", arg_index, arg.c_str()));\n    EXPECT_NE(expect_argv.end(), std::find(argv.begin(), argv.end(), arg));\n  }\n}\n\nvoid TestSelfProcess(const ProcessInfo& process_info) {\n  EXPECT_EQ(process_info.ProcessID(), getpid());\n  EXPECT_EQ(process_info.ParentProcessID(), getppid());\n\n  TestProcessSelfOrClone(process_info);\n}\n\nTEST(ProcessInfo, Self) {\n  ProcessInfo process_info;\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(getpid()));\n  ASSERT_TRUE(process_info.InitializeWithPtrace(&connection));\n#else\n  ASSERT_TRUE(process_info.InitializeWithPid(getpid()));\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n  TestSelfProcess(process_info);\n}\n\n#if BUILDFLAG(IS_APPLE)\nTEST(ProcessInfo, SelfTask) {\n  ProcessInfo process_info;\n  ASSERT_TRUE(process_info.InitializeWithTask(mach_task_self()));\n  TestSelfProcess(process_info);\n}\n#endif\n\nTEST(ProcessInfo, Pid1) {\n  // PID 1 is expected to be init or the system’s equivalent. This tests reading\n  // information about another process.\n  ProcessInfo process_info;\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(1));\n  ASSERT_TRUE(process_info.InitializeWithPtrace(&connection));\n#else\n  ASSERT_TRUE(process_info.InitializeWithPid(1));\n#endif\n\n  EXPECT_EQ(process_info.ProcessID(), implicit_cast<pid_t>(1));\n  EXPECT_EQ(process_info.ParentProcessID(), implicit_cast<pid_t>(0));\n  EXPECT_EQ(process_info.RealUserID(), implicit_cast<uid_t>(0));\n  EXPECT_EQ(process_info.EffectiveUserID(), implicit_cast<uid_t>(0));\n  EXPECT_EQ(process_info.SavedUserID(), implicit_cast<uid_t>(0));\n  EXPECT_EQ(process_info.RealGroupID(), implicit_cast<gid_t>(0));\n  EXPECT_EQ(process_info.EffectiveGroupID(), implicit_cast<gid_t>(0));\n  EXPECT_EQ(process_info.SavedGroupID(), implicit_cast<gid_t>(0));\n  EXPECT_FALSE(process_info.AllGroups().empty());\n}\n\nclass ProcessInfoForkedTest : public Multiprocess {\n public:\n  ProcessInfoForkedTest() : Multiprocess() {}\n\n  ProcessInfoForkedTest(const ProcessInfoForkedTest&) = delete;\n  ProcessInfoForkedTest& operator=(const ProcessInfoForkedTest&) = delete;\n\n  ~ProcessInfoForkedTest() {}\n\n  // Multiprocess:\n  void MultiprocessParent() override {\n    const pid_t pid = ChildPID();\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(pid));\n\n    ProcessInfo process_info;\n    ASSERT_TRUE(process_info.InitializeWithPtrace(&connection));\n#else\n    ProcessInfo process_info;\n    ASSERT_TRUE(process_info.InitializeWithPid(pid));\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ||\n        // BUILDFLAG(IS_ANDROID)\n\n    EXPECT_EQ(process_info.ProcessID(), pid);\n    EXPECT_EQ(process_info.ParentProcessID(), getpid());\n\n    TestProcessSelfOrClone(process_info);\n  }\n\n  void MultiprocessChild() override {\n    // Hang around until the parent is done.\n    CheckedReadFileAtEOF(ReadPipeHandle());\n  }\n};\n\nTEST(ProcessInfo, Forked) {\n  ProcessInfoForkedTest test;\n  test.Run();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/scoped_dir.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/scoped_dir.h\"\n\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nvoid ScopedDIRCloseTraits::Free(DIR* dir) {\n  if (dir && IGNORE_EINTR(closedir(dir)) != 0) {\n    PLOG(ERROR) << \"closedir\";\n  }\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/scoped_dir.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_SCOPED_DIR_H_\n#define CRASHPAD_UTIL_POSIX_SCOPED_DIR_H_\n\n#include <dirent.h>\n\n#include \"base/scoped_generic.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nstruct ScopedDIRCloseTraits {\n  static DIR* InvalidValue() { return nullptr; }\n  static void Free(DIR* dir);\n};\n\n}  // namespace internal\n\n//! \\brief Maintains a directory opened by `opendir`.\n//!\n//! On destruction, the directory will be closed by calling `closedir`.\nusing ScopedDIR = base::ScopedGeneric<DIR*, internal::ScopedDIRCloseTraits>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_SCOPED_DIR_H_\n"
  },
  {
    "path": "util/posix/scoped_mmap.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/scoped_mmap.h\"\n\n#include <unistd.h>\n\n#include <algorithm>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/memory/page_size.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/numerics/safe_math.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_LINUX)\n#include \"third_party/lss/lss.h\"\n#endif\n\nnamespace {\n\n#if BUILDFLAG(IS_LINUX)\nvoid* CallMmap(void* addr,\n               size_t len,\n               int prot,\n               int flags,\n               int fd,\n               off_t offset) {\n  return sys_mmap(addr, len, prot, flags, fd, offset);\n}\n\nint CallMunmap(void* addr, size_t len) {\n  return sys_munmap(addr, len);\n}\n\nint CallMprotect(void* addr, size_t len, int prot) {\n  return sys_mprotect(addr, len, prot);\n}\n#else\nvoid* CallMmap(void* addr,\n               size_t len,\n               int prot,\n               int flags,\n               int fd,\n               off_t offset) {\n  return mmap(addr, len, prot, flags, fd, offset);\n}\n\nint CallMunmap(void* addr, size_t len) {\n  return munmap(addr, len);\n}\n\nint CallMprotect(void* addr, size_t len, int prot) {\n  return mprotect(addr, len, prot);\n}\n#endif\n\nbool LoggingMunmap(uintptr_t addr, size_t len, bool can_log) {\n  if (CallMunmap(reinterpret_cast<void*>(addr), len) != 0) {\n    PLOG_IF(ERROR, can_log) << \"munmap\";\n    return false;\n  }\n\n  return true;\n}\n\nsize_t RoundPage(size_t size) {\n  const size_t kPageMask = base::checked_cast<size_t>(base::GetPageSize()) - 1;\n  return (size + kPageMask) & ~kPageMask;\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nScopedMmap::ScopedMmap(bool can_log) : can_log_(can_log) {}\n\nScopedMmap::~ScopedMmap() {\n  if (is_valid()) {\n    LoggingMunmap(\n        reinterpret_cast<uintptr_t>(addr_), RoundPage(len_), can_log_);\n  }\n}\n\nbool ScopedMmap::Reset() {\n  return ResetAddrLen(MAP_FAILED, 0);\n}\n\nbool ScopedMmap::ResetAddrLen(void* addr, size_t len) {\n  const uintptr_t new_addr = reinterpret_cast<uintptr_t>(addr);\n  const size_t new_len_round = RoundPage(len);\n\n  if (addr == MAP_FAILED) {\n    DCHECK_EQ(len, 0u);\n  } else {\n    DCHECK_NE(len, 0u);\n    DCHECK_EQ(new_addr % base::GetPageSize(), 0u);\n    DCHECK((base::CheckedNumeric<uintptr_t>(new_addr) + (new_len_round - 1))\n               .IsValid());\n  }\n\n  bool result = true;\n\n  if (is_valid()) {\n    const uintptr_t old_addr = reinterpret_cast<uintptr_t>(addr_);\n    const size_t old_len_round = RoundPage(len_);\n    if (old_addr < new_addr) {\n      result &= LoggingMunmap(\n          old_addr, std::min(old_len_round, new_addr - old_addr), can_log_);\n    }\n    if (old_addr + old_len_round > new_addr + new_len_round) {\n      uintptr_t unmap_start = std::max(old_addr, new_addr + new_len_round);\n      result &= LoggingMunmap(\n          unmap_start, old_addr + old_len_round - unmap_start, can_log_);\n    }\n  }\n\n  addr_ = addr;\n  len_ = len;\n\n  return result;\n}\n\nbool ScopedMmap::ResetMmap(void* addr,\n                           size_t len,\n                           int prot,\n                           int flags,\n                           int fd,\n                           off_t offset) {\n  // Reset() first, so that a new anonymous mapping can use the address space\n  // occupied by the old mapping if appropriate. The new mapping will be\n  // attempted even if there was something wrong with the old mapping, so don’t\n  // consider the return value from Reset().\n  Reset();\n\n  void* new_addr = CallMmap(addr, len, prot, flags, fd, offset);\n  if (new_addr == MAP_FAILED) {\n    PLOG_IF(ERROR, can_log_) << \"mmap\";\n    return false;\n  }\n\n  // The new mapping is effective even if there was something wrong with the old\n  // mapping, so don’t consider the return value from ResetAddrLen().\n  ResetAddrLen(new_addr, len);\n  return true;\n}\n\nbool ScopedMmap::Mprotect(int prot) {\n  if (CallMprotect(addr_, RoundPage(len_), prot) < 0) {\n    PLOG_IF(ERROR, can_log_) << \"mprotect\";\n    return false;\n  }\n\n  return true;\n}\n\nvoid* ScopedMmap::release() {\n  void* retval = addr_;\n  addr_ = MAP_FAILED;\n  len_ = 0;\n  return retval;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/scoped_mmap.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_SCOPED_MMAP_H_\n#define CRASHPAD_UTIL_POSIX_SCOPED_MMAP_H_\n\n\n#include <sys/mman.h>\n#include <sys/types.h>\n\n#include \"util/misc/from_pointer_cast.h\"\n\nnamespace crashpad {\n\n//! \\brief Maintains a memory-mapped region created by `mmap()`.\n//!\n//! On destruction, any memory-mapped region managed by an object of this class\n//! will be released by calling `munmap()`.\nclass ScopedMmap {\n public:\n  //! \\brief Constructs this object.\n  //!\n  //! \\param can_log `true` if methods of this class may log messages.\n  explicit ScopedMmap(bool can_log = true);\n\n  ScopedMmap(const ScopedMmap&) = delete;\n  ScopedMmap& operator=(const ScopedMmap&) = delete;\n\n  ~ScopedMmap();\n\n  //! \\brief Releases the memory-mapped region by calling `munmap()`.\n  //!\n  //! \\return `true` on success. `false` on failure, with a message logged.\n  bool Reset();\n\n  //! \\brief Releases any existing memory-mapped region and sets the object to\n  //!     maintain an already-established mapping.\n  //!\n  //! If \\a addr and \\a len indicate a region that overlaps with the existing\n  //! memory-mapped region, only the portion of the existing memory-mapped\n  //! region that does not overlap the new region, if any, will be released.\n  //!\n  //! \\param[in] addr The base address of the existing memory-mapped region to\n  //!     maintain.\n  //! \\param[in] len The size of the existing memory-mapped region to maintain.\n  //!\n  //! \\return `true` on success. `false` on failure, with a message logged.\n  bool ResetAddrLen(void* addr, size_t len);\n\n  //! \\brief Releases any existing memory-mapped region and establishes a new\n  //!     one by calling `mmap()`.\n  //!\n  //! The parameters to this method are passed directly to `mmap()`.\n  //!\n  //! \\return `true` on success. `false` on failure, with a message logged. A\n  //!     message will also be logged on failure to release any existing\n  //!     memory-mapped region, but this will not preclude `mmap()` from being\n  //!     called or a new mapping from being established, and if such a call to\n  //!     `mmap()` is successful, this method will return `true`.\n  bool ResetMmap(void* addr,\n                 size_t len,\n                 int prot,\n                 int flags,\n                 int fd,\n                 off_t offset);\n\n  //! \\brief Sets the protection of the memory-mapped region by calling\n  //!     `mprotect()`.\n  //!\n  //! \\a prot is passed directly to `mprotect()`.\n  //!\n  //! \\return `true` on success. `false` on failure, with a message logged.\n  bool Mprotect(int prot);\n\n  //! \\brief Returns the base address of the memory-mapped region and releases\n  //!     ownership.\n  void* release();\n\n  //! \\return Whether this object is managing a valid memory-mapped region.\n  bool is_valid() const { return addr_ != MAP_FAILED; }\n\n  //! \\brief Returns the base address of the memory-mapped region.\n  void* addr() const { return addr_; }\n\n  //! \\brief Returns the base address of the memory-mapped region, casted to\n  //!     a type of the caller’s choosing.\n  template <typename T>\n  T addr_as() const {\n    return FromPointerCast<T>(addr_);\n  }\n\n  //! \\brief Returns the size of the memory-mapped region.\n  //!\n  //! This is the value originally passed to ResetAddrLen() or ResetMmap(), or\n  //! after Reset(), `0`. It may not be a round number of pages. Providing the\n  //! passed-in value is intended to ease tracking the intended lengths of\n  //! memory-mapped regions backed by files whose sizes are not whole pages.\n  size_t len() const { return len_; }\n\n private:\n  void* addr_ = MAP_FAILED;\n  size_t len_ = 0;\n  bool can_log_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_SCOPED_MMAP_H_\n"
  },
  {
    "path": "util/posix/scoped_mmap_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/scoped_mmap.h\"\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <iterator>\n\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/rand_util.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nbool ScopedMmapResetMmap(ScopedMmap* mapping, size_t len) {\n  return mapping->ResetMmap(\n      nullptr, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);\n}\n\nvoid* BareMmap(size_t len) {\n  return mmap(\n      nullptr, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);\n}\n\n// A weird class. This is used to test that memory-mapped regions are freed\n// as expected by calling munmap(). This is difficult to test well because once\n// a region has been unmapped, the address space it formerly occupied becomes\n// eligible for reuse.\n//\n// The strategy taken here is that a random 64-bit cookie value is written into\n// a mapped region by SetUp(). While the mapping is active, Check() should not\n// crash, or for a Google Test expectation, Expected() and Observed() should not\n// crash and should be equal. After the region is unmapped, Check() should\n// crash, either because the region has been unmapped and the address not\n// reused, the address has been reused but is protected against reading\n// (unlikely), or because the address has been reused but the cookie value is no\n// longer present there.\nclass TestCookie {\n public:\n  // A weird constructor for a weird class. The member variable initialization\n  // assures that Check() won’t crash if called on an object that hasn’t had\n  // SetUp() called on it.\n  explicit TestCookie() : address_(&cookie_), cookie_(0) {}\n\n  TestCookie(const TestCookie&) = delete;\n  TestCookie& operator=(const TestCookie&) = delete;\n\n  ~TestCookie() {}\n\n  void SetUp(uint64_t* address) {\n    address_ = address, cookie_ = base::RandUint64();\n    *address_ = cookie_;\n  }\n\n  uint64_t Expected() const { return cookie_; }\n  uint64_t Observed() const { return *address_; }\n\n  void Check() const {\n    if (Observed() != Expected()) {\n      __builtin_trap();\n    }\n  }\n\n private:\n  uint64_t* address_;\n  uint64_t cookie_;\n};\n\nTEST(ScopedMmap, Mmap) {\n  TestCookie cookie;\n\n  ScopedMmap mapping;\n  EXPECT_FALSE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), 0u);\n\n  ASSERT_TRUE(mapping.Reset());\n  EXPECT_FALSE(mapping.is_valid());\n\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), kPageSize);\n\n  cookie.SetUp(mapping.addr_as<uint64_t*>());\n  EXPECT_EQ(cookie.Observed(), cookie.Expected());\n\n  ASSERT_TRUE(mapping.Reset());\n  EXPECT_FALSE(mapping.is_valid());\n}\n\nTEST(ScopedMmapDeathTest, Destructor) {\n  TestCookie cookie;\n  {\n    ScopedMmap mapping;\n\n    const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n    ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kPageSize));\n    EXPECT_TRUE(mapping.is_valid());\n    EXPECT_NE(mapping.addr(), MAP_FAILED);\n    EXPECT_EQ(mapping.len(), kPageSize);\n\n    cookie.SetUp(mapping.addr_as<uint64_t*>());\n  }\n\n  EXPECT_DEATH_CRASH(cookie.Check(), \"\");\n}\n\nTEST(ScopedMmapDeathTest, Reset) {\n  ScopedMmap mapping;\n\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), kPageSize);\n\n  TestCookie cookie;\n  cookie.SetUp(mapping.addr_as<uint64_t*>());\n\n  ASSERT_TRUE(mapping.Reset());\n\n  EXPECT_DEATH_CRASH(cookie.Check(), \"\");\n}\n\nTEST(ScopedMmapDeathTest, ResetAddrLen_Shrink) {\n  ScopedMmap mapping;\n\n  // Start with three pages mapped.\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, 3 * kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), 3 * kPageSize);\n\n  TestCookie cookies[3];\n  for (size_t index = 0; index < std::size(cookies); ++index) {\n    cookies[index].SetUp(reinterpret_cast<uint64_t*>(\n        mapping.addr_as<uintptr_t>() + index * kPageSize));\n  }\n\n  // Reset to the second page. The first and third pages should be unmapped.\n  void* const new_addr =\n      reinterpret_cast<void*>(mapping.addr_as<uintptr_t>() + kPageSize);\n  ASSERT_TRUE(mapping.ResetAddrLen(new_addr, kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), new_addr);\n  EXPECT_EQ(mapping.len(), kPageSize);\n\n  EXPECT_EQ(cookies[1].Observed(), cookies[1].Expected());\n\n  EXPECT_DEATH_CRASH(cookies[0].Check(), \"\");\n  EXPECT_DEATH_CRASH(cookies[2].Check(), \"\");\n}\n\nTEST(ScopedMmap, ResetAddrLen_Grow) {\n  // Start with three pages mapped, but ScopedMmap only aware of the the second\n  // page.\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  void* pages = BareMmap(3 * kPageSize);\n  ASSERT_NE(pages, MAP_FAILED);\n\n  ScopedMmap mapping;\n  void* const old_addr =\n      reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(pages) + kPageSize);\n  ASSERT_TRUE(mapping.ResetAddrLen(old_addr, kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), old_addr);\n  EXPECT_EQ(mapping.len(), kPageSize);\n\n  TestCookie cookies[3];\n  for (size_t index = 0; index < std::size(cookies); ++index) {\n    cookies[index].SetUp(reinterpret_cast<uint64_t*>(\n        reinterpret_cast<uintptr_t>(pages) + index * kPageSize));\n  }\n\n  // Reset to all three pages. Nothing should be unmapped until destruction.\n  ASSERT_TRUE(mapping.ResetAddrLen(pages, 3 * kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), pages);\n  EXPECT_EQ(mapping.len(), 3 * kPageSize);\n\n  for (size_t index = 0; index < std::size(cookies); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n    EXPECT_EQ(cookies[index].Observed(), cookies[index].Expected());\n  }\n}\n\nTEST(ScopedMmapDeathTest, ResetAddrLen_MoveDownAndGrow) {\n  // Start with three pages mapped, but ScopedMmap only aware of the third page.\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  void* pages = BareMmap(3 * kPageSize);\n  ASSERT_NE(pages, MAP_FAILED);\n\n  ScopedMmap mapping;\n  void* const old_addr = reinterpret_cast<void*>(\n      reinterpret_cast<uintptr_t>(pages) + 2 * kPageSize);\n  ASSERT_TRUE(mapping.ResetAddrLen(old_addr, kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), old_addr);\n  EXPECT_EQ(mapping.len(), kPageSize);\n\n  TestCookie cookies[3];\n  for (size_t index = 0; index < std::size(cookies); ++index) {\n    cookies[index].SetUp(reinterpret_cast<uint64_t*>(\n        reinterpret_cast<uintptr_t>(pages) + index * kPageSize));\n  }\n\n  // Reset to the first two pages. The third page should be unmapped.\n  ASSERT_TRUE(mapping.ResetAddrLen(pages, 2 * kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), pages);\n  EXPECT_EQ(mapping.len(), 2 * kPageSize);\n\n  EXPECT_EQ(cookies[0].Observed(), cookies[0].Expected());\n  EXPECT_EQ(cookies[1].Observed(), cookies[1].Expected());\n\n  EXPECT_DEATH_CRASH(cookies[2].Check(), \"\");\n}\n\nTEST(ScopedMmapDeathTest, ResetAddrLen_MoveUpAndShrink) {\n  // Start with three pages mapped, but ScopedMmap only aware of the first two\n  // pages.\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  void* pages = BareMmap(3 * kPageSize);\n  ASSERT_NE(pages, MAP_FAILED);\n\n  ScopedMmap mapping;\n  ASSERT_TRUE(mapping.ResetAddrLen(pages, 2 * kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), pages);\n  EXPECT_EQ(mapping.len(), 2 * kPageSize);\n\n  TestCookie cookies[3];\n  for (size_t index = 0; index < std::size(cookies); ++index) {\n    cookies[index].SetUp(reinterpret_cast<uint64_t*>(\n        reinterpret_cast<uintptr_t>(pages) + index * kPageSize));\n  }\n\n  // Reset to the third page. The first two pages should be unmapped.\n  void* const new_addr =\n      reinterpret_cast<void*>(mapping.addr_as<uintptr_t>() + 2 * kPageSize);\n  ASSERT_TRUE(mapping.ResetAddrLen(new_addr, kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), new_addr);\n  EXPECT_EQ(mapping.len(), kPageSize);\n\n  EXPECT_EQ(cookies[2].Observed(), cookies[2].Expected());\n\n  EXPECT_DEATH_CRASH(cookies[0].Check(), \"\");\n  EXPECT_DEATH_CRASH(cookies[1].Check(), \"\");\n}\n\nTEST(ScopedMmapDeathTest, ResetMmap) {\n  ScopedMmap mapping;\n\n  // Calling ScopedMmap::ResetMmap() frees the existing mapping before\n  // establishing the new one, so the new one may wind up at the same address as\n  // the old. In fact, this is likely. Create a two-page mapping and replace it\n  // with a single-page mapping, so that the test can assure that the second\n  // page isn’t mapped after establishing the second mapping.\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, 2 * kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), 2 * kPageSize);\n\n  TestCookie cookie;\n  cookie.SetUp(\n      reinterpret_cast<uint64_t*>(mapping.addr_as<char*>() + kPageSize));\n\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), kPageSize);\n\n  EXPECT_DEATH_CRASH(cookie.Check(), \"\");\n}\n\nTEST(ScopedMmapDeathTest, NotIntegralNumberOfPages) {\n  ScopedMmap mapping;\n  EXPECT_FALSE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), 0u);\n\n  ASSERT_TRUE(mapping.Reset());\n  EXPECT_FALSE(mapping.is_valid());\n\n  // Establishing a half-page mapping actually establishes a single page.\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  const size_t kHalfPageSize = kPageSize / 2;\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kHalfPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), kHalfPageSize);\n\n  TestCookie cookie;\n  cookie.SetUp(mapping.addr_as<uint64_t*>());\n\n  // Shrinking a one-page mapping to a half page is a no-op.\n  void* orig_addr = mapping.addr();\n  ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kHalfPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), orig_addr);\n  EXPECT_EQ(mapping.len(), kHalfPageSize);\n\n  EXPECT_EQ(cookie.Observed(), cookie.Expected());\n\n  // Same thing shrinking it to a single byte, or one byte less than a whole\n  // page.\n  ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, 1));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), orig_addr);\n  EXPECT_EQ(mapping.len(), 1u);\n\n  EXPECT_EQ(cookie.Observed(), cookie.Expected());\n\n  ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kPageSize - 1));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), orig_addr);\n  EXPECT_EQ(mapping.len(), kPageSize - 1);\n\n  EXPECT_EQ(cookie.Observed(), cookie.Expected());\n\n  // Shrinking a two-page mapping to a half page frees the second page but\n  // leaves the first alone.\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, 2 * kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), 2 * kPageSize);\n\n  TestCookie two_cookies[2];\n  for (size_t index = 0; index < std::size(two_cookies); ++index) {\n    two_cookies[index].SetUp(reinterpret_cast<uint64_t*>(\n        mapping.addr_as<uintptr_t>() + index * kPageSize));\n  }\n\n  orig_addr = mapping.addr();\n  ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kHalfPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), orig_addr);\n  EXPECT_EQ(mapping.len(), kHalfPageSize);\n\n  EXPECT_EQ(two_cookies[0].Observed(), two_cookies[0].Expected());\n  EXPECT_DEATH_CRASH(two_cookies[1].Check(), \"\");\n\n  // Shrinking a two-page mapping to a page and a half is a no-op.\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, 2 * kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), 2 * kPageSize);\n\n  for (size_t index = 0; index < std::size(two_cookies); ++index) {\n    two_cookies[index].SetUp(reinterpret_cast<uint64_t*>(\n        mapping.addr_as<uintptr_t>() + index * kPageSize));\n  }\n\n  orig_addr = mapping.addr();\n  ASSERT_TRUE(mapping.ResetAddrLen(orig_addr, kPageSize + kHalfPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_EQ(mapping.addr(), orig_addr);\n  EXPECT_EQ(mapping.len(), kPageSize + kHalfPageSize);\n\n  EXPECT_EQ(two_cookies[0].Observed(), two_cookies[0].Expected());\n  EXPECT_EQ(two_cookies[1].Observed(), two_cookies[1].Expected());\n}\n\nTEST(ScopedMmapDeathTest, Mprotect) {\n  ScopedMmap mapping;\n\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kPageSize));\n  EXPECT_TRUE(mapping.is_valid());\n  EXPECT_NE(mapping.addr(), MAP_FAILED);\n  EXPECT_EQ(mapping.len(), kPageSize);\n\n  char* addr = mapping.addr_as<char*>();\n  *addr = 1;\n\n  ASSERT_TRUE(mapping.Mprotect(PROT_READ));\n\n  EXPECT_DEATH_CRASH(*addr = 0, \"\");\n\n  ASSERT_TRUE(mapping.Mprotect(PROT_READ | PROT_WRITE));\n  EXPECT_EQ(*addr, 1);\n  *addr = 2;\n}\n\nTEST(ScopedMmapTest, Release) {\n  ScopedMmap mapping;\n\n  const size_t kPageSize = base::checked_cast<size_t>(getpagesize());\n  ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kPageSize));\n  ASSERT_TRUE(mapping.is_valid());\n\n  ScopedMmap mapping2;\n  ASSERT_TRUE(mapping2.ResetAddrLen(mapping.release(), kPageSize));\n  EXPECT_TRUE(mapping2.is_valid());\n  EXPECT_FALSE(mapping.is_valid());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/signals.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/signals.h\"\n\n#include <unistd.h>\n\n#include <iterator>\n#include <vector>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)\n#include <sys/syscall.h>\n#endif\n\nnamespace crashpad {\n\nnamespace {\n\n// These are the core-generating signals.\n//\n// On macOS, these come from 10.12.3 xnu-3789.41.3/bsd/sys/signalvar.h sigprop:\n// entries with SA_CORE are in the set.\n//\n// For Linux, see linux-4.4.52/kernel/signal.c get_signal() and\n// linux-4.4.52/include/linux/signal.h sig_kernel_coredump(): signals in\n// SIG_KERNEL_COREDUMP_MASK are in the set.\nconstexpr int kCrashSignals[] = {\n    SIGABRT,\n    SIGBUS,\n    SIGFPE,\n    SIGILL,\n    SIGQUIT,\n    SIGSEGV,\n    SIGSYS,\n    SIGTRAP,\n#if defined(SIGEMT)\n    SIGEMT,\n#endif  // defined(SIGEMT)\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    SIGXCPU,\n    SIGXFSZ,\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n};\n\n// These are the non-core-generating but terminating signals.\n//\n// On macOS, these come from 10.12.3 xnu-3789.41.3/bsd/sys/signalvar.h sigprop:\n// entries with SA_KILL but not SA_CORE are in the set. SIGKILL is excluded\n// because it is uncatchable.\n//\n// For Linux, see linux-4.4.52/kernel/signal.c get_signal() and\n// linux-4.4.52/include/linux/signal.h sig_kernel_coredump(),\n// sig_kernel_ignore(), and sig_kernel_stop(): signals not in\n// SIG_KERNEL_COREDUMP_MASK, SIG_KERNEL_IGNORE_MASK, or SIG_KERNEL_STOP_MASK are\n// in the set. SIGKILL is excluded because it is uncatchable (it’s in\n// SIG_KERNEL_ONLY_MASK and qualifies for sig_kernel_only()). Real-time signals\n// in the range [SIGRTMIN, SIGRTMAX) also have termination as the default\n// action, although they are not listed here.\nconstexpr int kTerminateSignals[] = {\n    SIGALRM,\n    SIGHUP,\n    SIGINT,\n    SIGPIPE,\n    SIGPROF,\n    SIGTERM,\n    SIGUSR1,\n    SIGUSR2,\n    SIGVTALRM,\n#if defined(SIGPWR)\n    SIGPWR,\n#endif  // defined(SIGPWR)\n#if defined(SIGSTKFLT)\n    SIGSTKFLT,\n#endif  // defined(SIGSTKFLT)\n#if BUILDFLAG(IS_APPLE)\n    SIGXCPU,\n    SIGXFSZ,\n#endif  // BUILDFLAG(IS_APPLE)\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    SIGIO,\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n};\n\nbool InstallHandlers(const std::vector<int>& signals,\n                     Signals::Handler handler,\n                     int flags,\n                     Signals::OldActions* old_actions,\n                     const std::set<int>* unhandled_signals) {\n  bool success = true;\n  for (int sig : signals) {\n    if (unhandled_signals &&\n        unhandled_signals->find(sig) != unhandled_signals->end()) {\n      continue;\n    }\n    success &= Signals::InstallHandler(\n        sig,\n        handler,\n        flags,\n        old_actions ? old_actions->ActionForSignal(sig) : nullptr);\n  }\n  return success;\n}\n\nbool IsSignalInSet(int sig, const int* set, size_t set_size) {\n  for (size_t index = 0; index < set_size; ++index) {\n    if (sig == set[index]) {\n      return true;\n    }\n  }\n  return false;\n}\n\n}  // namespace\n\nstruct sigaction* Signals::OldActions::ActionForSignal(int sig) {\n  DCHECK_GT(sig, 0);\n  const size_t slot = sig - 1;\n  DCHECK_LT(slot, std::size(actions_));\n  return &actions_[slot];\n}\n\n// static\nbool Signals::InstallHandler(int sig,\n                             Handler handler,\n                             int flags,\n                             struct sigaction* old_action) {\n  struct sigaction action;\n  sigemptyset(&action.sa_mask);\n  action.sa_flags = flags | SA_SIGINFO;\n  action.sa_sigaction = handler;\n  if (sigaction(sig, &action, old_action) != 0) {\n    PLOG(ERROR) << \"sigaction \" << sig;\n    return false;\n  }\n\n// Sanitizers can prevent the installation of signal handlers, but sigaction\n// does not report this as failure. Attempt to detect this by checking the\n// currently installed signal handler.\n#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \\\n    defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) ||    \\\n    defined(UNDEFINED_SANITIZER)\n  struct sigaction installed_handler;\n  CHECK_EQ(sigaction(sig, nullptr, &installed_handler), 0);\n  // If the installed handler does not point to the just installed handler, then\n  // the allow_user_segv_handler sanitizer flag is (probably) disabled.\n  if (installed_handler.sa_sigaction != handler) {\n    LOG(WARNING)\n        << \"sanitizers are preventing signal handler installation (sig \" << sig\n        << \")\";\n    return false;\n  }\n#endif\n\n  return true;\n}\n\n// static\nbool Signals::InstallDefaultHandler(int sig) {\n  struct sigaction action;\n  sigemptyset(&action.sa_mask);\n  action.sa_flags = 0;\n  action.sa_handler = SIG_DFL;\n  return sigaction(sig, &action, nullptr) == 0;\n}\n\n// static\nbool Signals::InstallCrashHandlers(Handler handler,\n                                   int flags,\n                                   OldActions* old_actions,\n                                   const std::set<int>* unhandled_signals) {\n  return InstallHandlers(\n      std::vector<int>(kCrashSignals, kCrashSignals + std::size(kCrashSignals)),\n      handler,\n      flags,\n      old_actions,\n      unhandled_signals);\n}\n\n// static\nbool Signals::InstallTerminateHandlers(Handler handler,\n                                       int flags,\n                                       OldActions* old_actions) {\n  return InstallHandlers(\n      std::vector<int>(kTerminateSignals,\n                       kTerminateSignals + std::size(kTerminateSignals)),\n      handler,\n      flags,\n      old_actions,\n      nullptr);\n}\n\n// static\nbool Signals::WillSignalReraiseAutonomously(const siginfo_t* siginfo) {\n  // Signals received other than via hardware faults, such as those raised\n  // asynchronously via kill() and raise(), and those arising via hardware traps\n  // such as int3 on x86 (resulting in SIGTRAP but advancing the instruction\n  // pointer), will not reoccur on their own when returning from the signal\n  // handler.\n  //\n  // Unfortunately, on macOS, when SIGBUS (on all CPUs) and SIGILL and SIGSEGV\n  // (on arm64) is received asynchronously via kill(), siginfo->si_code makes it\n  // appear as though it was actually received via a hardware fault. See 10.15.6\n  // xnu-6153.141.1/bsd/dev/i386/unix_signal.c sendsig() and 10.15.6\n  // xnu-6153.141.1/bsd/dev/arm/unix_signal.c sendsig(). Received\n  // asynchronously, these signals will not re-raise themselves autonomously,\n  // but this function (acting on information from the kernel) behaves as though\n  // they will. This isn’t ideal, but these signals occurring asynchronously is\n  // an unexpected condition. The alternative, to never treat these signals as\n  // autonomously re-raising, is a bad idea because the explicit re-raise would\n  // lose properties associated with the the original signal, which are valuable\n  // for debugging and are visible to a Mach exception handler. Since these\n  // signals are normally received synchronously in response to a hardware\n  // fault, don’t sweat the unexpected asynchronous case.\n  //\n  // SIGSEGV on macOS on x86[_64] originating from a general protection fault is\n  // a more difficult case: si_code is cleared, making the signal appear\n  // asynchronous. See 10.15.6 xnu-6153.141.1/bsd/dev/i386/unix_signal.c\n  // sendsig().\n  const int sig = siginfo->si_signo;\n  const int code = siginfo->si_code;\n\n  // Only these signals can be generated from hardware faults and can re-raise\n  // autonomously.\n  return (sig == SIGBUS ||\n          sig == SIGFPE ||\n          sig == SIGILL ||\n          sig == SIGSEGV) &&\n\n         // The signal was only generated from a hardware fault if the code is a\n         // positive number not matching one of these SI_* constants. See\n         // “Signal Actions” under XRAT “Rationale”/B.2.4 “Signal Concepts” in\n         // POSIX.1-2008, 2016 Edition, regarding si_code. The historical\n         // behavior does not use these SI_* constants and signals generated\n         // asynchronously show up with a code of 0. On macOS, the SI_*\n         // constants are defined but never used, and the historical value of 0\n         // remains. See 10.12.3 xnu-3789.41.3/bsd/kern/kern_sig.c\n         // psignal_internal().\n         (code > 0 &&\n          code != SI_ASYNCIO &&\n          code != SI_MESGQ &&\n          code != SI_QUEUE &&\n          code != SI_TIMER &&\n          code != SI_USER &&\n#if defined(SI_DETHREAD)\n          code != SI_DETHREAD &&\n#endif  // defiend(SI_DETHREAD)\n#if defined(SI_KERNEL)\n          // In Linux, SI_KERNEL is used for signals that are raised by the\n          // kernel in software, opposing SI_USER. See\n          // linux-4.4.52/kernel/signal.c __send_signal(). Signals originating\n          // from hardware faults do not use this SI_KERNEL, but a proper signal\n          // code translated in architecture-specific code from the\n          // characteristics of the hardware fault.\n          code != SI_KERNEL &&\n#endif  // defined(SI_KERNEL)\n#if defined(SI_SIGIO)\n          code != SI_SIGIO &&\n#endif  // defined(SI_SIGIO)\n#if defined(SI_TKILL)\n          code != SI_TKILL &&\n#endif  // defined(SI_TKILL)\n          true);\n}\n\n// static\nbool Signals::RestoreOrResetHandler(int sig,\n                                    const struct sigaction* old_action) {\n  struct sigaction default_action;\n  sigemptyset(&default_action.sa_mask);\n  default_action.sa_flags = 0;\n  default_action.sa_handler = SIG_DFL;\n\n  const struct sigaction* restore_action =\n      old_action ? old_action : &default_action;\n\n  // Try to restore restore_action. If that fails and restore_action was\n  // old_action, the problem may have been that old_action was bogus, so try to\n  // set the default action.\n  if (sigaction(sig, restore_action, nullptr) != 0 && old_action &&\n      sigaction(sig, &default_action, nullptr) != 0) {\n    return false;\n  }\n  return true;\n}\n\n// static\nvoid Signals::RestoreHandlerAndReraiseSignalOnReturn(\n    const siginfo_t* siginfo,\n    const struct sigaction* old_action) {\n  // Failures in this function should _exit(kFailureExitCode). This is a quick\n  // and quiet failure. This function runs in signal handler context, and it’s\n  // difficult to safely be loud from a signal handler.\n  constexpr int kFailureExitCode = 191;\n\n  const int sig = siginfo->si_signo;\n\n  if (!RestoreOrResetHandler(sig, old_action)) {\n    _exit(kFailureExitCode);\n  }\n\n  // If we can raise a signal with siginfo on this platform, do so. This ensures\n  // that we preserve the siginfo information for asynchronous signals (i.e.\n  // signals that do not re-raise autonomously), such as signals delivered via\n  // kill() and asynchronous hardware faults such as SEGV_MTEAERR, which would\n  // otherwise be lost when re-raising the signal via raise().\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)\n  int retval = syscall(SYS_rt_tgsigqueueinfo,\n                       getpid(),\n                       syscall(SYS_gettid),\n                       siginfo->si_signo,\n                       siginfo);\n  if (retval == 0) {\n    return;\n  }\n\n  // Kernels without commit 66dd34ad31e5 (\"signal: allow to send any siginfo to\n  // itself\"), which was first released in kernel version 3.9, did not permit a\n  // process to send arbitrary signals to itself, and will reject the\n  // rt_tgsigqueueinfo syscall with EPERM. If that happens, follow the non-Linux\n  // code path. Any other errno is unexpected and will cause us to exit.\n  if (errno != EPERM) {\n    _exit(kFailureExitCode);\n  }\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n  // Explicitly re-raise the signal if it will not re-raise itself. Because\n  // signal handlers normally execute with their signal blocked, this raise()\n  // cannot immediately deliver the signal. Delivery is deferred until the\n  // signal handler returns and the signal becomes unblocked. The re-raised\n  // signal will appear with the same context as where it was initially\n  // triggered.\n  if (!WillSignalReraiseAutonomously(siginfo) && raise(sig) != 0) {\n    _exit(kFailureExitCode);\n  }\n}\n\n// static\nbool Signals::IsCrashSignal(int sig) {\n  return IsSignalInSet(sig, kCrashSignals, std::size(kCrashSignals));\n}\n\n// static\nbool Signals::IsTerminateSignal(int sig) {\n  return IsSignalInSet(sig, kTerminateSignals, std::size(kTerminateSignals));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/signals.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_SIGNALS_H_\n#define CRASHPAD_UTIL_POSIX_SIGNALS_H_\n\n#include <signal.h>\n\n#include <set>\n\n\nnamespace crashpad {\n\n//! \\brief Utilities for handling POSIX signals.\nclass Signals {\n public:\n  //! \\brief A signal number used by Crashpad to simulate signals.\n  static constexpr int kSimulatedSigno = -1;\n\n  //! \\brief The type used for `struct sigaction::sa_sigaction`.\n  using Handler = void (*)(int, siginfo_t*, void*);\n\n  //! \\brief A group of `struct sigaction` structures corresponding to a set\n  //!     of signals’ previous actions, addressable by signal number.\n  //!\n  //! This type is used to store previous signal actions when new actions are\n  //! installed in batch by InstallCrashHandlers() or\n  //! InstallTerminateHandlers().\n  //!\n  //! This object is not initialized by any constructor. Its expected initial\n  //! state is to have its contents filled with zeroes. Because signal handlers\n  //! are stateless (there is no “context” parameter), any state must be\n  //! accessed via objects of static storage duration, and it is expected that\n  //! objects of this class will only ever exist with static storage duration,\n  //! which in the absence of a constructor will be zero-initialized as\n  //! expected. In the event that an object of this class must exist with a\n  //! different storage duration, such as automatic or dynamic storage duration,\n  //! it must be explicitly initialized. For example: `OldActions old_actions =\n  //! {};`.\n  class OldActions {\n   public:\n    // DISALLOW_COPY_AND_ASSIGN removes the default constructor, so explicitly\n    // opt for it. This should not result in any static initialization code even\n    // when an object of this class is given static storage duration.\n    OldActions() = default;\n\n    OldActions(const OldActions&) = delete;\n    OldActions& operator=(const OldActions&) = delete;\n\n    //! \\brief Returns a `struct sigaction` structure corresponding to the\n    //!     given signal.\n    //!\n    //! \\note This method is safe to call from a signal handler.\n    struct sigaction* ActionForSignal(int sig);\n\n   private:\n    // As a small storage optimization, don’t waste any space on a slot for\n    // signal 0, because there is no signal 0.\n    struct sigaction actions_[NSIG - 1];\n  };\n\n  Signals() = delete;\n  Signals(const Signals&) = delete;\n  Signals& operator=(const Signals&) = delete;\n\n  //! \\brief Installs a new signal handler.\n  //!\n  //! \\param[in] sig The signal number to handle.\n  //! \\param[in] handler A signal-handling function to execute, used as the\n  //!     `struct sigaction::sa_sigaction` field when calling `sigaction()`.\n  //! \\param[in] flags Flags to pass to `sigaction()` in the `struct\n  //!     sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.\n  //! \\param[out] old_action The previous action for the signal, replaced by the\n  //!     new action. May be `nullptr` if not needed.\n  //!\n  //! \\return `true` on success. `false` on failure with a message logged.\n  //!\n  //! \\warning This function may not be called from a signal handler because of\n  //!     its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()\n  //!     instead.\n  static bool InstallHandler(int sig,\n                             Handler handler,\n                             int flags,\n                             struct sigaction* old_action);\n\n  //! \\brief Installs `SIG_DFL` for the signal \\a sig.\n  //!\n  //! \\param[in] sig The signal to set the default action for.\n  //!\n  //! \\return `true` on success, `false` on failure with errno set. No message\n  //!     is logged.\n  static bool InstallDefaultHandler(int sig);\n\n  //! \\brief Installs a new signal handler for all signals associated with\n  //!     crashes.\n  //!\n  //! Signals associated with crashes are those whose default dispositions\n  //! involve creating a core dump. The precise set of signals involved varies\n  //! between operating systems.\n  //!\n  //! A single signal may either be associated with a crash or with termination\n  //! (see InstallTerminateHandlers()), and perhaps neither, but never both.\n  //!\n  //! \\param[in] handler A signal-handling function to execute, used as the\n  //!     `struct sigaction::sa_sigaction` field when calling `sigaction()`.\n  //! \\param[in] flags Flags to pass to `sigaction()` in the `struct\n  //!     sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.\n  //! \\param[out] old_actions The previous actions for the signals, replaced by\n  //!     the new action. May be `nullptr` if not needed. The same \\a\n  //!     old_actions object may be used for calls to both this function and\n  //!     InstallTerminateHandlers().\n  //! \\param[in] unhandled_signals Signal handlers will not be installed for\n  //!     signal numbers in this set. Optional.\n  //!\n  //! \\return `true` on success. `false` on failure with a message logged.\n  //!\n  //! \\warning This function may not be called from a signal handler because of\n  //!     its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()\n  //!     instead.\n  static bool InstallCrashHandlers(\n      Handler handler,\n      int flags,\n      OldActions* old_actions,\n      const std::set<int>* unhandled_signals = nullptr);\n\n  //! \\brief Installs a new signal handler for all signals associated with\n  //!     termination.\n  //!\n  //! Signals associated with termination are those whose default dispositions\n  //! involve terminating the process without creating a core dump. The precise\n  //! set of signals involved varies between operating systems.\n  //!\n  //! A single signal may either be associated with termination or with a\n  //! crash (see InstalCrashHandlers()), and perhaps neither, but never both.\n  //!\n  //! \\param[in] handler A signal-handling function to execute, used as the\n  //!     `struct sigaction::sa_sigaction` field when calling `sigaction()`.\n  //! \\param[in] flags Flags to pass to `sigaction()` in the `struct\n  //!     sigaction::sa_flags` field. `SA_SIGINFO` will be specified implicitly.\n  //! \\param[out] old_actions The previous actions for the signals, replaced by\n  //!     the new action. May be `nullptr` if not needed. The same \\a\n  //!     old_actions object may be used for calls to both this function and\n  //!     InstallCrashHandlers().\n  //!\n  //! \\return `true` on success. `false` on failure with a message logged.\n  //!\n  //! \\warning This function may not be called from a signal handler because of\n  //!     its use of logging. See RestoreHandlerAndReraiseSignalOnReturn()\n  //!     instead.\n  static bool InstallTerminateHandlers(Handler handler,\n                                       int flags,\n                                       OldActions* old_actions);\n\n  //! \\brief Determines whether a signal will be re-raised autonomously upon\n  //!     return from a signal handler.\n  //!\n  //! Certain signals, when generated synchronously in response to a hardware\n  //! fault, are unrecoverable. Upon return from the signal handler, the same\n  //! action that triggered the signal to be raised initially will be retried,\n  //! and unless the signal handler took action to mitigate this error, the same\n  //! signal will be re-raised. As an example, a CPU will not be able to read\n  //! unmapped memory (causing `SIGSEGV`), thus the signal will be re-raised\n  //! upon return from the signal handler unless the signal handler established\n  //! a memory mapping where required.\n  //!\n  //! It is important to distinguish between these synchronous signals generated\n  //! in response to a hardware fault and signals generated asynchronously or in\n  //! software. As an example, `SIGSEGV` will not re-raise autonomously if sent\n  //! by `kill()`.\n  //!\n  //! This function distinguishes between signals that can re-raise\n  //! autonomously, and for those that can, between instances of the signal that\n  //! were generated synchronously in response to a hardware fault and instances\n  //! that were generated by other means.\n  //!\n  //! \\param[in] siginfo A pointer to a `siginfo_t` object received by a signal\n  //!     handler.\n  //!\n  //! \\return `true` if the signal being handled will re-raise itself\n  //!     autonomously upon return from a signal handler. `false` if it will\n  //!     not. When this function returns `false`, a signal can still be\n  //!     re-raised upon return from a signal handler by calling `raise()` from\n  //!     within the signal handler.\n  //!\n  //! \\note This function is safe to call from a signal handler.\n  static bool WillSignalReraiseAutonomously(const siginfo_t* siginfo);\n\n  //! \\brief Restores a previous signal action or reinstalls the default signal\n  //!     handler for a given signal.\n  //!\n  //! Attempts to reinstate the action given by \\a old_action and, in case of\n  //! failure or if \\a old_actiono is `nullptr`, resets the handler for \\a sig\n  //! to the default action.\n  //!\n  //! \\param[in] sig The signal to manage.\n  //! \\param[in] old_action The previous action for the signal, which will be\n  //!     re-established as the signal’s action. May be `nullptr`, which directs\n  //!     the default action for the signal to be used.\n  //!\n  //! \\return `true` on success, `false` if `sigaction()` fails.\n  //!\n  //! \\note This function is safe to call from a signal handler.\n  static bool RestoreOrResetHandler(int sig,\n                                    const struct sigaction* old_action);\n\n  //! \\brief Restores a previous signal action and arranges to re-raise a signal\n  //!     on return from a signal handler.\n  //!\n  //! \\param[in] siginfo A pointer to a `siginfo_t` object received by a signal\n  //!     handler.\n  //! \\param[in] old_action The previous action for the signal, which will be\n  //!     re-established as the signal’s action. May be `nullptr`, which directs\n  //!     the default action for the signal to be used.\n  //!\n  //! If this function fails, it will immediately call `_exit()` and set an exit\n  //! status of `191`.\n  //!\n  //! \\note This function may only be called from a signal handler.\n  static void RestoreHandlerAndReraiseSignalOnReturn(\n      const siginfo_t* siginfo,\n      const struct sigaction* old_action);\n\n  //! \\brief Determines whether a signal is associated with a crash.\n  //!\n  //! Signals associated with crashes are those whose default dispositions\n  //! involve creating a core dump. The precise set of signals involved varies\n  //! between operating systems.\n  //!\n  //! \\param[in] sig The signal to test.\n  //!\n  //! \\return `true` if \\a sig is associated with a crash. `false` otherwise.\n  //!\n  //! \\note This function is safe to call from a signal handler.\n  static bool IsCrashSignal(int sig);\n\n  //! \\brief Determines whether a signal is associated with termination.\n  //!\n  //! Signals associated with termination are those whose default dispositions\n  //! involve terminating the process without creating a core dump. The precise\n  //! set of signals involved varies between operating systems.\n  //!\n  //! \\param[in] sig The signal to test.\n  //!\n  //! \\return `true` if \\a sig is associated with termination. `false`\n  //!     otherwise.\n  //!\n  //! \\note This function is safe to call from a signal handler.\n  static bool IsTerminateSignal(int sig);\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_SIGNALS_H_\n"
  },
  {
    "path": "util/posix/signals_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/signals.h\"\n\n#include <fcntl.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/time.h>\n#include <unistd.h>\n\n#include <iterator>\n#include <limits>\n\n#include \"base/files/scoped_file.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/multiprocess.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/posix/scoped_mmap.h\"\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)\n#include <sys/auxv.h>\n#include <sys/prctl.h>\n\n#if defined(ARCH_CPU_ARM64)\n#ifndef HWCAP2_MTE\n#define HWCAP2_MTE (1 << 18)\n#endif\n#ifndef SEGV_MTEAERR\n#define SEGV_MTEAERR 8\n#endif\n#ifndef PROT_MTE\n#define PROT_MTE 0x20\n#endif\n#ifndef PR_SET_TAGGED_ADDR_CTRL\n#define PR_SET_TAGGED_ADDR_CTRL 55\n#endif\n#ifndef PR_TAGGED_ADDR_ENABLE\n#define PR_TAGGED_ADDR_ENABLE (1UL << 0)\n#endif\n#ifndef PR_MTE_TCF_ASYNC\n#define PR_MTE_TCF_ASYNC (1UL << 2)\n#endif\n#endif  // defined(ARCH_CPU_ARM64)\n#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr int kUnexpectedExitStatus = 3;\n\nstruct TestableSignal {\n  int sig, code;\n};\n\n// Keep synchronized with CauseSignal().\nstd::vector<TestableSignal> TestableSignals() {\n  std::vector<TestableSignal> signals;\n  signals.push_back({SIGABRT, 0});\n  signals.push_back({SIGALRM, 0});\n  signals.push_back({SIGBUS, 0});\n/* According to DDI0487D (Armv8 Architecture Reference Manual) the expected\n * behavior for division by zero (Section 3.4.8) is: \"... results in a\n * zero being written to the destination register, without any\n * indication that the division by zero occurred.\".\n * This applies to Armv8 (and not earlier) for both 32bit and 64bit app code.\n */\n#if defined(ARCH_CPU_X86_FAMILY)\n  signals.push_back({SIGFPE, 0});\n#endif\n#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)\n  signals.push_back({SIGILL, 0});\n#endif  // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)\n  signals.push_back({SIGPIPE, 0});\n  signals.push_back({SIGSEGV, 0});\n#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || \\\n     BUILDFLAG(IS_CHROMEOS)) &&                      \\\n    defined(ARCH_CPU_ARM64)\n  if (getauxval(AT_HWCAP2) & HWCAP2_MTE) {\n    signals.push_back({SIGSEGV, SEGV_MTEAERR});\n  }\n#endif\n#if BUILDFLAG(IS_APPLE)\n  signals.push_back({SIGSYS, 0});\n#endif  // BUILDFLAG(IS_APPLE)\n#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)\n  signals.push_back({SIGTRAP, 0});\n#endif  // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)\n  return signals;\n}\n\n// Keep synchronized with TestableSignals().\nvoid CauseSignal(int sig, int code) {\n  switch (sig) {\n    case SIGABRT: {\n      abort();\n    }\n\n    case SIGALRM: {\n      struct itimerval itimer = {};\n      itimer.it_value.tv_usec = 1E3;  // 1 millisecond\n      if (setitimer(ITIMER_REAL, &itimer, nullptr) != 0) {\n        PLOG(ERROR) << \"setitimer\";\n        _exit(kUnexpectedExitStatus);\n      }\n\n      while (true) {\n        sleep(std::numeric_limits<unsigned int>::max());\n      }\n    }\n\n    case SIGBUS: {\n      ScopedMmap mapped_file;\n      {\n        base::ScopedFD fd;\n        {\n          ScopedTempDir temp_dir;\n          fd.reset(open(temp_dir.path().Append(\"empty\").value().c_str(),\n                        O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_CLOEXEC,\n                        0644));\n          if (fd.get() < 0) {\n            PLOG(ERROR) << \"open\";\n          }\n        }\n        if (fd.get() < 0) {\n          _exit(kUnexpectedExitStatus);\n        }\n\n        if (!mapped_file.ResetMmap(nullptr,\n                                   getpagesize(),\n                                   PROT_READ | PROT_WRITE,\n                                   MAP_PRIVATE,\n                                   fd.get(),\n                                   0)) {\n          _exit(kUnexpectedExitStatus);\n        }\n      }\n\n      *mapped_file.addr_as<char*>() = 0;\n\n      _exit(kUnexpectedExitStatus);\n    }\n\n    case SIGFPE: {\n/* Enabled only for x86, since a division by zero won't raise a signal\n * on Armv8, please see comment at the top of file concerning the\n * Arm architecture.\n */\n#if defined(ARCH_CPU_X86_FAMILY)\n      // Dividing by zero is undefined in C, so the compiler is permitted to\n      // optimize out the division. Instead, divide using inline assembly. As\n      // this instruction will trap anyway, we skip declaring any clobbers or\n      // output registers.\n      int a = 42, b = 0;\n      asm volatile(\"idivl %2\" : : \"a\"(0), \"d\"(a), \"r\"(b));\n#endif\n      break;\n    }\n\n#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)\n    case SIGILL: {\n      // __builtin_trap() causes SIGTRAP on arm64 on Android.\n      __builtin_trap();\n    }\n#endif  // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARMEL)\n\n    case SIGPIPE: {\n      int pipe_fds[2];\n      if (pipe(pipe_fds) != 0) {\n        PLOG(ERROR) << \"pipe\";\n        _exit(kUnexpectedExitStatus);\n      }\n\n      if (close(pipe_fds[0]) != 0) {\n        PLOG(ERROR) << \"close\";\n        _exit(kUnexpectedExitStatus);\n      }\n\n      char c = 0;\n      ssize_t rv = write(pipe_fds[1], &c, sizeof(c));\n      if (rv < 0) {\n        PLOG(ERROR) << \"write\";\n        _exit(kUnexpectedExitStatus);\n      } else if (rv != sizeof(c)) {\n        LOG(ERROR) << \"write\";\n        _exit(kUnexpectedExitStatus);\n      }\n      break;\n    }\n\n    case SIGSEGV: {\n      switch (code) {\n        case 0: {\n          volatile int* i = nullptr;\n          *i = 0;\n          break;\n        }\n#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || \\\n     BUILDFLAG(IS_CHROMEOS)) &&                      \\\n    defined(ARCH_CPU_ARM64)\n        case SEGV_MTEAERR: {\n          ScopedMmap mapping;\n          if (!mapping.ResetMmap(nullptr,\n                                 getpagesize(),\n                                 PROT_READ | PROT_WRITE | PROT_MTE,\n                                 MAP_PRIVATE | MAP_ANON,\n                                 -1,\n                                 0)) {\n            _exit(kUnexpectedExitStatus);\n          }\n          if (prctl(PR_SET_TAGGED_ADDR_CTRL,\n                PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_ASYNC,\n                0,\n                0,\n                0) != 0) {\n            _exit(kUnexpectedExitStatus);\n          }\n          mapping.addr_as<char*>()[1ULL << 56] = 0;\n          break;\n        }\n#endif  // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) ||\n        // BUILDFLAG(IS_CHROMEOS)) && defined(ARCH_CPU_ARM64)\n      }\n      break;\n    }\n\n#if BUILDFLAG(IS_APPLE)\n    case SIGSYS: {\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n      int rv = syscall(4095);\n#pragma clang diagnostic pop\n      if (rv != 0) {\n        PLOG(ERROR) << \"syscall\";\n        _exit(kUnexpectedExitStatus);\n      }\n      break;\n    }\n#endif  // BUILDFLAG(IS_APPLE)\n\n#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)\n    case SIGTRAP: {\n#if defined(ARCH_CPU_X86_FAMILY)\n      asm(\"int3\");\n#elif defined(ARCH_CPU_ARM64)\n      // bkpt #0 should work for 32-bit ARCH_CPU_ARMEL, but according to\n      // https://crrev.com/f53167270c44, it only causes SIGTRAP on Linux under a\n      // 64-bit kernel. For a pure 32-bit armv7 system, it generates SIGBUS.\n      asm(\"brk #0\");\n#endif\n      break;\n    }\n#endif  // defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM64)\n\n    default: {\n      LOG(ERROR) << \"unexpected signal \" << sig;\n      _exit(kUnexpectedExitStatus);\n    }\n  }\n}\n\nclass SignalsTest : public Multiprocess {\n public:\n  enum class SignalSource {\n    kCause,\n    kRaise,\n  };\n  enum class TestType {\n    kDefaultHandler,\n    kHandlerExits,\n    kHandlerReraisesToDefault,\n    kHandlerReraisesToPrevious,\n  };\n  static constexpr int kExitingHandlerExitStatus = 2;\n\n  SignalsTest(TestType test_type, SignalSource signal_source, int sig, int code)\n      : Multiprocess(),\n        sig_(sig),\n        code_(code),\n        test_type_(test_type),\n        signal_source_(signal_source) {}\n\n  SignalsTest(const SignalsTest&) = delete;\n  SignalsTest& operator=(const SignalsTest&) = delete;\n\n  ~SignalsTest() {}\n\n private:\n  static void SignalHandler_Exit(int sig, siginfo_t* siginfo, void* context) {\n    _exit(kExitingHandlerExitStatus);\n  }\n\n  static void SignalHandler_ReraiseToDefault(int sig,\n                                             siginfo_t* siginfo,\n                                             void* context) {\n    Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);\n  }\n\n  static void SignalHandler_ReraiseToPrevious(int sig,\n                                              siginfo_t* siginfo,\n                                              void* context) {\n    Signals::RestoreHandlerAndReraiseSignalOnReturn(\n        siginfo, old_actions_.ActionForSignal(sig));\n  }\n\n  // Multiprocess:\n  void MultiprocessParent() override {}\n\n  void MultiprocessChild() override {\n    bool (*install_handlers)(Signals::Handler, int, Signals::OldActions*);\n    if (Signals::IsCrashSignal(sig_)) {\n      install_handlers = [](Signals::Handler handler,\n                            int flags,\n                            Signals::OldActions* old_actions) {\n        return Signals::InstallCrashHandlers(\n            handler, flags, old_actions, nullptr);\n      };\n    } else if (Signals::IsTerminateSignal(sig_)) {\n      install_handlers = Signals::InstallTerminateHandlers;\n    } else {\n      _exit(kUnexpectedExitStatus);\n    }\n\n    switch (test_type_) {\n      case TestType::kDefaultHandler: {\n        // Don’t rely on the default handler being active. Something may have\n        // changed it (particularly on Android).\n        struct sigaction action;\n        sigemptyset(&action.sa_mask);\n        action.sa_flags = 0;\n        action.sa_handler = SIG_DFL;\n        ASSERT_EQ(sigaction(sig_, &action, nullptr), 0)\n            << ErrnoMessage(\"sigaction\");\n        break;\n      }\n\n      case TestType::kHandlerExits: {\n        ASSERT_TRUE(install_handlers(SignalHandler_Exit, 0, nullptr));\n        break;\n      }\n\n      case TestType::kHandlerReraisesToDefault: {\n        ASSERT_TRUE(\n            install_handlers(SignalHandler_ReraiseToDefault, 0, nullptr));\n        break;\n      }\n\n      case TestType::kHandlerReraisesToPrevious: {\n        ASSERT_TRUE(install_handlers(SignalHandler_Exit, 0, nullptr));\n        ASSERT_TRUE(install_handlers(\n            SignalHandler_ReraiseToPrevious, 0, &old_actions_));\n        break;\n      }\n    }\n\n    switch (signal_source_) {\n      case SignalSource::kCause:\n        CauseSignal(sig_, code_);\n        break;\n      case SignalSource::kRaise:\n        raise(sig_);\n        break;\n    }\n\n    _exit(kUnexpectedExitStatus);\n  }\n\n  int sig_;\n  int code_;\n  TestType test_type_;\n  SignalSource signal_source_;\n  static Signals::OldActions old_actions_;\n};\n\nSignals::OldActions SignalsTest::old_actions_;\n\nbool ShouldTestSignal(int sig) {\n  return Signals::IsCrashSignal(sig) || Signals::IsTerminateSignal(sig);\n}\n\nTEST(Signals, WillSignalReraiseAutonomously) {\n  const struct {\n    int sig;\n    int code;\n    bool result;\n  } kTestData[] = {\n      {SIGBUS, BUS_ADRALN, true},\n      {SIGFPE, FPE_FLTDIV, true},\n      {SIGILL, ILL_ILLOPC, true},\n      {SIGSEGV, SEGV_MAPERR, true},\n      {SIGBUS, 0, false},\n      {SIGFPE, -1, false},\n      {SIGILL, SI_USER, false},\n      {SIGSEGV, SI_QUEUE, false},\n      {SIGTRAP, TRAP_BRKPT, false},\n      {SIGHUP, SEGV_MAPERR, false},\n      {SIGINT, SI_USER, false},\n  };\n  for (size_t index = 0; index < std::size(kTestData); ++index) {\n    const auto test_data = kTestData[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"index %zu, sig %d, code %d\", index, test_data.sig, test_data.code));\n    siginfo_t siginfo = {};\n    siginfo.si_signo = test_data.sig;\n    siginfo.si_code = test_data.code;\n    EXPECT_EQ(Signals::WillSignalReraiseAutonomously(&siginfo),\n              test_data.result);\n  }\n}\n\nTEST(Signals, Cause_DefaultHandler) {\n  for (TestableSignal s : TestableSignals()) {\n    SCOPED_TRACE(base::StringPrintf(\n        \"sig %d (%s), code %d\", s.sig, strsignal(s.sig), s.code));\n\n    SignalsTest test(SignalsTest::TestType::kDefaultHandler,\n                     SignalsTest::SignalSource::kCause,\n                     s.sig,\n                     s.code);\n    test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, s.sig);\n    test.Run();\n  }\n}\n\nTEST(Signals, Cause_HandlerExits) {\n  for (TestableSignal s : TestableSignals()) {\n    SCOPED_TRACE(base::StringPrintf(\n        \"sig %d (%s), code %d\", s.sig, strsignal(s.sig), s.code));\n\n    SignalsTest test(SignalsTest::TestType::kHandlerExits,\n                     SignalsTest::SignalSource::kCause,\n                     s.sig,\n                     s.code);\n    test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,\n                                     SignalsTest::kExitingHandlerExitStatus);\n    test.Run();\n  }\n}\n\nTEST(Signals, Cause_HandlerReraisesToDefault) {\n  for (TestableSignal s : TestableSignals()) {\n    SCOPED_TRACE(base::StringPrintf(\n        \"sig %d (%s), code %d\", s.sig, strsignal(s.sig), s.code));\n\n    SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,\n                     SignalsTest::SignalSource::kCause,\n                     s.sig,\n                     s.code);\n    test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, s.sig);\n    test.Run();\n  }\n}\n\nTEST(Signals, Cause_HandlerReraisesToPrevious) {\n  for (TestableSignal s : TestableSignals()) {\n    SCOPED_TRACE(base::StringPrintf(\n        \"sig %d (%s), code %d\", s.sig, strsignal(s.sig), s.code));\n\n    SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,\n                     SignalsTest::SignalSource::kCause,\n                     s.sig,\n                     s.code);\n    test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,\n                                     SignalsTest::kExitingHandlerExitStatus);\n    test.Run();\n  }\n}\n\nTEST(Signals, Raise_DefaultHandler) {\n  for (int sig = 1; sig < NSIG; ++sig) {\n    SCOPED_TRACE(base::StringPrintf(\"sig %d (%s)\", sig, strsignal(sig)));\n\n    if (!ShouldTestSignal(sig)) {\n      continue;\n    }\n\n    SignalsTest test(SignalsTest::TestType::kDefaultHandler,\n                     SignalsTest::SignalSource::kRaise,\n                     sig,\n                     0);\n    test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);\n    test.Run();\n  }\n}\n\nTEST(Signals, Raise_HandlerExits) {\n  for (int sig = 1; sig < NSIG; ++sig) {\n    SCOPED_TRACE(base::StringPrintf(\"sig %d (%s)\", sig, strsignal(sig)));\n\n    if (!ShouldTestSignal(sig)) {\n      continue;\n    }\n\n    SignalsTest test(SignalsTest::TestType::kHandlerExits,\n                     SignalsTest::SignalSource::kRaise,\n                     sig,\n                     0);\n    test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,\n                                     SignalsTest::kExitingHandlerExitStatus);\n    test.Run();\n  }\n}\n\nTEST(Signals, Raise_HandlerReraisesToDefault) {\n  for (int sig = 1; sig < NSIG; ++sig) {\n    SCOPED_TRACE(base::StringPrintf(\"sig %d (%s)\", sig, strsignal(sig)));\n\n    if (!ShouldTestSignal(sig)) {\n      continue;\n    }\n\n#if BUILDFLAG(IS_APPLE)\n    if (sig == SIGBUS\n#if defined(ARCH_CPU_ARM64)\n        || sig == SIGILL || sig == SIGSEGV\n#endif  // defined(ARCH_CPU_ARM64)\n       ) {\n      // Signal handlers can’t distinguish between these signals arising out of\n      // hardware faults and raised asynchronously.\n      // Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that they\n      // come from hardware faults, but this test uses raise(), so the re-raise\n      // test must be skipped.\n      continue;\n    }\n#endif  // BUILDFLAG(IS_APPLE)\n\n    SignalsTest test(SignalsTest::TestType::kHandlerReraisesToDefault,\n                     SignalsTest::SignalSource::kRaise,\n                     sig,\n                     0);\n    test.SetExpectedChildTermination(Multiprocess::kTerminationSignal, sig);\n    test.Run();\n  }\n}\n\nTEST(Signals, Raise_HandlerReraisesToPrevious) {\n  for (int sig = 1; sig < NSIG; ++sig) {\n    SCOPED_TRACE(base::StringPrintf(\"sig %d (%s)\", sig, strsignal(sig)));\n\n    if (!ShouldTestSignal(sig)) {\n      continue;\n    }\n\n#if BUILDFLAG(IS_APPLE)\n    if (sig == SIGBUS\n#if defined(ARCH_CPU_ARM64)\n        || sig == SIGILL || sig == SIGSEGV\n#endif  // defined(ARCH_CPU_ARM64)\n       ) {\n      // Signal handlers can’t distinguish between these signals arising out of\n      // hardware faults and raised asynchronously.\n      // Signals::RestoreHandlerAndReraiseSignalOnReturn() assumes that they\n      // come from hardware faults, but this test uses raise(), so the re-raise\n      // test must be skipped.\n      continue;\n    }\n#endif  // BUILDFLAG(IS_APPLE)\n\n    SignalsTest test(SignalsTest::TestType::kHandlerReraisesToPrevious,\n                     SignalsTest::SignalSource::kRaise,\n                     sig,\n                     0);\n    test.SetExpectedChildTermination(Multiprocess::kTerminationNormal,\n                                     SignalsTest::kExitingHandlerExitStatus);\n    test.Run();\n  }\n}\n\nTEST(Signals, IsCrashSignal) {\n  // Always crash signals.\n  EXPECT_TRUE(Signals::IsCrashSignal(SIGABRT));\n  EXPECT_TRUE(Signals::IsCrashSignal(SIGBUS));\n  EXPECT_TRUE(Signals::IsCrashSignal(SIGFPE));\n  EXPECT_TRUE(Signals::IsCrashSignal(SIGILL));\n  EXPECT_TRUE(Signals::IsCrashSignal(SIGQUIT));\n  EXPECT_TRUE(Signals::IsCrashSignal(SIGSEGV));\n  EXPECT_TRUE(Signals::IsCrashSignal(SIGSYS));\n  EXPECT_TRUE(Signals::IsCrashSignal(SIGTRAP));\n\n  // Always terminate signals.\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGALRM));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGHUP));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGINT));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGPIPE));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGPROF));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGTERM));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGUSR1));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGUSR2));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGVTALRM));\n\n  // Never crash or terminate signals.\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGCHLD));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGCONT));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGTSTP));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGTTIN));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGTTOU));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGURG));\n  EXPECT_FALSE(Signals::IsCrashSignal(SIGWINCH));\n}\n\nTEST(Signals, IsTerminateSignal) {\n  // Always terminate signals.\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGALRM));\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGHUP));\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGINT));\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGPIPE));\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGPROF));\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGTERM));\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGUSR1));\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGUSR2));\n  EXPECT_TRUE(Signals::IsTerminateSignal(SIGVTALRM));\n\n  // Always crash signals.\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGABRT));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGBUS));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGFPE));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGILL));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGQUIT));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGSEGV));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGSYS));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGTRAP));\n\n  // Never crash or terminate signals.\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGCHLD));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGCONT));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGTSTP));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGTTIN));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGTTOU));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGURG));\n  EXPECT_FALSE(Signals::IsTerminateSignal(SIGWINCH));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/spawn_subprocess.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/spawn_subprocess.h\"\n\n#include <errno.h>\n#include <spawn.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include \"base/check.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"util/posix/close_multiple.h\"\n\n#if BUILDFLAG(IS_ANDROID)\n#include <android/api-level.h>\n#endif\n\nextern char** environ;\n\nnamespace crashpad {\n\nnamespace {\n\n#if BUILDFLAG(IS_APPLE)\n\nclass PosixSpawnAttr {\n public:\n  PosixSpawnAttr() {\n    PCHECK((errno = posix_spawnattr_init(&attr_)) == 0)\n        << \"posix_spawnattr_init\";\n  }\n\n  PosixSpawnAttr(const PosixSpawnAttr&) = delete;\n  PosixSpawnAttr& operator=(const PosixSpawnAttr&) = delete;\n\n  ~PosixSpawnAttr() {\n    PCHECK((errno = posix_spawnattr_destroy(&attr_)) == 0)\n        << \"posix_spawnattr_destroy\";\n  }\n\n  void SetFlags(short flags) {\n    PCHECK((errno = posix_spawnattr_setflags(&attr_, flags)) == 0)\n        << \"posix_spawnattr_setflags\";\n  }\n\n  const posix_spawnattr_t* Get() const { return &attr_; }\n\n private:\n  posix_spawnattr_t attr_;\n};\n\nclass PosixSpawnFileActions {\n public:\n  PosixSpawnFileActions() {\n    PCHECK((errno = posix_spawn_file_actions_init(&file_actions_)) == 0)\n        << \"posix_spawn_file_actions_init\";\n  }\n\n  PosixSpawnFileActions(const PosixSpawnFileActions&) = delete;\n  PosixSpawnFileActions& operator=(const PosixSpawnFileActions&) = delete;\n\n  ~PosixSpawnFileActions() {\n    PCHECK((errno = posix_spawn_file_actions_destroy(&file_actions_)) == 0)\n        << \"posix_spawn_file_actions_destroy\";\n  }\n\n  void AddInheritedFileDescriptor(int fd) {\n    PCHECK((errno = posix_spawn_file_actions_addinherit_np(&file_actions_,\n                                                           fd)) == 0)\n        << \"posix_spawn_file_actions_addinherit_np\";\n  }\n\n  const posix_spawn_file_actions_t* Get() const { return &file_actions_; }\n\n private:\n  posix_spawn_file_actions_t file_actions_;\n};\n\n#endif\n\n}  // namespace\n\nbool SpawnSubprocess(const std::vector<std::string>& argv,\n                     const std::vector<std::string>* envp,\n                     int preserve_fd,\n                     bool use_path,\n                     void (*child_function)()) {\n  // argv_c contains const char* pointers and is terminated by nullptr. This is\n  // suitable for passing to posix_spawn*() and execv*(). Although argv_c is not\n  // used in the parent process, it must be built in the parent process because\n  // it’s unsafe to do so in the child or grandchild process.\n  std::vector<const char*> argv_c;\n  argv_c.reserve(argv.size() + 1);\n  for (const std::string& argument : argv) {\n    argv_c.push_back(argument.c_str());\n  }\n  argv_c.push_back(nullptr);\n\n  std::vector<const char*> envp_c;\n  if (envp) {\n    envp_c.reserve(envp->size() + 1);\n    for (const std::string& variable : *envp) {\n      envp_c.push_back(variable.c_str());\n    }\n    envp_c.push_back(nullptr);\n  }\n\n  // The three processes involved are parent, child, and grandchild. The child\n  // exits immediately after spawning the grandchild, so the grandchild becomes\n  // an orphan and its parent process ID becomes 1. This relieves the parent and\n  // child of the responsibility to reap the grandchild with waitpid() or\n  // similar. The grandchild is expected to outlive the parent process, so the\n  // parent shouldn’t be concerned with reaping it. This approach means that\n  // accidental early termination of the handler process will not result in a\n  // zombie process.\n  pid_t pid = fork();\n  if (pid < 0) {\n    PLOG(ERROR) << \"fork\";\n    return false;\n  }\n\n  if (pid == 0) {\n    // Child process.\n\n    if (child_function) {\n      child_function();\n    }\n\n    // Call setsid(), creating a new process group and a new session, both led\n    // by this process. The new process group has no controlling terminal. This\n    // disconnects it from signals generated by the parent process’ terminal.\n    //\n    // setsid() is done in the child instead of the grandchild so that the\n    // grandchild will not be a session leader. If it were a session leader, an\n    // accidental open() of a terminal device without O_NOCTTY would make that\n    // terminal the controlling terminal.\n    //\n    // It’s not desirable for the grandchild to have a controlling terminal. The\n    // grandchild manages its own lifetime, such as by monitoring clients on its\n    // own and exiting when it loses all clients and when it deems it\n    // appropraite to do so. It may serve clients in different process groups or\n    // sessions than its original client, and receiving signals intended for its\n    // original client’s process group could be harmful in that case.\n    PCHECK(setsid() != -1) << \"setsid\";\n\n    // &argv_c[0] is a pointer to a pointer to const char data, but because of\n    // how C (not C++) works, posix_spawn*() and execv*() want a pointer to\n    // a const pointer to char data. They modify neither the data nor the\n    // pointers, so the const_cast is safe.\n    char* const* argv_for_spawn = const_cast<char* const*>(argv_c.data());\n\n    // This cast is safe for the same reason that the argv_for_spawn cast is.\n    char* const* envp_for_spawn =\n        envp ? const_cast<char* const*>(envp_c.data()) : environ;\n\n#if BUILDFLAG(IS_ANDROID) && __ANDROID_API__ < 28\n    pid = fork();\n    if (pid < 0) {\n      PLOG(FATAL) << \"fork\";\n    }\n\n    if (pid > 0) {\n      // Child process.\n\n      // _exit() instead of exit(), because fork() was called.\n      _exit(EXIT_SUCCESS);\n    }\n\n    // Grandchild process.\n\n    CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);\n\n    auto execve_fp = use_path ? execvpe : execve;\n    execve_fp(argv_for_spawn[0], argv_for_spawn, envp_for_spawn);\n    PLOG(FATAL) << (use_path ? \"execvpe\" : \"execve\") << \" \"\n                << argv_for_spawn[0];\n#else\n#if BUILDFLAG(IS_APPLE)\n    PosixSpawnAttr attr;\n    attr.SetFlags(POSIX_SPAWN_CLOEXEC_DEFAULT);\n\n    PosixSpawnFileActions file_actions;\n    for (int fd = 0; fd <= STDERR_FILENO; ++fd) {\n      file_actions.AddInheritedFileDescriptor(fd);\n    }\n    file_actions.AddInheritedFileDescriptor(preserve_fd);\n\n    const posix_spawnattr_t* attr_p = attr.Get();\n    const posix_spawn_file_actions_t* file_actions_p = file_actions.Get();\n#else\n    CloseMultipleNowOrOnExec(STDERR_FILENO + 1, preserve_fd);\n\n    const posix_spawnattr_t* attr_p = nullptr;\n    const posix_spawn_file_actions_t* file_actions_p = nullptr;\n#endif\n\n    auto posix_spawn_fp = use_path ? posix_spawnp : posix_spawn;\n    if ((errno = posix_spawn_fp(nullptr,\n                                argv_for_spawn[0],\n                                file_actions_p,\n                                attr_p,\n                                argv_for_spawn,\n                                envp_for_spawn)) != 0) {\n      PLOG(FATAL) << (use_path ? \"posix_spawnp\" : \"posix_spawn\") << \" \"\n                  << argv_for_spawn[0];\n    }\n\n    // _exit() instead of exit(), because fork() was called.\n    _exit(EXIT_SUCCESS);\n#endif\n  }\n\n  // waitpid() for the child, so that it does not become a zombie process. The\n  // child normally exits quickly.\n  //\n  // Failures from this point on may result in the accumulation of a zombie, but\n  // should not be considered fatal. Log only warnings, but don’t treat these\n  // failures as a failure of the function overall.\n  int status;\n  pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0));\n  if (wait_pid == -1) {\n    PLOG(WARNING) << \"waitpid\";\n    return true;\n  }\n  DCHECK_EQ(wait_pid, pid);\n\n  if (WIFSIGNALED(status)) {\n    int sig = WTERMSIG(status);\n    LOG(WARNING) << base::StringPrintf(\n        \"intermediate process terminated by signal %d (%s)%s\",\n        sig,\n        strsignal(sig),\n        WCOREDUMP(status) ? \" (core dumped)\" : \"\");\n  } else if (!WIFEXITED(status)) {\n    LOG(WARNING) << base::StringPrintf(\n        \"intermediate process: unknown termination 0x%x\", status);\n  } else if (WEXITSTATUS(status) != EXIT_SUCCESS) {\n    LOG(WARNING) << \"intermediate process exited with code \"\n                 << WEXITSTATUS(status);\n  }\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/spawn_subprocess.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_\n#define CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_\n\n#include <string>\n#include <vector>\n\nnamespace crashpad {\n\n//! \\brief Spawns a subprocess.\n//!\n//! A grandchild process will be started through the\n//! `fork()`-and-`posix_spawn()` pattern where supported, and\n//! double-`fork()`-and-`execv()` pattern elsewhere. This allows the grandchild\n//! to fully disassociate from the parent. The grandchild will not be a member\n//! of the parent’s process group or session and will not have a controlling\n//! terminal, providing isolation from signals not intended for it. The\n//! grandchild’s parent process, in terms of the process tree hierarchy, will be\n//! the process with process ID 1, relieving any other process of the\n//! responsibility to reap it via `waitpid()`. Aside from the three file\n//! descriptors associated with the standard input/output streams and any file\n//! descriptor passed in \\a preserve_fd, the grandchild will not inherit any\n//! file descriptors from the parent process.\n//!\n//! \\param[in] argv The argument vector to start the grandchild process with.\n//!     `argv[0]` is used as the path to the executable.\n//! \\param[in] envp A vector of environment variables of the form `var=value` to\n//!     be passed to the spawned process. If this value is `nullptr`, the\n//!     current environment is used.\n//! \\param[in] preserve_fd A file descriptor to be inherited by the grandchild\n//!     process. This file descriptor is inherited in addition to the three file\n//!     descriptors associated with the standard input/output streams. Use `-1`\n//!     if no additional file descriptors are to be inherited.\n//! \\param[in] use_path Whether to consult the `PATH` environment variable when\n//!     requested to start an executable at a non-absolute path.\n//! \\param[in] child_function If not `nullptr`, this function will be called in\n//!     the intermediate child process. Take note that this function will run in\n//!     the context of a forked process, and must be safe for that purpose.\n//!\n//! \\return `true` on success, and `false` on failure with a message logged.\n//!     Only failures that occur in the parent process that indicate a definite\n//!     failure to start the the grandchild are reported in the return value.\n//!     Failures in the intermediate child or grandchild processes cannot be\n//!     reported in the return value, and are addressed by logging a message and\n//!     terminating. The caller assumes the responsibility for detecting such\n//!     failures, for example, by observing a failure to perform a successful\n//!     handshake with the grandchild process.\nbool SpawnSubprocess(const std::vector<std::string>& argv,\n                     const std::vector<std::string>* envp,\n                     int preserve_fd,\n                     bool use_path,\n                     void (*child_function)());\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_SPAWN_SUBPROCESS_H_\n"
  },
  {
    "path": "util/posix/symbolic_constants_posix.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/symbolic_constants_posix.h\"\n\n#include <signal.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <string_view>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/implicit_cast.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n\nnamespace {\n\nconstexpr const char* kSignalNames[] = {\n    nullptr,\n\n#if BUILDFLAG(IS_APPLE)\n    // sed -Ene\n    // 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/\n    // \"\\1\",/p'\n    //     /usr/include/sys/signal.h\n    // and fix up by removing the entry for SIGPOLL.\n    \"HUP\",\n    \"INT\",\n    \"QUIT\",\n    \"ILL\",\n    \"TRAP\",\n    \"ABRT\",\n    \"EMT\",\n    \"FPE\",\n    \"KILL\",\n    \"BUS\",\n    \"SEGV\",\n    \"SYS\",\n    \"PIPE\",\n    \"ALRM\",\n    \"TERM\",\n    \"URG\",\n    \"STOP\",\n    \"TSTP\",\n    \"CONT\",\n    \"CHLD\",\n    \"TTIN\",\n    \"TTOU\",\n    \"IO\",\n    \"XCPU\",\n    \"XFSZ\",\n    \"VTALRM\",\n    \"PROF\",\n    \"WINCH\",\n    \"INFO\",\n    \"USR1\",\n    \"USR2\",\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#if defined(ARCH_CPU_MIPS_FAMILY)\n    \"HUP\",\n    \"INT\",\n    \"QUIT\",\n    \"ILL\",\n    \"TRAP\",\n    \"ABRT\",\n    \"EMT\",\n    \"FPE\",\n    \"KILL\",\n    \"BUS\",\n    \"SEGV\",\n    \"SYS\",\n    \"PIPE\",\n    \"ALRM\",\n    \"TERM\",\n    \"USR1\",\n    \"USR2\",\n    \"CHLD\",\n    \"PWR\",\n    \"WINCH\",\n    \"URG\",\n    \"IO\",\n    \"STOP\",\n    \"TSTP\",\n    \"CONT\",\n    \"TTIN\",\n    \"TTOU\",\n    \"VTALRM\",\n    \"PROF\",\n    \"XCPU\",\n    \"XFSZ\",\n#else\n    // sed -Ene 's/^#define[[:space:]]SIG([[:alnum:]]+)[[:space:]]+[[:digit:]]{1,2}([[:space:]]|$).*/    \"\\1\",/p'\n    //     /usr/include/asm-generic/signal.h\n    // and fix up by removing SIGIOT, SIGLOST, SIGUNUSED, and SIGRTMIN.\n    \"HUP\",\n    \"INT\",\n    \"QUIT\",\n    \"ILL\",\n    \"TRAP\",\n    \"ABRT\",\n    \"BUS\",\n    \"FPE\",\n    \"KILL\",\n    \"USR1\",\n    \"SEGV\",\n    \"USR2\",\n    \"PIPE\",\n    \"ALRM\",\n    \"TERM\",\n    \"STKFLT\",\n    \"CHLD\",\n    \"CONT\",\n    \"STOP\",\n    \"TSTP\",\n    \"TTIN\",\n    \"TTOU\",\n    \"URG\",\n    \"XCPU\",\n    \"XFSZ\",\n    \"VTALRM\",\n    \"PROF\",\n    \"WINCH\",\n    \"IO\",\n    \"PWR\",\n    \"SYS\",\n#endif  // defined(ARCH_CPU_MIPS_FAMILY)\n#endif\n};\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n// NSIG is 64 to account for real-time signals.\nstatic_assert(std::size(kSignalNames) == 32, \"kSignalNames length\");\n#else\nstatic_assert(std::size(kSignalNames) == NSIG, \"kSignalNames length\");\n#endif\n\nconstexpr char kSigPrefix[] = \"SIG\";\n\n}  // namespace\n\nnamespace crashpad {\n\nstd::string SignalToString(int signal,\n                           SymbolicConstantToStringOptions options) {\n  const char* signal_name =\n      implicit_cast<size_t>(signal) < std::size(kSignalNames)\n          ? kSignalNames[signal]\n          : nullptr;\n  if (!signal_name) {\n    if (options & kUnknownIsNumeric) {\n      return base::StringPrintf(\"%d\", signal);\n    }\n    return std::string();\n  }\n\n  if (options & kUseShortName) {\n    return std::string(signal_name);\n  }\n  return base::StringPrintf(\"%s%s\", kSigPrefix, signal_name);\n}\n\nbool StringToSignal(std::string_view string,\n                    StringToSymbolicConstantOptions options,\n                    int* signal) {\n  if ((options & kAllowFullName) || (options & kAllowShortName)) {\n    bool can_match_full =\n        (options & kAllowFullName) &&\n        string.substr(0, strlen(kSigPrefix)).compare(kSigPrefix) == 0;\n    std::string_view short_string =\n        can_match_full ? string.substr(strlen(kSigPrefix)) : string;\n    for (int index = 0; index < implicit_cast<int>(std::size(kSignalNames));\n         ++index) {\n      const char* signal_name = kSignalNames[index];\n      if (!signal_name) {\n        continue;\n      }\n      if (can_match_full && short_string.compare(signal_name) == 0) {\n        *signal = index;\n        return true;\n      }\n      if ((options & kAllowShortName) && string.compare(signal_name) == 0) {\n        *signal = index;\n        return true;\n      }\n    }\n  }\n\n  if (options & kAllowNumber) {\n    return StringToNumber(std::string(string.data(), string.length()), signal);\n  }\n\n  return false;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/posix/symbolic_constants_posix.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_\n#define CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_\n\n#include <string>\n#include <string_view>\n\n#include \"util/misc/symbolic_constants_common.h\"\n\nnamespace crashpad {\n\n//! \\brief Converts a POSIX signal value to a textual representation.\n//!\n//! \\param[in] signal The signal value to convert.\n//! \\param[in] options Options affecting the conversion. ::kUseOr is ignored.\n//!     For ::kUnknownIsNumeric, the format is `\"%d\"`.\n//!\n//! \\return The converted string.\nstd::string SignalToString(int signal, SymbolicConstantToStringOptions options);\n\n//! \\brief Converts a string to its corresponding POSIX signal value.\n//!\n//! \\param[in] string The string to convert.\n//! \\param[in] options Options affecting the conversion. ::kAllowOr is ignored.\n//! \\param[out] signal The converted POSIX signal value.\n//!\n//! \\return `true` on success, `false` if \\a string could not be converted as\n//!     requested.\nbool StringToSignal(std::string_view string,\n                    StringToSymbolicConstantOptions options,\n                    int* signal);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_POSIX_SYMBOLIC_CONSTANTS_POSIX_H_\n"
  },
  {
    "path": "util/posix/symbolic_constants_posix_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/posix/symbolic_constants_posix.h\"\n\n#include <signal.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <string_view>\n\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\n#define NUL_TEST_DATA(string) \\\n  { string, std::size(string) - 1 }\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr struct {\n  int signal;\n  const char* full_name;\n  const char* short_name;\n} kSignalTestData[] = {\n    {SIGABRT, \"SIGABRT\", \"ABRT\"},    {SIGALRM, \"SIGALRM\", \"ALRM\"},\n    {SIGBUS, \"SIGBUS\", \"BUS\"},       {SIGCHLD, \"SIGCHLD\", \"CHLD\"},\n    {SIGCONT, \"SIGCONT\", \"CONT\"},    {SIGFPE, \"SIGFPE\", \"FPE\"},\n    {SIGHUP, \"SIGHUP\", \"HUP\"},       {SIGILL, \"SIGILL\", \"ILL\"},\n    {SIGINT, \"SIGINT\", \"INT\"},       {SIGIO, \"SIGIO\", \"IO\"},\n    {SIGKILL, \"SIGKILL\", \"KILL\"},    {SIGPIPE, \"SIGPIPE\", \"PIPE\"},\n    {SIGPROF, \"SIGPROF\", \"PROF\"},    {SIGQUIT, \"SIGQUIT\", \"QUIT\"},\n    {SIGSEGV, \"SIGSEGV\", \"SEGV\"},    {SIGSTOP, \"SIGSTOP\", \"STOP\"},\n    {SIGSYS, \"SIGSYS\", \"SYS\"},       {SIGTERM, \"SIGTERM\", \"TERM\"},\n    {SIGTRAP, \"SIGTRAP\", \"TRAP\"},    {SIGTSTP, \"SIGTSTP\", \"TSTP\"},\n    {SIGTTIN, \"SIGTTIN\", \"TTIN\"},    {SIGTTOU, \"SIGTTOU\", \"TTOU\"},\n    {SIGURG, \"SIGURG\", \"URG\"},       {SIGUSR1, \"SIGUSR1\", \"USR1\"},\n    {SIGUSR2, \"SIGUSR2\", \"USR2\"},    {SIGVTALRM, \"SIGVTALRM\", \"VTALRM\"},\n    {SIGWINCH, \"SIGWINCH\", \"WINCH\"}, {SIGXCPU, \"SIGXCPU\", \"XCPU\"},\n#if BUILDFLAG(IS_APPLE)\n    {SIGEMT, \"SIGEMT\", \"EMT\"},       {SIGINFO, \"SIGINFO\", \"INFO\"},\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n    {SIGPWR, \"SIGPWR\", \"PWR\"},\n#if !defined(ARCH_CPU_MIPS_FAMILY)\n    {SIGSTKFLT, \"SIGSTKFLT\", \"STKFLT\"},\n#endif\n#endif\n};\n\n// If |expect| is nullptr, the conversion is expected to fail. If |expect| is\n// empty, the conversion is expected to succeed, but the precise returned string\n// value is unknown. Otherwise, the conversion is expected to succeed, and\n// |expect| contains the precise expected string value to be returned.\n//\n// Only set kUseFullName or kUseShortName when calling this. Other options are\n// exercised directly by this function.\nvoid TestSignalToStringOnce(int value,\n                            const char* expect,\n                            SymbolicConstantToStringOptions options) {\n  std::string actual = SignalToString(value, options | kUnknownIsEmpty);\n  std::string actual_numeric =\n      SignalToString(value, options | kUnknownIsNumeric);\n  if (expect) {\n    if (expect[0] == '\\0') {\n      EXPECT_FALSE(actual.empty()) << \"signal \" << value;\n    } else {\n      EXPECT_EQ(actual, expect) << \"signal \" << value;\n    }\n    EXPECT_EQ(actual_numeric, actual) << \"signal \" << value;\n  } else {\n    EXPECT_TRUE(actual.empty()) << \"signal \" << value << \", actual \" << actual;\n    EXPECT_FALSE(actual_numeric.empty())\n        << \"signal \" << value << \", actual_numeric \" << actual_numeric;\n  }\n}\n\nvoid TestSignalToString(int value,\n                        const char* expect_full,\n                        const char* expect_short) {\n  {\n    SCOPED_TRACE(\"full_name\");\n    TestSignalToStringOnce(value, expect_full, kUseFullName);\n  }\n\n  {\n    SCOPED_TRACE(\"short_name\");\n    TestSignalToStringOnce(value, expect_short, kUseShortName);\n  }\n}\n\nTEST(SymbolicConstantsPOSIX, SignalToString) {\n  for (size_t index = 0; index < std::size(kSignalTestData); ++index) {\n    SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n    TestSignalToString(kSignalTestData[index].signal,\n                       kSignalTestData[index].full_name,\n                       kSignalTestData[index].short_name);\n  }\n\n#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n  // NSIG is 64 to account for real-time signals.\n  constexpr int kSignalCount = 32;\n#else\n  constexpr int kSignalCount = NSIG;\n#endif\n\n  for (int signal = 0; signal < kSignalCount + 8; ++signal) {\n    SCOPED_TRACE(base::StringPrintf(\"signal %d\", signal));\n    if (signal > 0 && signal < kSignalCount) {\n      TestSignalToString(signal, \"\", \"\");\n    } else {\n      TestSignalToString(signal, nullptr, nullptr);\n    }\n  }\n}\n\nvoid TestStringToSignal(std::string_view string,\n                        StringToSymbolicConstantOptions options,\n                        bool expect_result,\n                        int expect_value) {\n  int actual_value;\n  bool actual_result = StringToSignal(string, options, &actual_value);\n  if (expect_result) {\n    EXPECT_TRUE(actual_result) << \"string \" << string << \", options \" << options\n                               << \", signal \" << expect_value;\n    if (actual_result) {\n      EXPECT_EQ(actual_value, expect_value) << \"string \" << string\n                                            << \", options \" << options;\n    }\n  } else {\n    EXPECT_FALSE(actual_result) << \"string \" << string << \", options \"\n                                << options << \", signal \" << actual_value;\n  }\n}\n\nTEST(SymbolicConstantsPOSIX, StringToSignal) {\n  static constexpr StringToSymbolicConstantOptions kOptions[] = {\n      0,\n      kAllowFullName,\n      kAllowShortName,\n      kAllowFullName | kAllowShortName,\n      kAllowNumber,\n      kAllowFullName | kAllowNumber,\n      kAllowShortName | kAllowNumber,\n      kAllowFullName | kAllowShortName | kAllowNumber,\n  };\n\n  for (size_t option_index = 0; option_index < std::size(kOptions);\n       ++option_index) {\n    SCOPED_TRACE(base::StringPrintf(\"option_index %zu\", option_index));\n    StringToSymbolicConstantOptions options = kOptions[option_index];\n    for (size_t index = 0; index < std::size(kSignalTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      int signal = kSignalTestData[index].signal;\n      {\n        SCOPED_TRACE(\"full_name\");\n        TestStringToSignal(kSignalTestData[index].full_name,\n                           options,\n                           options & kAllowFullName,\n                           signal);\n      }\n      {\n        SCOPED_TRACE(\"short_name\");\n        TestStringToSignal(kSignalTestData[index].short_name,\n                           options,\n                           options & kAllowShortName,\n                           signal);\n      }\n      {\n        SCOPED_TRACE(\"number\");\n        std::string number_string = base::StringPrintf(\"%d\", signal);\n        TestStringToSignal(\n            number_string, options, options & kAllowNumber, signal);\n      }\n    }\n\n    static constexpr const char* kNegativeTestData[] = {\n        \"SIGHUP \",\n        \" SIGINT\",\n        \"QUIT \",\n        \" ILL\",\n        \"SIGSIGTRAP\",\n        \"SIGABRTRON\",\n        \"FPES\",\n        \"SIGGARBAGE\",\n        \"random\",\n        \"\",\n    };\n\n    for (size_t index = 0; index < std::size(kNegativeTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      TestStringToSignal(kNegativeTestData[index], options, false, 0);\n    }\n\n    static constexpr struct {\n      const char* string;\n      size_t length;\n    } kNULTestData[] = {\n        NUL_TEST_DATA(\"\\0SIGBUS\"),\n        NUL_TEST_DATA(\"SIG\\0BUS\"),\n        NUL_TEST_DATA(\"SIGB\\0US\"),\n        NUL_TEST_DATA(\"SIGBUS\\0\"),\n        NUL_TEST_DATA(\"\\0BUS\"),\n        NUL_TEST_DATA(\"BUS\\0\"),\n        NUL_TEST_DATA(\"B\\0US\"),\n        NUL_TEST_DATA(\"\\0002\"),\n        NUL_TEST_DATA(\"2\\0\"),\n        NUL_TEST_DATA(\"1\\0002\"),\n    };\n\n    for (size_t index = 0; index < std::size(kNULTestData); ++index) {\n      SCOPED_TRACE(base::StringPrintf(\"index %zu\", index));\n      std::string_view string(kNULTestData[index].string,\n                              kNULTestData[index].length);\n      TestStringToSignal(string, options, false, 0);\n    }\n  }\n\n  // Ensure that a NUL is not required at the end of the string.\n  {\n    SCOPED_TRACE(\"trailing_NUL_full\");\n    TestStringToSignal(\n        std::string_view(\"SIGBUST\", 6), kAllowFullName, true, SIGBUS);\n  }\n  {\n    SCOPED_TRACE(\"trailing_NUL_short\");\n    TestStringToSignal(\n        std::string_view(\"BUST\", 3), kAllowShortName, true, SIGBUS);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_id.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_ID_H_\n#define CRASHPAD_UTIL_PROCESS_PROCESS_ID_H_\n\n#include <type_traits>\n\n#include \"base/format_macros.h\"\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include <sys/types.h>\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#elif BUILDFLAG(IS_FUCHSIA)\n#include <zircon/types.h>\n#endif\n\nnamespace crashpad {\n\n#if BUILDFLAG(IS_POSIX) || DOXYGEN\n//! \\brief Alias for platform-specific type to represent a process.\nusing ProcessID = pid_t;\nconstexpr ProcessID kInvalidProcessID = -1;\nstatic_assert(std::is_same<ProcessID, int>::value, \"Port.\");\n#define PRI_PROCESS_ID \"d\"\n#elif BUILDFLAG(IS_WIN)\nusing ProcessID = DWORD;\nconstexpr ProcessID kInvalidProcessID = 0;\n#define PRI_PROCESS_ID \"lu\"\n#elif BUILDFLAG(IS_FUCHSIA)\nusing ProcessID = zx_koid_t;\nconstexpr ProcessID kInvalidProcessID = ZX_KOID_INVALID;\nstatic_assert(std::is_same<ProcessID, int64_t>::value, \"Port.\");\n#define PRI_PROCESS_ID PRId64\n#else\n#error Port.\n#endif\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_PROCESS_PROCESS_ID_H_\n"
  },
  {
    "path": "util/process/process_memory.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory.h\"\n\n#include <string.h>\n\n#include <algorithm>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"util/numeric/safe_assignment.h\"\n\nnamespace crashpad {\n\nbool ProcessMemory::Read(VMAddress address, VMSize size, void* buffer) const {\n  size_t local_size;\n  if (!AssignIfInRange(&local_size, size)) {\n    LOG(ERROR) << \"size \" << size << \" out of bounds for size_t\";\n    return false;\n  }\n\n  char* buffer_c = static_cast<char*>(buffer);\n  while (local_size > 0) {\n    ssize_t bytes_read = ReadUpTo(address, local_size, buffer_c);\n    if (bytes_read < 0) {\n      return false;\n    }\n    if (bytes_read == 0) {\n      LOG(ERROR) << \"short read\";\n      return false;\n    }\n    DCHECK_LE(static_cast<size_t>(bytes_read), local_size);\n    local_size -= bytes_read;\n    address += bytes_read;\n    buffer_c += bytes_read;\n  }\n  return true;\n}\n\nbool ProcessMemory::ReadCStringInternal(VMAddress address,\n                                        bool has_size,\n                                        VMSize size,\n                                        std::string* string) const {\n  size_t local_size;\n  if (!AssignIfInRange(&local_size, size)) {\n    LOG(ERROR) << \"size \" << size << \" out of bounds for size_t\";\n    return false;\n  }\n\n  string->clear();\n\n  char buffer[4096];\n  do {\n    size_t read_size;\n    if (has_size) {\n      read_size = std::min(sizeof(buffer), local_size);\n    } else {\n      read_size = sizeof(buffer);\n    }\n\n    ssize_t bytes_read = ReadUpTo(address, read_size, buffer);\n    if (bytes_read < 0) {\n      return false;\n    }\n    if (bytes_read == 0) {\n      break;\n    }\n\n    char* nul = static_cast<char*>(memchr(buffer, '\\0', bytes_read));\n    if (nul != nullptr) {\n      string->append(buffer, nul - buffer);\n      return true;\n    }\n    string->append(buffer, bytes_read);\n\n    address += bytes_read;\n    local_size -= bytes_read;\n  } while (!has_size || local_size > 0);\n\n  LOG(ERROR) << \"unterminated string\";\n  return false;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_H_\n#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_H_\n\n#include <sys/types.h>\n\n#include <string>\n\n#include \"build/build_config.h\"\n#include \"util/misc/address_types.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <basetsd.h>\ntypedef SSIZE_T ssize_t;\n#endif  // BUILDFLAG(IS_WIN)\n\nnamespace crashpad {\n\n//! \\brief Abstract base class for accessing the memory of another process.\n//!\n//! Implementations are platform-specific.\nclass ProcessMemory {\n public:\n  //! \\brief Copies memory from the target process into a caller-provided buffer\n  //!     in the current process.\n  //!\n  //! \\param[in] address The address, in the target process' address space, of\n  //!     the memory region to copy.\n  //! \\param[in] size The size, in bytes, of the memory region to copy.\n  //!     \\a buffer must be at least this size.\n  //! \\param[out] buffer The buffer into which the contents of the other\n  //!     process' memory will be copied.\n  //!\n  //! \\return `true` on success, with \\a buffer filled appropriately. `false` on\n  //!     failure, with a message logged.\n  bool Read(VMAddress address, VMSize size, void* buffer) const;\n\n  //! \\brief Reads a `NUL`-terminated C string from the target process into a\n  //!     string in the current process.\n  //!\n  //! The length of the string need not be known ahead of time. This method will\n  //! read contiguous memory until a `NUL` terminator is found.\n  //!\n  //! \\param[in] address The address, in the target process’s address space, of\n  //!     the string to copy.\n  //! \\param[out] string The string read from the other process.\n  //!\n  //! \\return `true` on success, with \\a string set appropriately. `false` on\n  //!     failure, with a message logged. Failures can occur, for example, when\n  //!     encountering unmapped or unreadable pages.\n  bool ReadCString(VMAddress address, std::string* string) const {\n    return ReadCStringInternal(address, false, 0, string);\n  }\n\n  //! \\brief Reads a `NUL`-terminated C string from the target process into a\n  //!     string in the current process.\n  //!\n  //! \\param[in] address The address, in the target process’s address space, of\n  //!     the string to copy.\n  //! \\param[in] size The maximum number of bytes to read. The string is\n  //!     required to be `NUL`-terminated within this many bytes.\n  //! \\param[out] string The string read from the other process.\n  //!\n  //! \\return `true` on success, with \\a string set appropriately. `false` on\n  //!     failure, with a message logged. Failures can occur, for example, when\n  //!     a `NUL` terminator is not found within \\a size bytes, or when\n  //!     encountering unmapped or unreadable pages.\n  bool ReadCStringSizeLimited(VMAddress address,\n                              VMSize size,\n                              std::string* string) const {\n    return ReadCStringInternal(address, true, size, string);\n  }\n\n  virtual ~ProcessMemory() = default;\n\n protected:\n  ProcessMemory() = default;\n\n private:\n  //! \\brief Copies memory from the target process into a caller-provided buffer\n  //!     in the current process, up to a maximum number of bytes.\n  //!\n  //! \\param[in] address The address, in the target process' address space, of\n  //!     the memory region to copy.\n  //! \\param[in] size The maximum size, in bytes, of the memory region to copy.\n  //!     \\a buffer must be at least this size.\n  //! \\param[out] buffer The buffer into which the contents of the other\n  //!     process' memory will be copied.\n  //!\n  //! \\return the number of bytes copied, 0 if there is no more data to read, or\n  //!     -1 on failure with a message logged.\n  virtual ssize_t ReadUpTo(VMAddress address,\n                           size_t size,\n                           void* buffer) const = 0;\n\n  //! \\brief Reads a `NUL`-terminated C string from the target process into a\n  //!     string in the current process.\n  //!\n  //! \\param[in] address The address, in the target process’s address space, of\n  //!     the string to copy.\n  //! \\param[in] has_size If true, this method will read \\a size bytes. If\n  //!     false, this method will ignore \\a size and instead read contiguous\n  //!     memory until a `NUL` terminator is found.\n  //! \\param[in] size If \\a has_size is true, the maximum number of bytes to\n  //!     read. The string is required to be `NUL`-terminated within this many\n  //!     bytes. Ignored if \\a has_size is false.\n  //! \\param[out] string The string read from the other process.\n  //!\n  //! \\return `true` on success, with \\a string set appropriately. `false` on\n  //!     failure, with a message logged. Failures can occur, for example, when\n  //!     encountering unmapped or unreadable pages.\n  virtual bool ReadCStringInternal(VMAddress address,\n                                   bool has_size,\n                                   VMSize size,\n                                   std::string* string) const;\n\n  // Allow ProcessMemorySanitized to call ReadUpTo.\n  friend class ProcessMemorySanitized;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_H_\n"
  },
  {
    "path": "util/process/process_memory_fuchsia.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_fuchsia.h\"\n\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/fuchsia/fuchsia_logging.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nProcessMemoryFuchsia::ProcessMemoryFuchsia()\n    : ProcessMemory(), process_(), initialized_() {}\n\nProcessMemoryFuchsia::~ProcessMemoryFuchsia() {}\n\nbool ProcessMemoryFuchsia::Initialize(const zx::unowned_process& process) {\n  return Initialize(*process);\n}\n\nbool ProcessMemoryFuchsia::Initialize(const zx::process& process) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  process_ = zx::unowned_process(process);\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nssize_t ProcessMemoryFuchsia::ReadUpTo(VMAddress address,\n                                       size_t size,\n                                       void* buffer) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()});\n\n  size_t actual;\n  zx_status_t status = process_->read_memory(address, buffer, size, &actual);\n\n  if (status != ZX_OK) {\n    ZX_LOG(ERROR, status) << \"zx_process_read_memory\";\n    return -1;\n  }\n\n  return actual;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_fuchsia.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_\n#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_\n\n#include <lib/zx/process.h>\n\n#include <string>\n\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\n\n//! \\brief Accesses the memory of another Fuchsia process.\nclass ProcessMemoryFuchsia final : public ProcessMemory {\n public:\n  ProcessMemoryFuchsia();\n\n  ProcessMemoryFuchsia(const ProcessMemoryFuchsia&) = delete;\n  ProcessMemoryFuchsia& operator=(const ProcessMemoryFuchsia&) = delete;\n\n  ~ProcessMemoryFuchsia();\n\n  //! \\brief Initializes this object to read the memory of a process by handle.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class.\n  //!\n  //! \\param[in] process The handle to the target process.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Initialize(const zx::process& process);\n  // TODO(wez): Remove this overload when zx::unowned_process allows implicit\n  // copy.\n  bool Initialize(const zx::unowned_process& process);\n\n private:\n  ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;\n\n  zx::unowned_process process_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_\n"
  },
  {
    "path": "util/process/process_memory_linux.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_linux.h\"\n\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n#include \"util/file/filesystem.h\"\n#include \"util/linux/ptrace_connection.h\"\n\nnamespace crashpad {\n\nProcessMemoryLinux::ProcessMemoryLinux(PtraceConnection* connection)\n    : ProcessMemory(), mem_fd_(), ignore_top_byte_(false) {\n#if defined(ARCH_CPU_ARM_FAMILY)\n  if (connection->Is64Bit()) {\n    ignore_top_byte_ = true;\n  }\n#endif  // ARCH_CPU_ARM_FAMILY\n\n  char path[32];\n  snprintf(path, sizeof(path), \"/proc/%d/mem\", connection->GetProcessID());\n  mem_fd_.reset(HANDLE_EINTR(open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC)));\n  if (mem_fd_.is_valid()) {\n    read_up_to_ = [this](VMAddress address, size_t size, void* buffer) {\n      ssize_t bytes_read =\n          HANDLE_EINTR(pread64(mem_fd_.get(), buffer, size, address));\n      if (bytes_read < 0) {\n        PLOG(ERROR) << \"pread64\";\n      }\n      return bytes_read;\n    };\n    return;\n  }\n\n  read_up_to_ = std::bind(&PtraceConnection::ReadUpTo,\n                          connection,\n                          std::placeholders::_1,\n                          std::placeholders::_2,\n                          std::placeholders::_3);\n}\n\nProcessMemoryLinux::~ProcessMemoryLinux() {}\n\nVMAddress ProcessMemoryLinux::PointerToAddress(VMAddress address) const {\n  return ignore_top_byte_ ? address & 0x00ffffffffffffff : address;\n}\n\nssize_t ProcessMemoryLinux::ReadUpTo(VMAddress address,\n                                     size_t size,\n                                     void* buffer) const {\n  DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()});\n  return read_up_to_(PointerToAddress(address), size, buffer);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_linux.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_LINUX_H_\n#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_LINUX_H_\n\n#include <sys/types.h>\n\n#include <functional>\n#include <string>\n\n#include \"base/files/scoped_file.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\n\nclass PtraceConnection;\n\n//! \\brief Accesses the memory of another Linux process.\nclass ProcessMemoryLinux final : public ProcessMemory {\n public:\n  explicit ProcessMemoryLinux(PtraceConnection* connection);\n\n  ProcessMemoryLinux(const ProcessMemoryLinux&) = delete;\n  ProcessMemoryLinux& operator=(const ProcessMemoryLinux&) = delete;\n\n  ~ProcessMemoryLinux();\n\n  //! \\brief Returns the input pointer with any non-addressing bits, such as\n  //!     tags removed.\n  VMAddress PointerToAddress(VMAddress address) const;\n\n private:\n  ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;\n\n  std::function<ssize_t(VMAddress, size_t, void*)> read_up_to_;\n  base::ScopedFD mem_fd_;\n  bool ignore_top_byte_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_LINUX_H_\n"
  },
  {
    "path": "util/process/process_memory_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_mac.h\"\n\n#include <mach/mach_vm.h>\n#include <string.h>\n\n#include <algorithm>\n\n#include \"base/apple/mach_logging.h\"\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"util/stdlib/strnlen.h\"\n\nnamespace crashpad {\n\nProcessMemoryMac::MappedMemory::~MappedMemory() {}\n\nbool ProcessMemoryMac::MappedMemory::ReadCString(size_t offset,\n                                                 std::string* string) const {\n  if (offset >= user_size_) {\n    LOG(WARNING) << \"offset out of range\";\n    return false;\n  }\n\n  const char* string_base = reinterpret_cast<const char*>(data_) + offset;\n  size_t max_length = user_size_ - offset;\n  size_t string_length = strnlen(string_base, max_length);\n  if (string_length == max_length) {\n    LOG(WARNING) << \"unterminated string\";\n    return false;\n  }\n\n  string->assign(string_base, string_length);\n  return true;\n}\n\nProcessMemoryMac::MappedMemory::MappedMemory(vm_address_t vm_address,\n                                             size_t vm_size,\n                                             size_t user_offset,\n                                             size_t user_size)\n    : vm_(vm_address, vm_size),\n      data_(reinterpret_cast<const void*>(vm_address + user_offset)),\n      user_size_(user_size) {\n  vm_address_t vm_end = vm_address + vm_size;\n  vm_address_t user_address = reinterpret_cast<vm_address_t>(data_);\n  vm_address_t user_end = user_address + user_size;\n  DCHECK_GE(user_address, vm_address);\n  DCHECK_LE(user_address, vm_end);\n  DCHECK_GE(user_end, vm_address);\n  DCHECK_LE(user_end, vm_end);\n}\n\nProcessMemoryMac::ProcessMemoryMac() : task_(TASK_NULL), initialized_() {}\n\nbool ProcessMemoryMac::Initialize(task_t task) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  task_ = task;\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nstd::unique_ptr<ProcessMemoryMac::MappedMemory> ProcessMemoryMac::ReadMapped(\n    mach_vm_address_t address,\n    size_t size) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  if (size == 0) {\n    return std::unique_ptr<MappedMemory>(new MappedMemory(0, 0, 0, 0));\n  }\n\n  mach_vm_address_t region_address = mach_vm_trunc_page(address);\n  mach_vm_size_t region_size =\n      mach_vm_round_page(address - region_address + size);\n\n  vm_offset_t region;\n  mach_msg_type_number_t region_count;\n  kern_return_t kr =\n      mach_vm_read(task_, region_address, region_size, &region, &region_count);\n  if (kr != KERN_SUCCESS) {\n    MACH_LOG(WARNING, kr) << base::StringPrintf(\n        \"mach_vm_read(0x%llx, 0x%llx)\", region_address, region_size);\n    return std::unique_ptr<MappedMemory>();\n  }\n  if (region_count != region_size) {\n    LOG(ERROR) << base::StringPrintf(\n        \"mach_vm_read() unexpected read: 0x%x != 0x%llx bytes\",\n        region_count,\n        region_size);\n    if (region_count)\n      vm_deallocate(mach_task_self(), region, region_count);\n    return std::unique_ptr<MappedMemory>();\n  }\n\n  return std::unique_ptr<MappedMemory>(\n      new MappedMemory(region, region_size, address - region_address, size));\n}\n\nssize_t ProcessMemoryMac::ReadUpTo(VMAddress address,\n                                   size_t size,\n                                   void* buffer) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  DCHECK_LE(size, (size_t)std::numeric_limits<ssize_t>::max());\n\n  std::unique_ptr<MappedMemory> memory = ReadMapped(address, size);\n  if (!memory) {\n    // If we can not read the entire mapping, try to perform a short read of the\n    // first page instead. This is necessary to support ReadCString().\n    size_t short_read = PAGE_SIZE - (address % PAGE_SIZE);\n    if (short_read >= size)\n      return -1;\n\n    memory = ReadMapped(address, short_read);\n    if (!memory)\n      return -1;\n\n    size = short_read;\n  }\n\n  memcpy(buffer, memory->data(), size);\n  return static_cast<ssize_t>(size);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_mac.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_MAC_H_\n#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_MAC_H_\n\n#include <mach/mach.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <string>\n\n#include \"base/apple/scoped_mach_vm.h\"\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\n\n//! \\brief Accesses the memory of another Mach task.\nclass ProcessMemoryMac : public ProcessMemory {\n public:\n  //! \\brief A memory region mapped from another Mach task.\n  //!\n  //! The mapping is maintained until this object is destroyed.\n  class MappedMemory {\n   public:\n    MappedMemory(const MappedMemory&) = delete;\n    MappedMemory& operator=(const MappedMemory&) = delete;\n\n    ~MappedMemory();\n\n    //! \\brief Returns a pointer to the data requested by the user.\n    //!\n    //! This is the value of the \\a vm_address + \\a user_offset parameters\n    //! passed to the constructor, casted to `const void*`.\n    const void* data() const { return data_; }\n\n    //! \\brief Reads a `NUL`-terminated C string from the mapped region.\n    //!\n    //! This method will read contiguous memory until a `NUL` terminator is\n    //! found.\n    //!\n    //! \\param[in] offset The offset into data() of the string to be read.\n    //! \\param[out] string The string, whose contents begin at data() and\n    //!     continue up to a `NUL` terminator.\n    //!\n    //! \\return `true` on success, with \\a string set appropriately. If \\a\n    //!     offset is greater than or equal to the \\a user_size constructor\n    //!     parameter, or if no `NUL` terminator was found in data() after \\a\n    //!     offset, returns `false` with an appropriate warning logged.\n    bool ReadCString(size_t offset, std::string* string) const;\n\n   private:\n    //! \\brief Creates an object that owns a memory region mapped from another\n    //!     Mach task.\n    //!\n    //! \\param[in] vm_address The address in this process’ address space where\n    //!     the mapping begins. This must be page-aligned.\n    //! \\param[in] vm_size The total size of the mapping that begins at \\a\n    //!     vm_address. This must be page-aligned.\n    //! \\param[in] user_offset The offset into the mapped region where the data\n    //!     requested by the user begins. This accounts for the fact that a\n    //!     mapping must be page-aligned but the user data may not be. This\n    //!     parameter must be equal to or less than \\a vm_size.\n    //! \\param[in] user_size The size of the data requested by the user. This\n    //!     parameter can be used to compute the end address of user data, which\n    //!     must be within the mapped region.\n    MappedMemory(vm_address_t vm_address,\n                 size_t vm_size,\n                 size_t user_offset,\n                 size_t user_size);\n\n    base::apple::ScopedMachVM vm_;\n    const void* data_;\n    size_t user_size_;\n\n    // The outer class needs to be able to call this class’ private constructor.\n    friend class ProcessMemoryMac;\n  };\n\n  ProcessMemoryMac();\n\n  ProcessMemoryMac(const ProcessMemoryMac&) = delete;\n  ProcessMemoryMac& operator=(const ProcessMemoryMac&) = delete;\n\n  ~ProcessMemoryMac() {}\n\n  //! \\brief Initializes this object to read the memory of a task with the\n  //!     provided task port.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class.\n  //!\n  //! \\param[in] task A send right to the target task's task port. This object\n  //!     does not take ownership of the send right.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Initialize(task_t task);\n\n  //! \\brief Maps memory from the target task into the current task.\n  //!\n  //! This interface is an alternative to Read() that does not require the\n  //! caller to provide a buffer to fill. This avoids copying memory, which can\n  //! offer a performance improvement.\n  //!\n  //! \\param[in] address The address, in the target task’s address space, of the\n  //!     memory region to map.\n  //! \\param[in] size The size, in bytes, of the memory region to map.\n  //!\n  //! \\return On success, a MappedMemory object that provides access to the data\n  //!     requested. On faliure, `nullptr`, with a warning logged. Failures can\n  //!     occur, for example, when encountering unmapped or unreadable pages.\n  std::unique_ptr<MappedMemory> ReadMapped(mach_vm_address_t address,\n                                           size_t size) const;\n\n private:\n  ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;\n\n  task_t task_;  // weak\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_MAC_H_\n"
  },
  {
    "path": "util/process/process_memory_mac_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_mac.h\"\n\n#include <mach/mach.h>\n#include <string.h>\n\n#include <algorithm>\n#include <memory>\n#include <string>\n\n#include \"base/apple/scoped_mach_port.h\"\n#include \"base/apple/scoped_mach_vm.h\"\n#include \"base/containers/heap_array.h\"\n#include \"gtest/gtest.h\"\n#include \"test/mac/mach_errors.h\"\n#include \"util/misc/from_pointer_cast.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ProcessMemoryMac, ReadMappedSelf) {\n  vm_address_t address = 0;\n  const vm_size_t kSize = 4 * PAGE_SIZE;\n  kern_return_t kr =\n      vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_allocate\");\n  base::apple::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));\n\n  char* region = reinterpret_cast<char*>(address);\n  for (size_t index = 0; index < kSize; ++index) {\n    region[index] = (index % 256) ^ ((index >> 8) % 256);\n  }\n\n  ProcessMemoryMac memory;\n  ASSERT_TRUE(memory.Initialize(mach_task_self()));\n\n  std::string result(kSize, '\\0');\n  std::unique_ptr<ProcessMemoryMac::MappedMemory> mapped;\n\n  // Ensure that the entire region can be read.\n  ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize)));\n  EXPECT_EQ(memcmp(region, mapped->data(), kSize), 0);\n\n  // Ensure that a read of length 0 succeeds and doesn't touch the result.\n  result.assign(kSize, '\\0');\n  std::string zeroes = result;\n  ASSERT_TRUE((mapped = memory.ReadMapped(address, 0)));\n  EXPECT_EQ(result, zeroes);\n\n  // Ensure that a read starting at an unaligned address works.\n  ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 1)));\n  EXPECT_EQ(memcmp(region + 1, mapped->data(), kSize - 1), 0);\n\n  // Ensure that a read ending at an unaligned address works.\n  ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize - 1)));\n  EXPECT_EQ(memcmp(region, mapped->data(), kSize - 1), 0);\n\n  // Ensure that a read starting and ending at unaligned addresses works.\n  ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 2)));\n  EXPECT_EQ(memcmp(region + 1, mapped->data(), kSize - 2), 0);\n\n  // Ensure that a read of exactly one page works.\n  ASSERT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE, PAGE_SIZE)));\n  EXPECT_EQ(memcmp(region + PAGE_SIZE, mapped->data(), PAGE_SIZE), 0);\n\n  // Ensure that a read of a single byte works.\n  ASSERT_TRUE((mapped = memory.ReadMapped(address + 2, 1)));\n  EXPECT_EQ(reinterpret_cast<const char*>(mapped->data())[0], region[2]);\n\n  // Ensure that a read of length zero works and doesn't touch the data.\n  result[0] = 'M';\n  ASSERT_TRUE((mapped = memory.ReadMapped(address + 3, 0)));\n  EXPECT_EQ(result[0], 'M');\n}\n\nTEST(ProcessMemoryMac, ReadSelfUnmapped) {\n  vm_address_t address = 0;\n  const vm_size_t kSize = 2 * PAGE_SIZE;\n  kern_return_t kr =\n      vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_allocate\");\n  base::apple::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));\n\n  char* region = reinterpret_cast<char*>(address);\n  for (size_t index = 0; index < kSize; ++index) {\n    // Don't include any NUL bytes, because ReadCString stops when it encounters\n    // a NUL.\n    region[index] = (index % 255) + 1;\n  }\n\n  kr = vm_protect(\n      mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_protect\");\n\n  ProcessMemoryMac memory;\n  ASSERT_TRUE(memory.Initialize(mach_task_self()));\n  std::string result(kSize, '\\0');\n\n  EXPECT_FALSE(memory.Read(address, kSize, &result[0]));\n  EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0]));\n  EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0]));\n  EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));\n  EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));\n  EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));\n\n  // Do the same thing with the ReadMapped() interface.\n  std::unique_ptr<ProcessMemoryMac::MappedMemory> mapped;\n  EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));\n  EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));\n  EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));\n  EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));\n  EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));\n  EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));\n\n  // Repeat the test with an unmapped page instead of an unreadable one. This\n  // portion of the test may be flaky in the presence of other threads, if\n  // another thread maps something in the region that is deallocated here.\n  kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_deallocate\");\n  vm_owner.reset(address, PAGE_SIZE);\n\n  EXPECT_FALSE(memory.Read(address, kSize, &result[0]));\n  EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0]));\n  EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0]));\n  EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));\n  EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));\n  EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));\n\n  // Do the same thing with the ReadMapped() interface.\n  EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));\n  EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));\n  EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));\n  EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));\n  EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));\n  EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));\n}\n\nTEST(ProcessMemoryMac, ReadCStringSelfUnmapped) {\n  vm_address_t address = 0;\n  const vm_size_t kSize = 2 * PAGE_SIZE;\n  kern_return_t kr =\n      vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_allocate\");\n  base::apple::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize));\n\n  char* region = reinterpret_cast<char*>(address);\n  for (size_t index = 0; index < kSize; ++index) {\n    // Don't include any NUL bytes, because ReadCString stops when it encounters\n    // a NUL.\n    region[index] = (index % 255) + 1;\n  }\n\n  kr = vm_protect(\n      mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_protect\");\n\n  ProcessMemoryMac memory;\n  ASSERT_TRUE(memory.Initialize(mach_task_self()));\n  std::string result;\n  EXPECT_FALSE(memory.ReadCString(address, &result));\n\n  // Make sure that if the string is NUL-terminated within the mapped memory\n  // region, it can be read properly.\n  char terminator_or_not = '\\0';\n  std::swap(region[PAGE_SIZE - 1], terminator_or_not);\n  ASSERT_TRUE(memory.ReadCString(address, &result));\n  EXPECT_FALSE(result.empty());\n  EXPECT_EQ(result.size(), PAGE_SIZE - 1u);\n  EXPECT_EQ(result, region);\n\n  // Repeat the test with an unmapped page instead of an unreadable one. This\n  // portion of the test may be flaky in the presence of other threads, if\n  // another thread maps something in the region that is deallocated here.\n  std::swap(region[PAGE_SIZE - 1], terminator_or_not);\n  kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE);\n  ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, \"vm_deallocate\");\n  vm_owner.reset(address, PAGE_SIZE);\n\n  EXPECT_FALSE(memory.ReadCString(address, &result));\n\n  // Clear the result before testing that the string can be read. This makes\n  // sure that the result is actually filled in, because it already contains the\n  // expected value from the tests above.\n  result.clear();\n  std::swap(region[PAGE_SIZE - 1], terminator_or_not);\n  ASSERT_TRUE(memory.ReadCString(address, &result));\n  EXPECT_FALSE(result.empty());\n  EXPECT_EQ(result.size(), PAGE_SIZE - 1u);\n  EXPECT_EQ(result, region);\n}\n\nbool IsAddressMapped(vm_address_t address) {\n  vm_address_t region_address = address;\n  vm_size_t region_size;\n  mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;\n  vm_region_basic_info_64 info;\n  mach_port_t object;\n  kern_return_t kr = vm_region_64(mach_task_self(),\n                                  &region_address,\n                                  &region_size,\n                                  VM_REGION_BASIC_INFO_64,\n                                  reinterpret_cast<vm_region_info_t>(&info),\n                                  &count,\n                                  &object);\n  if (kr == KERN_SUCCESS) {\n    // |object| will be MACH_PORT_NULL (10.9.4 xnu-2422.110.17/osfmk/vm/vm_map.c\n    // vm_map_region()), but the interface acts as if it might carry a send\n    // right, so treat it as documented.\n    base::apple::ScopedMachSendRight object_owner(object);\n\n    return address >= region_address && address <= region_address + region_size;\n  }\n\n  if (kr == KERN_INVALID_ADDRESS) {\n    return false;\n  }\n\n  ADD_FAILURE() << MachErrorMessage(kr, \"vm_region_64\");\n  return false;\n}\n\nTEST(ProcessMemoryMac, MappedMemoryDeallocates) {\n  // This tests that once a ProcessMemoryMac::MappedMemory object is destroyed,\n  // it releases the mapped memory that it owned. Technically, this test is not\n  // valid because after the mapping is released, something else (on another\n  // thread) might wind up mapped in the same address. In the test environment,\n  // hopefully there are either no other threads or they're all quiescent, so\n  // nothing else should wind up mapped in the address.\n\n  ProcessMemoryMac memory;\n  ASSERT_TRUE(memory.Initialize(mach_task_self()));\n  std::unique_ptr<ProcessMemoryMac::MappedMemory> mapped;\n\n  static constexpr char kTestBuffer[] = \"hello!\";\n  mach_vm_address_t test_address =\n      FromPointerCast<mach_vm_address_t>(&kTestBuffer);\n  ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer))));\n  EXPECT_EQ(memcmp(kTestBuffer, mapped->data(), sizeof(kTestBuffer)), 0);\n\n  vm_address_t mapped_address = reinterpret_cast<vm_address_t>(mapped->data());\n  EXPECT_TRUE(IsAddressMapped(mapped_address));\n\n  mapped.reset();\n  EXPECT_FALSE(IsAddressMapped(mapped_address));\n\n  // This is the same but with a big buffer that’s definitely larger than a\n  // single page. This makes sure that the whole mapped region winds up being\n  // deallocated.\n  auto big_buffer = base::HeapArray<char>::Uninit(4 * PAGE_SIZE);\n  test_address = FromPointerCast<mach_vm_address_t>(&big_buffer[0]);\n  ASSERT_TRUE((mapped = memory.ReadMapped(test_address, big_buffer.size())));\n\n  mapped_address = reinterpret_cast<vm_address_t>(mapped->data());\n  vm_address_t mapped_last_address = mapped_address + big_buffer.size() - 1;\n  EXPECT_TRUE(IsAddressMapped(mapped_address));\n  EXPECT_TRUE(IsAddressMapped(mapped_address + PAGE_SIZE));\n  EXPECT_TRUE(IsAddressMapped(mapped_last_address));\n\n  mapped.reset();\n  EXPECT_FALSE(IsAddressMapped(mapped_address));\n  EXPECT_FALSE(IsAddressMapped(mapped_address + PAGE_SIZE));\n  EXPECT_FALSE(IsAddressMapped(mapped_last_address));\n}\n\nTEST(ProcessMemoryMac, MappedMemoryReadCString) {\n  // This tests the behavior of ProcessMemoryMac::MappedMemory::ReadCString().\n  ProcessMemoryMac memory;\n  ASSERT_TRUE(memory.Initialize(mach_task_self()));\n  std::unique_ptr<ProcessMemoryMac::MappedMemory> mapped;\n\n  static constexpr char kTestBuffer[] = \"0\\0\" \"2\\0\" \"45\\0\" \"789\";\n  const mach_vm_address_t kTestAddress =\n      FromPointerCast<mach_vm_address_t>(&kTestBuffer);\n  ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10)));\n\n  std::string string;\n  ASSERT_TRUE(mapped->ReadCString(0, &string));\n  EXPECT_EQ(string, \"0\");\n  ASSERT_TRUE(mapped->ReadCString(1, &string));\n  EXPECT_EQ(string, \"\");\n  ASSERT_TRUE(mapped->ReadCString(2, &string));\n  EXPECT_EQ(string, \"2\");\n  ASSERT_TRUE(mapped->ReadCString(3, &string));\n  EXPECT_EQ(string, \"\");\n  ASSERT_TRUE(mapped->ReadCString(4, &string));\n  EXPECT_EQ(string, \"45\");\n  ASSERT_TRUE(mapped->ReadCString(5, &string));\n  EXPECT_EQ(string, \"5\");\n  ASSERT_TRUE(mapped->ReadCString(6, &string));\n  EXPECT_EQ(string, \"\");\n\n  // kTestBuffer’s NUL terminator was not read, so these will see an\n  // unterminated string and fail.\n  EXPECT_FALSE(mapped->ReadCString(7, &string));\n  EXPECT_FALSE(mapped->ReadCString(8, &string));\n  EXPECT_FALSE(mapped->ReadCString(9, &string));\n\n  // This is out of the range of what was read, so it will fail.\n  EXPECT_FALSE(mapped->ReadCString(10, &string));\n  EXPECT_FALSE(mapped->ReadCString(11, &string));\n\n  // Read it again, this time with a length long enough to include the NUL\n  // terminator.\n  ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 11)));\n\n  ASSERT_TRUE(mapped->ReadCString(6, &string));\n  EXPECT_EQ(string, \"\");\n\n  // These should now succeed.\n  ASSERT_TRUE(mapped->ReadCString(7, &string));\n  EXPECT_EQ(string, \"789\");\n  ASSERT_TRUE(mapped->ReadCString(8, &string));\n  EXPECT_EQ(string, \"89\");\n  ASSERT_TRUE(mapped->ReadCString(9, &string));\n  EXPECT_EQ(string, \"9\");\n  EXPECT_TRUE(mapped->ReadCString(10, &string));\n  EXPECT_EQ(string, \"\");\n\n  // These are still out of range.\n  EXPECT_FALSE(mapped->ReadCString(11, &string));\n  EXPECT_FALSE(mapped->ReadCString(12, &string));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_native.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_FUCHSIA)\n#include \"util/process/process_memory_fuchsia.h\"\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\n#include \"util/process/process_memory_linux.h\"\n#elif BUILDFLAG(IS_WIN)\n#include \"util/process/process_memory_win.h\"\n#elif BUILDFLAG(IS_APPLE)\n#include \"util/process/process_memory_mac.h\"\n#endif\n\nnamespace crashpad {\n\n#if BUILDFLAG(IS_FUCHSIA) || DOXYGEN\n//! \\brief Alias for platform-specific native implementation of ProcessMemory.\nusing ProcessMemoryNative = ProcessMemoryFuchsia;\n#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)\nusing ProcessMemoryNative = ProcessMemoryLinux;\n#elif BUILDFLAG(IS_WIN)\nusing ProcessMemoryNative = ProcessMemoryWin;\n#elif BUILDFLAG(IS_APPLE)\nusing ProcessMemoryNative = ProcessMemoryMac;\n#else\n#error Port.\n#endif\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_range.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_range.h\"\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nProcessMemoryRange::ProcessMemoryRange()\n    : memory_(nullptr), range_(), initialized_() {}\n\nProcessMemoryRange::~ProcessMemoryRange() {}\n\nbool ProcessMemoryRange::Initialize(const ProcessMemory* memory,\n                                    bool is_64_bit,\n                                    VMAddress base,\n                                    VMSize size) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  memory_ = memory;\n  range_.SetRange(is_64_bit, base, size);\n  if (!range_.IsValid()) {\n    LOG(ERROR) << \"invalid range\";\n    return false;\n  }\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessMemoryRange::Initialize(const ProcessMemory* memory,\n                                    bool is_64_bit) {\n  VMSize max = is_64_bit ? std::numeric_limits<uint64_t>::max()\n                         : std::numeric_limits<uint32_t>::max();\n  return Initialize(memory, is_64_bit, 0, max);\n}\n\nbool ProcessMemoryRange::Initialize(const ProcessMemoryRange& other) {\n  return Initialize(other.memory_,\n                    other.range_.Is64Bit(),\n                    other.range_.Base(),\n                    other.range_.Size());\n}\n\nbool ProcessMemoryRange::RestrictRange(VMAddress base, VMSize size) {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  CheckedVMAddressRange new_range(range_.Is64Bit(), base, size);\n  if (!new_range.IsValid() || !range_.ContainsRange(new_range)) {\n    LOG(ERROR) << \"invalid range\";\n    return false;\n  }\n  range_ = new_range;\n  return true;\n}\n\nbool ProcessMemoryRange::Read(VMAddress address,\n                              VMSize size,\n                              void* buffer) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  CheckedVMAddressRange read_range(range_.Is64Bit(), address, size);\n  if (!read_range.IsValid() || !range_.ContainsRange(read_range)) {\n    LOG(ERROR) << \"read out of range\";\n    return false;\n  }\n  return memory_->Read(address, size, buffer);\n}\n\nbool ProcessMemoryRange::ReadCStringSizeLimited(VMAddress address,\n                                                VMSize size,\n                                                std::string* string) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (!range_.ContainsValue(address)) {\n    LOG(ERROR) << \"read out of range\";\n    return false;\n  }\n  size = std::min(size, range_.End() - address);\n  return memory_->ReadCStringSizeLimited(address, size, string);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_range.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_RANGE_H_\n#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_RANGE_H_\n\n#include <sys/types.h>\n\n#include <string>\n\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/numeric/checked_vm_address_range.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\n\n//! \\brief Provides range protected access to the memory of another process.\nclass ProcessMemoryRange {\n public:\n  ProcessMemoryRange();\n\n  ProcessMemoryRange(const ProcessMemoryRange&) = delete;\n  ProcessMemoryRange& operator=(const ProcessMemoryRange&) = delete;\n\n  ~ProcessMemoryRange();\n\n  //! \\brief Initializes this object.\n  //!\n  //! One of the Initialize methods must be successfully called on this object\n  //! before calling any other.\n  //!\n  //! \\param[in] memory The memory reader to delegate to.\n  //! \\param[in] is_64_bit Whether the target process is 64-bit.\n  //! \\param[in] base The base address of the initial range.\n  //! \\param[in] size The size of the initial range.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Initialize(const ProcessMemory* memory,\n                  bool is_64_bit,\n                  VMAddress base,\n                  VMSize size);\n\n  //! \\brief Initializes this object with the maximum range for the address\n  //!     space.\n  //!\n  //! One of the Initialize methods must be successfully called on this object\n  //! before calling any other.\n  //!\n  //! \\param[in] memory The memory reader to delegate to.\n  //! \\param[in] is_64_bit Whether the target process is 64-bit.\n  bool Initialize(const ProcessMemory* memory, bool is_64_bit);\n\n  //! \\brief Initializes this object from an existing memory range.\n  //!\n  //! One of the Initialize methods must be successfully called on this object\n  //! before calling any other.\n  //!\n  //! \\param[in] other The memory range object to initialize from.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool Initialize(const ProcessMemoryRange& other);\n\n  //! \\brief Returns whether the range is part of a 64-bit address space.\n  bool Is64Bit() const { return range_.Is64Bit(); }\n\n  //! \\brief Returns the base address of the range.\n  VMAddress Base() const { return range_.Base(); }\n\n  //! \\brief Returns the size of the range.\n  VMSize Size() const { return range_.Size(); }\n\n  //! \\brief Shrinks the range to the new base and size.\n  //!\n  //! The new range must be contained within the existing range for this object.\n  //!\n  //! \\param[in] base The new base of the range.\n  //! \\param[in] size The new size of the range.\n  //! \\return `true` on success. `false` on failure with a message logged.\n  bool RestrictRange(VMAddress base, VMSize size);\n\n  //! \\brief Copies memory from the target process into a caller-provided buffer\n  //!     in the current process.\n  //!\n  //! \\param[in] address The address, in the target process' address space, of\n  //!     the memory region to copy.\n  //! \\param[in] size The size, in bytes, of the memory region to copy.\n  //!     \\a buffer must be at least this size.\n  //! \\param[out] buffer The buffer into which the contents of the other\n  //!     process' memory will be copied.\n  //!\n  //! \\return `true` on success, with \\a buffer filled appropriately. `false` on\n  //!     failure, with a message logged.\n  bool Read(VMAddress address, VMSize size, void* buffer) const;\n\n  //! \\brief Reads a `NUL`-terminated C string from the target process into a\n  //!     string in the current process.\n  //!\n  //! \\param[in] address The address, in the target process’s address space, of\n  //!     the string to copy.\n  //! \\param[in] size The maximum number of bytes to read. The string is\n  //!     required to be `NUL`-terminated within this many bytes.\n  //! \\param[out] string The string read from the other process.\n  //!\n  //! \\return `true` on success, with \\a string set appropriately. `false` on\n  //!     failure, with a message logged. Failures can occur, for example, when\n  //!     a `NUL` terminator is not found within \\a size bytes, or when\n  //!     encountering unmapped or unreadable pages.\n  bool ReadCStringSizeLimited(VMAddress address,\n                              VMSize size,\n                              std::string* string) const;\n\n private:\n  const ProcessMemory* memory_;  // weak\n  CheckedVMAddressRange range_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_RANGE_H_\n"
  },
  {
    "path": "util/process/process_memory_range_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_range.h\"\n\n#include <iterator>\n#include <limits>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/process_type.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/process/process_memory_native.h\"\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#include \"test/linux/fake_ptrace_connection.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nstruct TestObject {\n  char string1[16];\n  char string2[16];\n} kTestObject = {\"string1\", \"string2\"};\n\nTEST(ProcessMemoryRange, Basic) {\n#if defined(ARCH_CPU_64_BITS)\n  constexpr bool is_64_bit = true;\n#else\n  constexpr bool is_64_bit = false;\n#endif  // ARCH_CPU_64_BITS\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(GetSelfProcess()));\n  ProcessMemoryLinux memory(&connection);\n#else\n  ProcessMemoryNative memory;\n  ASSERT_TRUE(memory.Initialize(GetSelfProcess()));\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n  ProcessMemoryRange range;\n  ASSERT_TRUE(range.Initialize(&memory, is_64_bit));\n  EXPECT_EQ(range.Is64Bit(), is_64_bit);\n\n  // Both strings are accessible within the object's range.\n  auto object_addr = FromPointerCast<VMAddress>(&kTestObject);\n  EXPECT_TRUE(range.RestrictRange(object_addr, sizeof(kTestObject)));\n\n  TestObject object;\n  ASSERT_TRUE(range.Read(object_addr, sizeof(object), &object));\n  EXPECT_EQ(memcmp(&object, &kTestObject, sizeof(object)), 0);\n\n  std::string string;\n  auto string1_addr = FromPointerCast<VMAddress>(kTestObject.string1);\n  auto string2_addr = FromPointerCast<VMAddress>(kTestObject.string2);\n  ASSERT_TRUE(range.ReadCStringSizeLimited(\n      string1_addr, std::size(kTestObject.string1), &string));\n  EXPECT_STREQ(string.c_str(), kTestObject.string1);\n\n  ASSERT_TRUE(range.ReadCStringSizeLimited(\n      string2_addr, std::size(kTestObject.string2), &string));\n  EXPECT_STREQ(string.c_str(), kTestObject.string2);\n\n  // Limit the range to remove access to string2.\n  ProcessMemoryRange range2;\n  ASSERT_TRUE(range2.Initialize(range));\n  ASSERT_TRUE(\n      range2.RestrictRange(string1_addr, std::size(kTestObject.string1)));\n  EXPECT_TRUE(range2.ReadCStringSizeLimited(\n      string1_addr, std::size(kTestObject.string1), &string));\n  EXPECT_FALSE(range2.ReadCStringSizeLimited(\n      string2_addr, std::size(kTestObject.string2), &string));\n  EXPECT_FALSE(range2.Read(object_addr, sizeof(object), &object));\n\n  // String reads fail if the NUL terminator is outside the range.\n  ASSERT_TRUE(range2.RestrictRange(string1_addr, strlen(kTestObject.string1)));\n  EXPECT_FALSE(range2.ReadCStringSizeLimited(\n      string1_addr, std::size(kTestObject.string1), &string));\n\n  // New range outside the old range.\n  EXPECT_FALSE(range2.RestrictRange(string1_addr - 1, 1));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_sanitized.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_sanitized.h\"\n\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n\nnamespace crashpad {\n\nProcessMemorySanitized::ProcessMemorySanitized()\n    : ProcessMemory(), memory_(nullptr), allowed_ranges_() {}\n\nProcessMemorySanitized::~ProcessMemorySanitized() {}\n\nbool ProcessMemorySanitized::Initialize(\n    const ProcessMemory* memory,\n    const std::vector<std::pair<VMAddress, VMAddress>>* allowed_ranges) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n  memory_ = memory;\n  if (allowed_ranges)\n    allowed_ranges_ = *allowed_ranges;\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nssize_t ProcessMemorySanitized::ReadUpTo(VMAddress address,\n                                         size_t size,\n                                         void* buffer) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n\n  VMAddress end = address + size;\n  for (auto&& entry : allowed_ranges_) {\n    if (address >= entry.first && address < entry.second &&\n        end >= entry.first && end <= entry.second) {\n      return memory_->ReadUpTo(address, size, buffer);\n    }\n  }\n\n  DLOG(ERROR)\n      << \"ProcessMemorySanitized failed to read disallowed region. address=\"\n      << address << \" size=\" << size;\n  return 0;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_sanitized.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_SANITIZED_H_\n#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_SANITIZED_H_\n\n#include <sys/types.h>\n\n#include <utility>\n#include <vector>\n\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory.h\"\n\nnamespace crashpad {\n\n//! \\brief Sanitized access to the memory of another process.\nclass ProcessMemorySanitized final : public ProcessMemory {\n public:\n  ProcessMemorySanitized();\n\n  ProcessMemorySanitized(const ProcessMemorySanitized&) = delete;\n  ProcessMemorySanitized& operator=(const ProcessMemorySanitized&) = delete;\n\n  ~ProcessMemorySanitized();\n\n  //! \\brief Initializes this object to read memory from the underlying\n  //!     \\a memory object if the memory range is in \\a allowed_ranges.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class.\n  //!\n  //! \\param[in] memory The memory object to read memory from.\n  //! \\param[in] allowed_ranges A list of allowed memory ranges.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Initialize(\n      const ProcessMemory* memory,\n      const std::vector<std::pair<VMAddress, VMAddress>>* allowed_ranges);\n\n private:\n  ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;\n\n  const ProcessMemory* memory_;\n  InitializationStateDcheck initialized_;\n  std::vector<std::pair<VMAddress, VMAddress>> allowed_ranges_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_SANITIZED_H_\n"
  },
  {
    "path": "util/process/process_memory_sanitized_test.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_sanitized.h\"\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/process_type.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/process/process_memory_native.h\"\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#include \"test/linux/fake_ptrace_connection.h\"\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ProcessMemorySanitized, DenyDisallowedMemory) {\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(GetSelfProcess()));\n  ProcessMemoryLinux memory(&connection);\n#else\n  ProcessMemoryNative memory;\n  ASSERT_TRUE(memory.Initialize(GetSelfProcess()));\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n  char c = 42;\n  char out;\n\n  ProcessMemorySanitized san_null;\n  san_null.Initialize(&memory, nullptr);\n  EXPECT_FALSE(san_null.Read(FromPointerCast<VMAddress>(&c), 1, &out));\n\n  std::vector<std::pair<VMAddress, VMAddress>> allowed_memory;\n  ProcessMemorySanitized san_empty;\n  san_empty.Initialize(&memory, &allowed_memory);\n  EXPECT_FALSE(san_empty.Read(FromPointerCast<VMAddress>(&c), 1, &out));\n}\n\nTEST(ProcessMemorySanitized, AllowedMemory) {\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n  FakePtraceConnection connection;\n  ASSERT_TRUE(connection.Initialize(GetSelfProcess()));\n  ProcessMemoryLinux memory(&connection);\n#else\n  ProcessMemoryNative memory;\n  ASSERT_TRUE(memory.Initialize(GetSelfProcess()));\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n  char str[4] = \"ABC\";\n  char out[4];\n\n  std::vector<std::pair<VMAddress, VMAddress>> allowed_memory;\n  allowed_memory.push_back(std::make_pair(FromPointerCast<VMAddress>(str + 1),\n                                          FromPointerCast<VMAddress>(str + 2)));\n\n  ProcessMemorySanitized sanitized;\n  sanitized.Initialize(&memory, &allowed_memory);\n\n  EXPECT_FALSE(sanitized.Read(FromPointerCast<VMAddress>(str), 1, &out));\n  EXPECT_TRUE(sanitized.Read(FromPointerCast<VMAddress>(str + 1), 1, &out));\n  EXPECT_FALSE(sanitized.Read(FromPointerCast<VMAddress>(str + 2), 1, &out));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory.h\"\n\n#include <string.h>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/memory/page_size.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/multiprocess.h\"\n#include \"test/multiprocess_exec.h\"\n#include \"test/process_type.h\"\n#include \"test/scoped_guarded_page.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/process/process_memory_native.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include \"test/mac/mach_multiprocess.h\"\n#endif  // BUILDFLAG(IS_APPLE)\n\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n#include \"test/linux/fake_ptrace_connection.h\"\n#include \"util/linux/direct_ptrace_connection.h\"\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// On macOS the ProcessMemoryTests require accessing the child process' task\n// port which requires root or a code signing entitlement. To account for this\n// we implement an adaptor class that wraps MachMultiprocess on macOS, because\n// it shares the child's task port, and makes it behave like MultiprocessExec.\n#if BUILDFLAG(IS_APPLE)\nclass MultiprocessAdaptor : public MachMultiprocess {\n public:\n  void SetChildTestMainFunction(const std::string& function_name) {\n    test_function_ = function_name;\n  }\n\n  ProcessType ChildProcess() { return ChildTask(); }\n\n  // Helpers to get I/O handles in the child process\n  static FileHandle OutputHandle() {\n    CHECK_NE(write_pipe_handle_, -1);\n    return write_pipe_handle_;\n  }\n\n  static FileHandle InputHandle() {\n    CHECK_NE(read_pipe_handle_, -1);\n    return read_pipe_handle_;\n  }\n\n private:\n  virtual void Parent() = 0;\n\n  void MachMultiprocessParent() override { Parent(); }\n\n  void MachMultiprocessChild() override {\n    read_pipe_handle_ = ReadPipeHandle();\n    write_pipe_handle_ = WritePipeHandle();\n    internal::CheckedInvokeMultiprocessChild(test_function_);\n  }\n\n  std::string test_function_;\n\n  static FileHandle read_pipe_handle_;\n  static FileHandle write_pipe_handle_;\n};\n\nFileHandle MultiprocessAdaptor::read_pipe_handle_ = -1;\nFileHandle MultiprocessAdaptor::write_pipe_handle_ = -1;\n#else\nclass MultiprocessAdaptor : public MultiprocessExec {\n public:\n  static FileHandle OutputHandle() {\n    return StdioFileHandle(StdioStream::kStandardOutput);\n  }\n\n  static FileHandle InputHandle() {\n    return StdioFileHandle(StdioStream::kStandardInput);\n  }\n\n private:\n  virtual void Parent() = 0;\n\n  void MultiprocessParent() override { Parent(); }\n};\n#endif  // BUILDFLAG(IS_APPLE)\n\nbase::HeapArray<char> DoChildReadTestSetup() {\n  auto region = base::HeapArray<char>::Uninit(4 * base::GetPageSize());\n  for (size_t index = 0; index < region.size(); ++index) {\n    region[index] = static_cast<char>(index % 256);\n  }\n  return region;\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ReadTestChild) {\n  auto region = DoChildReadTestSetup();\n  auto region_size = region.size();\n  FileHandle out = MultiprocessAdaptor::OutputHandle();\n  CheckedWriteFile(out, &region_size, sizeof(region_size));\n  VMAddress address = FromPointerCast<VMAddress>(region.data());\n  CheckedWriteFile(out, &address, sizeof(address));\n  CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle());\n  return 0;\n}\n\nclass ReadTest : public MultiprocessAdaptor {\n public:\n  ReadTest() : MultiprocessAdaptor() {\n    SetChildTestMainFunction(\"ReadTestChild\");\n  }\n\n  ReadTest(const ReadTest&) = delete;\n  ReadTest& operator=(const ReadTest&) = delete;\n\n  void RunAgainstSelf() {\n    auto region = DoChildReadTestSetup();\n    DoTest(GetSelfProcess(),\n           region.size(),\n           FromPointerCast<VMAddress>(region.data()));\n  }\n\n  void RunAgainstChild() { Run(); }\n\n private:\n  void Parent() override {\n    size_t region_size;\n    VMAddress region;\n    ASSERT_TRUE(\n        ReadFileExactly(ReadPipeHandle(), &region_size, sizeof(region_size)));\n    ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &region, sizeof(region)));\n    DoTest(ChildProcess(), region_size, region);\n  }\n\n  void DoTest(ProcessType process, size_t region_size, VMAddress address) {\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    FakePtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(process));\n    ProcessMemoryLinux memory(&connection);\n#else\n    ProcessMemoryNative memory;\n    ASSERT_TRUE(memory.Initialize(process));\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n    auto result = base::HeapArray<char>::Uninit(region_size);\n\n    // Ensure that the entire region can be read.\n    ASSERT_TRUE(memory.Read(address, result.size(), result.data()));\n    for (size_t i = 0; i < result.size(); ++i) {\n      EXPECT_EQ(result[i], static_cast<char>(i % 256));\n    }\n\n    // Ensure that a read of length 0 succeeds and doesn’t touch the result.\n    memset(result.data(), '\\0', result.size());\n    ASSERT_TRUE(memory.Read(address, 0, result.data()));\n    for (size_t i = 0; i < result.size(); ++i) {\n      EXPECT_EQ(result[i], 0);\n    }\n\n    // Ensure that a read starting at an unaligned address works.\n    ASSERT_TRUE(memory.Read(address + 1, result.size() - 1, result.data()));\n    for (size_t i = 0; i < result.size() - 1; ++i) {\n      EXPECT_EQ(result[i], static_cast<char>((i + 1) % 256));\n    }\n\n    // Ensure that a read ending at an unaligned address works.\n    ASSERT_TRUE(memory.Read(address, result.size() - 1, result.data()));\n    for (size_t i = 0; i < result.size() - 1; ++i) {\n      EXPECT_EQ(result[i], static_cast<char>(i % 256));\n    }\n\n    // Ensure that a read starting and ending at unaligned addresses works.\n    ASSERT_TRUE(memory.Read(address + 1, result.size() - 2, result.data()));\n    for (size_t i = 0; i < result.size() - 2; ++i) {\n      EXPECT_EQ(result[i], static_cast<char>((i + 1) % 256));\n    }\n\n    // Ensure that a read of exactly one page works.\n    size_t page_size = base::GetPageSize();\n    ASSERT_GE(result.size(), page_size + page_size);\n    ASSERT_TRUE(memory.Read(address + page_size, page_size, result.data()));\n    for (size_t i = 0; i < page_size; ++i) {\n      EXPECT_EQ(result[i], static_cast<char>((i + page_size) % 256));\n    }\n\n    // Ensure that reading exactly a single byte works.\n    result[1] = 'J';\n    ASSERT_TRUE(memory.Read(address + 2, 1, result.data()));\n    EXPECT_EQ(result[0], 2);\n    EXPECT_EQ(result[1], 'J');\n  }\n};\n\nTEST(ProcessMemory, ReadSelf) {\n  ReadTest test;\n  test.RunAgainstSelf();\n}\n\nTEST(ProcessMemory, ReadChild) {\n  ReadTest test;\n  test.RunAgainstChild();\n}\n\nconstexpr char kConstCharEmpty[] = \"\";\nconstexpr char kConstCharShort[] = \"A short const char[]\";\n\n#define SHORT_LOCAL_STRING \"A short local variable char[]\"\n\nstd::string MakeLongString() {\n  std::string long_string;\n  const size_t kStringLongSize = 4 * base::GetPageSize();\n  for (size_t index = 0; index < kStringLongSize; ++index) {\n    long_string.push_back((index % 255) + 1);\n  }\n  EXPECT_EQ(long_string.size(), kStringLongSize);\n  return long_string;\n}\n\nvoid DoChildCStringReadTestSetup(const char** const_empty,\n                                 const char** const_short,\n                                 const char** local_empty,\n                                 const char** local_short,\n                                 std::string* long_string) {\n  *const_empty = kConstCharEmpty;\n  *const_short = kConstCharShort;\n  *local_empty = \"\";\n  *local_short = SHORT_LOCAL_STRING;\n  *long_string = MakeLongString();\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ReadCStringTestChild) {\n  const char* const_empty;\n  const char* const_short;\n  const char* local_empty;\n  const char* local_short;\n  std::string long_string;\n  DoChildCStringReadTestSetup(\n      &const_empty, &const_short, &local_empty, &local_short, &long_string);\n  const auto write_address = [](const char* p) {\n    VMAddress address = FromPointerCast<VMAddress>(p);\n    CheckedWriteFile(\n        MultiprocessAdaptor::OutputHandle(), &address, sizeof(address));\n  };\n  write_address(const_empty);\n  write_address(const_short);\n  write_address(local_empty);\n  write_address(local_short);\n  write_address(long_string.c_str());\n  CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle());\n  return 0;\n}\n\nclass ReadCStringTest : public MultiprocessAdaptor {\n public:\n  ReadCStringTest(bool limit_size)\n      : MultiprocessAdaptor(), limit_size_(limit_size) {\n    SetChildTestMainFunction(\"ReadCStringTestChild\");\n  }\n\n  ReadCStringTest(const ReadCStringTest&) = delete;\n  ReadCStringTest& operator=(const ReadCStringTest&) = delete;\n\n  void RunAgainstSelf() {\n    const char* const_empty;\n    const char* const_short;\n    const char* local_empty;\n    const char* local_short;\n    std::string long_string;\n    DoChildCStringReadTestSetup(\n        &const_empty, &const_short, &local_empty, &local_short, &long_string);\n    DoTest(GetSelfProcess(),\n           FromPointerCast<VMAddress>(const_empty),\n           FromPointerCast<VMAddress>(const_short),\n           FromPointerCast<VMAddress>(local_empty),\n           FromPointerCast<VMAddress>(local_short),\n           FromPointerCast<VMAddress>(long_string.c_str()));\n  }\n  void RunAgainstChild() { Run(); }\n\n private:\n  void Parent() override {\n#define DECLARE_AND_READ_ADDRESS(name) \\\n  VMAddress name;                      \\\n  ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &name, sizeof(name)));\n    DECLARE_AND_READ_ADDRESS(const_empty_address);\n    DECLARE_AND_READ_ADDRESS(const_short_address);\n    DECLARE_AND_READ_ADDRESS(local_empty_address);\n    DECLARE_AND_READ_ADDRESS(local_short_address);\n    DECLARE_AND_READ_ADDRESS(long_string_address);\n#undef DECLARE_AND_READ_ADDRESS\n\n    DoTest(ChildProcess(),\n           const_empty_address,\n           const_short_address,\n           local_empty_address,\n           local_short_address,\n           long_string_address);\n  }\n\n  void Compare(ProcessMemory& memory, VMAddress address, const char* str) {\n    std::string result;\n    if (limit_size_) {\n      ASSERT_TRUE(\n          memory.ReadCStringSizeLimited(address, strlen(str) + 1, &result));\n      EXPECT_EQ(result, str);\n      ASSERT_TRUE(\n          memory.ReadCStringSizeLimited(address, strlen(str) + 2, &result));\n      EXPECT_EQ(result, str);\n      EXPECT_FALSE(\n          memory.ReadCStringSizeLimited(address, strlen(str), &result));\n    } else {\n      ASSERT_TRUE(memory.ReadCString(address, &result));\n      EXPECT_EQ(result, str);\n    }\n  }\n\n  void DoTest(ProcessType process,\n              VMAddress const_empty_address,\n              VMAddress const_short_address,\n              VMAddress local_empty_address,\n              VMAddress local_short_address,\n              VMAddress long_string_address) {\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    FakePtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(process));\n    ProcessMemoryLinux memory(&connection);\n#else\n    ProcessMemoryNative memory;\n    ASSERT_TRUE(memory.Initialize(process));\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n    Compare(memory, const_empty_address, kConstCharEmpty);\n    Compare(memory, const_short_address, kConstCharShort);\n    Compare(memory, local_empty_address, \"\");\n    Compare(memory, local_short_address, SHORT_LOCAL_STRING);\n    std::string long_string_for_comparison = MakeLongString();\n    Compare(memory, long_string_address, long_string_for_comparison.c_str());\n  }\n\n  const bool limit_size_;\n};\n\nTEST(ProcessMemory, ReadCStringSelf) {\n  ReadCStringTest test(/* limit_size= */ false);\n  test.RunAgainstSelf();\n}\n\nTEST(ProcessMemory, ReadCStringChild) {\n  ReadCStringTest test(/* limit_size= */ false);\n  test.RunAgainstChild();\n}\n\nTEST(ProcessMemory, ReadCStringSizeLimitedSelf) {\n  ReadCStringTest test(/* limit_size= */ true);\n  test.RunAgainstSelf();\n}\n\nTEST(ProcessMemory, ReadCStringSizeLimitedChild) {\n  ReadCStringTest test(/* limit_size= */ true);\n  test.RunAgainstChild();\n}\n\nvoid DoReadUnmappedChildMainSetup(void* page) {\n  char* region = reinterpret_cast<char*>(page);\n  for (size_t index = 0; index < base::GetPageSize(); ++index) {\n    region[index] = static_cast<char>(index % 256);\n  }\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ReadUnmappedChildMain) {\n  ScopedGuardedPage pages;\n  VMAddress address = reinterpret_cast<VMAddress>(pages.Pointer());\n  DoReadUnmappedChildMainSetup(pages.Pointer());\n  FileHandle out = MultiprocessAdaptor::OutputHandle();\n  CheckedWriteFile(out, &address, sizeof(address));\n  CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle());\n  return 0;\n}\n\n// This test only supports running against a child process because\n// ScopedGuardedPage is not thread-safe.\nclass ReadUnmappedTest : public MultiprocessAdaptor {\n public:\n  ReadUnmappedTest() : MultiprocessAdaptor() {\n    SetChildTestMainFunction(\"ReadUnmappedChildMain\");\n  }\n\n  ReadUnmappedTest(const ReadUnmappedTest&) = delete;\n  ReadUnmappedTest& operator=(const ReadUnmappedTest&) = delete;\n\n  void RunAgainstChild() { Run(); }\n\n private:\n  void Parent() override {\n    VMAddress address = 0;\n    ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &address, sizeof(address)));\n    DoTest(ChildProcess(), address);\n  }\n\n  void DoTest(ProcessType process, VMAddress address) {\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(process));\n    ProcessMemoryLinux memory(&connection);\n#else\n    ProcessMemoryNative memory;\n    ASSERT_TRUE(memory.Initialize(process));\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n    VMAddress page_addr1 = address;\n    VMAddress page_addr2 = page_addr1 + base::GetPageSize();\n\n    auto result = base::HeapArray<char>::Uninit(base::GetPageSize() * 2);\n    EXPECT_TRUE(memory.Read(page_addr1, base::GetPageSize(), result.data()));\n    EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result.data()));\n\n    EXPECT_FALSE(memory.Read(page_addr1, result.size(), result.data()));\n    EXPECT_FALSE(memory.Read(page_addr2, base::GetPageSize(), result.data()));\n    EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result.data()));\n  }\n};\n\nTEST(ProcessMemory, ReadUnmappedChild) {\n  ReadUnmappedTest test;\n  ASSERT_FALSE(testing::Test::HasFailure());\n  test.RunAgainstChild();\n}\n\nconstexpr size_t kChildProcessStringLength = 10;\n\nclass StringDataInChildProcess {\n public:\n  // This constructor only makes sense in the child process.\n  explicit StringDataInChildProcess(const char* cstring, bool valid)\n      : address_(FromPointerCast<VMAddress>(cstring)) {\n    if (valid) {\n      memcpy(expected_value_, cstring, kChildProcessStringLength + 1);\n    } else {\n      memset(expected_value_, 0xff, kChildProcessStringLength + 1);\n    }\n  }\n\n  void Write(FileHandle out) {\n    CheckedWriteFile(out, &address_, sizeof(address_));\n    CheckedWriteFile(out, &expected_value_, sizeof(expected_value_));\n  }\n\n  static StringDataInChildProcess Read(FileHandle in) {\n    StringDataInChildProcess str;\n    EXPECT_TRUE(ReadFileExactly(in, &str.address_, sizeof(str.address_)));\n    EXPECT_TRUE(\n        ReadFileExactly(in, &str.expected_value_, sizeof(str.expected_value_)));\n    return str;\n  }\n\n  VMAddress address() const { return address_; }\n  std::string expected_value() const { return expected_value_; }\n\n  private:\n   StringDataInChildProcess() : address_(0), expected_value_() {}\n\n   VMAddress address_;\n   char expected_value_[kChildProcessStringLength + 1];\n};\n\nvoid DoCStringUnmappedTestSetup(\n    void* page,\n    std::vector<StringDataInChildProcess>* strings) {\n  char* region = reinterpret_cast<char*>(page);\n  for (size_t index = 0; index < base::GetPageSize(); ++index) {\n    region[index] = 1 + index % 255;\n  }\n\n  // A string at the start of the mapped region\n  char* string1 = region;\n  string1[kChildProcessStringLength] = '\\0';\n\n  // A string near the end of the mapped region\n  char* string2 = region + base::GetPageSize() - kChildProcessStringLength * 2;\n  string2[kChildProcessStringLength] = '\\0';\n\n  // A string that crosses from the mapped into the unmapped region\n  char* string3 = region + base::GetPageSize() - kChildProcessStringLength + 1;\n\n  // A string entirely in the unmapped region\n  char* string4 = region + base::GetPageSize() + 10;\n\n  strings->push_back(StringDataInChildProcess(string1, true));\n  strings->push_back(StringDataInChildProcess(string2, true));\n  strings->push_back(StringDataInChildProcess(string3, false));\n  strings->push_back(StringDataInChildProcess(string4, false));\n}\n\nCRASHPAD_CHILD_TEST_MAIN(ReadCStringUnmappedChildMain) {\n  ScopedGuardedPage pages;\n  std::vector<StringDataInChildProcess> strings;\n  DoCStringUnmappedTestSetup(pages.Pointer(), &strings);\n  FileHandle out = MultiprocessAdaptor::OutputHandle();\n  strings[0].Write(out);\n  strings[1].Write(out);\n  strings[2].Write(out);\n  strings[3].Write(out);\n  CheckedReadFileAtEOF(MultiprocessAdaptor::InputHandle());\n  return 0;\n}\n\n// This test only supports running against a child process because\n// ScopedGuardedPage is not thread-safe.\nclass ReadCStringUnmappedTest : public MultiprocessAdaptor {\n public:\n  ReadCStringUnmappedTest(bool limit_size)\n      : MultiprocessAdaptor(), limit_size_(limit_size) {\n    SetChildTestMainFunction(\"ReadCStringUnmappedChildMain\");\n  }\n\n  ReadCStringUnmappedTest(const ReadCStringUnmappedTest&) = delete;\n  ReadCStringUnmappedTest& operator=(const ReadCStringUnmappedTest&) = delete;\n\n  void RunAgainstChild() { Run(); }\n\n private:\n  void Parent() override {\n    std::vector<StringDataInChildProcess> strings;\n    strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));\n    strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));\n    strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));\n    strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));\n    ASSERT_NO_FATAL_FAILURE(DoTest(ChildProcess(), strings));\n  }\n\n  void DoTest(ProcessType process,\n              const std::vector<StringDataInChildProcess>& strings) {\n#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)\n    DirectPtraceConnection connection;\n    ASSERT_TRUE(connection.Initialize(process));\n    ProcessMemoryLinux memory(&connection);\n#else\n    ProcessMemoryNative memory;\n    ASSERT_TRUE(memory.Initialize(process));\n#endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) ||\n        // BUILDFLAG(IS_CHROMEOS)\n\n    std::string result;\n    result.reserve(kChildProcessStringLength + 1);\n\n    if (limit_size_) {\n      ASSERT_TRUE(memory.ReadCStringSizeLimited(\n          strings[0].address(), kChildProcessStringLength + 1, &result));\n      EXPECT_EQ(result, strings[0].expected_value());\n      ASSERT_TRUE(memory.ReadCStringSizeLimited(\n          strings[1].address(), kChildProcessStringLength + 1, &result));\n      EXPECT_EQ(result, strings[1].expected_value());\n      EXPECT_FALSE(memory.ReadCStringSizeLimited(\n          strings[2].address(), kChildProcessStringLength + 1, &result));\n      EXPECT_FALSE(memory.ReadCStringSizeLimited(\n          strings[3].address(), kChildProcessStringLength + 1, &result));\n    } else {\n      ASSERT_TRUE(memory.ReadCString(strings[0].address(), &result));\n      EXPECT_EQ(result, strings[0].expected_value());\n      ASSERT_TRUE(memory.ReadCString(strings[1].address(), &result));\n      EXPECT_EQ(result, strings[1].expected_value());\n      EXPECT_FALSE(memory.ReadCString(strings[2].address(), &result));\n      EXPECT_FALSE(memory.ReadCString(strings[3].address(), &result));\n    }\n  }\n\n  const bool limit_size_;\n};\n\nTEST(ProcessMemory, ReadCStringUnmappedChild) {\n  ReadCStringUnmappedTest test(/* limit_size= */ false);\n  ASSERT_FALSE(testing::Test::HasFailure());\n  test.RunAgainstChild();\n}\n\nTEST(ProcessMemory, ReadCStringSizeLimitedUnmappedChild) {\n  ReadCStringUnmappedTest test(/* limit_size= */ true);\n  ASSERT_FALSE(testing::Test::HasFailure());\n  test.RunAgainstChild();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_win.cc",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/process/process_memory_win.h\"\n\n#include <windows.h>\n\n#include <algorithm>\n#include <limits>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/memory/page_size.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/strings/stringprintf.h\"\n\nnamespace crashpad {\n\nProcessMemoryWin::ProcessMemoryWin()\n    : ProcessMemory(), handle_(), process_info_(), initialized_() {}\n\nProcessMemoryWin::~ProcessMemoryWin() {}\n\nbool ProcessMemoryWin::Initialize(HANDLE handle) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  handle_ = handle;\n  if (!process_info_.Initialize(handle)) {\n    LOG(ERROR) << \"Failed to initialize ProcessInfo.\";\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nssize_t ProcessMemoryWin::ReadUpTo(VMAddress address,\n                                   size_t size,\n                                   void* buffer) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  DCHECK_LE(size, (size_t)std::numeric_limits<ssize_t>::max());\n\n  SIZE_T size_out = 0;\n  BOOL success = ReadProcessMemory(\n      handle_, reinterpret_cast<void*>(address), buffer, size, &size_out);\n  if (success)\n    return base::checked_cast<ssize_t>(size_out);\n\n  if (GetLastError() == ERROR_PARTIAL_COPY) {\n    // If we can not read the entire section, perform a short read of the first\n    // page instead. This is necessary to support ReadCString().\n    size_t short_read =\n        base::GetPageSize() - (address & (base::GetPageSize() - 1));\n    success = ReadProcessMemory(handle_,\n                                reinterpret_cast<void*>(address),\n                                buffer,\n                                short_read,\n                                &size_out);\n    if (success)\n      return base::checked_cast<ssize_t>(size_out);\n  }\n\n  PLOG(ERROR) << \"ReadMemory at 0x\" << std::hex << address << std::dec << \" of \"\n              << size << \" bytes failed\";\n  return -1;\n}\n\nsize_t ProcessMemoryWin::ReadAvailableMemory(VMAddress address,\n                                             size_t size,\n                                             void* buffer) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  DCHECK_LE(size, (size_t)std::numeric_limits<ssize_t>::max());\n\n  if (size == 0)\n    return 0;\n\n  auto ranges = process_info_.GetReadableRanges(\n      CheckedRange<WinVMAddress, WinVMSize>(address, size));\n\n  // We only read up until the first unavailable byte, so we only read from the\n  // first range. If we have no ranges, then no bytes were accessible anywhere\n  // in the range.\n  if (ranges.empty()) {\n    LOG(ERROR) << base::StringPrintf(\n        \"range at 0x%llx, size 0x%zx completely inaccessible\", address, size);\n    return 0;\n  }\n\n  // If the start address was adjusted, we couldn't read even the first\n  // requested byte.\n  if (ranges.front().base() != address) {\n    LOG(ERROR) << base::StringPrintf(\n        \"start of range at 0x%llx, size 0x%zx inaccessible\", address, size);\n    return 0;\n  }\n\n  DCHECK_LE(ranges.front().size(), size);\n\n  ssize_t result = ReadUpTo(ranges.front().base(),\n                            base::checked_cast<size_t>(ranges.front().size()),\n                            buffer);\n  if (result < 0)\n    return 0;\n\n  return base::checked_cast<size_t>(result);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/process/process_memory_win.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_\n#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_\n\n#include <windows.h>\n\n#include \"util/misc/address_types.h\"\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/process/process_memory.h\"\n#include \"util/win/process_info.h\"\n\nnamespace crashpad {\n\n//! \\brief Accesses the memory of another Windows process.\nclass ProcessMemoryWin final : public ProcessMemory {\n public:\n  ProcessMemoryWin();\n\n  ProcessMemoryWin(const ProcessMemoryWin&) = delete;\n  ProcessMemoryWin& operator=(const ProcessMemoryWin&) = delete;\n\n  ~ProcessMemoryWin();\n\n  //! \\brief Initializes this object to read the memory of a process with the\n  //!     provided handle.\n  //!\n  //! This method must be called successfully prior to calling any other method\n  //! in this class.\n  //!\n  //! \\param[in] handle The HANDLE of a target process.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Initialize(HANDLE handle);\n\n  //! \\brief Attempts to read \\a size bytes from the target process starting at\n  //!     address \\a address into \\a buffer. If some of the specified range is\n  //!     not accessible, reads up to the first inaccessible byte.\n  //!\n  //! \\return The actual number of bytes read.\n  size_t ReadAvailableMemory(VMAddress address,\n                             size_t num_bytes,\n                             void* buffer) const;\n\n private:\n  ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;\n\n  HANDLE handle_;\n  ProcessInfo process_info_;\n  InitializationStateDcheck initialized_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_WIN_H_\n"
  },
  {
    "path": "util/stdlib/aligned_allocator.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/aligned_allocator.h\"\n\n#include <algorithm>\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX) || defined(_LIBCPP_STD_VER)\n#include <stdlib.h>\n#elif BUILDFLAG(IS_WIN)\n#include <malloc.h>\n#include <xutility>\n#endif  // BUILDFLAG(IS_POSIX)\n\nnamespace {\n\n// Throws std::bad_alloc() by calling an internal function provided by the C++\n// library to do so. This works even if C++ exceptions are disabled, causing\n// program termination if uncaught.\nvoid ThrowBadAlloc() {\n#if BUILDFLAG(IS_POSIX) || defined(_LIBCPP_STD_VER)\n  // This works with both libc++ and libstdc++.\n  std::__throw_bad_alloc();\n#elif BUILDFLAG(IS_WIN)\n  std::_Xbad_alloc();\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nvoid* AlignedAllocate(size_t alignment, size_t size) {\n#if BUILDFLAG(IS_POSIX)\n  // posix_memalign() requires that alignment be at least sizeof(void*), so the\n  // power-of-2 check needs to happen before potentially changing the alignment.\n  if (alignment == 0 || alignment & (alignment - 1)) {\n    ThrowBadAlloc();\n  }\n\n  void* pointer;\n  if (posix_memalign(&pointer, std::max(alignment, sizeof(void*)), size) != 0) {\n    ThrowBadAlloc();\n  }\n#elif BUILDFLAG(IS_WIN)\n  void* pointer = _aligned_malloc(size, alignment);\n  if (pointer == nullptr) {\n    ThrowBadAlloc();\n  }\n#endif  // BUILDFLAG(IS_POSIX)\n\n  return pointer;\n}\n\nvoid AlignedFree(void* pointer) {\n#if BUILDFLAG(IS_POSIX)\n  free(pointer);\n#elif BUILDFLAG(IS_WIN)\n  _aligned_free(pointer);\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stdlib/aligned_allocator.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STDLIB_ALIGNED_ALLOCATOR_H_\n#define CRASHPAD_UTIL_STDLIB_ALIGNED_ALLOCATOR_H_\n\n#include <stddef.h>\n\n#include <limits>\n#include <memory>\n#include <new>\n#include <utility>\n#include <vector>\n\nnamespace crashpad {\n\n//! \\brief Allocates memory with the specified alignment constraint.\n//!\n//! This function wraps `posix_memalign()` or `_aligned_malloc()`. Memory\n//! allocated by this function must be released by AlignFree().\nvoid* AlignedAllocate(size_t alignment, size_t size);\n\n//! \\brief Frees memory allocated by AlignedAllocate().\n//!\n//! This function wraps `free()` or `_aligned_free()`.\nvoid AlignedFree(void* pointer);\n\n//! \\brief A standard allocator that aligns its allocations as requested,\n//!     suitable for use as an allocator in standard containers.\n//!\n//! This is similar to `std::allocator<T>`, with the addition of an alignment\n//! guarantee. \\a Alignment must be a power of 2. If \\a Alignment is not\n//! specified, the default alignment for type \\a T is used.\ntemplate <class T, size_t Alignment = alignof(T)>\nstruct AlignedAllocator {\n public:\n  using value_type = T;\n  using pointer = T*;\n  using const_pointer = const T*;\n  using reference = T&;\n  using const_reference = const T&;\n  using size_type = size_t;\n  using difference_type = ptrdiff_t;\n\n  template <class U>\n  struct rebind {\n    using other = AlignedAllocator<U, Alignment>;\n  };\n\n  AlignedAllocator() noexcept {}\n  AlignedAllocator(const AlignedAllocator& other) noexcept {}\n\n  template <typename U>\n  AlignedAllocator(const AlignedAllocator<U, Alignment>& other) noexcept {}\n\n  ~AlignedAllocator() {}\n\n  pointer address(reference x) const noexcept { return &x; }\n  const_pointer address(const_reference x) const noexcept { return &x; }\n\n  pointer allocate(size_type n, const void* hint = 0) {\n    return reinterpret_cast<pointer>(\n        AlignedAllocate(Alignment, sizeof(value_type) * n));\n  }\n\n  void deallocate(pointer p, size_type n) { AlignedFree(p); }\n\n  size_type max_size() const noexcept {\n    return std::numeric_limits<size_type>::max() / sizeof(value_type);\n  }\n\n  template <class U, class... Args>\n  void construct(U* p, Args&&... args) {\n    new (reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...);\n  }\n\n  template <class U>\n  void destroy(U* p) {\n    p->~U();\n  }\n};\n\ntemplate <class T1, class T2, size_t Alignment>\nbool operator==(const AlignedAllocator<T1, Alignment>& lhs,\n                const AlignedAllocator<T2, Alignment>& rhs) noexcept {\n  return true;\n}\n\ntemplate <class T1, class T2, size_t Alignment>\nbool operator!=(const AlignedAllocator<T1, Alignment>& lhs,\n                const AlignedAllocator<T2, Alignment>& rhs) noexcept {\n  return false;\n}\n\n//! \\brief A `std::vector` using AlignedAllocator.\n//!\n//! This is similar to `std::vector<T>`, with the addition of an alignment\n//! guarantee. \\a Alignment must be a power of 2. If \\a Alignment is not\n//! specified, the default alignment for type \\a T is used.\ntemplate <typename T, size_t Alignment = alignof(T)>\nusing AlignedVector = std::vector<T, AlignedAllocator<T, Alignment>>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STDLIB_ALIGNED_ALLOCATOR_H_\n"
  },
  {
    "path": "util/stdlib/aligned_allocator_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/aligned_allocator.h\"\n\n#include <stdint.h>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/gtest_death.h\"\n\n#if BUILDFLAG(IS_WIN)\n#include <crtdbg.h>\n#endif\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nbool IsAligned(void* pointer, size_t alignment) {\n  uintptr_t address = reinterpret_cast<uintptr_t>(pointer);\n  return (address & (alignment - 1)) == 0;\n}\n\nTEST(AlignedAllocator, AlignedVector) {\n  // Test a structure with natural alignment.\n  struct NaturalAlignedStruct {\n    int i;\n  };\n\n  AlignedVector<NaturalAlignedStruct> natural_aligned_vector;\n  natural_aligned_vector.push_back(NaturalAlignedStruct());\n  EXPECT_TRUE(\n      IsAligned(&natural_aligned_vector[0], alignof(NaturalAlignedStruct)));\n\n  natural_aligned_vector.resize(3);\n  EXPECT_TRUE(\n      IsAligned(&natural_aligned_vector[0], alignof(NaturalAlignedStruct)));\n  EXPECT_TRUE(\n      IsAligned(&natural_aligned_vector[1], alignof(NaturalAlignedStruct)));\n  EXPECT_TRUE(\n      IsAligned(&natural_aligned_vector[2], alignof(NaturalAlignedStruct)));\n\n  // Test a structure that declares its own alignment.\n  struct alignas(16) AlignedStruct {\n    int i;\n  };\n  ASSERT_EQ(alignof(AlignedStruct), 16u);\n\n  AlignedVector<AlignedStruct> aligned_vector;\n  aligned_vector.push_back(AlignedStruct());\n  EXPECT_TRUE(IsAligned(&aligned_vector[0], alignof(AlignedStruct)));\n\n  aligned_vector.resize(3);\n  EXPECT_TRUE(IsAligned(&aligned_vector[0], alignof(AlignedStruct)));\n  EXPECT_TRUE(IsAligned(&aligned_vector[1], alignof(AlignedStruct)));\n  EXPECT_TRUE(IsAligned(&aligned_vector[2], alignof(AlignedStruct)));\n\n  // Try a custom alignment. Since the structure itself doesn’t specify an\n  // alignment constraint, only the base address will be aligned to the\n  // requested boundary.\n  AlignedVector<NaturalAlignedStruct, 32> custom_aligned_vector;\n  custom_aligned_vector.push_back(NaturalAlignedStruct());\n  EXPECT_TRUE(IsAligned(&custom_aligned_vector[0], 32));\n\n  // Try a structure with a pretty big alignment request.\n  struct alignas(64) BigAlignedStruct {\n    int i;\n  };\n  ASSERT_EQ(alignof(BigAlignedStruct), 64u);\n\n  AlignedVector<BigAlignedStruct> big_aligned_vector;\n  big_aligned_vector.push_back(BigAlignedStruct());\n  EXPECT_TRUE(IsAligned(&big_aligned_vector[0], alignof(BigAlignedStruct)));\n\n  big_aligned_vector.resize(3);\n  EXPECT_TRUE(IsAligned(&big_aligned_vector[0], alignof(BigAlignedStruct)));\n  EXPECT_TRUE(IsAligned(&big_aligned_vector[1], alignof(BigAlignedStruct)));\n  EXPECT_TRUE(IsAligned(&big_aligned_vector[2], alignof(BigAlignedStruct)));\n}\n\nvoid BadAlignmentTest() {\n#if BUILDFLAG(IS_WIN)\n  // Suppress the assertion MessageBox() normally displayed by the CRT in debug\n  // mode. In release mode, _CrtSetReportMode() is #defined to ((int)0), so\n  // |previous| would appear unused, thus the [[maybe_unused]].\n  [[maybe_unused]] int previous =\n      _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);\n#endif\n\n  // Alignment constraints must be powers of 2. 7 is not valid.\n  AlignedVector<int, 7> bad_aligned_vector;\n  bad_aligned_vector.push_back(0);\n\n#if BUILDFLAG(IS_WIN)\n  _CrtSetReportMode(_CRT_ASSERT, previous);\n#endif\n}\n\nTEST(AlignedAllocatorDeathTest, BadAlignment) {\n  ASSERT_DEATH_CRASH(BadAlignmentTest(), \"\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stdlib/map_insert.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_\n#define CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_\n\n#include <map>\n#include <utility>\n\nnamespace crashpad {\n\n//! \\brief Inserts a mapping from \\a key to \\a value into \\a map, or replaces\n//!     an existing mapping so that \\a key maps to \\a value.\n//!\n//! This behaves similarly to `std::map<>::%insert_or_assign()` proposed for\n//! C++17, except that the \\a old_value parameter is added.\n//!\n//! \\param[in,out] map The map to operate on.\n//! \\param[in] key The key that should be mapped to \\a value.\n//! \\param[in] value The value that \\a key should map to.\n//! \\param[out] old_value If \\a key was previously present in \\a map, this will\n//!     be set to its previous value. This parameter is optional and may be\n//!     `nullptr` if this information is not required.\n//!\n//! \\return `false` if \\a key was previously present in \\a map. If \\a old_value\n//!     is not `nullptr`, it will be set to the previous value. `true` if \\a\n//!     key was not present in the map and was inserted.\ntemplate <typename T>\nbool MapInsertOrReplace(T* map,\n                        const typename T::key_type& key,\n                        const typename T::mapped_type& value,\n                        typename T::mapped_type* old_value) {\n  const auto result = map->insert(std::make_pair(key, value));\n  if (!result.second) {\n    if (old_value) {\n      *old_value = result.first->second;\n    }\n    result.first->second = value;\n  }\n  return result.second;\n}\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STDLIB_MAP_INSERT_H_\n"
  },
  {
    "path": "util/stdlib/map_insert_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/map_insert.h\"\n\n#include <string>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(MapInsert, MapInsertOrReplace) {\n  std::map<std::string, int> map;\n  int old_value;\n  EXPECT_TRUE(MapInsertOrReplace(&map, \"key\", 1, &old_value));\n  std::map<std::string, int> expect_map;\n  expect_map[\"key\"] = 1;\n  EXPECT_EQ(map, expect_map);\n\n  EXPECT_FALSE(MapInsertOrReplace(&map, \"key\", 2, &old_value));\n  EXPECT_EQ(old_value, 1);\n  expect_map[\"key\"] = 2;\n  EXPECT_EQ(map, expect_map);\n\n  EXPECT_TRUE(MapInsertOrReplace(&map, \"another\", 3, &old_value));\n  expect_map[\"another\"] = 3;\n  EXPECT_EQ(map, expect_map);\n\n  // Make sure nullptr is accepted as old_value.\n  EXPECT_TRUE(MapInsertOrReplace(&map, \"yet another\", 5, nullptr));\n  expect_map[\"yet another\"] = 5;\n  EXPECT_EQ(map, expect_map);\n\n  EXPECT_FALSE(MapInsertOrReplace(&map, \"yet another\", 6, nullptr));\n  expect_map[\"yet another\"] = 6;\n  EXPECT_EQ(map, expect_map);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stdlib/objc.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STDLIB_OBJC_H_\n#define CRASHPAD_UTIL_STDLIB_OBJC_H_\n\n#include <Availability.h>\n#include <objc/objc.h>\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_8\n\n// In order for the @NO and @YES literals to work, NO and YES must be defined as\n// __objc_no and __objc_yes. See\n// https://clang.llvm.org/docs/ObjectiveCLiterals.html.\n//\n// NO and YES are defined properly for this purpose in the 10.8 SDK, but not in\n// earlier SDKs. Because this code is never expected to be compiled with a\n// compiler that does not understand the modern forms of these boolean\n// constants, but it may be built with an older SDK, replace the outdated SDK\n// definitions unconditionally.\n#undef NO\n#undef YES\n#define NO __objc_no\n#define YES __objc_yes\n\n#endif\n\n#endif  // CRASHPAD_UTIL_STDLIB_OBJC_H_\n"
  },
  {
    "path": "util/stdlib/string_number_conversion.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/string_number_conversion.h\"\n\n#include <errno.h>\n#include <inttypes.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <limits>\n\n#include \"base/strings/string_util.h\"\n\nnamespace {\n\ntemplate <typename TIntType, typename TLongType>\nstruct StringToIntegerTraits {\n  using IntType = TIntType;\n  using LongType = TLongType;\n  static void TypeCheck() {\n    static_assert(std::numeric_limits<TIntType>::is_integer &&\n                      std::numeric_limits<TLongType>::is_integer,\n                  \"IntType and LongType must be integer\");\n    static_assert(std::numeric_limits<TIntType>::is_signed ==\n                      std::numeric_limits<TLongType>::is_signed,\n                  \"IntType and LongType signedness must agree\");\n    static_assert(std::numeric_limits<TIntType>::min() >=\n                          std::numeric_limits<TLongType>::min() &&\n                      std::numeric_limits<TIntType>::min() <\n                          std::numeric_limits<TLongType>::max(),\n                  \"IntType min must be in LongType range\");\n    static_assert(std::numeric_limits<TIntType>::max() >\n                          std::numeric_limits<TLongType>::min() &&\n                      std::numeric_limits<TIntType>::max() <=\n                          std::numeric_limits<TLongType>::max(),\n                  \"IntType max must be in LongType range\");\n  }\n};\n\ntemplate <typename TIntType, typename TLongType>\nstruct StringToSignedIntegerTraits\n    : public StringToIntegerTraits<TIntType, TLongType> {\n  static void TypeCheck() {\n    static_assert(std::numeric_limits<TIntType>::is_signed,\n                  \"StringToSignedTraits IntType must be signed\");\n    return super::TypeCheck();\n  }\n  static bool IsNegativeOverflow(TLongType value) {\n    return value < std::numeric_limits<TIntType>::min();\n  }\n\n private:\n  using super = StringToIntegerTraits<TIntType, TLongType>;\n};\n\ntemplate <typename TIntType, typename TLongType>\nstruct StringToUnsignedIntegerTraits\n    : public StringToIntegerTraits<TIntType, TLongType> {\n  static void TypeCheck() {\n    static_assert(!std::numeric_limits<TIntType>::is_signed,\n                  \"StringToUnsignedTraits IntType must be unsigned\");\n    return super::TypeCheck();\n  }\n  static bool IsNegativeOverflow(TLongType value) { return false; }\n\n private:\n  using super = StringToIntegerTraits<TIntType, TLongType>;\n};\n\nstruct StringToIntTraits : public StringToSignedIntegerTraits<int, long> {\n  static LongType Convert(const char* str, char** end, int base) {\n    return strtol(str, end, base);\n  }\n};\n\nstruct StringToUnsignedIntTraits\n    : public StringToUnsignedIntegerTraits<unsigned int, unsigned long> {\n  static LongType Convert(const char* str, char** end, int base) {\n    if (str[0] == '-') {\n      *end = const_cast<char*>(str);\n      return 0;\n    }\n    return strtoul(str, end, base);\n  }\n};\n\nstruct StringToLongTraits\n    : public StringToSignedIntegerTraits<long, long long> {\n  static LongType Convert(const char* str, char** end, int base) {\n    return strtoll(str, end, base);\n  }\n};\n\nstruct StringToUnsignedLongTraits\n    : public StringToUnsignedIntegerTraits<unsigned long, unsigned long long> {\n  static LongType Convert(const char* str, char** end, int base) {\n    if (str[0] == '-') {\n      *end = const_cast<char*>(str);\n      return 0;\n    }\n    return strtoull(str, end, base);\n  }\n};\n\nstruct StringToLongLongTraits\n    : public StringToSignedIntegerTraits<long long, long long> {\n  static LongType Convert(const char* str, char** end, int base) {\n    return strtoll(str, end, base);\n  }\n};\n\nstruct StringToUnsignedLongLongTraits\n    : public StringToUnsignedIntegerTraits<unsigned long long,\n                                           unsigned long long> {\n  static LongType Convert(const char* str, char** end, int base) {\n    if (str[0] == '-') {\n      *end = const_cast<char*>(str);\n      return 0;\n    }\n    return strtoull(str, end, base);\n  }\n};\n\ntemplate <typename Traits>\nbool StringToIntegerInternal(const std::string& string,\n                             typename Traits::IntType* number) {\n  using IntType = typename Traits::IntType;\n  using LongType = typename Traits::LongType;\n\n  Traits::TypeCheck();\n\n  if (string.empty() || base::IsAsciiWhitespace(string[0])) {\n    return false;\n  }\n\n  errno = 0;\n  char* end;\n  LongType result = Traits::Convert(string.data(), &end, 0);\n  if (Traits::IsNegativeOverflow(result) ||\n      result > std::numeric_limits<IntType>::max() ||\n      errno == ERANGE ||\n      end != string.data() + string.length()) {\n    return false;\n  }\n  *number = static_cast<IntType>(result);\n  return true;\n}\n\n}  // namespace\n\nnamespace crashpad {\n\nbool StringToNumber(const std::string& string, int* number) {\n  return StringToIntegerInternal<StringToIntTraits>(string, number);\n}\n\nbool StringToNumber(const std::string& string, unsigned int* number) {\n  return StringToIntegerInternal<StringToUnsignedIntTraits>(string, number);\n}\n\nbool StringToNumber(const std::string& string, long* number) {\n  return StringToIntegerInternal<StringToLongTraits>(string, number);\n}\n\nbool StringToNumber(const std::string& string, unsigned long* number) {\n  return StringToIntegerInternal<StringToUnsignedLongTraits>(string, number);\n}\n\nbool StringToNumber(const std::string& string, long long* number) {\n  return StringToIntegerInternal<StringToLongLongTraits>(string, number);\n}\n\nbool StringToNumber(const std::string& string, unsigned long long* number) {\n  return StringToIntegerInternal<StringToUnsignedLongLongTraits>(string,\n                                                                 number);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stdlib/string_number_conversion.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_\n#define CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_\n\n#include <string>\n\nnamespace crashpad {\n\n// Convert between strings and numbers.\n//\n// These functions will only set *number if a perfect conversion can be\n// performed. A perfect conversion contains no leading or trailing characters\n// (including whitespace) other than the number to convert, and does not\n// overflow the targeted data type. If a perfect conversion is possible, *number\n// is set and these functions return true. Otherwise, they return false.\n//\n// The interface in base/strings/string_number_conversions.h doesn’t allow\n// arbitrary bases based on whether the string begins with prefixes such as \"0x\"\n// as strtol does with base = 0. The functions here are implemented on the\n// strtol family with base = 0, and thus do accept such input.\n\n//! \\{\n//! \\brief Convert a string to a number.\n//!\n//! A conversion will only be performed if it can be done perfectly: if \\a\n//! string contains no leading or trailing characters (including whitespace)\n//! other than the number to convert, and does not overflow the targeted data\n//! type.\n//!\n//! \\param[in] string The string to convert to a number. As in `strtol()` with a\n//!     `base` parameter of `0`, the string is treated as decimal unless it\n//!     begins with a `\"0x\"` or `\"0X\"` prefix, in which case it is treated as\n//!     hexadecimal, or a `\"0\"` prefix, in which case it is treated as octal.\n//! \\param[out] number The converted number. This will only be set if a perfect\n//!     conversion can be performed.\n//!\n//! \\return `true` if a perfect conversion could be performed, with \\a number\n//!     set appropriately. `false` if a perfect conversion was not possible.\n//!\n//! \\note The interface in `base/strings/string_number_conversions.h` doesn’t\n//!     allow arbitrary bases based on whether the string begins with a prefix\n//!     indicating its base. The functions here are provided for situations\n//!     where such prefix recognition is desirable.\nbool StringToNumber(const std::string& string, int* number);\nbool StringToNumber(const std::string& string, unsigned int* number);\nbool StringToNumber(const std::string& string, long* number);\nbool StringToNumber(const std::string& string, unsigned long* number);\nbool StringToNumber(const std::string& string, long long* number);\nbool StringToNumber(const std::string& string, unsigned long long* number);\n//! \\}\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STDLIB_STRING_NUMBER_CONVERSION_H_\n"
  },
  {
    "path": "util/stdlib/string_number_conversion_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/string_number_conversion.h\"\n\n#include <sys/types.h>\n\n#include <array>\n#include <iterator>\n#include <limits>\n#include <type_traits>\n\n#include \"gtest/gtest.h\"\n\n#define STRINGIFY(a) STR(a)\n#define STR(a) #a\n\ntemplate <typename TValueType>\nstruct TestSpecification {\n  const char* string;\n  bool valid;\n  TValueType value;\n};\n\n// Signed 32-bit test data\ntemplate <typename TIntType,\n          typename std::enable_if<std::is_integral<TIntType>::value &&\n                                  std::is_signed<TIntType>::value &&\n                                  (sizeof(TIntType) == 4)>::type* = nullptr>\nstatic constexpr std::array<TestSpecification<TIntType>, 61> kTestDataFunc() {\n  return {{\n      {\"\", false, 0},\n      {\"0\", true, 0},\n      {\"1\", true, 1},\n      {\"2147483647\", true, std::numeric_limits<TIntType>::max()},\n      {\"2147483648\", false, 0},\n      {\"4294967295\", false, 0},\n      {\"4294967296\", false, 0},\n      {\"-1\", true, -1},\n      {\"-2147483648\", true, std::numeric_limits<TIntType>::min()},\n      {\"-2147483649\", false, 0},\n      {\"00\", true, 0},\n      {\"01\", true, 1},\n      {\"-01\", true, -1},\n      {\"+2\", true, 2},\n      {\"0x10\", true, 16},\n      {\"-0x10\", true, -16},\n      {\"+0x20\", true, 32},\n      {\"0xf\", true, 15},\n      {\"0xg\", false, 0},\n      {\"0x7fffffff\", true, std::numeric_limits<TIntType>::max()},\n      {\"0x7FfFfFfF\", true, std::numeric_limits<TIntType>::max()},\n      {\"0x80000000\", false, 0},\n      {\"0xFFFFFFFF\", false, 0},\n      {\"-0x7fffffff\", true, -2147483647},\n      {\"-0x80000000\", true, std::numeric_limits<TIntType>::min()},\n      {\"-0x80000001\", false, 0},\n      {\"-0xffffffff\", false, 0},\n      {\"0x100000000\", false, 0},\n      {\"0xabcdef\", true, 11259375},\n      {\"010\", true, 8},\n      {\"-010\", true, -8},\n      {\"+020\", true, 16},\n      {\"07\", true, 7},\n      {\"08\", false, 0},\n      {\" 0\", false, 0},\n      {\"0 \", false, 0},\n      {\" 0 \", false, 0},\n      {\" 1\", false, 0},\n      {\"1 \", false, 0},\n      {\" 1 \", false, 0},\n      {\"a2\", false, 0},\n      {\"2a\", false, 0},\n      {\"2a2\", false, 0},\n      {\".0\", false, 0},\n      {\".1\", false, 0},\n      {\"-.2\", false, 0},\n      {\"+.3\", false, 0},\n      {\"1.23\", false, 0},\n      {\"-273.15\", false, 0},\n      {\"+98.6\", false, 0},\n      {\"1e1\", false, 0},\n      {\"1E1\", false, 0},\n      {\"0x123p4\", false, 0},\n      {\"infinity\", false, 0},\n      {\"NaN\", false, 0},\n      {\"-9223372036854775810\", false, 0},\n      {\"-9223372036854775809\", false, 0},\n      {\"9223372036854775808\", false, 0},\n      {\"9223372036854775809\", false, 0},\n      {\"18446744073709551615\", false, 0},\n      {\"18446744073709551616\", false, 0},\n  }};\n}\n\n// Unsigned 32-bit test data\ntemplate <typename TIntType,\n          typename std::enable_if<std::is_integral<TIntType>::value &&\n                                  !std::is_signed<TIntType>::value &&\n                                  (sizeof(TIntType) == 4)>::type* = nullptr>\nstatic constexpr std::array<TestSpecification<TIntType>, 61> kTestDataFunc() {\n  return {{\n      {\"\", false, 0},\n      {\"0\", true, 0},\n      {\"1\", true, 1},\n      {\"2147483647\", true, 2147483647},\n      {\"2147483648\", true, 2147483648},\n      {\"4294967295\", true, std::numeric_limits<TIntType>::max()},\n      {\"4294967296\", false, 0},\n      {\"-1\", false, 0},\n      {\"-2147483648\", false, 0},\n      {\"-2147483649\", false, 0},\n      {\"00\", true, 0},\n      {\"01\", true, 1},\n      {\"-01\", false, 0},\n      {\"+2\", true, 2},\n      {\"0x10\", true, 16},\n      {\"-0x10\", false, 0},\n      {\"+0x20\", true, 32},\n      {\"0xf\", true, 15},\n      {\"0xg\", false, 0},\n      {\"0x7fffffff\", true, 0x7fffffff},\n      {\"0x7FfFfFfF\", true, 0x7fffffff},\n      {\"0x80000000\", true, 0x80000000},\n      {\"0xFFFFFFFF\", true, 0xffffffff},\n      {\"-0x7fffffff\", false, 0},\n      {\"-0x80000000\", false, 0},\n      {\"-0x80000001\", false, 0},\n      {\"-0xffffffff\", false, 0},\n      {\"0x100000000\", false, 0},\n      {\"0xabcdef\", true, 11259375},\n      {\"010\", true, 8},\n      {\"-010\", false, 0},\n      {\"+020\", true, 16},\n      {\"07\", true, 7},\n      {\"08\", false, 0},\n      {\" 0\", false, 0},\n      {\"0 \", false, 0},\n      {\" 0 \", false, 0},\n      {\" 1\", false, 0},\n      {\"1 \", false, 0},\n      {\" 1 \", false, 0},\n      {\"a2\", false, 0},\n      {\"2a\", false, 0},\n      {\"2a2\", false, 0},\n      {\".0\", false, 0},\n      {\".1\", false, 0},\n      {\"-.2\", false, 0},\n      {\"+.3\", false, 0},\n      {\"1.23\", false, 0},\n      {\"-273.15\", false, 0},\n      {\"+98.6\", false, 0},\n      {\"1e1\", false, 0},\n      {\"1E1\", false, 0},\n      {\"0x123p4\", false, 0},\n      {\"infinity\", false, 0},\n      {\"NaN\", false, 0},\n      {\"-9223372036854775810\", false, 0},\n      {\"-9223372036854775809\", false, 0},\n      {\"9223372036854775808\", false, 0},\n      {\"9223372036854775809\", false, 0},\n      {\"18446744073709551615\", false, 0},\n      {\"18446744073709551616\", false, 0},\n  }};\n}\n\n// Signed 64-bit test data\ntemplate <typename TIntType,\n          typename std::enable_if<std::is_integral<TIntType>::value &&\n                                  std::is_signed<TIntType>::value &&\n                                  (sizeof(TIntType) == 8)>::type* = nullptr>\nstatic constexpr std::array<TestSpecification<TIntType>, 24> kTestDataFunc() {\n  return {{\n      {\"\", false, 0},\n      {\"0\", true, 0},\n      {\"1\", true, 1},\n      {\"2147483647\", true, 2147483647},\n      {\"2147483648\", true, 2147483648},\n      {\"4294967295\", true, 4294967295},\n      {\"4294967296\", true, 4294967296},\n      {\"9223372036854775807\", true, std::numeric_limits<TIntType>::max()},\n      {\"9223372036854775808\", false, 0},\n      {\"18446744073709551615\", false, 0},\n      {\"18446744073709551616\", false, 0},\n      {\"-1\", true, -1},\n      {\"-2147483648\", true, INT64_C(-2147483648)},\n      {\"-2147483649\", true, INT64_C(-2147483649)},\n      {\"-9223372036854775808\", true, std::numeric_limits<TIntType>::min()},\n      {\"-9223372036854775809\", false, 0},\n      {\"0x7fffffffffffffff\", true, std::numeric_limits<TIntType>::max()},\n      {\"0x8000000000000000\", false, 0},\n      {\"0xffffffffffffffff\", false, 0},\n      {\"0x10000000000000000\", false, 0},\n      {\"-0x7fffffffffffffff\", true, -9223372036854775807},\n      {\"-0x8000000000000000\", true, std::numeric_limits<TIntType>::min()},\n      {\"-0x8000000000000001\", false, 0},\n      {\"0x7Fffffffffffffff\", true, std::numeric_limits<TIntType>::max()},\n  }};\n}\n\n// Unsigned 64-bit test data\ntemplate <typename TIntType,\n          typename std::enable_if<std::is_integral<TIntType>::value &&\n                                  !std::is_signed<TIntType>::value &&\n                                  (sizeof(TIntType) == 8)>::type* = nullptr>\nstatic constexpr std::array<TestSpecification<TIntType>, 25> kTestDataFunc() {\n  return {{\n      {\"\", false, 0},\n      {\"0\", true, 0},\n      {\"1\", true, 1},\n      {\"2147483647\", true, 2147483647},\n      {\"2147483648\", true, 2147483648},\n      {\"4294967295\", true, 4294967295},\n      {\"4294967296\", true, 4294967296},\n      {\"9223372036854775807\", true, 9223372036854775807},\n      {\"9223372036854775808\", true, 9223372036854775808u},\n      {\"18446744073709551615\", true, std::numeric_limits<TIntType>::max()},\n      {\"18446744073709551616\", false, 0},\n      {\"-1\", false, 0},\n      {\"-2147483648\", false, 0},\n      {\"-2147483649\", false, 0},\n      {\"-2147483648\", false, 0},\n      {\"-9223372036854775808\", false, 0},\n      {\"-9223372036854775809\", false, 0},\n      {\"0x7fffffffffffffff\", true, 9223372036854775807},\n      {\"0x8000000000000000\", true, 9223372036854775808u},\n      {\"0xffffffffffffffff\", true, std::numeric_limits<TIntType>::max()},\n      {\"0x10000000000000000\", false, 0},\n      {\"-0x7fffffffffffffff\", false, 0},\n      {\"-0x8000000000000000\", false, 0},\n      {\"-0x8000000000000001\", false, 0},\n      {\"0xFfffffffffffffff\", true, std::numeric_limits<TIntType>::max()},\n  }};\n}\n\n// This string is split to avoid MSVC warning:\n//   \"decimal digit terminates octal escape sequence\".\nstatic constexpr char kEmbeddedNullInputRaw[] = \"6\\000\" \"6\";\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(StringNumberConversion, StringToInt) {\n  static_assert(sizeof(int) == 4, \"Test only configured for 32-bit int.\");\n  static constexpr auto kTestData = kTestDataFunc<int>();\n\n  for (size_t index = 0; index < kTestData.size(); ++index) {\n    int value;\n    bool valid = StringToNumber(kTestData[index].string, &value);\n    if (kTestData[index].valid) {\n      EXPECT_TRUE(valid) << \"index \" << index << \", string \"\n                         << kTestData[index].string;\n      if (valid) {\n        EXPECT_EQ(value, kTestData[index].value)\n            << \"index \" << index << \", string \" << kTestData[index].string;\n      }\n    } else {\n      EXPECT_FALSE(valid) << \"index \" << index << \", string \"\n                          << kTestData[index].string << \", value \" << value;\n    }\n  }\n\n  // Ensure that embedded NUL characters are treated as bad input. The string\n  // is split to avoid MSVC warning:\n  //   \"decimal digit terminates octal escape sequence\".\n  int output;\n  std::string kEmbeddedNullInput(kEmbeddedNullInputRaw,\n                                 std::size(kEmbeddedNullInputRaw) - 1);\n  EXPECT_FALSE(StringToNumber(kEmbeddedNullInput, &output));\n}\n\nTEST(StringNumberConversion, StringToUnsignedInt) {\n  static_assert(sizeof(unsigned int) == 4,\n                \"Test only configured for 32-bit unsigned int.\");\n  static constexpr auto kTestData = kTestDataFunc<unsigned int>();\n\n  for (size_t index = 0; index < kTestData.size(); ++index) {\n    unsigned int value;\n    bool valid = StringToNumber(kTestData[index].string, &value);\n    if (kTestData[index].valid) {\n      EXPECT_TRUE(valid) << \"index \" << index << \", string \"\n                         << kTestData[index].string;\n      if (valid) {\n        EXPECT_EQ(value, kTestData[index].value)\n            << \"index \" << index << \", string \" << kTestData[index].string;\n      }\n    } else {\n      EXPECT_FALSE(valid) << \"index \" << index << \", string \"\n                          << kTestData[index].string << \", value \" << value;\n    }\n  }\n\n  // Ensure that embedded NUL characters are treated as bad input. The string\n  // is split to avoid MSVC warning:\n  //   \"decimal digit terminates octal escape sequence\".\n  unsigned int output;\n  std::string kEmbeddedNullInput(kEmbeddedNullInputRaw,\n                                 std::size(kEmbeddedNullInputRaw) - 1);\n  EXPECT_FALSE(StringToNumber(kEmbeddedNullInput, &output));\n}\n\nTEST(StringNumberConversion, StringToLong) {\n  static_assert(\n      sizeof(long) == 4 || sizeof(long) == 8,\n      \"Test not configured for \" STRINGIFY(__SIZEOF_LONG__) \"-byte long\");\n  static constexpr auto kTestData = kTestDataFunc<long>();\n\n  for (size_t index = 0; index < kTestData.size(); ++index) {\n    long value;\n    bool valid = StringToNumber(kTestData[index].string, &value);\n    if (kTestData[index].valid) {\n      EXPECT_TRUE(valid) << \"index \" << index << \", string \"\n                         << kTestData[index].string;\n      if (valid) {\n        EXPECT_EQ(value, kTestData[index].value)\n            << \"index \" << index << \", string \" << kTestData[index].string;\n      }\n    } else {\n      EXPECT_FALSE(valid) << \"index \" << index << \", string \"\n                          << kTestData[index].string << \", value \" << value;\n    }\n  }\n}\n\nTEST(StringNumberConversion, StringToUnsignedLong) {\n  static_assert(\n      sizeof(long) == 4 || sizeof(long) == 8,\n      \"Test not configured for \" STRINGIFY(__SIZEOF_LONG__) \"-byte long\");\n  static constexpr auto kTestData = kTestDataFunc<unsigned long>();\n\n  for (size_t index = 0; index < kTestData.size(); ++index) {\n    unsigned long value;\n    bool valid = StringToNumber(kTestData[index].string, &value);\n    if (kTestData[index].valid) {\n      EXPECT_TRUE(valid) << \"index \" << index << \", string \"\n                         << kTestData[index].string;\n      if (valid) {\n        EXPECT_EQ(value, kTestData[index].value)\n            << \"index \" << index << \", string \" << kTestData[index].string;\n      }\n    } else {\n      EXPECT_FALSE(valid) << \"index \" << index << \", string \"\n                          << kTestData[index].string << \", value \" << value;\n    }\n  }\n}\n\nTEST(StringNumberConversion, StringToLongLong) {\n  static_assert(sizeof(long long) == 8,\n                \"Test only configured for 64-bit long long.\");\n  static constexpr auto kTestData = kTestDataFunc<long long>();\n\n  for (size_t index = 0; index < kTestData.size(); ++index) {\n    long long value;\n    bool valid = StringToNumber(kTestData[index].string, &value);\n    if (kTestData[index].valid) {\n      EXPECT_TRUE(valid) << \"index \" << index << \", string \"\n                         << kTestData[index].string;\n      if (valid) {\n        EXPECT_EQ(value, kTestData[index].value)\n            << \"index \" << index << \", string \" << kTestData[index].string;\n      }\n    } else {\n      EXPECT_FALSE(valid) << \"index \" << index << \", string \"\n                          << kTestData[index].string << \", value \" << value;\n    }\n  }\n}\n\nTEST(StringNumberConversion, StringToUnsignedLongLong) {\n  static_assert(sizeof(unsigned long long) == 8,\n                \"Test only configured for 64-bit unsigned long long.\");\n  static constexpr auto kTestData = kTestDataFunc<unsigned long long>();\n\n  for (size_t index = 0; index < kTestData.size(); ++index) {\n    unsigned long long value;\n    bool valid = StringToNumber(kTestData[index].string, &value);\n    if (kTestData[index].valid) {\n      EXPECT_TRUE(valid) << \"index \" << index << \", string \"\n                         << kTestData[index].string;\n      if (valid) {\n        EXPECT_EQ(value, kTestData[index].value)\n            << \"index \" << index << \", string \" << kTestData[index].string;\n      }\n    } else {\n      EXPECT_FALSE(valid) << \"index \" << index << \", string \"\n                          << kTestData[index].string << \", value \" << value;\n    }\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stdlib/strlcpy.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/strlcpy.h\"\n\n#include <string>\n\nnamespace crashpad {\n\nsize_t c16lcpy(char16_t* destination, const char16_t* source, size_t length) {\n  size_t source_length = std::char_traits<char16_t>::length(source);\n  if (source_length < length) {\n    std::char_traits<char16_t>::copy(destination, source, source_length + 1);\n  } else if (length != 0) {\n    std::char_traits<char16_t>::copy(destination, source, length - 1);\n    destination[length - 1] = '\\0';\n  }\n  return source_length;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stdlib/strlcpy.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STDLIB_STRLCPY_H_\n#define CRASHPAD_UTIL_STDLIB_STRLCPY_H_\n\n#include <sys/types.h>\n#include <uchar.h>\n\nnamespace crashpad {\n\n//! \\brief Copy a `NUL`-terminated char16_t-based string to a fixed-size buffer.\n//!\n//! This function behaves identically to `strlcpy()`, but it operates on\n//! char16_t data instead of `char` data. It copies the `NUL`-terminated string\n//! in the buffer beginning at \\a source to the buffer of size \\a length at \\a\n//! destination, ensuring that the destination buffer is `NUL`-terminated. No\n//! data will be written outside of the \\a destination buffer, but if \\a length\n//! is smaller than the length of the string at \\a source, the string will be\n//! truncated.\n//!\n//! \\param[out] destination A pointer to a buffer of at least size \\a length\n//!     char16_t units (not bytes). The string will be copied to this buffer,\n//!     possibly with truncation, and `NUL`-terminated. Nothing will be written\n//!     following the `NUL` terminator.\n//! \\param[in] source A pointer to a `NUL`-terminated string of char16_t data.\n//!     The `NUL` terminator must be a `NUL` value in a char16_t unit, not just\n//!     a single `NUL` byte.\n//! \\param[in] length The length of the \\a destination buffer in char16_t units,\n//!     not bytes. A maximum of \\a `length - 1` char16_t units from \\a source\n//!     will be copied to \\a destination.\n//!\n//! \\return The length of the \\a source string in char16_t units, not including\n//!     its `NUL` terminator. When truncation occurs, the return value will be\n//!     equal to or greater than than the \\a length parameter.\nsize_t c16lcpy(char16_t* destination, const char16_t* source, size_t length);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STDLIB_STRLCPY_H_\n"
  },
  {
    "path": "util/stdlib/strlcpy_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/strlcpy.h\"\n\n#include <string.h>\n#include <wchar.h>\n\n#include <algorithm>\n#include <iterator>\n#include <string>\n\n#include \"base/format_macros.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nsize_t C16Len(const char16_t* s) {\n  return std::char_traits<char16_t>::length(s);\n}\n\nint C16Memcmp(const char16_t* s1, const char16_t* s2, size_t n) {\n  return std::char_traits<char16_t>::compare(s1, s2, n);\n}\n\nTEST(strlcpy, c16lcpy) {\n  // Use a destination buffer that’s larger than the length passed to c16lcpy.\n  // The unused portion is a guard area that must not be written to.\n  struct TestBuffer {\n    char16_t lead_guard[64];\n    char16_t data[128];\n    char16_t trail_guard[64];\n  };\n  TestBuffer expected_untouched;\n  memset(&expected_untouched, 0xa5, sizeof(expected_untouched));\n\n  // Test with M, é, Ā, ő, and Ḙ. This is a mix of characters that have zero and\n  // nonzero low and high bytes.\n  static constexpr char16_t test_characters[] = {\n      0x4d, 0xe9, 0x100, 0x151, 0x1e18};\n\n  for (size_t index = 0; index < std::size(test_characters); ++index) {\n    char16_t test_character = test_characters[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"character index %\" PRIuS \", character 0x%x\", index, test_character));\n    for (size_t length = 0; length < 256; ++length) {\n      SCOPED_TRACE(\n          base::StringPrintf(\"index %\" PRIuS, length));\n      std::u16string test_string(length, test_character);\n\n      TestBuffer destination;\n      memset(&destination, 0xa5, sizeof(destination));\n\n      EXPECT_EQ(c16lcpy(destination.data,\n                        test_string.c_str(),\n                        std::size(destination.data)),\n                length);\n\n      // Make sure that the destination buffer is NUL-terminated, and that as\n      // much of the test string was copied as could fit.\n      size_t expected_destination_length =\n          std::min(length, std::size(destination.data) - 1);\n\n      EXPECT_EQ(destination.data[expected_destination_length], '\\0');\n      EXPECT_EQ(C16Len(destination.data), expected_destination_length);\n      EXPECT_TRUE(C16Memcmp(test_string.c_str(),\n                            destination.data,\n                            expected_destination_length) == 0);\n\n      // Make sure that the portion of the destination buffer that was not used\n      // was not touched. This includes the guard areas and the unused portion\n      // of the buffer passed to c16lcpy.\n      EXPECT_TRUE(C16Memcmp(expected_untouched.lead_guard,\n                            destination.lead_guard,\n                            std::size(destination.lead_guard)) == 0);\n      size_t expected_untouched_length =\n          std::size(destination.data) - expected_destination_length - 1;\n      EXPECT_TRUE(C16Memcmp(expected_untouched.data,\n                            &destination.data[expected_destination_length + 1],\n                            expected_untouched_length) == 0);\n      EXPECT_TRUE(C16Memcmp(expected_untouched.trail_guard,\n                            destination.trail_guard,\n                            std::size(destination.trail_guard)) == 0);\n    }\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stdlib/strnlen.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/strnlen.h\"\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_7\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n// Redeclare a method only available on Mac OS X 10.7 and later to suppress a\n// -Wpartial-availability warning.\nextern \"C\" {\nsize_t strnlen(const char* string, size_t max_length);\n}  // extern \"C\"\n#endif\n\nnamespace crashpad {\n\nsize_t strnlen(const char* string, size_t max_length) {\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7\n  if (::strnlen) {\n    return ::strnlen(string, max_length);\n  }\n#endif\n\n  for (size_t index = 0; index < max_length; ++index) {\n    if (string[index] == '\\0') {\n      return index;\n    }\n  }\n\n  return max_length;\n}\n\n}  // namespace crashpad\n\n#endif\n"
  },
  {
    "path": "util/stdlib/strnlen.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STDLIB_STRNLEN_H_\n#define CRASHPAD_UTIL_STDLIB_STRNLEN_H_\n\n#include <string.h>\n#include <sys/types.h>\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_MAC)\n#include <Availability.h>\n#endif\n\nnamespace crashpad {\n\n//! \\brief Returns the length of a string, not to exceed a maximum.\n//!\n//! \\param[in] string The string whose length is to be calculated.\n//! \\param[in] max_length The maximum length to return.\n//!\n//! \\return The length of \\a string, determined as the index of the first `NUL`\n//!     byte found, not exceeding \\a max_length.\n//!\n//! \\note This function is provided because it was introduced in POSIX.1-2008,\n//!     and not all systems’ standard libraries provide an implementation.\nsize_t strnlen(const char* string, size_t max_length);\n\n#if !BUILDFLAG(IS_MAC) || __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_7\ninline size_t strnlen(const char* string, size_t max_length) {\n  return ::strnlen(string, max_length);\n}\n#endif\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STDLIB_STRNLEN_H_\n"
  },
  {
    "path": "util/stdlib/strnlen_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/strnlen.h\"\n\n#include <string.h>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(strnlen, strnlen) {\n  static constexpr char kTestBuffer[] = \"abc\\0d\";\n  ASSERT_EQ(strlen(kTestBuffer), 3u);\n  EXPECT_EQ(crashpad::strnlen(kTestBuffer, 0), 0u);\n  EXPECT_EQ(crashpad::strnlen(kTestBuffer, 1), 1u);\n  EXPECT_EQ(crashpad::strnlen(kTestBuffer, 2), 2u);\n  EXPECT_EQ(crashpad::strnlen(kTestBuffer, 3), 3u);\n  EXPECT_EQ(crashpad::strnlen(kTestBuffer, 4), 3u);\n  EXPECT_EQ(crashpad::strnlen(kTestBuffer, 5), 3u);\n  EXPECT_EQ(crashpad::strnlen(kTestBuffer, 6), 3u);\n\n  static constexpr char kEmptyBuffer[] = \"\\0\";\n  ASSERT_EQ(strlen(kEmptyBuffer), 0u);\n  EXPECT_EQ(crashpad::strnlen(kEmptyBuffer, 0), 0u);\n  EXPECT_EQ(crashpad::strnlen(kEmptyBuffer, 1), 0u);\n  EXPECT_EQ(crashpad::strnlen(kEmptyBuffer, 2), 0u);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stdlib/thread_safe_vector.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STDLIB_THREAD_SAFE_VECTOR_H_\n#define CRASHPAD_UTIL_STDLIB_THREAD_SAFE_VECTOR_H_\n\n#include <utility>\n#include <vector>\n\n#include \"base/synchronization/lock.h\"\n\nnamespace crashpad {\n\n//! \\brief A wrapper for a `std::vector<>` that can be accessed safely from\n//!    multiple threads.\n//!\n//! This is not a drop-in replacement for `std::vector<>`. Only necessary\n//! operations are defined.\ntemplate <typename T>\nclass ThreadSafeVector {\n public:\n  ThreadSafeVector() : vector_(), lock_() {}\n\n  ThreadSafeVector(const ThreadSafeVector&) = delete;\n  ThreadSafeVector& operator=(const ThreadSafeVector&) = delete;\n\n  ~ThreadSafeVector() {}\n\n  //! \\brief Wraps `std::vector<>::%push_back()`.\n  void PushBack(const T& element) {\n    base::AutoLock lock_owner(lock_);\n    vector_.push_back(element);\n  }\n\n  //! \\brief Atomically clears the underlying vector and returns its previous\n  //!     contents.\n  std::vector<T> Drain() {\n    std::vector<T> contents;\n    {\n      base::AutoLock lock_owner(lock_);\n      std::swap(vector_, contents);\n    }\n    return contents;\n  }\n\n private:\n  std::vector<T> vector_;\n  base::Lock lock_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STDLIB_THREAD_SAFE_VECTOR_H_\n"
  },
  {
    "path": "util/stdlib/thread_safe_vector_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stdlib/thread_safe_vector.h\"\n\n#include <iterator>\n\n#include \"gtest/gtest.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr int kElementsPerThread = 100;\n\nclass ThreadSafeVectorTestThread : public Thread {\n public:\n  ThreadSafeVectorTestThread() : thread_safe_vector_(nullptr), start_(0) {}\n\n  ThreadSafeVectorTestThread(const ThreadSafeVectorTestThread&) = delete;\n  ThreadSafeVectorTestThread& operator=(const ThreadSafeVectorTestThread&) =\n      delete;\n\n  ~ThreadSafeVectorTestThread() {}\n\n  void SetTestParameters(ThreadSafeVector<int>* thread_safe_vector, int start) {\n    thread_safe_vector_ = thread_safe_vector;\n    start_ = start;\n  }\n\n  // Thread:\n  void ThreadMain() override {\n    for (int i = start_; i < start_ + kElementsPerThread; ++i) {\n      thread_safe_vector_->PushBack(i);\n    }\n  }\n\n private:\n  ThreadSafeVector<int>* thread_safe_vector_;\n  int start_;\n};\n\nTEST(ThreadSafeVector, ThreadSafeVector) {\n  ThreadSafeVector<int> thread_safe_vector;\n  std::vector<int> vector = thread_safe_vector.Drain();\n  EXPECT_TRUE(vector.empty());\n\n  ThreadSafeVectorTestThread threads[100];\n  for (size_t index = 0; index < std::size(threads); ++index) {\n    threads[index].SetTestParameters(\n        &thread_safe_vector, static_cast<int>(index * kElementsPerThread));\n  }\n\n  for (size_t index = 0; index < std::size(threads); ++index) {\n    threads[index].Start();\n\n    if (index % 10 == 0) {\n      // Drain the vector periodically to test that simultaneous Drain() and\n      // PushBack() operations work properly.\n      std::vector<int> drained = thread_safe_vector.Drain();\n      vector.insert(vector.end(), drained.begin(), drained.end());\n    }\n  }\n\n  for (ThreadSafeVectorTestThread& thread : threads) {\n    thread.Join();\n  }\n\n  std::vector<int> drained = thread_safe_vector.Drain();\n  vector.insert(vector.end(), drained.begin(), drained.end());\n  bool found[std::size(threads) * kElementsPerThread] = {};\n  EXPECT_EQ(vector.size(), std::size(found));\n  for (int element : vector) {\n    EXPECT_FALSE(found[element]) << element;\n    found[element] = true;\n  }\n\n  vector = thread_safe_vector.Drain();\n  EXPECT_TRUE(vector.empty());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/base94_output_stream.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/base94_output_stream.h\"\n\n#include <algorithm>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n\nnamespace crashpad {\n\nnamespace {\n// To improve the space efficiency, we can encode 14 bits into two symbols\n// if 14-bit number is less than 94^2 which is total number can be encoded\n// by 2 digits of base94 number, in another word, if 13 bit number is\n// smaller than 643, we could read one more bit, because even if the 14th\n// bit is 1, the 14-bit number doesn’t exceed the max value.\nconstexpr uint16_t kMaxValueOf14BitEncoding = (94 * 94 - 1) & 0x1FFF;\n\nconstexpr size_t kMaxBuffer = 4096;\n\ninline uint8_t EncodeByte(uint8_t byte) {\n  DCHECK(byte < 94);\n  return (byte >= 94u) ? 0xff : (byte + '!');\n}\n\ninline uint8_t DecodeByte(uint8_t byte) {\n  DCHECK(byte >= '!' && byte <= '~');\n  return std::min(static_cast<uint8_t>(byte - '!'), static_cast<uint8_t>(94));\n}\n\n}  // namespace\n\nBase94OutputStream::Base94OutputStream(\n    Mode mode,\n    std::unique_ptr<OutputStreamInterface> output_stream)\n    : mode_(mode),\n      output_stream_(std::move(output_stream)),\n      bit_buf_(0),\n      bit_count_(0),\n      symbol_buffer_(0),\n      flush_needed_(false),\n      flushed_(false) {\n  buffer_.reserve(kMaxBuffer);\n}\n\nBase94OutputStream::~Base94OutputStream() {\n  DCHECK(!flush_needed_);\n}\n\nbool Base94OutputStream::Write(const uint8_t* data, size_t size) {\n  DCHECK(!flushed_);\n  flush_needed_ = true;\n  return mode_ == Mode::kEncode ? Encode(data, size) : Decode(data, size);\n}\n\nbool Base94OutputStream::Flush() {\n  flushed_ = true;\n  if (flush_needed_) {\n    flush_needed_ = false;\n    if (!((mode_ == Mode::kEncode) ? FinishEncoding() : FinishDecoding()))\n      return false;\n  }\n  return output_stream_->Flush();\n}\n\nbool Base94OutputStream::Encode(const uint8_t* data, size_t size) {\n  const uint8_t* cur = data;\n  while (size--) {\n    bit_buf_ |= *(cur++) << bit_count_;\n    bit_count_ += 8;\n    if (bit_count_ < 14)\n      continue;\n\n    uint16_t block;\n    // Check if 13-bit or 14-bit data should be encoded.\n    if ((bit_buf_ & 0x1FFF) > kMaxValueOf14BitEncoding) {\n      block = bit_buf_ & 0x1FFF;\n      bit_buf_ >>= 13;\n      bit_count_ -= 13;\n    } else {\n      block = bit_buf_ & 0x3FFF;\n      bit_buf_ >>= 14;\n      bit_count_ -= 14;\n    }\n    buffer_.push_back(EncodeByte(block % 94));\n    buffer_.push_back(EncodeByte(base::saturated_cast<uint8_t>(block / 94)));\n\n    if (buffer_.size() > kMaxBuffer - 2 && !WriteOutputStream())\n      return false;\n  }\n  return WriteOutputStream();\n}\n\nbool Base94OutputStream::Decode(const uint8_t* data, size_t size) {\n  const uint8_t* cur = data;\n  while (size--) {\n    if (DecodeByte(*cur) == 94) {\n      LOG(ERROR) << \"Decode: invalid input\";\n      return false;\n    }\n    if (symbol_buffer_ == 0) {\n      symbol_buffer_ = *cur;\n      cur++;\n      continue;\n    }\n    uint16_t v = DecodeByte(symbol_buffer_) + DecodeByte(*cur) * 94;\n    cur++;\n    symbol_buffer_ = 0;\n    bit_buf_ |= v << bit_count_;\n    bit_count_ += (v & 0x1FFF) > kMaxValueOf14BitEncoding ? 13 : 14;\n    while (bit_count_ > 7) {\n      buffer_.push_back(bit_buf_ & 0xff);\n      bit_buf_ >>= 8;\n      bit_count_ -= 8;\n    }\n    if (buffer_.size() > kMaxBuffer - 2) {\n      if (!WriteOutputStream())\n        return false;\n    }\n  }\n  return WriteOutputStream();\n}\n\nbool Base94OutputStream::FinishEncoding() {\n  if (bit_count_ == 0)\n    return true;\n  // Up to 13 bits data is left over.\n  buffer_.push_back(EncodeByte(bit_buf_ % 94));\n  if (bit_buf_ > 93 || bit_count_ > 8)\n    buffer_.push_back(EncodeByte(base::saturated_cast<uint8_t>(bit_buf_ / 94)));\n  bit_count_ = 0;\n  bit_buf_ = 0;\n  return WriteOutputStream();\n}\n\nbool Base94OutputStream::FinishDecoding() {\n  // The left over bit is padding and all zero, if there is no symbol\n  // unprocessed.\n  if (symbol_buffer_ == 0) {\n    DCHECK(!bit_buf_);\n    return true;\n  }\n  bit_buf_ |= DecodeByte(symbol_buffer_) << bit_count_;\n  buffer_.push_back(bit_buf_ & 0xff);\n  bit_buf_ >>= 8;\n  // The remaining bits are either encode padding or zeros from bit shift.\n  DCHECK(!bit_buf_);\n  return WriteOutputStream();\n}\n\nbool Base94OutputStream::WriteOutputStream() {\n  if (buffer_.empty())\n    return true;\n\n  bool result = output_stream_->Write(buffer_.data(), buffer_.size());\n  buffer_.clear();\n  return result;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/base94_output_stream.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STREAM_BASE94_OUTPUT_STREAM_H_\n#define CRASHPAD_UTIL_STREAM_BASE94_OUTPUT_STREAM_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <vector>\n\n#include \"util/stream/output_stream_interface.h\"\n\nnamespace crashpad {\n\n//! \\brief This class implements Base94 encoding/decoding, it uses all\n//! printable characters except space for encoding, and no padding is required.\n//!\n//! This implementation uses two base94 symbols to encoding 13 or 14 bit data,\n//! To maximize encoding efficiency, 14-bit data is encoded into two base94\n//! symbols if its low 13-bit is less than 644 ( = 94^2 - 2^13), otherwise\n//! 13-bit data is encoded.\nclass Base94OutputStream : public OutputStreamInterface {\n public:\n  //! \\brief Whether this object is configured to encode or decode data.\n  enum class Mode : bool {\n    //! \\brief Data passed through this object is encoded.\n    kEncode = false,\n    //! \\brief Data passed through this object is decoded.\n    kDecode = true\n  };\n\n  //! \\param[in] mode The work mode of this object.\n  //! \\param[in] output_stream The output_stream that this object writes to.\n  Base94OutputStream(Mode mode,\n                     std::unique_ptr<OutputStreamInterface> output_stream);\n\n  Base94OutputStream(const Base94OutputStream&) = delete;\n  Base94OutputStream& operator=(const Base94OutputStream&) = delete;\n\n  ~Base94OutputStream() override;\n\n  // OutputStreamInterface:\n  bool Write(const uint8_t* data, size_t size) override;\n  bool Flush() override;\n\n private:\n  bool Encode(const uint8_t* data, size_t size);\n  bool Decode(const uint8_t* data, size_t size);\n  bool FinishEncoding();\n  bool FinishDecoding();\n  // Write encoded/decoded data to |output_stream_| and empty the |buffer_|.\n  bool WriteOutputStream();\n\n  Mode mode_;\n  std::unique_ptr<OutputStreamInterface> output_stream_;\n  std::vector<uint8_t> buffer_;\n  uint32_t bit_buf_;\n  // The number of valid bit in bit_buf_.\n  size_t bit_count_;\n  char symbol_buffer_;\n  bool flush_needed_;\n  bool flushed_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STREAM_BASE94_OUTPUT_STREAM_H_\n"
  },
  {
    "path": "util/stream/base94_output_stream_test.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/base94_output_stream.h\"\n\n#include <string.h>\n\n#include <algorithm>\n#include <iterator>\n#include <sstream>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/rand_util.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"util/stream/test_output_stream.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr size_t kLongDataLength = 4096 * 10;\n\nstd::string DumpInput(const uint8_t* input, size_t size) {\n  std::stringstream s;\n  size_t index = 0;\n  size_t byte_count = 0;\n  while (index < size) {\n    s << \"0x\" << std::hex << static_cast<int>(*(input + index++)) << \",\";\n    if (byte_count++ > 1024) {\n      s << \"\\n\";\n      byte_count = 0;\n    }\n  }\n  return s.str();\n}\n\nclass Base94OutputStreamTest : public testing::Test {\n public:\n  Base94OutputStreamTest() {}\n\n  Base94OutputStreamTest(const Base94OutputStreamTest&) = delete;\n  Base94OutputStreamTest& operator=(const Base94OutputStreamTest&) = delete;\n\n protected:\n  void SetUp() override {\n    auto output_stream = std::make_unique<TestOutputStream>();\n    encode_test_output_stream_ = output_stream.get();\n    encoder_ = std::make_unique<Base94OutputStream>(\n        Base94OutputStream::Mode::kEncode, std::move(output_stream));\n    output_stream = std::make_unique<TestOutputStream>();\n    decode_test_output_stream_ = output_stream.get();\n    decoder_ = std::make_unique<Base94OutputStream>(\n        Base94OutputStream::Mode::kDecode, std::move(output_stream));\n    output_stream = std::make_unique<TestOutputStream>();\n    round_trip_test_output_stream_ = output_stream.get();\n    round_trip_ = std::make_unique<Base94OutputStream>(\n        Base94OutputStream::Mode::kEncode,\n        std::make_unique<Base94OutputStream>(Base94OutputStream::Mode::kDecode,\n                                             std::move(output_stream)));\n  }\n\n  const uint8_t* BuildDeterministicInput(size_t size) {\n    deterministic_input_ = base::HeapArray<uint8_t>::WithSize(size);\n    while (size-- > 0)\n      deterministic_input_[size] = static_cast<uint8_t>(size);\n    return deterministic_input_.data();\n  }\n\n  const uint8_t* BuildRandomInput(size_t size) {\n    input_ = base::HeapArray<uint8_t>::Uninit(size);\n    base::RandBytes(input_);\n    return input_.data();\n  }\n\n  Base94OutputStream* round_trip() const { return round_trip_.get(); }\n  const TestOutputStream& round_trip_test_output_stream() const {\n    return *round_trip_test_output_stream_;\n  }\n\n  static void VerifyEncoding(const TestOutputStream& out,\n                             const std::string& expected) {\n    EXPECT_EQ(out.all_data().size(), expected.size());\n    EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()),\n              0);\n  }\n\n  static void VerifyDecoding(const TestOutputStream& out,\n                             const std::vector<uint8_t>& expected) {\n    EXPECT_EQ(out.all_data().size(), expected.size());\n    EXPECT_EQ(memcmp(out.all_data().data(), expected.data(), expected.size()),\n              0);\n  }\n\n  void RunTest(const std::string& text, const std::vector<uint8_t>& binary) {\n    EXPECT_TRUE(encoder_->Write(binary.data(), binary.size()));\n    EXPECT_TRUE(encoder_->Flush());\n    VerifyEncoding(*encode_test_output_stream_, text);\n    EXPECT_TRUE(decoder_->Write(reinterpret_cast<const uint8_t*>(text.data()),\n                                text.size()));\n    EXPECT_TRUE(decoder_->Flush());\n    VerifyDecoding(*decode_test_output_stream_, binary);\n  }\n\n  void VerifyRoundTrip(const std::vector<uint8_t>& expected) {\n    TestOutputStream* out = round_trip_test_output_stream_;\n    EXPECT_EQ(out->all_data().size(), expected.size());\n    EXPECT_EQ(memcmp(out->all_data().data(), expected.data(), expected.size()),\n              0);\n  }\n\n private:\n  std::unique_ptr<Base94OutputStream> encoder_;\n  std::unique_ptr<Base94OutputStream> decoder_;\n  std::unique_ptr<Base94OutputStream> round_trip_;\n  TestOutputStream* encode_test_output_stream_;\n  TestOutputStream* decode_test_output_stream_;\n  TestOutputStream* round_trip_test_output_stream_;\n  base::HeapArray<uint8_t> input_;\n  base::HeapArray<uint8_t> deterministic_input_;\n};\n\nTEST_F(Base94OutputStreamTest, Encoding) {\n  std::vector<uint8_t> binary = {0x0};\n  std::string text(\"!\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding1) {\n  std::vector<uint8_t> binary = {0x0, 0x0};\n  std::string text(\"!!!\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding2) {\n  std::vector<uint8_t> binary = {0x0, 0x0, 0x0};\n  std::string text(\"!!!!\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding3) {\n  std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0};\n  std::string text(\"!!!!!\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding4) {\n  std::vector<uint8_t> binary = {0x0, 0x0, 0x0, 0x0, 0x0};\n  std::string text(\"!!!!!!\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding10) {\n  std::vector<uint8_t> binary = {0xFF};\n  std::string text(\"d#\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding11) {\n  std::vector<uint8_t> binary = {0xFF, 0xFF};\n  std::string text(\".x(\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding12) {\n  std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF};\n  std::string text(\".xj6\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding13) {\n  std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF};\n  std::string text(\".x.x`\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Encoding14) {\n  std::vector<uint8_t> binary = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};\n  std::string text(\".x.x.x\\\"\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, Printable94) {\n  std::vector<uint8_t> binary = {\n      0x5e, 0x0,  0x47, 0xa0, 0x1d, 0x60, 0xa,  0xab, 0x41, 0x41, 0xa4, 0x9,\n      0x64, 0x71, 0x32, 0xc,  0x47, 0xf9, 0x20, 0x22, 0xa3, 0x44, 0xa0, 0x84,\n      0x15, 0xe0, 0xf2, 0x61, 0xfc, 0x4c, 0xb7, 0xe1, 0x39, 0x9b, 0x47, 0xff,\n      0x64, 0x21, 0x5c, 0x74, 0x91, 0xec, 0x52, 0x75, 0xa2, 0x51, 0x93, 0x4a,\n      0x5e, 0x45, 0x2d, 0xd8, 0xf5, 0xc0, 0xdc, 0x58, 0x33, 0x63, 0x69, 0x8b,\n      0x4d, 0xbd, 0x25, 0x39, 0x54, 0x77, 0xf0, 0xcc, 0x5e, 0xf1, 0x23, 0x81,\n      0x6,  0x21, 0x71, 0x28, 0x28, 0x2};\n  std::string text(\n      \"!\\\"#$%&'()*+,-./\"\n      \"0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`\"\n      \"abcdefghijklmnopqrstuvwxyz{|}~!\");\n  RunTest(text, binary);\n}\n\nTEST_F(Base94OutputStreamTest, WriteLongDataMultipleTimes) {\n  const uint8_t* input = BuildRandomInput(kLongDataLength);\n  SCOPED_TRACE(base::StringPrintf(\"Input: %s\",\n                                  DumpInput(input, kLongDataLength).c_str()));\n  // Call Write() a random number of times.\n  size_t index = 0;\n  while (index < kLongDataLength) {\n    size_t write_length =\n        std::min(static_cast<size_t>(base::RandInt(0, 4096 * 2)),\n                 kLongDataLength - index);\n    SCOPED_TRACE(\n        base::StringPrintf(\"index %zu, write_length %zu\", index, write_length));\n    EXPECT_TRUE(round_trip()->Write(input + index, write_length));\n    index += write_length;\n  }\n  EXPECT_TRUE(round_trip()->Flush());\n  EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength);\n  EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(),\n                   input,\n                   kLongDataLength),\n            0);\n}\n\nTEST_F(Base94OutputStreamTest, WriteDeterministicLongDataMultipleTimes) {\n  const uint8_t* input = BuildDeterministicInput(kLongDataLength);\n\n  static constexpr size_t kWriteLengths[] = {\n      4, 96, 40, kLongDataLength - 4 - 96 - 40};\n\n  size_t offset = 0;\n  for (size_t index = 0; index < std::size(kWriteLengths); ++index) {\n    const size_t write_length = kWriteLengths[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"offset %zu, write_length %zu\", offset, write_length));\n    EXPECT_TRUE(round_trip()->Write(input + offset, write_length));\n    offset += write_length;\n  }\n  EXPECT_TRUE(round_trip()->Flush());\n  EXPECT_EQ(round_trip_test_output_stream().all_data().size(), kLongDataLength);\n  EXPECT_EQ(memcmp(round_trip_test_output_stream().all_data().data(),\n                   input,\n                   kLongDataLength),\n            0);\n}\n\nTEST_F(Base94OutputStreamTest, NoWriteOrFlush) {\n  EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);\n  EXPECT_EQ(round_trip_test_output_stream().flush_count(), 0u);\n  EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());\n}\n\nTEST_F(Base94OutputStreamTest, FlushWithoutWrite) {\n  EXPECT_TRUE(round_trip()->Flush());\n  EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);\n  EXPECT_EQ(round_trip_test_output_stream().flush_count(), 1u);\n  EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());\n}\n\nTEST_F(Base94OutputStreamTest, WriteEmptyData) {\n  std::vector<uint8_t> empty_data;\n  EXPECT_TRUE(round_trip()->Write(\n      static_cast<const uint8_t*>(empty_data.data()), empty_data.size()));\n  EXPECT_TRUE(round_trip()->Flush());\n  EXPECT_TRUE(round_trip()->Flush());\n  EXPECT_EQ(round_trip_test_output_stream().write_count(), 0u);\n  EXPECT_EQ(round_trip_test_output_stream().flush_count(), 2u);\n  EXPECT_TRUE(round_trip_test_output_stream().all_data().empty());\n}\n\nTEST_F(Base94OutputStreamTest, Process7bitsInFinishDecoding) {\n  std::vector<uint8_t> input = {\n      0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};\n  EXPECT_TRUE(round_trip()->Write(static_cast<const uint8_t*>(input.data()),\n                                  input.size()));\n  EXPECT_TRUE(round_trip()->Flush());\n  VerifyRoundTrip(input);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/file_encoder.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/file_encoder.h\"\n\n#include <memory>\n#include <tuple>\n\n#include \"util/file/file_io.h\"\n#include \"util/file/file_reader.h\"\n#include \"util/file/scoped_remove_file.h\"\n#include \"util/stream/base94_output_stream.h\"\n#include \"util/stream/file_output_stream.h\"\n#include \"util/stream/output_stream_interface.h\"\n#include \"util/stream/zlib_output_stream.h\"\n\nnamespace crashpad {\n\nFileEncoder::FileEncoder(Mode mode,\n                         const base::FilePath& input_path,\n                         const base::FilePath& output_path)\n    : mode_(mode), input_path_(input_path), output_path_(output_path) {}\n\nFileEncoder::~FileEncoder() {}\n\nbool FileEncoder::Process() {\n  ScopedRemoveFile file_remover;\n  ScopedFileHandle write_handle(LoggingOpenFileForWrite(\n      output_path_, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));\n  if (!write_handle.is_valid())\n    return false;\n\n  // Remove the output file on failure.\n  file_remover.reset(output_path_);\n\n  std::unique_ptr<OutputStreamInterface> output;\n  if (mode_ == Mode::kEncode) {\n    output = std::make_unique<ZlibOutputStream>(\n        ZlibOutputStream::Mode::kCompress,\n        std::make_unique<Base94OutputStream>(\n            Base94OutputStream::Mode::kEncode,\n            std::make_unique<FileOutputStream>(write_handle.get())));\n  } else {\n    output = std::make_unique<Base94OutputStream>(\n        Base94OutputStream::Mode::kDecode,\n        std::make_unique<ZlibOutputStream>(\n            ZlibOutputStream::Mode::kDecompress,\n            std::make_unique<FileOutputStream>(write_handle.get())));\n  }\n\n  FileReader file_reader;\n  if (!file_reader.Open(input_path_))\n    return false;\n\n  FileOperationResult read_result;\n  do {\n    uint8_t buffer[4096];\n    read_result = file_reader.Read(buffer, sizeof(buffer));\n    if (read_result < 0)\n      return false;\n\n    if (read_result > 0 && (!output->Write(buffer, read_result)))\n      return false;\n  } while (read_result > 0);\n\n  if (!output->Flush())\n    return false;\n\n  std::ignore = file_remover.release();\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/file_encoder.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STREAM_FILE_ENCODER_H_\n#define CRASHPAD_UTIL_STREAM_FILE_ENCODER_H_\n\n#include \"base/files/file_path.h\"\n\nnamespace crashpad {\n\n//! \\brief The class is used to compress and base94-encode, or base94-decode\n//! and decompress the given input file to the output file.\nclass FileEncoder {\n public:\n  //! \\brief Whether this object is configured to encode or decode data.\n  enum class Mode : bool {\n    //! \\brief Data passed through this object is encoded.\n    kEncode = false,\n    //! \\brief Data passed through this object is decoded.\n    kDecode = true\n  };\n\n  //! \\param[in] mode The work mode of this object.\n  //! \\param[in] input_path The input file that this object reads from.\n  //! \\param[in] output_path The output file that this object writes to.\n  FileEncoder(Mode mode,\n              const base::FilePath& input_path,\n              const base::FilePath& output_path);\n\n  FileEncoder(const FileEncoder&) = delete;\n  FileEncoder& operator=(const FileEncoder&) = delete;\n\n  ~FileEncoder();\n\n  //! \\brief Encode/decode the data from \\a input_path_ file according work\n  //! \\a mode, and write the result to \\a output_path_ on success.\n  //!\n  //! \\return `true` on success.\n  bool Process();\n\n private:\n  Mode mode_;\n  base::FilePath input_path_;\n  base::FilePath output_path_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STREAM_FILE_ENCODER_H_\n"
  },
  {
    "path": "util/stream/file_encoder_test.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/file_encoder.h\"\n\n#include <algorithm>\n#include <memory>\n#include <string>\n\n#include \"base/files/file_path.h\"\n#include \"gtest/gtest.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"util/file/file_io.h\"\n#include \"util/stream/file_output_stream.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr size_t kBufferSize = 4096;\n\nclass FileEncoderTest : public testing::Test {\n public:\n  FileEncoderTest() {}\n\n  void Verify(size_t size) {\n    std::string contents;\n    ASSERT_TRUE(LoggingReadEntireFile(decoded_, &contents));\n    ASSERT_EQ(contents.size(), size);\n    EXPECT_EQ(memcmp(deterministic_input_.get(), contents.data(), size), 0);\n  }\n\n  const uint8_t* BuildDeterministicInput(size_t size) {\n    deterministic_input_ = std::make_unique<uint8_t[]>(size);\n    uint8_t* deterministic_input_base = deterministic_input_.get();\n    while (size-- > 0)\n      deterministic_input_base[size] = static_cast<uint8_t>(size);\n    return deterministic_input_base;\n  }\n\n  void GenerateOrigFile(size_t size) {\n    ScopedFileHandle write_handle(OpenFileForWrite(\n        orig_, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));\n    ASSERT_TRUE(write_handle.is_valid());\n    FileOutputStream out(write_handle.get());\n    const uint8_t* buf = BuildDeterministicInput(size);\n    while (size > 0) {\n      size_t m = std::min(kBufferSize, size);\n      ASSERT_TRUE(out.Write(buf, m));\n      size -= m;\n      buf += m;\n    }\n    ASSERT_TRUE(out.Flush());\n  }\n\n  FileEncoder* encoder() const { return encoder_.get(); }\n\n  FileEncoder* decoder() const { return decoder_.get(); }\n\n protected:\n  void SetUp() override {\n    temp_dir_ = std::make_unique<ScopedTempDir>();\n    orig_ = base::FilePath(temp_dir_->path().Append(FILE_PATH_LITERAL(\"orig\")));\n    encoded_ =\n        base::FilePath(temp_dir_->path().Append(FILE_PATH_LITERAL(\"encoded\")));\n    decoded_ =\n        base::FilePath(temp_dir_->path().Append(FILE_PATH_LITERAL(\"decoded\")));\n    encoder_ = std::make_unique<FileEncoder>(\n        FileEncoder::Mode::kEncode, orig_, encoded_);\n    decoder_ = std::make_unique<FileEncoder>(\n        FileEncoder::Mode::kDecode, encoded_, decoded_);\n  }\n\n private:\n  std::unique_ptr<ScopedTempDir> temp_dir_;\n  base::FilePath orig_;\n  base::FilePath encoded_;\n  base::FilePath decoded_;\n  std::unique_ptr<FileEncoder> encoder_;\n  std::unique_ptr<FileEncoder> decoder_;\n  std::unique_ptr<uint8_t[]> deterministic_input_;\n};\n\nTEST_F(FileEncoderTest, ProcessShortFile) {\n  GenerateOrigFile(kBufferSize - 512);\n  EXPECT_TRUE(encoder()->Process());\n  EXPECT_TRUE(decoder()->Process());\n  Verify(kBufferSize - 512);\n}\n\nTEST_F(FileEncoderTest, ProcessLongFile) {\n  GenerateOrigFile(kBufferSize + 512);\n  EXPECT_TRUE(encoder()->Process());\n  EXPECT_TRUE(decoder()->Process());\n  Verify(kBufferSize + 512);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/file_output_stream.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/file_output_stream.h\"\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nFileOutputStream::FileOutputStream(FileHandle file_handle)\n    : writer_(file_handle), flush_needed_(false), flushed_(false) {}\n\nFileOutputStream::~FileOutputStream() {\n  DCHECK(!flush_needed_);\n}\n\nbool FileOutputStream::Write(const uint8_t* data, size_t size) {\n  DCHECK(!flushed_);\n\n  if (!writer_.Write(data, size)) {\n    LOG(ERROR) << \"Write: Failed\";\n    return false;\n  }\n  flush_needed_ = true;\n  return true;\n}\n\nbool FileOutputStream::Flush() {\n  flush_needed_ = false;\n  flushed_ = true;\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/file_output_stream.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STREAM_FILE_OUTPUT_STREAM_H_\n#define CRASHPAD_UTIL_STREAM_FILE_OUTPUT_STREAM_H_\n\n#include \"util/file/file_io.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/stream/output_stream_interface.h\"\n\nnamespace crashpad {\n\n//! \\brief The class is used to write data to a file.\nclass FileOutputStream : public OutputStreamInterface {\n public:\n  //! \\param[in] file_handle The file that this object writes to.\n  explicit FileOutputStream(FileHandle file_handle);\n\n  FileOutputStream(const FileOutputStream&) = delete;\n  FileOutputStream& operator=(const FileOutputStream&) = delete;\n\n  ~FileOutputStream();\n\n  // OutputStream.\n  bool Write(const uint8_t* data, size_t size) override;\n  bool Flush() override;\n\n private:\n  WeakFileHandleFileWriter writer_;\n  bool flush_needed_;\n  bool flushed_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STREAM_FILE_OUTPUT_STREAM_H_\n"
  },
  {
    "path": "util/stream/log_output_stream.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/log_output_stream.h\"\n\n#include <string.h>\n\n#include <algorithm>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nLogOutputStream::LogOutputStream(std::unique_ptr<Delegate> delegate)\n    : delegate_(std::move(delegate)),\n      output_count_(0),\n      flush_needed_(false),\n      flushed_(false) {\n  buffer_.reserve(delegate_->LineWidth());\n}\n\nLogOutputStream::~LogOutputStream() {\n  DCHECK(!flush_needed_);\n}\n\nbool LogOutputStream::Write(const uint8_t* data, size_t size) {\n  DCHECK(!flushed_);\n\n  static constexpr char kBeginMessage[] = \"-----BEGIN CRASHPAD MINIDUMP-----\";\n  if (output_count_ == 0 && !WriteToLog(kBeginMessage)) {\n    return false;\n  }\n\n  flush_needed_ = true;\n  while (size > 0) {\n    size_t m = std::min(delegate_->LineWidth() - buffer_.size(), size);\n    buffer_.append(reinterpret_cast<const char*>(data), m);\n    data += m;\n    size -= m;\n    if (buffer_.size() == delegate_->LineWidth() && !WriteBuffer()) {\n      return false;\n    }\n  }\n  return true;\n}\n\nbool LogOutputStream::WriteBuffer() {\n  if (buffer_.empty())\n    return true;\n\n  static constexpr char kAbortMessage[] = \"-----ABORT CRASHPAD MINIDUMP-----\";\n\n  output_count_ += buffer_.size();\n  if (output_count_ > delegate_->OutputCap()) {\n    WriteToLog(kAbortMessage);\n    flush_needed_ = false;\n    return false;\n  }\n\n  if (!WriteToLog(buffer_.c_str())) {\n    flush_needed_ = false;\n    return false;\n  }\n\n  buffer_.clear();\n  return true;\n}\n\nint LogOutputStream::WriteToLog(const char* buf) {\n  return delegate_->Log(buf);\n}\n\nbool LogOutputStream::Flush() {\n  bool result = true;\n  if (flush_needed_) {\n    flush_needed_ = false;\n    flushed_ = true;\n\n    static constexpr char kEndMessage[] = \"-----END CRASHPAD MINIDUMP-----\";\n    if (!WriteBuffer() || !WriteToLog(kEndMessage)) {\n      result = false;\n    }\n  }\n  return result;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/log_output_stream.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STREAM_LOG_OUTPUT_STREAM_H_\n#define CRASHPAD_UTIL_STREAM_LOG_OUTPUT_STREAM_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <string>\n\n#include \"util/stream/output_stream_interface.h\"\n\nnamespace crashpad {\n\n//! \\brief This class outputs a stream of data as a series of log messages.\nclass LogOutputStream : public OutputStreamInterface {\n public:\n  //! \\brief An interface to a log output sink.\n  class Delegate {\n   public:\n    Delegate() = default;\n    virtual ~Delegate() = default;\n\n    //! \\brief Logs |buf| to the output sink.\n    //!\n    //! \\param buf the buffer to write to the log. More bytes than are in |buf|\n    //!     may be written, e.g. to convey metadata.\n    //! \\return `true` if the |buf| was successfully written.\n    virtual bool Log(const char* buf) = 0;\n\n    //! \\brief Returns the maximum number of bytes to allow writing to this log.\n    virtual size_t OutputCap() = 0;\n\n    //! \\brief Returns the maximum length of buffers allowed to be passed to\n    //!     Log().\n    virtual size_t LineWidth() = 0;\n  };\n\n  explicit LogOutputStream(std::unique_ptr<Delegate> delegate);\n\n  LogOutputStream(const LogOutputStream&) = delete;\n  LogOutputStream& operator=(const LogOutputStream&) = delete;\n\n  ~LogOutputStream() override;\n\n  // OutputStreamInterface:\n  bool Write(const uint8_t* data, size_t size) override;\n  bool Flush() override;\n\n private:\n  // Flushes buffer_, returning false on failure.\n  bool WriteBuffer();\n\n  int WriteToLog(const char* buf);\n\n  std::string buffer_;\n  std::unique_ptr<Delegate> delegate_;\n  size_t output_count_;\n  bool flush_needed_;\n  bool flushed_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STREAM_LOG_OUTPUT_STREAM_H_\n"
  },
  {
    "path": "util/stream/log_output_stream_test.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/log_output_stream.h\"\n\n#include <algorithm>\n#include <memory>\n#include <string>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr size_t kOutputCap = 128 * 1024;\nconstexpr size_t kLineBufferSize = 512;\nconst char* kBeginGuard = \"-----BEGIN CRASHPAD MINIDUMP-----\";\nconst char* kEndGuard = \"-----END CRASHPAD MINIDUMP-----\";\nconst char* kAbortGuard = \"-----ABORT CRASHPAD MINIDUMP-----\";\n\nclass LogOutputStreamTestDelegate final : public LogOutputStream::Delegate {\n public:\n  explicit LogOutputStreamTestDelegate(std::string* logging_destination)\n      : logging_destination_(logging_destination) {}\n  ~LogOutputStreamTestDelegate() override = default;\n\n  bool Log(const char* buf) override {\n    size_t len = strnlen(buf, kLineBufferSize + 1);\n    EXPECT_LE(len, kLineBufferSize);\n    logging_destination_->append(buf, len);\n    return true;\n  }\n\n  size_t OutputCap() override { return kOutputCap; }\n  size_t LineWidth() override { return kLineBufferSize; }\n\n private:\n  std::string* logging_destination_;\n};\n\nclass LogOutputStreamTest : public testing::Test {\n public:\n  LogOutputStreamTest() {}\n\n  LogOutputStreamTest(const LogOutputStreamTest&) = delete;\n  LogOutputStreamTest& operator=(const LogOutputStreamTest&) = delete;\n\n protected:\n  void SetUp() override {\n    log_stream_ = std::make_unique<LogOutputStream>(\n        std::make_unique<LogOutputStreamTestDelegate>(&test_log_output_));\n  }\n\n  const uint8_t* BuildDeterministicInput(size_t size) {\n    deterministic_input_ = std::make_unique<uint8_t[]>(size);\n    uint8_t* deterministic_input_base = deterministic_input_.get();\n    while (size-- > 0)\n      deterministic_input_base[size] = static_cast<uint8_t>('a');\n    return deterministic_input_base;\n  }\n\n  const std::string& test_log_output() const { return test_log_output_; }\n\n  LogOutputStream* log_stream() const { return log_stream_.get(); }\n\n private:\n  std::unique_ptr<LogOutputStream> log_stream_;\n  std::string test_log_output_;\n  std::unique_ptr<uint8_t[]> deterministic_input_;\n};\n\nTEST_F(LogOutputStreamTest, WriteShortLog) {\n  const uint8_t* input = BuildDeterministicInput(2);\n  EXPECT_TRUE(log_stream()->Write(input, 2));\n  EXPECT_TRUE(log_stream()->Flush());\n  // Verify OutputStream wrote 2 guards and data.\n  EXPECT_FALSE(test_log_output().empty());\n  EXPECT_EQ(test_log_output(),\n            std::string(kBeginGuard).append(\"aa\").append(kEndGuard));\n}\n\nTEST_F(LogOutputStreamTest, WriteLongLog) {\n  size_t input_length = kLineBufferSize + kLineBufferSize / 2;\n  const uint8_t* input = BuildDeterministicInput(input_length);\n  // Verify OutputStream wrote 2 guards and data.\n  EXPECT_TRUE(log_stream()->Write(input, input_length));\n  EXPECT_TRUE(log_stream()->Flush());\n  EXPECT_EQ(test_log_output().size(),\n            strlen(kBeginGuard) + strlen(kEndGuard) + input_length);\n}\n\nTEST_F(LogOutputStreamTest, WriteAbort) {\n  size_t input_length = kOutputCap + kLineBufferSize;\n  const uint8_t* input = BuildDeterministicInput(input_length);\n  EXPECT_FALSE(log_stream()->Write(input, input_length));\n  EXPECT_EQ(\n      test_log_output().substr(test_log_output().size() - strlen(kAbortGuard)),\n      kAbortGuard);\n}\n\nTEST_F(LogOutputStreamTest, FlushAbort) {\n  size_t input_length = kOutputCap - strlen(kBeginGuard) + kLineBufferSize / 2;\n  const uint8_t* input = BuildDeterministicInput(input_length);\n  EXPECT_TRUE(log_stream()->Write(input, input_length));\n  EXPECT_FALSE(log_stream()->Flush());\n  EXPECT_EQ(\n      test_log_output().substr(test_log_output().size() - strlen(kAbortGuard)),\n      kAbortGuard);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/output_stream_interface.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STREAM_OUTPUT_STREAM_INTERFACE_H_\n#define CRASHPAD_UTIL_STREAM_OUTPUT_STREAM_INTERFACE_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\nnamespace crashpad {\n\n//! \\brief The interface for an output stream pipeline.\n//!\n//! Example:\n//! <code>\n//!   class OutputStreamInterfaceImpl : public OutputStreamInterface {\n//!     ...\n//!   };\n//!\n//!   // Create a OutputStream.\n//!   OutputStreamInterfaceImpl impl(...);\n//!   // Write the data multiple times.\n//!   while (has_data) {\n//!     impl.Write(data, size);\n//!     ...\n//!   }\n//!   // Flush internal buffer to indicate all data has been written.\n//!   impl.Flush();\n//! </code>\n//!\nclass OutputStreamInterface {\n public:\n  virtual ~OutputStreamInterface() = default;\n\n  //! \\brief Writes \\a data to this stream. This method may be called multiple\n  //! times for streaming.\n  //!\n  //! \\param[in] data The data that should be written.\n  //! \\param[in] size The size of \\a data.\n  //!\n  //! \\return `true` on success.\n  virtual bool Write(const uint8_t* data, size_t size) = 0;\n\n  //! \\brief Flush the internal buffer after all data has been written.\n  //!\n  //! Write() can't be called afterwards.\n  //! \\return `true` on success.\n  virtual bool Flush() = 0;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STREAM_OUTPUT_STREAM_INTERFACE_H_\n"
  },
  {
    "path": "util/stream/test_output_stream.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/test_output_stream.h\"\n#include \"base/check.h\"\n\nnamespace crashpad {\nnamespace test {\n\nTestOutputStream::TestOutputStream()\n    : last_written_data_(),\n      all_data_(),\n      write_count_(0),\n      flush_count_(0),\n      flush_needed_(false) {}\n\nTestOutputStream::~TestOutputStream() {\n  DCHECK(!flush_needed_);\n}\n\nbool TestOutputStream::Write(const uint8_t* data, size_t size) {\n  last_written_data_.assign(data, data + size);\n  all_data_.insert(all_data_.end(), data, data + size);\n\n  flush_needed_ = true;\n  write_count_++;\n  return true;\n}\n\nbool TestOutputStream::Flush() {\n  flush_needed_ = false;\n  flush_count_++;\n  return true;\n}\n\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/test_output_stream.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STREAM_TEST_OUTPUT_STREAM_H_\n#define CRASHPAD_UTIL_STREAM_TEST_OUTPUT_STREAM_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <vector>\n\n#include \"util/stream/output_stream_interface.h\"\n\nnamespace crashpad {\nnamespace test {\n\n//! \\brief The help class for \\a OutputStreamInterface related tests.\nclass TestOutputStream : public OutputStreamInterface {\n public:\n  TestOutputStream();\n\n  TestOutputStream(const TestOutputStream&) = delete;\n  TestOutputStream& operator=(const TestOutputStream&) = delete;\n\n  ~TestOutputStream() override;\n\n  // OutputStreamInterface:\n  bool Write(const uint8_t* data, size_t size) override;\n  bool Flush() override;\n\n  //! \\return the data that has been received by the last call of Write().\n  const std::vector<uint8_t>& last_written_data() const {\n    return last_written_data_;\n  }\n\n  //! \\return all data that has been received.\n  const std::vector<uint8_t>& all_data() const { return all_data_; }\n\n  //! \\return the number of times Write() has been called.\n  size_t write_count() const { return write_count_; }\n\n  //! \\return the number of times Flush() has been called.\n  size_t flush_count() const { return flush_count_; }\n\n private:\n  std::vector<uint8_t> last_written_data_;\n  std::vector<uint8_t> all_data_;\n  size_t write_count_;\n  size_t flush_count_;\n  bool flush_needed_;\n};\n\n}  // namespace test\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STREAM_TEST_OUTPUT_STREAM_H_\n"
  },
  {
    "path": "util/stream/zlib_output_stream.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/zlib_output_stream.h\"\n\n#include <iterator>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"util/misc/zlib.h\"\n\nnamespace crashpad {\n\nZlibOutputStream::ZlibOutputStream(\n    Mode mode,\n    std::unique_ptr<OutputStreamInterface> output_stream)\n    : output_stream_(std::move(output_stream)),\n      mode_(mode),\n      initialized_(),\n      flush_needed_(false) {}\n\nZlibOutputStream::~ZlibOutputStream() {\n  if (!initialized_.is_valid())\n    return;\n  DCHECK(!flush_needed_);\n  if (mode_ == Mode::kCompress) {\n    if (deflateEnd(&zlib_stream_) != Z_OK)\n      LOG(ERROR) << \"deflateEnd: \" << zlib_stream_.msg;\n  } else if (mode_ == Mode::kDecompress) {\n    if (inflateEnd(&zlib_stream_) != Z_OK)\n      LOG(ERROR) << \"inflateEnd: \" << zlib_stream_.msg;\n  }\n}\n\nbool ZlibOutputStream::Write(const uint8_t* data, size_t size) {\n  if (initialized_.is_uninitialized()) {\n    initialized_.set_invalid();\n\n    zlib_stream_.zalloc = Z_NULL;\n    zlib_stream_.zfree = Z_NULL;\n    zlib_stream_.opaque = Z_NULL;\n\n    if (mode_ == Mode::kDecompress) {\n      int result = inflateInit(&zlib_stream_);\n      if (result != Z_OK) {\n        LOG(ERROR) << \"inflateInit: \" << ZlibErrorString(result);\n        return false;\n      }\n    } else if (mode_ == Mode::kCompress) {\n      int result = deflateInit(&zlib_stream_, Z_BEST_COMPRESSION);\n      if (result != Z_OK) {\n        LOG(ERROR) << \"deflateInit: \" << ZlibErrorString(result);\n        return false;\n      }\n    }\n    zlib_stream_.next_out = buffer_;\n    zlib_stream_.avail_out = base::saturated_cast<uInt>(std::size(buffer_));\n    initialized_.set_valid();\n  }\n\n  if (!initialized_.is_valid())\n    return false;\n\n  zlib_stream_.next_in = data;\n  zlib_stream_.avail_in = base::saturated_cast<uInt>(size);\n  flush_needed_ = false;\n  while (zlib_stream_.avail_in > 0) {\n    if (mode_ == Mode::kCompress) {\n      if (deflate(&zlib_stream_, Z_NO_FLUSH) != Z_OK) {\n        LOG(ERROR) << \"deflate: \" << zlib_stream_.msg;\n        return false;\n      }\n    } else if (mode_ == Mode::kDecompress) {\n      int result = inflate(&zlib_stream_, Z_NO_FLUSH);\n      if (result == Z_STREAM_END) {\n        if (zlib_stream_.avail_in > 0) {\n          LOG(ERROR) << \"inflate: unconsumed input\";\n          return false;\n        }\n      } else if (result != Z_OK) {\n        LOG(ERROR) << \"inflate: \" << zlib_stream_.msg;\n        return false;\n      }\n    }\n\n    if (!WriteOutputStream())\n      return false;\n  }\n  flush_needed_ = true;\n  return true;\n}\n\nbool ZlibOutputStream::Flush() {\n  if (initialized_.is_valid() && flush_needed_) {\n    flush_needed_ = false;\n    int result = Z_OK;\n    do {\n      if (mode_ == Mode::kCompress) {\n        result = deflate(&zlib_stream_, Z_FINISH);\n        if (result != Z_STREAM_END && result != Z_BUF_ERROR && result != Z_OK) {\n          LOG(ERROR) << \"deflate: \" << zlib_stream_.msg;\n          return false;\n        }\n      } else if (mode_ == Mode::kDecompress) {\n        result = inflate(&zlib_stream_, Z_FINISH);\n        if (result != Z_STREAM_END && result != Z_BUF_ERROR && result != Z_OK) {\n          LOG(ERROR) << \"inflate: \" << zlib_stream_.msg;\n          return false;\n        }\n      }\n      if (!WriteOutputStream())\n        return false;\n    } while (result != Z_STREAM_END);\n  }\n  return output_stream_->Flush();\n}\n\nbool ZlibOutputStream::WriteOutputStream() {\n  auto valid_size = std::size(buffer_) - zlib_stream_.avail_out;\n  if (valid_size > 0 && !output_stream_->Write(buffer_, valid_size))\n    return false;\n\n  zlib_stream_.next_out = buffer_;\n  zlib_stream_.avail_out = base::saturated_cast<uInt>(std::size(buffer_));\n\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/stream/zlib_output_stream.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STREAM_ZLIB_OUTPUT_STREAM_H_\n#define CRASHPAD_UTIL_STREAM_ZLIB_OUTPUT_STREAM_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n\n#include \"third_party/zlib/zlib_crashpad.h\"\n#include \"util/misc/initialization_state.h\"\n#include \"util/stream/output_stream_interface.h\"\n\nnamespace crashpad {\n\n//! \\brief The class wraps zlib into \\a OutputStreamInterface.\nclass ZlibOutputStream : public OutputStreamInterface {\n public:\n  //! \\brief Whether this object is configured to compress or decompress data.\n  enum class Mode : bool {\n    //! \\brief Data passed through this object is compressed.\n    kCompress = false,\n    //! \\brief Data passed through this object is decompressed.\n    kDecompress = true\n  };\n\n  //! \\param[in] mode The work mode of this object.\n  //! \\param[in] output_stream The output_stream that this object writes to.\n  //!\n  //! To construct an output pipeline, the output stream needs an output stream\n  //! to write the result to. For example, the code below constructs a\n  //! compress->base94-encoding->log output stream pipline.\n  //!\n  //! <code>\n  //!   ZlibOutputStream zlib_output_stream(\n  //!        ZlibOutputStream::Mode::kDeflate,\n  //!        std::make_unique<Base94OutputStream>(\n  //!            Base94OutputStream::Mode::kEncode,\n  //!            std::make_unique<LogOutputStream>()));\n  //! </code>\n  //!\n  //!\n  ZlibOutputStream(Mode mode,\n                   std::unique_ptr<OutputStreamInterface> output_stream);\n\n  ZlibOutputStream(const ZlibOutputStream&) = delete;\n  ZlibOutputStream& operator=(const ZlibOutputStream&) = delete;\n\n  ~ZlibOutputStream() override;\n\n  // OutputStreamInterface:\n  bool Write(const uint8_t* data, size_t size) override;\n  bool Flush() override;\n\n private:\n  // Write compressed/decompressed data to |output_stream_| and empty the output\n  // buffer in |zlib_stream_|.\n  bool WriteOutputStream();\n\n  uint8_t buffer_[4096];\n  z_stream zlib_stream_;\n  std::unique_ptr<OutputStreamInterface> output_stream_;\n  Mode mode_;\n  InitializationState initialized_;  // protects zlib_stream_\n  bool flush_needed_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STREAM_ZLIB_OUTPUT_STREAM_H_\n"
  },
  {
    "path": "util/stream/zlib_output_stream_test.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/stream/zlib_output_stream.h\"\n\n#include <string.h>\n\n#include <algorithm>\n#include <iterator>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/rand_util.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"util/stream/test_output_stream.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr size_t kShortDataLength = 10;\nconstexpr size_t kLongDataLength = 4096 * 10;\n\nclass ZlibOutputStreamTest : public testing::Test {\n public:\n  ZlibOutputStreamTest() : input_(), deterministic_input_() {\n    auto test_output_stream = std::make_unique<TestOutputStream>();\n    test_output_stream_ = test_output_stream.get();\n    zlib_output_stream_ = std::make_unique<ZlibOutputStream>(\n        ZlibOutputStream::Mode::kCompress,\n        std::make_unique<ZlibOutputStream>(ZlibOutputStream::Mode::kDecompress,\n                                           std::move(test_output_stream)));\n  }\n\n  ZlibOutputStreamTest(const ZlibOutputStreamTest&) = delete;\n  ZlibOutputStreamTest& operator=(const ZlibOutputStreamTest&) = delete;\n\n  const uint8_t* BuildDeterministicInput(size_t size) {\n    deterministic_input_ = base::HeapArray<uint8_t>::WithSize(size);\n    while (size-- > 0)\n      deterministic_input_[size] = static_cast<uint8_t>(size);\n    return deterministic_input_.data();\n  }\n\n  const uint8_t* BuildRandomInput(size_t size) {\n    input_ = base::HeapArray<uint8_t>::Uninit(size);\n    base::RandBytes(input_);\n    return input_.data();\n  }\n\n  const TestOutputStream& test_output_stream() const {\n    return *test_output_stream_;\n  }\n\n  ZlibOutputStream* zlib_output_stream() const {\n    return zlib_output_stream_.get();\n  }\n\n private:\n  std::unique_ptr<ZlibOutputStream> zlib_output_stream_;\n  base::HeapArray<uint8_t> input_;\n  base::HeapArray<uint8_t> deterministic_input_;\n  TestOutputStream* test_output_stream_;  // weak, owned by zlib_output_stream_\n};\n\nTEST_F(ZlibOutputStreamTest, WriteDeterministicShortData) {\n  const uint8_t* input = BuildDeterministicInput(kShortDataLength);\n  EXPECT_TRUE(zlib_output_stream()->Write(input, kShortDataLength));\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_EQ(test_output_stream().last_written_data().size(), kShortDataLength);\n  EXPECT_EQ(memcmp(test_output_stream().last_written_data().data(),\n                   input,\n                   kShortDataLength),\n            0);\n}\n\nTEST_F(ZlibOutputStreamTest, WriteDeterministicLongDataOneTime) {\n  const uint8_t* input = BuildDeterministicInput(kLongDataLength);\n  EXPECT_TRUE(zlib_output_stream()->Write(input, kLongDataLength));\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_EQ(test_output_stream().all_data().size(), kLongDataLength);\n  EXPECT_EQ(\n      memcmp(test_output_stream().all_data().data(), input, kLongDataLength),\n      0);\n}\n\nTEST_F(ZlibOutputStreamTest, WriteDeterministicLongDataMultipleTimes) {\n  const uint8_t* input = BuildDeterministicInput(kLongDataLength);\n\n  static constexpr size_t kWriteLengths[] = {\n      4, 96, 40, kLongDataLength - 4 - 96 - 40};\n\n  size_t offset = 0;\n  for (size_t index = 0; index < std::size(kWriteLengths); ++index) {\n    const size_t write_length = kWriteLengths[index];\n    SCOPED_TRACE(base::StringPrintf(\n        \"offset %zu, write_length %zu\", offset, write_length));\n    EXPECT_TRUE(zlib_output_stream()->Write(input + offset, write_length));\n    offset += write_length;\n  }\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_EQ(test_output_stream().all_data().size(), kLongDataLength);\n  EXPECT_EQ(\n      memcmp(test_output_stream().all_data().data(), input, kLongDataLength),\n      0);\n}\n\nTEST_F(ZlibOutputStreamTest, WriteShortData) {\n  const uint8_t* input = BuildRandomInput(kShortDataLength);\n  EXPECT_TRUE(zlib_output_stream()->Write(input, kShortDataLength));\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_EQ(memcmp(test_output_stream().last_written_data().data(),\n                   input,\n                   kShortDataLength),\n            0);\n  EXPECT_EQ(test_output_stream().last_written_data().size(), kShortDataLength);\n}\n\nTEST_F(ZlibOutputStreamTest, WriteLongDataOneTime) {\n  const uint8_t* input = BuildRandomInput(kLongDataLength);\n  EXPECT_TRUE(zlib_output_stream()->Write(input, kLongDataLength));\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_EQ(test_output_stream().all_data().size(), kLongDataLength);\n  EXPECT_EQ(\n      memcmp(test_output_stream().all_data().data(), input, kLongDataLength),\n      0);\n}\n\nTEST_F(ZlibOutputStreamTest, WriteLongDataMultipleTimes) {\n  const uint8_t* input = BuildRandomInput(kLongDataLength);\n\n  // Call Write() a random number of times.\n  size_t index = 0;\n  while (index < kLongDataLength) {\n    size_t write_length =\n        std::min(static_cast<size_t>(base::RandInt(0, 4096 * 2)),\n                 kLongDataLength - index);\n    SCOPED_TRACE(\n        base::StringPrintf(\"index %zu, write_length %zu\", index, write_length));\n    EXPECT_TRUE(zlib_output_stream()->Write(input + index, write_length));\n    index += write_length;\n  }\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_EQ(test_output_stream().all_data().size(), kLongDataLength);\n  EXPECT_EQ(\n      memcmp(test_output_stream().all_data().data(), input, kLongDataLength),\n      0);\n}\n\nTEST_F(ZlibOutputStreamTest, NoWriteOrFlush) {\n  EXPECT_EQ(test_output_stream().write_count(), 0u);\n  EXPECT_EQ(test_output_stream().flush_count(), 0u);\n  EXPECT_TRUE(test_output_stream().all_data().empty());\n}\n\nTEST_F(ZlibOutputStreamTest, FlushWithoutWrite) {\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_EQ(test_output_stream().write_count(), 0u);\n  EXPECT_EQ(test_output_stream().flush_count(), 1u);\n  EXPECT_TRUE(test_output_stream().all_data().empty());\n}\n\nTEST_F(ZlibOutputStreamTest, WriteEmptyData) {\n  std::vector<uint8_t> empty_data;\n  EXPECT_TRUE(zlib_output_stream()->Write(\n      static_cast<const uint8_t*>(empty_data.data()), empty_data.size()));\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_TRUE(zlib_output_stream()->Flush());\n  EXPECT_EQ(test_output_stream().write_count(), 0u);\n  EXPECT_EQ(test_output_stream().flush_count(), 2u);\n  EXPECT_TRUE(test_output_stream().all_data().empty());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/string/split_string.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/string/split_string.h\"\n\n#include <sys/types.h>\n\nnamespace crashpad {\n\nbool SplitStringFirst(const std::string& string,\n                      char delimiter,\n                      std::string* left,\n                      std::string* right) {\n  size_t delimiter_pos = string.find(delimiter);\n  if (delimiter_pos == 0 || delimiter_pos == std::string::npos) {\n    return false;\n  }\n\n  left->assign(string, 0, delimiter_pos);\n  right->assign(string, delimiter_pos + 1, std::string::npos);\n  return true;\n}\n\nstd::vector<std::string> SplitString(const std::string& string,\n                                     char delimiter) {\n  std::vector<std::string> result;\n  if (string.empty())\n    return result;\n\n  size_t start = 0;\n  while (start != std::string::npos) {\n    size_t end = string.find_first_of(delimiter, start);\n\n    std::string part;\n    if (end == std::string::npos) {\n      part = string.substr(start);\n      start = std::string::npos;\n    } else {\n      part = string.substr(start, end - start);\n      start = end + 1;\n    }\n\n    result.push_back(part);\n  }\n  return result;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/string/split_string.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_STRING_SPLIT_STRING_H_\n#define CRASHPAD_UTIL_STRING_SPLIT_STRING_H_\n\n#include <string>\n#include <vector>\n\nnamespace crashpad {\n\n//! \\brief Splits a string into two parts at the first delimiter found.\n//!\n//! \\param[in] string The string to split.\n//! \\param[in] delimiter The delimiter to split at.\n//! \\param[out] left The portion of \\a string up to, but not including, the\n//!     first \\a delimiter character.\n//! \\param[out] right The portion of \\a string after the first \\a delimiter\n//!     character.\n//!\n//! \\return `true` if \\a string was split successfully. `false` if \\a string\n//!     did not contain a \\a delimiter character or began with a \\a delimiter\n//!     character.\nbool SplitStringFirst(const std::string& string,\n                      char delimiter,\n                      std::string* left,\n                      std::string* right);\n\n//! \\brief Splits a string into multiple parts on the given delimiter.\n//!\n//! \\param[in] string The string to split.\n//! \\param[in] delimiter The delimiter to split at.\n//!\n//! \\return The individual parts of the string.\nstd::vector<std::string> SplitString(const std::string& string, char delimiter);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_STRING_SPLIT_STRING_H_\n"
  },
  {
    "path": "util/string/split_string_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/string/split_string.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(SplitString, SplitStringFirst) {\n  std::string left;\n  std::string right;\n\n  EXPECT_FALSE(SplitStringFirst(\"\", '=', &left, &right));\n  EXPECT_FALSE(SplitStringFirst(\"no equals\", '=', &left, &right));\n  EXPECT_FALSE(SplitStringFirst(\"=\", '=', &left, &right));\n  EXPECT_FALSE(SplitStringFirst(\"=beginequals\", '=', &left, &right));\n\n  ASSERT_TRUE(SplitStringFirst(\"a=b\", '=', &left, &right));\n  EXPECT_EQ(left, \"a\");\n  EXPECT_EQ(right, \"b\");\n\n  ASSERT_TRUE(SplitStringFirst(\"EndsEquals=\", '=', &left, &right));\n  EXPECT_EQ(left, \"EndsEquals\");\n  EXPECT_TRUE(right.empty());\n\n  ASSERT_TRUE(SplitStringFirst(\"key=VALUE\", '=', &left, &right));\n  EXPECT_EQ(left, \"key\");\n  EXPECT_EQ(right, \"VALUE\");\n\n  EXPECT_FALSE(SplitStringFirst(\"a=b\", '|', &left, &right));\n\n  ASSERT_TRUE(SplitStringFirst(\"ls | less\", '|', &left, &right));\n  EXPECT_EQ(left, \"ls \");\n  EXPECT_EQ(right, \" less\");\n\n  ASSERT_TRUE(SplitStringFirst(\"when in\", ' ', &left, &right));\n  EXPECT_EQ(left, \"when\");\n  EXPECT_EQ(right, \"in\");\n\n  ASSERT_TRUE(SplitStringFirst(\"zoo\", 'o', &left, &right));\n  EXPECT_EQ(left, \"z\");\n  EXPECT_EQ(right, \"o\");\n\n  ASSERT_FALSE(SplitStringFirst(\"ooze\", 'o', &left, &right));\n}\n\nTEST(SplitString, SplitString) {\n  std::vector<std::string> parts;\n\n  parts = SplitString(\"\", '.');\n  EXPECT_EQ(parts.size(), 0u);\n\n  parts = SplitString(\".\", '.');\n  ASSERT_EQ(parts.size(), 2u);\n  EXPECT_EQ(parts[0], \"\");\n  EXPECT_EQ(parts[1], \"\");\n\n  parts = SplitString(\"a,b\", ',');\n  ASSERT_EQ(parts.size(), 2u);\n  EXPECT_EQ(parts[0], \"a\");\n  EXPECT_EQ(parts[1], \"b\");\n\n  parts = SplitString(\"zoo\", 'o');\n  ASSERT_EQ(parts.size(), 3u);\n  EXPECT_EQ(parts[0], \"z\");\n  EXPECT_EQ(parts[1], \"\");\n  EXPECT_EQ(parts[2], \"\");\n\n  parts = SplitString(\"0x100,0x200,0x300,0x400,0x500,0x600\", ',');\n  ASSERT_EQ(parts.size(), 6u);\n  EXPECT_EQ(parts[0], \"0x100\");\n  EXPECT_EQ(parts[1], \"0x200\");\n  EXPECT_EQ(parts[2], \"0x300\");\n  EXPECT_EQ(parts[3], \"0x400\");\n  EXPECT_EQ(parts[4], \"0x500\");\n  EXPECT_EQ(parts[5], \"0x600\");\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/synchronization/scoped_spin_guard.h",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_SYNCHRONIZATION_SCOPED_SPIN_GUARD_H_\n#define CRASHPAD_UTIL_SYNCHRONIZATION_SCOPED_SPIN_GUARD_H_\n\n#include <atomic>\n#include <optional>\n#include <utility>\n\n#include \"base/check.h\"\n#include \"base/notreached.h\"\n#include \"util/misc/clock.h\"\n\nnamespace crashpad {\n\n//! \\brief Spinlock state for `ScopedSpinGuard`.\nstruct SpinGuardState final {\n  //! \\brief A `ScopedSpinGuard` in an unlocked state.\n  constexpr SpinGuardState() : locked(false) {}\n\n  SpinGuardState(const SpinGuardState&) = delete;\n  SpinGuardState& operator=(const SpinGuardState&) = delete;\n\n  //! \\brief `true` if the `ScopedSpinGuard` is locked, `false` otherwise.\n  std::atomic<bool> locked;\n  static_assert(std::atomic<bool>::is_always_lock_free,\n                \"std::atomic<bool> may not be signal-safe\");\n  static_assert(sizeof(std::atomic<bool>) == sizeof(bool),\n                \"std::atomic<bool> adds size to bool\");\n};\n\n//! \\brief A scoped mutual-exclusion guard wrapping a `SpinGuardState` with RAII\n//!     semantics.\nclass ScopedSpinGuard final {\n  //! \\brief The duration in nanoseconds between attempts to lock the spinlock.\n  static constexpr uint64_t kSpinGuardSleepTimeNanos = 10;\n\n public:\n  ScopedSpinGuard(const ScopedSpinGuard&) = delete;\n  ScopedSpinGuard& operator=(const ScopedSpinGuard&) = delete;\n  ScopedSpinGuard(ScopedSpinGuard&& other) noexcept : state_(nullptr) {\n    std::swap(state_, other.state_);\n  }\n  ScopedSpinGuard& operator=(ScopedSpinGuard&& other) {\n    std::swap(state_, other.state_);\n    return *this;\n  }\n\n  //! \\brief Spins up to `timeout_nanos` nanoseconds trying to lock `state`.\n  //! \\param[in] timeout_nanos The timeout in nanoseconds after which this gives\n  //!     up trying to lock the spinlock and returns `std::nullopt`.\n  //! \\param[in,out] state The spinlock state to attempt to lock. This method\n  //!     holds a pointer to `state`, so `state` must outlive the lifetime of\n  //!     this object.\n  //! \\return The locked `ScopedSpinGuard` on success, or `std::nullopt` on\n  //!     timeout.\n  static std::optional<ScopedSpinGuard> TryCreateScopedSpinGuard(\n      uint64_t timeout_nanos,\n      SpinGuardState& state) {\n    const uint64_t clock_end_time_nanos =\n        ClockMonotonicNanoseconds() + timeout_nanos;\n    while (true) {\n      bool expected_current_value = false;\n      // `std::atomic::compare_exchange_weak()` is allowed to spuriously fail on\n      // architectures like ARM, which can cause timeouts even for\n      // single-threaded cases (https://crbug.com/340980960,\n      // http://b/296082201).\n      //\n      // Use `std::compare_exchange_strong()` instead to avoid spurious failures\n      // in the common case.\n      if (state.locked.compare_exchange_strong(expected_current_value,\n                                               true,\n                                               std::memory_order_acquire,\n                                               std::memory_order_relaxed)) {\n        return std::make_optional<ScopedSpinGuard>(state);\n      }\n      if (ClockMonotonicNanoseconds() >= clock_end_time_nanos) {\n        return std::nullopt;\n      }\n      SleepNanoseconds(kSpinGuardSleepTimeNanos);\n    }\n\n    NOTREACHED();\n  }\n\n  ~ScopedSpinGuard() {\n    if (state_) {\n#ifdef NDEBUG\n      state_->locked.store(false, std::memory_order_release);\n#else\n      bool old = state_->locked.exchange(false, std::memory_order_release);\n      DCHECK(old);\n#endif\n    }\n  }\n\n  //! \\brief A `ScopedSpinGuard` wrapping a locked `SpinGuardState`.\n  //! \\param[in,out] locked_state A locked `SpinGuardState`. This method\n  //!     holds a pointer to `state`, so `state` must outlive the lifetime of\n  //!     this object.\n  ScopedSpinGuard(SpinGuardState& locked_state) : state_(&locked_state) {\n    DCHECK(locked_state.locked);\n  }\n\n private:\n  // \\brief Optional spinlock state, unlocked when this object goes out of\n  //     scope.\n  //\n  // If this is `nullptr`, then this object has been moved from, and the state\n  // is no longer valid. In that case, nothing will be unlocked when this object\n  // is destroyed.\n  SpinGuardState* state_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_SYNCHRONIZATION_SCOPED_SPIN_GUARD_H_\n"
  },
  {
    "path": "util/synchronization/scoped_spin_guard_test.cc",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/synchronization/scoped_spin_guard.h\"\n\n#include <optional>\n#include <thread>\n\n#include \"gtest/gtest.h\"\n#include \"util/misc/clock.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ScopedSpinGuard, TryCreateScopedSpinGuardShouldLockStateWhileInScope) {\n  SpinGuardState s;\n  EXPECT_FALSE(s.locked);\n  {\n    std::optional<ScopedSpinGuard> guard =\n        ScopedSpinGuard::TryCreateScopedSpinGuard(/*timeout_nanos=*/0, s);\n    EXPECT_NE(std::nullopt, guard);\n    EXPECT_TRUE(s.locked);\n  }\n  EXPECT_FALSE(s.locked);\n}\n\nTEST(\n    ScopedSpinGuard,\n    TryCreateScopedSpinGuardWithZeroTimeoutShouldFailImmediatelyIfStateLocked) {\n  SpinGuardState s;\n  s.locked = true;\n  std::optional<ScopedSpinGuard> guard =\n      ScopedSpinGuard::TryCreateScopedSpinGuard(/*timeout_nanos=*/0, s);\n  EXPECT_EQ(std::nullopt, guard);\n  EXPECT_TRUE(s.locked);\n}\n\nTEST(\n    ScopedSpinGuard,\n    TryCreateScopedSpinGuardWithNonZeroTimeoutShouldSucceedIfStateUnlockedDuringTimeout) {\n  SpinGuardState s;\n  s.locked = true;\n  std::thread unlock_thread([&s] {\n    constexpr uint64_t kUnlockThreadSleepTimeNanos = 10000;  // 10 us\n    EXPECT_TRUE(s.locked);\n    SleepNanoseconds(kUnlockThreadSleepTimeNanos);\n    s.locked = false;\n  });\n  constexpr uint64_t kLockThreadTimeoutNanos = 180000000000ULL;  // 3 minutes\n  std::optional<ScopedSpinGuard> guard =\n      ScopedSpinGuard::TryCreateScopedSpinGuard(kLockThreadTimeoutNanos, s);\n  EXPECT_NE(std::nullopt, guard);\n  EXPECT_TRUE(s.locked);\n  unlock_thread.join();\n}\n\nTEST(ScopedSpinGuard, SwapShouldMaintainSpinLock) {\n  SpinGuardState s;\n  std::optional<ScopedSpinGuard> outer_guard;\n  EXPECT_EQ(std::nullopt, outer_guard);\n  {\n    std::optional<ScopedSpinGuard> inner_guard =\n        ScopedSpinGuard::TryCreateScopedSpinGuard(/*timeout_nanos=*/0, s);\n    EXPECT_NE(std::nullopt, inner_guard);\n    EXPECT_TRUE(s.locked);\n    // If the move-assignment operator for `ScopedSpinGuard` is implemented\n    // incorrectly (e.g., the `= default` implementation), `inner_guard`\n    // will incorrectly think it still \"owns\" the spinlock after the swap,\n    // and when it falls out of scope, it will release the lock prematurely\n    // when it destructs.\n    //\n    // Confirm that the spinlock stays locked even after the swap.\n    std::swap(outer_guard, inner_guard);\n    EXPECT_TRUE(s.locked);\n    EXPECT_EQ(std::nullopt, inner_guard);\n  }\n  EXPECT_NE(std::nullopt, outer_guard);\n  EXPECT_TRUE(s.locked);\n}\n\nTEST(ScopedSpinGuard, MoveAssignmentShouldMaintainSpinLock) {\n  SpinGuardState s;\n  std::optional<ScopedSpinGuard> outer_guard;\n  EXPECT_EQ(std::nullopt, outer_guard);\n  {\n    outer_guard =\n        ScopedSpinGuard::TryCreateScopedSpinGuard(/*timeout_nanos=*/0, s);\n    EXPECT_NE(std::nullopt, outer_guard);\n    EXPECT_TRUE(s.locked);\n  }\n  EXPECT_NE(std::nullopt, outer_guard);\n  EXPECT_TRUE(s.locked);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/synchronization/semaphore.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_\n#define CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_\n\n#include <limits>\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_APPLE)\n#include <dispatch/dispatch.h>\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#elif BUILDFLAG(IS_ANDROID)\n#include <condition_variable>\n#include <mutex>\n#else\n#include <semaphore.h>\n#endif\n\nnamespace crashpad {\n\n//! \\brief An anonymous in-process counting sempahore.\nclass Semaphore {\n public:\n  //! \\brief A TimedWait() argument that causes an indefinite wait.\n  static constexpr double kIndefiniteWait =\n      std::numeric_limits<double>::infinity();\n\n  //! \\brief Initializes the semaphore.\n  //!\n  //! \\param[in] value The initial value of the semaphore.\n  //!\n  //! If the semaphore cannot be created, execution is terminated.\n  explicit Semaphore(int value);\n\n  ~Semaphore();\n\n  //! \\brief Performs the wait (or “procure”) operation on the semaphore.\n  //!\n  //! Atomically decrements the value of the semaphore by 1. If the new value is\n  //! negative, this function blocks and will not return until the semaphore’s\n  //! value is incremented to 0 by Signal().\n  //!\n  //! \\sa TimedWait()\n  void Wait();\n\n  //! \\brief Performs a timed wait (or “procure”) operation on the semaphore.\n  //!\n  //! \\param[in] seconds The maximum number of seconds to wait for the operation\n  //!     to complete. If \\a seconds is #kIndefiniteWait, this method behaves as\n  //!     Wait(), and will not time out.\n  //!\n  //! \\return `false` if the wait timed out, `true` otherwise.\n  //!\n  //! This method is simlar to Wait(), except that the amount of time that it\n  //! blocks is limited.\n  bool TimedWait(double seconds);\n\n  //! \\brief Performs the signal (or “post”) operation on the semaphore.\n  //!\n  //! Atomically increments the value of the semaphore by 1. If the new value is\n  //! 0, a caller blocked in Wait() will be awakened.\n  void Signal();\n\n private:\n#if BUILDFLAG(IS_APPLE)\n  dispatch_semaphore_t semaphore_;\n#elif BUILDFLAG(IS_WIN)\n  HANDLE semaphore_;\n#elif BUILDFLAG(IS_ANDROID)\n  std::condition_variable cv_;\n  std::mutex mutex_;\n  int value_;\n#else\n  sem_t semaphore_;\n#endif\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_\n"
  },
  {
    "path": "util/synchronization/semaphore_mac.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/synchronization/semaphore.h\"\n\n#include <cmath>\n#include <ostream>\n\n#include \"base/check_op.h\"\n\nnamespace crashpad {\n\nSemaphore::Semaphore(int value)\n    : semaphore_(dispatch_semaphore_create(value)) {\n  CHECK(semaphore_) << \"dispatch_semaphore_create\";\n}\n\nSemaphore::~Semaphore() {\n  dispatch_release(semaphore_);\n}\n\nvoid Semaphore::Wait() {\n  CHECK_EQ(dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER), 0);\n}\n\nbool Semaphore::TimedWait(double seconds) {\n  DCHECK_GE(seconds, 0.0);\n\n  if (std::isinf(seconds)) {\n    Wait();\n    return true;\n  }\n\n  const dispatch_time_t timeout =\n      dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);\n  return dispatch_semaphore_wait(semaphore_, timeout) == 0;\n}\n\nvoid Semaphore::Signal() {\n  dispatch_semaphore_signal(semaphore_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/synchronization/semaphore_posix.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/synchronization/semaphore.h\"\n\n#include <errno.h>\n#include <time.h>\n\n#include <chrono>\n#include <cmath>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n#include \"base/posix/eintr_wrapper.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/time.h\"\n\nnamespace crashpad {\n\n#if BUILDFLAG(IS_ANDROID)\n\nSemaphore::Semaphore(int value) : cv_(), mutex_(), value_(value) {}\n\nSemaphore::~Semaphore() = default;\n\nvoid Semaphore::Wait() {\n  std::unique_lock<std::mutex> lock(mutex_);\n  cv_.wait(lock, [this] { return this->value_ > 0; });\n  --value_;\n}\n\nbool Semaphore::TimedWait(double seconds) {\n  DCHECK_GE(seconds, 0.0);\n\n  if (std::isinf(seconds)) {\n    Wait();\n    return true;\n  }\n\n  std::unique_lock<std::mutex> lock(mutex_);\n  if (!cv_.wait_for(lock, std::chrono::duration<double>(seconds), [this] {\n        return this->value_ > 0;\n      })) {\n    return false;\n  }\n  --value_;\n  return true;\n}\n\nvoid Semaphore::Signal() {\n  std::lock_guard<std::mutex> lock(mutex_);\n  ++value_;\n  cv_.notify_one();\n}\n\n#elif !BUILDFLAG(IS_APPLE)\n\nSemaphore::Semaphore(int value) {\n  PCHECK(sem_init(&semaphore_, 0, value) == 0) << \"sem_init\";\n}\n\nSemaphore::~Semaphore() {\n  PCHECK(sem_destroy(&semaphore_) == 0) << \"sem_destroy\";\n}\n\nvoid Semaphore::Wait() {\n  PCHECK(HANDLE_EINTR(sem_wait(&semaphore_)) == 0) << \"sem_wait\";\n}\n\nbool Semaphore::TimedWait(double seconds) {\n  DCHECK_GE(seconds, 0.0);\n\n  if (std::isinf(seconds)) {\n    Wait();\n    return true;\n  }\n\n  timespec current_time;\n  if (clock_gettime(CLOCK_REALTIME, &current_time) != 0) {\n    PLOG(ERROR) << \"clock_gettime\";\n    return false;\n  }\n  timespec timeout;\n  timeout.tv_sec = seconds;\n  timeout.tv_nsec = (seconds - trunc(seconds)) * 1E9;\n  AddTimespec(current_time, timeout, &timeout);\n\n  int rv = HANDLE_EINTR(sem_timedwait(&semaphore_, &timeout));\n  PCHECK(rv == 0 || errno == ETIMEDOUT) << \"sem_timedwait\";\n  return rv == 0;\n}\n\nvoid Semaphore::Signal() {\n  PCHECK(sem_post(&semaphore_) == 0) << \"sem_post\";\n}\n\n#endif  // BUILDFLAG(IS_ANDROID)\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/synchronization/semaphore_test.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/synchronization/semaphore.h\"\n\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include <pthread.h>\n#endif  // BUILDFLAG(IS_POSIX)\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Semaphore, Simple) {\n  Semaphore semaphore(1);\n  semaphore.Wait();\n  semaphore.Signal();\n}\n\nTEST(Semaphore, TimedWait) {\n  Semaphore semaphore(0);\n  semaphore.Signal();\n  EXPECT_TRUE(semaphore.TimedWait(0.01));  // 10ms\n}\n\nTEST(Semaphore, TimedWaitTimeout) {\n  Semaphore semaphore(0);\n  semaphore.Signal();\n  constexpr double kTenMs = 0.01;\n  EXPECT_TRUE(semaphore.TimedWait(kTenMs));\n  EXPECT_FALSE(semaphore.TimedWait(kTenMs));\n}\n\nTEST(Semaphore, TimedWaitInfinite_0) {\n  Semaphore semaphore(0);\n  semaphore.Signal();\n  EXPECT_TRUE(semaphore.TimedWait(std::numeric_limits<double>::infinity()));\n}\n\nTEST(Semaphore, TimedWaitInfinite_1) {\n  Semaphore semaphore(1);\n  EXPECT_TRUE(semaphore.TimedWait(std::numeric_limits<double>::infinity()));\n  semaphore.Signal();\n}\n\nstruct ThreadMainInfo {\n#if BUILDFLAG(IS_POSIX)\n  pthread_t pthread;\n#elif BUILDFLAG(IS_WIN)\n  HANDLE thread;\n#endif\n  Semaphore* semaphore;\n  size_t iterations;\n};\n\n#if BUILDFLAG(IS_POSIX)\nvoid*\n#elif BUILDFLAG(IS_WIN)\nDWORD WINAPI\n#endif  // BUILDFLAG(IS_POSIX)\nThreadMain(void* argument) {\n  ThreadMainInfo* info = reinterpret_cast<ThreadMainInfo*>(argument);\n  for (size_t iteration = 0; iteration < info->iterations; ++iteration) {\n    info->semaphore->Wait();\n  }\n#if BUILDFLAG(IS_POSIX)\n  return nullptr;\n#elif BUILDFLAG(IS_WIN)\n  return 0;\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\nvoid StartThread(ThreadMainInfo* info) {\n#if BUILDFLAG(IS_POSIX)\n  int rv = pthread_create(&info->pthread, nullptr, ThreadMain, info);\n  ASSERT_EQ(rv, 0) << \"pthread_create\";\n#elif BUILDFLAG(IS_WIN)\n  info->thread = CreateThread(nullptr, 0, ThreadMain, info, 0, nullptr);\n  ASSERT_NE(info->thread, nullptr) << \"CreateThread\";\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\nvoid JoinThread(ThreadMainInfo* info) {\n#if BUILDFLAG(IS_POSIX)\n  int rv = pthread_join(info->pthread, nullptr);\n  EXPECT_EQ(rv, 0) << \"pthread_join\";\n#elif BUILDFLAG(IS_WIN)\n  DWORD result = WaitForSingleObject(info->thread, INFINITE);\n  EXPECT_EQ(result, WAIT_OBJECT_0) << \"WaitForSingleObject\";\n#endif  // BUILDFLAG(IS_POSIX)\n}\n\nTEST(Semaphore, Threaded) {\n  Semaphore semaphore(0);\n  ThreadMainInfo info;\n  info.semaphore = &semaphore;\n  info.iterations = 1;\n\n  ASSERT_NO_FATAL_FAILURE(StartThread(&info));\n\n  semaphore.Signal();\n\n  JoinThread(&info);\n}\n\nTEST(Semaphore, TenThreaded) {\n  // This test has a smaller initial value (5) than threads contending for these\n  // resources (10), and the threads each try to obtain the resource a different\n  // number of times.\n  Semaphore semaphore(5);\n  ThreadMainInfo info[10];\n  size_t iterations = 0;\n  for (size_t index = 0; index < std::size(info); ++index) {\n    info[index].semaphore = &semaphore;\n    info[index].iterations = index;\n    iterations += info[index].iterations;\n\n    ASSERT_NO_FATAL_FAILURE(StartThread(&info[index]));\n  }\n\n  for (size_t iteration = 0; iteration < iterations; ++iteration) {\n    semaphore.Signal();\n  }\n\n  for (size_t index = 0; index < std::size(info); ++index) {\n    JoinThread(&info[index]);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/synchronization/semaphore_win.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/synchronization/semaphore.h\"\n\n#include <cmath>\n#include <limits>\n#include <ostream>\n\n#include \"base/check_op.h\"\n\nnamespace crashpad {\n\nSemaphore::Semaphore(int value)\n    : semaphore_(CreateSemaphore(nullptr,\n                                 value,\n                                 std::numeric_limits<LONG>::max(),\n                                 nullptr)) {\n  PCHECK(semaphore_) << \"CreateSemaphore\";\n}\n\nSemaphore::~Semaphore() {\n  PCHECK(CloseHandle(semaphore_));\n}\n\nvoid Semaphore::Wait() {\n  PCHECK(WaitForSingleObject(semaphore_, INFINITE) == WAIT_OBJECT_0);\n}\n\nbool Semaphore::TimedWait(double seconds) {\n  DCHECK_GE(seconds, 0.0);\n\n  if (std::isinf(seconds)) {\n    Wait();\n    return true;\n  }\n\n  DWORD rv = WaitForSingleObject(semaphore_, static_cast<DWORD>(seconds * 1E3));\n  PCHECK(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT) << \"WaitForSingleObject\";\n  return rv == WAIT_OBJECT_0;\n}\n\nvoid Semaphore::Signal() {\n  PCHECK(ReleaseSemaphore(semaphore_, 1, nullptr));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/thread/stoppable.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_THREAD_STOPPABLE_H_\n#define CRASHPAD_UTIL_THREAD_STOPPABLE_H_\n\n\nnamespace crashpad {\n\n//! \\brief An interface for operations that may be Started and Stopped.\nclass Stoppable {\n public:\n  virtual ~Stoppable() = default;\n\n  //! \\brief Starts the operation.\n  virtual void Start() = 0;\n\n  //! \\brief Stops the operation.\n  virtual void Stop() = 0;\n\n protected:\n  Stoppable() = default;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_THREAD_STOPPABLE_H_\n"
  },
  {
    "path": "util/thread/thread.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/thread/thread.h\"\n\n#include \"base/check.h\"\n\nnamespace crashpad {\n\nThread::Thread() : platform_thread_(0) {\n}\n\nThread::~Thread() {\n  DCHECK(!platform_thread_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/thread/thread.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_THREAD_THREAD_H_\n#define CRASHPAD_UTIL_THREAD_THREAD_H_\n\n#include \"build/build_config.h\"\n\n#if BUILDFLAG(IS_POSIX)\n#include <pthread.h>\n#include <stdint.h>\n#elif BUILDFLAG(IS_WIN)\n#include <windows.h>\n#endif  // BUILDFLAG(IS_POSIX)\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n//! \\brief Basic thread abstraction. Users should derive from this\n//!     class and implement ThreadMain().\nclass Thread {\n public:\n  Thread();\n\n  Thread(const Thread&) = delete;\n  Thread& operator=(const Thread&) = delete;\n\n  virtual ~Thread();\n\n  //! \\brief Create a platform thread, and run ThreadMain() on that thread. Must\n  //!     be paired with a call to Join().\n  void Start();\n\n  //! \\brief Block until ThreadMain() exits. This may be called from any thread.\n  //!     Must paired with a call to Start().\n  void Join();\n\n#if BUILDFLAG(IS_APPLE)\n  //! \\brief Returns the thread id of the Thread pthread_t.\n  static uint64_t GetThreadIdForTesting();\n#endif  // BUILDFLAG(IS_APPLE)\n\n private:\n  //! \\brief The thread entry point to be implemented by the subclass.\n  virtual void ThreadMain() = 0;\n\n  static\n#if BUILDFLAG(IS_POSIX)\n      void*\n#elif BUILDFLAG(IS_WIN)\n      DWORD WINAPI\n#endif  // BUILDFLAG(IS_POSIX)\n      ThreadEntryThunk(void* argument);\n\n#if BUILDFLAG(IS_POSIX)\n  pthread_t platform_thread_;\n#elif BUILDFLAG(IS_WIN)\n  HANDLE platform_thread_;\n#endif\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_THREAD_THREAD_H_\n"
  },
  {
    "path": "util/thread/thread_log_messages.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/thread/thread_log_messages.h\"\n\n#include <sys/types.h>\n\n#include \"base/check_op.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nthread_local std::vector<std::string>* thread_local_log_messages;\n\nbool LogMessageHandler(logging::LogSeverity severity,\n                       const char* file_path,\n                       int line,\n                       size_t message_start,\n                       const std::string& string) {\n  if (thread_local_log_messages) {\n    thread_local_log_messages->push_back(string);\n  }\n\n  // Don’t consume the message. Allow it to be logged as if nothing was set as\n  // the log message handler.\n  return false;\n}\n\n}  // namespace\n\nThreadLogMessages::ThreadLogMessages()\n    : log_messages_(),\n      reset_thread_local_log_messages_(&thread_local_log_messages,\n                                       &log_messages_) {\n  [[maybe_unused]] static bool initialized = [] {\n    DCHECK(!logging::GetLogMessageHandler());\n    logging::SetLogMessageHandler(LogMessageHandler);\n    return true;\n  }();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/thread/thread_log_messages.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_\n#define CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_\n\n#include <string>\n#include <vector>\n\n#include \"base/auto_reset.h\"\n\nnamespace crashpad {\n\n//! \\brief Captures log messages produced on the current thread during an\n//!     object’s lifetime.\n//!\n//! At most one object of this class type may exist on a single thread at a\n//! time. When using this class, no other part of the program may call\n//! `logging::SetLogMessageHandler()` at any time.\nclass ThreadLogMessages {\n public:\n  ThreadLogMessages();\n\n  ThreadLogMessages(const ThreadLogMessages&) = delete;\n  ThreadLogMessages& operator=(const ThreadLogMessages&) = delete;\n\n  ~ThreadLogMessages() = default;\n\n  //! \\return The log messages collected on the thread that this object was\n  //!     created on since the time it was created.\n  const std::vector<std::string>& log_messages() const { return log_messages_; }\n\n private:\n  std::vector<std::string> log_messages_;\n  const base::AutoReset<std::vector<std::string>*>\n      reset_thread_local_log_messages_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_THREAD_THREAD_LOG_MESSAGES_H_\n"
  },
  {
    "path": "util/thread/thread_log_messages_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/thread/thread_log_messages.h\"\n\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"gtest/gtest.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(ThreadLogMessages, Empty) {\n  ThreadLogMessages thread_log_messages;\n\n  const std::vector<std::string>& log_messages =\n      thread_log_messages.log_messages();\n\n  EXPECT_TRUE(log_messages.empty());\n}\n\nvoid ExpectLogMessage(const std::string& message, const std::string& expected) {\n  ASSERT_GT(message.size(), expected.size());\n  EXPECT_EQ(message.back(), '\\n');\n  EXPECT_STREQ(\n      message.substr(message.size() - expected.size() - 1, expected.size())\n          .c_str(),\n      expected.c_str());\n}\n\nTEST(ThreadLogMessages, Basic) {\n  // Logging must be enabled at least at this level for this test to work.\n  ASSERT_TRUE(LOG_IS_ON(INFO));\n\n  {\n    static constexpr const char* kMessages[] = {\n      \"An info message\",\n      \"A warning message\",\n      \"An error message\",\n    };\n\n    ThreadLogMessages thread_log_messages;\n\n    LOG(INFO) << kMessages[0];\n    LOG(WARNING) << kMessages[1];\n    LOG(ERROR) << kMessages[2];\n\n    const std::vector<std::string>& log_messages =\n        thread_log_messages.log_messages();\n\n    EXPECT_EQ(log_messages.size(), std::size(kMessages));\n    for (size_t index = 0; index < std::size(kMessages); ++index) {\n      ASSERT_NO_FATAL_FAILURE(\n          ExpectLogMessage(log_messages[index], kMessages[index]))\n          << \"index \" << index;\n    }\n  }\n\n  {\n    static constexpr char kMessage[] = \"Sample error message\";\n\n    ThreadLogMessages thread_log_messages;\n\n    LOG(ERROR) << kMessage;\n\n    const std::vector<std::string>& log_messages =\n        thread_log_messages.log_messages();\n\n    EXPECT_EQ(log_messages.size(), 1u);\n    ExpectLogMessage(log_messages[0], kMessage);\n  }\n\n  {\n    ThreadLogMessages thread_log_messages;\n\n    LOG(INFO) << \"I can't believe I \" << \"streamed\" << \" the whole thing.\";\n\n    const std::vector<std::string>& log_messages =\n        thread_log_messages.log_messages();\n\n    EXPECT_EQ(log_messages.size(), 1u);\n    ExpectLogMessage(log_messages[0],\n                     \"I can't believe I streamed the whole thing.\");\n  }\n}\n\nclass LoggingTestThread : public Thread {\n public:\n  LoggingTestThread() : thread_number_(0), start_(0), count_(0) {}\n\n  LoggingTestThread(const LoggingTestThread&) = delete;\n  LoggingTestThread& operator=(const LoggingTestThread&) = delete;\n\n  ~LoggingTestThread() override {}\n\n  void Initialize(size_t thread_number, int start, int count) {\n    thread_number_ = thread_number;\n    start_ = start;\n    count_ = count;\n  }\n\n private:\n  void ThreadMain() override {\n    ThreadLogMessages thread_log_messages;\n\n    std::vector<std::string> expected_messages;\n    for (int index = start_; index < start_ + count_; ++index) {\n      std::string message = base::StringPrintf(\"message %d\", index);\n      expected_messages.push_back(message);\n      LOG(WARNING) << message;\n    }\n\n    const std::vector<std::string>& log_messages =\n        thread_log_messages.log_messages();\n\n    ASSERT_EQ(log_messages.size(), static_cast<size_t>(count_));\n    for (size_t index = 0; index < log_messages.size(); ++index) {\n      ASSERT_NO_FATAL_FAILURE(\n          ExpectLogMessage(log_messages[index], expected_messages[index]))\n          << \"thread_number_ \" << thread_number_ << \", index \" << index;\n    }\n  }\n\n  size_t thread_number_;\n  int start_;\n  int count_;\n};\n\nTEST(ThreadLogMessages, Multithreaded) {\n  // Logging must be enabled at least at this level for this test to work.\n  ASSERT_TRUE(LOG_IS_ON(WARNING));\n\n  LoggingTestThread threads[20];\n  int start = 0;\n  for (size_t index = 0; index < std::size(threads); ++index) {\n    threads[index].Initialize(\n        index, static_cast<int>(start), static_cast<int>(index));\n    start += static_cast<int>(index);\n\n    ASSERT_NO_FATAL_FAILURE(threads[index].Start());\n  }\n\n  for (LoggingTestThread& thread : threads) {\n    thread.Join();\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/thread/thread_posix.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/thread/thread.h\"\n\n#include <errno.h>\n\n#include <ostream>\n\n#include \"base/check.h\"\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\nvoid Thread::Start() {\n  DCHECK(!platform_thread_);\n  errno = pthread_create(&platform_thread_, nullptr, ThreadEntryThunk, this);\n  PCHECK(errno == 0) << \"pthread_create\";\n}\n\nvoid Thread::Join() {\n  DCHECK(platform_thread_);\n  errno = pthread_join(platform_thread_, nullptr);\n  PCHECK(errno == 0) << \"pthread_join\";\n  platform_thread_ = 0;\n}\n\n#if BUILDFLAG(IS_APPLE)\n// static\nuint64_t Thread::GetThreadIdForTesting() {\n  uint64_t thread_self;\n  errno = pthread_threadid_np(pthread_self(), &thread_self);\n  PCHECK(errno == 0) << \"pthread_threadid_np\";\n  return thread_self;\n}\n#endif  // BUILDFLAG(IS_APPLE)\n\n// static\nvoid* Thread::ThreadEntryThunk(void* argument) {\n  Thread* self = reinterpret_cast<Thread*>(argument);\n  self->ThreadMain();\n  return nullptr;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/thread/thread_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/thread/thread.h\"\n\n#include \"gtest/gtest.h\"\n#include \"util/synchronization/semaphore.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass NoopThread : public Thread {\n public:\n  NoopThread() {}\n\n  NoopThread(const NoopThread&) = delete;\n  NoopThread& operator=(const NoopThread&) = delete;\n\n  ~NoopThread() override {}\n\n private:\n  void ThreadMain() override {}\n};\n\nclass WaitThread : public Thread {\n public:\n  explicit WaitThread(Semaphore* semaphore) : semaphore_(semaphore) {}\n\n  WaitThread(const WaitThread&) = delete;\n  WaitThread& operator=(const WaitThread&) = delete;\n\n  ~WaitThread() override {}\n\n private:\n  void ThreadMain() override { semaphore_->Wait(); }\n\n  Semaphore* semaphore_;\n};\n\nclass JoinAndSignalThread : public Thread {\n public:\n  JoinAndSignalThread(Thread* thread, Semaphore* semaphore)\n      : thread_(thread), semaphore_(semaphore) {}\n\n  JoinAndSignalThread(const JoinAndSignalThread&) = delete;\n  JoinAndSignalThread& operator=(const JoinAndSignalThread&) = delete;\n\n  ~JoinAndSignalThread() override {}\n\n private:\n  void ThreadMain() override {\n    thread_->Join();\n    semaphore_->Signal();\n  }\n\n  Thread* thread_;\n  Semaphore* semaphore_;\n};\n\nTEST(ThreadTest, NoStart) {\n  NoopThread thread;\n}\n\nTEST(ThreadTest, Start) {\n  NoopThread thread;\n  thread.Start();\n  thread.Join();\n}\n\nTEST(ThreadTest, JoinBlocks) {\n  Semaphore unblock_wait_thread_semaphore(0);\n  Semaphore join_completed_semaphore(0);\n  WaitThread wait_thread(&unblock_wait_thread_semaphore);\n  wait_thread.Start();\n  JoinAndSignalThread join_and_signal_thread(&wait_thread,\n                                             &join_completed_semaphore);\n  join_and_signal_thread.Start();\n  // join_completed_semaphore will be signaled when wait_thread.Join() returns\n  // (in JoinAndSignalThread::ThreadMain). Since wait_thread is blocking on\n  // unblock_wait_thread_semaphore, we don't expect the Join to return yet. We\n  // wait up to 100ms to give a broken implementation of Thread::Join a chance\n  // to return.\n  ASSERT_FALSE(join_completed_semaphore.TimedWait(.1));\n  unblock_wait_thread_semaphore.Signal();\n  join_completed_semaphore.Wait();\n  join_and_signal_thread.Join();\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/thread/thread_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/thread/thread.h\"\n\n#include <ostream>\n\n#include \"base/check.h\"\n\nnamespace crashpad {\n\nvoid Thread::Start() {\n  DCHECK(!platform_thread_);\n  platform_thread_ =\n      CreateThread(nullptr, 0, ThreadEntryThunk, this, 0, nullptr);\n  PCHECK(platform_thread_) << \"CreateThread\";\n}\n\nvoid Thread::Join() {\n  DCHECK(platform_thread_);\n  DWORD result = WaitForSingleObject(platform_thread_, INFINITE);\n  PCHECK(result == WAIT_OBJECT_0) << \"WaitForSingleObject\";\n  platform_thread_ = 0;\n}\n\n// static\nDWORD WINAPI Thread::ThreadEntryThunk(void* argument) {\n  Thread* self = reinterpret_cast<Thread*>(argument);\n  self->ThreadMain();\n  return 0;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/thread/worker_thread.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/thread/worker_thread.h\"\n\n#include \"base/check.h\"\n#include \"util/thread/thread.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\nclass WorkerThreadImpl final : public Thread {\n public:\n  WorkerThreadImpl(WorkerThread* self, double initial_work_delay)\n      : semaphore_(0),\n        initial_work_delay_(initial_work_delay),\n        self_(self) {}\n  ~WorkerThreadImpl() {}\n\n  void ThreadMain() override {\n    if (initial_work_delay_ > 0)\n      semaphore_.TimedWait(initial_work_delay_);\n\n    while (self_->running_ || self_->do_work_now_) {\n      self_->delegate_->DoWork(self_);\n      self_->do_work_now_ = false;\n      semaphore_.TimedWait(self_->work_interval_);\n    }\n  }\n\n  void SignalSemaphore() {\n    semaphore_.Signal();\n  }\n\n private:\n  // TODO(mark): Use a condition variable instead?\n  Semaphore semaphore_;\n  double initial_work_delay_;\n  WorkerThread* self_;  // Weak, owns this.\n};\n\n}  // namespace internal\n\nWorkerThread::WorkerThread(double work_interval,\n                           WorkerThread::Delegate* delegate)\n    : work_interval_(work_interval),\n      delegate_(delegate),\n      impl_(),\n      running_(false),\n      do_work_now_(false) {}\n\nWorkerThread::~WorkerThread() {\n  DCHECK(!running_);\n}\n\nvoid WorkerThread::Start(double initial_work_delay) {\n  DCHECK(!impl_);\n  DCHECK(!running_);\n\n  running_ = true;\n  impl_.reset(new internal::WorkerThreadImpl(this, initial_work_delay));\n  impl_->Start();\n}\n\nvoid WorkerThread::Stop() {\n  DCHECK(running_);\n  DCHECK(impl_);\n\n  if (!running_)\n    return;\n\n  running_ = false;\n\n  impl_->SignalSemaphore();\n  impl_->Join();\n  impl_.reset();\n}\n\nvoid WorkerThread::DoWorkNow() {\n  DCHECK(running_);\n  do_work_now_ = true;\n  impl_->SignalSemaphore();\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/thread/worker_thread.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_THREAD_WORKER_THREAD_H_\n#define CRASHPAD_UTIL_THREAD_WORKER_THREAD_H_\n\n#include <atomic>\n#include <memory>\n\n#include \"util/synchronization/semaphore.h\"\n\nnamespace crashpad {\n\nnamespace internal {\nclass WorkerThreadImpl;\n}  // namespace internal\n\n//! \\brief A WorkerThread executes its Delegate's DoWork method repeatedly on a\n//!     dedicated thread at a set time interval.\nclass WorkerThread {\n public:\n  //! \\brief An interface for doing work on a WorkerThread.\n  class Delegate {\n   public:\n    //! \\brief The work function executed by the WorkerThread every work\n    //!     interval.\n    virtual void DoWork(const WorkerThread* thread) = 0;\n\n   protected:\n    virtual ~Delegate() {}\n  };\n\n  //! \\brief A delay or interval argument that causes an indefinite wait.\n  static constexpr double kIndefiniteWait = Semaphore::kIndefiniteWait;\n\n  //! \\brief Creates a new WorkerThread that is not yet running.\n  //!\n  //! \\param[in] work_interval The time interval in seconds at which the \\a\n  //!     delegate runs. The interval counts from the completion of\n  //!     Delegate::DoWork() to the next invocation. This can be\n  //!     #kIndefiniteWait if work should only be done when DoWorkNow() is\n  //!     called.\n  //! \\param[in] delegate The work delegate to invoke every interval.\n  WorkerThread(double work_interval, Delegate* delegate);\n\n  WorkerThread(const WorkerThread&) = delete;\n  WorkerThread& operator=(const WorkerThread&) = delete;\n\n  ~WorkerThread();\n\n  //! \\brief Starts the worker thread.\n  //!\n  //! This may not be called if the thread is_running().\n  //!\n  //! \\param[in] initial_work_delay The amount of time in seconds to wait\n  //!     before invoking the \\a delegate for the first time. Pass `0` for\n  //!     no delay. This can be #kIndefiniteWait if work should not be done\n  //!     until DoWorkNow() is called.\n  void Start(double initial_work_delay);\n\n  //! \\brief Stops the worker thread from running.\n  //!\n  //! This may only be called if the thread is_running().\n  //!\n  //! If the work function is currently executing, this will not interrupt it.\n  //! This method stops any future work from occurring. This method is safe\n  //! to call from any thread with the exception of the worker thread itself,\n  //! as this joins the thread.\n  void Stop();\n\n  //! \\brief Interrupts a \\a work_interval to execute the work function\n  //!     immediately. This invokes Delegate::DoWork() on the thread, without\n  //!     waiting for the current \\a work_interval to expire. After the\n  //!     delegate is invoked, the WorkerThread will start waiting for a new\n  //!     \\a work_interval.\n  void DoWorkNow();\n\n  //! \\return `true` if the thread is running, `false` if it is not.\n  bool is_running() const { return running_; }\n\n private:\n  friend class internal::WorkerThreadImpl;\n\n  double work_interval_;\n  Delegate* delegate_;  // weak\n  std::unique_ptr<internal::WorkerThreadImpl> impl_;\n  std::atomic_bool running_;\n  std::atomic_bool do_work_now_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_THREAD_WORKER_THREAD_H_\n"
  },
  {
    "path": "util/thread/worker_thread_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/thread/worker_thread.h\"\n\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"util/misc/clock.h\"\n#include \"util/synchronization/semaphore.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr uint64_t kNanosecondsPerSecond = static_cast<uint64_t>(1E9);\n\nclass WorkDelegate : public WorkerThread::Delegate {\n public:\n  WorkDelegate() {}\n\n  WorkDelegate(const WorkDelegate&) = delete;\n  WorkDelegate& operator=(const WorkDelegate&) = delete;\n\n  ~WorkDelegate() {}\n\n  void DoWork(const WorkerThread* thread) override {\n    if (work_count_ < waiting_for_count_) {\n      if (++work_count_ == waiting_for_count_) {\n        semaphore_.Signal();\n      }\n    }\n  }\n\n  void SetDesiredWorkCount(int times) {\n    waiting_for_count_ = times;\n  }\n\n  //! \\brief Suspends the calling thread until the DoWork() has been called\n  //!     the number of times specified by SetDesiredWorkCount().\n  void WaitForWorkCount() {\n    semaphore_.Wait();\n  }\n\n  int work_count() const { return work_count_; }\n\n private:\n  Semaphore semaphore_{0};\n  int work_count_ = 0;\n  int waiting_for_count_ = -1;\n};\n\nTEST(WorkerThread, DoWork) {\n  WorkDelegate delegate;\n  WorkerThread thread(0.05, &delegate);\n\n  uint64_t start = ClockMonotonicNanoseconds();\n\n  delegate.SetDesiredWorkCount(2);\n  thread.Start(0);\n  EXPECT_TRUE(thread.is_running());\n\n  delegate.WaitForWorkCount();\n  thread.Stop();\n  EXPECT_FALSE(thread.is_running());\n\n// Fuchsia's scheduler is very antagonistic. The assumption that the two work\n// items complete in some particular amount of time is strictly incorrect, but\n// also somewhat useful. The expected time \"should\" be ~40-50ms with a work\n// interval of 0.05s, but on Fuchsia, 1200ms was observed. So, on Fuchsia, use a\n// much larger timeout. See https://crashpad.chromium.org/bug/231.\n#if BUILDFLAG(IS_FUCHSIA)\n  constexpr uint64_t kUpperBoundTime = 10;\n#else\n  constexpr uint64_t kUpperBoundTime = 1;\n#endif\n  EXPECT_GE(kUpperBoundTime * kNanosecondsPerSecond,\n            ClockMonotonicNanoseconds() - start);\n}\n\nTEST(WorkerThread, StopBeforeDoWork) {\n  WorkDelegate delegate;\n  WorkerThread thread(1, &delegate);\n\n  thread.Start(15);\n  thread.Stop();\n\n  EXPECT_EQ(delegate.work_count(), 0);\n}\n\nTEST(WorkerThread, Restart) {\n  WorkDelegate delegate;\n  WorkerThread thread(0.05, &delegate);\n\n  delegate.SetDesiredWorkCount(1);\n  thread.Start(0);\n  EXPECT_TRUE(thread.is_running());\n\n  delegate.WaitForWorkCount();\n  thread.Stop();\n  ASSERT_FALSE(thread.is_running());\n\n  delegate.SetDesiredWorkCount(2);\n  thread.Start(0);\n  delegate.WaitForWorkCount();\n  thread.Stop();\n  ASSERT_FALSE(thread.is_running());\n}\n\nTEST(WorkerThread, DoWorkNow) {\n  WorkDelegate delegate;\n  WorkerThread thread(100, &delegate);\n\n  uint64_t start = ClockMonotonicNanoseconds();\n\n  delegate.SetDesiredWorkCount(1);\n  thread.Start(0);\n  EXPECT_TRUE(thread.is_running());\n\n  delegate.WaitForWorkCount();\n  EXPECT_EQ(delegate.work_count(), 1);\n\n  delegate.SetDesiredWorkCount(2);\n  thread.DoWorkNow();\n  delegate.WaitForWorkCount();\n  thread.Stop();\n  EXPECT_EQ(delegate.work_count(), 2);\n\n  EXPECT_GE(100 * kNanosecondsPerSecond, ClockMonotonicNanoseconds() - start);\n}\n\nTEST(WorkerThread, DoWorkNowAtStart) {\n  WorkDelegate delegate;\n  WorkerThread thread(100, &delegate);\n\n  uint64_t start = ClockMonotonicNanoseconds();\n\n  delegate.SetDesiredWorkCount(1);\n  thread.Start(100);\n  EXPECT_TRUE(thread.is_running());\n\n  thread.DoWorkNow();\n  delegate.WaitForWorkCount();\n  EXPECT_EQ(delegate.work_count(), 1);\n\n  EXPECT_GE(100 * kNanosecondsPerSecond, ClockMonotonicNanoseconds() - start);\n\n  thread.Stop();\n  EXPECT_FALSE(thread.is_running());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/address_types.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_\n#define CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_\n\n#include <stdint.h>\n\nnamespace crashpad {\n\n//! \\brief Type used to represent an address in a process, potentially across\n//!     bitness.\nusing WinVMAddress = uint64_t;\n\n//! \\brief Type used to represent the size of a memory range (with a\n//!     WinVMAddress), potentially across bitness.\nusing WinVMSize = uint64_t;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_ADDRESS_TYPES_H_\n"
  },
  {
    "path": "util/win/checked_win_address_range.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_\n#define CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_\n\n#include \"util/numeric/checked_address_range.h\"\n#include \"util/win/address_types.h\"\n\nnamespace crashpad {\n\n//! \\brief Ensures that a range, composed of a base and a size, does not\n//!     overflow the pointer type of the process it describes a range in.\n//!\n//! This class checks bases of type WinVMAddress and sizes of type WinVMSize\n//! against a process whose pointer type is either 32 or 64 bits wide.\n//!\n//! Aside from varying the overall range on the basis of a process' pointer type\n//! width, this class functions very similarly to CheckedRange.\nusing CheckedWinAddressRange =\n    internal::CheckedAddressRangeGeneric<WinVMAddress, WinVMSize>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_CHECKED_WIN_ADDRESS_RANGE_H_\n"
  },
  {
    "path": "util/win/command_line.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/command_line.h\"\n\n#include <sys/types.h>\n\nnamespace crashpad {\n\n// Ref:\n// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/\nvoid AppendCommandLineArgument(const std::wstring& argument,\n                               std::wstring* command_line) {\n  if (!command_line->empty()) {\n    command_line->push_back(L' ');\n  }\n\n  // Don’t bother quoting if unnecessary.\n  if (!argument.empty() &&\n      argument.find_first_of(L\" \\t\\n\\v\\\"\") == std::wstring::npos) {\n    command_line->append(argument);\n  } else {\n    command_line->push_back(L'\"');\n    for (std::wstring::const_iterator i = argument.begin();; ++i) {\n      size_t backslash_count = 0;\n      while (i != argument.end() && *i == L'\\\\') {\n        ++i;\n        ++backslash_count;\n      }\n      if (i == argument.end()) {\n        // Escape all backslashes, but let the terminating double quotation mark\n        // we add below be interpreted as a metacharacter.\n        command_line->append(backslash_count * 2, L'\\\\');\n        break;\n      } else if (*i == L'\"') {\n        // Escape all backslashes and the following double quotation mark.\n        command_line->append(backslash_count * 2 + 1, L'\\\\');\n        command_line->push_back(*i);\n      } else {\n        // Backslashes aren’t special here.\n        command_line->append(backslash_count, L'\\\\');\n        command_line->push_back(*i);\n      }\n    }\n    command_line->push_back(L'\"');\n  }\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/command_line.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_COMMAND_LINE_H_\n#define CRASHPAD_UTIL_WIN_COMMAND_LINE_H_\n\n#include <string>\n\nnamespace crashpad {\n\n//! \\brief Utility function for building escaped command lines.\n//!\n//! This builds a command line so that individual arguments can be reliably\n//! decoded by `CommandLineToArgvW()`.\n//!\n//! \\a argument is appended to \\a command_line. If necessary, it will be placed\n//! in quotation marks and escaped properly. If \\a command_line is initially\n//! non-empty, a space will precede \\a argument.\n//!\n//! \\param[in] argument The argument to append to \\a command_line.\n//! \\param[in,out] command_line The command line being constructed.\nvoid AppendCommandLineArgument(const std::wstring& argument,\n                               std::wstring* command_line);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_COMMAND_LINE_H_\n"
  },
  {
    "path": "util/win/command_line_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/command_line.h\"\n\n#include <windows.h>\n#include <shellapi.h>\n#include <sys/types.h>\n\n#include <iterator>\n\n#include \"base/scoped_generic.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"util/win/scoped_local_alloc.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Calls AppendCommandLineArgument() for every argument in argv, then calls\n// CommandLineToArgvW() to decode the string into a vector again, and compares\n// the input and output.\nvoid AppendCommandLineArgumentTest(size_t argc, const wchar_t* const argv[]) {\n  std::wstring command_line;\n  for (size_t index = 0; index < argc; ++index) {\n    AppendCommandLineArgument(argv[index], &command_line);\n  }\n\n  int test_argc;\n  wchar_t** test_argv = CommandLineToArgvW(command_line.c_str(), &test_argc);\n\n  ASSERT_TRUE(test_argv) << ErrorMessage(\"CommandLineToArgvW\");\n  ScopedLocalAlloc test_argv_owner(test_argv);\n  ASSERT_EQ(test_argc, static_cast<int>(argc));\n\n  for (size_t index = 0; index < argc; ++index) {\n    EXPECT_STREQ(argv[index], test_argv[index]) << \"index \" << index;\n  }\n  EXPECT_FALSE(test_argv[argc]);\n}\n\nTEST(CommandLine, AppendCommandLineArgument) {\n  // Most of these test cases come from\n  // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/,\n  // which was also a reference for the implementation of\n  // AppendCommandLineArgument().\n\n  {\n    SCOPED_TRACE(\"simple\");\n\n    static constexpr const wchar_t* kArguments[] = {\n        L\"child.exe\",\n        L\"argument 1\",\n        L\"argument 2\",\n    };\n    AppendCommandLineArgumentTest(std::size(kArguments), kArguments);\n  }\n\n  {\n    SCOPED_TRACE(\"path with spaces\");\n\n    static constexpr const wchar_t* kArguments[] = {\n        L\"child.exe\",\n        L\"argument1\",\n        L\"argument 2\",\n        L\"\\\\some\\\\path with\\\\spaces\",\n    };\n    AppendCommandLineArgumentTest(std::size(kArguments), kArguments);\n  }\n\n  {\n    SCOPED_TRACE(\"argument with embedded quotation marks\");\n\n    static constexpr const wchar_t* kArguments[] = {\n        L\"child.exe\",\n        L\"argument1\",\n        L\"she said, \\\"you had me at hello\\\"\",\n        L\"\\\\some\\\\path with\\\\spaces\",\n    };\n    AppendCommandLineArgumentTest(std::size(kArguments), kArguments);\n  }\n\n  {\n    SCOPED_TRACE(\"argument with unbalanced quotation marks\");\n\n    static constexpr const wchar_t* kArguments[] = {\n        L\"child.exe\",\n        L\"argument1\",\n        L\"argument\\\"2\",\n        L\"argument3\",\n        L\"argument4\",\n    };\n    AppendCommandLineArgumentTest(std::size(kArguments), kArguments);\n  }\n\n  {\n    SCOPED_TRACE(\"argument ending with backslash\");\n\n    static constexpr const wchar_t* kArguments[] = {\n        L\"child.exe\",\n        L\"\\\\some\\\\directory with\\\\spaces\\\\\",\n        L\"argument2\",\n    };\n    AppendCommandLineArgumentTest(std::size(kArguments), kArguments);\n  }\n\n  {\n    SCOPED_TRACE(\"empty argument\");\n\n    static constexpr const wchar_t* kArguments[] = {\n        L\"child.exe\",\n        L\"\",\n        L\"argument2\",\n    };\n    AppendCommandLineArgumentTest(std::size(kArguments), kArguments);\n  }\n\n  {\n    SCOPED_TRACE(\"funny nonprintable characters\");\n\n    static constexpr const wchar_t* kArguments[] = {\n        L\"child.exe\",\n        L\"argument 1\",\n        L\"argument\\t2\",\n        L\"argument\\n3\",\n        L\"argument\\v4\",\n        L\"argument\\\"5\",\n        L\" \",\n        L\"\\t\",\n        L\"\\n\",\n        L\"\\v\",\n        L\"\\\"\",\n        L\" x\",\n        L\"\\tx\",\n        L\"\\nx\",\n        L\"\\vx\",\n        L\"\\\"x\",\n        L\"x \",\n        L\"x\\t\",\n        L\"x\\n\",\n        L\"x\\v\",\n        L\"x\\\"\",\n        L\" \",\n        L\"\\t\\t\",\n        L\"\\n\\n\",\n        L\"\\v\\v\",\n        L\"\\\"\\\"\",\n        L\" \\t\\n\\v\\\"\",\n    };\n    AppendCommandLineArgumentTest(std::size(kArguments), kArguments);\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/context_wrappers.h",
    "content": "// Copyright 2018 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_\n#define CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_\n\n#include <windows.h>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n//! \\brief Retrieve program counter from `CONTEXT` structure for different\n//!     architectures supported by Windows.\ninline void* ProgramCounterFromCONTEXT(const CONTEXT* context) {\n#if defined(ARCH_CPU_X86)\n  return reinterpret_cast<void*>(context->Eip);\n#elif defined(ARCH_CPU_X86_64)\n  return reinterpret_cast<void*>(context->Rip);\n#elif defined(ARCH_CPU_ARM64)\n  return reinterpret_cast<void*>(context->Pc);\n#else\n#error Unsupported Windows Arch\n#endif  // ARCH_CPU_X86\n}\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_CONTEXT_WRAPPERS_H_\n"
  },
  {
    "path": "util/win/critical_section_with_debug_info.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/critical_section_with_debug_info.h\"\n\n#include \"base/logging.h\"\n#include \"util/win/get_function.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nbool CrashpadInitializeCriticalSectionEx(\n    CRITICAL_SECTION* critical_section,\n    DWORD spin_count,\n    DWORD flags) {\n  static const auto initialize_critical_section_ex =\n      GET_FUNCTION_REQUIRED(L\"kernel32.dll\", ::InitializeCriticalSectionEx);\n  BOOL ret =\n      initialize_critical_section_ex(critical_section, spin_count, flags);\n  if (!ret) {\n    PLOG(ERROR) << \"InitializeCriticalSectionEx\";\n    return false;\n  }\n  return true;\n}\n\n}  // namespace\n\nbool InitializeCriticalSectionWithDebugInfoIfPossible(\n    CRITICAL_SECTION* critical_section) {\n  // On XP and Vista, a plain initialization causes the CRITICAL_SECTION to be\n  // allocated with .DebugInfo. On 8 and above, we can pass an additional flag\n  // to InitializeCriticalSectionEx() to force the .DebugInfo on. Before Win 8,\n  // that flag causes InitializeCriticalSectionEx() to fail. So, for XP, Vista,\n  // and 7 we use InitializeCriticalSection(), and for 8 and above,\n  // InitializeCriticalSectionEx() with the additional flag.\n  //\n  // TODO(scottmg): Try to find a solution for Win 7. It's unclear how to force\n  // it on for Win 7, however the Loader Lock does have .DebugInfo so there may\n  // be a way to do it. The comments in winnt.h imply that perhaps it's passed\n  // to InitializeCriticalSectionAndSpinCount() as the top bits of the spin\n  // count, but that doesn't appear to work. For now, we initialize a valid\n  // CRITICAL_SECTION, but without .DebugInfo.\n\n  const DWORD version = GetVersion();\n  const DWORD major_version = LOBYTE(LOWORD(version));\n  const DWORD minor_version = HIBYTE(LOWORD(version));\n  const bool win7_or_lower =\n      major_version < 6 || (major_version == 6 && minor_version <= 1);\n\n  if (win7_or_lower) {\n    InitializeCriticalSection(critical_section);\n    return true;\n  }\n\n  return CrashpadInitializeCriticalSectionEx(\n      critical_section, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/critical_section_with_debug_info.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_\n#define CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_\n\n#include <windows.h>\n\nnamespace crashpad {\n\n//! \\brief Equivalent to `InitializeCritialSection()`, but attempts to allocate\n//!     with a valid `.DebugInfo` field on versions of Windows where it's\n//!     possible to do so.\n//!\n//! \\return `true` on success, or `false` on failure with a message logged.\n//!     Success means that the critical section was successfully initialized,\n//!     but it does not necessarily have a valid `.DebugInfo` field.\nbool InitializeCriticalSectionWithDebugInfoIfPossible(\n    CRITICAL_SECTION* critical_section);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_CRITICAL_SECTION_WITH_DEBUG_INFO_H_\n"
  },
  {
    "path": "util/win/critical_section_with_debug_info_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/critical_section_with_debug_info.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(CriticalSectionWithDebugInfo, CriticalSectionWithDebugInfo) {\n  CRITICAL_SECTION critical_section;\n  ASSERT_TRUE(\n      InitializeCriticalSectionWithDebugInfoIfPossible(&critical_section));\n  EnterCriticalSection(&critical_section);\n  LeaveCriticalSection(&critical_section);\n  DeleteCriticalSection(&critical_section);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/exception_codes.h",
    "content": "// Copyright 2022 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_EXCEPTION_CODES_H_\n#define CRASHPAD_UTIL_WIN_EXCEPTION_CODES_H_\n\n#include <stdint.h>\n\nnamespace crashpad {\n\n//! \\brief Crashpad-specific exception codes that are used as arguments to\n//!     `RaiseException()` in unusual circumstances.\nenum ExceptionCodes : uint32_t {\n  //! \\brief The exception code (roughly \"Client called\") used when\n  //!     DumpAndCrashTargetProcess() triggers an exception in a target\n  //!     process.\n  //!\n  //! \\note This value does not have any bits of the top nibble set, to avoid\n  //!     confusion with real exception codes which tend to have those bits\n  //!     set.\n  kTriggeredExceptionCode = 0xcca11ed,\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_EXCEPTION_CODES_H_\n"
  },
  {
    "path": "util/win/exception_handler_server.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/exception_handler_server.h\"\n\n#include <stdint.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <iterator>\n#include <utility>\n\n#include \"base/check.h\"\n#include \"base/containers/heap_array.h\"\n#include \"base/logging.h\"\n#include \"base/numerics/safe_conversions.h\"\n#include \"base/rand_util.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"util/file/file_writer.h\"\n#include \"util/misc/tri_state.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/win/get_function.h\"\n#include \"util/win/handle.h\"\n#include \"util/win/registration_protocol_win.h\"\n#include \"util/win/safe_terminate_process.h\"\n#include \"util/win/xp_compat.h\"\n\nnamespace crashpad {\n\nnamespace {\n\ndecltype(GetNamedPipeClientProcessId)* GetNamedPipeClientProcessIdFunction() {\n  static const auto get_named_pipe_client_process_id =\n      GET_FUNCTION(L\"kernel32.dll\", ::GetNamedPipeClientProcessId);\n  return get_named_pipe_client_process_id;\n}\n\nHANDLE DuplicateEvent(HANDLE process, HANDLE event) {\n  HANDLE handle;\n  if (DuplicateHandle(GetCurrentProcess(),\n                      event,\n                      process,\n                      &handle,\n                      SYNCHRONIZE | EVENT_MODIFY_STATE,\n                      false,\n                      0)) {\n    return handle;\n  }\n  return nullptr;\n}\n\n}  // namespace\n\nnamespace internal {\n\n//! \\brief Context information for the named pipe handler threads.\nclass PipeServiceContext {\n public:\n  PipeServiceContext(HANDLE port,\n                     HANDLE pipe,\n                     ExceptionHandlerServer::Delegate* delegate,\n                     base::Lock* clients_lock,\n                     std::set<internal::ClientData*>* clients,\n                     uint64_t shutdown_token)\n      : port_(port),\n        pipe_(pipe),\n        delegate_(delegate),\n        clients_lock_(clients_lock),\n        clients_(clients),\n        shutdown_token_(shutdown_token) {}\n\n  PipeServiceContext(const PipeServiceContext&) = delete;\n  PipeServiceContext& operator=(const PipeServiceContext&) = delete;\n\n  HANDLE port() const { return port_; }\n  HANDLE pipe() const { return pipe_.get(); }\n  ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }\n  base::Lock* clients_lock() const { return clients_lock_; }\n  std::set<internal::ClientData*>* clients() const { return clients_; }\n  uint64_t shutdown_token() const { return shutdown_token_; }\n\n private:\n  HANDLE port_;  // weak\n  ScopedKernelHANDLE pipe_;\n  ExceptionHandlerServer::Delegate* delegate_;  // weak\n  base::Lock* clients_lock_;  // weak\n  std::set<internal::ClientData*>* clients_;  // weak\n  uint64_t shutdown_token_;\n};\n\n//! \\brief The context data for registered threadpool waits.\n//!\n//! This object must be created and destroyed on the main thread. Access must be\n//! guarded by use of the lock() with the exception of the threadpool wait\n//! variables which are accessed only by the main thread.\nclass ClientData {\n public:\n  ClientData(HANDLE port,\n             ExceptionHandlerServer::Delegate* delegate,\n             ScopedKernelHANDLE process,\n             ScopedKernelHANDLE crash_dump_requested_event,\n             ScopedKernelHANDLE non_crash_dump_requested_event,\n             ScopedKernelHANDLE non_crash_dump_completed_event,\n             WinVMAddress crash_exception_information_address,\n             WinVMAddress non_crash_exception_information_address,\n             WinVMAddress debug_critical_section_address,\n             WAITORTIMERCALLBACK crash_dump_request_callback,\n             WAITORTIMERCALLBACK non_crash_dump_request_callback,\n             WAITORTIMERCALLBACK process_end_callback)\n      : crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),\n        non_crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),\n        process_end_thread_pool_wait_(INVALID_HANDLE_VALUE),\n        lock_(),\n        port_(port),\n        delegate_(delegate),\n        crash_dump_requested_event_(std::move(crash_dump_requested_event)),\n        non_crash_dump_requested_event_(\n            std::move(non_crash_dump_requested_event)),\n        non_crash_dump_completed_event_(\n            std::move(non_crash_dump_completed_event)),\n        process_(std::move(process)),\n        crash_exception_information_address_(\n            crash_exception_information_address),\n        non_crash_exception_information_address_(\n            non_crash_exception_information_address),\n        debug_critical_section_address_(debug_critical_section_address) {\n    RegisterThreadPoolWaits(crash_dump_request_callback,\n                            non_crash_dump_request_callback,\n                            process_end_callback);\n  }\n\n  ClientData(const ClientData&) = delete;\n  ClientData& operator=(const ClientData&) = delete;\n\n  ~ClientData() {\n    // It is important that this only access the threadpool waits (it's called\n    // from the main thread) until the waits are unregistered, to ensure that\n    // any outstanding callbacks are complete.\n    UnregisterThreadPoolWaits();\n  }\n\n  base::Lock* lock() { return &lock_; }\n  HANDLE port() const { return port_; }\n  ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }\n  HANDLE crash_dump_requested_event() const {\n    return crash_dump_requested_event_.get();\n  }\n  HANDLE non_crash_dump_requested_event() const {\n    return non_crash_dump_requested_event_.get();\n  }\n  HANDLE non_crash_dump_completed_event() const {\n    return non_crash_dump_completed_event_.get();\n  }\n  WinVMAddress crash_exception_information_address() const {\n    return crash_exception_information_address_;\n  }\n  WinVMAddress non_crash_exception_information_address() const {\n    return non_crash_exception_information_address_;\n  }\n  WinVMAddress debug_critical_section_address() const {\n    return debug_critical_section_address_;\n  }\n  HANDLE process() const { return process_.get(); }\n\n private:\n  void RegisterThreadPoolWaits(\n      WAITORTIMERCALLBACK crash_dump_request_callback,\n      WAITORTIMERCALLBACK non_crash_dump_request_callback,\n      WAITORTIMERCALLBACK process_end_callback) {\n    if (!RegisterWaitForSingleObject(&crash_dump_request_thread_pool_wait_,\n                                     crash_dump_requested_event_.get(),\n                                     crash_dump_request_callback,\n                                     this,\n                                     INFINITE,\n                                     WT_EXECUTEDEFAULT)) {\n      LOG(ERROR) << \"RegisterWaitForSingleObject crash dump requested\";\n    }\n\n    if (!RegisterWaitForSingleObject(&non_crash_dump_request_thread_pool_wait_,\n                                     non_crash_dump_requested_event_.get(),\n                                     non_crash_dump_request_callback,\n                                     this,\n                                     INFINITE,\n                                     WT_EXECUTEDEFAULT)) {\n      LOG(ERROR) << \"RegisterWaitForSingleObject non-crash dump requested\";\n    }\n\n    if (!RegisterWaitForSingleObject(&process_end_thread_pool_wait_,\n                                     process_.get(),\n                                     process_end_callback,\n                                     this,\n                                     INFINITE,\n                                     WT_EXECUTEONLYONCE)) {\n      LOG(ERROR) << \"RegisterWaitForSingleObject process end\";\n    }\n  }\n\n  // This blocks until outstanding calls complete so that we know it's safe to\n  // delete this object. Because of this, it must be executed on the main\n  // thread, not a threadpool thread.\n  void UnregisterThreadPoolWaits() {\n    UnregisterWaitEx(crash_dump_request_thread_pool_wait_,\n                     INVALID_HANDLE_VALUE);\n    crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;\n    UnregisterWaitEx(non_crash_dump_request_thread_pool_wait_,\n                     INVALID_HANDLE_VALUE);\n    non_crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;\n    UnregisterWaitEx(process_end_thread_pool_wait_, INVALID_HANDLE_VALUE);\n    process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE;\n  }\n\n  // These are only accessed on the main thread.\n  HANDLE crash_dump_request_thread_pool_wait_;\n  HANDLE non_crash_dump_request_thread_pool_wait_;\n  HANDLE process_end_thread_pool_wait_;\n\n  base::Lock lock_;\n  // Access to these fields must be guarded by lock_.\n  HANDLE port_;  // weak\n  ExceptionHandlerServer::Delegate* delegate_;  // weak\n  ScopedKernelHANDLE crash_dump_requested_event_;\n  ScopedKernelHANDLE non_crash_dump_requested_event_;\n  ScopedKernelHANDLE non_crash_dump_completed_event_;\n  ScopedKernelHANDLE process_;\n  WinVMAddress crash_exception_information_address_;\n  WinVMAddress non_crash_exception_information_address_;\n  WinVMAddress debug_critical_section_address_;\n};\n\n}  // namespace internal\n\nExceptionHandlerServer::Delegate::~Delegate() {\n}\n\nExceptionHandlerServer::ExceptionHandlerServer(bool persistent)\n    : pipe_name_(),\n      port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),\n      first_pipe_instance_(),\n      clients_lock_(),\n      clients_(),\n      persistent_(persistent) {\n}\n\nExceptionHandlerServer::~ExceptionHandlerServer() {\n}\n\nvoid ExceptionHandlerServer::SetPipeName(const std::wstring& pipe_name) {\n  DCHECK(pipe_name_.empty());\n  DCHECK(!pipe_name.empty());\n\n  pipe_name_ = pipe_name;\n}\n\nvoid ExceptionHandlerServer::InitializeWithInheritedDataForInitialClient(\n    const InitialClientData& initial_client_data,\n    Delegate* delegate) {\n  DCHECK(pipe_name_.empty());\n  DCHECK(!first_pipe_instance_.is_valid());\n\n  first_pipe_instance_.reset(initial_client_data.first_pipe_instance());\n\n  // TODO(scottmg): Vista+. Might need to pass through or possibly find an Nt*.\n  auto data = base::HeapArray<uint8_t>::Uninit(sizeof(wchar_t) * _MAX_PATH +\n                                               sizeof(FILE_NAME_INFO));\n  if (!GetFileInformationByHandleEx(first_pipe_instance_.get(),\n                                    FileNameInfo,\n                                    data.data(),\n                                    static_cast<DWORD>(data.size()))) {\n    PLOG(FATAL) << \"GetFileInformationByHandleEx\";\n  }\n  FILE_NAME_INFO* file_name_info =\n      reinterpret_cast<FILE_NAME_INFO*>(data.data());\n  pipe_name_ =\n      L\"\\\\\\\\.\\\\pipe\" + std::wstring(file_name_info->FileName,\n                                    file_name_info->FileNameLength /\n                                        sizeof(file_name_info->FileName[0]));\n\n  {\n    base::AutoLock lock(clients_lock_);\n    internal::ClientData* client = new internal::ClientData(\n        port_.get(),\n        delegate,\n        ScopedKernelHANDLE(initial_client_data.client_process()),\n        ScopedKernelHANDLE(initial_client_data.request_crash_dump()),\n        ScopedKernelHANDLE(initial_client_data.request_non_crash_dump()),\n        ScopedKernelHANDLE(initial_client_data.non_crash_dump_completed()),\n        initial_client_data.crash_exception_information(),\n        initial_client_data.non_crash_exception_information(),\n        initial_client_data.debug_critical_section_address(),\n        &OnCrashDumpEvent,\n        &OnNonCrashDumpEvent,\n        &OnProcessEnd);\n    clients_.insert(client);\n  }\n}\n\nvoid ExceptionHandlerServer::Run(Delegate* delegate) {\n  uint64_t shutdown_token = base::RandUint64();\n  ScopedKernelHANDLE thread_handles[kPipeInstances];\n  for (size_t i = 0; i < std::size(thread_handles); ++i) {\n    HANDLE pipe;\n    if (first_pipe_instance_.is_valid()) {\n      pipe = first_pipe_instance_.release();\n    } else {\n      pipe = CreateNamedPipeInstance(pipe_name_, i == 0);\n      PCHECK(pipe != INVALID_HANDLE_VALUE) << \"CreateNamedPipe\";\n    }\n\n    // Ownership of this object (and the pipe instance) is given to the new\n    // thread. We close the thread handles at the end of the scope. They clean\n    // up the context object and the pipe instance on termination.\n    internal::PipeServiceContext* context =\n        new internal::PipeServiceContext(port_.get(),\n                                         pipe,\n                                         delegate,\n                                         &clients_lock_,\n                                         &clients_,\n                                         shutdown_token);\n    thread_handles[i].reset(\n        CreateThread(nullptr, 0, &PipeServiceProc, context, 0, nullptr));\n    PCHECK(thread_handles[i].is_valid()) << \"CreateThread\";\n  }\n\n  delegate->ExceptionHandlerServerStarted();\n\n  // This is the main loop of the server. Most work is done on the threadpool,\n  // other than process end handling which is posted back to this main thread,\n  // as we must unregister the threadpool waits here.\n  for (;;) {\n    OVERLAPPED* ov = nullptr;\n    ULONG_PTR key = 0;\n    DWORD bytes = 0;\n    GetQueuedCompletionStatus(port_.get(), &bytes, &key, &ov, INFINITE);\n    if (!key) {\n      // Shutting down.\n      break;\n    }\n\n    // Otherwise, this is a request to unregister and destroy the given client.\n    // delete'ing the ClientData blocks in UnregisterWaitEx to ensure all\n    // outstanding threadpool waits are complete. This is important because the\n    // process handle can be signalled *before* the dump request is signalled.\n    internal::ClientData* client = reinterpret_cast<internal::ClientData*>(key);\n    base::AutoLock lock(clients_lock_);\n    clients_.erase(client);\n    delete client;\n    if (!persistent_ && clients_.empty())\n      break;\n  }\n\n  // Signal to the named pipe instances that they should terminate.\n  for (size_t i = 0; i < std::size(thread_handles); ++i) {\n    ClientToServerMessage message;\n    memset(&message, 0, sizeof(message));\n    message.type = ClientToServerMessage::kShutdown;\n    message.shutdown.token = shutdown_token;\n    ServerToClientMessage response;\n    SendToCrashHandlerServer(pipe_name_,\n                             reinterpret_cast<ClientToServerMessage&>(message),\n                             &response);\n  }\n\n  for (auto& handle : thread_handles)\n    WaitForSingleObject(handle.get(), INFINITE);\n\n  // Deleting ClientData does a blocking wait until the threadpool executions\n  // have terminated when unregistering them.\n  {\n    base::AutoLock lock(clients_lock_);\n    for (auto* client : clients_)\n      delete client;\n    clients_.clear();\n  }\n}\n\nvoid ExceptionHandlerServer::Stop() {\n  // Post a null key (third argument) to trigger shutdown.\n  PostQueuedCompletionStatus(port_.get(), 0, 0, nullptr);\n}\n\n// This function must be called with service_context.pipe() already connected to\n// a client pipe. It exchanges data with the client and adds a ClientData record\n// to service_context->clients().\n//\n// static\nbool ExceptionHandlerServer::ServiceClientConnection(\n    const internal::PipeServiceContext& service_context) {\n  ClientToServerMessage message;\n\n  if (!LoggingReadFileExactly(\n          service_context.pipe(), &message, sizeof(message)))\n    return false;\n\n  switch (message.type) {\n    case ClientToServerMessage::kShutdown: {\n      if (message.shutdown.token != service_context.shutdown_token()) {\n        LOG(ERROR) << \"forged shutdown request, got: \"\n                   << message.shutdown.token;\n        return false;\n      }\n      ServerToClientMessage shutdown_response = {};\n      LoggingWriteFile(service_context.pipe(),\n                       &shutdown_response,\n                       sizeof(shutdown_response));\n      return true;\n    }\n\n    case ClientToServerMessage::kPing: {\n      // No action required, the fact that the message was processed is\n      // sufficient.\n      ServerToClientMessage shutdown_response = {};\n      LoggingWriteFile(service_context.pipe(),\n                       &shutdown_response,\n                       sizeof(shutdown_response));\n      return false;\n    }\n\n    case ClientToServerMessage::kRegister:\n      // Handled below.\n      break;\n\n    default:\n      LOG(ERROR) << \"unhandled message type: \" << message.type;\n      return false;\n  }\n\n  if (message.registration.version != RegistrationRequest::kMessageVersion) {\n    LOG(ERROR) << \"unexpected version. got: \" << message.registration.version\n               << \" expecting: \" << RegistrationRequest::kMessageVersion;\n    return false;\n  }\n\n  decltype(GetNamedPipeClientProcessId)* get_named_pipe_client_process_id =\n      GetNamedPipeClientProcessIdFunction();\n  if (get_named_pipe_client_process_id) {\n    // GetNamedPipeClientProcessId is only available on Vista+.\n    DWORD real_pid = 0;\n    if (get_named_pipe_client_process_id(service_context.pipe(), &real_pid) &&\n        message.registration.client_process_id != real_pid) {\n      LOG(ERROR) << \"forged client pid, real pid: \" << real_pid\n                 << \", got: \" << message.registration.client_process_id;\n      return false;\n    }\n  }\n\n  // We attempt to open the process as us. This is the main case that should\n  // almost always succeed as the server will generally be more privileged. If\n  // we're running as a different user, it may be that we will fail to open\n  // the process, but the client will be able to, so we make a second attempt\n  // having impersonated the client.\n  HANDLE client_process = OpenProcess(\n      kXPProcessAllAccess, false, message.registration.client_process_id);\n  if (!client_process) {\n    if (!ImpersonateNamedPipeClient(service_context.pipe())) {\n      PLOG(ERROR) << \"ImpersonateNamedPipeClient\";\n      return false;\n    }\n    client_process = OpenProcess(\n        kXPProcessAllAccess, false, message.registration.client_process_id);\n    PCHECK(RevertToSelf());\n    if (!client_process) {\n      LOG(ERROR) << \"failed to open \" << message.registration.client_process_id;\n      return false;\n    }\n  }\n\n  internal::ClientData* client;\n  {\n    base::AutoLock lock(*service_context.clients_lock());\n    client = new internal::ClientData(\n        service_context.port(),\n        service_context.delegate(),\n        ScopedKernelHANDLE(client_process),\n        ScopedKernelHANDLE(\n            CreateEvent(nullptr, false /* auto reset */, false, nullptr)),\n        ScopedKernelHANDLE(\n            CreateEvent(nullptr, false /* auto reset */, false, nullptr)),\n        ScopedKernelHANDLE(\n            CreateEvent(nullptr, false /* auto reset */, false, nullptr)),\n        message.registration.crash_exception_information,\n        message.registration.non_crash_exception_information,\n        message.registration.critical_section_address,\n        &OnCrashDumpEvent,\n        &OnNonCrashDumpEvent,\n        &OnProcessEnd);\n    service_context.clients()->insert(client);\n  }\n\n  // Duplicate the events back to the client so they can request a dump.\n  ServerToClientMessage response;\n  response.registration.request_crash_dump_event =\n      HandleToInt(DuplicateEvent(\n          client->process(), client->crash_dump_requested_event()));\n  response.registration.request_non_crash_dump_event =\n      HandleToInt(DuplicateEvent(\n          client->process(), client->non_crash_dump_requested_event()));\n  response.registration.non_crash_dump_completed_event =\n      HandleToInt(DuplicateEvent(\n          client->process(), client->non_crash_dump_completed_event()));\n\n  if (!LoggingWriteFile(service_context.pipe(), &response, sizeof(response)))\n    return false;\n\n  return false;\n}\n\n// static\nDWORD __stdcall ExceptionHandlerServer::PipeServiceProc(void* ctx) {\n  internal::PipeServiceContext* service_context =\n      reinterpret_cast<internal::PipeServiceContext*>(ctx);\n  DCHECK(service_context);\n\n  for (;;) {\n    bool ret = !!ConnectNamedPipe(service_context->pipe(), nullptr);\n    if (!ret && GetLastError() != ERROR_PIPE_CONNECTED) {\n      PLOG(ERROR) << \"ConnectNamedPipe\";\n    } else if (ServiceClientConnection(*service_context)) {\n      break;\n    }\n    DisconnectNamedPipe(service_context->pipe());\n  }\n\n  delete service_context;\n\n  return 0;\n}\n\n// static\nvoid __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) {\n  // This function is executed on the thread pool.\n  internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);\n  base::AutoLock lock(*client->lock());\n\n  // Capture the exception.\n  unsigned int exit_code = client->delegate()->ExceptionHandlerServerException(\n      client->process(),\n      client->crash_exception_information_address(),\n      client->debug_critical_section_address());\n\n  SafeTerminateProcess(client->process(), exit_code);\n}\n\n// static\nvoid __stdcall ExceptionHandlerServer::OnNonCrashDumpEvent(void* ctx, BOOLEAN) {\n  // This function is executed on the thread pool.\n  internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);\n  base::AutoLock lock(*client->lock());\n\n  // Capture the exception.\n  client->delegate()->ExceptionHandlerServerException(\n      client->process(),\n      client->non_crash_exception_information_address(),\n      client->debug_critical_section_address());\n\n  bool result = !!SetEvent(client->non_crash_dump_completed_event());\n  PLOG_IF(ERROR, !result) << \"SetEvent\";\n}\n\n// static\nvoid __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) {\n  // This function is executed on the thread pool.\n  internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);\n  base::AutoLock lock(*client->lock());\n\n  // Post back to the main thread to have it delete this client record.\n  PostQueuedCompletionStatus(client->port(), 0, ULONG_PTR(client), nullptr);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/exception_handler_server.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_\n#define CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_\n\n#include <set>\n#include <string>\n\n#include \"base/synchronization/lock.h\"\n#include \"util/file/file_io.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/initial_client_data.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace crashpad {\n\nnamespace internal {\nclass PipeServiceContext;\nclass ClientData;\n}  // namespace internal\n\n//! \\brief Runs the main exception-handling server in Crashpad's handler\n//!     process.\nclass ExceptionHandlerServer {\n public:\n  class Delegate {\n   public:\n    //! \\brief Called when the server has created the named pipe connection\n    //!     points and is ready to service requests.\n    virtual void ExceptionHandlerServerStarted() = 0;\n\n    //! \\brief Called when the client has signalled that it has encountered an\n    //!     exception and so wants a crash dump to be taken.\n    //!\n    //! \\param[in] process A handle to the client process. Ownership of the\n    //!     lifetime of this handle is not passed to the delegate.\n    //! \\param[in] exception_information_address The address in the client's\n    //!     address space of an ExceptionInformation structure.\n    //! \\param[in] debug_critical_section_address The address in the client's\n    //!     address space of a `CRITICAL_SECTION` allocated with a valid\n    //!     `.DebugInfo` field, or `0` if unavailable.\n    //! \\return The exit code that should be used when terminating the client\n    //!     process.\n    virtual unsigned int ExceptionHandlerServerException(\n        HANDLE process,\n        WinVMAddress exception_information_address,\n        WinVMAddress debug_critical_section_address) = 0;\n\n   protected:\n    ~Delegate();\n  };\n\n  //! \\brief Constructs the exception handling server.\n  //!\n  //! \\param[in] persistent `true` if Run() should not return until Stop() is\n  //!     called. If `false`, Run() will return when all clients have exited,\n  //!     although Run() will always wait for the first client to connect.\n  explicit ExceptionHandlerServer(bool persistent);\n\n  ExceptionHandlerServer(const ExceptionHandlerServer&) = delete;\n  ExceptionHandlerServer& operator=(const ExceptionHandlerServer&) = delete;\n\n  ~ExceptionHandlerServer();\n\n  //! \\brief Sets the pipe name to listen for client registrations on.\n  //!\n  //! This method, or InitializeWithInheritedDataForInitialClient(), must be\n  //! called before Run().\n  //!\n  //! \\param[in] pipe_name The name of the pipe to listen on. Must be of the\n  //!     form \"\\\\.\\pipe\\<some_name>\".\n  void SetPipeName(const std::wstring& pipe_name);\n\n  //! \\brief Sets the pipe to listen for client registrations on, providing\n  //!     the first precreated instance.\n  //!\n  //! This method, or SetPipeName(), must be called before Run(). All of these\n  //! parameters are generally created in a parent process that launches the\n  //! handler. For more details see the Windows implementation of\n  //! CrashpadClient.\n  //!\n  //! \\sa CrashpadClient\n  //! \\sa RegistrationRequest\n  //!\n  //! \\param[in] initial_client_data The handles and addresses of data inherited\n  //!     from a parent process needed to initialize and register the first\n  //!     client. Ownership of these handles is taken.\n  //! \\param[in] delegate The interface to which the exceptions are delegated\n  //!     when they are caught in Run(). Ownership is not transferred.\n  void InitializeWithInheritedDataForInitialClient(\n      const InitialClientData& initial_client_data,\n      Delegate* delegate);\n\n  //! \\brief Runs the exception-handling server.\n  //!\n  //! \\param[in] delegate The interface to which the exceptions are delegated\n  //!     when they are caught in Run(). Ownership is not transferred.\n  void Run(Delegate* delegate);\n\n  //! \\brief Stops the exception-handling server. Returns immediately. The\n  //!     object must not be destroyed until Run() returns.\n  void Stop();\n\n  //! \\brief The number of server-side pipe instances that the exception handler\n  //!     server creates to listen for connections from clients.\n  static const size_t kPipeInstances = 2;\n\n private:\n  static bool ServiceClientConnection(\n      const internal::PipeServiceContext& service_context);\n  static DWORD __stdcall PipeServiceProc(void* ctx);\n  static void __stdcall OnCrashDumpEvent(void* ctx, BOOLEAN);\n  static void __stdcall OnNonCrashDumpEvent(void* ctx, BOOLEAN);\n  static void __stdcall OnProcessEnd(void* ctx, BOOLEAN);\n\n  std::wstring pipe_name_;\n  ScopedKernelHANDLE port_;\n  ScopedFileHandle first_pipe_instance_;\n\n  base::Lock clients_lock_;\n  std::set<internal::ClientData*> clients_;\n\n  bool persistent_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_EXCEPTION_HANDLER_SERVER_H_\n"
  },
  {
    "path": "util/win/exception_handler_server_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/exception_handler_server.h\"\n\n#include <windows.h>\n#include <sys/types.h>\n\n#include <string>\n#include <vector>\n\n#include \"base/strings/utf_string_conversions.h\"\n#include \"client/crashpad_client.h\"\n#include \"gtest/gtest.h\"\n#include \"test/win/win_child_process.h\"\n#include \"util/thread/thread.h\"\n#include \"util/win/address_types.h\"\n#include \"util/win/registration_protocol_win.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Runs the ExceptionHandlerServer on a background thread.\nclass RunServerThread : public Thread {\n public:\n  // Instantiates a thread which will invoke server->Run(delegate).\n  RunServerThread(ExceptionHandlerServer* server,\n                  ExceptionHandlerServer::Delegate* delegate)\n      : server_(server), delegate_(delegate) {}\n\n  RunServerThread(const RunServerThread&) = delete;\n  RunServerThread& operator=(const RunServerThread&) = delete;\n\n  ~RunServerThread() override {}\n\n private:\n  // Thread:\n  void ThreadMain() override { server_->Run(delegate_); }\n\n  ExceptionHandlerServer* server_;\n  ExceptionHandlerServer::Delegate* delegate_;\n};\n\nclass TestDelegate : public ExceptionHandlerServer::Delegate {\n public:\n  explicit TestDelegate(HANDLE server_ready) : server_ready_(server_ready) {}\n\n  TestDelegate(const TestDelegate&) = delete;\n  TestDelegate& operator=(const TestDelegate&) = delete;\n\n  ~TestDelegate() {}\n\n  void ExceptionHandlerServerStarted() override {\n    SetEvent(server_ready_);\n  }\n  unsigned int ExceptionHandlerServerException(\n      HANDLE process,\n      WinVMAddress exception_information_address,\n      WinVMAddress debug_critical_section_address) override {\n    return 0;\n  }\n\n  void WaitForStart() { WaitForSingleObject(server_ready_, INFINITE); }\n\n private:\n  HANDLE server_ready_;  // weak\n};\n\nclass ExceptionHandlerServerTest : public testing::Test {\n public:\n  ExceptionHandlerServerTest()\n      : server_(true),\n        pipe_name_(L\"\\\\\\\\.\\\\pipe\\\\test_name\"),\n        server_ready_(CreateEvent(nullptr, false, false, nullptr)),\n        delegate_(server_ready_.get()),\n        server_thread_(&server_, &delegate_) {\n    server_.SetPipeName(pipe_name_);\n  }\n\n  ExceptionHandlerServerTest(const ExceptionHandlerServerTest&) = delete;\n  ExceptionHandlerServerTest& operator=(const ExceptionHandlerServerTest&) =\n      delete;\n\n  TestDelegate& delegate() { return delegate_; }\n  ExceptionHandlerServer& server() { return server_; }\n  Thread& server_thread() { return server_thread_; }\n  const std::wstring& pipe_name() const { return pipe_name_; }\n\n private:\n  ExceptionHandlerServer server_;\n  std::wstring pipe_name_;\n  ScopedKernelHANDLE server_ready_;\n  TestDelegate delegate_;\n  RunServerThread server_thread_;\n};\n\n// During destruction, ensures that the server is stopped and the background\n// thread joined.\nclass ScopedStopServerAndJoinThread {\n public:\n  ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, Thread* thread)\n      : server_(server), thread_(thread) {}\n\n  ScopedStopServerAndJoinThread(const ScopedStopServerAndJoinThread&) = delete;\n  ScopedStopServerAndJoinThread& operator=(\n      const ScopedStopServerAndJoinThread&) = delete;\n\n  ~ScopedStopServerAndJoinThread() {\n    server_->Stop();\n    thread_->Join();\n  }\n\n private:\n  ExceptionHandlerServer* server_;\n  Thread* thread_;\n};\n\nTEST_F(ExceptionHandlerServerTest, Instantiate) {\n}\n\nTEST_F(ExceptionHandlerServerTest, StartAndStop) {\n  server_thread().Start();\n  ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(\n      &server(), &server_thread());\n  ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart());\n}\n\nTEST_F(ExceptionHandlerServerTest, StopWhileConnected) {\n  server_thread().Start();\n  ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(\n      &server(), &server_thread());\n  ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart());\n  CrashpadClient client;\n  client.SetHandlerIPCPipe(pipe_name());\n  // Leaving this scope causes the server to be stopped, while the connection\n  // is still open.\n}\n\nstd::wstring ReadWString(FileHandle handle) {\n  size_t length = 0;\n  EXPECT_TRUE(LoggingReadFileExactly(handle, &length, sizeof(length)));\n  std::wstring str(length, L'\\0');\n  if (length > 0) {\n    EXPECT_TRUE(\n        LoggingReadFileExactly(handle, &str[0], length * sizeof(str[0])));\n  }\n  return str;\n}\n\nvoid WriteWString(FileHandle handle, const std::wstring& str) {\n  size_t length = str.size();\n  EXPECT_TRUE(LoggingWriteFile(handle, &length, sizeof(length)));\n  if (length > 0) {\n    EXPECT_TRUE(LoggingWriteFile(handle, &str[0], length * sizeof(str[0])));\n  }\n}\n\nclass TestClient final : public WinChildProcess {\n public:\n  TestClient() : WinChildProcess() {}\n\n  TestClient(const TestClient&) = delete;\n  TestClient& operator=(const TestClient&) = delete;\n\n  ~TestClient() {}\n\n private:\n  int Run() override {\n    std::wstring pipe_name = ReadWString(ReadPipeHandle());\n    CrashpadClient client;\n    if (!client.SetHandlerIPCPipe(pipe_name)) {\n      ADD_FAILURE();\n      return EXIT_FAILURE;\n    }\n    WriteWString(WritePipeHandle(), L\"OK\");\n    return EXIT_SUCCESS;\n  }\n};\n\nTEST_F(ExceptionHandlerServerTest, MultipleConnections) {\n  WinChildProcess::EntryPoint<TestClient>();\n\n  std::unique_ptr<WinChildProcess::Handles> handles_1 =\n      WinChildProcess::Launch();\n  std::unique_ptr<WinChildProcess::Handles> handles_2 =\n      WinChildProcess::Launch();\n  std::unique_ptr<WinChildProcess::Handles> handles_3 =\n      WinChildProcess::Launch();\n\n  // Must ensure the delegate outlasts the server.\n  {\n    server_thread().Start();\n    ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(\n        &server(), &server_thread());\n    ASSERT_NO_FATAL_FAILURE(delegate().WaitForStart());\n\n    // Tell all the children where to connect.\n    WriteWString(handles_1->write.get(), pipe_name());\n    WriteWString(handles_2->write.get(), pipe_name());\n    WriteWString(handles_3->write.get(), pipe_name());\n\n    ASSERT_EQ(ReadWString(handles_3->read.get()), L\"OK\");\n    ASSERT_EQ(ReadWString(handles_2->read.get()), L\"OK\");\n    ASSERT_EQ(ReadWString(handles_1->read.get()), L\"OK\");\n  }\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/get_function.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/get_function.h\"\n\n#include <ostream>\n\n#include \"base/check.h\"\n#include \"base/strings/utf_string_conversions.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nFARPROC GetFunctionInternal(\n    const wchar_t* library, const char* function, bool required) {\n  HMODULE module = LoadLibrary(library);\n  DPCHECK(!required || module) << \"LoadLibrary \" << base::WideToUTF8(library);\n  if (!module) {\n    return nullptr;\n  }\n\n  // Strip off any leading :: that may have come from stringifying the\n  // function’s name.\n  if (function[0] == ':' && function[1] == ':' &&\n      function[2] && function[2] != ':') {\n    function += 2;\n  }\n\n  FARPROC proc = GetProcAddress(module, function);\n  DPCHECK(!required || proc) << \"GetProcAddress \" << function;\n  return proc;\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/get_function.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_GET_FUNCTION_H_\n#define CRASHPAD_UTIL_WIN_GET_FUNCTION_H_\n\n#include <windows.h>\n\n//! \\file\n\nnamespace crashpad {\nnamespace internal {\n\n//! \\brief Returns a function pointer to a named function in a library.\n//!\n//! Do not call this directly, use the GET_FUNCTION() or GET_FUNCTION_REQUIRED()\n//! macros instead.\n//!\n//! This accesses \\a library by calling `LoadLibrary()` and is subject to the\n//! same restrictions as that function. Notably, it can’t be used from a\n//! `DllMain()` entry point.\n//!\n//! \\param[in] library The library to search in.\n//! \\param[in] function The function to search for. If a leading `::` is\n//!     present, it will be stripped.\n//! \\param[in] required If `true`, require the function to resolve by `DCHECK`.\n//!\n//! \\return A pointer to the requested function on success. If \\a required is\n//!     `true`, triggers a `DCHECK` assertion on failure, otherwise, `nullptr`\n//!     on failure.\nFARPROC GetFunctionInternal(\n    const wchar_t* library, const char* function, bool required);\n\n//! \\copydoc GetFunctionInternal\ntemplate <typename FunctionType>\nFunctionType* GetFunction(\n    const wchar_t* library, const char* function, bool required) {\n  return reinterpret_cast<FunctionType*>(\n      internal::GetFunctionInternal(library, function, required));\n}\n\n}  // namespace internal\n}  // namespace crashpad\n\n//! \\brief Returns a function pointer to a named function in a library without\n//!     requiring that it be found.\n//!\n//! If the library or function cannot be found, this will return `nullptr`. This\n//! macro is intended to be used to access functions that may not be available\n//! at runtime.\n//!\n//! This macro returns a properly-typed function pointer. It is expected to be\n//! used in this way:\n//! \\code\n//!     static const auto get_named_pipe_client_process_id =\n//!         GET_FUNCTION(L\"kernel32.dll\", ::GetNamedPipeClientProcessId);\n//!     if (get_named_pipe_client_process_id) {\n//!       BOOL rv = get_named_pipe_client_process_id(pipe, &client_process_id);\n//!     }\n//! \\endcode\n//!\n//! This accesses \\a library by calling `LoadLibrary()` and is subject to the\n//! same restrictions as that function. Notably, it can’t be used from a\n//! `DllMain()` entry point.\n//!\n//! \\param[in] library The library to search in.\n//! \\param[in] function The function to search for. A leading `::` is\n//!     recommended when a wrapper function of the same name is present.\n//!\n//! \\return A pointer to the requested function on success, or `nullptr` on\n//!     failure.\n//!\n//! \\sa GET_FUNCTION_REQUIRED\n#define GET_FUNCTION(library, function)                  \\\n    crashpad::internal::GetFunction<decltype(function)>( \\\n        library, #function, false)\n\n//! \\brief Returns a function pointer to a named function in a library,\n//!     requiring that it be found.\n//!\n//! If the library or function cannot be found, this will trigger a `DCHECK`\n//! assertion. This macro is intended to be used to access functions that are\n//! always expected to be available at runtime but which are not present in any\n//! import library.\n//!\n//! This macro returns a properly-typed function pointer. It is expected to be\n//! used in this way:\n//! \\code\n//!     static const auto nt_query_object =\n//!         GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtQueryObject);\n//!     NTSTATUS status =\n//!         nt_query_object(handle, type, &info, info_length, &return_length);\n//! \\endcode\n//!\n//! This accesses \\a library by calling `LoadLibrary()` and is subject to the\n//! same restrictions as that function. Notably, it can’t be used from a\n//! `DllMain()` entry point.\n//!\n//! \\param[in] library The library to search in.\n//! \\param[in] function The function to search for. A leading `::` is\n//!     recommended when a wrapper function of the same name is present.\n//!\n//! \\return A pointer to the requested function.\n//!\n//! \\sa GET_FUNCTION\n#define GET_FUNCTION_REQUIRED(library, function)         \\\n    crashpad::internal::GetFunction<decltype(function)>( \\\n        library, #function, true)\n\n#endif  // CRASHPAD_UTIL_WIN_GET_FUNCTION_H_\n"
  },
  {
    "path": "util/win/get_function_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/get_function.h\"\n\n#include <windows.h>\n#include <winternl.h>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(GetFunction, GetFunction) {\n  // Check equivalence of GET_FUNCTION_REQUIRED() with functions that are\n  // available in the SDK normally.\n  EXPECT_EQ(GET_FUNCTION_REQUIRED(L\"kernel32.dll\", GetProcAddress),\n            &GetProcAddress);\n  EXPECT_EQ(GET_FUNCTION_REQUIRED(L\"kernel32.dll\", LoadLibraryW),\n            &LoadLibraryW);\n\n  // Make sure that a function pointer retrieved by GET_FUNCTION_REQUIRED() can\n  // be called and that it works correctly.\n  const auto get_current_process_id =\n      GET_FUNCTION_REQUIRED(L\"kernel32.dll\", GetCurrentProcessId);\n  EXPECT_EQ(get_current_process_id, &GetCurrentProcessId);\n  ASSERT_TRUE(get_current_process_id);\n  EXPECT_EQ(get_current_process_id(), GetCurrentProcessId());\n\n  // GET_FUNCTION_REQUIRED() and GET_FUNCTION() should behave identically when\n  // the function is present.\n  EXPECT_EQ(GET_FUNCTION(L\"kernel32.dll\", GetCurrentProcessId),\n            get_current_process_id);\n\n  // Using a leading :: should also work.\n  EXPECT_EQ(GET_FUNCTION(L\"kernel32.dll\", ::GetCurrentProcessId),\n            get_current_process_id);\n  EXPECT_EQ(GET_FUNCTION_REQUIRED(L\"kernel32.dll\", ::GetCurrentProcessId),\n            get_current_process_id);\n\n  // Try a function that’s declared in the SDK’s headers but that has no import\n  // library.\n  EXPECT_TRUE(GET_FUNCTION_REQUIRED(L\"ntdll.dll\", RtlNtStatusToDosError));\n\n  // GetNamedPipeClientProcessId() is only available on Vista and later.\n  const auto get_named_pipe_client_process_id =\n      GET_FUNCTION(L\"kernel32.dll\", GetNamedPipeClientProcessId);\n  const DWORD version = GetVersion();\n  const DWORD major_version = LOBYTE(LOWORD(version));\n  EXPECT_EQ(get_named_pipe_client_process_id != nullptr, major_version >= 6);\n\n  // Test that GET_FUNCTION() can fail by trying a nonexistent library and a\n  // symbol that doesn’t exist in the specified library.\n  EXPECT_FALSE(GET_FUNCTION(L\"not_a_real_library.dll\", TerminateProcess));\n  EXPECT_FALSE(GET_FUNCTION(L\"ntdll.dll\", TerminateProcess));\n  EXPECT_FALSE(GET_FUNCTION(L\"not_a_real_library.dll\", ::TerminateProcess));\n  EXPECT_FALSE(GET_FUNCTION(L\"ntdll.dll\", ::TerminateProcess));\n\n  // Here it is!\n  EXPECT_TRUE(GET_FUNCTION(L\"kernel32.dll\", TerminateProcess));\n  EXPECT_TRUE(GET_FUNCTION(L\"kernel32.dll\", ::TerminateProcess));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/get_module_information.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/get_module_information.h\"\n\n#include \"util/win/get_function.h\"\n\nnamespace crashpad {\n\nBOOL CrashpadGetModuleInformation(HANDLE process,\n                                  HMODULE module,\n                                  MODULEINFO* module_info,\n                                  DWORD cb) {\n#if PSAPI_VERSION == 1\n  static const auto get_module_information =\n    GET_FUNCTION_REQUIRED(L\"psapi.dll\", GetModuleInformation);\n  return get_module_information(process, module, module_info, cb);\n#elif PSAPI_VERSION == 2\n  return GetModuleInformation(process, module, module_info, cb);\n#endif\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/get_module_information.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_\n#define CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_\n\n#include <windows.h>\n\n#ifndef PSAPI_VERSION\n#define PSAPI_VERSION 2\n#endif\n#include <psapi.h>\n\nnamespace crashpad {\n\n//! \\brief Proxy function for `GetModuleInformation()`.\nBOOL CrashpadGetModuleInformation(HANDLE process,\n                                  HMODULE module,\n                                  MODULEINFO* module_info,\n                                  DWORD cb);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_GET_MODULE_INFORMATION_H_\n"
  },
  {
    "path": "util/win/handle.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/handle.h\"\n\n#include <stdint.h>\n\n#include \"base/numerics/safe_conversions.h\"\n#include \"util/misc/from_pointer_cast.h\"\n\nnamespace crashpad {\n\n// These functions use “int” for the 32-bit integer handle type because\n// sign-extension needs to work correctly. INVALID_HANDLE_VALUE is defined as\n// ((HANDLE)(LONG_PTR)-1), and this needs to round-trip through an integer and\n// back to the same HANDLE value.\n\nint HandleToInt(HANDLE handle) {\n  return FromPointerCast<int>(handle);\n}\n\nHANDLE IntToHandle(int handle_int) {\n  return reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle_int));\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/handle.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_\n#define CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_\n\n#include <windows.h>\n\nnamespace crashpad {\n\n//! \\brief Converts a `HANDLE` to an `int`.\n//!\n//! `HANDLE` is a `typedef` for `void *`, but kernel `HANDLE` values aren’t\n//! pointers to anything. Only 32 bits of kernel `HANDLE`s are significant, even\n//! in 64-bit processes on 64-bit operating systems. See <a\n//! href=\"https://msdn.microsoft.com/library/aa384203.aspx\">Interprocess\n//! Communication Between 32-bit and 64-bit Applications</a>.\n//!\n//! This function safely converts a kernel `HANDLE` to an `int` similarly to a\n//! cast operation. It checks that the operation can be performed safely, and\n//! aborts execution if it cannot.\n//!\n//! \\param[in] handle The kernel `HANDLE` to convert.\n//!\n//! \\return An equivalent `int`, truncated (if necessary) from \\a handle. If\n//!     truncation would have resulted in an `int` that could not be converted\n//!     back to \\a handle, aborts execution.\n//!\n//! \\sa IntToHandle()\nint HandleToInt(HANDLE handle);\n\n//! \\brief Converts an `int` to an `HANDLE`.\n//!\n//! `HANDLE` is a `typedef` for `void *`, but kernel `HANDLE` values aren’t\n//! pointers to anything. Only 32 bits of kernel `HANDLE`s are significant, even\n//! in 64-bit processes on 64-bit operating systems. See <a\n//! href=\"https://msdn.microsoft.com/library/aa384203.aspx\">Interprocess\n//! Communication Between 32-bit and 64-bit Applications</a>.\n//!\n//! This function safely convert an `int` to a kernel `HANDLE` similarly to a\n//! cast operation.\n//!\n//! \\param[in] handle_int The `int` to convert. This must have been produced by\n//!     HandleToInt(), possibly in a different process.\n//!\n//! \\return An equivalent kernel `HANDLE`, sign-extended (if necessary) from \\a\n//!     handle_int.\n//!\n//! \\sa HandleToInt()\nHANDLE IntToHandle(int handle_int);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_HANDLE_TO_INT_H_\n"
  },
  {
    "path": "util/win/handle_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/handle.h\"\n\n#include <stdint.h>\n\n#include <limits>\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(Handle, HandleToInt) {\n  EXPECT_EQ(HandleToInt(nullptr), 0);\n  EXPECT_EQ(HandleToInt(INVALID_HANDLE_VALUE), -1);\n  EXPECT_EQ(HandleToInt(reinterpret_cast<HANDLE>(1)), 1);\n  EXPECT_EQ(HandleToInt(reinterpret_cast<HANDLE>(\n                static_cast<intptr_t>(std::numeric_limits<int>::max()))),\n            std::numeric_limits<int>::max());\n  EXPECT_EQ(HandleToInt(reinterpret_cast<HANDLE>(\n                static_cast<intptr_t>(std::numeric_limits<int>::min()))),\n            std::numeric_limits<int>::min());\n}\n\nTEST(Handle, IntToHandle) {\n  EXPECT_EQ(IntToHandle(0), nullptr);\n  EXPECT_EQ(IntToHandle(-1), INVALID_HANDLE_VALUE);\n  EXPECT_EQ(IntToHandle(1), reinterpret_cast<HANDLE>(1));\n  EXPECT_EQ(IntToHandle(std::numeric_limits<int>::max()),\n            reinterpret_cast<HANDLE>(\n                static_cast<intptr_t>(std::numeric_limits<int>::max())));\n  EXPECT_EQ(IntToHandle(std::numeric_limits<int>::min()),\n            reinterpret_cast<HANDLE>(\n                static_cast<intptr_t>(std::numeric_limits<int>::min())));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/initial_client_data.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/initial_client_data.h\"\n\n#include <vector>\n\n#include \"base/format_macros.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"util/stdlib/string_number_conversion.h\"\n#include \"util/string/split_string.h\"\n#include \"util/win/handle.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nbool HandleFromString(const std::string& str, HANDLE* handle) {\n  unsigned int handle_uint;\n  if (!StringToNumber(str, &handle_uint) ||\n      (*handle = IntToHandle(handle_uint)) == INVALID_HANDLE_VALUE) {\n    LOG(ERROR) << \"could not convert '\" << str << \"' to HANDLE\";\n    return false;\n  }\n  return true;\n}\n\nbool AddressFromString(const std::string& str, WinVMAddress* address) {\n  if (!StringToNumber(str, address)) {\n    LOG(ERROR) << \"could not convert '\" << str << \"' to WinVMAddress\";\n    return false;\n  }\n  return true;\n}\n\n}  // namespace\n\nInitialClientData::InitialClientData()\n    : crash_exception_information_(0),\n      non_crash_exception_information_(0),\n      debug_critical_section_address_(0),\n      request_crash_dump_(nullptr),\n      request_non_crash_dump_(nullptr),\n      non_crash_dump_completed_(nullptr),\n      first_pipe_instance_(INVALID_HANDLE_VALUE),\n      client_process_(nullptr),\n      is_valid_(false) {}\n\nInitialClientData::InitialClientData(\n    HANDLE request_crash_dump,\n    HANDLE request_non_crash_dump,\n    HANDLE non_crash_dump_completed,\n    HANDLE first_pipe_instance,\n    HANDLE client_process,\n    WinVMAddress crash_exception_information,\n    WinVMAddress non_crash_exception_information,\n    WinVMAddress debug_critical_section_address)\n    : crash_exception_information_(crash_exception_information),\n      non_crash_exception_information_(non_crash_exception_information),\n      debug_critical_section_address_(debug_critical_section_address),\n      request_crash_dump_(request_crash_dump),\n      request_non_crash_dump_(request_non_crash_dump),\n      non_crash_dump_completed_(non_crash_dump_completed),\n      first_pipe_instance_(first_pipe_instance),\n      client_process_(client_process),\n      is_valid_(true) {}\n\nbool InitialClientData::InitializeFromString(const std::string& str) {\n  std::vector<std::string> parts(SplitString(str, ','));\n  if (parts.size() != 8) {\n    LOG(ERROR) << \"expected 8 comma separated arguments\";\n    return false;\n  }\n\n  if (!HandleFromString(parts[0], &request_crash_dump_) ||\n      !HandleFromString(parts[1], &request_non_crash_dump_) ||\n      !HandleFromString(parts[2], &non_crash_dump_completed_) ||\n      !HandleFromString(parts[3], &first_pipe_instance_) ||\n      !HandleFromString(parts[4], &client_process_) ||\n      !AddressFromString(parts[5], &crash_exception_information_) ||\n      !AddressFromString(parts[6], &non_crash_exception_information_) ||\n      !AddressFromString(parts[7], &debug_critical_section_address_)) {\n    return false;\n  }\n\n  is_valid_ = true;\n  return true;\n}\n\nstd::string InitialClientData::StringRepresentation() const {\n  return base::StringPrintf(\"0x%x,0x%x,0x%x,0x%x,0x%x,0x%\" PRIx64 \",0x%\" PRIx64\n                            \",0x%\" PRIx64,\n                            HandleToInt(request_crash_dump_),\n                            HandleToInt(request_non_crash_dump_),\n                            HandleToInt(non_crash_dump_completed_),\n                            HandleToInt(first_pipe_instance_),\n                            HandleToInt(client_process_),\n                            crash_exception_information_,\n                            non_crash_exception_information_,\n                            debug_critical_section_address_);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/initial_client_data.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_\n#define CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_\n\n#include <windows.h>\n\n#include <string>\n\n#include \"util/win/address_types.h\"\n\nnamespace crashpad {\n\n//! \\brief A container for the data associated with the `--initial-client-data`\n//!     method for initializing the handler process on Windows.\nclass InitialClientData {\n public:\n  //! \\brief Constructs an unintialized instance to be used with\n  //!     InitializeFromString().\n  InitialClientData();\n\n  //! \\brief Constructs an instance of InitialClientData. This object does not\n  //!     take ownership of any of the referenced HANDLEs.\n  //!\n  //! \\param[in] request_crash_dump An event signalled from the client on crash.\n  //! \\param[in] request_non_crash_dump An event signalled from the client when\n  //!     it would like a dump to be taken, but allowed to continue afterwards.\n  //! \\param[in] non_crash_dump_completed An event signalled from the handler to\n  //!     tell the client that the non-crash dump has completed, and it can\n  //!     continue execution.\n  //! \\param[in] first_pipe_instance The server end and first instance of a pipe\n  //!     that will be used for communication with all other clients after this\n  //!     initial one.\n  //! \\param[in] client_process A process handle for the client being\n  //!     registered.\n  //! \\param[in] crash_exception_information The address, in the client's\n  //!     address space, of an ExceptionInformation structure, used when\n  //!     handling a crash dump request.\n  //! \\param[in] non_crash_exception_information The address, in the client's\n  //!     address space, of an ExceptionInformation structure, used when\n  //!     handling a non-crashing dump request.\n  //! \\param[in] debug_critical_section_address The address, in the client\n  //!     process's address space, of a `CRITICAL_SECTION` allocated with a\n  //!     valid .DebugInfo field. This can be accomplished by using\n  //!     InitializeCriticalSectionWithDebugInfoIfPossible() or equivalent. This\n  //!     value can be `0`, however then limited lock data will be available in\n  //!     minidumps.\n  InitialClientData(HANDLE request_crash_dump,\n                    HANDLE request_non_crash_dump,\n                    HANDLE non_crash_dump_completed,\n                    HANDLE first_pipe_instance,\n                    HANDLE client_process,\n                    WinVMAddress crash_exception_information,\n                    WinVMAddress non_crash_exception_information,\n                    WinVMAddress debug_critical_section_address);\n\n  InitialClientData(const InitialClientData&) = delete;\n  InitialClientData& operator=(const InitialClientData&) = delete;\n\n  //! \\brief Returns whether the object has been initialized successfully.\n  bool IsValid() const { return is_valid_; }\n\n  //! Initializes this object from a string representation presumed to have been\n  //!     created by StringRepresentation().\n  //!\n  //! \\param[in] str The output of StringRepresentation().\n  //!\n  //! \\return `true` on success, or `false` with a message logged on failure.\n  bool InitializeFromString(const std::string& str);\n\n  //! \\brief Returns a string representation of the data of this object,\n  //!     suitable for passing on the command line.\n  std::string StringRepresentation() const;\n\n  HANDLE request_crash_dump() const { return request_crash_dump_; }\n  HANDLE request_non_crash_dump() const { return request_non_crash_dump_; }\n  HANDLE non_crash_dump_completed() const { return non_crash_dump_completed_; }\n  HANDLE first_pipe_instance() const { return first_pipe_instance_; }\n  HANDLE client_process() const { return client_process_; }\n  WinVMAddress crash_exception_information() const {\n    return crash_exception_information_;\n  }\n  WinVMAddress non_crash_exception_information() const {\n    return non_crash_exception_information_;\n  }\n  WinVMAddress debug_critical_section_address() const {\n    return debug_critical_section_address_;\n  }\n\n private:\n  WinVMAddress crash_exception_information_;\n  WinVMAddress non_crash_exception_information_;\n  WinVMAddress debug_critical_section_address_;\n  HANDLE request_crash_dump_;\n  HANDLE request_non_crash_dump_;\n  HANDLE non_crash_dump_completed_;\n  HANDLE first_pipe_instance_;\n  HANDLE client_process_;\n  bool is_valid_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_INITIAL_CLIENT_DATA_H_\n"
  },
  {
    "path": "util/win/initial_client_data_test.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/initial_client_data.h\"\n\n#include \"gtest/gtest.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(InitialClientData, Validity) {\n  InitialClientData icd1;\n  EXPECT_FALSE(icd1.IsValid());\n\n  InitialClientData icd2(\n      reinterpret_cast<HANDLE>(0x123),\n      reinterpret_cast<HANDLE>(0x456),\n      reinterpret_cast<HANDLE>(0x789),\n      reinterpret_cast<HANDLE>(0xabc),\n      reinterpret_cast<HANDLE>(0xdef),\n      0x7fff000012345678ull,\n      0x100000ull,\n      0xccccddddeeeeffffull);\n  EXPECT_TRUE(icd2.IsValid());\n}\n\nTEST(InitialClientData, RoundTrip) {\n  InitialClientData first(\n      reinterpret_cast<HANDLE>(0x123),\n      reinterpret_cast<HANDLE>(0x456),\n      reinterpret_cast<HANDLE>(0x789),\n      reinterpret_cast<HANDLE>(0xabc),\n      reinterpret_cast<HANDLE>(0xdef),\n      0x7fff000012345678ull,\n      0x100000ull,\n      0xccccddddeeeeffffull);\n\n  std::string as_string = first.StringRepresentation();\n  EXPECT_EQ(as_string,\n            \"0x123,0x456,0x789,0xabc,0xdef,\"\n            \"0x7fff000012345678,0x100000,0xccccddddeeeeffff\");\n\n  InitialClientData second;\n  ASSERT_TRUE(second.InitializeFromString(as_string));\n  EXPECT_EQ(second.request_crash_dump(), first.request_crash_dump());\n  EXPECT_EQ(second.request_non_crash_dump(), first.request_non_crash_dump());\n  EXPECT_EQ(second.non_crash_dump_completed(),\n            first.non_crash_dump_completed());\n  EXPECT_EQ(second.first_pipe_instance(), first.first_pipe_instance());\n  EXPECT_EQ(second.client_process(), first.client_process());\n  EXPECT_EQ(second.crash_exception_information(),\n            first.crash_exception_information());\n  EXPECT_EQ(second.non_crash_exception_information(),\n            first.non_crash_exception_information());\n  EXPECT_EQ(second.debug_critical_section_address(),\n            first.debug_critical_section_address());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/loader_lock.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/loader_lock.h\"\n\n#include <windows.h>\n\n#include \"build/build_config.h\"\n#include \"util/win/process_structs.h\"\n\nnamespace crashpad {\n\nnamespace {\n\n#ifdef ARCH_CPU_64_BITS\nusing NativeTraits = process_types::internal::Traits64;\n#else\nusing NativeTraits = process_types::internal::Traits32;\n#endif  // ARCH_CPU_64_BITS\n\nusing PEB = process_types::PEB<NativeTraits>;\nusing TEB = process_types::TEB<NativeTraits>;\nusing RTL_CRITICAL_SECTION = process_types::RTL_CRITICAL_SECTION<NativeTraits>;\n\nTEB* GetTeb() {\n  return reinterpret_cast<TEB*>(NtCurrentTeb());\n}\n\nPEB* GetPeb() {\n  return reinterpret_cast<PEB*>(GetTeb()->ProcessEnvironmentBlock);\n}\n\n}  // namespace\n\nbool IsThreadInLoaderLock() {\n  RTL_CRITICAL_SECTION* loader_lock =\n      reinterpret_cast<RTL_CRITICAL_SECTION*>(GetPeb()->LoaderLock);\n  return loader_lock->OwningThread == GetTeb()->ClientId.UniqueThread;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/loader_lock.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_LOADER_LOCK_H_\n#define CRASHPAD_UTIL_WIN_LOADER_LOCK_H_\n\nnamespace crashpad {\n\n//! \\return `true` if the current thread holds the loader lock.\nbool IsThreadInLoaderLock();\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_LOADER_LOCK_H_\n"
  },
  {
    "path": "util/win/loader_lock_test.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/loader_lock.h\"\n\n#include \"gtest/gtest.h\"\n#include \"util/win/get_function.h\"\n\nextern \"C\" bool LoaderLockDetected();\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nTEST(LoaderLock, Detected) {\n  EXPECT_FALSE(IsThreadInLoaderLock());\n  auto* loader_lock_detected = GET_FUNCTION_REQUIRED(\n      L\"crashpad_util_test_loader_lock_test.dll\", LoaderLockDetected);\n  EXPECT_TRUE(loader_lock_detected());\n  EXPECT_FALSE(IsThreadInLoaderLock());\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/loader_lock_test_dll.cc",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <windows.h>\n\n#include \"util/win/loader_lock.h\"\n\nnamespace {\n\nbool g_loader_lock_detected = false;\n\n}  // namespace\n\nextern \"C\" {\n\n__declspec(dllexport) bool LoaderLockDetected() {\n  return g_loader_lock_detected;\n}\n\n}  // extern \"C\"\n\nBOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) {\n  switch (reason) {\n    case DLL_PROCESS_ATTACH:\n      g_loader_lock_detected = crashpad::IsThreadInLoaderLock();\n      break;\n  }\n\n  return TRUE;\n}\n"
  },
  {
    "path": "util/win/module_version.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/module_version.h\"\n\n#include <windows.h>\n#include <stdint.h>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/logging.h\"\n#include \"base/strings/utf_string_conversions.h\"\n\nnamespace crashpad {\n\nbool GetModuleVersionAndType(const base::FilePath& path,\n                             VS_FIXEDFILEINFO* vs_fixedfileinfo) {\n  DWORD size = GetFileVersionInfoSize(path.value().c_str(), nullptr);\n  if (!size) {\n    PLOG_IF(WARNING, GetLastError() != ERROR_RESOURCE_TYPE_NOT_FOUND)\n        << \"GetFileVersionInfoSize: \" << base::WideToUTF8(path.value());\n    return false;\n  }\n\n  auto data = base::HeapArray<uint8_t>::Uninit(size);\n  if (!GetFileVersionInfo(path.value().c_str(),\n                          0,\n                          static_cast<DWORD>(data.size()),\n                          data.data())) {\n    PLOG(WARNING) << \"GetFileVersionInfo: \" << base::WideToUTF8(path.value());\n    return false;\n  }\n\n  VS_FIXEDFILEINFO* fixed_file_info;\n  UINT ffi_size;\n  if (!VerQueryValue(data.data(),\n                     L\"\\\\\",\n                     reinterpret_cast<void**>(&fixed_file_info),\n                     &ffi_size)) {\n    PLOG(WARNING) << \"VerQueryValue\";\n    return false;\n  }\n\n  *vs_fixedfileinfo = *fixed_file_info;\n  vs_fixedfileinfo->dwFileFlags &= vs_fixedfileinfo->dwFileFlagsMask;\n  return true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/module_version.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_MODULE_VERSION_H_\n#define CRASHPAD_UTIL_WIN_MODULE_VERSION_H_\n\n#include <windows.h>\n\n#include \"base/files/file_path.h\"\n\nnamespace crashpad {\n\n//! \\brief Retrieve the type and version information from a given module (exe,\n//!     dll, etc.)\n//!\n//! This function calls `GetFileVersionInfo()`, which can implicitly call\n//! `LoadLibrary()` to load \\a path into the calling process. Do not call this\n//! function on an untrusted module, because there is a risk of executing the\n//! module’s code.\n//!\n//! \\param[in] path The path to the module to be inspected.\n//! \\param[out] vs_fixedfileinfo The VS_FIXEDFILEINFO on success.\n//!     VS_FIXEDFILEINFO::dwFileFlags will have been masked with\n//!     VS_FIXEDFILEINFO::dwFileFlagsMask already.\n//!\n//! \\return `true` on success, or `false` on failure with a message logged. If\n//!     the module has no `VERSIONINFO` resource, `false` will be returned\n//!     without any messages logged.\nbool GetModuleVersionAndType(const base::FilePath& path,\n                             VS_FIXEDFILEINFO* vs_fixedfileinfo);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_MODULE_VERSION_H_\n"
  },
  {
    "path": "util/win/nt_internals.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/nt_internals.h\"\n\n#include \"util/win/get_function.h\"\n\n// Declarations that the system headers should provide but don’t.\n\nextern \"C\" {\n\nNTSTATUS NTAPI NtCreateThreadEx(PHANDLE ThreadHandle,\n                                ACCESS_MASK DesiredAccess,\n                                POBJECT_ATTRIBUTES ObjectAttributes,\n                                HANDLE ProcessHandle,\n                                PVOID StartRoutine,\n                                PVOID Argument,\n                                ULONG CreateFlags,\n                                SIZE_T ZeroBits,\n                                SIZE_T StackSize,\n                                SIZE_T MaximumStackSize,\n                                PVOID /*PPS_ATTRIBUTE_LIST*/ AttributeList);\n\nNTSTATUS NTAPI NtOpenThread(HANDLE* ThreadHandle,\n                            ACCESS_MASK DesiredAccess,\n                            OBJECT_ATTRIBUTES* ObjectAttributes,\n                            CLIENT_ID* ClientId);\n\nNTSTATUS NTAPI NtSuspendProcess(HANDLE);\n\nNTSTATUS NTAPI NtResumeProcess(HANDLE);\n\nVOID NTAPI RtlGetUnloadEventTraceEx(PULONG* ElementSize,\n                                    PULONG* ElementCount,\n                                    PVOID* EventTrace);\n\n}  // extern \"C\"\n\nnamespace crashpad {\n\nNTSTATUS NtClose(HANDLE handle) {\n  static const auto nt_close = GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtClose);\n  return nt_close(handle);\n}\n\nNTSTATUS\nNtCreateThreadEx(PHANDLE thread_handle,\n                 ACCESS_MASK desired_access,\n                 POBJECT_ATTRIBUTES object_attributes,\n                 HANDLE process_handle,\n                 PVOID start_routine,\n                 PVOID argument,\n                 ULONG create_flags,\n                 SIZE_T zero_bits,\n                 SIZE_T stack_size,\n                 SIZE_T maximum_stack_size,\n                 PVOID attribute_list) {\n  static const auto nt_create_thread_ex =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtCreateThreadEx);\n  return nt_create_thread_ex(thread_handle,\n                             desired_access,\n                             object_attributes,\n                             process_handle,\n                             start_routine,\n                             argument,\n                             create_flags,\n                             zero_bits,\n                             stack_size,\n                             maximum_stack_size,\n                             attribute_list);\n}\n\nNTSTATUS NtQuerySystemInformation(\n    SYSTEM_INFORMATION_CLASS system_information_class,\n    PVOID system_information,\n    ULONG system_information_length,\n    PULONG return_length) {\n  static const auto nt_query_system_information =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtQuerySystemInformation);\n  return nt_query_system_information(system_information_class,\n                                     system_information,\n                                     system_information_length,\n                                     return_length);\n}\n\nNTSTATUS NtQueryInformationThread(HANDLE thread_handle,\n                                  THREADINFOCLASS thread_information_class,\n                                  PVOID thread_information,\n                                  ULONG thread_information_length,\n                                  PULONG return_length) {\n  static const auto nt_query_information_thread =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtQueryInformationThread);\n  return nt_query_information_thread(thread_handle,\n                                     thread_information_class,\n                                     thread_information,\n                                     thread_information_length,\n                                     return_length);\n}\n\ntemplate <class Traits>\nNTSTATUS NtOpenThread(PHANDLE thread_handle,\n                      ACCESS_MASK desired_access,\n                      POBJECT_ATTRIBUTES object_attributes,\n                      const process_types::CLIENT_ID<Traits>* client_id) {\n  static const auto nt_open_thread =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtOpenThread);\n  return nt_open_thread(\n      thread_handle,\n      desired_access,\n      object_attributes,\n      const_cast<CLIENT_ID*>(reinterpret_cast<const CLIENT_ID*>(client_id)));\n}\n\nNTSTATUS NtQueryObject(HANDLE handle,\n                       OBJECT_INFORMATION_CLASS object_information_class,\n                       void* object_information,\n                       ULONG object_information_length,\n                       ULONG* return_length) {\n  static const auto nt_query_object =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtQueryObject);\n  return nt_query_object(handle,\n                         object_information_class,\n                         object_information,\n                         object_information_length,\n                         return_length);\n}\n\nNTSTATUS NtSuspendProcess(HANDLE handle) {\n  static const auto nt_suspend_process =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtSuspendProcess);\n  return nt_suspend_process(handle);\n}\n\nNTSTATUS NtResumeProcess(HANDLE handle) {\n  static const auto nt_resume_process =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtResumeProcess);\n  return nt_resume_process(handle);\n}\n\nvoid RtlGetUnloadEventTraceEx(ULONG** element_size,\n                              ULONG** element_count,\n                              void** event_trace) {\n  static const auto rtl_get_unload_event_trace_ex =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::RtlGetUnloadEventTraceEx);\n  rtl_get_unload_event_trace_ex(element_size, element_count, event_trace);\n}\n\n// Explicit instantiations with the only 2 valid template arguments to avoid\n// putting the body of the function in the header.\ntemplate NTSTATUS NtOpenThread<process_types::internal::Traits32>(\n    PHANDLE thread_handle,\n    ACCESS_MASK desired_access,\n    POBJECT_ATTRIBUTES object_attributes,\n    const process_types::CLIENT_ID<process_types::internal::Traits32>*\n        client_id);\n\ntemplate NTSTATUS NtOpenThread<process_types::internal::Traits64>(\n    PHANDLE thread_handle,\n    ACCESS_MASK desired_access,\n    POBJECT_ATTRIBUTES object_attributes,\n    const process_types::CLIENT_ID<process_types::internal::Traits64>*\n        client_id);\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/nt_internals.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_NT_INTERNALS_H_\n#define CRASHPAD_UTIL_WIN_NT_INTERNALS_H_\n\n#include <windows.h>\n#include <winternl.h>\n\n#include \"util/win/process_structs.h\"\n\n// Copied from ntstatus.h because um/winnt.h conflicts with general inclusion of\n// ntstatus.h.\n#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)\n#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)\n#define STATUS_PROCESS_IS_TERMINATING ((NTSTATUS)0xC000010AL)\n\nnamespace crashpad {\n\nNTSTATUS NtClose(HANDLE handle);\n\n// http://processhacker.sourceforge.net/doc/ntpsapi_8h_source.html\n#define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002\nNTSTATUS\nNtCreateThreadEx(PHANDLE thread_handle,\n                 ACCESS_MASK desired_access,\n                 POBJECT_ATTRIBUTES object_attributes,\n                 HANDLE process_handle,\n                 PVOID start_routine,\n                 PVOID argument,\n                 ULONG create_flags,\n                 SIZE_T zero_bits,\n                 SIZE_T stack_size,\n                 SIZE_T maximum_stack_size,\n                 PVOID /*PPS_ATTRIBUTE_LIST*/ attribute_list);\n\n// winternal.h defines THREADINFOCLASS, but not all members.\nenum { ThreadBasicInformation = 0 };\n\n// winternal.h defines SYSTEM_INFORMATION_CLASS, but not all members.\nenum { SystemExtendedHandleInformation = 64 };\n\nNTSTATUS NtQuerySystemInformation(\n    SYSTEM_INFORMATION_CLASS system_information_class,\n    PVOID system_information,\n    ULONG system_information_length,\n    PULONG return_length);\n\nNTSTATUS NtQueryInformationThread(HANDLE thread_handle,\n                                  THREADINFOCLASS thread_information_class,\n                                  PVOID thread_information,\n                                  ULONG thread_information_length,\n                                  PULONG return_length);\n\ntemplate <class Traits>\nNTSTATUS NtOpenThread(PHANDLE thread_handle,\n                      ACCESS_MASK desired_access,\n                      POBJECT_ATTRIBUTES object_attributes,\n                      const process_types::CLIENT_ID<Traits>* client_id);\n\nNTSTATUS NtQueryObject(HANDLE handle,\n                       OBJECT_INFORMATION_CLASS object_information_class,\n                       void* object_information,\n                       ULONG object_information_length,\n                       ULONG* return_length);\n\nNTSTATUS NtSuspendProcess(HANDLE handle);\n\nNTSTATUS NtResumeProcess(HANDLE handle);\n\n// From https://msdn.microsoft.com/library/cc678403.aspx.\ntemplate <class Traits>\nstruct RTL_UNLOAD_EVENT_TRACE {\n  typename Traits::Pointer BaseAddress;\n  typename Traits::UnsignedIntegral SizeOfImage;\n  ULONG Sequence;\n  ULONG TimeDateStamp;\n  ULONG CheckSum;\n  WCHAR ImageName[32];\n};\n\nvoid RtlGetUnloadEventTraceEx(ULONG** element_size,\n                              ULONG** element_count,\n                              void** event_trace);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_NT_INTERNALS_H_\n"
  },
  {
    "path": "util/win/ntstatus_logging.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/ntstatus_logging.h\"\n\n#include <iterator>\n#include <string>\n\n#include \"base/immediate_crash.h\"\n#include \"base/scoped_clear_last_error.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n\nnamespace {\n\nstd::string FormatNtstatus(DWORD ntstatus) {\n  char msgbuf[256];\n  DWORD len = FormatMessageA(\n      FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |\n          FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_FROM_HMODULE,\n      GetModuleHandle(L\"ntdll.dll\"),\n      ntstatus,\n      0,\n      msgbuf,\n      static_cast<DWORD>(std::size(msgbuf)),\n      nullptr);\n  if (len) {\n    // Most system messages end in a period and a space. Remove the space if\n    // it’s there, because ~NtstatusLogMessage() includes one.\n    if (len >= 1 && msgbuf[len - 1] == ' ') {\n      msgbuf[len - 1] = '\\0';\n    }\n    return msgbuf;\n  } else {\n    return base::StringPrintf(\"<failed to retrieve error message (0x%lx)>\",\n                              GetLastError());\n  }\n}\n\n}  // namespace\n\nnamespace logging {\n\nNtstatusLogMessage::NtstatusLogMessage(\n#if defined(MINI_CHROMIUM_BASE_LOGGING_H_)\n    const char* function,\n#endif\n    const char* file_path,\n    int line,\n    LogSeverity severity,\n    DWORD ntstatus)\n    : LogMessage(\n#if defined(MINI_CHROMIUM_BASE_LOGGING_H_)\n          function,\n#endif\n          file_path,\n          line,\n          severity),\n      ntstatus_(ntstatus) {\n}\n\nNtstatusLogMessage::~NtstatusLogMessage() {\n  AppendError();\n}\n\nvoid NtstatusLogMessage::AppendError() {\n  // Don't let actions from this method affect the system error after returning.\n  base::ScopedClearLastError scoped_clear_last_error;\n\n  stream() << \": \" << FormatNtstatus(ntstatus_)\n           << base::StringPrintf(\" (0x%08lx)\", ntstatus_);\n}\n\n#if defined(COMPILER_MSVC)\n// Ignore warning that ~NtStatusLogMessageFatal never returns.\n#pragma warning(push)\n#pragma warning(disable : 4722)\n#endif  // COMPILER_MSVC\nNtstatusLogMessageFatal::~NtstatusLogMessageFatal() {\n  AppendError();\n  Flush();\n  base::ImmediateCrash();\n}\n#if defined(COMPILER_MSVC)\n#pragma warning(pop)  // C4722\n#endif  // COMPILER_MSVC\n\n}  // namespace logging\n"
  },
  {
    "path": "util/win/ntstatus_logging.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_\n#define CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_\n\n#include <windows.h>\n\n#include \"base/logging.h\"\n\nnamespace logging {\n\nclass NtstatusLogMessage : public logging::LogMessage {\n public:\n  NtstatusLogMessage(\n#if defined(MINI_CHROMIUM_BASE_LOGGING_H_)\n      const char* function,\n#endif\n      const char* file_path,\n      int line,\n      LogSeverity severity,\n      DWORD ntstatus);\n\n  NtstatusLogMessage(const NtstatusLogMessage&) = delete;\n  NtstatusLogMessage& operator=(const NtstatusLogMessage&) = delete;\n\n  ~NtstatusLogMessage();\n\n protected:\n  void AppendError();\n\n private:\n  DWORD ntstatus_;\n};\n\nclass NtstatusLogMessageFatal final : public NtstatusLogMessage {\n public:\n  using NtstatusLogMessage::NtstatusLogMessage;\n  [[noreturn]] ~NtstatusLogMessageFatal() override;\n};\n\n}  // namespace logging\n\n#define NTSTATUS_LOG_STREAM(severity, ntstatus) \\\n  COMPACT_GOOGLE_LOG_EX_##severity(NtstatusLogMessage, ntstatus).stream()\n\n#if defined(MINI_CHROMIUM_BASE_LOGGING_H_)\n\n#define NTSTATUS_VLOG_STREAM(verbose_level, ntstatus)                    \\\n  logging::NtstatusLogMessage(                                           \\\n      __PRETTY_FUNCTION__, __FILE__, __LINE__, -verbose_level, ntstatus) \\\n      .stream()\n\n#else\n\n#define NTSTATUS_VLOG_STREAM(verbose_level, ntstatus)                       \\\n  logging::NtstatusLogMessage(__FILE__, __LINE__, -verbose_level, ntstatus) \\\n      .stream()\n\n#endif  // MINI_CHROMIUM_BASE_LOGGING_H_\n\n#define NTSTATUS_LOG(severity, ntstatus) \\\n  LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), LOG_IS_ON(severity))\n#define NTSTATUS_LOG_IF(severity, condition, ntstatus) \\\n  LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), \\\n              LOG_IS_ON(severity) && (condition))\n\n#define NTSTATUS_VLOG(verbose_level, ntstatus)               \\\n  LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \\\n              VLOG_IS_ON(verbose_level))\n#define NTSTATUS_VLOG_IF(verbose_level, condition, ntstatus) \\\n  LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \\\n              VLOG_IS_ON(verbose_level) && (condition))\n\n#define NTSTATUS_CHECK(condition, ntstatus)                       \\\n  LAZY_STREAM(NTSTATUS_LOG_STREAM(FATAL, ntstatus), !(condition)) \\\n      << \"Check failed: \" #condition << \". \"\n\n#define NTSTATUS_DLOG(severity, ntstatus) \\\n  LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus), DLOG_IS_ON(severity))\n#define NTSTATUS_DLOG_IF(severity, condition, ntstatus) \\\n  LAZY_STREAM(NTSTATUS_LOG_STREAM(severity, ntstatus),  \\\n              DLOG_IS_ON(severity) && (condition))\n\n#define NTSTATUS_DVLOG(verbose_level, ntstatus)              \\\n  LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus), \\\n              DVLOG_IS_ON(verbose_level))\n#define NTSTATUS_DVLOG_IF(verbose_level, condition, ntstatus) \\\n  LAZY_STREAM(NTSTATUS_VLOG_STREAM(verbose_level, ntstatus),  \\\n              DVLOG_IS_ON(verbose_level) && (condition))\n\n#define NTSTATUS_DCHECK(condition, ntstatus)        \\\n  LAZY_STREAM(NTSTATUS_LOG_STREAM(FATAL, ntstatus), \\\n              DCHECK_IS_ON && !(condition))         \\\n      << \"Check failed: \" #condition << \". \"\n\n#endif  // CRASHPAD_UTIL_WIN_NTSTATUS_LOGGING_H_\n"
  },
  {
    "path": "util/win/process_info.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/process_info.h\"\n\n#include <stddef.h>\n#include <winternl.h>\n\n#include <algorithm>\n#include <limits>\n#include <memory>\n#include <new>\n#include <type_traits>\n\n#include \"base/check_op.h\"\n#include \"base/containers/heap_array.h\"\n#include \"base/logging.h\"\n#include \"base/memory/free_deleter.h\"\n#include \"base/process/memory.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"build/build_config.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/numeric/safe_assignment.h\"\n#include \"util/win/get_function.h\"\n#include \"util/win/handle.h\"\n#include \"util/win/nt_internals.h\"\n#include \"util/win/ntstatus_logging.h\"\n#include \"util/win/process_structs.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nusing UniqueMallocPtr = std::unique_ptr<uint8_t[], base::FreeDeleter>;\n\nUniqueMallocPtr UncheckedAllocate(size_t size) {\n  void* raw_ptr = nullptr;\n  if (!base::UncheckedMalloc(size, &raw_ptr))\n    return UniqueMallocPtr();\n\n  return UniqueMallocPtr(new (raw_ptr) uint8_t[size]);\n}\n\nNTSTATUS NtQueryInformationProcess(HANDLE process_handle,\n                                   PROCESSINFOCLASS process_information_class,\n                                   PVOID process_information,\n                                   ULONG process_information_length,\n                                   PULONG return_length) {\n  static const auto nt_query_information_process =\n      GET_FUNCTION_REQUIRED(L\"ntdll.dll\", ::NtQueryInformationProcess);\n  return nt_query_information_process(process_handle,\n                                      process_information_class,\n                                      process_information,\n                                      process_information_length,\n                                      return_length);\n}\n\nbool IsProcessWow64(HANDLE process_handle) {\n  static const auto is_wow64_process =\n      GET_FUNCTION(L\"kernel32.dll\", ::IsWow64Process);\n  if (!is_wow64_process)\n    return false;\n  BOOL is_wow64;\n  if (!is_wow64_process(process_handle, &is_wow64)) {\n    PLOG(ERROR) << \"IsWow64Process\";\n    return false;\n  }\n  return !!is_wow64;\n}\n\ntemplate <class T>\nbool ReadUnicodeString(HANDLE process,\n                       const process_types::UNICODE_STRING<T>& us,\n                       std::wstring* result) {\n  if (us.Length == 0) {\n    result->clear();\n    return true;\n  }\n  DCHECK_EQ(us.Length % sizeof(wchar_t), 0u);\n  result->resize(us.Length / sizeof(wchar_t));\n  SIZE_T bytes_read;\n  if (!ReadProcessMemory(\n          process,\n          reinterpret_cast<const void*>(static_cast<uintptr_t>(us.Buffer)),\n          &result->operator[](0),\n          us.Length,\n          &bytes_read)) {\n    PLOG(ERROR) << \"ReadProcessMemory UNICODE_STRING\";\n    return false;\n  }\n  if (bytes_read != us.Length) {\n    LOG(ERROR) << \"ReadProcessMemory UNICODE_STRING incorrect size\";\n    return false;\n  }\n  return true;\n}\n\ntemplate <class T>\nbool ReadStruct(HANDLE process, WinVMAddress at, T* into) {\n  SIZE_T bytes_read;\n  if (!ReadProcessMemory(process,\n                         reinterpret_cast<const void*>(at),\n                         into,\n                         sizeof(T),\n                         &bytes_read)) {\n    // We don't have a name for the type we're reading, so include the signature\n    // to get the type of T.\n    PLOG(ERROR) << \"ReadProcessMemory \" << __FUNCSIG__;\n    return false;\n  }\n  if (bytes_read != sizeof(T)) {\n    LOG(ERROR) << \"ReadProcessMemory \" << __FUNCSIG__ << \" incorrect size\";\n    return false;\n  }\n  return true;\n}\n\nbool RegionIsAccessible(const MEMORY_BASIC_INFORMATION64& memory_info) {\n  return memory_info.State == MEM_COMMIT &&\n         (memory_info.Protect & PAGE_NOACCESS) == 0 &&\n         (memory_info.Protect & PAGE_GUARD) == 0;\n}\n\nMEMORY_BASIC_INFORMATION64 MemoryBasicInformationToMemoryBasicInformation64(\n    const MEMORY_BASIC_INFORMATION& mbi) {\n  MEMORY_BASIC_INFORMATION64 mbi64 = {0};\n  mbi64.BaseAddress = FromPointerCast<ULONGLONG>(mbi.BaseAddress);\n  mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase);\n  mbi64.AllocationProtect = mbi.AllocationProtect;\n  mbi64.RegionSize = mbi.RegionSize;\n  mbi64.State = mbi.State;\n  mbi64.Protect = mbi.Protect;\n  mbi64.Type = mbi.Type;\n  return mbi64;\n}\n\n// NtQueryObject with a retry for size mismatch as well as a minimum size to\n// retrieve (and expect).\nbase::HeapArray<uint8_t> QueryObject(\n    HANDLE handle,\n    OBJECT_INFORMATION_CLASS object_information_class,\n    ULONG minimum_size) {\n  ULONG return_length;\n  auto buffer = base::HeapArray<uint8_t>::Uninit(minimum_size);\n  NTSTATUS status = crashpad::NtQueryObject(handle,\n                                            object_information_class,\n                                            buffer.data(),\n                                            (ULONG)buffer.size(),\n                                            &return_length);\n  if (status == STATUS_INFO_LENGTH_MISMATCH) {\n    DCHECK_GT(return_length, buffer.size());\n\n    buffer = base::HeapArray<uint8_t>::Uninit(return_length);\n    status = crashpad::NtQueryObject(handle,\n                                     object_information_class,\n                                     buffer.data(),\n                                     (ULONG)buffer.size(),\n                                     &return_length);\n  }\n\n  if (!NT_SUCCESS(status)) {\n    NTSTATUS_LOG(ERROR, status) << \"NtQueryObject\";\n    return base::HeapArray<uint8_t>();\n  }\n\n  DCHECK_LE(return_length, buffer.size());\n  DCHECK_GE(return_length, minimum_size);\n  return buffer;\n}\n\n}  // namespace\n\ntemplate <class Traits>\nbool GetProcessBasicInformation(HANDLE process,\n                                bool is_wow64,\n                                ProcessInfo* process_info,\n                                WinVMAddress* peb_address,\n                                WinVMSize* peb_size) {\n  ULONG bytes_returned;\n  process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information;\n  NTSTATUS status =\n      crashpad::NtQueryInformationProcess(process,\n                                          ProcessBasicInformation,\n                                          &process_basic_information,\n                                          sizeof(process_basic_information),\n                                          &bytes_returned);\n  if (!NT_SUCCESS(status)) {\n    NTSTATUS_LOG(ERROR, status) << \"NtQueryInformationProcess\";\n    return false;\n  }\n  if (bytes_returned != sizeof(process_basic_information)) {\n    LOG(ERROR) << \"NtQueryInformationProcess incorrect size\";\n    return false;\n  }\n\n  // API functions (e.g. OpenProcess) take only a DWORD, so there's no sense in\n  // maintaining the top bits.\n  process_info->process_id_ =\n      static_cast<DWORD>(process_basic_information.UniqueProcessId);\n  process_info->inherited_from_process_id_ = static_cast<DWORD>(\n      process_basic_information.InheritedFromUniqueProcessId);\n\n  // We now want to read the PEB to gather the rest of our information. The\n  // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,\n  // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).\n  // The address of this is found by a second call to NtQueryInformationProcess.\n  if (!is_wow64) {\n    *peb_address = process_basic_information.PebBaseAddress;\n    *peb_size = sizeof(process_types::PEB<Traits>);\n  } else {\n    ULONG_PTR wow64_peb_address;\n    status = crashpad::NtQueryInformationProcess(process,\n                                                 ProcessWow64Information,\n                                                 &wow64_peb_address,\n                                                 sizeof(wow64_peb_address),\n                                                 &bytes_returned);\n    if (!NT_SUCCESS(status)) {\n      NTSTATUS_LOG(ERROR, status) << \"NtQueryInformationProcess\";\n      return false;\n    }\n    if (bytes_returned != sizeof(wow64_peb_address)) {\n      LOG(ERROR) << \"NtQueryInformationProcess incorrect size\";\n      return false;\n    }\n    *peb_address = wow64_peb_address;\n    *peb_size = sizeof(process_types::PEB<process_types::internal::Traits32>);\n  }\n\n  return true;\n}\n\ntemplate <class Traits>\nbool ReadProcessData(HANDLE process,\n                     WinVMAddress peb_address_vmaddr,\n                     ProcessInfo* process_info) {\n  typename Traits::Pointer peb_address;\n  if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) {\n    LOG(ERROR) << base::StringPrintf(\"peb address 0x%llx out of range\",\n                                     peb_address_vmaddr);\n    return false;\n  }\n\n  // Try to read the process environment block.\n  process_types::PEB<Traits> peb;\n  if (!ReadStruct(process, peb_address, &peb))\n    return false;\n\n  process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;\n  if (!ReadStruct(process, peb.ProcessParameters, &process_parameters))\n    return false;\n\n  if (!ReadUnicodeString(process,\n                         process_parameters.CommandLine,\n                         &process_info->command_line_)) {\n    return false;\n  }\n\n  process_types::PEB_LDR_DATA<Traits> peb_ldr_data;\n  if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))\n    return false;\n\n  process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry;\n  ProcessInfo::Module module;\n\n  // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded\n  // modules. We use this method rather than EnumProcessModules to get the\n  // modules in load order rather than memory order. Notably, this includes the\n  // main executable as the first element.\n  typename Traits::Pointer last = peb_ldr_data.InLoadOrderModuleList.Blink;\n  for (typename Traits::Pointer cur = peb_ldr_data.InLoadOrderModuleList.Flink;;\n       cur = ldr_data_table_entry.InLoadOrderLinks.Flink) {\n    // |cur| is the pointer to the LIST_ENTRY embedded in the\n    // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need\n    // to read from the target, and also offset back to the beginning of the\n    // structure.\n    if (!ReadStruct(process,\n                    static_cast<WinVMAddress>(cur) -\n                        offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,\n                                 InLoadOrderLinks),\n                    &ldr_data_table_entry)) {\n      break;\n    }\n    // TODO(scottmg): Capture Checksum, etc. too?\n    if (!ReadUnicodeString(\n            process, ldr_data_table_entry.FullDllName, &module.name)) {\n      module.name = L\"???\";\n    }\n    module.dll_base = ldr_data_table_entry.DllBase;\n    module.size = ldr_data_table_entry.SizeOfImage;\n    module.timestamp = ldr_data_table_entry.TimeDateStamp;\n    process_info->modules_.push_back(module);\n    if (cur == last)\n      break;\n  }\n\n  return true;\n}\n\nbool ReadMemoryInfo(HANDLE process, bool is_64_bit, ProcessInfo* process_info) {\n  DCHECK(process_info->memory_info_.empty());\n\n  constexpr WinVMAddress min_address = 0;\n  // We can't use GetSystemInfo() to get the address space range for another\n  // process. VirtualQueryEx() will fail with ERROR_INVALID_PARAMETER if the\n  // address is above the highest memory address accessible to the process, so\n  // we just probe the entire potential range (2^32 for x86, or 2^64 for x64).\n  const WinVMAddress max_address = is_64_bit\n                                       ? std::numeric_limits<uint64_t>::max()\n                                       : std::numeric_limits<uint32_t>::max();\n  MEMORY_BASIC_INFORMATION memory_basic_information;\n  for (WinVMAddress address = min_address; address <= max_address;\n       address += memory_basic_information.RegionSize) {\n    size_t result = VirtualQueryEx(process,\n                                   reinterpret_cast<void*>(address),\n                                   &memory_basic_information,\n                                   sizeof(memory_basic_information));\n    if (result == 0) {\n      if (GetLastError() == ERROR_INVALID_PARAMETER)\n        break;\n      PLOG(ERROR) << \"VirtualQueryEx\";\n      return false;\n    }\n\n    process_info->memory_info_.push_back(\n        MemoryBasicInformationToMemoryBasicInformation64(\n            memory_basic_information));\n\n    if (memory_basic_information.RegionSize == 0) {\n      LOG(ERROR) << \"RegionSize == 0\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nstd::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector(\n    HANDLE process) const {\n  ULONG buffer_size = 2 * 1024 * 1024;\n  // Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would\n  // return the correct size in the final argument, but it does not for\n  // SystemExtendedHandleInformation, so we loop and attempt larger sizes.\n  NTSTATUS status;\n  ULONG returned_length;\n  UniqueMallocPtr buffer;\n  for (int tries = 0; tries < 5; ++tries) {\n    buffer.reset();\n    buffer = UncheckedAllocate(buffer_size);\n    if (!buffer) {\n      LOG(ERROR) << \"UncheckedAllocate\";\n      return std::vector<Handle>();\n    }\n\n    status = crashpad::NtQuerySystemInformation(\n        static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation),\n        buffer.get(),\n        buffer_size,\n        &returned_length);\n    if (NT_SUCCESS(status) || status != STATUS_INFO_LENGTH_MISMATCH)\n      break;\n\n    buffer_size *= 2;\n  }\n\n  if (!NT_SUCCESS(status)) {\n    NTSTATUS_LOG(ERROR, status)\n        << \"NtQuerySystemInformation SystemExtendedHandleInformation\";\n    return std::vector<Handle>();\n  }\n\n  const auto& system_handle_information_ex =\n      *reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX*>(\n          buffer.get());\n\n  DCHECK_LE(offsetof(process_types::SYSTEM_HANDLE_INFORMATION_EX, Handles) +\n                system_handle_information_ex.NumberOfHandles *\n                    sizeof(system_handle_information_ex.Handles[0]),\n            returned_length);\n\n  std::vector<Handle> handles;\n\n  for (size_t i = 0; i < system_handle_information_ex.NumberOfHandles; ++i) {\n    const auto& handle = system_handle_information_ex.Handles[i];\n    if (handle.UniqueProcessId != process_id_)\n      continue;\n\n    Handle result_handle;\n    result_handle.handle = HandleToInt(handle.HandleValue);\n    result_handle.attributes = handle.HandleAttributes;\n    result_handle.granted_access = handle.GrantedAccess;\n\n    // TODO(scottmg): Could special case for self.\n    HANDLE dup_handle;\n    if (DuplicateHandle(process,\n                        handle.HandleValue,\n                        GetCurrentProcess(),\n                        &dup_handle,\n                        0,\n                        false,\n                        DUPLICATE_SAME_ACCESS)) {\n      // Some handles cannot be duplicated, for example, handles of type\n      // EtwRegistration. If we fail to duplicate, then we can't gather any more\n      // information, but include the information that we do have already.\n      ScopedKernelHANDLE scoped_dup_handle(dup_handle);\n\n      auto object_basic_information_buffer =\n          QueryObject(dup_handle,\n                      ObjectBasicInformation,\n                      sizeof(PUBLIC_OBJECT_BASIC_INFORMATION));\n      if (!object_basic_information_buffer.empty()) {\n        PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information =\n            reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>(\n                object_basic_information_buffer.data());\n        // The Attributes and GrantedAccess sometimes differ slightly between\n        // the data retrieved in SYSTEM_HANDLE_INFORMATION_EX and\n        // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in\n        // SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the\n        // target process, rather than on the duplicated handle, so don't use\n        // them here.\n\n        // Subtract one to account for our DuplicateHandle() and another for\n        // NtQueryObject() while the query was being executed.\n        DCHECK_GT(object_basic_information->PointerCount, 2u);\n        result_handle.pointer_count =\n            object_basic_information->PointerCount - 2;\n\n        // Subtract one to account for our DuplicateHandle().\n        DCHECK_GT(object_basic_information->HandleCount, 1u);\n        result_handle.handle_count = object_basic_information->HandleCount - 1;\n      }\n\n      auto object_type_information_buffer =\n          QueryObject(dup_handle,\n                      ObjectTypeInformation,\n                      sizeof(PUBLIC_OBJECT_TYPE_INFORMATION));\n      if (!object_type_information_buffer.empty()) {\n        PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information =\n            reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>(\n                object_type_information_buffer.data());\n\n        DCHECK_EQ(object_type_information->TypeName.Length %\n                      sizeof(result_handle.type_name[0]),\n                  0u);\n        result_handle.type_name =\n            std::wstring(object_type_information->TypeName.Buffer,\n                         object_type_information->TypeName.Length /\n                             sizeof(result_handle.type_name[0]));\n      }\n    }\n\n    handles.push_back(result_handle);\n  }\n  return handles;\n}\n\nProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() {\n}\n\nProcessInfo::Module::~Module() {\n}\n\nProcessInfo::Handle::Handle()\n    : type_name(),\n      handle(0),\n      attributes(0),\n      granted_access(0),\n      pointer_count(0),\n      handle_count(0) {\n}\n\nProcessInfo::Handle::~Handle() {\n}\n\nProcessInfo::ProcessInfo()\n    : process_id_(),\n      inherited_from_process_id_(),\n      process_(),\n      command_line_(),\n      peb_address_(0),\n      peb_size_(0),\n      modules_(),\n      memory_info_(),\n      handles_(),\n      is_64_bit_(false),\n      is_wow64_(false),\n      initialized_() {\n}\n\nProcessInfo::~ProcessInfo() {\n}\n\nbool ProcessInfo::Initialize(HANDLE process) {\n  INITIALIZATION_STATE_SET_INITIALIZING(initialized_);\n\n  process_ = process;\n\n  is_wow64_ = IsProcessWow64(process);\n\n  if (is_wow64_) {\n    // If it's WoW64, then it's 32-on-64.\n    is_64_bit_ = false;\n  } else {\n    // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to\n    // distinguish between these two cases.\n    SYSTEM_INFO system_info;\n    GetSystemInfo(&system_info);\n\n#if defined(ARCH_CPU_X86_FAMILY)\n    constexpr uint16_t kNative64BitArchitecture = PROCESSOR_ARCHITECTURE_AMD64;\n#elif defined(ARCH_CPU_ARM_FAMILY)\n    constexpr uint16_t kNative64BitArchitecture = PROCESSOR_ARCHITECTURE_ARM64;\n#endif\n\n    is_64_bit_ = system_info.wProcessorArchitecture == kNative64BitArchitecture;\n  }\n\n#if defined(ARCH_CPU_32_BITS)\n  if (is_64_bit_) {\n    LOG(ERROR) << \"Reading x64 process from x86 process not supported\";\n    return false;\n  }\n#endif  // ARCH_CPU_32_BITS\n\n#if defined(ARCH_CPU_64_BITS)\n  bool result = GetProcessBasicInformation<process_types::internal::Traits64>(\n      process, is_wow64_, this, &peb_address_, &peb_size_);\n#else\n  bool result = GetProcessBasicInformation<process_types::internal::Traits32>(\n      process, false, this, &peb_address_, &peb_size_);\n#endif  // ARCH_CPU_64_BITS\n\n  if (!result) {\n    LOG(ERROR) << \"GetProcessBasicInformation failed\";\n    return false;\n  }\n\n  result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>(\n                            process, peb_address_, this)\n                      : ReadProcessData<process_types::internal::Traits32>(\n                            process, peb_address_, this);\n  if (!result) {\n    LOG(ERROR) << \"ReadProcessData failed\";\n    return false;\n  }\n\n  if (!ReadMemoryInfo(process, is_64_bit_, this)) {\n    LOG(ERROR) << \"ReadMemoryInfo failed\";\n    return false;\n  }\n\n  INITIALIZATION_STATE_SET_VALID(initialized_);\n  return true;\n}\n\nbool ProcessInfo::Is64Bit() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return is_64_bit_;\n}\n\nbool ProcessInfo::IsWow64() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return is_wow64_;\n}\n\ncrashpad::ProcessID ProcessInfo::ProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return process_id_;\n}\n\ncrashpad::ProcessID ProcessInfo::ParentProcessID() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return inherited_from_process_id_;\n}\n\nbool ProcessInfo::CommandLine(std::wstring* command_line) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *command_line = command_line_;\n  return true;\n}\n\nvoid ProcessInfo::Peb(WinVMAddress* peb_address, WinVMSize* peb_size) const {\n  *peb_address = peb_address_;\n  *peb_size = peb_size_;\n}\n\nbool ProcessInfo::Modules(std::vector<Module>* modules) const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  *modules = modules_;\n  return true;\n}\n\nconst ProcessInfo::MemoryBasicInformation64Vector& ProcessInfo::MemoryInfo()\n    const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  return memory_info_;\n}\n\nstd::vector<CheckedRange<WinVMAddress, WinVMSize>>\nProcessInfo::GetReadableRanges(\n    const CheckedRange<WinVMAddress, WinVMSize>& range) const {\n  return GetReadableRangesOfMemoryMap(range, MemoryInfo());\n}\n\nbool ProcessInfo::LoggingRangeIsFullyReadable(\n    const CheckedRange<WinVMAddress, WinVMSize>& range) const {\n  const auto ranges = GetReadableRanges(range);\n  if (ranges.empty()) {\n    LOG(ERROR) << base::StringPrintf(\n        \"range at 0x%llx, size 0x%llx fully unreadable\",\n        range.base(),\n        range.size());\n    return false;\n  }\n\n  if (ranges.size() != 1 ||\n      ranges[0].base() != range.base() || ranges[0].size() != range.size()) {\n    LOG(ERROR) << base::StringPrintf(\n        \"range at 0x%llx, size 0x%llx partially unreadable\",\n        range.base(),\n        range.size());\n    return false;\n  }\n\n  return true;\n}\n\nconst std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() const {\n  INITIALIZATION_STATE_DCHECK_VALID(initialized_);\n  if (handles_.empty())\n    handles_ = BuildHandleVector(process_);\n  return handles_;\n}\n\nstd::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(\n    const CheckedRange<WinVMAddress, WinVMSize>& range,\n    const ProcessInfo::MemoryBasicInformation64Vector& memory_info) {\n  using Range = CheckedRange<WinVMAddress, WinVMSize>;\n\n  // Constructing Ranges and using OverlapsRange() is very, very slow in Debug\n  // builds, so do a manual check in this loop. The ranges are still validated\n  // by a CheckedRange before being returned.\n  WinVMAddress range_base = range.base();\n  WinVMAddress range_end = range.end();\n\n  // Find all the ranges that overlap the target range, maintaining their order.\n  ProcessInfo::MemoryBasicInformation64Vector overlapping;\n  const size_t size = memory_info.size();\n\n  // This loop is written in an ugly fashion to make Debug performance\n  // reasonable.\n  const MEMORY_BASIC_INFORMATION64* begin = &memory_info[0];\n  for (size_t i = 0; i < size; ++i) {\n    const MEMORY_BASIC_INFORMATION64& mi = *(begin + i);\n    static_assert(std::is_same<decltype(mi.BaseAddress), WinVMAddress>::value,\n                  \"expected range address to be WinVMAddress\");\n    static_assert(std::is_same<decltype(mi.RegionSize), WinVMSize>::value,\n                  \"expected range size to be WinVMSize\");\n    WinVMAddress mi_end = mi.BaseAddress + mi.RegionSize;\n    if (range_base < mi_end && mi.BaseAddress < range_end)\n      overlapping.push_back(mi);\n  }\n  if (overlapping.empty())\n    return std::vector<Range>();\n\n  // For the first and last, trim to the boundary of the incoming range.\n  MEMORY_BASIC_INFORMATION64& front = overlapping.front();\n  WinVMAddress original_front_base_address = front.BaseAddress;\n  front.BaseAddress = std::max(front.BaseAddress, range.base());\n  front.RegionSize =\n      (original_front_base_address + front.RegionSize) - front.BaseAddress;\n\n  MEMORY_BASIC_INFORMATION64& back = overlapping.back();\n  WinVMAddress back_end = back.BaseAddress + back.RegionSize;\n  back.RegionSize = std::min(range.end(), back_end) - back.BaseAddress;\n\n  // Discard all non-accessible.\n  overlapping.erase(std::remove_if(overlapping.begin(),\n                                   overlapping.end(),\n                                   [](const MEMORY_BASIC_INFORMATION64& mbi) {\n                                     return !RegionIsAccessible(mbi);\n                                   }),\n                    overlapping.end());\n  if (overlapping.empty())\n    return std::vector<Range>();\n\n  // Convert to return type.\n  std::vector<Range> as_ranges;\n  for (const auto& mi : overlapping) {\n    as_ranges.push_back(Range(mi.BaseAddress, mi.RegionSize));\n    DCHECK(as_ranges.back().IsValid());\n  }\n\n  // Coalesce remaining regions.\n  std::vector<Range> result;\n  result.push_back(as_ranges[0]);\n  for (size_t i = 1; i < as_ranges.size(); ++i) {\n    if (result.back().end() == as_ranges[i].base()) {\n      result.back().SetRange(result.back().base(),\n                             result.back().size() + as_ranges[i].size());\n    } else {\n      result.push_back(as_ranges[i]);\n    }\n    DCHECK(result.back().IsValid());\n  }\n\n  return result;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/process_info.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_PROCESS_INFO_H_\n#define CRASHPAD_UTIL_WIN_PROCESS_INFO_H_\n\n#include <windows.h>\n#include <sys/types.h>\n\n#include <string>\n#include <vector>\n\n#include \"util/misc/initialization_state_dcheck.h\"\n#include \"util/numeric/checked_range.h\"\n#include \"util/process/process_id.h\"\n#include \"util/stdlib/aligned_allocator.h\"\n#include \"util/win/address_types.h\"\n\nnamespace crashpad {\n\n//! \\brief Gathers information about a process given its `HANDLE`. This consists\n//!     primarily of information stored in the Process Environment Block.\nclass ProcessInfo {\n public:\n  //! \\brief The return type of MemoryInfo(), for convenience.\n  using MemoryBasicInformation64Vector =\n      AlignedVector<MEMORY_BASIC_INFORMATION64>;\n\n  //! \\brief Contains information about a module loaded into a process.\n  struct Module {\n    Module();\n    ~Module();\n\n    //! \\brief The pathname used to load the module from disk.\n    std::wstring name;\n\n    //! \\brief The base address of the loaded DLL.\n    WinVMAddress dll_base;\n\n    //! \\brief The size of the module.\n    WinVMSize size;\n\n    //! \\brief The module's timestamp.\n    time_t timestamp;\n  };\n\n  struct Handle {\n    Handle();\n    ~Handle();\n\n    //! \\brief A string representation of the handle's type.\n    std::wstring type_name;\n\n    //! \\brief The handle's value.\n    int handle;\n\n    //! \\brief The attributes for the handle, e.g. `OBJ_INHERIT`,\n    //!     `OBJ_CASE_INSENSITIVE`, etc.\n    uint32_t attributes;\n\n    //! \\brief The `ACCESS_MASK` for the handle in this process.\n    //!\n    //! See\n    //! https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/\n    //! for more information.\n    uint32_t granted_access;\n\n    //! \\brief The number of kernel references to the object that this handle\n    //!     refers to.\n    uint32_t pointer_count;\n\n    //! \\brief The number of open handles to the object that this handle refers\n    //!     to.\n    uint32_t handle_count;\n  };\n\n  ProcessInfo();\n\n  ProcessInfo(const ProcessInfo&) = delete;\n  ProcessInfo& operator=(const ProcessInfo&) = delete;\n\n  ~ProcessInfo();\n\n  //! \\brief Initializes this object with information about the given\n  //!     \\a process.\n  //!\n  //! This method must be called successfully prior to calling any other\n  //! method in this class. This method may only be called once.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Initialize(HANDLE process);\n\n  //! \\return `true` if the target process is a 64-bit process.\n  bool Is64Bit() const;\n\n  //! \\return `true` if the target process is running on the Win32-on-Win64\n  //!     subsystem.\n  bool IsWow64() const;\n\n  //! \\return The target process's process ID.\n  crashpad::ProcessID ProcessID() const;\n\n  //! \\return The target process's parent process ID.\n  crashpad::ProcessID ParentProcessID() const;\n\n  //! \\return The command line from the target process's Process Environment\n  //!     Block.\n  bool CommandLine(std::wstring* command_line) const;\n\n  //! \\brief Gets the address and size of the process's Process Environment\n  //!     Block.\n  //!\n  //! \\param[out] peb_address The address of the Process Environment Block.\n  //! \\param[out] peb_size The size of the Process Environment Block.\n  void Peb(WinVMAddress* peb_address, WinVMSize* peb_size) const;\n\n  //! \\brief Retrieves the modules loaded into the target process.\n  //!\n  //! The modules are enumerated in initialization order as detailed in the\n  //!     Process Environment Block. The main executable will always be the\n  //!     first element.\n  bool Modules(std::vector<Module>* modules) const;\n\n  //! \\brief Retrieves information about all pages mapped into the process.\n  const MemoryBasicInformation64Vector& MemoryInfo() const;\n\n  //! \\brief Given a range to be read from the target process, returns a vector\n  //!     of ranges, representing the readable portions of the original range.\n  //!\n  //! \\param[in] range The range being identified.\n  //!\n  //! \\return A vector of ranges corresponding to the portion of \\a range that\n  //!     is readable based on the memory map.\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRanges(\n      const CheckedRange<WinVMAddress, WinVMSize>& range) const;\n\n  //! \\brief Given a range in the target process, determines if the entire range\n  //!     is readable.\n  //!\n  //! \\param[in] range The range being inspected.\n  //!\n  //! \\return `true` if the range is fully readable, otherwise `false` with a\n  //!     message logged.\n  bool LoggingRangeIsFullyReadable(\n      const CheckedRange<WinVMAddress, WinVMSize>& range) const;\n\n  //! \\brief Retrieves information about open handles in the target process.\n  const std::vector<Handle>& Handles() const;\n\n private:\n  template <class Traits>\n  friend bool GetProcessBasicInformation(HANDLE process,\n                                         bool is_wow64,\n                                         ProcessInfo* process_info,\n                                         WinVMAddress* peb_address,\n                                         WinVMSize* peb_size);\n  template <class Traits>\n  friend bool ReadProcessData(HANDLE process,\n                              WinVMAddress peb_address_vmaddr,\n                              ProcessInfo* process_info);\n\n  friend bool ReadMemoryInfo(HANDLE process,\n                             bool is_64_bit,\n                             ProcessInfo* process_info);\n\n  // This function is best-effort under low memory conditions.\n  std::vector<Handle> BuildHandleVector(HANDLE process) const;\n\n  crashpad::ProcessID process_id_;\n  crashpad::ProcessID inherited_from_process_id_;\n  HANDLE process_;\n  std::wstring command_line_;\n  WinVMAddress peb_address_;\n  WinVMSize peb_size_;\n  std::vector<Module> modules_;\n\n  // memory_info_ is a MemoryBasicInformation64Vector instead of a\n  // std::vector<MEMORY_BASIC_INFORMATION64> because MEMORY_BASIC_INFORMATION64\n  // is declared with __declspec(align(16)), but std::vector<> does not maintain\n  // this alignment on 32-bit x86. clang-cl (but not MSVC cl) takes advantage of\n  // the presumed alignment and emits SSE instructions that require aligned\n  // storage. clang-cl should relax (unfortunately), but in the mean time, this\n  // provides aligned storage. See https://crbug.com/564691 and\n  // https://llvm.org/PR25779.\n  //\n  // TODO(mark): Remove this workaround when https://llvm.org/PR25779 is fixed\n  // and the fix is present in the clang-cl that compiles this code.\n  MemoryBasicInformation64Vector memory_info_;\n\n  // Handles() is logically const, but updates this member on first retrieval.\n  // See https://crashpad.chromium.org/bug/9.\n  mutable std::vector<Handle> handles_;\n\n  bool is_64_bit_;\n  bool is_wow64_;\n  InitializationStateDcheck initialized_;\n};\n\n//! \\brief Given a memory map of a process, and a range to be read from the\n//!     target process, returns a vector of ranges, representing the readable\n//!     portions of the original range.\n//!\n//! This is a free function for testing, but prefer\n//! ProcessInfo::GetReadableRanges().\nstd::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(\n    const CheckedRange<WinVMAddress, WinVMSize>& range,\n    const ProcessInfo::MemoryBasicInformation64Vector& memory_info);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_PROCESS_INFO_H_\n"
  },
  {
    "path": "util/win/process_info_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/process_info.h\"\n\n#include <dbghelp.h>\n#include <intrin.h>\n#include <wchar.h>\n\n#include <memory>\n\n#include \"base/containers/heap_array.h\"\n#include \"base/files/file_path.h\"\n#include \"base/logging.h\"\n#include \"base/strings/stringprintf.h\"\n#include \"base/strings/utf_string_conversions.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/scoped_temp_dir.h\"\n#include \"test/test_paths.h\"\n#include \"test/win/child_launcher.h\"\n#include \"util/file/file_io.h\"\n#include \"util/misc/from_pointer_cast.h\"\n#include \"util/misc/random_string.h\"\n#include \"util/misc/uuid.h\"\n#include \"util/win/command_line.h\"\n#include \"util/win/get_function.h\"\n#include \"util/win/handle.h\"\n#include \"util/win/scoped_handle.h\"\n#include \"util/win/scoped_registry_key.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nconstexpr wchar_t kNtdllName[] = L\"\\\\ntdll.dll\";\n\n#if !defined(ARCH_CPU_64_BITS)\nbool IsProcessWow64(HANDLE process_handle) {\n  static const auto is_wow64_process =\n      GET_FUNCTION(L\"kernel32.dll\", ::IsWow64Process);\n  if (!is_wow64_process)\n    return false;\n  BOOL is_wow64;\n  if (!is_wow64_process(process_handle, &is_wow64)) {\n    PLOG(ERROR) << \"IsWow64Process\";\n    return false;\n  }\n  return !!is_wow64;\n}\n#endif\n\nvoid VerifyAddressInInCodePage(const ProcessInfo& process_info,\n                               WinVMAddress code_address) {\n  // Make sure the child code address is an code page address with the right\n  // information.\n  const ProcessInfo::MemoryBasicInformation64Vector& memory_info =\n      process_info.MemoryInfo();\n  bool found_region = false;\n  for (const auto& mi : memory_info) {\n    if (mi.BaseAddress <= code_address &&\n        mi.BaseAddress + mi.RegionSize > code_address) {\n      EXPECT_EQ(mi.State, static_cast<DWORD>(MEM_COMMIT));\n      EXPECT_EQ(mi.Protect, static_cast<DWORD>(PAGE_EXECUTE_READ));\n      EXPECT_EQ(mi.Type, static_cast<DWORD>(MEM_IMAGE));\n      EXPECT_FALSE(found_region);\n      found_region = true;\n    }\n  }\n  EXPECT_TRUE(found_region);\n}\n\nTEST(ProcessInfo, Self) {\n  ProcessInfo process_info;\n  ASSERT_TRUE(process_info.Initialize(GetCurrentProcess()));\n  EXPECT_EQ(process_info.ProcessID(), GetCurrentProcessId());\n  EXPECT_GT(process_info.ParentProcessID(), 0u);\n\n#if defined(ARCH_CPU_64_BITS)\n  EXPECT_TRUE(process_info.Is64Bit());\n  EXPECT_FALSE(process_info.IsWow64());\n#else\n  EXPECT_FALSE(process_info.Is64Bit());\n  if (IsProcessWow64(GetCurrentProcess()))\n    EXPECT_TRUE(process_info.IsWow64());\n  else\n    EXPECT_FALSE(process_info.IsWow64());\n#endif\n\n  std::wstring command_line;\n  EXPECT_TRUE(process_info.CommandLine(&command_line));\n  EXPECT_EQ(command_line, std::wstring(GetCommandLine()));\n\n  std::vector<ProcessInfo::Module> modules;\n  EXPECT_TRUE(process_info.Modules(&modules));\n  ASSERT_GE(modules.size(), 2u);\n  std::wstring self_name =\n      std::wstring(1, '\\\\') +\n      TestPaths::ExpectedExecutableBasename(L\"crashpad_util_test\").value();\n  ASSERT_GE(modules[0].name.size(), self_name.size());\n  EXPECT_EQ(modules[0].name.substr(modules[0].name.size() - self_name.size()),\n            self_name);\n  ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));\n  EXPECT_EQ(modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)),\n            kNtdllName);\n\n  EXPECT_EQ(modules[0].dll_base,\n            reinterpret_cast<uintptr_t>(GetModuleHandle(nullptr)));\n  EXPECT_EQ(modules[1].dll_base,\n            reinterpret_cast<uintptr_t>(GetModuleHandle(L\"ntdll.dll\")));\n\n  EXPECT_GT(modules[0].size, 0u);\n  EXPECT_GT(modules[1].size, 0u);\n\n  EXPECT_EQ(modules[0].timestamp,\n            GetTimestampForLoadedLibrary(GetModuleHandle(nullptr)));\n  // System modules are forced to particular stamps and the file header values\n  // don't match the on-disk times. Just make sure we got some data here.\n  EXPECT_GT(modules[1].timestamp, 0);\n\n  // Find something we know is a code address and confirm expected memory\n  // information settings.\n  VerifyAddressInInCodePage(process_info,\n                            FromPointerCast<WinVMAddress>(_ReturnAddress()));\n}\n\nvoid TestOtherProcess(TestPaths::Architecture architecture) {\n  ProcessInfo process_info;\n\n  UUID done_uuid;\n  done_uuid.InitializeWithNew();\n\n  ScopedKernelHANDLE done(\n      CreateEvent(nullptr, true, false, done_uuid.ToWString().c_str()));\n  ASSERT_TRUE(done.get()) << ErrorMessage(\"CreateEvent\");\n\n  base::FilePath child_test_executable =\n      TestPaths::BuildArtifact(L\"util\",\n                               L\"process_info_test_child\",\n                               TestPaths::FileType::kExecutable,\n                               architecture);\n  std::wstring args;\n  AppendCommandLineArgument(done_uuid.ToWString(), &args);\n\n  ChildLauncher child(child_test_executable, args);\n  ASSERT_NO_FATAL_FAILURE(child.Start());\n\n  // The child sends us a code address we can look up in the memory map.\n  WinVMAddress code_address;\n  CheckedReadFileExactly(\n      child.stdout_read_handle(), &code_address, sizeof(code_address));\n\n  ASSERT_TRUE(process_info.Initialize(child.process_handle()));\n\n  // Tell the test it's OK to shut down now that we've read our data.\n  EXPECT_TRUE(SetEvent(done.get())) << ErrorMessage(\"SetEvent\");\n\n  EXPECT_EQ(child.WaitForExit(), 0u);\n\n  std::vector<ProcessInfo::Module> modules;\n  EXPECT_TRUE(process_info.Modules(&modules));\n  ASSERT_GE(modules.size(), 3u);\n  std::wstring child_name = L\"\\\\crashpad_util_test_process_info_test_child.exe\";\n  ASSERT_GE(modules[0].name.size(), child_name.size());\n  EXPECT_EQ(modules[0].name.substr(modules[0].name.size() - child_name.size()),\n            child_name);\n  ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));\n  EXPECT_EQ(modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)),\n            kNtdllName);\n  // lz32.dll is an uncommonly-used-but-always-available module that the test\n  // binary manually loads.\n  static constexpr wchar_t kLz32dllName[] = L\"\\\\lz32.dll\";\n  auto& lz32 = modules[modules.size() - 2];\n  ASSERT_GE(lz32.name.size(), wcslen(kLz32dllName));\n  EXPECT_EQ(lz32.name.substr(lz32.name.size() - wcslen(kLz32dllName)),\n            kLz32dllName);\n\n  // Note that the test code corrupts the PEB MemoryOrder list, whereas\n  // ProcessInfo::Modules() retrieves the module names via the PEB LoadOrder\n  // list. These are expected to point to the same strings, but theoretically\n  // could be separate.\n  auto& corrupted = modules.back();\n  EXPECT_EQ(corrupted.name, L\"???\");\n\n  VerifyAddressInInCodePage(process_info, code_address);\n}\n\nTEST(ProcessInfo, OtherProcess) {\n  TestOtherProcess(TestPaths::Architecture::kDefault);\n}\n\n#if defined(ARCH_CPU_64_BITS)\nTEST(ProcessInfo, OtherProcessWOW64) {\n  if (!TestPaths::Has32BitBuildArtifacts()) {\n    GTEST_SKIP();\n  }\n\n  TestOtherProcess(TestPaths::Architecture::k32Bit);\n}\n#endif  // ARCH_CPU_64_BITS\n\nTEST(ProcessInfo, AccessibleRangesNone) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_FREE;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4),\n                                   memory_info);\n\n  EXPECT_TRUE(result.empty());\n}\n\nTEST(ProcessInfo, AccessibleRangesOneInside) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(2, 4),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 2u);\n  EXPECT_EQ(result[0].size(), 4u);\n}\n\nTEST(ProcessInfo, AccessibleRangesOneTruncatedSize) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 20;\n  mbi.State = MEM_FREE;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 5u);\n  EXPECT_EQ(result[0].size(), 5u);\n}\n\nTEST(ProcessInfo, AccessibleRangesOneMovedStart) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_FREE;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 20;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 10u);\n  EXPECT_EQ(result[0].size(), 5u);\n}\n\nTEST(ProcessInfo, ReserveIsInaccessible) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_RESERVE;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 20;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 10u);\n  EXPECT_EQ(result[0].size(), 5u);\n}\n\nTEST(ProcessInfo, PageGuardIsInaccessible) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_COMMIT;\n  mbi.Protect = PAGE_GUARD;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 20;\n  mbi.State = MEM_COMMIT;\n  mbi.Protect = 0;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 10u);\n  EXPECT_EQ(result[0].size(), 5u);\n}\n\nTEST(ProcessInfo, PageNoAccessIsInaccessible) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_COMMIT;\n  mbi.Protect = PAGE_NOACCESS;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 20;\n  mbi.State = MEM_COMMIT;\n  mbi.Protect = 0;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 10u);\n  EXPECT_EQ(result[0].size(), 5u);\n}\n\nTEST(ProcessInfo, AccessibleRangesCoalesced) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_FREE;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 2;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 12;\n  mbi.RegionSize = 5;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(11, 4),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 11u);\n  EXPECT_EQ(result[0].size(), 4u);\n}\n\nTEST(ProcessInfo, AccessibleRangesMiddleUnavailable) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 0;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 5;\n  mbi.State = MEM_FREE;\n  memory_info.push_back(mbi);\n\n  mbi.BaseAddress = 15;\n  mbi.RegionSize = 100;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 45),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 2u);\n  EXPECT_EQ(result[0].base(), 5u);\n  EXPECT_EQ(result[0].size(), 5u);\n  EXPECT_EQ(result[1].base(), 15u);\n  EXPECT_EQ(result[1].size(), 35u);\n}\n\nTEST(ProcessInfo, RequestedBeforeMap) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(CheckedRange<WinVMAddress, WinVMSize>(5, 10),\n                                   memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 10u);\n  EXPECT_EQ(result[0].size(), 5u);\n}\n\nTEST(ProcessInfo, RequestedAfterMap) {\n  ProcessInfo::MemoryBasicInformation64Vector memory_info;\n  MEMORY_BASIC_INFORMATION64 mbi = {0};\n\n  mbi.BaseAddress = 10;\n  mbi.RegionSize = 10;\n  mbi.State = MEM_COMMIT;\n  memory_info.push_back(mbi);\n\n  std::vector<CheckedRange<WinVMAddress, WinVMSize>> result =\n      GetReadableRangesOfMemoryMap(\n          CheckedRange<WinVMAddress, WinVMSize>(15, 100), memory_info);\n\n  ASSERT_EQ(result.size(), 1u);\n  EXPECT_EQ(result[0].base(), 15u);\n  EXPECT_EQ(result[0].size(), 5u);\n}\n\nTEST(ProcessInfo, ReadableRanges) {\n  SYSTEM_INFO system_info;\n  GetSystemInfo(&system_info);\n\n  const size_t kBlockSize = system_info.dwPageSize;\n\n  // Allocate 6 pages, and then commit the second, fourth, and fifth, and mark\n  // two as committed, but PAGE_NOACCESS, so we have a setup like this:\n  // 0       1       2       3       4       5\n  // +-----------------------------------------------+\n  // | ????? |       | xxxxx |       |       | ????? |\n  // +-----------------------------------------------+\n  void* reserve_region =\n      VirtualAlloc(nullptr, kBlockSize * 6, MEM_RESERVE, PAGE_READWRITE);\n  ASSERT_TRUE(reserve_region);\n  uintptr_t reserved_as_int = reinterpret_cast<uintptr_t>(reserve_region);\n  void* readable1 =\n      VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + kBlockSize),\n                   kBlockSize,\n                   MEM_COMMIT,\n                   PAGE_READWRITE);\n  ASSERT_TRUE(readable1);\n  void* readable2 =\n      VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + (kBlockSize * 3)),\n                   kBlockSize * 2,\n                   MEM_COMMIT,\n                   PAGE_READWRITE);\n  ASSERT_TRUE(readable2);\n\n  void* no_access =\n      VirtualAlloc(reinterpret_cast<void*>(reserved_as_int + (kBlockSize * 2)),\n                   kBlockSize,\n                   MEM_COMMIT,\n                   PAGE_NOACCESS);\n  ASSERT_TRUE(no_access);\n\n  HANDLE current_process = GetCurrentProcess();\n  ProcessInfo info;\n  info.Initialize(current_process);\n  auto ranges = info.GetReadableRanges(\n      CheckedRange<WinVMAddress, WinVMSize>(reserved_as_int, kBlockSize * 6));\n\n  ASSERT_EQ(ranges.size(), 2u);\n  EXPECT_EQ(ranges[0].base(), reserved_as_int + kBlockSize);\n  EXPECT_EQ(ranges[0].size(), kBlockSize);\n  EXPECT_EQ(ranges[1].base(), reserved_as_int + (kBlockSize * 3));\n  EXPECT_EQ(ranges[1].size(), kBlockSize * 2);\n\n  // Also make sure what we think we can read corresponds with what we can\n  // actually read.\n  auto into = base::HeapArray<unsigned char>::Uninit(kBlockSize * 6);\n  SIZE_T bytes_read;\n\n  EXPECT_TRUE(ReadProcessMemory(\n      current_process, readable1, into.data(), kBlockSize, &bytes_read));\n  EXPECT_EQ(bytes_read, kBlockSize);\n\n  EXPECT_TRUE(ReadProcessMemory(\n      current_process, readable2, into.data(), kBlockSize * 2, &bytes_read));\n  EXPECT_EQ(bytes_read, kBlockSize * 2);\n\n  EXPECT_FALSE(ReadProcessMemory(\n      current_process, no_access, into.data(), kBlockSize, &bytes_read));\n  EXPECT_FALSE(ReadProcessMemory(\n      current_process, reserve_region, into.data(), kBlockSize, &bytes_read));\n  EXPECT_FALSE(ReadProcessMemory(\n      current_process, reserve_region, into.data(), into.size(), &bytes_read));\n}\n\nTEST(ProcessInfo, Handles) {\n  ScopedTempDir temp_dir;\n\n  ScopedFileHandle file(LoggingOpenFileForWrite(\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"test_file\")),\n      FileWriteMode::kTruncateOrCreate,\n      FilePermissions::kWorldReadable));\n  ASSERT_TRUE(file.is_valid());\n\n  SECURITY_ATTRIBUTES security_attributes = {0};\n  security_attributes.nLength = sizeof(security_attributes);\n  security_attributes.bInheritHandle = true;\n  ScopedFileHandle inherited_file(CreateFile(\n      temp_dir.path().Append(FILE_PATH_LITERAL(\"inheritable\")).value().c_str(),\n      GENERIC_WRITE,\n      0,\n      &security_attributes,\n      CREATE_NEW,\n      FILE_ATTRIBUTE_NORMAL,\n      nullptr));\n  ASSERT_TRUE(inherited_file.is_valid());\n\n  HKEY key;\n  ASSERT_EQ(RegOpenKeyEx(\n                HKEY_CURRENT_USER, L\"SOFTWARE\\\\Microsoft\", 0, KEY_READ, &key),\n            ERROR_SUCCESS);\n  ScopedRegistryKey scoped_key(key);\n  ASSERT_TRUE(scoped_key.is_valid());\n\n  std::wstring mapping_name =\n      base::UTF8ToWide(base::StringPrintf(\"Local\\\\test_mapping_%lu_%s\",\n                                          GetCurrentProcessId(),\n                                          RandomString().c_str()));\n  ScopedKernelHANDLE mapping(CreateFileMapping(INVALID_HANDLE_VALUE,\n                                               nullptr,\n                                               PAGE_READWRITE,\n                                               0,\n                                               1024,\n                                               mapping_name.c_str()));\n  ASSERT_TRUE(mapping.is_valid()) << ErrorMessage(\"CreateFileMapping\");\n\n  ProcessInfo info;\n  info.Initialize(GetCurrentProcess());\n  bool found_file_handle = false;\n  bool found_inherited_file_handle = false;\n  bool found_key_handle = false;\n  bool found_mapping_handle = false;\n  for (auto handle : info.Handles()) {\n    if (handle.handle == HandleToInt(file.get())) {\n      EXPECT_FALSE(found_file_handle);\n      found_file_handle = true;\n      EXPECT_EQ(handle.type_name, L\"File\");\n      EXPECT_EQ(handle.handle_count, 1u);\n      EXPECT_NE(handle.pointer_count, 0u);\n      EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL,\n                static_cast<uint32_t>(STANDARD_RIGHTS_READ |\n                                      STANDARD_RIGHTS_WRITE | SYNCHRONIZE));\n      EXPECT_EQ(handle.attributes, 0u);\n    }\n    if (handle.handle == HandleToInt(inherited_file.get())) {\n      EXPECT_FALSE(found_inherited_file_handle);\n      found_inherited_file_handle = true;\n      EXPECT_EQ(handle.type_name, L\"File\");\n      EXPECT_EQ(handle.handle_count, 1u);\n      EXPECT_NE(handle.pointer_count, 0u);\n      EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL,\n                static_cast<uint32_t>(STANDARD_RIGHTS_READ |\n                                      STANDARD_RIGHTS_WRITE | SYNCHRONIZE));\n\n      // OBJ_INHERIT from ntdef.h, but including that conflicts with other\n      // headers.\n      constexpr uint32_t kObjInherit = 0x2;\n      EXPECT_EQ(handle.attributes, kObjInherit);\n    }\n    if (handle.handle == HandleToInt(scoped_key.get())) {\n      EXPECT_FALSE(found_key_handle);\n      found_key_handle = true;\n      EXPECT_EQ(handle.type_name, L\"Key\");\n      EXPECT_EQ(handle.handle_count, 1u);\n      EXPECT_NE(handle.pointer_count, 0u);\n      EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL,\n                static_cast<uint32_t>(STANDARD_RIGHTS_READ));\n      EXPECT_EQ(handle.attributes, 0u);\n    }\n    if (handle.handle == HandleToInt(mapping.get())) {\n      EXPECT_FALSE(found_mapping_handle);\n      found_mapping_handle = true;\n      EXPECT_EQ(handle.type_name, L\"Section\");\n      EXPECT_EQ(handle.handle_count, 1u);\n      EXPECT_NE(handle.pointer_count, 0u);\n      EXPECT_EQ(handle.granted_access & STANDARD_RIGHTS_ALL,\n                static_cast<uint32_t>(DELETE | READ_CONTROL | WRITE_DAC |\n                                      WRITE_OWNER | STANDARD_RIGHTS_READ |\n                                      STANDARD_RIGHTS_WRITE));\n      EXPECT_EQ(handle.attributes, 0u);\n    }\n  }\n  EXPECT_TRUE(found_file_handle);\n  EXPECT_TRUE(found_inherited_file_handle);\n  EXPECT_TRUE(found_key_handle);\n  EXPECT_TRUE(found_mapping_handle);\n}\n\nTEST(ProcessInfo, OutOfRangeCheck) {\n  auto safe_memory = base::HeapArray<char>::Uninit(12345);\n\n  ProcessInfo info;\n  info.Initialize(GetCurrentProcess());\n\n  EXPECT_TRUE(\n      info.LoggingRangeIsFullyReadable(CheckedRange<WinVMAddress, WinVMSize>(\n          FromPointerCast<WinVMAddress>(safe_memory.data()),\n          safe_memory.size())));\n  EXPECT_FALSE(info.LoggingRangeIsFullyReadable(\n      CheckedRange<WinVMAddress, WinVMSize>(0, 1024)));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/process_info_test_child.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <intrin.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <wchar.h>\n#include <windows.h>\n#include <winternl.h>\n\nnamespace {\n\nbool UnicodeStringEndsWithCaseInsensitive(const UNICODE_STRING& us,\n                                          const wchar_t* ends_with) {\n  const size_t len = wcslen(ends_with);\n  // Recall that UNICODE_STRING.Length is in bytes, not characters.\n  const size_t us_len_in_chars = us.Length / sizeof(wchar_t);\n  if (us_len_in_chars < len)\n    return false;\n  return _wcsnicmp(&us.Buffer[us_len_in_chars - len], ends_with, len) == 0;\n}\n\n}  // namespace\n\n// A simple binary to be loaded and inspected by ProcessInfo.\nint wmain(int argc, wchar_t** argv) {\n  if (argc != 2)\n    abort();\n\n  // Get a handle to the event we use to communicate with our parent.\n  HANDLE done_event = CreateEvent(nullptr, true, false, argv[1]);\n  if (!done_event)\n    abort();\n\n  // Load an unusual module (that we don't depend upon) so we can do an\n  // existence check. It's also important that these DLLs don't depend on\n  // any other DLLs, otherwise there'll be additional modules in the list, which\n  // the test expects not to be there.\n  if (!LoadLibrary(L\"lz32.dll\"))\n    abort();\n\n  // Load another unusual module so we can destroy its FullDllName field in the\n  // PEB to test corrupted name reads.\n  static constexpr wchar_t kCorruptableDll[] = L\"kbdurdu.dll\";\n  if (!LoadLibrary(kCorruptableDll))\n    abort();\n\n  // Find and corrupt the buffer pointer to the name in the PEB.\n  HINSTANCE ntdll = GetModuleHandle(L\"ntdll.dll\");\n  decltype(NtQueryInformationProcess)* nt_query_information_process =\n      reinterpret_cast<decltype(NtQueryInformationProcess)*>(\n          GetProcAddress(ntdll, \"NtQueryInformationProcess\"));\n  if (!nt_query_information_process)\n    abort();\n\n  PROCESS_BASIC_INFORMATION pbi;\n  if (nt_query_information_process(GetCurrentProcess(),\n                                   ProcessBasicInformation,\n                                   &pbi,\n                                   sizeof(pbi),\n                                   nullptr) < 0) {\n    abort();\n  }\n\n  PEB_LDR_DATA* ldr = pbi.PebBaseAddress->Ldr;\n  LIST_ENTRY* head = &ldr->InMemoryOrderModuleList;\n  LIST_ENTRY* next = head->Flink;\n  while (next != head) {\n    LDR_DATA_TABLE_ENTRY* entry =\n        CONTAINING_RECORD(next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);\n    if (UnicodeStringEndsWithCaseInsensitive(entry->FullDllName,\n                                             kCorruptableDll)) {\n      // Corrupt the pointer to the name.\n      entry->FullDllName.Buffer = 0;\n    }\n    next = next->Flink;\n  }\n\n  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);\n  if (out == INVALID_HANDLE_VALUE)\n    abort();\n  // We just want any valid address that's known to be code.\n  uint64_t code_address = reinterpret_cast<uint64_t>(_ReturnAddress());\n  DWORD bytes_written;\n  if (!WriteFile(\n          out, &code_address, sizeof(code_address), &bytes_written, nullptr) ||\n      bytes_written != sizeof(code_address)) {\n    abort();\n  }\n\n  if (WaitForSingleObject(done_event, INFINITE) != WAIT_OBJECT_0)\n    abort();\n\n  CloseHandle(done_event);\n\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "util/win/process_structs.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_\n#define CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_\n\n#include <windows.h>\n\nnamespace crashpad {\nnamespace process_types {\n\nnamespace internal {\n\nstruct Traits32 {\n  using Pad = DWORD;\n  using UnsignedIntegral = DWORD;\n  using Pointer = DWORD;\n};\n\nstruct Traits64 {\n  using Pad = DWORD64;\n  using UnsignedIntegral = DWORD64;\n  using Pointer = DWORD64;\n};\n\n}  // namespace internal\n\n//! \\{\n\n//! \\brief Selected structures from winternl.h, ntddk.h, and `dt ntdll!xxx`,\n//!     customized to have both x86 and x64 sizes available.\n//!\n//! The structure and field names follow the Windows names for clarity. We do,\n//! however, use plain integral types rather than pointer types. This is both\n//! easier to define, and avoids accidentally treating them as pointers into the\n//! current address space.\n//!\n//! The templates below should be instantiated with either internal::Traits32\n//! for structures targeting x86, or internal::Traits64 for x64.\n\n// We set packing to 1 so that we can explicitly control the layout to make it\n// match the OS defined structures.\n#pragma pack(push, 1)\n\ntemplate <class Traits>\nstruct PROCESS_BASIC_INFORMATION {\n  union {\n    DWORD ExitStatus;\n    typename Traits::Pad padding_for_x64_0;\n  };\n  typename Traits::Pointer PebBaseAddress;\n  typename Traits::UnsignedIntegral AffinityMask;\n  union {\n    DWORD BasePriority;\n    typename Traits::Pad padding_for_x64_1;\n  };\n  typename Traits::UnsignedIntegral UniqueProcessId;\n  typename Traits::UnsignedIntegral InheritedFromUniqueProcessId;\n};\n\ntemplate <class Traits>\nstruct LIST_ENTRY {\n  typename Traits::Pointer Flink;\n  typename Traits::Pointer Blink;\n};\n\ntemplate <class Traits>\nstruct UNICODE_STRING {\n  union {\n    struct {\n      USHORT Length;\n      USHORT MaximumLength;\n    };\n    typename Traits::Pad padding_for_x64;\n  };\n  typename Traits::Pointer Buffer;\n};\n\ntemplate <class Traits>\nstruct PEB_LDR_DATA {\n  ULONG Length;\n  DWORD Initialized;\n  typename Traits::Pointer SsHandle;\n  LIST_ENTRY<Traits> InLoadOrderModuleList;\n  LIST_ENTRY<Traits> InMemoryOrderModuleList;\n  LIST_ENTRY<Traits> InInitializationOrderModuleList;\n};\n\ntemplate <class Traits>\nstruct LDR_DATA_TABLE_ENTRY {\n  LIST_ENTRY<Traits> InLoadOrderLinks;\n  LIST_ENTRY<Traits> InMemoryOrderLinks;\n  LIST_ENTRY<Traits> InInitializationOrderLinks;\n  typename Traits::Pointer DllBase;\n  typename Traits::Pointer EntryPoint;\n  union {\n    ULONG SizeOfImage;\n    typename Traits::Pad padding_for_x64;\n  };\n  UNICODE_STRING<Traits> FullDllName;\n  UNICODE_STRING<Traits> BaseDllName;\n  ULONG Flags;\n  USHORT ObsoleteLoadCount;\n  USHORT TlsIndex;\n  LIST_ENTRY<Traits> HashLinks;\n  ULONG TimeDateStamp;\n};\n\ntemplate <class Traits>\nstruct CURDIR {\n  UNICODE_STRING<Traits> DosPath;\n  typename Traits::Pointer Handle;\n};\n\ntemplate <class Traits>\nstruct STRING {\n  union {\n    struct {\n      USHORT Length;\n      USHORT MaximumLength;\n    };\n    typename Traits::Pad padding_for_x64;\n  };\n  typename Traits::Pointer Buffer;\n};\n\ntemplate <class Traits>\nstruct RTL_DRIVE_LETTER_CURDIR {\n  WORD Flags;\n  WORD Length;\n  DWORD TimeStamp;\n  STRING<Traits> DosPath;\n};\n\ntemplate <class Traits>\nstruct RTL_USER_PROCESS_PARAMETERS {\n  DWORD MaximumLength;\n  DWORD Length;\n  DWORD Flags;\n  DWORD DebugFlags;\n  typename Traits::Pointer ConsoleHandle;\n  union {\n    DWORD ConsoleFlags;\n    typename Traits::Pad padding_for_x64_0;\n  };\n  typename Traits::Pointer StandardInput;\n  typename Traits::Pointer StandardOutput;\n  typename Traits::Pointer StandardError;\n  CURDIR<Traits> CurrentDirectory;\n  UNICODE_STRING<Traits> DllPath;\n  UNICODE_STRING<Traits> ImagePathName;\n  UNICODE_STRING<Traits> CommandLine;\n  typename Traits::Pointer Environment;\n  DWORD StartingX;\n  DWORD StartingY;\n  DWORD CountX;\n  DWORD CountY;\n  DWORD CountCharsX;\n  DWORD CountCharsY;\n  DWORD FillAttribute;\n  DWORD WindowFlags;\n  union {\n    DWORD ShowWindowFlags;\n    typename Traits::Pad padding_for_x64_1;\n  };\n  UNICODE_STRING<Traits> WindowTitle;\n  UNICODE_STRING<Traits> DesktopInfo;\n  UNICODE_STRING<Traits> ShellInfo;\n  UNICODE_STRING<Traits> RuntimeData;\n  RTL_DRIVE_LETTER_CURDIR<Traits> CurrentDirectores[32];  // sic.\n  ULONG EnvironmentSize;\n};\n\ntemplate <class T>\nstruct GdiHandleBufferCountForBitness;\n\ntemplate <>\nstruct GdiHandleBufferCountForBitness<internal::Traits32> {\n  enum { value = 34 };\n};\ntemplate <>\nstruct GdiHandleBufferCountForBitness<internal::Traits64> {\n  enum { value = 60 };\n};\n\ntemplate <class Traits>\nstruct PEB {\n  union {\n    struct {\n      BYTE InheritedAddressSpace;\n      BYTE ReadImageFileExecOptions;\n      BYTE BeingDebugged;\n      BYTE BitField;\n    };\n    typename Traits::Pad padding_for_x64_0;\n  };\n  typename Traits::Pointer Mutant;\n  typename Traits::Pointer ImageBaseAddress;\n  typename Traits::Pointer Ldr;\n  typename Traits::Pointer ProcessParameters;\n  typename Traits::Pointer SubSystemData;\n  typename Traits::Pointer ProcessHeap;\n  typename Traits::Pointer FastPebLock;\n  typename Traits::Pointer AtlThunkSListPtr;\n  typename Traits::Pointer IFEOKey;\n  union {\n    DWORD CrossProcessFlags;\n    typename Traits::Pad padding_for_x64_1;\n  };\n  typename Traits::Pointer KernelCallbackTable;\n  DWORD SystemReserved;\n  DWORD AtlThunkSListPtr32;\n  typename Traits::Pointer ApiSetMap;\n  union {\n    DWORD TlsExpansionCounter;\n    typename Traits::Pad padding_for_x64_2;\n  };\n  typename Traits::Pointer TlsBitmap;\n  DWORD TlsBitmapBits[2];\n  typename Traits::Pointer ReadOnlySharedMemoryBase;\n  typename Traits::Pointer SparePvoid0;\n  typename Traits::Pointer ReadOnlyStaticServerData;\n  typename Traits::Pointer AnsiCodePageData;\n  typename Traits::Pointer OemCodePageData;\n  typename Traits::Pointer UnicodeCaseTableData;\n  DWORD NumberOfProcessors;\n  DWORD NtGlobalFlag;\n  DWORD alignment_for_x86;\n  LARGE_INTEGER CriticalSectionTimeout;\n  typename Traits::UnsignedIntegral HeapSegmentReserve;\n  typename Traits::UnsignedIntegral HeapSegmentCommit;\n  typename Traits::UnsignedIntegral HeapDeCommitTotalFreeThreshold;\n  typename Traits::UnsignedIntegral HeapDeCommitFreeBlockThreshold;\n  DWORD NumberOfHeaps;\n  DWORD MaximumNumberOfHeaps;\n  typename Traits::Pointer ProcessHeaps;\n  typename Traits::Pointer GdiSharedHandleTable;\n  typename Traits::Pointer ProcessStarterHelper;\n  DWORD GdiDCAttributeList;\n  typename Traits::Pointer LoaderLock;\n  DWORD OSMajorVersion;\n  DWORD OSMinorVersion;\n  WORD OSBuildNumber;\n  WORD OSCSDVersion;\n  DWORD OSPlatformId;\n  DWORD ImageSubsystem;\n  DWORD ImageSubsystemMajorVersion;\n  union {\n    DWORD ImageSubsystemMinorVersion;\n    typename Traits::Pad padding_for_x64_3;\n  };\n  typename Traits::UnsignedIntegral ActiveProcessAffinityMask;\n  DWORD GdiHandleBuffer[GdiHandleBufferCountForBitness<Traits>::value];\n  typename Traits::Pointer PostProcessInitRoutine;\n  typename Traits::Pointer TlsExpansionBitmap;\n  DWORD TlsExpansionBitmapBits[32];\n  union {\n    DWORD SessionId;\n    typename Traits::Pad padding_for_x64_4;\n  };\n  ULARGE_INTEGER AppCompatFlags;\n  ULARGE_INTEGER AppCompatFlagsUser;\n  typename Traits::Pointer pShimData;\n  typename Traits::Pointer AppCompatInfo;\n  UNICODE_STRING<Traits> CSDVersion;\n  typename Traits::Pointer ActivationContextData;\n  typename Traits::Pointer ProcessAssemblyStorageMap;\n  typename Traits::Pointer SystemDefaultActivationContextData;\n  typename Traits::Pointer SystemAssemblyStorageMap;\n  typename Traits::UnsignedIntegral MinimumStackCommit;\n  typename Traits::Pointer FlsCallback;\n  LIST_ENTRY<Traits> FlsListHead;\n  typename Traits::Pointer FlsBitmap;\n  DWORD FlsBitmapBits[4];\n  DWORD FlsHighIndex;\n};\n\ntemplate <class Traits>\nstruct NT_TIB {\n  union {\n    // See https://msdn.microsoft.com/library/dn424783.aspx.\n    typename Traits::Pointer Wow64Teb;\n    struct {\n      typename Traits::Pointer ExceptionList;\n      typename Traits::Pointer StackBase;\n      typename Traits::Pointer StackLimit;\n      typename Traits::Pointer SubSystemTib;\n      union {\n        typename Traits::Pointer FiberData;\n        BYTE Version[4];\n      };\n      typename Traits::Pointer ArbitraryUserPointer;\n      typename Traits::Pointer Self;\n    };\n  };\n};\n\n// See https://msdn.microsoft.com/library/gg750647.aspx.\ntemplate <class Traits>\nstruct CLIENT_ID {\n  typename Traits::Pointer UniqueProcess;\n  typename Traits::Pointer UniqueThread;\n};\n\n// This is a partial definition of the TEB, as we do not currently use many\n// fields of it. See https://nirsoft.net/kernel_struct/vista/TEB.html, and the\n// (arch-specific) definition of _TEB in winternl.h.\ntemplate <class Traits>\nstruct TEB {\n  NT_TIB<Traits> NtTib;\n  typename Traits::Pointer EnvironmentPointer;\n  CLIENT_ID<Traits> ClientId;\n  typename Traits::Pointer ActiveRpcHandle;\n  typename Traits::Pointer ThreadLocalStoragePointer;\n  typename Traits::Pointer ProcessEnvironmentBlock;\n  typename Traits::Pointer RemainderOfReserved2[399];\n  BYTE Reserved3[1952];\n  typename Traits::Pointer TlsSlots[64];\n  BYTE Reserved4[8];\n  typename Traits::Pointer Reserved5[26];\n  typename Traits::Pointer ReservedForOle;\n  typename Traits::Pointer Reserved6[4];\n  typename Traits::Pointer TlsExpansionSlots;\n};\n\n// See https://msdn.microsoft.com/library/gg750724.aspx.\ntemplate <class Traits>\nstruct SYSTEM_THREAD_INFORMATION {\n  union {\n    struct {\n      LARGE_INTEGER KernelTime;\n      LARGE_INTEGER UserTime;\n      LARGE_INTEGER CreateTime;\n      union {\n        ULONG WaitTime;\n        typename Traits::Pad padding_for_x64_0;\n      };\n      typename Traits::Pointer StartAddress;\n      CLIENT_ID<Traits> ClientId;\n      LONG Priority;\n      LONG BasePriority;\n      ULONG ContextSwitches;\n      ULONG ThreadState;\n      union {\n        ULONG WaitReason;\n        typename Traits::Pad padding_for_x64_1;\n      };\n    };\n    LARGE_INTEGER alignment_for_x86[8];\n  };\n};\n\n// There's an extra field in the x64 VM_COUNTERS (or maybe it's VM_COUNTERS_EX,\n// it's not clear), so we just make separate specializations for 32/64.\ntemplate <class Traits>\nstruct VM_COUNTERS {};\n\ntemplate <>\nstruct VM_COUNTERS<internal::Traits32> {\n  SIZE_T PeakVirtualSize;\n  SIZE_T VirtualSize;\n  ULONG PageFaultCount;\n  SIZE_T PeakWorkingSetSize;\n  SIZE_T WorkingSetSize;\n  SIZE_T QuotaPeakPagedPoolUsage;\n  SIZE_T QuotaPagedPoolUsage;\n  SIZE_T QuotaPeakNonPagedPoolUsage;\n  SIZE_T QuotaNonPagedPoolUsage;\n  SIZE_T PagefileUsage;\n  SIZE_T PeakPagefileUsage;\n};\n\ntemplate <>\nstruct VM_COUNTERS<internal::Traits64> {\n  SIZE_T PeakVirtualSize;\n  SIZE_T VirtualSize;\n  union {\n    ULONG PageFaultCount;\n    internal::Traits64::Pad padding_for_x64;\n  };\n  SIZE_T PeakWorkingSetSize;\n  SIZE_T WorkingSetSize;\n  SIZE_T QuotaPeakPagedPoolUsage;\n  SIZE_T QuotaPagedPoolUsage;\n  SIZE_T QuotaPeakNonPagedPoolUsage;\n  SIZE_T QuotaNonPagedPoolUsage;\n  SIZE_T PagefileUsage;\n  SIZE_T PeakPagefileUsage;\n  SIZE_T PrivateUsage;\n};\n\n// https://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/System%20Information/Structures/SYSTEM_PROCESS_INFORMATION.html\ntemplate <class Traits>\nstruct SYSTEM_PROCESS_INFORMATION {\n  ULONG NextEntryOffset;\n  ULONG NumberOfThreads;\n  LARGE_INTEGER WorkingSetPrivateSize;\n  ULONG HardFaultCount;\n  ULONG NumberOfThreadsHighWatermark;\n  ULONGLONG CycleTime;\n  LARGE_INTEGER CreateTime;\n  LARGE_INTEGER UserTime;\n  LARGE_INTEGER KernelTime;\n  UNICODE_STRING<Traits> ImageName;\n  union {\n    LONG BasePriority;\n    typename Traits::Pad padding_for_x64_0;\n  };\n  union {\n    DWORD UniqueProcessId;\n    typename Traits::Pad padding_for_x64_1;\n  };\n  union {\n    DWORD InheritedFromUniqueProcessId;\n    typename Traits::Pad padding_for_x64_2;\n  };\n  ULONG HandleCount;\n  ULONG SessionId;\n  typename Traits::Pointer UniqueProcessKey;\n  union {\n    VM_COUNTERS<Traits> VirtualMemoryCounters;\n    LARGE_INTEGER alignment_for_x86[6];\n  };\n  IO_COUNTERS IoCounters;\n  SYSTEM_THREAD_INFORMATION<Traits> Threads[1];\n};\n\n// https://undocumented.ntinternals.net/source/usermode/structures/THREAD_BASIC_INFORMATION.html\ntemplate <class Traits>\nstruct THREAD_BASIC_INFORMATION {\n  union {\n    LONG ExitStatus;\n    typename Traits::Pad padding_for_x64_0;\n  };\n  typename Traits::Pointer TebBaseAddress;\n  CLIENT_ID<Traits> ClientId;\n  typename Traits::Pointer AffinityMask;\n  ULONG Priority;\n  LONG BasePriority;\n};\n\ntemplate <class Traits>\nstruct EXCEPTION_POINTERS {\n  typename Traits::Pointer ExceptionRecord;\n  typename Traits::Pointer ContextRecord;\n};\n\nusing EXCEPTION_POINTERS32 = EXCEPTION_POINTERS<internal::Traits32>;\nusing EXCEPTION_POINTERS64 = EXCEPTION_POINTERS<internal::Traits64>;\n\n// This is defined in winnt.h, but not for cross-bitness.\ntemplate <class Traits>\nstruct RTL_CRITICAL_SECTION {\n  typename Traits::Pointer DebugInfo;\n  LONG LockCount;\n  LONG RecursionCount;\n  typename Traits::Pointer OwningThread;\n  typename Traits::Pointer LockSemaphore;\n  typename Traits::UnsignedIntegral SpinCount;\n};\n\ntemplate <class Traits>\nstruct RTL_CRITICAL_SECTION_DEBUG {\n  union {\n    struct {\n      WORD Type;\n      WORD CreatorBackTraceIndex;\n    };\n    typename Traits::Pad alignment_for_x64;\n  };\n  typename Traits::Pointer CriticalSection;\n  LIST_ENTRY<Traits> ProcessLocksList;\n  DWORD EntryCount;\n  DWORD ContentionCount;\n  DWORD Flags;\n  WORD CreatorBackTraceIndexHigh;\n  WORD SpareWORD;\n};\n\nstruct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX {\n  void* Object;\n  ULONG_PTR UniqueProcessId;\n  HANDLE HandleValue;\n  ULONG GrantedAccess;\n  USHORT CreatorBackTraceIndex;\n  USHORT ObjectTypeIndex;\n  ULONG HandleAttributes;\n  ULONG Reserved;\n};\n\nstruct SYSTEM_HANDLE_INFORMATION_EX {\n  ULONG_PTR NumberOfHandles;\n  ULONG_PTR Reserved;\n  SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Handles[1];\n};\n\n#pragma pack(pop)\n\n//! \\}\n\n}  // namespace process_types\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_PROCESS_STRUCTS_H_\n"
  },
  {
    "path": "util/win/registration_protocol_win.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/registration_protocol_win.h\"\n\n#include <windows.h>\n#include <aclapi.h>\n#include <sddl.h>\n#include <stddef.h>\n\n#include <iterator>\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"util/win/exception_handler_server.h\"\n#include \"util/win/loader_lock.h\"\n#include \"util/win/scoped_handle.h\"\n#include \"util/win/scoped_local_alloc.h\"\n\nnamespace crashpad {\n\nnamespace {\n\nvoid* GetSecurityDescriptorWithUser(const wchar_t* sddl_string, size_t* size) {\n  if (size)\n    *size = 0;\n\n  PSECURITY_DESCRIPTOR base_sec_desc;\n  if (!ConvertStringSecurityDescriptorToSecurityDescriptor(\n          sddl_string, SDDL_REVISION_1, &base_sec_desc, nullptr)) {\n    PLOG(ERROR) << \"ConvertStringSecurityDescriptorToSecurityDescriptor\";\n    return nullptr;\n  }\n\n  ScopedLocalAlloc base_sec_desc_owner(base_sec_desc);\n  EXPLICIT_ACCESS access;\n  wchar_t username[] = L\"CURRENT_USER\";\n  BuildExplicitAccessWithName(\n      &access, username, GENERIC_ALL, GRANT_ACCESS, NO_INHERITANCE);\n\n  PSECURITY_DESCRIPTOR user_sec_desc;\n  ULONG user_sec_desc_size;\n  DWORD error = BuildSecurityDescriptor(nullptr,\n                                        nullptr,\n                                        1,\n                                        &access,\n                                        0,\n                                        nullptr,\n                                        base_sec_desc,\n                                        &user_sec_desc_size,\n                                        &user_sec_desc);\n  if (error != ERROR_SUCCESS) {\n    SetLastError(error);\n    PLOG(ERROR) << \"BuildSecurityDescriptor\";\n    return nullptr;\n  }\n\n  *size = user_sec_desc_size;\n  return user_sec_desc;\n}\n\n}  // namespace\n\nbool SendToCrashHandlerServer(const std::wstring& pipe_name,\n                              const ClientToServerMessage& message,\n                              ServerToClientMessage* response) {\n  // Retry CreateFile() in a loop. If the handler isn’t actively waiting in\n  // ConnectNamedPipe() on a pipe instance because it’s busy doing something\n  // else, CreateFile() will fail with ERROR_PIPE_BUSY. WaitNamedPipe() waits\n  // until a pipe instance is ready, but there’s no way to wait for this\n  // condition and atomically open the client side of the pipe in a single\n  // operation. CallNamedPipe() implements similar retry logic to this, also in\n  // user-mode code.\n  //\n  // This loop is only intended to retry on ERROR_PIPE_BUSY. Notably, if the\n  // handler is so lazy that it hasn’t even called CreateNamedPipe() yet,\n  // CreateFile() will fail with ERROR_FILE_NOT_FOUND, and this function is\n  // expected to fail without retrying anything. If the handler is started at\n  // around the same time as its client, something external to this code must be\n  // done to guarantee correct ordering. When the client starts the handler\n  // itself, CrashpadClient::StartHandler() provides this synchronization.\n  for (;;) {\n    ScopedFileHANDLE pipe(\n        CreateFile(pipe_name.c_str(),\n                   GENERIC_READ | GENERIC_WRITE,\n                   0,\n                   nullptr,\n                   OPEN_EXISTING,\n                   SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION,\n                   nullptr));\n    if (!pipe.is_valid()) {\n      if (GetLastError() != ERROR_PIPE_BUSY) {\n        PLOG(ERROR) << \"CreateFile\";\n        return false;\n      }\n\n      if (!WaitNamedPipe(pipe_name.c_str(), NMPWAIT_WAIT_FOREVER)) {\n        PLOG(ERROR) << \"WaitNamedPipe\";\n        return false;\n      }\n\n      continue;\n    }\n\n    DWORD mode = PIPE_READMODE_MESSAGE;\n    if (!SetNamedPipeHandleState(pipe.get(), &mode, nullptr, nullptr)) {\n      PLOG(ERROR) << \"SetNamedPipeHandleState\";\n      return false;\n    }\n    DWORD bytes_read = 0;\n    BOOL result = TransactNamedPipe(\n        pipe.get(),\n        // This is [in], but is incorrectly declared non-const.\n        const_cast<ClientToServerMessage*>(&message),\n        sizeof(message),\n        response,\n        sizeof(*response),\n        &bytes_read,\n        nullptr);\n    if (!result) {\n      PLOG(ERROR) << \"TransactNamedPipe\";\n      return false;\n    }\n    if (bytes_read != sizeof(*response)) {\n      LOG(ERROR) << \"TransactNamedPipe: expected \" << sizeof(*response)\n                 << \", observed \" << bytes_read;\n      return false;\n    }\n    return true;\n  }\n}\n\nHANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,\n                               bool first_instance) {\n  SECURITY_ATTRIBUTES security_attributes;\n  SECURITY_ATTRIBUTES* security_attributes_pointer = nullptr;\n\n  if (first_instance) {\n    // Pre-Vista does not have integrity levels.\n    const DWORD version = GetVersion();\n    const DWORD major_version = LOBYTE(LOWORD(version));\n    const bool is_vista_or_later = major_version >= 6;\n    if (is_vista_or_later) {\n      memset(&security_attributes, 0, sizeof(security_attributes));\n      security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);\n      security_attributes.lpSecurityDescriptor =\n          const_cast<void*>(GetSecurityDescriptorForNamedPipeInstance(nullptr));\n      security_attributes.bInheritHandle = TRUE;\n      security_attributes_pointer = &security_attributes;\n    }\n  }\n\n  return CreateNamedPipe(\n      pipe_name.c_str(),\n      PIPE_ACCESS_DUPLEX | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),\n      PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,\n      ExceptionHandlerServer::kPipeInstances,\n      512,\n      512,\n      0,\n      security_attributes_pointer);\n}\n\nconst void* GetFallbackSecurityDescriptorForNamedPipeInstance(size_t* size) {\n  // Mandatory Label, no ACE flags, no ObjectType, integrity level untrusted is\n  // \"S:(ML;;;;;S-1-16-0)\". This static security descriptor is used as a\n  // fallback if GetSecurityDescriptorWithUser fails, to avoid losing crashes\n  // from non-AppContainer sandboxed applications.\n\n#pragma pack(push, 1)\n  static constexpr struct SecurityDescriptorBlob {\n    // See https://msdn.microsoft.com/library/cc230366.aspx.\n    SECURITY_DESCRIPTOR_RELATIVE sd_rel;\n    struct {\n      ACL acl;\n      struct {\n        // This is equivalent to SYSTEM_MANDATORY_LABEL_ACE, but there's no\n        // DWORD offset to the SID, instead it's inline.\n        ACE_HEADER header;\n        ACCESS_MASK mask;\n        SID sid;\n      } ace[1];\n    } sacl;\n  } kSecDescBlob = {\n      // sd_rel.\n      {\n          SECURITY_DESCRIPTOR_REVISION1,  // Revision.\n          0x00,  // Sbz1.\n          SE_SELF_RELATIVE | SE_SACL_PRESENT,  // Control.\n          0,  // OffsetOwner.\n          0,  // OffsetGroup.\n          offsetof(SecurityDescriptorBlob, sacl),  // OffsetSacl.\n          0,  // OffsetDacl.\n      },\n\n      // sacl.\n      {\n          // acl.\n          {\n              ACL_REVISION,  // AclRevision.\n              0,  // Sbz1.\n              sizeof(kSecDescBlob.sacl),  // AclSize.\n              static_cast<WORD>(std::size(kSecDescBlob.sacl.ace)),  // AceCount.\n              0,  // Sbz2.\n          },\n\n          // ace[0].\n          {\n              {\n                  // header.\n                  {\n                      SYSTEM_MANDATORY_LABEL_ACE_TYPE,  // AceType.\n                      0,  // AceFlags.\n                      sizeof(kSecDescBlob.sacl.ace[0]),  // AceSize.\n                  },\n\n                  // mask.\n                  0,\n\n                  // sid.\n                  {\n                      SID_REVISION,  // Revision.\n                                     // SubAuthorityCount.\n                      static_cast<BYTE>(\n                          std::size(kSecDescBlob.sacl.ace[0].sid.SubAuthority)),\n                      // IdentifierAuthority.\n                      {SECURITY_MANDATORY_LABEL_AUTHORITY},\n                      {SECURITY_MANDATORY_UNTRUSTED_RID},  // SubAuthority.\n                  },\n              },\n          },\n      },\n  };\n#pragma pack(pop)\n\n  if (size)\n    *size = sizeof(kSecDescBlob);\n  return reinterpret_cast<const void*>(&kSecDescBlob);\n}\n\nconst void* GetSecurityDescriptorForNamedPipeInstance(size_t* size) {\n  CHECK(!IsThreadInLoaderLock());\n\n  // Get a security descriptor which grants the current user and SYSTEM full\n  // access to the named pipe. Also grant AppContainer RW access through the ALL\n  // APPLICATION PACKAGES SID (S-1-15-2-1). Finally add an Untrusted Mandatory\n  // Label for non-AppContainer sandboxed users.\n  static size_t sd_size;\n  static void* sec_desc = GetSecurityDescriptorWithUser(\n      L\"D:(A;;GA;;;SY)(A;;GWGR;;;S-1-15-2-1)S:(ML;;;;;S-1-16-0)\", &sd_size);\n\n  if (!sec_desc)\n    return GetFallbackSecurityDescriptorForNamedPipeInstance(size);\n\n  if (size)\n    *size = sd_size;\n  return sec_desc;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/registration_protocol_win.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_\n#define CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_\n\n#include <windows.h>\n#include <stdint.h>\n\n#include <string>\n\n#include \"util/win/address_types.h\"\n#include \"util/win/registration_protocol_win_structs.h\"\n\nnamespace crashpad {\n\n//! \\brief Connect over the given \\a pipe_name, passing \\a message to the\n//!     server, storing the server's reply into \\a response.\n//!\n//! Typically clients will not use this directly, instead using\n//! CrashpadClient::SetHandler().\n//!\n//! \\sa CrashpadClient::SetHandler()\nbool SendToCrashHandlerServer(const std::wstring& pipe_name,\n                              const ClientToServerMessage& message,\n                              ServerToClientMessage* response);\n\n//! \\brief Wraps CreateNamedPipe() to create a single named pipe instance.\n//!\n//! \\param[in] pipe_name The name to use for the pipe.\n//! \\param[in] first_instance If `true`, the named pipe instance will be\n//!     created with `FILE_FLAG_FIRST_PIPE_INSTANCE`. This ensures that the the\n//!     pipe name is not already in use when created. The first instance will be\n//!     created with an untrusted integrity SACL so instances of this pipe can\n//!     be connected to by processes of any integrity level.\nHANDLE CreateNamedPipeInstance(const std::wstring& pipe_name,\n                               bool first_instance);\n\n//! \\brief Returns the `SECURITY_DESCRIPTOR` blob that will be used for creating\n//!     the connection pipe in CreateNamedPipeInstance().\n//!\n//! This function is only exposed for testing.\n//!\n//! \\param[out] size The size of the returned blob. May be `nullptr` if not\n//!     required.\n//!\n//! \\return A pointer to a self-relative `SECURITY_DESCRIPTOR`. Ownership is not\n//!     transferred to the caller.\nconst void* GetSecurityDescriptorForNamedPipeInstance(size_t* size);\n\n//! \\brief Returns the `SECURITY_DESCRIPTOR` blob that will be used for creating\n//!     the connection pipe in CreateNamedPipeInstance() if the full descriptor\n//!     can't be created.\n//!\n//! This function is only exposed for testing.\n//!\n//! \\param[out] size The size of the returned blob. May be `nullptr` if not\n//!     required.\n//!\n//! \\return A pointer to a self-relative `SECURITY_DESCRIPTOR`. Ownership is not\n//!     transferred to the caller.\nconst void* GetFallbackSecurityDescriptorForNamedPipeInstance(size_t* size);\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_H_\n"
  },
  {
    "path": "util/win/registration_protocol_win_structs.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_STRUCTS_H_\n#define CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_STRUCTS_H_\n\n#include <windows.h>\n#include <stdint.h>\n\n#include \"util/win/address_types.h\"\n\nnamespace crashpad {\n\n#pragma pack(push, 1)\n\n//! \\brief Structure read out of the client process by the crash handler when an\n//!     exception occurs.\nstruct ExceptionInformation {\n  //! \\brief The address of an EXCEPTION_POINTERS structure in the client\n  //!     process that describes the exception.\n  WinVMAddress exception_pointers;\n\n  //! \\brief The thread on which the exception happened.\n  DWORD thread_id;\n};\n\n//! \\brief Context to be passed to WerRegisterRuntimeExceptionModule().\n//!\n//! Used by the crashpad client, and the WER exception DLL.\nstruct WerRegistration {\n  //! \\brief The expected value of `version`. This should be changed whenever\n  //!     this struct is modified incompatibly.\n  enum { kWerRegistrationVersion = 1 };\n  //! \\brief Version field to detect skew between target process and helper.\n  //!     Should be set to kWerRegistrationVersion.\n  int version;\n  //! \\brief Used by DumpWithoutCrashing and the WER module to initiate a dump.\n  //!     These handles are leaked in the client process.\n  HANDLE dump_without_crashing;\n  //! \\brief Used by DumpWithoutCrashing to signal that a dump has been taken.\n  //!     These handles are leaked in the client process.\n  HANDLE dump_completed;\n  //! \\brief Set just before and cleared just after the events above are\n  //!     triggered or signalled in a normal DumpWithoutCrashing call.\n  //! When `true` the WER handler should not set the exception structures until\n  //! after dump_completed has been signalled.\n  bool in_dump_without_crashing;\n  //! \\brief Address of g_non_crash_exception_information.\n  //!\n  //! Provided by the target process. Just before dumping we will point\n  //! (*crashpad_exception_info).exception_pointers at `pointers`. As WerFault\n  //! loads the helper with the same bitness as the client this can be void*.\n  void* crashpad_exception_info;\n  //! \\brief These will point into the `exception` and `context` members in this\n  //!     structure.\n  //!\n  //! Filled in by the helper DLL.\n  EXCEPTION_POINTERS pointers;\n  //! \\brief The exception provided by WerFault.\n  //!\n  //! Filled in by the helper DLL.\n  EXCEPTION_RECORD exception;\n  //! \\brief The context provided by WerFault.\n  //!\n  //! Filled in by the helper DLL.\n  CONTEXT context;\n};\n\n//! \\brief A client registration request.\nstruct RegistrationRequest {\n  //! \\brief The expected value of `version`. This should be changed whenever\n  //!     the messages or ExceptionInformation are modified incompatibly.\n  enum { kMessageVersion = 1 };\n\n  //! \\brief Version field to detect skew between client and server. Should be\n  //!     set to kMessageVersion.\n  int version;\n\n  //! \\brief The PID of the client process.\n  DWORD client_process_id;\n\n  //! \\brief The address, in the client process's address space, of an\n  //!     ExceptionInformation structure, used when handling a crash dump\n  //!     request.\n  WinVMAddress crash_exception_information;\n\n  //! \\brief The address, in the client process's address space, of an\n  //!     ExceptionInformation structure, used when handling a non-crashing dump\n  //!     request.\n  WinVMAddress non_crash_exception_information;\n\n  //! \\brief The address, in the client process's address space, of a\n  //!     `CRITICAL_SECTION` allocated with a valid .DebugInfo field. This can\n  //!     be accomplished by using\n  //!     InitializeCriticalSectionWithDebugInfoIfPossible() or equivalent. This\n  //!     value can be `0`, however then limited lock data will be available in\n  //!     minidumps.\n  WinVMAddress critical_section_address;\n};\n\n//! \\brief A message only sent to the server by itself to trigger shutdown.\nstruct ShutdownRequest {\n  //! \\brief A randomly generated token used to validate the the shutdown\n  //!     request was not sent from another process.\n  uint64_t token;\n};\n\n//! \\brief The message passed from client to server by\n//!     SendToCrashHandlerServer().\nstruct ClientToServerMessage {\n  //! \\brief Indicates which field of the union is in use.\n  enum Type : uint32_t {\n    //! \\brief For RegistrationRequest.\n    kRegister,\n\n    //! \\brief For ShutdownRequest.\n    kShutdown,\n\n    //! \\brief An empty message sent by the initial client in asynchronous mode.\n    //!     No data is required, this just confirms that the server is ready to\n    //!     accept client registrations.\n    kPing,\n  } type;\n\n  union {\n    RegistrationRequest registration;\n    ShutdownRequest shutdown;\n  };\n};\n\n//! \\brief A client registration response.\nstruct RegistrationResponse {\n  //! \\brief An event `HANDLE`, valid in the client process, that should be\n  //!     signaled to request a crash report. Clients should convert the value\n  //!     to a `HANDLE` by calling IntToHandle().\n  int request_crash_dump_event;\n\n  //! \\brief An event `HANDLE`, valid in the client process, that should be\n  //!     signaled to request a non-crashing dump be taken. Clients should\n  //!     convert the value to a `HANDLE` by calling IntToHandle().\n  int request_non_crash_dump_event;\n\n  //! \\brief An event `HANDLE`, valid in the client process, that will be\n  //!     signaled by the server when the non-crashing dump is complete. Clients\n  //!     should convert the value to a `HANDLE` by calling IntToHandle().\n  int non_crash_dump_completed_event;\n};\n\n//! \\brief The response sent back to the client via SendToCrashHandlerServer().\nunion ServerToClientMessage {\n  RegistrationResponse registration;\n};\n\n#pragma pack(pop)\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_REGISTRATION_PROTOCOL_WIN_STRUCTS_H_\n"
  },
  {
    "path": "util/win/registration_protocol_win_test.cc",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/registration_protocol_win.h\"\n\n#include <aclapi.h>\n#include <sddl.h>\n#include <string.h>\n#include <wchar.h>\n#include <windows.h>\n\n#include <vector>\n\n#include \"base/logging.h\"\n#include \"base/notreached.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"util/win/scoped_handle.h\"\n#include \"util/win/scoped_local_alloc.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nstd::wstring GetStringFromSid(PSID sid) {\n  LPWSTR sid_str;\n  if (!ConvertSidToStringSid(sid, &sid_str)) {\n    PLOG(ERROR) << \"ConvertSidToStringSid\";\n    return std::wstring();\n  }\n  ScopedLocalAlloc sid_str_ptr(sid_str);\n  return sid_str;\n}\n\nstd::wstring GetUserSidString() {\n  HANDLE token_handle;\n  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle)) {\n    PLOG(ERROR) << \"OpenProcessToken\";\n    return std::wstring();\n  }\n\n  ScopedKernelHANDLE token(token_handle);\n  DWORD user_size = 0;\n  GetTokenInformation(token.get(), TokenUser, nullptr, 0, &user_size);\n  if (user_size == 0) {\n    PLOG(ERROR) << \"GetTokenInformation Size\";\n    return std::wstring();\n  }\n\n  std::vector<char> user(user_size);\n  if (!GetTokenInformation(\n          token.get(), TokenUser, user.data(), user_size, &user_size)) {\n    PLOG(ERROR) << \"GetTokenInformation\";\n    return std::wstring();\n  }\n\n  TOKEN_USER* user_ptr = reinterpret_cast<TOKEN_USER*>(user.data());\n  return GetStringFromSid(user_ptr->User.Sid);\n}\n\nvoid CheckAce(PACL acl,\n              DWORD index,\n              BYTE check_ace_type,\n              ACCESS_MASK check_mask,\n              const std::wstring& check_sid) {\n  ASSERT_FALSE(check_sid.empty());\n  void* ace_ptr;\n  ASSERT_TRUE(GetAce(acl, index, &ace_ptr));\n\n  ACE_HEADER* header = static_cast<ACE_HEADER*>(ace_ptr);\n  ASSERT_EQ(check_ace_type, header->AceType);\n  ASSERT_EQ(0, header->AceFlags);\n\n  PSID sid = nullptr;\n  ACCESS_MASK mask = 0;\n  switch (header->AceType) {\n    case ACCESS_ALLOWED_ACE_TYPE: {\n      ACCESS_ALLOWED_ACE* allowed_ace =\n          static_cast<ACCESS_ALLOWED_ACE*>(ace_ptr);\n      sid = &allowed_ace->SidStart;\n      mask = allowed_ace->Mask;\n    } break;\n    case SYSTEM_MANDATORY_LABEL_ACE_TYPE: {\n      SYSTEM_MANDATORY_LABEL_ACE* label_ace =\n          static_cast<SYSTEM_MANDATORY_LABEL_ACE*>(ace_ptr);\n      sid = &label_ace->SidStart;\n      mask = label_ace->Mask;\n    } break;\n    default:\n      NOTREACHED();\n  }\n\n  ASSERT_EQ(check_mask, mask);\n  ASSERT_EQ(check_sid, GetStringFromSid(sid));\n}\n\nTEST(SecurityDescriptor, NamedPipeDefault) {\n  const void* sec_desc = GetSecurityDescriptorForNamedPipeInstance(nullptr);\n\n  PACL acl;\n  BOOL acl_present;\n  BOOL acl_defaulted;\n  ASSERT_TRUE(GetSecurityDescriptorDacl(\n      const_cast<void*>(sec_desc), &acl_present, &acl, &acl_defaulted));\n  ASSERT_EQ(3, acl->AceCount);\n  CheckAce(acl, 0, ACCESS_ALLOWED_ACE_TYPE, GENERIC_ALL, GetUserSidString());\n  // Check SYSTEM user SID.\n  CheckAce(acl, 1, ACCESS_ALLOWED_ACE_TYPE, GENERIC_ALL, L\"S-1-5-18\");\n  // Check ALL APPLICATION PACKAGES group SID.\n  CheckAce(acl,\n           2,\n           ACCESS_ALLOWED_ACE_TYPE,\n           GENERIC_READ | GENERIC_WRITE,\n           L\"S-1-15-2-1\");\n\n  ASSERT_TRUE(GetSecurityDescriptorSacl(\n      const_cast<void*>(sec_desc), &acl_present, &acl, &acl_defaulted));\n  ASSERT_EQ(1, acl->AceCount);\n  CheckAce(acl, 0, SYSTEM_MANDATORY_LABEL_ACE_TYPE, 0, L\"S-1-16-0\");\n}\n\nTEST(SecurityDescriptor, MatchesAdvapi32) {\n  // This security descriptor is built manually in the connection code to avoid\n  // calling the advapi32 functions. Verify that it returns the same thing as\n  // ConvertStringSecurityDescriptorToSecurityDescriptor() would.\n\n  // Mandatory Label, no ACE flags, no ObjectType, integrity level\n  // untrusted.\n  static constexpr wchar_t kSddl[] = L\"S:(ML;;;;;S-1-16-0)\";\n  PSECURITY_DESCRIPTOR sec_desc;\n  ULONG sec_desc_len;\n  ASSERT_TRUE(ConvertStringSecurityDescriptorToSecurityDescriptor(\n      kSddl, SDDL_REVISION_1, &sec_desc, &sec_desc_len))\n      << ErrorMessage(\"ConvertStringSecurityDescriptorToSecurityDescriptor\");\n  ScopedLocalAlloc sec_desc_owner(sec_desc);\n\n  size_t created_len;\n  const void* const created =\n      GetFallbackSecurityDescriptorForNamedPipeInstance(&created_len);\n  ASSERT_EQ(created_len, sec_desc_len);\n  EXPECT_EQ(memcmp(sec_desc, created, sec_desc_len), 0);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/safe_terminate_process.asm",
    "content": "; Copyright 2017 The Crashpad Authors\n;\n; Licensed under the Apache License, Version 2.0 (the \"License\");\n; you may not use this file except in compliance with the License.\n; You may obtain a copy of the License at\n;\n;     http://www.apache.org/licenses/LICENSE-2.0\n;\n; Unless required by applicable law or agreed to in writing, software\n; distributed under the License is distributed on an \"AS IS\" BASIS,\n; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n; See the License for the specific language governing permissions and\n; limitations under the License.\n\n; Detect ml64 assembling for x86_64 by checking for rax.\nifdef rax\n_M_X64 equ 1\nelse\n_M_IX86 equ 1\nendif\n\nifdef _M_IX86\n.586\n.xmm\n.model flat\n\nincludelib kernel32.lib\n\nextern __imp__TerminateProcess@8:proc\n\n; namespace crashpad {\n; bool SafeTerminateProcess(HANDLE process, UINT exit_code);\n; }  // namespace crashpad\nSAFETERMINATEPROCESS_SYMBOL equ ?SafeTerminateProcess@crashpad@@YA_NPAXI@Z\n\n_TEXT segment\npublic SAFETERMINATEPROCESS_SYMBOL\n\nSAFETERMINATEPROCESS_SYMBOL proc\n\n  ; This function is written in assembler source because it’s important for it\n  ; to not be inlined, for it to allocate a stack frame, and most critically,\n  ; for it to not trust esp on return from TerminateProcess().\n  ; __declspec(noinline) can prevent inlining and #pragma optimize(\"y\", off) can\n  ; disable frame pointer omission, but there’s no way to force a C compiler to\n  ; distrust esp, and even if there was a way, it’d probably be fragile.\n\n  push ebp\n  mov ebp, esp\n\n  push [ebp+12]\n  push [ebp+8]\n  call dword ptr [__imp__TerminateProcess@8]\n\n  ; Convert from BOOL to bool.\n  test eax, eax\n  setne al\n\n  ; TerminateProcess() is supposed to be stdcall (callee clean-up), and esp and\n  ; ebp are expected to already be equal. But if it’s been patched badly by\n  ; something that’s cdecl (caller clean-up), this next move will get things\n  ; back on track.\n  mov esp, ebp\n  pop ebp\n\n  ret\n\nSAFETERMINATEPROCESS_SYMBOL endp\n\n_TEXT ends\n\nendif\n\nend\n"
  },
  {
    "path": "util/win/safe_terminate_process.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_SAFE_TERMINATE_PROCESS_H_\n#define CRASHPAD_UTIL_WIN_SAFE_TERMINATE_PROCESS_H_\n\n#include <windows.h>\n\n#include \"build/build_config.h\"\n\nnamespace crashpad {\n\n//! \\brief Calls `TerminateProcess()`.\n//!\n//! `TerminateProcess()` has been observed in the wild as being patched badly on\n//! 32-bit x86: it’s patched with code adhering to the `cdecl` (caller clean-up)\n//! convention, although it’s supposed to be `stdcall` (callee clean-up). The\n//! mix-up means that neither caller nor callee perform parameter clean-up from\n//! the stack, causing the stack pointer to have an unexpected value on return\n//! from the patched function. This typically results in a crash shortly\n//! thereafter. See <a href=\"https://crashpad.chromium.org/bug/179\">Crashpad bug\n//! 179</a>.\n//!\n//! On 32-bit x86, this replacement function calls `TerminateProcess()` without\n//! making any assumptions about the stack pointer on its return. As such, it’s\n//! compatible with the badly patched `cdecl` version as well as the native\n//! `stdcall` version (and other less badly patched versions).\n//!\n//! Elsewhere, this function calls `TerminateProcess()` directly without any\n//! additional fanfare.\n//!\n//! Call this function instead of `TerminateProcess()` anywhere that\n//! `TerminateProcess()` would normally be called.\nbool SafeTerminateProcess(HANDLE process, UINT exit_code);\n\n#if !defined(ARCH_CPU_X86)\ninline bool SafeTerminateProcess(HANDLE process, UINT exit_code) {\n  return TerminateProcess(process, exit_code) != FALSE;\n}\n#endif  // !ARCH_CPU_X86\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_SAFE_TERMINATE_PROCESS_H_\n"
  },
  {
    "path": "util/win/safe_terminate_process_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/safe_terminate_process.h\"\n\n#include <string.h>\n\n#include <iterator>\n#include <memory>\n#include <string>\n\n#include \"base/check.h\"\n#include \"base/files/file_path.h\"\n#include \"build/build_config.h\"\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/test_paths.h\"\n#include \"test/win/child_launcher.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// Patches executable code, saving a copy of the original code so that it can be\n// restored on destruction.\nclass ScopedExecutablePatch {\n public:\n  ScopedExecutablePatch(void* target, const void* source, size_t size)\n      : original_(new uint8_t[size]), target_(target), size_(size) {\n    memcpy(original_.get(), target_, size_);\n\n    ScopedVirtualProtectRWX protect_rwx(target_, size_);\n    memcpy(target_, source, size_);\n  }\n\n  ScopedExecutablePatch(const ScopedExecutablePatch&) = delete;\n  ScopedExecutablePatch& operator=(const ScopedExecutablePatch&) = delete;\n\n  ~ScopedExecutablePatch() {\n    ScopedVirtualProtectRWX protect_rwx(target_, size_);\n    memcpy(target_, original_.get(), size_);\n  }\n\n private:\n  // Sets the protection on (address, size) to PAGE_EXECUTE_READWRITE by calling\n  // VirtualProtect(), and restores the original protection on destruction. Note\n  // that the region may span multiple pages, but the first page’s original\n  // protection will be applied to the entire region on destruction. This\n  // shouldn’t be a problem in practice for patching a function for this test’s\n  // purposes.\n  class ScopedVirtualProtectRWX {\n   public:\n    // If either the constructor or destructor fails, PCHECK() to terminate\n    // immediately, because the process will be in a weird and untrustworthy\n    // state, and Google Test error handling isn’t worthwhile at that point.\n\n    ScopedVirtualProtectRWX(void* address, size_t size)\n        : address_(address), size_(size) {\n      PCHECK(VirtualProtect(\n          address_, size_, PAGE_EXECUTE_READWRITE, &old_protect_))\n          << \"VirtualProtect\";\n    }\n\n    ScopedVirtualProtectRWX(const ScopedVirtualProtectRWX&) = delete;\n    ScopedVirtualProtectRWX& operator=(const ScopedVirtualProtectRWX&) = delete;\n\n    ~ScopedVirtualProtectRWX() {\n      DWORD last_protect_;\n      PCHECK(VirtualProtect(address_, size_, old_protect_, &last_protect_))\n          << \"VirtualProtect\";\n    }\n\n   private:\n    void* address_;\n    size_t size_;\n    DWORD old_protect_;\n  };\n\n  std::unique_ptr<uint8_t[]> original_;\n  void* target_;\n  size_t size_;\n};\n\n// SafeTerminateProcess is calling convention specific only for x86.\n#if defined(ARCH_CPU_X86_FAMILY)\nTEST(SafeTerminateProcess, PatchBadly) {\n  // This is a test of SafeTerminateProcess(), but it doesn’t actually terminate\n  // anything. Instead, it works with a process handle for the current process\n  // that doesn’t have PROCESS_TERMINATE access. The whole point of this test is\n  // to patch the real TerminateProcess() badly with a cdecl implementation to\n  // ensure that SafeTerminateProcess() can recover from such gross misconduct.\n  // The actual termination isn’t relevant to this test.\n  //\n  // Notably, don’t duplicate the process handle with PROCESS_TERMINATE access\n  // or with the DUPLICATE_SAME_ACCESS option. The SafeTerminateProcess() calls\n  // that follow operate on a duplicate of the current process’ process handle,\n  // and they’re supposed to fail, not terminate this process.\n  HANDLE process;\n  ASSERT_TRUE(DuplicateHandle(GetCurrentProcess(),\n                              GetCurrentProcess(),\n                              GetCurrentProcess(),\n                              &process,\n                              PROCESS_QUERY_INFORMATION,\n                              false,\n                              0))\n      << ErrorMessage(\"DuplicateHandle\");\n  ScopedKernelHANDLE process_owner(process);\n\n  // Make sure that TerminateProcess() works as a baseline.\n  SetLastError(ERROR_SUCCESS);\n  EXPECT_FALSE(TerminateProcess(process, 0));\n  EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_ACCESS_DENIED));\n\n  // Make sure that SafeTerminateProcess() works, calling through to\n  // TerminateProcess() properly.\n  SetLastError(ERROR_SUCCESS);\n  EXPECT_FALSE(SafeTerminateProcess(process, 0));\n  EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_ACCESS_DENIED));\n\n  {\n    // Patch TerminateProcess() badly. This turns it into a no-op that returns 0\n    // without cleaning up arguments from the stack, as a stdcall function is\n    // expected to do.\n    //\n    // This simulates the unexpected cdecl-patched TerminateProcess() as seen at\n    // https://crashpad.chromium.org/bug/179. In reality, this only affects\n    // 32-bit x86, as there’s no calling convention confusion on x86_64. It\n    // doesn’t hurt to run this test in the 64-bit environment, though.\n    static constexpr uint8_t patch[] = {\n#if defined(ARCH_CPU_X86)\n        0x31, 0xc0,  // xor eax, eax\n#elif defined(ARCH_CPU_X86_64)\n        0x48, 0x31, 0xc0,  // xor rax, rax\n#else\n#error Port\n#endif\n        0xc3,  // ret\n    };\n\n    void* target = reinterpret_cast<void*>(TerminateProcess);\n    ScopedExecutablePatch executable_patch(target, patch, std::size(patch));\n\n    // Make sure that SafeTerminateProcess() can be called. Since it’s been\n    // patched with a no-op stub, GetLastError() shouldn’t be modified.\n    SetLastError(ERROR_SUCCESS);\n    EXPECT_FALSE(SafeTerminateProcess(process, 0));\n    EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_SUCCESS));\n  }\n\n  // Now that the real TerminateProcess() has been restored, verify that it\n  // still works properly.\n  SetLastError(ERROR_SUCCESS);\n  EXPECT_FALSE(SafeTerminateProcess(process, 0));\n  EXPECT_EQ(GetLastError(), static_cast<DWORD>(ERROR_ACCESS_DENIED));\n}\n#endif  // ARCH_CPU_X86_FAMILY\n\nTEST(SafeTerminateProcess, TerminateChild) {\n  base::FilePath child_executable =\n      TestPaths::BuildArtifact(L\"util\",\n                               L\"safe_terminate_process_test_child\",\n                               TestPaths::FileType::kExecutable);\n  ChildLauncher child(child_executable, L\"\");\n  ASSERT_NO_FATAL_FAILURE(child.Start());\n\n  constexpr DWORD kExitCode = 0x51ee9d1e;  // Sort of like “sleep and die.”\n\n  ASSERT_TRUE(SafeTerminateProcess(child.process_handle(), kExitCode))\n      << ErrorMessage(\"TerminateProcess\");\n  EXPECT_EQ(child.WaitForExit(), kExitCode);\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/safe_terminate_process_test_child.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdlib.h>\n#include <wchar.h>\n#include <windows.h>\n\nint wmain(int argc, wchar_t** argv) {\n  Sleep(INFINITE);\n  return EXIT_FAILURE;\n}\n"
  },
  {
    "path": "util/win/scoped_handle.cc",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/scoped_handle.h\"\n\n#include \"base/check.h\"\n#include \"util/file/file_io.h\"\n\nnamespace crashpad {\nnamespace internal {\n\nvoid ScopedFileHANDLECloseTraits::Free(HANDLE handle) {\n  CheckedCloseFile(handle);\n}\n\nvoid ScopedKernelHANDLECloseTraits::Free(HANDLE handle) {\n  PCHECK(CloseHandle(handle)) << \"CloseHandle\";\n}\n\nvoid ScopedSearchHANDLECloseTraits::Free(HANDLE handle) {\n  PCHECK(FindClose(handle)) << \"FindClose\";\n}\n\nvoid ScopedVectoredExceptionRegistrationCloseTraits::Free(PVOID handle) {\n  PCHECK(::RemoveVectoredExceptionHandler(handle));\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/scoped_handle.h",
    "content": "// Copyright 2014 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_\n#define CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_\n\n#include <windows.h>\n\n#include \"base/scoped_generic.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\nstruct ScopedFileHANDLECloseTraits {\n  static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }\n  static void Free(HANDLE handle);\n};\n\nstruct ScopedKernelHANDLECloseTraits {\n  static HANDLE InvalidValue() { return nullptr; }\n  static void Free(HANDLE handle);\n};\n\nstruct ScopedSearchHANDLECloseTraits {\n  static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }\n  static void Free(HANDLE handle);\n};\n\nstruct ScopedVectoredExceptionRegistrationCloseTraits {\n  static PVOID InvalidValue() { return nullptr; }\n  static void Free(PVOID handle);\n};\n\n}  // namespace internal\n\nusing ScopedFileHANDLE =\n    base::ScopedGeneric<HANDLE, internal::ScopedFileHANDLECloseTraits>;\nusing ScopedKernelHANDLE =\n    base::ScopedGeneric<HANDLE, internal::ScopedKernelHANDLECloseTraits>;\nusing ScopedSearchHANDLE =\n    base::ScopedGeneric<HANDLE, internal::ScopedSearchHANDLECloseTraits>;\nusing ScopedVectoredExceptionRegistration = base::ScopedGeneric<\n    PVOID,\n    internal::ScopedVectoredExceptionRegistrationCloseTraits>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_SCOPED_HANDLE_H_\n"
  },
  {
    "path": "util/win/scoped_local_alloc.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/scoped_local_alloc.h\"\n\n#include \"base/logging.h\"\n\nnamespace crashpad {\nnamespace internal {\n\n// static\nvoid LocalAllocTraits::Free(HLOCAL memory) {\n  PLOG_IF(ERROR, LocalFree(memory) != nullptr) << \"LocalFree\";\n}\n\n}  // namespace internal\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/scoped_local_alloc.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_\n#define CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_\n\n#include <windows.h>\n\n#include \"base/scoped_generic.h\"\n\nnamespace crashpad {\n\nnamespace internal {\n\nstruct LocalAllocTraits {\n  static HLOCAL InvalidValue() { return nullptr; }\n  static void Free(HLOCAL mem);\n};\n\n}  // namespace internal\n\nusing ScopedLocalAlloc =\n    base::ScopedGeneric<HLOCAL, internal::LocalAllocTraits>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_SCOPED_LOCAL_ALLOC_H_\n"
  },
  {
    "path": "util/win/scoped_process_suspend.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/scoped_process_suspend.h\"\n\n#include <stddef.h>\n#include <winternl.h>\n\n#include \"util/win/nt_internals.h\"\n#include \"util/win/ntstatus_logging.h\"\n\nnamespace crashpad {\n\nScopedProcessSuspend::ScopedProcessSuspend(HANDLE process) {\n  NTSTATUS status = NtSuspendProcess(process);\n  if (NT_SUCCESS(status)) {\n    process_ = process;\n  } else {\n    process_ = nullptr;\n    NTSTATUS_LOG(ERROR, status) << \"NtSuspendProcess\";\n  }\n}\n\nScopedProcessSuspend::~ScopedProcessSuspend() {\n  if (process_) {\n    NTSTATUS status = NtResumeProcess(process_);\n    if (!NT_SUCCESS(status) &&\n        (!tolerate_termination_ || status != STATUS_PROCESS_IS_TERMINATING)) {\n      NTSTATUS_LOG(ERROR, status) << \"NtResumeProcess\";\n    }\n  }\n}\n\nvoid ScopedProcessSuspend::TolerateTermination() {\n  tolerate_termination_ = true;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/scoped_process_suspend.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_\n#define CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_\n\n#include <windows.h>\n\n\nnamespace crashpad {\n\n//! \\brief Manages the suspension of another process.\n//!\n//! While an object of this class exists, the other process will be suspended.\n//! Once the object is destroyed, the other process will become eligible for\n//! resumption.\n//!\n//! If this process crashes while this object exists, there is no guarantee that\n//! the other process will be resumed.\nclass ScopedProcessSuspend {\n public:\n  //! Does not take ownership of \\a process.\n  explicit ScopedProcessSuspend(HANDLE process);\n\n  ScopedProcessSuspend(const ScopedProcessSuspend&) = delete;\n  ScopedProcessSuspend& operator=(const ScopedProcessSuspend&) = delete;\n\n  ~ScopedProcessSuspend();\n\n  //! \\brief Informs the object that the suspended process may be terminating,\n  //!     and that this should not be treated as an error.\n  //!\n  //! Normally, attempting to resume a terminating process during destruction\n  //! results in an error message being logged for\n  //! `STATUS_PROCESS_IS_TERMINATING`. When it is known that a process may be\n  //! terminating, this method may be called to suppress that error message.\n  void TolerateTermination();\n\n private:\n  HANDLE process_;\n  bool tolerate_termination_ = false;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_\n"
  },
  {
    "path": "util/win/scoped_process_suspend_test.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/scoped_process_suspend.h\"\n\n#include <stddef.h>\n#include <tlhelp32.h>\n\n#include <algorithm>\n#include <vector>\n\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n#include \"test/win/win_child_process.h\"\n#include \"util/win/xp_compat.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\n// There is no per-process suspend count on Windows, only a per-thread suspend\n// count. NtSuspendProcess just suspends all threads of a given process. So,\n// verify that all thread's suspend counts match the desired suspend count.\nbool SuspendCountMatches(HANDLE process, DWORD desired_suspend_count) {\n  DWORD process_id = GetProcessId(process);\n\n  ScopedKernelHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));\n  if (!snapshot.is_valid()) {\n    ADD_FAILURE() << ErrorMessage(\"CreateToolhelp32Snapshot\");\n    return false;\n  }\n\n  THREADENTRY32 te;\n  te.dwSize = sizeof(te);\n\n  BOOL ret = Thread32First(snapshot.get(), &te);\n  if (!ret) {\n    ADD_FAILURE() << ErrorMessage(\"Thread32First\");\n    return false;\n  }\n  do {\n    if (te.dwSize >= offsetof(THREADENTRY32, th32OwnerProcessID) +\n                         sizeof(te.th32OwnerProcessID) &&\n        te.th32OwnerProcessID == process_id) {\n      ScopedKernelHANDLE thread(\n          OpenThread(kXPThreadAllAccess, false, te.th32ThreadID));\n      EXPECT_TRUE(thread.is_valid()) << ErrorMessage(\"OpenThread\");\n      DWORD result = SuspendThread(thread.get());\n      EXPECT_NE(result, static_cast<DWORD>(-1))\n          << ErrorMessage(\"SuspendThread\");\n      if (result != static_cast<DWORD>(-1)) {\n        EXPECT_NE(ResumeThread(thread.get()), static_cast<DWORD>(-1))\n            << ErrorMessage(\"ResumeThread\");\n      }\n      if (result != desired_suspend_count)\n        return false;\n    }\n    te.dwSize = sizeof(te);\n  } while (Thread32Next(snapshot.get(), &te));\n\n  return true;\n}\n\nclass ScopedProcessSuspendTest final : public WinChildProcess {\n public:\n  ScopedProcessSuspendTest() : WinChildProcess() {}\n\n  ScopedProcessSuspendTest(const ScopedProcessSuspendTest&) = delete;\n  ScopedProcessSuspendTest& operator=(const ScopedProcessSuspendTest&) = delete;\n\n  ~ScopedProcessSuspendTest() {}\n\n private:\n  int Run() override {\n    char c;\n    // Wait for notification from parent.\n    EXPECT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));\n    EXPECT_EQ(c, ' ');\n    return EXIT_SUCCESS;\n  }\n};\n\nTEST(ScopedProcessSuspend, ScopedProcessSuspend) {\n  WinChildProcess::EntryPoint<ScopedProcessSuspendTest>();\n  std::unique_ptr<WinChildProcess::Handles> handles = WinChildProcess::Launch();\n\n  EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0));\n\n  {\n    ScopedProcessSuspend suspend0(handles->process.get());\n    EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1));\n\n    {\n      ScopedProcessSuspend suspend1(handles->process.get());\n      EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 2));\n    }\n\n    EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1));\n  }\n\n  EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0));\n\n  // Tell the child it's OK to terminate.\n  char c = ' ';\n  EXPECT_TRUE(WriteFile(handles->write.get(), &c, sizeof(c)));\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/scoped_registry_key.h",
    "content": "// Copyright 2019 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_\n#define CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_\n\n#include <windows.h>\n\n#include \"base/scoped_generic.h\"\n\nnamespace crashpad {\n\nstruct ScopedRegistryKeyCloseTraits {\n  static HKEY InvalidValue() { return nullptr; }\n  static void Free(HKEY key) { RegCloseKey(key); }\n};\n\nusing ScopedRegistryKey =\n    base::ScopedGeneric<HKEY, ScopedRegistryKeyCloseTraits>;\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_SCOPED_REGISTRY_KEY_H_\n"
  },
  {
    "path": "util/win/scoped_set_event.cc",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/scoped_set_event.h\"\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n\nnamespace crashpad {\n\nScopedSetEvent::ScopedSetEvent(HANDLE event) : event_(event) {\n  DCHECK(event_);\n}\n\nScopedSetEvent::~ScopedSetEvent() {\n  if (event_) {\n    Set();\n  }\n}\n\nbool ScopedSetEvent::Set() {\n  bool rv = !!SetEvent(event_);\n  if (!rv) {\n    PLOG(ERROR) << \"SetEvent\";\n  }\n  event_ = nullptr;\n  return rv;\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/scoped_set_event.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_SCOPED_SET_EVENT_H_\n#define CRASHPAD_UTIL_WIN_SCOPED_SET_EVENT_H_\n\n#include <windows.h>\n\n\nnamespace crashpad {\n\n//! \\brief Calls `SetEvent()` on destruction at latest.\n//!\n//! Does not assume ownership of the event handle. Use ScopedKernelHANDLE for\n//! ownership.\nclass ScopedSetEvent {\n public:\n  explicit ScopedSetEvent(HANDLE event);\n\n  ScopedSetEvent(const ScopedSetEvent&) = delete;\n  ScopedSetEvent& operator=(const ScopedSetEvent&) = delete;\n\n  ~ScopedSetEvent();\n\n  //! \\brief Calls `SetEvent()` immediately.\n  //!\n  //! `SetEvent()` will not be called on destruction.\n  //!\n  //! \\return `true` on success, `false` on failure with a message logged.\n  bool Set();\n\n private:\n  HANDLE event_;  // weak\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_SCOPED_SET_EVENT_H_\n"
  },
  {
    "path": "util/win/session_end_watcher.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/session_end_watcher.h\"\n\n#include \"base/check.h\"\n#include \"base/logging.h\"\n#include \"base/scoped_generic.h\"\n#include \"util/win/scoped_set_event.h\"\n\nextern \"C\" {\nextern IMAGE_DOS_HEADER __ImageBase;\n}  // extern \"C\"\n\nnamespace crashpad {\n\nnamespace {\n\n// ScopedWindowClass and ScopedWindow operate on ATOM* and HWND*, respectively,\n// instead of ATOM and HWND, so that the actual storage can exist as a local\n// variable or a member variable, and the scoper can be responsible for\n// releasing things only if the actual storage hasn’t been released and zeroed\n// already by something else.\nstruct ScopedWindowClassTraits {\n  static ATOM* InvalidValue() { return nullptr; }\n  static void Free(ATOM* window_class) {\n    if (*window_class) {\n      if (!UnregisterClass(MAKEINTATOM(*window_class), 0)) {\n        PLOG(ERROR) << \"UnregisterClass\";\n      } else {\n        *window_class = 0;\n      }\n    }\n  }\n};\nusing ScopedWindowClass = base::ScopedGeneric<ATOM*, ScopedWindowClassTraits>;\n\nstruct ScopedWindowTraits {\n  static HWND* InvalidValue() { return nullptr; }\n  static void Free(HWND* window) {\n    if (*window) {\n      if (!DestroyWindow(*window)) {\n        PLOG(ERROR) << \"DestroyWindow\";\n      } else {\n        *window = nullptr;\n      }\n    }\n  }\n};\nusing ScopedWindow = base::ScopedGeneric<HWND*, ScopedWindowTraits>;\n\n// GetWindowLongPtr()’s return value doesn’t unambiguously indicate whether it\n// was successful, because 0 could either represent successful retrieval of the\n// value 0, or failure. This wrapper is more convenient to use.\nbool GetWindowLongPtrAndSuccess(HWND window, int index, LONG_PTR* value) {\n  SetLastError(ERROR_SUCCESS);\n  *value = GetWindowLongPtr(window, index);\n  return *value || GetLastError() == ERROR_SUCCESS;\n}\n\n// SetWindowLongPtr() has the same problem as GetWindowLongPtr(). Use this\n// wrapper instead.\nbool SetWindowLongPtrAndGetSuccess(HWND window, int index, LONG_PTR value) {\n  SetLastError(ERROR_SUCCESS);\n  LONG_PTR previous = SetWindowLongPtr(window, index, value);\n  return previous || GetLastError() == ERROR_SUCCESS;\n}\n\n}  // namespace\n\nSessionEndWatcher::SessionEndWatcher()\n    : Thread(),\n      window_(nullptr),\n      started_(nullptr),\n      stopped_(nullptr) {\n  // Set bManualReset for these events so that WaitForStart() and WaitForStop()\n  // can be called multiple times.\n\n  started_.reset(CreateEvent(nullptr, true, false, nullptr));\n  PLOG_IF(ERROR, !started_.get()) << \"CreateEvent\";\n\n  stopped_.reset(CreateEvent(nullptr, true, false, nullptr));\n  PLOG_IF(ERROR, !stopped_.get()) << \"CreateEvent\";\n\n  Start();\n}\n\nSessionEndWatcher::~SessionEndWatcher() {\n  // Tear everything down by posting a WM_CLOSE to the window. This obviously\n  // can’t work until the window has been created, and that happens on a\n  // different thread, so wait for the start event to be signaled first.\n  WaitForStart();\n  if (window_) {\n    if (!PostMessage(window_, WM_CLOSE, 0, 0)) {\n      PLOG(ERROR) << \"PostMessage\";\n    }\n  }\n\n  Join();\n  DCHECK(!window_);\n}\n\nvoid SessionEndWatcher::WaitForStart() {\n  if (WaitForSingleObject(started_.get(), INFINITE) != WAIT_OBJECT_0) {\n    PLOG(ERROR) << \"WaitForSingleObject\";\n  }\n}\n\nvoid SessionEndWatcher::WaitForStop() {\n  if (WaitForSingleObject(stopped_.get(), INFINITE) != WAIT_OBJECT_0) {\n    PLOG(ERROR) << \"WaitForSingleObject\";\n  }\n}\n\nvoid SessionEndWatcher::ThreadMain() {\n  ATOM atom = 0;\n  ScopedWindowClass window_class(&atom);\n  ScopedWindow window(&window_);\n\n  ScopedSetEvent call_set_stop(stopped_.get());\n\n  {\n    ScopedSetEvent call_set_start(started_.get());\n\n    WNDCLASS wndclass = {};\n    wndclass.lpfnWndProc = WindowProc;\n    wndclass.hInstance = reinterpret_cast<HMODULE>(&__ImageBase);\n    wndclass.lpszClassName = L\"crashpad_SessionEndWatcher\";\n    atom = RegisterClass(&wndclass);\n    if (!atom) {\n      PLOG(ERROR) << \"RegisterClass\";\n      return;\n    }\n\n    window_ = CreateWindow(MAKEINTATOM(atom),  // lpClassName\n                           nullptr,  // lpWindowName\n                           0,  // dwStyle\n                           0,  // x\n                           0,  // y\n                           0,  // nWidth\n                           0,  // nHeight\n                           nullptr,  // hWndParent\n                           nullptr,  // hMenu\n                           nullptr,  // hInstance\n                           this);  // lpParam\n    if (!window_) {\n      PLOG(ERROR) << \"CreateWindow\";\n      return;\n    }\n  }\n\n  MSG message;\n  BOOL rv = 0;\n  while (window_ && (rv = GetMessage(&message, window_, 0, 0)) > 0) {\n    TranslateMessage(&message);\n    DispatchMessage(&message);\n  }\n  if (window_ && rv == -1) {\n    PLOG(ERROR) << \"GetMessage\";\n    return;\n  }\n}\n\n// static\nLRESULT CALLBACK SessionEndWatcher::WindowProc(HWND window,\n                                               UINT message,\n                                               WPARAM w_param,\n                                               LPARAM l_param) {\n  // Figure out which object this is. A pointer to it is stuffed into the last\n  // parameter of CreateWindow(), which shows up as CREATESTRUCT::lpCreateParams\n  // in a WM_CREATE message. That should be processed before any of the other\n  // messages of interest to this function. Once the object is known, save a\n  // pointer to it in the GWLP_USERDATA slot for later retrieval when processing\n  // other messages.\n  SessionEndWatcher* self;\n  if (!GetWindowLongPtrAndSuccess(\n          window, GWLP_USERDATA, reinterpret_cast<LONG_PTR*>(&self))) {\n    PLOG(ERROR) << \"GetWindowLongPtr\";\n  }\n  if (!self && message == WM_CREATE) {\n    CREATESTRUCT* create = reinterpret_cast<CREATESTRUCT*>(l_param);\n    self = reinterpret_cast<SessionEndWatcher*>(create->lpCreateParams);\n    if (!SetWindowLongPtrAndGetSuccess(\n            window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self))) {\n      PLOG(ERROR) << \"SetWindowLongPtr\";\n    }\n  }\n\n  if (self) {\n    if (message == WM_ENDSESSION) {\n      // If w_param is false, this WM_ENDSESSION message cancels a previous\n      // WM_QUERYENDSESSION.\n      if (w_param) {\n        self->SessionEnding();\n\n        // If the session is ending, post a close message which will kick off\n        // window destruction and cause the message loop thread to terminate.\n        if (!PostMessage(self->window_, WM_CLOSE, 0, 0)) {\n          PLOG(ERROR) << \"PostMessage\";\n        }\n      }\n    } else if (message == WM_DESTROY) {\n      // The window is being destroyed. Clear GWLP_USERDATA so that |self| won’t\n      // be found during a subsequent call into this function for this window.\n      // Clear self->window_ too, because it refers to an object that soon won’t\n      // exist. That signals the message loop to stop processing messages.\n      if (!SetWindowLongPtrAndGetSuccess(window, GWLP_USERDATA, 0)) {\n        PLOG(ERROR) << \"SetWindowLongPtr\";\n      }\n      self->window_ = nullptr;\n    }\n  }\n\n  // If the message is WM_CLOSE, DefWindowProc() will call DestroyWindow(), and\n  // this function will be called again with a WM_DESTROY message.\n  return DefWindowProc(window, message, w_param, l_param);\n}\n\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/session_end_watcher.h",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_SESSION_END_WATCHER_H_\n#define CRASHPAD_UTIL_WIN_SESSION_END_WATCHER_H_\n\n#include <windows.h>\n\n#include \"util/thread/thread.h\"\n#include \"util/win/scoped_handle.h\"\n\nnamespace crashpad {\n\n//! \\brief Creates a hidden window and waits for a `WM_ENDSESSION` message,\n//!     indicating that the session is ending and the application should\n//!     terminate.\n//!\n//! A dedicated thread will be created to run the `GetMessage()`-based message\n//! loop required to monitor for this message.\n//!\n//! Users should subclass this class and receive notifications by implementing\n//! the SessionEndWatcherEvent() method.\nclass SessionEndWatcher : public Thread {\n public:\n  SessionEndWatcher();\n\n  SessionEndWatcher(const SessionEndWatcher&) = delete;\n  SessionEndWatcher& operator=(const SessionEndWatcher&) = delete;\n\n  //! \\note The destructor waits for the thread that runs the message loop to\n  //!     terminate.\n  ~SessionEndWatcher() override;\n\n protected:\n  // Exposed for testing.\n  HWND GetWindow() const { return window_; }\n\n  // Exposed for testing. Blocks until window_ has been created. May be called\n  // multiple times if necessary.\n  void WaitForStart();\n\n  // Exposed for testing. Blocks until the message loop ends. May be called\n  // multiple times if necessary.\n  void WaitForStop();\n\n private:\n  // Thread:\n  void ThreadMain() override;\n\n  static LRESULT CALLBACK WindowProc(HWND window,\n                                     UINT message,\n                                     WPARAM w_param,\n                                     LPARAM l_param);\n\n  //! \\brief A `WM_ENDSESSION` message was received and it indicates that the\n  //!     user session will be ending imminently.\n  //!\n  //! This method is called on the thread that runs the message loop.\n  virtual void SessionEnding() = 0;\n\n  HWND window_;  // Conceptually strong, but ownership managed in ThreadMain()\n  ScopedKernelHANDLE started_;\n  ScopedKernelHANDLE stopped_;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_SESSION_END_WATCHER_H_\n"
  },
  {
    "path": "util/win/session_end_watcher_test.cc",
    "content": "// Copyright 2017 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"util/win/session_end_watcher.h\"\n\n#include \"gtest/gtest.h\"\n#include \"test/errors.h\"\n\nnamespace crashpad {\nnamespace test {\nnamespace {\n\nclass SessionEndWatcherTest final : public SessionEndWatcher {\n public:\n  SessionEndWatcherTest() : SessionEndWatcher(), called_(false) {}\n\n  SessionEndWatcherTest(const SessionEndWatcherTest&) = delete;\n  SessionEndWatcherTest& operator=(const SessionEndWatcherTest&) = delete;\n\n  ~SessionEndWatcherTest() override {}\n\n  void Run() {\n    WaitForStart();\n\n    HWND window = GetWindow();\n    ASSERT_TRUE(window);\n    EXPECT_TRUE(PostMessage(window, WM_ENDSESSION, 1, 0));\n\n    WaitForStop();\n\n    EXPECT_TRUE(called_);\n  }\n\n private:\n  // SessionEndWatcher:\n  void SessionEnding() override { called_ = true; }\n\n  bool called_;\n};\n\nTEST(SessionEndWatcher, SessionEndWatcher) {\n  SessionEndWatcherTest test;\n  test.Run();\n}\n\nTEST(SessionEndWatcher, DoNothing) {\n  SessionEndWatcherTest test;\n}\n\n}  // namespace\n}  // namespace test\n}  // namespace crashpad\n"
  },
  {
    "path": "util/win/termination_codes.h",
    "content": "// Copyright 2016 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_TERMINATION_CODES_H_\n#define CRASHPAD_UTIL_WIN_TERMINATION_CODES_H_\n\nnamespace crashpad {\n\n//! \\brief Crashpad-specific codes that are used as arguments to\n//!     SafeTerminateProcess() or `TerminateProcess()` in unusual circumstances.\nenum TerminationCodes : unsigned int {\n  //! \\brief The crash handler did not respond, and the client self-terminated.\n  kTerminationCodeCrashNoDump = 0xffff7001,\n\n  //! \\brief The initial process snapshot failed, so the correct client\n  //!     termination code could not be retrieved.\n  kTerminationCodeSnapshotFailed = 0xffff7002,\n\n  //! \\brief A dump was requested for a client that was never registered with\n  //!     the crash handler.\n  kTerminationCodeNotConnectedToHandler = 0xffff7003,\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_TERMINATION_CODES_H_\n"
  },
  {
    "path": "util/win/traits.h",
    "content": "// Copyright 2020 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_TRAITS_H_\n#define CRASHPAD_UTIL_WIN_TRAITS_H_\n\n#include <stdint.h>\n\nnamespace crashpad {\n\nstruct Traits32 {\n  using Address = DWORD;\n};\n\nstruct Traits64 {\n  using Address = DWORD64;\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_TRAITS_H_\n"
  },
  {
    "path": "util/win/xp_compat.h",
    "content": "// Copyright 2015 The Crashpad Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef CRASHPAD_UTIL_WIN_XP_COMPAT_H_\n#define CRASHPAD_UTIL_WIN_XP_COMPAT_H_\n\n#include <windows.h>\n\nnamespace crashpad {\n\nenum {\n  //! \\brief This is the XP-suitable value of `PROCESS_ALL_ACCESS`.\n  //!\n  //! Requesting `PROCESS_ALL_ACCESS` with the value defined when building\n  //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP.\n  //! See https://msdn.microsoft.com/library/ms684880.aspx.\n  kXPProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF,\n\n  //! \\brief This is the XP-suitable value of `THREAD_ALL_ACCESS`.\n  //!\n  //! Requesting `THREAD_ALL_ACCESS` with the value defined when building\n  //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP.\n  //! See https://msdn.microsoft.com/library/ms686769.aspx.\n  kXPThreadAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF,\n};\n\n}  // namespace crashpad\n\n#endif  // CRASHPAD_UTIL_WIN_XP_COMPAT_H_\n"
  }
]