Update sentry-native to 0.7.2

This commit is contained in:
Marek Roszko 2024-04-29 20:11:06 -04:00
parent 7fdb5e98a2
commit 2f0ff4e557
292 changed files with 27639 additions and 5178 deletions

View File

@ -1,11 +1,68 @@
# Changelog # Changelog
## 0.7.2
**Features**:
- Add optional Gzip transport compression via build option `SENTRY_TRANSPORT_COMPRESSION`. Requires system `zlib`. ([#954](https://github.com/getsentry/sentry-native/pull/954))
- Enable automatic MIME detection of attachments sent with crash-reports from the `crashpad_handler`. ([#973](https://github.com/getsentry/sentry-native/pull/973), [crashpad#98](https://github.com/getsentry/crashpad/pull/98))
**Fixes**:
- Fix the Linux build when targeting RISC-V. ([#972](https://github.com/getsentry/sentry-native/pull/972))
**Thank you**:
- [@Strive-Sun](https://github.com/Strive-Sun)
- [@jwinarske](https://github.com/jwinarske)
## 0.7.1
**Features**:
- Add user feedback capability to the Native SDK. ([#966](https://github.com/getsentry/sentry-native/pull/966))
**Internal**:
- Remove the `CRASHPAD_WER_ENABLED` build flag. The WER module is now built for all supported Windows targets, and registration is conditional on runtime Windows version checks. ([#950](https://github.com/getsentry/sentry-native/pull/950), [crashpad#96](https://github.com/getsentry/crashpad/pull/96))
**Docs**:
- Add usage of the breadcrumb `data` property to the example. [#951](https://github.com/getsentry/sentry-native/pull/951)
## 0.7.0
**Breaking changes**:
- Make `crashpad` the default backend for Linux. ([#927](https://github.com/getsentry/sentry-native/pull/927))
- Remove build option `SENTRY_CRASHPAD_SYSTEM`. ([#928](https://github.com/getsentry/sentry-native/pull/928))
**Fixes**:
- Maintain `crashpad` client instance during Native SDK lifecycle. ([#910](https://github.com/getsentry/sentry-native/pull/910))
- Specify correct dependencies for CMake client projects using a system-provided breakpad. ([#926](https://github.com/getsentry/sentry-native/pull/926))
- Correct the Windows header include used by `sentry.h`, which fixes the build of [Swift bindings](https://github.com/thebrowsercompany/swift-sentry). ([#935](https://github.com/getsentry/sentry-native/pull/935))
**Internal**:
- Updated `crashpad` to 2023-11-24. ([#912](https://github.com/getsentry/sentry-native/pull/912), [crashpad#91](https://github.com/getsentry/crashpad/pull/91))
- Fixing `crashpad` build for Windows on ARM64. ([#919](https://github.com/getsentry/sentry-native/pull/919), [crashpad#90](https://github.com/getsentry/crashpad/pull/90), [crashpad#92](https://github.com/getsentry/crashpad/pull/92), [crashpad#93](https://github.com/getsentry/crashpad/pull/93), [crashpad#94](https://github.com/getsentry/crashpad/pull/94))
- Remove options memory leak during consent setting. ([#922](https://github.com/getsentry/sentry-native/pull/922))
**Thank you**:
Features, fixes and improvements in this release have been contributed by:
- [@compnerd](https://github.com/compnerd)
- [@stima](https://github.com/stima)
- [@hyp](https://github.com/hyp)
## 0.6.7 ## 0.6.7
**Fixes**: **Fixes**:
- Disable sigaltstack on Android ([#901](https://github.com/getsentry/sentry-native/pull/901)) - Disable sigaltstack on Android. ([#901](https://github.com/getsentry/sentry-native/pull/901))
- Prevent stuck crashpad-client on Windows ([#902](https://github.com/getsentry/sentry-native/pull/902), [crashpad#89](https://github.com/getsentry/crashpad/pull/89)) - Prevent stuck crashpad-client on Windows. ([#902](https://github.com/getsentry/sentry-native/pull/902), [crashpad#89](https://github.com/getsentry/crashpad/pull/89))
## 0.6.6 ## 0.6.6

View File

@ -26,11 +26,11 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
endif() endif()
if(NOT CMAKE_C_STANDARD) if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
endif() endif()
if(NOT CMAKE_CXX_STANDARD) if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
endif() endif()
include(GNUInstallDirs) include(GNUInstallDirs)
@ -55,6 +55,8 @@ endif()
option(SENTRY_PIC "Build sentry (and dependent) libraries as position independent libraries" ON) option(SENTRY_PIC "Build sentry (and dependent) libraries as position independent libraries" ON)
option(SENTRY_TRANSPORT_COMPRESSION "Enable transport gzip compression" OFF)
option(SENTRY_BUILD_TESTS "Build sentry-native tests" "${SENTRY_MAIN_PROJECT}") option(SENTRY_BUILD_TESTS "Build sentry-native tests" "${SENTRY_MAIN_PROJECT}")
option(SENTRY_BUILD_EXAMPLES "Build sentry-native example(s)" "${SENTRY_MAIN_PROJECT}") option(SENTRY_BUILD_EXAMPLES "Build sentry-native example(s)" "${SENTRY_MAIN_PROJECT}")
@ -96,7 +98,7 @@ else()
endif() endif()
set(SENTRY_TRANSPORT ${SENTRY_DEFAULT_TRANSPORT} CACHE STRING set(SENTRY_TRANSPORT ${SENTRY_DEFAULT_TRANSPORT} CACHE STRING
"The HTTP transport that sentry uses to submit events to the sentry server, can be either 'none', 'curl' or 'winhttp' on windows.") "The HTTP transport that sentry uses to submit events to the sentry server, can be either 'none', 'curl' or 'winhttp' on windows.")
if(SENTRY_TRANSPORT STREQUAL "winhttp") if(SENTRY_TRANSPORT STREQUAL "winhttp")
set(SENTRY_TRANSPORT_WINHTTP TRUE) set(SENTRY_TRANSPORT_WINHTTP TRUE)
@ -125,10 +127,8 @@ option(SENTRY_ENABLE_INSTALL "Enable sentry installation" "${SENTRY_MAIN_PROJECT
if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$") if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$")
message(WARNING "Crashpad is not supported for MSVC with XP toolset. Default backend was switched to 'breakpad'") message(WARNING "Crashpad is not supported for MSVC with XP toolset. Default backend was switched to 'breakpad'")
set(SENTRY_DEFAULT_BACKEND "breakpad") set(SENTRY_DEFAULT_BACKEND "breakpad")
elseif((APPLE AND NOT IOS) OR WIN32) elseif((APPLE AND NOT IOS) OR WIN32 OR LINUX)
set(SENTRY_DEFAULT_BACKEND "crashpad") set(SENTRY_DEFAULT_BACKEND "crashpad")
elseif(LINUX)
set(SENTRY_DEFAULT_BACKEND "breakpad")
else() else()
set(SENTRY_DEFAULT_BACKEND "inproc") set(SENTRY_DEFAULT_BACKEND "inproc")
endif() endif()
@ -280,17 +280,20 @@ if(SENTRY_TRANSPORT_CURL)
find_package(CURL REQUIRED COMPONENTS AsynchDNS) find_package(CURL REQUIRED COMPONENTS AsynchDNS)
endif() endif()
if(TARGET CURL::libcurl) # Only available in cmake 3.12+ target_link_libraries(sentry PRIVATE CURL::libcurl)
target_link_libraries(sentry PRIVATE CURL::libcurl) endif()
else()
# Needed for cmake < 3.12 support (cmake 3.12 introduced the target CURL::libcurl) if(SENTRY_TRANSPORT_COMPRESSION)
target_include_directories(sentry PRIVATE ${CURL_INCLUDE_DIR}) if(NOT ZLIB_FOUND)
# The exported sentry target must not contain any path of the build machine, therefore use generator expressions find_package(ZLIB REQUIRED)
string(REPLACE ";" "$<SEMICOLON>" GENEX_CURL_LIBRARIES "${CURL_LIBRARIES}")
string(REPLACE ";" "$<SEMICOLON>" GENEX_CURL_COMPILE_DEFINITIONS "${CURL_COMPILE_DEFINITIONS}")
target_link_libraries(sentry PRIVATE $<BUILD_INTERFACE:${GENEX_CURL_LIBRARIES}>)
target_compile_definitions(sentry PRIVATE $<BUILD_INTERFACE:${GENEX_CURL_COMPILE_DEFINITIONS}>)
endif() endif()
if(SENTRY_BACKEND_CRASHPAD)
set(CRASHPAD_ZLIB_SYSTEM ON CACHE BOOL "Force CRASHPAD_ZLIB_SYSTEM when enabling transport compression" FORCE)
endif()
target_link_libraries(sentry PRIVATE ZLIB::ZLIB)
target_compile_definitions(sentry PRIVATE SENTRY_TRANSPORT_COMPRESSION)
endif() endif()
set_property(TARGET sentry PROPERTY C_VISIBILITY_PRESET hidden) set_property(TARGET sentry PROPERTY C_VISIBILITY_PRESET hidden)
@ -367,11 +370,11 @@ endif()
# handle platform libraries # handle platform libraries
if(ANDROID) if(ANDROID)
set(_SENTRY_PLATFORM_LIBS "dl" "log") set(_SENTRY_PLATFORM_LIBS "dl" "log")
elseif(LINUX) elseif(LINUX)
set(_SENTRY_PLATFORM_LIBS "dl" "rt") set(_SENTRY_PLATFORM_LIBS "dl" "rt")
elseif(WIN32) elseif(WIN32)
set(_SENTRY_PLATFORM_LIBS "dbghelp" "shlwapi" "version") set(_SENTRY_PLATFORM_LIBS "dbghelp" "shlwapi" "version")
endif() endif()
if(SENTRY_TRANSPORT_WINHTTP) if(SENTRY_TRANSPORT_WINHTTP)
@ -385,9 +388,9 @@ endif()
# apply platform libraries to sentry library # apply platform libraries to sentry library
if(SENTRY_LIBRARY_TYPE STREQUAL "STATIC") if(SENTRY_LIBRARY_TYPE STREQUAL "STATIC")
target_link_libraries(sentry PUBLIC ${_SENTRY_PLATFORM_LIBS}) target_link_libraries(sentry PUBLIC ${_SENTRY_PLATFORM_LIBS})
else() else()
target_link_libraries(sentry PRIVATE ${_SENTRY_PLATFORM_LIBS}) target_link_libraries(sentry PRIVATE ${_SENTRY_PLATFORM_LIBS})
endif() endif()
# suppress some errors and warnings for MinGW target # suppress some errors and warnings for MinGW target
@ -415,88 +418,73 @@ if(SENTRY_WITH_LIBUNWINDSTACK)
endif() endif()
if(SENTRY_BACKEND_CRASHPAD) if(SENTRY_BACKEND_CRASHPAD)
option(SENTRY_CRASHPAD_SYSTEM "Use system crashpad" OFF) if(SENTRY_BUILD_SHARED_LIBS)
if(SENTRY_CRASHPAD_SYSTEM) set(CRASHPAD_ENABLE_INSTALL OFF CACHE BOOL "Enable crashpad installation" FORCE)
find_package(crashpad REQUIRED)
target_link_libraries(sentry PUBLIC crashpad::client)
else() else()
# FIXME: required for cmake 3.12 and lower: set(CRASHPAD_ENABLE_INSTALL ON CACHE BOOL "Enable crashpad installation" FORCE)
# - NEW behavior lets normal variable override option endif()
cmake_policy(SET CMP0077 NEW) add_subdirectory(external/crashpad crashpad_build)
if(SENTRY_BUILD_SHARED_LIBS)
set(CRASHPAD_ENABLE_INSTALL OFF CACHE BOOL "Enable crashpad installation" FORCE)
else()
set(CRASHPAD_ENABLE_INSTALL ON CACHE BOOL "Enable crashpad installation" FORCE)
endif()
add_subdirectory(external/crashpad crashpad_build)
if(CRASHPAD_WER_ENABLED) if(WIN32)
add_dependencies(sentry crashpad::wer) add_dependencies(sentry crashpad::wer)
endif() endif()
# set static runtime if enabled # set static runtime if enabled
if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC) if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC)
set_property(TARGET crashpad_client PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_client PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_compat PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_compat PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_getopt PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_getopt PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_handler PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_handler PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_handler_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_handler_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_minidump PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_minidump PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_snapshot PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_snapshot PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_tools PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_tools PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_util PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_util PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
if(CRASHPAD_WER_ENABLED) set_property(TARGET crashpad_wer PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_wer PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") set_property(TARGET crashpad_zlib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif() set_property(TARGET mini_chromium PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET crashpad_zlib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") endif()
set_property(TARGET mini_chromium PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
if(DEFINED SENTRY_FOLDER) if(DEFINED SENTRY_FOLDER)
set_target_properties(crashpad_client PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_client PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_compat PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_compat PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_getopt PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_getopt PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_handler PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_handler PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_handler_lib PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_handler_lib PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_minidump PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_minidump PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_snapshot PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_snapshot PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_tools PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_tools PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_util PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_util PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_zlib PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(crashpad_zlib PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(mini_chromium PROPERTIES FOLDER ${SENTRY_FOLDER}) set_target_properties(mini_chromium PROPERTIES FOLDER ${SENTRY_FOLDER})
if(CRASHPAD_WER_ENABLED) set_target_properties(crashpad_wer PROPERTIES FOLDER ${SENTRY_FOLDER})
set_target_properties(crashpad_wer PROPERTIES FOLDER ${SENTRY_FOLDER}) endif()
endif()
endif()
target_link_libraries(sentry PRIVATE target_link_libraries(sentry PRIVATE
$<BUILD_INTERFACE:crashpad::client> $<BUILD_INTERFACE:crashpad::client>
$<INSTALL_INTERFACE:sentry_crashpad::client> $<INSTALL_INTERFACE:sentry_crashpad::client>
) )
install(EXPORT crashpad_export NAMESPACE sentry_crashpad:: FILE sentry_crashpad-targets.cmake install(EXPORT crashpad_export NAMESPACE sentry_crashpad:: FILE sentry_crashpad-targets.cmake
DESTINATION "${CMAKE_INSTALL_CMAKEDIR}" DESTINATION "${CMAKE_INSTALL_CMAKEDIR}"
) )
if(WIN32 AND MSVC) if(WIN32 AND MSVC)
sentry_install(FILES $<TARGET_PDB_FILE:crashpad_handler> sentry_install(FILES $<TARGET_PDB_FILE:crashpad_handler>
DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL) DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
if (CRASHPAD_WER_ENABLED) sentry_install(FILES $<TARGET_PDB_FILE:crashpad_wer>
sentry_install(FILES $<TARGET_PDB_FILE:crashpad_wer> DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
endif()
endif()
endif() endif()
add_dependencies(sentry crashpad::handler) add_dependencies(sentry crashpad::handler)
if(CRASHPAD_WER_ENABLED)
add_compile_definitions(CRASHPAD_WER_ENABLED)
endif()
elseif(SENTRY_BACKEND_BREAKPAD) elseif(SENTRY_BACKEND_BREAKPAD)
option(SENTRY_BREAKPAD_SYSTEM "Use system breakpad" OFF) option(SENTRY_BREAKPAD_SYSTEM "Use system breakpad" OFF)
if(SENTRY_BREAKPAD_SYSTEM) if(SENTRY_BREAKPAD_SYSTEM)
# system breakpad is using pkg-config, see `external/breakpad/breakpad-client.pc.in` # system breakpad is using pkg-config, see `external/breakpad/breakpad-client.pc.in`
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(BREAKPAD REQUIRED IMPORTED_TARGET breakpad-client) pkg_check_modules(BREAKPAD REQUIRED IMPORTED_TARGET breakpad-client)
target_link_libraries(sentry PUBLIC PkgConfig::BREAKPAD) if(SENTRY_BUILD_SHARED_LIBS)
target_link_libraries(sentry PRIVATE PkgConfig::BREAKPAD)
else()
target_link_libraries(sentry PUBLIC PkgConfig::BREAKPAD)
endif()
else() else()
add_subdirectory(external) add_subdirectory(external)
target_include_directories(sentry PRIVATE target_include_directories(sentry PRIVATE
@ -579,10 +567,9 @@ if(SENTRY_BUILD_EXAMPLES)
if(MSVC) if(MSVC)
target_compile_options(sentry_example PRIVATE $<BUILD_INTERFACE:/wd5105>) target_compile_options(sentry_example PRIVATE $<BUILD_INTERFACE:/wd5105>)
if(CRASHPAD_WER_ENABLED)
# to test handling SEH by-passing exceptions we need to enable the control flow guard # to test handling SEH by-passing exceptions we need to enable the control flow guard
target_compile_options(sentry_example PRIVATE $<BUILD_INTERFACE:/guard:cf>) target_compile_options(sentry_example PRIVATE $<BUILD_INTERFACE:/guard:cf>)
endif()
endif() endif()
# set static runtime if enabled # set static runtime if enabled
@ -608,4 +595,4 @@ endif()
if(SENTRY_BUILD_SHARED_LIBS) if(SENTRY_BUILD_SHARED_LIBS)
target_link_libraries(sentry PRIVATE target_link_libraries(sentry PRIVATE
"$<$<OR:$<PLATFORM_ID:Linux>,$<PLATFORM_ID:Android>>:-Wl,--build-id=sha1,--version-script=${PROJECT_SOURCE_DIR}/src/exports.map>") "$<$<OR:$<PLATFORM_ID:Linux>,$<PLATFORM_ID:Android>>:-Wl,--build-id=sha1,--version-script=${PROJECT_SOURCE_DIR}/src/exports.map>")
endif() endif()

View File

@ -17,6 +17,10 @@ applications, optimized for C and C++. Sentry allows to add tags, breadcrumbs
and arbitrary custom context to enrich error reports. Supports Sentry _20.6.0_ and arbitrary custom context to enrich error reports. Supports Sentry _20.6.0_
and later. and later.
### Note <!-- omit in toc -->
Using the `sentry-native` SDK in a standalone use case is currently an experimental feature. The SDKs primary function is to fuel our other SDKs, like [`sentry-java`](https://github.com/getsentry/sentry-java) or [`sentry-unreal`](https://github.com/getsentry/sentry-unreal). Support from our side is best effort and we do what we can to respond to issues in a timely fashion, but please understand if we wont be able to address your issues or feature suggestions.
## Resources <!-- omit in toc --> ## Resources <!-- omit in toc -->
- [SDK Documentation](https://docs.sentry.io/platforms/native/) - [SDK Documentation](https://docs.sentry.io/platforms/native/)
@ -227,9 +231,9 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`.
Sentry can use different backends depending on platform. Sentry can use different backends depending on platform.
- **crashpad**: This uses the out-of-process crashpad handler. It is currently - **crashpad**: This uses the out-of-process crashpad handler. It is currently
only supported on Desktop OSs, and used as the default on Windows and macOS. only supported on Desktop OSs, and used as the default on Windows, Linux and macOS.
- **breakpad**: This uses the in-process breakpad handler. It is currently - **breakpad**: This uses the in-process breakpad handler. It is currently
only supported on Desktop OSs, and used as the default on Linux. only supported on Desktop OSs.
- **inproc**: A small in-process handler which is supported on all platforms, - **inproc**: A small in-process handler which is supported on all platforms,
and is used as default on Android. and is used as default on Android.
- **none**: This builds `sentry-native` without a backend, so it does not handle - **none**: This builds `sentry-native` without a backend, so it does not handle
@ -238,12 +242,8 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`.
- `SENTRY_INTEGRATION_QT` (Default: OFF): - `SENTRY_INTEGRATION_QT` (Default: OFF):
Builds the Qt integration, which turns Qt log messages into breadcrumbs. Builds the Qt integration, which turns Qt log messages into breadcrumbs.
- `SENTRY_BREAKPAD_SYSTEM` / `SENTRY_CRASHPAD_SYSTEM` (Default: OFF): - `SENTRY_BREAKPAD_SYSTEM` (Default: OFF):
This instructs the build system to use system-installed breakpad or crashpad This instructs the build system to use system-installed breakpad libraries instead of using the in-tree version.
libraries instead of using the in-tree version. This is generally not recommended
for crashpad, as sentry uses a patched version that has attachment support.
This is being worked on upstream as well, and a future version might work with
an unmodified crashpad version as well.
| Feature | Windows | macOS | Linux | Android | iOS | | Feature | Windows | macOS | Linux | Android | iOS |
| ---------- | ------- | ----- | ----- | ------- | --- | | ---------- | ------- | ----- | ----- | ------- | --- |

View File

@ -9,17 +9,21 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#ifdef NDEBUG #ifdef NDEBUG
# undef NDEBUG # undef NDEBUG
#endif #endif
#include <assert.h> #include <assert.h>
#ifdef SENTRY_PLATFORM_WINDOWS #ifdef SENTRY_PLATFORM_WINDOWS
# include <synchapi.h> # include <synchapi.h>
# define sleep_s(SECONDS) Sleep((SECONDS)*1000) # define sleep_s(SECONDS) Sleep((SECONDS)*1000)
#else #else
# include <signal.h> # include <signal.h>
# include <unistd.h> # include <unistd.h>
# define sleep_s(SECONDS) sleep(SECONDS) # define sleep_s(SECONDS) sleep(SECONDS)
#endif #endif
@ -93,7 +97,9 @@ has_arg(int argc, char **argv, const char *arg)
return false; return false;
} }
#ifdef CRASHPAD_WER_ENABLED #if defined(SENTRY_PLATFORM_WINDOWS) && !defined(__MINGW32__) \
&& !defined(__MINGW64__)
int int
call_rffe_many_times() call_rffe_many_times()
{ {
@ -138,7 +144,7 @@ trigger_fastfail_crash()
__fastfail(77); __fastfail(77);
} }
#endif // CRASHPAD_WER_ENABLED #endif
#ifdef SENTRY_PLATFORM_AIX #ifdef SENTRY_PLATFORM_AIX
// AIX has a null page mapped to the bottom of memory, which means null derefs // AIX has a null page mapped to the bottom of memory, which means null derefs
@ -258,6 +264,21 @@ main(int argc, char **argv)
debug_crumb, "category", sentry_value_new_string("example!")); debug_crumb, "category", sentry_value_new_string("example!"));
sentry_value_set_by_key( sentry_value_set_by_key(
debug_crumb, "level", sentry_value_new_string("debug")); debug_crumb, "level", sentry_value_new_string("debug"));
// extend the `http` crumb with (optional) data properties as documented
// here:
// https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/#breadcrumb-types
sentry_value_t http_data = sentry_value_new_object();
sentry_value_set_by_key(http_data, "url",
sentry_value_new_string("https://example.com/api/1.0/users"));
sentry_value_set_by_key(
http_data, "method", sentry_value_new_string("GET"));
sentry_value_set_by_key(
http_data, "status_code", sentry_value_new_int32(200));
sentry_value_set_by_key(
http_data, "reason", sentry_value_new_string("OK"));
sentry_value_set_by_key(debug_crumb, "data", http_data);
sentry_add_breadcrumb(debug_crumb); sentry_add_breadcrumb(debug_crumb);
sentry_value_t nl_crumb sentry_value_t nl_crumb
@ -301,7 +322,8 @@ main(int argc, char **argv)
if (has_arg(argc, argv, "crash")) { if (has_arg(argc, argv, "crash")) {
trigger_crash(); trigger_crash();
} }
#ifdef CRASHPAD_WER_ENABLED #if defined(SENTRY_PLATFORM_WINDOWS) && !defined(__MINGW32__) \
&& !defined(__MINGW64__)
if (has_arg(argc, argv, "fastfail")) { if (has_arg(argc, argv, "fastfail")) {
trigger_fastfail_crash(); trigger_fastfail_crash();
} }
@ -343,6 +365,16 @@ main(int argc, char **argv)
sentry_capture_event(event); sentry_capture_event(event);
} }
if (has_arg(argc, argv, "capture-user-feedback")) {
sentry_value_t event = sentry_value_new_message_event(
SENTRY_LEVEL_INFO, "my-logger", "Hello user feedback!");
sentry_uuid_t event_id = sentry_capture_event(event);
sentry_value_t user_feedback = sentry_value_new_user_feedback(
&event_id, "some-name", "some-email", "some-comment");
sentry_capture_user_feedback(user_feedback);
}
if (has_arg(argc, argv, "capture-transaction")) { if (has_arg(argc, argv, "capture-transaction")) {
sentry_transaction_context_t *tx_ctx sentry_transaction_context_t *tx_ctx

View File

@ -34,6 +34,18 @@ jobs:
cmake -B cmake-build-stacks -D CRASHPAD_ENABLE_STACKTRACE=ON cmake -B cmake-build-stacks -D CRASHPAD_ENABLE_STACKTRACE=ON
cmake --build cmake-build-stacks --parallel cmake --build cmake-build-stacks --parallel
- name: Build crashpad Windows ARM64
if: ${{ runner.os == 'Windows' }}
run: |
cmake -B cmake-build-arm64 -DCMAKE_TOOLCHAIN_FILE="cmake/toolchains/win_arm64.cmake" -DCRASHPAD_BUILD_TOOLS=On
cmake --build cmake-build-arm64 --config RelWithDebInfo -- /p:Platform=ARM64
- name: Build crashpad with client-side stack traces Windows ARM64
if: ${{ runner.os == 'Windows' }}
run: |
cmake -B cmake-build-stacks-arm64 -DCMAKE_TOOLCHAIN_FILE="cmake/toolchains/win_arm64.cmake" -DCRASHPAD_ENABLE_STACKTRACE=ON
cmake --build cmake-build-stacks-arm64 --config RelWithDebInfo -- /p:Platform=ARM64
build-ios: build-ios:
runs-on: macos-latest runs-on: macos-latest
steps: steps:

View File

@ -21,7 +21,7 @@ else()
endif() endif()
option(CRASHPAD_ZLIB_SYSTEM "Use system zlib library" "${CRASHPAD_ZLIB_SYSTEM_DEFAULT}") option(CRASHPAD_ZLIB_SYSTEM "Use system zlib library" "${CRASHPAD_ZLIB_SYSTEM_DEFAULT}")
if(CRASHPAD_ZLIB_SYSTEM) if(CRASHPAD_ZLIB_SYSTEM AND NOT ZLIB_FOUND)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
endif() endif()
@ -50,7 +50,11 @@ function(crashpad_install_dev)
endfunction() endfunction()
if(WIN32) if(WIN32)
enable_language(ASM_MASM) if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES ARM64)
enable_language(ASM_MARMASM)
else()
enable_language(ASM_MASM)
endif()
if(MINGW) if(MINGW)
find_program(JWASM_FOUND jwasm) find_program(JWASM_FOUND jwasm)
@ -119,24 +123,10 @@ if(MSVC)
$<$<COMPILE_LANGUAGE:C,CXX>:/wd4577> # 'noexcept' used with no exception handling mode specified. $<$<COMPILE_LANGUAGE:C,CXX>:/wd4577> # 'noexcept' used with no exception handling mode specified.
$<$<COMPILE_LANGUAGE:C,CXX>:/wd4996> # 'X' was declared deprecated. $<$<COMPILE_LANGUAGE:C,CXX>:/wd4996> # 'X' was declared deprecated.
) )
# WER support is only available starting from Win10 build 10941
if("${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" VERSION_LESS 10.0.19041)
message(STATUS "WER support disabled. Needs target platform >= 10.0.19041 (actual: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})")
else()
SET(CRASHPAD_WER_ENABLED TRUE)
SET(CRASHPAD_WER_ENABLED TRUE PARENT_SCOPE)
message(STATUS "WER support enabled")
endif()
elseif(MINGW) elseif(MINGW)
# redirect to wmain # redirect to wmain
# FIXME: cmake 3.13 added target_link_options # FIXME: cmake 3.13 added target_link_options
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode")
if(CRASHPAD_WER_ENABLED)
message(STATUS "WER support enabled")
else()
message(STATUS "WER support disabled. Define CRASHPAD_WER_ENABLED = TRUE to enable.")
endif()
endif() endif()
add_library(crashpad::interface ALIAS crashpad_interface) add_library(crashpad::interface ALIAS crashpad_interface)

View File

@ -47,13 +47,13 @@ deps = {
'9719c1e1e676814c456b55f5f070eabad6709d31', '9719c1e1e676814c456b55f5f070eabad6709d31',
'crashpad/third_party/mini_chromium/mini_chromium': 'crashpad/third_party/mini_chromium/mini_chromium':
Var('chromium_git') + '/chromium/mini_chromium@' + Var('chromium_git') + '/chromium/mini_chromium@' +
'10f39a97650a0fe0b305415c15434443c0690a20', '9e21183c1ea369398d6f6ddd302c8db580bd19c4',
'crashpad/third_party/libfuzzer/src': 'crashpad/third_party/libfuzzer/src':
Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' + Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
'fda403cf93ecb8792cb1d061564d89a6553ca020', 'fda403cf93ecb8792cb1d061564d89a6553ca020',
'crashpad/third_party/zlib/zlib': 'crashpad/third_party/zlib/zlib':
Var('chromium_git') + '/chromium/src/third_party/zlib@' + Var('chromium_git') + '/chromium/src/third_party/zlib@' +
'13dc246a58e4b72104d35f9b1809af95221ebda7', 'fef58692c1d7bec94c4ed3d030a45a1832a9615d',
# CIPD packages. # CIPD packages.
'buildtools/linux64': { 'buildtools/linux64': {
@ -89,8 +89,8 @@ deps = {
'crashpad/third_party/linux/clang/linux-amd64': { 'crashpad/third_party/linux/clang/linux-amd64': {
'packages': [ 'packages': [
{ {
'package': 'fuchsia/clang/linux-amd64', 'package': 'fuchsia/third_party/clang/linux-amd64',
'version': 'goma', 'version': 'Tpc85d1ZwSlZ6UKl2d96GRUBGNA5JKholOKe24sRDr0C',
}, },
], ],
'condition': 'checkout_linux and pull_linux_clang', 'condition': 'checkout_linux and pull_linux_clang',
@ -99,8 +99,8 @@ deps = {
'crashpad/third_party/fuchsia/clang/mac-amd64': { 'crashpad/third_party/fuchsia/clang/mac-amd64': {
'packages': [ 'packages': [
{ {
'package': 'fuchsia/clang/mac-amd64', 'package': 'fuchsia/third_party/clang/mac-amd64',
'version': 'goma', 'version': 'MAOjNhwTu5JU3P_0C9dITiyCTtQ1n7lRJnMfB9hhvOkC',
}, },
], ],
'condition': 'checkout_fuchsia and host_os == "mac"', 'condition': 'checkout_fuchsia and host_os == "mac"',
@ -109,8 +109,8 @@ deps = {
'crashpad/third_party/fuchsia/clang/linux-amd64': { 'crashpad/third_party/fuchsia/clang/linux-amd64': {
'packages': [ 'packages': [
{ {
'package': 'fuchsia/clang/linux-amd64', 'package': 'fuchsia/third_party/clang/linux-amd64',
'version': 'goma', 'version': 'Tpc85d1ZwSlZ6UKl2d96GRUBGNA5JKholOKe24sRDr0C',
}, },
], ],
'condition': 'checkout_fuchsia and host_os == "linux"', 'condition': 'checkout_fuchsia and host_os == "linux"',

View File

@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>${IOS_BUNDLE_ID_PREFIX}.googletest.${GTEST_BUNDLE_ID_SUFFIX:rfc1034identifier}</string> <string>${IOS_BUNDLE_ID_PREFIX}.${GTEST_BUNDLE_ID_SUFFIX:rfc1034identifier}</string>
<key>UIApplicationDelegate</key> <key>UIApplicationDelegate</key>
<string>CrashpadUnitTestDelegate</string> <string>CrashpadUnitTestDelegate</string>
</dict> </dict>

View File

@ -246,6 +246,14 @@ class Annotation {
std::atomic<Annotation*>& link_node() { return link_node_; } std::atomic<Annotation*>& link_node() { return link_node_; }
Annotation* GetLinkNode(std::memory_order order = std::memory_order_seq_cst) {
return link_node_.load(order);
}
const Annotation* GetLinkNode(
std::memory_order order = std::memory_order_seq_cst) const {
return link_node_.load(order);
}
private: private:
//! \brief Linked list next-node pointer. Accessed only by \sa AnnotationList. //! \brief Linked list next-node pointer. Accessed only by \sa AnnotationList.
//! //!
@ -323,9 +331,11 @@ class StringAnnotation : public Annotation {
void Set(base::StringPiece string) { void Set(base::StringPiece string) {
Annotation::ValueSizeType size = Annotation::ValueSizeType size =
std::min(MaxSize, base::saturated_cast<ValueSizeType>(string.size())); std::min(MaxSize, base::saturated_cast<ValueSizeType>(string.size()));
memcpy(value_, string.data(), size); string = string.substr(0, size);
std::copy(string.begin(), string.end(), value_);
// Check for no embedded `NUL` characters. // Check for no embedded `NUL` characters.
DCHECK(!memchr(value_, '\0', size)) << "embedded NUL"; DCHECK(string.find('\0', /*pos=*/0) == base::StringPiece::npos)
<< "embedded NUL";
SetSize(size); SetSize(size);
} }

View File

@ -77,7 +77,7 @@ Annotation* AnnotationList::Iterator::operator*() const {
AnnotationList::Iterator& AnnotationList::Iterator::operator++() { AnnotationList::Iterator& AnnotationList::Iterator::operator++() {
CHECK_NE(curr_, tail_); CHECK_NE(curr_, tail_);
curr_ = curr_->link_node(); curr_ = curr_->GetLinkNode();
return *this; return *this;
} }
@ -86,12 +86,42 @@ bool AnnotationList::Iterator::operator==(
return curr_ == other.curr_; return curr_ == other.curr_;
} }
AnnotationList::ConstIterator::ConstIterator(const Annotation* head,
const Annotation* tail)
: curr_(head), tail_(tail) {}
AnnotationList::ConstIterator::~ConstIterator() = default;
const Annotation* AnnotationList::ConstIterator::operator*() const {
CHECK_NE(curr_, tail_);
return curr_;
}
AnnotationList::ConstIterator& AnnotationList::ConstIterator::operator++() {
CHECK_NE(curr_, tail_);
curr_ = curr_->GetLinkNode();
return *this;
}
bool AnnotationList::ConstIterator::operator==(
const AnnotationList::ConstIterator& other) const {
return curr_ == other.curr_;
}
AnnotationList::Iterator AnnotationList::begin() { AnnotationList::Iterator AnnotationList::begin() {
return Iterator(head_.link_node(), tail_pointer_); return Iterator(head_.GetLinkNode(), tail_pointer_);
}
AnnotationList::ConstIterator AnnotationList::cbegin() const {
return ConstIterator(head_.GetLinkNode(), tail_pointer_);
} }
AnnotationList::Iterator AnnotationList::end() { AnnotationList::Iterator AnnotationList::end() {
return Iterator(&tail_, tail_pointer_); return Iterator(&tail_, tail_pointer_);
} }
AnnotationList::ConstIterator AnnotationList::cend() const {
return ConstIterator(&tail_, tail_pointer_);
}
} // namespace crashpad } // namespace crashpad

View File

@ -80,11 +80,37 @@ class AnnotationList {
// Copy and assign are required. // Copy and assign are required.
}; };
//! \brief An InputIterator for iterating a const AnnotationList.
class ConstIterator {
public:
~ConstIterator();
const Annotation* operator*() const;
ConstIterator& operator++();
bool operator==(const ConstIterator& other) const;
bool operator!=(const ConstIterator& other) const {
return !(*this == other);
}
private:
friend class AnnotationList;
ConstIterator(const Annotation* head, const Annotation* tail);
const Annotation* curr_;
const Annotation* const tail_;
// Copy and assign are required.
};
//! \brief Returns an iterator to the first element of the annotation list. //! \brief Returns an iterator to the first element of the annotation list.
Iterator begin(); Iterator begin();
ConstIterator begin() const { return cbegin(); }
ConstIterator cbegin() const;
//! \brief Returns an iterator past the last element of the annotation list. //! \brief Returns an iterator past the last element of the annotation list.
Iterator end(); Iterator end();
ConstIterator end() const { return cend(); }
ConstIterator cend() const;
protected: protected:
#if BUILDFLAG(IS_IOS) #if BUILDFLAG(IS_IOS)

View File

@ -128,6 +128,100 @@ TEST_F(AnnotationList, DuplicateKeys) {
EXPECT_EQ(1u, annotations.size()); EXPECT_EQ(1u, annotations.size());
} }
TEST_F(AnnotationList, IteratorSingleAnnotation) {
ASSERT_EQ(annotations_.begin(), annotations_.end());
ASSERT_EQ(annotations_.cbegin(), annotations_.cend());
one_.Set("1");
auto iterator = annotations_.begin();
auto const_iterator = annotations_.cbegin();
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &one_);
EXPECT_EQ(*const_iterator, &one_);
++iterator;
++const_iterator;
EXPECT_EQ(iterator, annotations_.end());
EXPECT_EQ(const_iterator, annotations_.cend());
}
TEST_F(AnnotationList, IteratorMultipleAnnotationsInserted) {
ASSERT_EQ(annotations_.begin(), annotations_.end());
ASSERT_EQ(annotations_.cbegin(), annotations_.cend());
one_.Set("1");
two_.Set("2");
// New annotations are inserted to the beginning of the list. Hence, |two_|
// must be the first annotation, followed by |one_|.
{
auto iterator = annotations_.begin();
auto const_iterator = annotations_.cbegin();
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &two_);
EXPECT_EQ(*const_iterator, &two_);
++iterator;
++const_iterator;
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &one_);
EXPECT_EQ(*const_iterator, &one_);
++iterator;
++const_iterator;
EXPECT_EQ(iterator, annotations_.end());
EXPECT_EQ(const_iterator, annotations_.cend());
}
}
TEST_F(AnnotationList, IteratorMultipleAnnotationsInsertedAndRemoved) {
ASSERT_EQ(annotations_.begin(), annotations_.end());
ASSERT_EQ(annotations_.cbegin(), annotations_.cend());
one_.Set("1");
two_.Set("2");
one_.Clear();
two_.Clear();
// Even after clearing, Annotations are still inserted in the list and
// reachable via the iterators.
auto iterator = annotations_.begin();
auto const_iterator = annotations_.cbegin();
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &two_);
EXPECT_EQ(*const_iterator, &two_);
++iterator;
++const_iterator;
ASSERT_NE(iterator, annotations_.end());
ASSERT_NE(const_iterator, annotations_.cend());
EXPECT_EQ(*iterator, &one_);
EXPECT_EQ(*const_iterator, &one_);
++iterator;
++const_iterator;
EXPECT_EQ(iterator, annotations_.end());
EXPECT_EQ(const_iterator, annotations_.cend());
}
class RaceThread : public Thread { class RaceThread : public Thread {
public: public:
explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {} explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {}

View File

@ -22,6 +22,7 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "client/settings.h" #include "client/settings.h"

View File

@ -34,8 +34,8 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/scoped_generic.h" #include "base/scoped_generic.h"
#include "base/strings/strcat.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "client/settings.h" #include "client/settings.h"
#include "util/file/directory_reader.h" #include "util/file/directory_reader.h"
@ -116,9 +116,9 @@ bool CreateOrEnsureDirectoryExists(const base::FilePath& path) {
// have changed, and new_name determines whether the returned xattr name will be // have changed, and new_name determines whether the returned xattr name will be
// the old name or its new equivalent. // the old name or its new equivalent.
std::string XattrNameInternal(const base::StringPiece& name, bool new_name) { std::string XattrNameInternal(const base::StringPiece& name, bool new_name) {
return base::StringPrintf(new_name ? "org.chromium.crashpad.database.%s" return base::StrCat({new_name ? "org.chromium.crashpad.database."
: "com.googlecode.crashpad.%s", : "com.googlecode.crashpad.",
name.data()); name});
} }
} // namespace } // namespace

View File

@ -25,6 +25,7 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_math.h" #include "base/numerics/safe_math.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
@ -545,8 +546,7 @@ void Metadata::Write() {
for (const auto& report : reports_) { for (const auto& report : reports_) {
const base::FilePath& path = report.file_path; const base::FilePath& path = report.file_path;
if (path.DirName() != report_dir_) { if (path.DirName() != report_dir_) {
LOG(ERROR) << path.value().c_str() << " expected to start with " LOG(ERROR) << path << " expected to start with " << report_dir_;
<< base::WideToUTF8(report_dir_.value());
return; return;
} }
records.push_back(MetadataFileReportRecord(report, &string_table)); records.push_back(MetadataFileReportRecord(report, &string_table));
@ -590,12 +590,11 @@ OperationStatus Metadata::VerifyReport(const ReportDisk& report_disk,
bool EnsureDirectory(const base::FilePath& path) { bool EnsureDirectory(const base::FilePath& path) {
DWORD fileattr = GetFileAttributes(path.value().c_str()); DWORD fileattr = GetFileAttributes(path.value().c_str());
if (fileattr == INVALID_FILE_ATTRIBUTES) { if (fileattr == INVALID_FILE_ATTRIBUTES) {
PLOG(ERROR) << "GetFileAttributes " << base::WideToUTF8(path.value()); PLOG(ERROR) << "GetFileAttributes " << path;
return false; return false;
} }
if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) == 0) { if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
LOG(ERROR) << "GetFileAttributes " << base::WideToUTF8(path.value()) LOG(ERROR) << "GetFileAttributes " << path << ": not a directory";
<< ": not a directory";
return false; return false;
} }
return true; return true;
@ -877,7 +876,7 @@ OperationStatus CrashReportDatabaseWin::DeleteReport(const UUID& uuid) {
return os; return os;
if (!DeleteFile(report_path.value().c_str())) { if (!DeleteFile(report_path.value().c_str())) {
PLOG(ERROR) << "DeleteFile " << base::WideToUTF8(report_path.value()); PLOG(ERROR) << "DeleteFile " << report_path;
return kFileSystemError; return kFileSystemError;
} }
@ -1021,8 +1020,7 @@ void CrashReportDatabaseWin::CleanOrphanedAttachments() {
if (IsDirectory(path, false)) { if (IsDirectory(path, false)) {
UUID uuid; UUID uuid;
if (!uuid.InitializeFromString(filename.value())) { if (!uuid.InitializeFromString(filename.value())) {
LOG(ERROR) << "unexpected attachment dir name " LOG(ERROR) << "unexpected attachment dir name " << filename;
<< filename.value().c_str();
continue; continue;
} }

View File

@ -835,11 +835,17 @@ class CrashpadClient {
#endif #endif
private: private:
#if BUILDFLAG(IS_WIN)
//! \brief Registers process handlers for the client.
void RegisterHandlers();
#endif
#if BUILDFLAG(IS_APPLE) #if BUILDFLAG(IS_APPLE)
base::apple::ScopedMachSendRight exception_port_; base::apple::ScopedMachSendRight exception_port_;
#elif BUILDFLAG(IS_WIN) #elif BUILDFLAG(IS_WIN)
std::wstring ipc_pipe_; std::wstring ipc_pipe_;
ScopedKernelHANDLE handler_start_thread_; ScopedKernelHANDLE handler_start_thread_;
ScopedVectoredExceptionRegistration vectored_handler_;
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) #elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
std::set<int> unhandled_signals_; std::set<int> unhandled_signals_;
#endif // BUILDFLAG(IS_APPLE) #endif // BUILDFLAG(IS_APPLE)

View File

@ -20,6 +20,7 @@
#include <lib/zx/process.h> #include <lib/zx/process.h>
#include <zircon/processargs.h> #include <zircon/processargs.h>
#include "base/check_op.h"
#include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h" #include "base/logging.h"
#include "client/client_argv_handling.h" #include "client/client_argv_handling.h"

View File

@ -30,6 +30,7 @@
#include <atomic> #include <atomic>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "build/build_config.h" #include "build/build_config.h"

View File

@ -25,6 +25,7 @@
#include <utility> #include <utility>
#include "base/apple/mach_logging.h" #include "base/apple/mach_logging.h"
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "util/mac/mac_util.h" #include "util/mac/mac_util.h"

View File

@ -23,8 +23,10 @@
#include <string.h> #include <string.h>
#include <memory> #include <memory>
#include <string_view>
#include "base/atomicops.h" #include "base/atomicops.h"
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/scoped_generic.h" #include "base/scoped_generic.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
@ -203,6 +205,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
#if !defined(ADDRESS_SANITIZER)
LONG WINAPI HandleHeapCorruption(EXCEPTION_POINTERS* exception_pointers) { LONG WINAPI HandleHeapCorruption(EXCEPTION_POINTERS* exception_pointers) {
if (exception_pointers->ExceptionRecord->ExceptionCode == if (exception_pointers->ExceptionRecord->ExceptionCode ==
STATUS_HEAP_CORRUPTION) { STATUS_HEAP_CORRUPTION) {
@ -211,6 +214,7 @@ LONG WINAPI HandleHeapCorruption(EXCEPTION_POINTERS* exception_pointers) {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
#endif
void HandleAbortSignal(int signum) { void HandleAbortSignal(int signum) {
DCHECK_EQ(signum, SIGABRT); DCHECK_EQ(signum, SIGABRT);
@ -537,7 +541,7 @@ bool StartHandlerProcess(
// invalid command line where the first argument needed by rundll32 is not in // invalid command line where the first argument needed by rundll32 is not in
// the correct format as required in: // the correct format as required in:
// https://support.microsoft.com/en-ca/help/164787/info-windows-rundll-and-rundll32-interface // https://support.microsoft.com/en-ca/help/164787/info-windows-rundll-and-rundll32-interface
const base::WStringPiece kRunDll32Exe(L"rundll32.exe"); const std::wstring_view kRunDll32Exe(L"rundll32.exe");
bool is_embedded_in_dll = false; bool is_embedded_in_dll = false;
if (data->handler.value().size() >= kRunDll32Exe.size() && if (data->handler.value().size() >= kRunDll32Exe.size() &&
_wcsicmp(data->handler.value() _wcsicmp(data->handler.value()
@ -609,45 +613,10 @@ void CommonInProcessInitialization() {
g_non_crash_dump_lock = new base::Lock(); g_non_crash_dump_lock = new base::Lock();
} }
void RegisterHandlers() {
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
// Windows swallows heap corruption failures but we can intercept them with
// a vectored exception handler.
#if defined(ADDRESS_SANITIZER)
// Let ASAN have first go.
bool go_first = false;
#else
bool go_first = true;
#endif
AddVectoredExceptionHandler(go_first, HandleHeapCorruption);
// The Windows CRT's signal.h lists:
// - SIGINT
// - SIGILL
// - SIGFPE
// - SIGSEGV
// - SIGTERM
// - SIGBREAK
// - SIGABRT
// SIGILL and SIGTERM are documented as not being generated. SIGBREAK and
// SIGINT are for Ctrl-Break and Ctrl-C, and aren't something for which
// capturing a dump is warranted. SIGFPE and SIGSEGV are captured as regular
// exceptions through the unhandled exception filter. This leaves SIGABRT. In
// the standard CRT, abort() is implemented as a synchronous call to the
// SIGABRT signal handler if installed, but after doing so, the unhandled
// exception filter is not triggered (it instead __fastfail()s). So, register
// to handle SIGABRT to catch abort() calls, as client code might use this and
// expect it to cause a crash dump. This will only work when the abort()
// that's called in client code is the same (or has the same behavior) as the
// one in use here.
void (*rv)(int) = signal(SIGABRT, HandleAbortSignal);
DCHECK_NE(rv, SIG_ERR);
}
} // namespace } // namespace
CrashpadClient::CrashpadClient() : ipc_pipe_(), handler_start_thread_() {} CrashpadClient::CrashpadClient()
: ipc_pipe_(), handler_start_thread_(), vectored_handler_() {}
CrashpadClient::~CrashpadClient() {} CrashpadClient::~CrashpadClient() {}
@ -723,6 +692,42 @@ bool CrashpadClient::StartHandler(
} }
} }
void CrashpadClient::RegisterHandlers() {
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
// Windows swallows heap corruption failures but we can intercept them with
// a vectored exception handler. Note that a vectored exception handler is
// not compatible with or generally helpful in ASAN builds (ASAN inserts a
// bad dereference at the beginning of the handler, leading to recursive
// invocation of the handler).
#if !defined(ADDRESS_SANITIZER)
PVOID handler = AddVectoredExceptionHandler(true, HandleHeapCorruption);
vectored_handler_.reset(handler);
#endif
// The Windows CRT's signal.h lists:
// - SIGINT
// - SIGILL
// - SIGFPE
// - SIGSEGV
// - SIGTERM
// - SIGBREAK
// - SIGABRT
// SIGILL and SIGTERM are documented as not being generated. SIGBREAK and
// SIGINT are for Ctrl-Break and Ctrl-C, and aren't something for which
// capturing a dump is warranted. SIGFPE and SIGSEGV are captured as regular
// exceptions through the unhandled exception filter. This leaves SIGABRT. In
// the standard CRT, abort() is implemented as a synchronous call to the
// SIGABRT signal handler if installed, but after doing so, the unhandled
// exception filter is not triggered (it instead __fastfail()s). So, register
// to handle SIGABRT to catch abort() calls, as client code might use this and
// expect it to cause a crash dump. This will only work when the abort()
// that's called in client code is the same (or has the same behavior) as the
// one in use here.
void (*rv)(int) = signal(SIGABRT, HandleAbortSignal);
DCHECK_NE(rv, SIG_ERR);
}
bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) { bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) {
DCHECK(ipc_pipe_.empty()); DCHECK(ipc_pipe_.empty());
DCHECK(!ipc_pipe.empty()); DCHECK(!ipc_pipe.empty());

View File

@ -324,6 +324,20 @@ bool ModulePathMatchesSinkhole(const char* path, const char* sinkhole) {
#endif #endif
} }
//! \brief Helper to release memory from calls to __cxa_allocate_exception.
class ScopedException {
public:
explicit ScopedException(objc_exception* exception) : exception_(exception) {}
ScopedException(const ScopedException&) = delete;
ScopedException& operator=(const ScopedException&) = delete;
~ScopedException() { __cxxabiv1::__cxa_free_exception(exception_); }
private:
objc_exception* exception_; // weak
};
id ObjcExceptionPreprocessor(id exception) { id ObjcExceptionPreprocessor(id exception) {
// Some sinkholes don't use objc_exception_rethrow when they should, which // Some sinkholes don't use objc_exception_rethrow when they should, which
// would otherwise prevent the exception_preprocessor from getting called // would otherwise prevent the exception_preprocessor from getting called
@ -384,6 +398,7 @@ id ObjcExceptionPreprocessor(id exception) {
// From 10.15.0 objc4-779.1/runtime/objc-exception.mm objc_exception_throw. // From 10.15.0 objc4-779.1/runtime/objc-exception.mm objc_exception_throw.
objc_exception* exception_objc = reinterpret_cast<objc_exception*>( objc_exception* exception_objc = reinterpret_cast<objc_exception*>(
__cxxabiv1::__cxa_allocate_exception(sizeof(objc_exception))); __cxxabiv1::__cxa_allocate_exception(sizeof(objc_exception)));
ScopedException exception_objc_owner(exception_objc);
exception_objc->obj = exception; exception_objc->obj = exception;
exception_objc->tinfo.vtable = objc_ehtype_vtable + 2; exception_objc->tinfo.vtable = objc_ehtype_vtable + 2;
exception_objc->tinfo.name = object_getClassName(exception); exception_objc->tinfo.name = object_getClassName(exception);

View File

@ -24,6 +24,7 @@
#include <iterator> #include <iterator>
#include <optional> #include <optional>
#include "base/check_op.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "snapshot/snapshot_constants.h" #include "snapshot/snapshot_constants.h"
#include "util/ios/ios_intermediate_dump_writer.h" #include "util/ios/ios_intermediate_dump_writer.h"

View File

@ -15,6 +15,7 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <pthread.h> #include <pthread.h>
#include "base/check.h"
#include "base/logging.h" #include "base/logging.h"
#include "client/crashpad_client.h" #include "client/crashpad_client.h"
#include "util/misc/no_cfi_icall.h" #include "util/misc/no_cfi_icall.h"

View File

@ -0,0 +1,6 @@
# Toolchain file that should provide required and non-conflicting build-
# parameters to allow normal and cross-compilation to ARM64 targets on
# Windows using any generator.
SET(CMAKE_GENERATOR_PLATFORM "ARM64")
SET(CMAKE_SYSTEM_PROCESSOR "ARM64")
SET(CMAKE_SYSTEM_NAME "Windows")

View File

@ -123,7 +123,7 @@ if(NOT IOS)
) )
endif() endif()
if(CRASHPAD_WER_ENABLED) if (WIN32)
add_library(crashpad_wer SHARED add_library(crashpad_wer SHARED
win/wer/crashpad_wer.cc win/wer/crashpad_wer.cc
win/wer/crashpad_wer.h win/wer/crashpad_wer.h
@ -147,4 +147,4 @@ if(CRASHPAD_WER_ENABLED)
install(TARGETS crashpad_wer EXPORT crashpad_export install(TARGETS crashpad_wer EXPORT crashpad_export
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
) )
endif() endif()

View File

@ -25,6 +25,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"

View File

@ -253,7 +253,10 @@ class ExceptionHandlerServerTest : public testing::TestWithParam<bool> {
pid_t last_client; pid_t last_client;
ASSERT_TRUE(server_test_->Delegate()->WaitForException( ASSERT_TRUE(server_test_->Delegate()->WaitForException(
5.0, &last_client, &last_address)); 5.0, &last_client, &last_address));
EXPECT_EQ(last_address, info.exception_information_address); // `exception_information_address` is underaligned and `EXPECT_EQ`
// internally takes arguments by reference. Copy it into a temporary
// before comparing to avoid undefined behavior.
EXPECT_EQ(last_address, VMAddress{info.exception_information_address});
EXPECT_EQ(last_client, ChildPID()); EXPECT_EQ(last_client, ChildPID());
} else { } else {
CheckedReadFileAtEOF(ReadPipeHandle()); CheckedReadFileAtEOF(ReadPipeHandle());

View File

@ -17,6 +17,7 @@
#include <utility> #include <utility>
#include "base/apple/mach_logging.h" #include "base/apple/mach_logging.h"
#include "base/check.h"
#include "base/logging.h" #include "base/logging.h"
#include "util/mach/composite_mach_message_server.h" #include "util/mach/composite_mach_message_server.h"
#include "util/mach/mach_extensions.h" #include "util/mach/mach_extensions.h"

View File

@ -115,7 +115,7 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
for (const auto& attachment : (*attachments_)) { for (const auto& attachment : (*attachments_)) {
FileReader file_reader; FileReader file_reader;
if (!file_reader.Open(attachment)) { if (!file_reader.Open(attachment)) {
LOG(ERROR) << "attachment " << attachment.value().c_str() LOG(ERROR) << "attachment " << attachment
<< " couldn't be opened, skipping"; << " couldn't be opened, skipping";
continue; continue;
} }
@ -124,7 +124,7 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
FileWriter* file_writer = FileWriter* file_writer =
new_report->AddAttachment(base::WideToUTF8(filename.value())); new_report->AddAttachment(base::WideToUTF8(filename.value()));
if (file_writer == nullptr) { if (file_writer == nullptr) {
LOG(ERROR) << "attachment " << filename.value().c_str() LOG(ERROR) << "attachment " << filename
<< " couldn't be created, skipping"; << " couldn't be created, skipping";
continue; continue;
} }

View File

@ -25,6 +25,7 @@
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include "base/check.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/logging.h" #include "base/logging.h"
#include "build/build_config.h" #include "build/build_config.h"

View File

@ -14,6 +14,7 @@
#include <string.h> #include <string.h>
#include "base/check.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/logging.h" #include "base/logging.h"
#include "client/crashpad_client.h" #include "client/crashpad_client.h"

View File

@ -17,6 +17,7 @@
#include <iterator> #include <iterator>
#include "base/check.h"
#include "base/debug/alias.h" #include "base/debug/alias.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/notreached.h" #include "base/notreached.h"

View File

@ -14,6 +14,7 @@
#include <string.h> #include <string.h>
#include "base/check.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/logging.h" #include "base/logging.h"
#include "client/crashpad_client.h" #include "client/crashpad_client.h"

View File

@ -16,6 +16,7 @@
#include <memory> #include <memory>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"
#include "util/numeric/safe_assignment.h" #include "util/numeric/safe_assignment.h"

View File

@ -14,6 +14,7 @@
#include "minidump/minidump_byte_array_writer.h" #include "minidump/minidump_byte_array_writer.h"
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"
#include "util/numeric/safe_assignment.h" #include "util/numeric/safe_assignment.h"

View File

@ -19,6 +19,7 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include "base/check_op.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/logging.h" #include "base/logging.h"
#include "build/build_config.h" #include "build/build_config.h"

View File

@ -16,6 +16,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "minidump/minidump_crashpad_info_writer.h" #include "minidump/minidump_crashpad_info_writer.h"
#include "minidump/minidump_exception_writer.h" #include "minidump/minidump_exception_writer.h"

View File

@ -16,6 +16,7 @@
#include <string> #include <string>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "minidump/minidump_extensions.h" #include "minidump/minidump_extensions.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"

View File

@ -19,6 +19,7 @@
#include <utility> #include <utility>
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"
#include "util/numeric/safe_assignment.h" #include "util/numeric/safe_assignment.h"

View File

@ -145,7 +145,7 @@ class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
//! \brief Conversion functions from a native UTF16 C-string to a char16_t //! \brief Conversion functions from a native UTF16 C-string to a char16_t
//! C-string. No-op where the native UTF16 string is std::u16string. //! C-string. No-op where the native UTF16 string is std::u16string.
#if defined(WCHAR_T_IS_UTF16) || DOXYGEN #if defined(WCHAR_T_IS_16_BIT) || DOXYGEN
inline const char16_t* AsU16CStr(const wchar_t* str) { inline const char16_t* AsU16CStr(const wchar_t* str) {
return reinterpret_cast<const char16_t*>(str); return reinterpret_cast<const char16_t*>(str);
} }

View File

@ -178,8 +178,11 @@ void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_5>(
expected_misc_info.XStateData.SizeOfInfo); expected_misc_info.XStateData.SizeOfInfo);
EXPECT_EQ(observed_misc_info.XStateData.ContextSize, EXPECT_EQ(observed_misc_info.XStateData.ContextSize,
expected_misc_info.XStateData.ContextSize); expected_misc_info.XStateData.ContextSize);
EXPECT_EQ(observed_misc_info.XStateData.EnabledFeatures, // `EnabledFeatures` is underaligned and `EXPECT_EQ` internally takes
expected_misc_info.XStateData.EnabledFeatures); // arguments by reference. Copy it into a temporary before comparing to avoid
// undefined behavior.
EXPECT_EQ(uint64_t{observed_misc_info.XStateData.EnabledFeatures},
uint64_t{expected_misc_info.XStateData.EnabledFeatures});
for (size_t feature_index = 0; for (size_t feature_index = 0;
feature_index < std::size(observed_misc_info.XStateData.Features); feature_index < std::size(observed_misc_info.XStateData.Features);
++feature_index) { ++feature_index) {

View File

@ -16,6 +16,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "minidump/minidump_annotation_writer.h" #include "minidump/minidump_annotation_writer.h"
#include "minidump/minidump_simple_string_dictionary_writer.h" #include "minidump/minidump_simple_string_dictionary_writer.h"

View File

@ -19,6 +19,7 @@
#include <limits> #include <limits>
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "minidump/minidump_string_writer.h" #include "minidump/minidump_string_writer.h"

View File

@ -16,6 +16,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"
#include "util/numeric/safe_assignment.h" #include "util/numeric/safe_assignment.h"

View File

@ -16,6 +16,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"
#include "util/numeric/safe_assignment.h" #include "util/numeric/safe_assignment.h"

View File

@ -6,6 +6,7 @@
#include <limits> #include <limits>
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "snapshot/exception_snapshot.h" #include "snapshot/exception_snapshot.h"
#include "snapshot/thread_snapshot.h" #include "snapshot/thread_snapshot.h"

View File

@ -16,6 +16,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "minidump/minidump_writer_util.h" #include "minidump/minidump_writer_util.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"

View File

@ -16,6 +16,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "minidump/minidump_thread_id_map.h" #include "minidump/minidump_thread_id_map.h"
#include "snapshot/thread_snapshot.h" #include "snapshot/thread_snapshot.h"

View File

@ -16,6 +16,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "minidump/minidump_context_writer.h" #include "minidump/minidump_context_writer.h"
#include "minidump/minidump_memory_writer.h" #include "minidump/minidump_memory_writer.h"

View File

@ -17,6 +17,7 @@
#include <limits> #include <limits>
#include <utility> #include <utility>
#include "base/check_op.h"
#include "minidump/minidump_writer_util.h" #include "minidump/minidump_writer_util.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"
#include "util/numeric/in_range_cast.h" #include "util/numeric/in_range_cast.h"

View File

@ -18,6 +18,7 @@
#include <iterator> #include <iterator>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "util/file/file_writer.h" #include "util/file/file_writer.h"
#include "util/numeric/safe_assignment.h" #include "util/numeric/safe_assignment.h"

View File

@ -21,6 +21,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_math.h" #include "base/numerics/safe_math.h"
#include "build/build_config.h" #include "build/build_config.h"

View File

@ -18,6 +18,7 @@
#include <link.h> #include <link.h>
#include <zircon/syscalls.h> #include <zircon/syscalls.h>
#include "base/check_op.h"
#include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/fuchsia_logging.h"
#include "base/logging.h" #include "base/logging.h"
#include "util/fuchsia/koid_utilities.h" #include "util/fuchsia/koid_utilities.h"

View File

@ -15,6 +15,7 @@
#include "snapshot/ios/exception_snapshot_ios_intermediate_dump.h" #include "snapshot/ios/exception_snapshot_ios_intermediate_dump.h"
#include "base/apple/mach_logging.h" #include "base/apple/mach_logging.h"
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "snapshot/cpu_context.h" #include "snapshot/cpu_context.h"
#include "snapshot/ios/intermediate_dump_reader_util.h" #include "snapshot/ios/intermediate_dump_reader_util.h"

View File

@ -14,6 +14,8 @@
#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h" #include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h"
#include "base/check_op.h"
namespace crashpad { namespace crashpad {
namespace internal { namespace internal {

View File

@ -20,6 +20,7 @@
#include <algorithm> #include <algorithm>
#include "base/check_op.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/notreached.h" #include "base/notreached.h"

View File

@ -22,6 +22,7 @@
#include <limits> #include <limits>
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "client/crashpad_info.h" #include "client/crashpad_info.h"

View File

@ -20,6 +20,7 @@
#include <utility> #include <utility>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "build/build_config.h" #include "build/build_config.h"
@ -39,38 +40,26 @@ std::string SizeLimitedCString(const char* c_string, size_t max_length) {
} // namespace } // namespace
bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type, bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,
const std::string& module_name, const std::string& module_name) {
bool* has_timestamp) {
#if defined(ARCH_CPU_X86_FAMILY) #if defined(ARCH_CPU_X86_FAMILY)
if (mach_o_file_type != MH_BUNDLE) { if (mach_o_file_type != MH_BUNDLE) {
return false; return false;
} }
if (module_name == "cl_kernels") { if (module_name == "cl_kernels") {
if (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 || return __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||
MacOSVersionNumber() >= 10'10'00) { MacOSVersionNumber() >= 10'10'00;
if (has_timestamp) {
*has_timestamp = false;
}
return true;
}
return false;
} }
static const char kCvmsObjectPathPrefix[] = static const char kCvmsObjectPathPrefix[] =
"/private/var/db/CVMS/cvmsCodeSignObj"; "/private/var/db/CVMS/cvmsCodeSignObj";
if (module_name.compare( return module_name.compare(
0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 && 0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 &&
(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14 || (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14 ||
MacOSVersionNumber() >= 10'14'00)) { MacOSVersionNumber() >= 10'14'00);
if (has_timestamp) { #else
*has_timestamp = true;
}
return true;
}
#endif // ARCH_CPU_X86_FAMILY
return false; return false;
#endif // ARCH_CPU_X86_FAMILY
} }
MachOImageSegmentReader::MachOImageSegmentReader() MachOImageSegmentReader::MachOImageSegmentReader()
@ -165,9 +154,8 @@ bool MachOImageSegmentReader::Initialize(ProcessReaderMac* process_reader,
// //
// https://openradar.appspot.com/20239912 // https://openradar.appspot.com/20239912
if (section_segment_name != segment_name && if (section_segment_name != segment_name &&
!(IsMalformedCLKernelsModule(file_type, module_name, nullptr) && !(IsMalformedCLKernelsModule(file_type, module_name) &&
segment_name == SEG_TEXT && segment_name == SEG_TEXT && section_segment_name == "__LD" &&
section_segment_name == "__LD" &&
section_name == "__compact_unwind" && section_name == "__compact_unwind" &&
(section.flags & S_ATTR_DEBUG))) { (section.flags & S_ATTR_DEBUG))) {
LOG(WARNING) << "section.segname incorrect in segment " << segment_name LOG(WARNING) << "section.segname incorrect in segment " << segment_name

View File

@ -53,18 +53,11 @@ namespace crashpad {
//! \param[in] mach_o_file_type The Mach-O type of the module being examined. //! \param[in] mach_o_file_type The Mach-O type of the module being examined.
//! \param[in] module_name The pathname that `dyld` reported having loaded the //! \param[in] module_name The pathname that `dyld` reported having loaded the
//! module from. //! module from.
//! \param[out] has_timestamp Optional, may be `nullptr`. If provided, and the
//! module is a maformed `cl_kernels` module, this will be set to `true` if
//! the module was loaded from the filesystem (as is the case when loaded
//! from the CVMS directory) and is expected to have a timestamp, and
//! `false` otherwise. Note that even when loaded from the filesystem, these
//! modules are unlinked from the filesystem after loading.
//! //!
//! \return `true` if the module appears to be a malformed `cl_kernels` module //! \return `true` if the module appears to be a malformed `cl_kernels` module
//! based on the provided information, `false` otherwise. //! based on the provided information, `false` otherwise.
bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type, bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,
const std::string& module_name, const std::string& module_name);
bool* has_timestamp);
//! \brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O //! \brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O
//! images mapped into another process. //! images mapped into another process.

View File

@ -24,6 +24,7 @@
#include "base/apple/mach_logging.h" #include "base/apple/mach_logging.h"
#include "base/apple/scoped_mach_port.h" #include "base/apple/scoped_mach_port.h"
#include "base/apple/scoped_mach_vm.h" #include "base/apple/scoped_mach_vm.h"
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "snapshot/mac/mach_o_image_reader.h" #include "snapshot/mac/mach_o_image_reader.h"
@ -509,26 +510,27 @@ void ProcessReaderMac::InitializeModules() {
} }
if (file_type == MH_EXECUTE) { if (file_type == MH_EXECUTE) {
// On Mac OS X 10.6, the main executable does not normally show up at // On macOS 14, the main executable does not normally show up at
// index 0. This is because of how 10.6.8 dyld-132.13/src/dyld.cpp // index 0. In previous versions of dyld, each loaded image was
// notifyGDB(), the function resposible for causing // appended to the all image info vector as it was loaded.
// dyld_all_image_infos::infoArray to be updated, is called. It is // (For example, see RuntimeState::notifyDebuggerLoad in dyld-1066.8).
// registered to be called when all dependents of an image have been // Starting from dyld-1122.1, notifyDebuggerLoad calls
// mapped (dyld_image_state_dependents_mapped), meaning that the main // ExternallyViewableState::addImages for all but the main executable
// executable wont be added to the list until all of the libraries it // (which has already been added). ExternallyViewableState::addImages
// depends on are, even though dyld begins looking at the main executable // inserts all new image infos at the front of the vector, leaving the
// first. This changed in later versions of dyld, including those present // main executable as the last item.
// in 10.7. 10.9.4 dyld-239.4/src/dyld.cpp updateAllImages() (renamed from
// notifyGDB()) is registered to be called when an image itself has been
// mapped (dyld_image_state_mapped), regardless of the libraries that it
// depends on.
// //
// The interface requires that the main executable be first in the list, // The interface requires that the main executable be first in the list,
// so swap it into the right position. // so swap it into the right position.
size_t index = modules_.size() - 1; size_t index = modules_.size() - 1;
if (main_executable_count == 0) { if (index > 0) {
std::swap(modules_[0], modules_[index]); CHECK_EQ(index, image_info_vector.size() - 1);
} else { if (main_executable_count == 0) {
std::rotate(
modules_.rbegin(), modules_.rbegin() + 1, modules_.rend());
}
}
if (main_executable_count > 0) {
LOG(WARNING) << base::StringPrintf( LOG(WARNING) << base::StringPrintf(
"multiple MH_EXECUTE modules (%s, %s)", "multiple MH_EXECUTE modules (%s, %s)",
modules_[0].name.c_str(), modules_[0].name.c_str(),

View File

@ -28,6 +28,7 @@
#include <iterator> #include <iterator>
#include <map> #include <map>
#include <unordered_set>
#include <utility> #include <utility>
#include "base/apple/mach_logging.h" #include "base/apple/mach_logging.h"
@ -54,6 +55,15 @@ namespace crashpad {
namespace test { namespace test {
namespace { namespace {
using ModulePathAndAddress = std::pair<std::string, mach_vm_address_t>;
struct PathAndAddressHash {
std::size_t operator()(const ModulePathAndAddress& pair) const {
return std::hash<std::string>()(pair.first) ^
std::hash<mach_vm_address_t>()(pair.second);
}
};
using ModuleSet = std::unordered_set<ModulePathAndAddress, PathAndAddressHash>;
constexpr char kDyldPath[] = "/usr/lib/dyld"; constexpr char kDyldPath[] = "/usr/lib/dyld";
TEST(ProcessReaderMac, SelfBasic) { TEST(ProcessReaderMac, SelfBasic) {
@ -654,9 +664,8 @@ T GetDyldFunction(const char* symbol) {
return reinterpret_cast<T>(dlsym(dl_handle, symbol)); return reinterpret_cast<T>(dlsym(dl_handle, symbol));
} }
void VerifyImageExistenceAndTimestamp(const char* path, time_t timestamp) { void VerifyImageExistence(const char* path) {
const char* stat_path; const char* stat_path;
bool timestamp_may_be_0;
#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_16 #if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_16
static auto _dyld_shared_cache_contains_path = static auto _dyld_shared_cache_contains_path =
@ -686,18 +695,13 @@ void VerifyImageExistenceAndTimestamp(const char* path, time_t timestamp) {
}(); }();
stat_path = dyld_shared_cache_file_path; stat_path = dyld_shared_cache_file_path;
timestamp_may_be_0 = true;
} else { } else {
stat_path = path; stat_path = path;
timestamp_may_be_0 = false;
} }
struct stat stat_buf; struct stat stat_buf;
int rv = stat(stat_path, &stat_buf); int rv = stat(stat_path, &stat_buf);
EXPECT_EQ(rv, 0) << ErrnoMessage("stat"); EXPECT_EQ(rv, 0) << ErrnoMessage("stat");
if (rv == 0 && (!timestamp_may_be_0 || timestamp != 0)) {
EXPECT_EQ(timestamp, stat_buf.st_mtime);
}
} }
// cl_kernels images (OpenCL kernels) are weird. Theyre not ld output and dont // cl_kernels images (OpenCL kernels) are weird. Theyre not ld output and dont
@ -839,67 +843,57 @@ TEST(ProcessReaderMac, SelfModules) {
ASSERT_TRUE(process_reader.Initialize(mach_task_self())); ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
uint32_t dyld_image_count = _dyld_image_count(); uint32_t dyld_image_count = _dyld_image_count();
const std::vector<ProcessReaderMac::Module>& modules =
process_reader.Modules();
// There needs to be at least an entry for the main executable, for a dylib, std::set<std::string> cl_kernel_names;
// and for dyld. auto modules = process_reader.Modules();
ASSERT_GE(modules.size(), 3u); ModuleSet actual_modules;
for (size_t i = 0; i < modules.size(); ++i) {
// dyld_image_count doesnt include an entry for dyld itself, but |modules| auto& module = modules[i];
// does. ASSERT_TRUE(module.reader);
ASSERT_EQ(modules.size(), dyld_image_count + 1); if (i == modules.size() - 1) {
EXPECT_EQ(module.name, kDyldPath);
bool found_cl_kernels = false; const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
for (uint32_t index = 0; index < dyld_image_count; ++index) { if (dyld_image_infos->version >= 2) {
SCOPED_TRACE(base::StringPrintf( EXPECT_EQ(module.reader->Address(),
"index %u, name %s", index, modules[index].name.c_str())); FromPointerCast<mach_vm_address_t>(
dyld_image_infos->dyldImageLoadAddress));
const char* dyld_image_name = _dyld_get_image_name(index);
EXPECT_EQ(modules[index].name, dyld_image_name);
ASSERT_TRUE(modules[index].reader);
EXPECT_EQ(
modules[index].reader->Address(),
FromPointerCast<mach_vm_address_t>(_dyld_get_image_header(index)));
bool expect_timestamp;
if (index == 0 && MacOSVersionNumber() < 12'00'00) {
// Pre-dyld4, dyld didnt set the main executable's timestamp, and it was
// reported as 0.
EXPECT_EQ(modules[index].timestamp, 0);
} else if (IsMalformedCLKernelsModule(modules[index].reader->FileType(),
modules[index].name,
&expect_timestamp)) {
// cl_kernels doesnt exist as a file, but may still have a timestamp.
if (!expect_timestamp) {
EXPECT_EQ(modules[index].timestamp, 0);
} else {
EXPECT_NE(modules[index].timestamp, 0);
} }
found_cl_kernels = true; // Don't include dyld, since dyld image APIs will not have an entry for
// dyld itself.
continue;
}
// Ensure executable is first, and that there's only one.
uint32_t file_type = module.reader->FileType();
if (i == 0) {
EXPECT_EQ(file_type, static_cast<uint32_t>(MH_EXECUTE));
} else { } else {
// Hope that the module didnt change on disk. EXPECT_NE(file_type, static_cast<uint32_t>(MH_EXECUTE));
VerifyImageExistenceAndTimestamp(dyld_image_name, }
modules[index].timestamp); if (IsMalformedCLKernelsModule(module.reader->FileType(), module.name)) {
cl_kernel_names.insert(module.name);
}
actual_modules.insert(
std::make_pair(module.name, module.reader->Address()));
}
EXPECT_EQ(cl_kernel_names.size() > 0,
ExpectCLKernels() && ensure_cl_kernels.success());
// There needs to be at least an entry for the main executable and a dylib.
ASSERT_GE(actual_modules.size(), 2u);
ASSERT_EQ(actual_modules.size(), dyld_image_count);
ModuleSet expect_modules;
for (uint32_t index = 0; index < dyld_image_count; ++index) {
const char* dyld_image_name = _dyld_get_image_name(index);
mach_vm_address_t dyld_image_address =
FromPointerCast<mach_vm_address_t>(_dyld_get_image_header(index));
expect_modules.insert(
std::make_pair(std::string(dyld_image_name), dyld_image_address));
if (cl_kernel_names.find(dyld_image_name) == cl_kernel_names.end()) {
VerifyImageExistence(dyld_image_name);
} }
} }
EXPECT_EQ(actual_modules, expect_modules);
EXPECT_EQ(found_cl_kernels, ExpectCLKernels() && ensure_cl_kernels.success());
size_t index = modules.size() - 1;
EXPECT_EQ(modules[index].name, kDyldPath);
// dyld didnt load itself either, so it couldnt record its timestamp, and it
// is also reported as 0.
EXPECT_EQ(modules[index].timestamp, 0);
const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
if (dyld_image_infos->version >= 2) {
ASSERT_TRUE(modules[index].reader);
EXPECT_EQ(modules[index].reader->Address(),
FromPointerCast<mach_vm_address_t>(
dyld_image_infos->dyldImageLoadAddress));
}
} }
class ProcessReaderModulesChild final : public MachMultiprocess { class ProcessReaderModulesChild final : public MachMultiprocess {
@ -918,27 +912,45 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
void MachMultiprocessParent() override { void MachMultiprocessParent() override {
ProcessReaderMac process_reader; ProcessReaderMac process_reader;
ASSERT_TRUE(process_reader.Initialize(ChildTask())); ASSERT_TRUE(process_reader.Initialize(ChildTask()));
const std::vector<ProcessReaderMac::Module>& modules = const std::vector<ProcessReaderMac::Module>& modules =
process_reader.Modules(); process_reader.Modules();
ModuleSet actual_modules;
std::set<std::string> cl_kernel_names;
for (size_t i = 0; i < modules.size(); ++i) {
auto& module = modules[i];
ASSERT_TRUE(module.reader);
uint32_t file_type = module.reader->FileType();
if (i == 0) {
EXPECT_EQ(file_type, static_cast<uint32_t>(MH_EXECUTE));
} else if (i == modules.size() - 1) {
EXPECT_EQ(file_type, static_cast<uint32_t>(MH_DYLINKER));
} else {
EXPECT_NE(file_type, static_cast<uint32_t>(MH_EXECUTE));
EXPECT_NE(file_type, static_cast<uint32_t>(MH_DYLINKER));
}
if (IsMalformedCLKernelsModule(module.reader->FileType(), module.name)) {
cl_kernel_names.insert(module.name);
}
actual_modules.insert(
std::make_pair(module.name, module.reader->Address()));
}
// There needs to be at least an entry for the main executable, for a dylib, // There needs to be at least an entry for the main executable, for a dylib,
// and for dyld. // and for dyld.
ASSERT_GE(modules.size(), 3u); ASSERT_GE(actual_modules.size(), 3u);
FileHandle read_handle = ReadPipeHandle(); FileHandle read_handle = ReadPipeHandle();
uint32_t expect_modules; uint32_t expect_modules_size;
CheckedReadFileExactly( CheckedReadFileExactly(
read_handle, &expect_modules, sizeof(expect_modules)); read_handle, &expect_modules_size, sizeof(expect_modules_size));
ASSERT_EQ(modules.size(), expect_modules); ASSERT_EQ(actual_modules.size(), expect_modules_size);
ModuleSet expect_modules;
bool found_cl_kernels = false;
for (size_t index = 0; index < modules.size(); ++index) {
SCOPED_TRACE(base::StringPrintf(
"index %zu, name %s", index, modules[index].name.c_str()));
for (size_t index = 0; index < expect_modules_size; ++index) {
uint32_t expect_name_length; uint32_t expect_name_length;
CheckedReadFileExactly( CheckedReadFileExactly(
read_handle, &expect_name_length, sizeof(expect_name_length)); read_handle, &expect_name_length, sizeof(expect_name_length));
@ -946,40 +958,18 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
// The NUL terminator is not read. // The NUL terminator is not read.
std::string expect_name(expect_name_length, '\0'); std::string expect_name(expect_name_length, '\0');
CheckedReadFileExactly(read_handle, &expect_name[0], expect_name_length); CheckedReadFileExactly(read_handle, &expect_name[0], expect_name_length);
EXPECT_EQ(modules[index].name, expect_name);
mach_vm_address_t expect_address; mach_vm_address_t expect_address;
CheckedReadFileExactly( CheckedReadFileExactly(
read_handle, &expect_address, sizeof(expect_address)); read_handle, &expect_address, sizeof(expect_address));
ASSERT_TRUE(modules[index].reader); expect_modules.insert(std::make_pair(expect_name, expect_address));
EXPECT_EQ(modules[index].reader->Address(), expect_address); if (cl_kernel_names.find(expect_name) == cl_kernel_names.end()) {
VerifyImageExistence(expect_name.c_str());
bool expect_timestamp;
if ((index == 0 && MacOSVersionNumber() < 12'00'00) ||
index == modules.size() - 1) {
// Pre-dyld4, dyld didnt set the main executable's timestamp, and it
// was reported as 0.
// The last module is dyld.
EXPECT_EQ(modules[index].timestamp, 0);
} else if (IsMalformedCLKernelsModule(modules[index].reader->FileType(),
modules[index].name,
&expect_timestamp)) {
// cl_kernels doesnt exist as a file, but may still have a timestamp.
if (!expect_timestamp) {
EXPECT_EQ(modules[index].timestamp, 0);
} else {
EXPECT_NE(modules[index].timestamp, 0);
}
found_cl_kernels = true;
} else {
// Hope that the module didnt change on disk.
VerifyImageExistenceAndTimestamp(expect_name.c_str(),
modules[index].timestamp);
} }
} }
EXPECT_EQ(cl_kernel_names.size() > 0,
EXPECT_EQ(found_cl_kernels,
ExpectCLKernels() && ensure_cl_kernels_success_); ExpectCLKernels() && ensure_cl_kernels_success_);
EXPECT_EQ(expect_modules, actual_modules);
} }
void MachMultiprocessChild() override { void MachMultiprocessChild() override {

View File

@ -17,6 +17,7 @@
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include "base/check_op.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "minidump/minidump_extensions.h" #include "minidump/minidump_extensions.h"

View File

@ -346,7 +346,7 @@ bool ProcessSnapshotMinidump::InitializeMiscInfo() {
switch (stream_it->second->DataSize) { switch (stream_it->second->DataSize) {
case sizeof(MINIDUMP_MISC_INFO_5): case sizeof(MINIDUMP_MISC_INFO_5):
case sizeof(MINIDUMP_MISC_INFO_4): case sizeof(MINIDUMP_MISC_INFO_4):
#if defined(WCHAR_T_IS_UTF16) #if defined(WCHAR_T_IS_16_BIT)
full_version_ = base::WideToUTF8(info.BuildString); full_version_ = base::WideToUTF8(info.BuildString);
#else #else
full_version_ = base::UTF16ToUTF8(info.BuildString); full_version_ = base::UTF16ToUTF8(info.BuildString);

View File

@ -868,8 +868,9 @@ TEST(ProcessSnapshotMinidump, ThreadsWithNames) {
} }
TEST(ProcessSnapshotMinidump, System) { TEST(ProcessSnapshotMinidump, System) {
const char* cpu_info = "GenuineIntel"; const char cpu_info[] = "GenuineIntel";
const uint32_t* cpu_info_bytes = reinterpret_cast<const uint32_t*>(cpu_info); uint32_t cpu_info_bytes[3];
memcpy(cpu_info_bytes, cpu_info, sizeof(cpu_info_bytes));
StringFile string_file; StringFile string_file;
MINIDUMP_HEADER header = {}; MINIDUMP_HEADER header = {};

View File

@ -14,6 +14,8 @@
#include "snapshot/sanitized/module_snapshot_sanitized.h" #include "snapshot/sanitized/module_snapshot_sanitized.h"
#include "base/strings/pattern.h"
namespace crashpad { namespace crashpad {
namespace internal { namespace internal {
@ -22,7 +24,7 @@ namespace {
bool KeyIsAllowed(const std::string& name, bool KeyIsAllowed(const std::string& name,
const std::vector<std::string>& allowed_keys) { const std::vector<std::string>& allowed_keys) {
for (const auto& key : allowed_keys) { for (const auto& key : allowed_keys) {
if (name == key) { if (base::MatchPattern(name, key)) {
return true; return true;
} }
} }

View File

@ -53,6 +53,7 @@ class ProcessSnapshotSanitized final : public ProcessSnapshot {
//! \param[in] allowed_annotations A list of annotations names to allow to //! \param[in] allowed_annotations A list of annotations names to allow to
//! be returned by AnnotationsSimpleMap() or from this object's module //! be returned by AnnotationsSimpleMap() or from this object's module
//! snapshots. If `nullptr`, all annotations will be returned. //! snapshots. If `nullptr`, all annotations will be returned.
// These annotation names support pattern matching, eg: "switch-*"
//! \param[in] allowed_memory_ranges A list of memory ranges to allow to be //! \param[in] allowed_memory_ranges A list of memory ranges to allow to be
//! accessible via Memory(), or `nullptr` to allow all ranges. //! accessible via Memory(), or `nullptr` to allow all ranges.
//! \param[in] target_module_address An address in the target process' //! \param[in] target_module_address An address in the target process'

View File

@ -79,6 +79,8 @@ class ExceptionGenerator {
}; };
constexpr char kAllowedAnnotationName[] = "name_of_allowed_anno"; constexpr char kAllowedAnnotationName[] = "name_of_allowed_anno";
constexpr char kAllowedAnnotationNamePattern[] = "name_of_another_*";
constexpr char kAllowedAnnotationNamePatternActual[] = "name_of_another_anno";
constexpr char kAllowedAnnotationValue[] = "some_value"; constexpr char kAllowedAnnotationValue[] = "some_value";
constexpr char kNonAllowedAnnotationName[] = "non_allowed_anno"; constexpr char kNonAllowedAnnotationName[] = "non_allowed_anno";
constexpr char kNonAllowedAnnotationValue[] = "private_annotation"; constexpr char kNonAllowedAnnotationValue[] = "private_annotation";
@ -99,6 +101,10 @@ void ChildTestFunction() {
static StringAnnotation<32> allowed_annotation(kAllowedAnnotationName); static StringAnnotation<32> allowed_annotation(kAllowedAnnotationName);
allowed_annotation.Set(kAllowedAnnotationValue); allowed_annotation.Set(kAllowedAnnotationValue);
static StringAnnotation<32> allowed_matched_annotation(
kAllowedAnnotationNamePatternActual);
allowed_matched_annotation.Set(kAllowedAnnotationValue);
static StringAnnotation<32> non_allowed_annotation(kNonAllowedAnnotationName); static StringAnnotation<32> non_allowed_annotation(kNonAllowedAnnotationName);
non_allowed_annotation.Set(kNonAllowedAnnotationValue); non_allowed_annotation.Set(kNonAllowedAnnotationValue);
@ -129,11 +135,15 @@ CRASHPAD_CHILD_TEST_MAIN(ChildToBeSanitized) {
void ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) { void ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) {
bool found_allowed = false; bool found_allowed = false;
bool found_matched_allowed = false;
bool found_non_allowed = false; bool found_non_allowed = false;
for (auto module : snapshot->Modules()) { for (auto module : snapshot->Modules()) {
for (const auto& anno : module->AnnotationObjects()) { for (const auto& anno : module->AnnotationObjects()) {
if (anno.name == kAllowedAnnotationName) { if (anno.name == kAllowedAnnotationName) {
found_allowed = true; found_allowed = true;
}
if (anno.name == kAllowedAnnotationNamePatternActual) {
found_matched_allowed = true;
} else if (anno.name == kNonAllowedAnnotationName) { } else if (anno.name == kNonAllowedAnnotationName) {
found_non_allowed = true; found_non_allowed = true;
} }
@ -141,6 +151,7 @@ void ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) {
} }
EXPECT_TRUE(found_allowed); EXPECT_TRUE(found_allowed);
EXPECT_TRUE(found_matched_allowed);
if (sanitized) { if (sanitized) {
EXPECT_FALSE(found_non_allowed); EXPECT_FALSE(found_non_allowed);
} else { } else {
@ -279,6 +290,7 @@ class SanitizeTest : public MultiprocessExec {
auto allowed_annotations = std::make_unique<std::vector<std::string>>(); auto allowed_annotations = std::make_unique<std::vector<std::string>>();
allowed_annotations->push_back(kAllowedAnnotationName); allowed_annotations->push_back(kAllowedAnnotationName);
allowed_annotations->push_back(kAllowedAnnotationNamePattern);
auto allowed_memory_ranges = auto allowed_memory_ranges =
std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>(); std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();

View File

@ -22,6 +22,7 @@
#include <memory> #include <memory>
#include "base/check_op.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
@ -173,9 +174,12 @@ void DoStackWalk(ProcessReaderWin::Thread* thread,
// ctx = (LPVOID)ctx_; // ctx = (LPVOID)ctx_;
// } // }
// TODO: we dont support this right away, maybe in the future #elif defined(ARCH_CPU_ARM64)
// #elif defined(ARCH_CPU_ARM64) machine_type = IMAGE_FILE_MACHINE_ARM64;
// machine_type = IMAGE_FILE_MACHINE_ARM64; ctx = *thread->context.context<CONTEXT>();
stack_frame.AddrPC.Offset = ctx.Pc;
stack_frame.AddrFrame.Offset = ctx.Fp;
stack_frame.AddrStack.Offset = ctx.Sp;
#else #else
#error Unsupported Windows Arch #error Unsupported Windows Arch
#endif // ARCH_CPU_X86 #endif // ARCH_CPU_X86

View File

@ -19,6 +19,7 @@
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <string_view>
#include <utility> #include <utility>
#include "base/logging.h" #include "base/logging.h"
@ -329,7 +330,7 @@ void ProcessSnapshotWin::InitializeUnloadedModules() {
uet.SizeOfImage, uet.SizeOfImage,
uet.CheckSum, uet.CheckSum,
uet.TimeDateStamp, uet.TimeDateStamp,
base::WideToUTF8(base::WStringPiece( base::WideToUTF8(std::wstring_view(
uet.ImageName, uet.ImageName,
wcsnlen(uet.ImageName, std::size(uet.ImageName)))))); wcsnlen(uet.ImageName, std::size(uet.ImageName))))));
} }

View File

@ -65,8 +65,11 @@ bundle_data("crashy_module_bundle") {
} }
ios_app_bundle("ios_crash_xcuitests") { ios_app_bundle("ios_crash_xcuitests") {
info_plist = "Info.plist"
testonly = true testonly = true
info_plist = "Info.plist"
if (crashpad_is_in_chromium) {
bundle_identifier = shared_bundle_id_for_test_apps
}
deps = [ deps = [
":app_host_sources", ":app_host_sources",
":crashy_module_bundle", ":crashy_module_bundle",

View File

@ -9,7 +9,7 @@
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string> <string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>${IOS_BUNDLE_ID_PREFIX}.googletest.${EXECUTABLE_NAME:rfc1034identifier}</string> <string>${BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>

View File

@ -17,9 +17,11 @@
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
#include <malloc/malloc.h> #include <malloc/malloc.h>
#include <pthread.h> #include <pthread.h>
#include <limits> #include <limits>
#include "base/apple/mach_logging.h" #include "base/apple/mach_logging.h"
#include "base/check_op.h"
#include "client/crashpad_client.h" #include "client/crashpad_client.h"
#include "util/ios/raw_logging.h" #include "util/ios/raw_logging.h"

View File

@ -17,6 +17,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "base/check.h"
#include "base/logging.h" #include "base/logging.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "util/misc/paths.h" #include "util/misc/paths.h"

View File

@ -20,6 +20,7 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include "base/check.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"

View File

@ -15,7 +15,7 @@ mc_append_sources(
check.h check.h
check_op.h check_op.h
compiler_specific.h compiler_specific.h
cxx17_backports.h containers/span.h
debug/alias.cc debug/alias.cc
debug/alias.h debug/alias.h
files/file_path.cc files/file_path.cc
@ -52,6 +52,9 @@ mc_append_sources(
scoped_generic.h scoped_generic.h
strings/pattern.cc strings/pattern.cc
strings/pattern.h strings/pattern.h
strings/strcat.cc
strings/strcat.h
strings/strcat_internal.h
strings/string_number_conversions.cc strings/string_number_conversions.cc
strings/string_number_conversions.h strings/string_number_conversions.h
strings/string_piece.h strings/string_piece.h
@ -72,6 +75,7 @@ mc_append_sources(
third_party/icu/icu_utf.h third_party/icu/icu_utf.h
threading/thread_local_storage.cc threading/thread_local_storage.cc
threading/thread_local_storage.h threading/thread_local_storage.h
types/cxx23_to_underlying.h
) )
if(NOT MINGW) if(NOT MINGW)
@ -84,26 +88,7 @@ else()
) )
endif() endif()
if(APPLE AND NOT IOS) if(APPLE)
mc_append_sources(
mac/close_nocancel.cc
mac/scoped_ioobject.h
mac/scoped_launch_data.h
apple/bridging.h
apple/foundation_util.h
apple/foundation_util.mm
apple/mach_logging.cc
apple/mach_logging.h
apple/scoped_cftyperef.h
apple/scoped_mach_port.cc
apple/scoped_mach_port.h
apple/scoped_mach_vm.cc
apple/scoped_mach_vm.h
apple/scoped_nsautorelease_pool.h
apple/scoped_nsautorelease_pool.mm
strings/sys_string_conversions_mac.mm
)
elseif(IOS)
mc_append_sources( mc_append_sources(
apple/bridging.h apple/bridging.h
apple/foundation_util.h apple/foundation_util.h
@ -120,6 +105,13 @@ elseif(IOS)
apple/scoped_typeref.h apple/scoped_typeref.h
strings/sys_string_conversions_mac.mm strings/sys_string_conversions_mac.mm
) )
if (NOT IOS)
mc_append_sources(
mac/close_nocancel.cc
mac/scoped_ioobject.h
mac/scoped_launch_data.h
)
endif()
endif() endif()
if(WIN32) if(WIN32)

View File

@ -14,6 +14,7 @@ static_library("base") {
"check.h", "check.h",
"check_op.h", "check_op.h",
"compiler_specific.h", "compiler_specific.h",
"containers/span.h",
"debug/alias.cc", "debug/alias.cc",
"debug/alias.h", "debug/alias.h",
"files/file_path.cc", "files/file_path.cc",
@ -50,6 +51,9 @@ static_library("base") {
"scoped_generic.h", "scoped_generic.h",
"strings/pattern.cc", "strings/pattern.cc",
"strings/pattern.h", "strings/pattern.h",
"strings/strcat.cc",
"strings/strcat.h",
"strings/strcat_internal.h",
"strings/string_number_conversions.cc", "strings/string_number_conversions.cc",
"strings/string_number_conversions.h", "strings/string_number_conversions.h",
"strings/string_piece.h", "strings/string_piece.h",

View File

@ -4,6 +4,8 @@
#include "base/apple/foundation_util.h" #include "base/apple/foundation_util.h"
#include "base/check.h"
#if !BUILDFLAG(IS_IOS) #if !BUILDFLAG(IS_IOS)
extern "C" { extern "C" {
CFTypeID SecACLGetTypeID(); CFTypeID SecACLGetTypeID();

View File

@ -9,7 +9,7 @@
#include <algorithm> #include <algorithm>
#include "base/logging.h" #include "base/check.h"
// Use ScopedMachVM to supervise ownership of pages in the current process // Use ScopedMachVM to supervise ownership of pages in the current process
// through the Mach VM subsystem. Pages allocated with vm_allocate can be // through the Mach VM subsystem. Pages allocated with vm_allocate can be

View File

@ -5,7 +5,7 @@
#ifndef MINI_CHROMIUM_BASE_APPLE_SCOPED_TYPEREF_H_ #ifndef MINI_CHROMIUM_BASE_APPLE_SCOPED_TYPEREF_H_
#define MINI_CHROMIUM_BASE_APPLE_SCOPED_TYPEREF_H_ #define MINI_CHROMIUM_BASE_APPLE_SCOPED_TYPEREF_H_
#include "base/logging.h" #include "base/check.h"
#include "base/memory/scoped_policy.h" #include "base/memory/scoped_policy.h"
namespace base { namespace base {
@ -17,68 +17,125 @@ struct ScopedTypeRefTraits;
template <typename T, typename Traits = ScopedTypeRefTraits<T>> template <typename T, typename Traits = ScopedTypeRefTraits<T>>
class ScopedTypeRef { class ScopedTypeRef {
public: public:
typedef T element_type; using element_type = T;
ScopedTypeRef( // Construction from underlying type
T object = Traits::InvalidValue(),
explicit constexpr ScopedTypeRef(
element_type object = Traits::InvalidValue(),
base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
: object_(object) { : object_(object) {
if (object_ && policy == base::scoped_policy::RETAIN) if (object_ != Traits::InvalidValue() &&
policy == base::scoped_policy::RETAIN) {
object_ = Traits::Retain(object_); object_ = Traits::Retain(object_);
}
} }
ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) : object_(that.object_) { // Copy construction
if (object_)
ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) : object_(that.get()) {
if (object_ != Traits::InvalidValue()) {
object_ = Traits::Retain(object_); object_ = Traits::Retain(object_);
}
} }
~ScopedTypeRef() { template <typename R, typename RTraits>
if (object_) ScopedTypeRef(const ScopedTypeRef<R, RTraits>& that) : object_(that.get()) {
Traits::Release(object_); if (object_ != Traits::InvalidValue()) {
object_ = Traits::Retain(object_);
}
} }
// Copy assignment
ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) { ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) {
reset(that.get(), base::scoped_policy::RETAIN); reset(that.get(), base::scoped_policy::RETAIN);
return *this; return *this;
} }
[[nodiscard]] T* InitializeInto() { template <typename R, typename RTraits>
DCHECK(!object_); ScopedTypeRef& operator=(const ScopedTypeRef<R, RTraits>& that) {
return &object_; reset(that.get(), base::scoped_policy::RETAIN);
return *this;
} }
void reset(T object = Traits::InvalidValue(), // Move construction
ScopedTypeRef(ScopedTypeRef<T, Traits>&& that) : object_(that.release()) {}
template <typename R, typename RTraits>
ScopedTypeRef(ScopedTypeRef<R, RTraits>&& that) : object_(that.release()) {}
// Move assignment
ScopedTypeRef& operator=(ScopedTypeRef<T, Traits>&& that) {
reset(that.release(), base::scoped_policy::ASSUME);
return *this;
}
template <typename R, typename RTraits>
ScopedTypeRef& operator=(ScopedTypeRef<R, RTraits>&& that) {
reset(that.release(), base::scoped_policy::ASSUME);
return *this;
}
// Resetting
template <typename R, typename RTraits>
void reset(const ScopedTypeRef<R, RTraits>& that) {
reset(that.get(), base::scoped_policy::RETAIN);
}
void reset(element_type object = Traits::InvalidValue(),
base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::OwnershipPolicy policy =
base::scoped_policy::ASSUME) { base::scoped_policy::ASSUME) {
if (object && policy == base::scoped_policy::RETAIN) if (object != Traits::InvalidValue() &&
policy == base::scoped_policy::RETAIN) {
object = Traits::Retain(object); object = Traits::Retain(object);
if (object_) }
if (object_ != Traits::InvalidValue()) {
Traits::Release(object_); Traits::Release(object_);
}
object_ = object; object_ = object;
} }
bool operator==(T that) const { return object_ == that; } // Destruction
bool operator!=(T that) const { return object_ != that; } ~ScopedTypeRef() {
if (object_ != Traits::InvalidValue()) {
Traits::Release(object_);
}
}
operator T() const { return object_; } [[nodiscard]] element_type* InitializeInto() {
CHECK_EQ(object_, Traits::InvalidValue());
return &object_;
}
T get() const { return object_; } bool operator==(const ScopedTypeRef& that) const {
return object_ == that.object_;
}
bool operator!=(const ScopedTypeRef& that) const {
return object_ != that.object_;
}
explicit operator bool() const { return object_ != Traits::InvalidValue(); }
element_type get() const { return object_; }
void swap(ScopedTypeRef& that) { void swap(ScopedTypeRef& that) {
T temp = that.object_; element_type temp = that.object_;
that.object_ = object_; that.object_ = object_;
object_ = temp; object_ = temp;
} }
[[nodiscard]] T release() { [[nodiscard]] element_type release() {
T temp = object_; element_type temp = object_;
object_ = Traits::InvalidValue(); object_ = Traits::InvalidValue();
return temp; return temp;
} }
private: private:
T object_; element_type object_;
}; };
} // namespace apple } // namespace apple

View File

@ -5,6 +5,7 @@
#ifndef MINI_CHROMIUM_BASE_CHECK_OP_H_ #ifndef MINI_CHROMIUM_BASE_CHECK_OP_H_
#define MINI_CHROMIUM_BASE_CHECK_OP_H_ #define MINI_CHROMIUM_BASE_CHECK_OP_H_
#include "base/check.h"
#include "base/logging.h" #include "base/logging.h"
namespace logging { namespace logging {

View File

@ -0,0 +1,516 @@
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CONTAINERS_SPAN_H_
#define BASE_CONTAINERS_SPAN_H_
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <iterator>
#include <limits>
#include <type_traits>
#include <utility>
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/numerics/safe_conversions.h"
#include "base/template_util.h"
namespace base {
// [views.constants]
constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
template <typename T,
size_t Extent = dynamic_extent,
typename InternalPtrType = T*>
class span;
namespace internal {
template <size_t I>
using size_constant = std::integral_constant<size_t, I>;
template <typename T>
struct ExtentImpl : size_constant<dynamic_extent> {};
template <typename T, size_t N>
struct ExtentImpl<T[N]> : size_constant<N> {};
template <typename T, size_t N>
struct ExtentImpl<std::array<T, N>> : size_constant<N> {};
template <typename T, size_t N>
struct ExtentImpl<base::span<T, N>> : size_constant<N> {};
template <typename T>
using Extent = ExtentImpl<remove_cvref_t<T>>;
template <typename T>
struct IsSpanImpl : std::false_type {};
template <typename T, size_t Extent>
struct IsSpanImpl<span<T, Extent>> : std::true_type {};
template <typename T>
using IsNotSpan = std::negation<IsSpanImpl<std::decay_t<T>>>;
template <typename T>
struct IsStdArrayImpl : std::false_type {};
template <typename T, size_t N>
struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
template <typename T>
using IsNotStdArray = std::negation<IsStdArrayImpl<std::decay_t<T>>>;
template <typename T>
using IsNotCArray = std::negation<std::is_array<std::remove_reference_t<T>>>;
template <typename From, typename To>
using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
template <typename Container, typename T>
using ContainerHasConvertibleData = IsLegalDataConversion<
std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>,
T>;
template <typename Container>
using ContainerHasIntegralSize =
std::is_integral<decltype(std::size(std::declval<Container>()))>;
template <typename From, size_t FromExtent, typename To, size_t ToExtent>
using EnableIfLegalSpanConversion =
std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
IsLegalDataConversion<From, To>::value>;
// SFINAE check if Array can be converted to a span<T>.
template <typename Array, typename T, size_t Extent>
using EnableIfSpanCompatibleArray =
std::enable_if_t<(Extent == dynamic_extent ||
Extent == internal::Extent<Array>::value) &&
ContainerHasConvertibleData<Array, T>::value>;
// SFINAE check if Container can be converted to a span<T>.
template <typename Container, typename T>
using IsSpanCompatibleContainer =
std::conjunction<IsNotSpan<Container>,
IsNotStdArray<Container>,
IsNotCArray<Container>,
ContainerHasConvertibleData<Container, T>,
ContainerHasIntegralSize<Container>>;
template <typename Container, typename T>
using EnableIfSpanCompatibleContainer =
std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>;
template <typename Container, typename T, size_t Extent>
using EnableIfSpanCompatibleContainerAndSpanIsDynamic =
std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value &&
Extent == dynamic_extent>;
// A helper template for storing the size of a span. Spans with static extents
// don't require additional storage, since the extent itself is specified in the
// template parameter.
template <size_t Extent>
class ExtentStorage {
public:
constexpr explicit ExtentStorage(size_t size) noexcept {}
constexpr size_t size() const noexcept { return Extent; }
};
// Specialization of ExtentStorage for dynamic extents, which do require
// explicit storage for the size.
template <>
struct ExtentStorage<dynamic_extent> {
constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {}
constexpr size_t size() const noexcept { return size_; }
private:
size_t size_;
};
// must_not_be_dynamic_extent prevents |dynamic_extent| from being returned in a
// constexpr context.
template <size_t kExtent>
constexpr size_t must_not_be_dynamic_extent() {
static_assert(
kExtent != dynamic_extent,
"EXTENT should only be used for containers with a static extent.");
return kExtent;
}
} // namespace internal
// A span is a value type that represents an array of elements of type T. Since
// it only consists of a pointer to memory with an associated size, it is very
// light-weight. It is cheap to construct, copy, move and use spans, so that
// users are encouraged to use it as a pass-by-value parameter. A span does not
// own the underlying memory, so care must be taken to ensure that a span does
// not outlive the backing store.
//
// span is somewhat analogous to StringPiece, but with arbitrary element types,
// allowing mutation if T is non-const.
//
// span is implicitly convertible from C++ arrays, as well as most [1]
// container-like types that provide a data() and size() method (such as
// std::vector<T>). A mutable span<T> can also be implicitly converted to an
// immutable span<const T>.
//
// Consider using a span for functions that take a data pointer and size
// parameter: it allows the function to still act on an array-like type, while
// allowing the caller code to be a bit more concise.
//
// For read-only data access pass a span<const T>: the caller can supply either
// a span<const T> or a span<T>, while the callee will have a read-only view.
// For read-write access a mutable span<T> is required.
//
// Without span:
// Read-Only:
// // std::string HexEncode(const uint8_t* data, size_t size);
// std::vector<uint8_t> data_buffer = GenerateData();
// std::string r = HexEncode(data_buffer.data(), data_buffer.size());
//
// Mutable:
// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
// char str_buffer[100];
// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
//
// With span:
// Read-Only:
// // std::string HexEncode(base::span<const uint8_t> data);
// std::vector<uint8_t> data_buffer = GenerateData();
// std::string r = HexEncode(data_buffer);
//
// Mutable:
// // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
// char str_buffer[100];
// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
//
// Spans with "const" and pointers
// -------------------------------
//
// Const and pointers can get confusing. Here are vectors of pointers and their
// corresponding spans:
//
// const std::vector<int*> => base::span<int* const>
// std::vector<const int*> => base::span<const int*>
// const std::vector<const int*> => base::span<const int* const>
//
// Differences from the C++20 draft
// --------------------------------
//
// http://eel.is/c++draft/views contains the latest C++20 draft of std::span.
// Chromium tries to follow the draft as close as possible. Differences between
// the draft and the implementation are documented in subsections below.
//
// Differences from [span.objectrep]:
// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of
// std::byte (std::byte is a C++17 feature)
//
// Differences from [span.cons]:
// - Constructing a static span (i.e. Extent != dynamic_extent) from a dynamic
// sized container (e.g. std::vector) requires an explicit conversion (in the
// C++20 draft this is simply UB)
//
// Furthermore, all constructors and methods are marked noexcept due to the lack
// of exceptions in Chromium.
//
// Due to the lack of class template argument deduction guides in C++14
// appropriate make_span() utility functions are provided.
// [span], class template span
template <typename T, size_t Extent, typename InternalPtrType>
class span : public internal::ExtentStorage<Extent> {
private:
using ExtentStorage = internal::ExtentStorage<Extent>;
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = T*;
using reverse_iterator = std::reverse_iterator<iterator>;
static constexpr size_t extent = Extent;
// [span.cons], span constructors, copy, assignment, and destructor
constexpr span() noexcept : ExtentStorage(0), data_(nullptr) {
static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
}
constexpr span(T* data, size_t size) noexcept
: ExtentStorage(size), data_(data) {
CHECK(Extent == dynamic_extent || Extent == size);
}
// Artificially templatized to break ambiguity for span(ptr, 0).
template <typename = void>
constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(begin <= end);
}
template <
size_t N,
typename = internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
constexpr span(T (&array)[N]) noexcept : span(std::data(array), N) {}
template <
typename U,
size_t N,
typename =
internal::EnableIfSpanCompatibleArray<std::array<U, N>&, T, Extent>>
constexpr span(std::array<U, N>& array) noexcept
: span(std::data(array), N) {}
template <typename U,
size_t N,
typename = internal::
EnableIfSpanCompatibleArray<const std::array<U, N>&, T, Extent>>
constexpr span(const std::array<U, N>& array) noexcept
: span(std::data(array), N) {}
// Conversion from a container that has compatible std::data() and integral
// std::size().
template <
typename Container,
typename =
internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&,
T,
Extent>>
constexpr span(Container& container) noexcept
: span(std::data(container), std::size(container)) {}
template <
typename Container,
typename = internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<
const Container&,
T,
Extent>>
constexpr span(const Container& container) noexcept
: span(std::data(container), std::size(container)) {}
constexpr span(const span& other) noexcept = default;
// Conversions from spans of compatible types and extents: this allows a
// span<T> to be seamlessly used as a span<const T>, but not the other way
// around. If extent is not dynamic, OtherExtent has to be equal to Extent.
template <
typename U,
size_t OtherExtent,
typename =
internal::EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
constexpr span(const span<U, OtherExtent>& other)
: span(other.data(), other.size()) {}
constexpr span& operator=(const span& other) noexcept = default;
~span() noexcept = default;
// [span.sub], span subviews
template <size_t Count>
constexpr span<T, Count> first() const noexcept {
static_assert(Count <= Extent, "Count must not exceed Extent");
CHECK(Extent != dynamic_extent || Count <= size());
return {data(), Count};
}
template <size_t Count>
constexpr span<T, Count> last() const noexcept {
static_assert(Count <= Extent, "Count must not exceed Extent");
CHECK(Extent != dynamic_extent || Count <= size());
return {data() + (size() - Count), Count};
}
template <size_t Offset, size_t Count = dynamic_extent>
constexpr span<T,
(Count != dynamic_extent
? Count
: (Extent != dynamic_extent ? Extent - Offset
: dynamic_extent))>
subspan() const noexcept {
static_assert(Offset <= Extent, "Offset must not exceed Extent");
static_assert(Count == dynamic_extent || Count <= Extent - Offset,
"Count must not exceed Extent - Offset");
CHECK(Extent != dynamic_extent || Offset <= size());
CHECK(Extent != dynamic_extent || Count == dynamic_extent ||
Count <= size() - Offset);
return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
}
constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(count <= size());
return {data(), count};
}
constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(count <= size());
return {data() + (size() - count), count};
}
constexpr span<T, dynamic_extent> subspan(size_t offset,
size_t count = dynamic_extent) const
noexcept {
// Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
CHECK(offset <= size());
CHECK(count == dynamic_extent || count <= size() - offset);
return {data() + offset, count != dynamic_extent ? count : size() - offset};
}
// [span.obs], span observers
constexpr size_t size() const noexcept { return ExtentStorage::size(); }
constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
[[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
// [span.elem], span element access
constexpr T& operator[](size_t idx) const noexcept {
// Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
CHECK(idx < size());
return *(data() + idx);
}
constexpr T& front() const noexcept {
static_assert(Extent == dynamic_extent || Extent > 0,
"Extent must not be 0");
CHECK(Extent != dynamic_extent || !empty());
return *data();
}
constexpr T& back() const noexcept {
static_assert(Extent == dynamic_extent || Extent > 0,
"Extent must not be 0");
CHECK(Extent != dynamic_extent || !empty());
return *(data() + size() - 1);
}
constexpr T* data() const noexcept { return data_; }
// [span.iter], span iterator support
constexpr iterator begin() const noexcept {
return data();
}
constexpr iterator end() const noexcept {
return data() + size();
}
constexpr reverse_iterator rbegin() const noexcept {
return reverse_iterator(end());
}
constexpr reverse_iterator rend() const noexcept {
return reverse_iterator(begin());
}
private:
// This field is not a raw_ptr<> because it was filtered by the rewriter
// for: #constexpr-ctor-field-initializer, #global-scope, #union
InternalPtrType data_;
};
// span<T, Extent>::extent can not be declared inline prior to C++17, hence this
// definition is required.
template <class T, size_t Extent, typename InternalPtrType>
constexpr size_t span<T, Extent, InternalPtrType>::extent;
template <typename It,
typename T = std::remove_reference_t<iter_reference_t<It>>>
span(It, StrictNumeric<size_t>) -> span<T>;
template <typename It,
typename End,
typename = std::enable_if_t<!std::is_convertible_v<End, size_t>>,
typename T = std::remove_reference_t<iter_reference_t<It>>>
span(It, End) -> span<T>;
template <typename T, size_t N>
span(T (&)[N]) -> span<T, N>;
template <typename T, size_t N>
span(std::array<T, N>&) -> span<T, N>;
template <typename T, size_t N>
span(const std::array<T, N>&) -> span<const T, N>;
template <typename Container,
typename T = std::remove_pointer_t<
decltype(std::data(std::declval<Container>()))>,
size_t X = internal::Extent<Container>::value>
span(Container&&) -> span<T, X>;
// [span.objectrep], views of object representation
template <typename T, size_t X>
span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
as_bytes(span<T, X> s) noexcept {
return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
}
template <typename T,
size_t X,
typename = std::enable_if_t<!std::is_const_v<T>>>
span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
as_writable_bytes(span<T, X> s) noexcept {
return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
}
// Type-deducing helpers for constructing a span.
template <int&... ExplicitArgumentBarrier, typename T>
constexpr span<T> make_span(T* data, size_t size) noexcept {
return {data, size};
}
template <int&... ExplicitArgumentBarrier, typename T>
constexpr span<T> make_span(T* begin, T* end) noexcept {
return {begin, end};
}
// make_span utility function that deduces both the span's value_type and extent
// from the passed in argument.
//
// Usage: auto span = base::make_span(...);
template <int&... ExplicitArgumentBarrier, typename Container>
constexpr auto make_span(Container&& container) noexcept {
using T =
std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>;
using Extent = internal::Extent<Container>;
return span<T, Extent::value>(std::forward<Container>(container));
}
// make_span utility functions that allow callers to explicit specify the span's
// extent, the value_type is deduced automatically. This is useful when passing
// a dynamically sized container to a method expecting static spans, when the
// container is known to have the correct size.
//
// Note: This will CHECK that N indeed matches size(container).
//
// Usage: auto static_span = base::make_span<N>(...);
template <size_t N, int&... ExplicitArgumentBarrier, typename Container>
constexpr auto make_span(Container&& container) noexcept {
using T =
std::remove_pointer_t<decltype(std::data(std::declval<Container>()))>;
return span<T, N>(std::data(container), std::size(container));
}
} // namespace base
// EXTENT returns the size of any type that can be converted to a |base::span|
// with definite extent, i.e. everything that is a contiguous storage of some
// sort with static size. Specifically, this works for std::array in a constexpr
// context. Note:
// * |std::size| should be preferred for plain arrays.
// * In run-time contexts, functions such as |std::array::size| should be
// preferred.
#define EXTENT(x) \
::base::internal::must_not_be_dynamic_extent<decltype( \
::base::make_span(x))::extent>()
#endif // BASE_CONTAINERS_SPAN_H_

View File

@ -6,8 +6,15 @@
#include <ctype.h> #include <ctype.h>
#include <ostream>
#include "base/check.h"
#include "base/logging.h" #include "base/logging.h"
#if defined(FILE_PATH_USES_WIDE_CHARACTERS)
#include "base/strings/utf_string_conversions.h"
#endif // FILE_PATH_USES_WIDE_CHARACTERS
namespace base { namespace base {
#if defined(FILE_PATH_USES_WIN_SEPARATORS) #if defined(FILE_PATH_USES_WIN_SEPARATORS)
@ -284,6 +291,10 @@ void FilePath::StripTrailingSeparatorsInternal() {
} // namespace base } // namespace base
void PrintTo(const base::FilePath& path, std::ostream* out) { std::ostream& operator<<(std::ostream& os, const base::FilePath& file_path) {
*out << path.value().c_str(); #ifdef FILE_PATH_USES_WIDE_CHARACTERS
return os << base::WideToUTF8(file_path.value());
#else
return os << file_path.value();
#endif
} }

View File

@ -116,6 +116,7 @@
#if BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_WIN)
#define FILE_PATH_USES_DRIVE_LETTERS #define FILE_PATH_USES_DRIVE_LETTERS
#define FILE_PATH_USES_WIN_SEPARATORS #define FILE_PATH_USES_WIN_SEPARATORS
#define FILE_PATH_USES_WIDE_CHARACTERS
#endif // BUILDFLAG(IS_WIN) #endif // BUILDFLAG(IS_WIN)
namespace base { namespace base {
@ -225,8 +226,11 @@ class FilePath {
} // namespace base } // namespace base
// This is required by googletest to print a readable output on test failures. // Streams `file_path`'s value to a byte stream, converting from wide
extern void PrintTo(const base::FilePath& path, std::ostream* out); // characters if called for. (also lets googletest print a readable output on
// test failures)
extern std::ostream& operator<<(std::ostream& os,
const base::FilePath& file_path);
// Macros for string literal initialization of FilePath::CharType[], and for // Macros for string literal initialization of FilePath::CharType[], and for
// using a FilePath::CharType[] in a printf-style format string. // using a FilePath::CharType[] in a printf-style format string.

View File

@ -10,6 +10,8 @@
#if BUILDFLAG(IS_POSIX) #if BUILDFLAG(IS_POSIX)
#include <unistd.h> #include <unistd.h>
#include "base/check.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#endif #endif

View File

@ -23,25 +23,18 @@
#include "build/build_config.h" #include "build/build_config.h"
#if BUILDFLAG(IS_POSIX) #if BUILDFLAG(IS_POSIX) && \
(defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
#error "inttypes.h has already been included before this header file, but " #error "inttypes.h has already been included before this header file, but "
#error "without __STDC_FORMAT_MACROS defined." #error "without __STDC_FORMAT_MACROS defined."
#endif #endif
#if !defined(__STDC_FORMAT_MACROS) #if BUILDFLAG(IS_POSIX) && !defined(__STDC_FORMAT_MACROS)
#define __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS
#endif #endif
#include <inttypes.h> #include <inttypes.h>
// GCC will concatenate wide and narrow strings correctly, so nothing needs to
// be done here.
#define WidePRId64 PRId64
#define WidePRIu64 PRIu64
#define WidePRIx64 PRIx64
#if !defined(PRIuS) #if !defined(PRIuS)
#define PRIuS "zu" #define PRIuS "zu"
#endif #endif
@ -74,28 +67,4 @@
#endif #endif
#endif // BUILDFLAG(IS_APPLE) #endif // BUILDFLAG(IS_APPLE)
#else // BUILDFLAG(IS_WIN)
#if !defined(PRId64)
#define PRId64 "I64d"
#endif
#if !defined(PRIu64)
#define PRIu64 "I64u"
#endif
#if !defined(PRIx64)
#define PRIx64 "I64x"
#endif
#define WidePRId64 L"I64d"
#define WidePRIu64 L"I64u"
#define WidePRIx64 L"I64x"
#if !defined(PRIuS)
#define PRIuS "Iu"
#endif
#endif
#endif // BASE_FORMAT_MACROS_H_ #endif // BASE_FORMAT_MACROS_H_

View File

@ -54,6 +54,7 @@
#include <lib/syslog/global.h> #include <lib/syslog/global.h>
#endif #endif
#include "base/check_op.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"

View File

@ -12,9 +12,6 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include "base/check.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "build/build_config.h" #include "build/build_config.h"
namespace logging { namespace logging {

View File

@ -11,8 +11,8 @@
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include "base/check_op.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/logging.h"
#include "build/build_config.h" #include "build/build_config.h"
#if BUILDFLAG(IS_FUCHSIA) #if BUILDFLAG(IS_FUCHSIA)

View File

@ -0,0 +1,17 @@
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/strings/strcat.h"
#include <string>
#include "base/strings/strcat_internal.h"
namespace base {
std::string StrCat(span<const StringPiece> pieces) {
return internal::StrCatT(pieces);
}
} // namespace base

View File

@ -0,0 +1,66 @@
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_STRINGS_STRCAT_H_
#define BASE_STRINGS_STRCAT_H_
#include <initializer_list>
#include "base/containers/span.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_WIN)
// Guard against conflict with Win32 API StrCat macro:
// check StrCat wasn't and will not be redefined.
#define StrCat StrCat
#endif
namespace base {
// StrCat ----------------------------------------------------------------------
//
// StrCat is a function to perform concatenation on a sequence of strings.
// It is preferrable to a sequence of "a + b + c" because it is both faster and
// generates less code.
//
// std::string result = base::StrCat({"foo ", result, "\nfoo ", bar});
//
// MORE INFO
//
// StrCat can see all arguments at once, so it can allocate one return buffer
// of exactly the right size and copy once, as opposed to a sequence of
// operator+ which generates a series of temporary strings, copying as it goes.
// And by using StringPiece arguments, StrCat can avoid creating temporary
// string objects for char* constants.
//
// ALTERNATIVES
//
// Internal Google / Abseil has a similar StrCat function. That version takes
// an overloaded number of arguments instead of initializer list (overflowing
// to initializer list for many arguments). We don't have any legacy
// requirements and using only initializer_list is simpler and generates
// roughly the same amount of code at the call sites.
//
// Abseil's StrCat also allows numbers by using an intermediate class that can
// be implicitly constructed from either a string or various number types. This
// class formats the numbers into a static buffer for increased performance,
// and the call sites look nice.
//
// As-written Abseil's helper class for numbers generates slightly more code
// than the raw StringPiece version. We can de-inline the helper class'
// constructors which will cause the StringPiece constructors to be de-inlined
// for this call and generate slightly less code. This is something we can
// explore more in the future.
[[nodiscard]] std::string StrCat(span<const StringPiece> pieces);
// Initializer list forwards to the array version.
inline std::string StrCat(std::initializer_list<StringPiece> pieces) {
return StrCat(make_span(pieces));
}
} // namespace base
#endif // BASE_STRINGS_STRCAT_H_

View File

@ -0,0 +1,76 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_STRINGS_STRCAT_INTERNAL_H_
#define BASE_STRINGS_STRCAT_INTERNAL_H_
#include <string>
#include "base/containers/span.h"
#include "base/template_util.h"
namespace base {
namespace internal {
// Optimized version of `std::basic_string::resize()` that skips zero
// initialization of appended characters. Reading from the newly allocated
// characters results in undefined behavior if they are not explicitly
// initialized afterwards. Currently proposed for standardization as
// std::basic_string::resize_and_overwrite: https://wg21.link/P1072R6
template <typename CharT>
auto Resize(std::basic_string<CharT>& str, size_t total_size, priority_tag<1>)
-> decltype(str.__resize_default_init(total_size)) {
str.__resize_default_init(total_size);
}
// Fallback to regular std::basic_string::resize() if invoking
// __resize_default_init is ill-formed.
template <typename CharT>
void Resize(std::basic_string<CharT>& str, size_t total_size, priority_tag<0>) {
str.resize(total_size);
}
// Appends `pieces` to `dest`. Instead of simply calling `dest.append()`
// `pieces.size()` times, this method first resizes `dest` to be of the desired
// size, and then appends each piece via `std::char_traits::copy`. This achieves
// two goals:
// 1) Allocating the desired size all at once avoids other allocations that
// could happen if intermediate allocations did not reserve enough capacity.
// 2) Invoking std::char_traits::copy instead of std::basic_string::append
// avoids having to write the terminating '\0' character n times.
template <typename CharT, typename StringT>
void StrAppendT(std::basic_string<CharT>& dest, span<const StringT> pieces) {
const size_t initial_size = dest.size();
size_t total_size = initial_size;
for (const auto& cur : pieces)
total_size += cur.size();
// Note: As opposed to `reserve()` calling `resize()` with an argument smaller
// than the current `capacity()` does not result in the string releasing spare
// capacity. Furthermore, common std::string implementations apply a geometric
// growth strategy if the current capacity is not sufficient for the newly
// added characters. Since this codepath is also triggered by `resize()`, we
// don't have to manage the std::string's capacity ourselves here to avoid
// performance hits in case `StrAppend()` gets called in a loop.
Resize(dest, total_size, priority_tag<1>());
CharT* dest_char = &dest[initial_size];
for (const auto& cur : pieces) {
std::char_traits<CharT>::copy(dest_char, cur.data(), cur.size());
dest_char += cur.size();
}
}
template <typename StringT>
auto StrCatT(span<const StringT> pieces) {
std::basic_string<typename StringT::value_type> result;
StrAppendT(result, pieces);
return result;
}
} // namespace internal
} // namespace base
#endif // BASE_STRINGS_STRCAT_INTERNAL_H_

View File

@ -160,7 +160,6 @@ std::ostream& operator<<(std::ostream& ostream,
typedef BasicStringPiece<std::string> StringPiece; typedef BasicStringPiece<std::string> StringPiece;
typedef BasicStringPiece<std::u16string> StringPiece16; typedef BasicStringPiece<std::u16string> StringPiece16;
typedef BasicStringPiece<std::wstring> WStringPiece;
inline bool operator==(const StringPiece& x, const StringPiece& y) { inline bool operator==(const StringPiece& x, const StringPiece& y) {
if (x.size() != y.size()) if (x.size() != y.size())

View File

@ -5,7 +5,7 @@
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/apple/bridging.h" #include "base/apple/bridging.h"
#include "base/logging.h" #include "base/check_op.h"
namespace base { namespace base {

View File

@ -32,10 +32,13 @@ bool ReadUnicodeCharacter(const char* src,
int32_t src_len, int32_t src_len,
int32_t* char_index, int32_t* char_index,
uint32_t* code_point_out) { uint32_t* code_point_out) {
int32_t code_point; base_icu::UChar32 code_point;
CBU8_NEXT(src, *char_index, src_len, code_point); CBU8_NEXT(reinterpret_cast<const uint8_t*>(src), *char_index, src_len,
*code_point_out = static_cast<uint32_t>(code_point); code_point);
*code_point_out = code_point;
// The ICU macro above moves to the next char, we want to point to the last
// char consumed.
(*char_index)--; (*char_index)--;
return IsValidCodepoint(code_point); return IsValidCodepoint(code_point);

View File

@ -7,6 +7,7 @@
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <string_view>
#include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversion_utils.h"
#include "build/build_config.h" #include "build/build_config.h"
@ -58,8 +59,8 @@ std::string UTF16ToUTF8(const StringPiece16& utf16) {
return ret; return ret;
} }
#if defined(WCHAR_T_IS_UTF16) #if defined(WCHAR_T_IS_16_BIT)
std::string WideToUTF8(WStringPiece wide) { std::string WideToUTF8(std::wstring_view wide) {
std::string ret; std::string ret;
UTF16ToUTF8( UTF16ToUTF8(
reinterpret_cast<const char16_t*>(wide.data()), wide.size(), &ret); reinterpret_cast<const char16_t*>(wide.data()), wide.size(), &ret);
@ -71,6 +72,6 @@ std::wstring UTF8ToWide(StringPiece utf8) {
return std::wstring(reinterpret_cast<const wchar_t*>(utf16.data()), return std::wstring(reinterpret_cast<const wchar_t*>(utf16.data()),
utf16.size()); utf16.size());
} }
#endif // defined(WCHAR_T_IS_UTF16) #endif // defined(WCHAR_T_IS_16_BIT)
} // namespace } // namespace

View File

@ -6,6 +6,7 @@
#define MINI_CHROMIUM_BASE_STRINGS_UTF_STRING_CONVERSIONS_H_ #define MINI_CHROMIUM_BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
#include <string> #include <string>
#include <string_view>
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "build/build_config.h" #include "build/build_config.h"
@ -17,10 +18,10 @@ std::u16string UTF8ToUTF16(const StringPiece& utf8);
bool UTF16ToUTF8(const char16_t* src, size_t src_len, std::string* output); bool UTF16ToUTF8(const char16_t* src, size_t src_len, std::string* output);
std::string UTF16ToUTF8(const StringPiece16& utf16); std::string UTF16ToUTF8(const StringPiece16& utf16);
#if defined(WCHAR_T_IS_UTF16) #if defined(WCHAR_T_IS_16_BIT)
std::string WideToUTF8(WStringPiece wide); std::string WideToUTF8(std::wstring_view wide);
std::wstring UTF8ToWide(StringPiece utf8); std::wstring UTF8ToWide(StringPiece utf8);
#endif // defined(WCHAR_T_IS_UTF16) #endif // defined(WCHAR_T_IS_16_BIT)
} // namespace } // namespace

View File

@ -4,7 +4,7 @@
#include "base/synchronization/condition_variable.h" #include "base/synchronization/condition_variable.h"
#include "base/logging.h" #include "base/check_op.h"
namespace base { namespace base {

View File

@ -8,7 +8,7 @@
#include "base/synchronization/lock.h" #include "base/synchronization/lock.h"
#include "base/logging.h" #include "base/check_op.h"
#ifndef NDEBUG #ifndef NDEBUG

View File

@ -6,7 +6,7 @@
#include <errno.h> #include <errno.h>
#include "base/logging.h" #include "base/check_op.h"
namespace base { namespace base {
namespace internal { namespace internal {

View File

@ -5,123 +5,48 @@
#ifndef MINI_CHROMIUM_BASE_TEMPLATE_UTIL_H_ #ifndef MINI_CHROMIUM_BASE_TEMPLATE_UTIL_H_
#define MINI_CHROMIUM_BASE_TEMPLATE_UTIL_H_ #define MINI_CHROMIUM_BASE_TEMPLATE_UTIL_H_
#include <stddef.h> #include <type_traits>
#include "build/build_config.h"
namespace base { namespace base {
// template definitions from tr1
template<class T, T v>
struct integral_constant {
static const T value = v;
typedef T value_type;
typedef integral_constant<T, v> type;
};
template <class T, T v> const T integral_constant<T, v>::value;
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
template <class T> struct is_pointer : false_type {};
template <class T> struct is_pointer<T*> : true_type {};
// Member function pointer detection. This is built-in to C++ 11's stdlib, and
// we can remove this when we switch to it.
template<typename T>
struct is_member_function_pointer : false_type {};
template <typename R, typename Z, typename... A>
struct is_member_function_pointer<R(Z::*)(A...)> : true_type {};
template <typename R, typename Z, typename... A>
struct is_member_function_pointer<R(Z::*)(A...) const> : true_type {};
template <class T, class U> struct is_same : public false_type {};
template <class T> struct is_same<T,T> : true_type {};
template<class> struct is_array : public false_type {};
template<class T, size_t n> struct is_array<T[n]> : public true_type {};
template<class T> struct is_array<T[]> : public true_type {};
template <class T> struct is_non_const_reference : false_type {};
template <class T> struct is_non_const_reference<T&> : true_type {};
template <class T> struct is_non_const_reference<const T&> : false_type {};
template <class T> struct is_const : false_type {};
template <class T> struct is_const<const T> : true_type {};
template <class T> struct is_void : false_type {};
template <> struct is_void<void> : true_type {};
namespace internal { namespace internal {
// Types YesType and NoType are guaranteed such that sizeof(YesType) < // Helper to express preferences in an overload set. If more than one overload
// sizeof(NoType). // are available for a given set of parameters the overload with the higher
typedef char YesType; // priority will be chosen.
template <size_t I>
struct priority_tag : priority_tag<I - 1> {};
struct NoType { template <>
YesType dummy[2]; struct priority_tag<0> {};
};
// This class is an implementation detail for is_convertible, and you
// don't need to know how it works to use is_convertible. For those
// who care: we declare two different functions, one whose argument is
// of type To and one with a variadic argument list. We give them
// return types of different size, so we can use sizeof to trick the
// compiler into telling us which function it would have chosen if we
// had called it with an argument of type From. See Alexandrescu's
// _Modern C++ Design_ for more details on this sort of trick.
struct ConvertHelper {
template <typename To>
static YesType Test(To);
template <typename To>
static NoType Test(...);
template <typename From>
static From& Create();
};
// Used to determine if a type is a struct/union/class. Inspired by Boost's
// is_class type_trait implementation.
struct IsClassHelper {
template <typename C>
static YesType Test(void(C::*)(void));
template <typename C>
static NoType Test(...);
};
} // namespace internal } // namespace internal
// Inherits from true_type if From is convertible to To, false_type otherwise. // Implementation of C++20's std::remove_cvref.
// //
// Note that if the type is convertible, this will be a true_type REGARDLESS // References:
// of whether or not the conversion would emit a warning. // - https://en.cppreference.com/w/cpp/types/remove_cvref
template <typename From, typename To> // - https://wg21.link/meta.trans.other#lib:remove_cvref
struct is_convertible
: integral_constant<bool,
sizeof(internal::ConvertHelper::Test<To>(
internal::ConvertHelper::Create<From>())) ==
sizeof(internal::YesType)> {
};
template <typename T> template <typename T>
struct is_class struct remove_cvref {
: integral_constant<bool, using type = std::remove_cv_t<std::remove_reference_t<T>>;
sizeof(internal::IsClassHelper::Test<T>(0)) ==
sizeof(internal::YesType)> {
}; };
template<bool B, class T = void> // Implementation of C++20's std::remove_cvref_t.
struct enable_if {}; //
// References:
// - https://en.cppreference.com/w/cpp/types/remove_cvref
// - https://wg21.link/meta.type.synop#lib:remove_cvref_t
template <typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
template<class T> // Simplified implementation of C++20's std::iter_reference_t.
struct enable_if<true, T> { typedef T type; }; // As opposed to std::iter_reference_t, this implementation does not restrict
// the type of `Iter`.
//
// Reference: https://wg21.link/iterator.synopsis#:~:text=iter_reference_t
template <typename Iter>
using iter_reference_t = decltype(*std::declval<Iter&>());
} // namespace base } // namespace base

Some files were not shown because too many files have changed in this diff Show More