Update sentry-native to 0.7.2
This commit is contained in:
parent
7fdb5e98a2
commit
2f0ff4e557
|
@ -1,11 +1,68 @@
|
|||
# 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
|
||||
|
||||
**Fixes**:
|
||||
|
||||
- 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))
|
||||
- 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))
|
||||
|
||||
## 0.6.6
|
||||
|
||||
|
|
|
@ -26,11 +26,11 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
|
|||
endif()
|
||||
|
||||
if(NOT CMAKE_C_STANDARD)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
@ -55,6 +55,8 @@ endif()
|
|||
|
||||
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_EXAMPLES "Build sentry-native example(s)" "${SENTRY_MAIN_PROJECT}")
|
||||
|
||||
|
@ -96,7 +98,7 @@ else()
|
|||
endif()
|
||||
|
||||
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")
|
||||
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$")
|
||||
message(WARNING "Crashpad is not supported for MSVC with XP toolset. Default backend was switched to '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")
|
||||
elseif(LINUX)
|
||||
set(SENTRY_DEFAULT_BACKEND "breakpad")
|
||||
else()
|
||||
set(SENTRY_DEFAULT_BACKEND "inproc")
|
||||
endif()
|
||||
|
@ -280,17 +280,20 @@ if(SENTRY_TRANSPORT_CURL)
|
|||
find_package(CURL REQUIRED COMPONENTS AsynchDNS)
|
||||
endif()
|
||||
|
||||
if(TARGET CURL::libcurl) # Only available in cmake 3.12+
|
||||
target_link_libraries(sentry PRIVATE CURL::libcurl)
|
||||
else()
|
||||
# Needed for cmake < 3.12 support (cmake 3.12 introduced the target CURL::libcurl)
|
||||
target_include_directories(sentry PRIVATE ${CURL_INCLUDE_DIR})
|
||||
# The exported sentry target must not contain any path of the build machine, therefore use generator expressions
|
||||
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}>)
|
||||
target_link_libraries(sentry PRIVATE CURL::libcurl)
|
||||
endif()
|
||||
|
||||
if(SENTRY_TRANSPORT_COMPRESSION)
|
||||
if(NOT ZLIB_FOUND)
|
||||
find_package(ZLIB REQUIRED)
|
||||
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()
|
||||
|
||||
set_property(TARGET sentry PROPERTY C_VISIBILITY_PRESET hidden)
|
||||
|
@ -367,11 +370,11 @@ endif()
|
|||
|
||||
# handle platform libraries
|
||||
if(ANDROID)
|
||||
set(_SENTRY_PLATFORM_LIBS "dl" "log")
|
||||
set(_SENTRY_PLATFORM_LIBS "dl" "log")
|
||||
elseif(LINUX)
|
||||
set(_SENTRY_PLATFORM_LIBS "dl" "rt")
|
||||
set(_SENTRY_PLATFORM_LIBS "dl" "rt")
|
||||
elseif(WIN32)
|
||||
set(_SENTRY_PLATFORM_LIBS "dbghelp" "shlwapi" "version")
|
||||
set(_SENTRY_PLATFORM_LIBS "dbghelp" "shlwapi" "version")
|
||||
endif()
|
||||
|
||||
if(SENTRY_TRANSPORT_WINHTTP)
|
||||
|
@ -385,9 +388,9 @@ endif()
|
|||
|
||||
# apply platform libraries to sentry library
|
||||
if(SENTRY_LIBRARY_TYPE STREQUAL "STATIC")
|
||||
target_link_libraries(sentry PUBLIC ${_SENTRY_PLATFORM_LIBS})
|
||||
target_link_libraries(sentry PUBLIC ${_SENTRY_PLATFORM_LIBS})
|
||||
else()
|
||||
target_link_libraries(sentry PRIVATE ${_SENTRY_PLATFORM_LIBS})
|
||||
target_link_libraries(sentry PRIVATE ${_SENTRY_PLATFORM_LIBS})
|
||||
endif()
|
||||
|
||||
# suppress some errors and warnings for MinGW target
|
||||
|
@ -415,88 +418,73 @@ if(SENTRY_WITH_LIBUNWINDSTACK)
|
|||
endif()
|
||||
|
||||
if(SENTRY_BACKEND_CRASHPAD)
|
||||
option(SENTRY_CRASHPAD_SYSTEM "Use system crashpad" OFF)
|
||||
if(SENTRY_CRASHPAD_SYSTEM)
|
||||
find_package(crashpad REQUIRED)
|
||||
target_link_libraries(sentry PUBLIC crashpad::client)
|
||||
if(SENTRY_BUILD_SHARED_LIBS)
|
||||
set(CRASHPAD_ENABLE_INSTALL OFF CACHE BOOL "Enable crashpad installation" FORCE)
|
||||
else()
|
||||
# FIXME: required for cmake 3.12 and lower:
|
||||
# - NEW behavior lets normal variable override option
|
||||
cmake_policy(SET CMP0077 NEW)
|
||||
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)
|
||||
set(CRASHPAD_ENABLE_INSTALL ON CACHE BOOL "Enable crashpad installation" FORCE)
|
||||
endif()
|
||||
add_subdirectory(external/crashpad crashpad_build)
|
||||
|
||||
if(CRASHPAD_WER_ENABLED)
|
||||
add_dependencies(sentry crashpad::wer)
|
||||
endif()
|
||||
if(WIN32)
|
||||
add_dependencies(sentry crashpad::wer)
|
||||
endif()
|
||||
|
||||
# set static runtime if enabled
|
||||
if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC)
|
||||
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_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_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_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_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>")
|
||||
endif()
|
||||
set_property(TARGET crashpad_zlib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
set_property(TARGET mini_chromium PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
# set static runtime if enabled
|
||||
if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC)
|
||||
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_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_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_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_util 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>")
|
||||
set_property(TARGET mini_chromium PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
|
||||
if(DEFINED SENTRY_FOLDER)
|
||||
set_target_properties(crashpad_client 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_handler 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_snapshot 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_zlib 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})
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED SENTRY_FOLDER)
|
||||
set_target_properties(crashpad_client 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_handler 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_snapshot 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_zlib PROPERTIES FOLDER ${SENTRY_FOLDER})
|
||||
set_target_properties(mini_chromium PROPERTIES FOLDER ${SENTRY_FOLDER})
|
||||
set_target_properties(crashpad_wer PROPERTIES FOLDER ${SENTRY_FOLDER})
|
||||
endif()
|
||||
|
||||
target_link_libraries(sentry PRIVATE
|
||||
$<BUILD_INTERFACE:crashpad::client>
|
||||
$<INSTALL_INTERFACE:sentry_crashpad::client>
|
||||
)
|
||||
install(EXPORT crashpad_export NAMESPACE sentry_crashpad:: FILE sentry_crashpad-targets.cmake
|
||||
DESTINATION "${CMAKE_INSTALL_CMAKEDIR}"
|
||||
)
|
||||
if(WIN32 AND MSVC)
|
||||
sentry_install(FILES $<TARGET_PDB_FILE:crashpad_handler>
|
||||
DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
|
||||
if (CRASHPAD_WER_ENABLED)
|
||||
sentry_install(FILES $<TARGET_PDB_FILE:crashpad_wer>
|
||||
DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
target_link_libraries(sentry PRIVATE
|
||||
$<BUILD_INTERFACE:crashpad::client>
|
||||
$<INSTALL_INTERFACE:sentry_crashpad::client>
|
||||
)
|
||||
install(EXPORT crashpad_export NAMESPACE sentry_crashpad:: FILE sentry_crashpad-targets.cmake
|
||||
DESTINATION "${CMAKE_INSTALL_CMAKEDIR}"
|
||||
)
|
||||
if(WIN32 AND MSVC)
|
||||
sentry_install(FILES $<TARGET_PDB_FILE:crashpad_handler>
|
||||
DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
|
||||
sentry_install(FILES $<TARGET_PDB_FILE:crashpad_wer>
|
||||
DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL)
|
||||
endif()
|
||||
add_dependencies(sentry crashpad::handler)
|
||||
|
||||
if(CRASHPAD_WER_ENABLED)
|
||||
add_compile_definitions(CRASHPAD_WER_ENABLED)
|
||||
endif()
|
||||
elseif(SENTRY_BACKEND_BREAKPAD)
|
||||
option(SENTRY_BREAKPAD_SYSTEM "Use system breakpad" OFF)
|
||||
if(SENTRY_BREAKPAD_SYSTEM)
|
||||
# system breakpad is using pkg-config, see `external/breakpad/breakpad-client.pc.in`
|
||||
find_package(PkgConfig REQUIRED)
|
||||
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()
|
||||
add_subdirectory(external)
|
||||
target_include_directories(sentry PRIVATE
|
||||
|
@ -579,10 +567,9 @@ if(SENTRY_BUILD_EXAMPLES)
|
|||
|
||||
if(MSVC)
|
||||
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
|
||||
target_compile_options(sentry_example PRIVATE $<BUILD_INTERFACE:/guard:cf>)
|
||||
endif()
|
||||
|
||||
# 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>)
|
||||
endif()
|
||||
|
||||
# set static runtime if enabled
|
||||
|
|
|
@ -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 later.
|
||||
|
||||
### Note <!-- omit in toc -->
|
||||
|
||||
Using the `sentry-native` SDK in a standalone use case is currently an experimental feature. The SDK’s 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 won’t be able to address your issues or feature suggestions.
|
||||
|
||||
## Resources <!-- omit in toc -->
|
||||
|
||||
- [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.
|
||||
|
||||
- **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
|
||||
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,
|
||||
and is used as default on Android.
|
||||
- **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):
|
||||
Builds the Qt integration, which turns Qt log messages into breadcrumbs.
|
||||
|
||||
- `SENTRY_BREAKPAD_SYSTEM` / `SENTRY_CRASHPAD_SYSTEM` (Default: OFF):
|
||||
This instructs the build system to use system-installed breakpad or crashpad
|
||||
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.
|
||||
- `SENTRY_BREAKPAD_SYSTEM` (Default: OFF):
|
||||
This instructs the build system to use system-installed breakpad libraries instead of using the in-tree version.
|
||||
|
||||
| Feature | Windows | macOS | Linux | Android | iOS |
|
||||
| ---------- | ------- | ----- | ----- | ------- | --- |
|
||||
|
|
|
@ -9,17 +9,21 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef NDEBUG
|
||||
# undef NDEBUG
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef SENTRY_PLATFORM_WINDOWS
|
||||
# include <synchapi.h>
|
||||
# define sleep_s(SECONDS) Sleep((SECONDS)*1000)
|
||||
#else
|
||||
|
||||
# include <signal.h>
|
||||
# include <unistd.h>
|
||||
|
||||
# define sleep_s(SECONDS) sleep(SECONDS)
|
||||
#endif
|
||||
|
||||
|
@ -93,7 +97,9 @@ has_arg(int argc, char **argv, const char *arg)
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifdef CRASHPAD_WER_ENABLED
|
||||
#if defined(SENTRY_PLATFORM_WINDOWS) && !defined(__MINGW32__) \
|
||||
&& !defined(__MINGW64__)
|
||||
|
||||
int
|
||||
call_rffe_many_times()
|
||||
{
|
||||
|
@ -138,7 +144,7 @@ trigger_fastfail_crash()
|
|||
__fastfail(77);
|
||||
}
|
||||
|
||||
#endif // CRASHPAD_WER_ENABLED
|
||||
#endif
|
||||
|
||||
#ifdef SENTRY_PLATFORM_AIX
|
||||
// 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!"));
|
||||
sentry_value_set_by_key(
|
||||
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_value_t nl_crumb
|
||||
|
@ -301,7 +322,8 @@ main(int argc, char **argv)
|
|||
if (has_arg(argc, argv, "crash")) {
|
||||
trigger_crash();
|
||||
}
|
||||
#ifdef CRASHPAD_WER_ENABLED
|
||||
#if defined(SENTRY_PLATFORM_WINDOWS) && !defined(__MINGW32__) \
|
||||
&& !defined(__MINGW64__)
|
||||
if (has_arg(argc, argv, "fastfail")) {
|
||||
trigger_fastfail_crash();
|
||||
}
|
||||
|
@ -343,6 +365,16 @@ main(int argc, char **argv)
|
|||
|
||||
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")) {
|
||||
sentry_transaction_context_t *tx_ctx
|
||||
|
|
|
@ -34,6 +34,18 @@ jobs:
|
|||
cmake -B cmake-build-stacks -D CRASHPAD_ENABLE_STACKTRACE=ON
|
||||
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:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
|
|
|
@ -21,7 +21,7 @@ else()
|
|||
endif()
|
||||
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)
|
||||
endif()
|
||||
|
||||
|
@ -50,7 +50,11 @@ function(crashpad_install_dev)
|
|||
endfunction()
|
||||
|
||||
if(WIN32)
|
||||
enable_language(ASM_MASM)
|
||||
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES ARM64)
|
||||
enable_language(ASM_MARMASM)
|
||||
else()
|
||||
enable_language(ASM_MASM)
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
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>:/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)
|
||||
# redirect to wmain
|
||||
# FIXME: cmake 3.13 added target_link_options
|
||||
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()
|
||||
add_library(crashpad::interface ALIAS crashpad_interface)
|
||||
|
||||
|
|
|
@ -47,13 +47,13 @@ deps = {
|
|||
'9719c1e1e676814c456b55f5f070eabad6709d31',
|
||||
'crashpad/third_party/mini_chromium/mini_chromium':
|
||||
Var('chromium_git') + '/chromium/mini_chromium@' +
|
||||
'10f39a97650a0fe0b305415c15434443c0690a20',
|
||||
'9e21183c1ea369398d6f6ddd302c8db580bd19c4',
|
||||
'crashpad/third_party/libfuzzer/src':
|
||||
Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
|
||||
'fda403cf93ecb8792cb1d061564d89a6553ca020',
|
||||
'crashpad/third_party/zlib/zlib':
|
||||
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
|
||||
'13dc246a58e4b72104d35f9b1809af95221ebda7',
|
||||
'fef58692c1d7bec94c4ed3d030a45a1832a9615d',
|
||||
|
||||
# CIPD packages.
|
||||
'buildtools/linux64': {
|
||||
|
@ -89,8 +89,8 @@ deps = {
|
|||
'crashpad/third_party/linux/clang/linux-amd64': {
|
||||
'packages': [
|
||||
{
|
||||
'package': 'fuchsia/clang/linux-amd64',
|
||||
'version': 'goma',
|
||||
'package': 'fuchsia/third_party/clang/linux-amd64',
|
||||
'version': 'Tpc85d1ZwSlZ6UKl2d96GRUBGNA5JKholOKe24sRDr0C',
|
||||
},
|
||||
],
|
||||
'condition': 'checkout_linux and pull_linux_clang',
|
||||
|
@ -99,8 +99,8 @@ deps = {
|
|||
'crashpad/third_party/fuchsia/clang/mac-amd64': {
|
||||
'packages': [
|
||||
{
|
||||
'package': 'fuchsia/clang/mac-amd64',
|
||||
'version': 'goma',
|
||||
'package': 'fuchsia/third_party/clang/mac-amd64',
|
||||
'version': 'MAOjNhwTu5JU3P_0C9dITiyCTtQ1n7lRJnMfB9hhvOkC',
|
||||
},
|
||||
],
|
||||
'condition': 'checkout_fuchsia and host_os == "mac"',
|
||||
|
@ -109,8 +109,8 @@ deps = {
|
|||
'crashpad/third_party/fuchsia/clang/linux-amd64': {
|
||||
'packages': [
|
||||
{
|
||||
'package': 'fuchsia/clang/linux-amd64',
|
||||
'version': 'goma',
|
||||
'package': 'fuchsia/third_party/clang/linux-amd64',
|
||||
'version': 'Tpc85d1ZwSlZ6UKl2d96GRUBGNA5JKholOKe24sRDr0C',
|
||||
},
|
||||
],
|
||||
'condition': 'checkout_fuchsia and host_os == "linux"',
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<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>
|
||||
<string>CrashpadUnitTestDelegate</string>
|
||||
</dict>
|
||||
|
|
|
@ -246,6 +246,14 @@ class Annotation {
|
|||
|
||||
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:
|
||||
//! \brief Linked list next-node pointer. Accessed only by \sa AnnotationList.
|
||||
//!
|
||||
|
@ -323,9 +331,11 @@ class StringAnnotation : public Annotation {
|
|||
void Set(base::StringPiece string) {
|
||||
Annotation::ValueSizeType 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.
|
||||
DCHECK(!memchr(value_, '\0', size)) << "embedded NUL";
|
||||
DCHECK(string.find('\0', /*pos=*/0) == base::StringPiece::npos)
|
||||
<< "embedded NUL";
|
||||
SetSize(size);
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ Annotation* AnnotationList::Iterator::operator*() const {
|
|||
|
||||
AnnotationList::Iterator& AnnotationList::Iterator::operator++() {
|
||||
CHECK_NE(curr_, tail_);
|
||||
curr_ = curr_->link_node();
|
||||
curr_ = curr_->GetLinkNode();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -86,12 +86,42 @@ bool AnnotationList::Iterator::operator==(
|
|||
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() {
|
||||
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() {
|
||||
return Iterator(&tail_, tail_pointer_);
|
||||
}
|
||||
|
||||
AnnotationList::ConstIterator AnnotationList::cend() const {
|
||||
return ConstIterator(&tail_, tail_pointer_);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
|
|
@ -80,11 +80,37 @@ class AnnotationList {
|
|||
// 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.
|
||||
Iterator begin();
|
||||
ConstIterator begin() const { return cbegin(); }
|
||||
ConstIterator cbegin() const;
|
||||
|
||||
//! \brief Returns an iterator past the last element of the annotation list.
|
||||
Iterator end();
|
||||
ConstIterator end() const { return cend(); }
|
||||
ConstIterator cend() const;
|
||||
|
||||
protected:
|
||||
#if BUILDFLAG(IS_IOS)
|
||||
|
|
|
@ -128,6 +128,100 @@ TEST_F(AnnotationList, DuplicateKeys) {
|
|||
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 {
|
||||
public:
|
||||
explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
#include "client/settings.h"
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/scoped_generic.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "client/settings.h"
|
||||
#include "util/file/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
|
||||
// the old name or its new equivalent.
|
||||
std::string XattrNameInternal(const base::StringPiece& name, bool new_name) {
|
||||
return base::StringPrintf(new_name ? "org.chromium.crashpad.database.%s"
|
||||
: "com.googlecode.crashpad.%s",
|
||||
name.data());
|
||||
return base::StrCat({new_name ? "org.chromium.crashpad.database."
|
||||
: "com.googlecode.crashpad.",
|
||||
name});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_math.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
|
@ -545,8 +546,7 @@ void Metadata::Write() {
|
|||
for (const auto& report : reports_) {
|
||||
const base::FilePath& path = report.file_path;
|
||||
if (path.DirName() != report_dir_) {
|
||||
LOG(ERROR) << path.value().c_str() << " expected to start with "
|
||||
<< base::WideToUTF8(report_dir_.value());
|
||||
LOG(ERROR) << path << " expected to start with " << report_dir_;
|
||||
return;
|
||||
}
|
||||
records.push_back(MetadataFileReportRecord(report, &string_table));
|
||||
|
@ -590,12 +590,11 @@ OperationStatus Metadata::VerifyReport(const ReportDisk& report_disk,
|
|||
bool EnsureDirectory(const base::FilePath& path) {
|
||||
DWORD fileattr = GetFileAttributes(path.value().c_str());
|
||||
if (fileattr == INVALID_FILE_ATTRIBUTES) {
|
||||
PLOG(ERROR) << "GetFileAttributes " << base::WideToUTF8(path.value());
|
||||
PLOG(ERROR) << "GetFileAttributes " << path;
|
||||
return false;
|
||||
}
|
||||
if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
LOG(ERROR) << "GetFileAttributes " << base::WideToUTF8(path.value())
|
||||
<< ": not a directory";
|
||||
LOG(ERROR) << "GetFileAttributes " << path << ": not a directory";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -877,7 +876,7 @@ OperationStatus CrashReportDatabaseWin::DeleteReport(const UUID& uuid) {
|
|||
return os;
|
||||
|
||||
if (!DeleteFile(report_path.value().c_str())) {
|
||||
PLOG(ERROR) << "DeleteFile " << base::WideToUTF8(report_path.value());
|
||||
PLOG(ERROR) << "DeleteFile " << report_path;
|
||||
return kFileSystemError;
|
||||
}
|
||||
|
||||
|
@ -1021,8 +1020,7 @@ void CrashReportDatabaseWin::CleanOrphanedAttachments() {
|
|||
if (IsDirectory(path, false)) {
|
||||
UUID uuid;
|
||||
if (!uuid.InitializeFromString(filename.value())) {
|
||||
LOG(ERROR) << "unexpected attachment dir name "
|
||||
<< filename.value().c_str();
|
||||
LOG(ERROR) << "unexpected attachment dir name " << filename;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -835,11 +835,17 @@ class CrashpadClient {
|
|||
#endif
|
||||
|
||||
private:
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
//! \brief Registers process handlers for the client.
|
||||
void RegisterHandlers();
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_APPLE)
|
||||
base::apple::ScopedMachSendRight exception_port_;
|
||||
#elif BUILDFLAG(IS_WIN)
|
||||
std::wstring ipc_pipe_;
|
||||
ScopedKernelHANDLE handler_start_thread_;
|
||||
ScopedVectoredExceptionRegistration vectored_handler_;
|
||||
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
|
||||
std::set<int> unhandled_signals_;
|
||||
#endif // BUILDFLAG(IS_APPLE)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <lib/zx/process.h>
|
||||
#include <zircon/processargs.h>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/fuchsia/fuchsia_logging.h"
|
||||
#include "base/logging.h"
|
||||
#include "client/client_argv_handling.h"
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "build/build_config.h"
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "base/apple/mach_logging.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "util/mac/mac_util.h"
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "base/atomicops.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/scoped_generic.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
|
@ -203,6 +205,7 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
|||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
#if !defined(ADDRESS_SANITIZER)
|
||||
LONG WINAPI HandleHeapCorruption(EXCEPTION_POINTERS* exception_pointers) {
|
||||
if (exception_pointers->ExceptionRecord->ExceptionCode ==
|
||||
STATUS_HEAP_CORRUPTION) {
|
||||
|
@ -211,6 +214,7 @@ LONG WINAPI HandleHeapCorruption(EXCEPTION_POINTERS* exception_pointers) {
|
|||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
#endif
|
||||
|
||||
void HandleAbortSignal(int signum) {
|
||||
DCHECK_EQ(signum, SIGABRT);
|
||||
|
@ -537,7 +541,7 @@ bool StartHandlerProcess(
|
|||
// invalid command line where the first argument needed by rundll32 is not in
|
||||
// the correct format as required in:
|
||||
// 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;
|
||||
if (data->handler.value().size() >= kRunDll32Exe.size() &&
|
||||
_wcsicmp(data->handler.value()
|
||||
|
@ -609,45 +613,10 @@ void CommonInProcessInitialization() {
|
|||
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
|
||||
|
||||
CrashpadClient::CrashpadClient() : ipc_pipe_(), handler_start_thread_() {}
|
||||
CrashpadClient::CrashpadClient()
|
||||
: ipc_pipe_(), handler_start_thread_(), vectored_handler_() {}
|
||||
|
||||
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) {
|
||||
DCHECK(ipc_pipe_.empty());
|
||||
DCHECK(!ipc_pipe.empty());
|
||||
|
|
|
@ -324,6 +324,20 @@ bool ModulePathMatchesSinkhole(const char* path, const char* sinkhole) {
|
|||
#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) {
|
||||
// Some sinkholes don't use objc_exception_rethrow when they should, which
|
||||
// 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.
|
||||
objc_exception* exception_objc = reinterpret_cast<objc_exception*>(
|
||||
__cxxabiv1::__cxa_allocate_exception(sizeof(objc_exception)));
|
||||
ScopedException exception_objc_owner(exception_objc);
|
||||
exception_objc->obj = exception;
|
||||
exception_objc->tinfo.vtable = objc_ehtype_vtable + 2;
|
||||
exception_objc->tinfo.name = object_getClassName(exception);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <iterator>
|
||||
#include <optional>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "build/build_config.h"
|
||||
#include "snapshot/snapshot_constants.h"
|
||||
#include "util/ios/ios_intermediate_dump_writer.h"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/logging.h"
|
||||
#include "client/crashpad_client.h"
|
||||
#include "util/misc/no_cfi_icall.h"
|
||||
|
|
|
@ -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")
|
|
@ -123,7 +123,7 @@ if(NOT IOS)
|
|||
)
|
||||
endif()
|
||||
|
||||
if(CRASHPAD_WER_ENABLED)
|
||||
if (WIN32)
|
||||
add_library(crashpad_wer SHARED
|
||||
win/wer/crashpad_wer.cc
|
||||
win/wer/crashpad_wer.h
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
|
|
|
@ -253,7 +253,10 @@ class ExceptionHandlerServerTest : public testing::TestWithParam<bool> {
|
|||
pid_t last_client;
|
||||
ASSERT_TRUE(server_test_->Delegate()->WaitForException(
|
||||
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());
|
||||
} else {
|
||||
CheckedReadFileAtEOF(ReadPipeHandle());
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "base/apple/mach_logging.h"
|
||||
#include "base/check.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/mach/composite_mach_message_server.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
|
|
|
@ -115,7 +115,7 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
|
|||
for (const auto& attachment : (*attachments_)) {
|
||||
FileReader file_reader;
|
||||
if (!file_reader.Open(attachment)) {
|
||||
LOG(ERROR) << "attachment " << attachment.value().c_str()
|
||||
LOG(ERROR) << "attachment " << attachment
|
||||
<< " couldn't be opened, skipping";
|
||||
continue;
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
|
|||
FileWriter* file_writer =
|
||||
new_report->AddAttachment(base::WideToUTF8(filename.value()));
|
||||
if (file_writer == nullptr) {
|
||||
LOG(ERROR) << "attachment " << filename.value().c_str()
|
||||
LOG(ERROR) << "attachment " << filename
|
||||
<< " couldn't be created, skipping";
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "client/crashpad_client.h"
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <iterator>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/debug/alias.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/notreached.h"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "client/crashpad_client.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "minidump/minidump_byte_array_writer.h"
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "minidump/minidump_crashpad_info_writer.h"
|
||||
#include "minidump/minidump_exception_writer.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "minidump/minidump_extensions.h"
|
||||
#include "util/file/file_writer.h"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
|
|
|
@ -145,7 +145,7 @@ class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter {
|
|||
|
||||
//! \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.
|
||||
#if defined(WCHAR_T_IS_UTF16) || DOXYGEN
|
||||
#if defined(WCHAR_T_IS_16_BIT) || DOXYGEN
|
||||
inline const char16_t* AsU16CStr(const wchar_t* str) {
|
||||
return reinterpret_cast<const char16_t*>(str);
|
||||
}
|
||||
|
|
|
@ -178,8 +178,11 @@ void ExpectMiscInfoEqual<MINIDUMP_MISC_INFO_5>(
|
|||
expected_misc_info.XStateData.SizeOfInfo);
|
||||
EXPECT_EQ(observed_misc_info.XStateData.ContextSize,
|
||||
expected_misc_info.XStateData.ContextSize);
|
||||
EXPECT_EQ(observed_misc_info.XStateData.EnabledFeatures,
|
||||
expected_misc_info.XStateData.EnabledFeatures);
|
||||
// `EnabledFeatures` is underaligned and `EXPECT_EQ` internally takes
|
||||
// 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;
|
||||
feature_index < std::size(observed_misc_info.XStateData.Features);
|
||||
++feature_index) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "minidump/minidump_annotation_writer.h"
|
||||
#include "minidump/minidump_simple_string_dictionary_writer.h"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "minidump/minidump_string_writer.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "snapshot/exception_snapshot.h"
|
||||
#include "snapshot/thread_snapshot.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "minidump/minidump_writer_util.h"
|
||||
#include "util/file/file_writer.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "minidump/minidump_thread_id_map.h"
|
||||
#include "snapshot/thread_snapshot.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "minidump/minidump_context_writer.h"
|
||||
#include "minidump/minidump_memory_writer.h"
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "minidump/minidump_writer_util.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/in_range_cast.h"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <iterator>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_math.h"
|
||||
#include "build/build_config.h"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <link.h>
|
||||
#include <zircon/syscalls.h>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/fuchsia/fuchsia_logging.h"
|
||||
#include "base/logging.h"
|
||||
#include "util/fuchsia/koid_utilities.h"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "snapshot/ios/exception_snapshot_ios_intermediate_dump.h"
|
||||
|
||||
#include "base/apple/mach_logging.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "snapshot/cpu_context.h"
|
||||
#include "snapshot/ios/intermediate_dump_reader_util.h"
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h"
|
||||
|
||||
#include "base/check_op.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/notreached.h"
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "client/crashpad_info.h"
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <utility>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "build/build_config.h"
|
||||
|
@ -39,38 +40,26 @@ std::string SizeLimitedCString(const char* c_string, size_t max_length) {
|
|||
} // namespace
|
||||
|
||||
bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,
|
||||
const std::string& module_name,
|
||||
bool* has_timestamp) {
|
||||
const std::string& module_name) {
|
||||
#if defined(ARCH_CPU_X86_FAMILY)
|
||||
if (mach_o_file_type != MH_BUNDLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (module_name == "cl_kernels") {
|
||||
if (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||
|
||||
MacOSVersionNumber() >= 10'10'00) {
|
||||
if (has_timestamp) {
|
||||
*has_timestamp = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||
|
||||
MacOSVersionNumber() >= 10'10'00;
|
||||
}
|
||||
|
||||
static const char kCvmsObjectPathPrefix[] =
|
||||
"/private/var/db/CVMS/cvmsCodeSignObj";
|
||||
if (module_name.compare(
|
||||
0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 &&
|
||||
(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14 ||
|
||||
MacOSVersionNumber() >= 10'14'00)) {
|
||||
if (has_timestamp) {
|
||||
*has_timestamp = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // ARCH_CPU_X86_FAMILY
|
||||
|
||||
return module_name.compare(
|
||||
0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 &&
|
||||
(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14 ||
|
||||
MacOSVersionNumber() >= 10'14'00);
|
||||
#else
|
||||
return false;
|
||||
#endif // ARCH_CPU_X86_FAMILY
|
||||
}
|
||||
|
||||
MachOImageSegmentReader::MachOImageSegmentReader()
|
||||
|
@ -165,9 +154,8 @@ bool MachOImageSegmentReader::Initialize(ProcessReaderMac* process_reader,
|
|||
//
|
||||
// https://openradar.appspot.com/20239912
|
||||
if (section_segment_name != segment_name &&
|
||||
!(IsMalformedCLKernelsModule(file_type, module_name, nullptr) &&
|
||||
segment_name == SEG_TEXT &&
|
||||
section_segment_name == "__LD" &&
|
||||
!(IsMalformedCLKernelsModule(file_type, module_name) &&
|
||||
segment_name == SEG_TEXT && section_segment_name == "__LD" &&
|
||||
section_name == "__compact_unwind" &&
|
||||
(section.flags & S_ATTR_DEBUG))) {
|
||||
LOG(WARNING) << "section.segname incorrect in segment " << segment_name
|
||||
|
|
|
@ -53,18 +53,11 @@ namespace crashpad {
|
|||
//! \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
|
||||
//! 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
|
||||
//! based on the provided information, `false` otherwise.
|
||||
bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,
|
||||
const std::string& module_name,
|
||||
bool* has_timestamp);
|
||||
const std::string& module_name);
|
||||
|
||||
//! \brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O
|
||||
//! images mapped into another process.
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "base/apple/mach_logging.h"
|
||||
#include "base/apple/scoped_mach_port.h"
|
||||
#include "base/apple/scoped_mach_vm.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "snapshot/mac/mach_o_image_reader.h"
|
||||
|
@ -509,26 +510,27 @@ void ProcessReaderMac::InitializeModules() {
|
|||
}
|
||||
|
||||
if (file_type == MH_EXECUTE) {
|
||||
// On Mac OS X 10.6, the main executable does not normally show up at
|
||||
// index 0. This is because of how 10.6.8 dyld-132.13/src/dyld.cpp
|
||||
// notifyGDB(), the function resposible for causing
|
||||
// dyld_all_image_infos::infoArray to be updated, is called. It is
|
||||
// registered to be called when all dependents of an image have been
|
||||
// mapped (dyld_image_state_dependents_mapped), meaning that the main
|
||||
// executable won’t be added to the list until all of the libraries it
|
||||
// depends on are, even though dyld begins looking at the main executable
|
||||
// first. This changed in later versions of dyld, including those present
|
||||
// in 10.7. 10.9.4 dyld-239.4/src/dyld.cpp updateAllImages() (renamed from
|
||||
// notifyGDB()) is registered to be called when an image itself has been
|
||||
// mapped (dyld_image_state_mapped), regardless of the libraries that it
|
||||
// depends on.
|
||||
// On macOS 14, the main executable does not normally show up at
|
||||
// index 0. In previous versions of dyld, each loaded image was
|
||||
// appended to the all image info vector as it was loaded.
|
||||
// (For example, see RuntimeState::notifyDebuggerLoad in dyld-1066.8).
|
||||
// Starting from dyld-1122.1, notifyDebuggerLoad calls
|
||||
// ExternallyViewableState::addImages for all but the main executable
|
||||
// (which has already been added). ExternallyViewableState::addImages
|
||||
// inserts all new image infos at the front of the vector, leaving the
|
||||
// main executable as the last item.
|
||||
//
|
||||
// The interface requires that the main executable be first in the list,
|
||||
// so swap it into the right position.
|
||||
size_t index = modules_.size() - 1;
|
||||
if (main_executable_count == 0) {
|
||||
std::swap(modules_[0], modules_[index]);
|
||||
} else {
|
||||
if (index > 0) {
|
||||
CHECK_EQ(index, image_info_vector.size() - 1);
|
||||
if (main_executable_count == 0) {
|
||||
std::rotate(
|
||||
modules_.rbegin(), modules_.rbegin() + 1, modules_.rend());
|
||||
}
|
||||
}
|
||||
if (main_executable_count > 0) {
|
||||
LOG(WARNING) << base::StringPrintf(
|
||||
"multiple MH_EXECUTE modules (%s, %s)",
|
||||
modules_[0].name.c_str(),
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "base/apple/mach_logging.h"
|
||||
|
@ -54,6 +55,15 @@ namespace crashpad {
|
|||
namespace test {
|
||||
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";
|
||||
|
||||
TEST(ProcessReaderMac, SelfBasic) {
|
||||
|
@ -654,9 +664,8 @@ T GetDyldFunction(const char* 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;
|
||||
bool timestamp_may_be_0;
|
||||
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_16
|
||||
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;
|
||||
timestamp_may_be_0 = true;
|
||||
} else {
|
||||
stat_path = path;
|
||||
timestamp_may_be_0 = false;
|
||||
}
|
||||
|
||||
struct stat stat_buf;
|
||||
int rv = stat(stat_path, &stat_buf);
|
||||
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. They’re not ld output and don’t
|
||||
|
@ -839,67 +843,57 @@ TEST(ProcessReaderMac, SelfModules) {
|
|||
ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
|
||||
|
||||
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,
|
||||
// and for dyld.
|
||||
ASSERT_GE(modules.size(), 3u);
|
||||
|
||||
// dyld_image_count doesn’t include an entry for dyld itself, but |modules|
|
||||
// does.
|
||||
ASSERT_EQ(modules.size(), dyld_image_count + 1);
|
||||
|
||||
bool found_cl_kernels = false;
|
||||
for (uint32_t index = 0; index < dyld_image_count; ++index) {
|
||||
SCOPED_TRACE(base::StringPrintf(
|
||||
"index %u, name %s", index, modules[index].name.c_str()));
|
||||
|
||||
const char* dyld_image_name = _dyld_get_image_name(index);
|
||||
EXPECT_EQ(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 didn’t 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 doesn’t 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);
|
||||
std::set<std::string> cl_kernel_names;
|
||||
auto modules = process_reader.Modules();
|
||||
ModuleSet actual_modules;
|
||||
for (size_t i = 0; i < modules.size(); ++i) {
|
||||
auto& module = modules[i];
|
||||
ASSERT_TRUE(module.reader);
|
||||
if (i == modules.size() - 1) {
|
||||
EXPECT_EQ(module.name, kDyldPath);
|
||||
const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos();
|
||||
if (dyld_image_infos->version >= 2) {
|
||||
EXPECT_EQ(module.reader->Address(),
|
||||
FromPointerCast<mach_vm_address_t>(
|
||||
dyld_image_infos->dyldImageLoadAddress));
|
||||
}
|
||||
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 {
|
||||
// Hope that the module didn’t change on disk.
|
||||
VerifyImageExistenceAndTimestamp(dyld_image_name,
|
||||
modules[index].timestamp);
|
||||
EXPECT_NE(file_type, static_cast<uint32_t>(MH_EXECUTE));
|
||||
}
|
||||
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(found_cl_kernels, ExpectCLKernels() && ensure_cl_kernels.success());
|
||||
|
||||
size_t index = modules.size() - 1;
|
||||
EXPECT_EQ(modules[index].name, kDyldPath);
|
||||
|
||||
// dyld didn’t load itself either, so it couldn’t 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));
|
||||
}
|
||||
EXPECT_EQ(actual_modules, expect_modules);
|
||||
}
|
||||
|
||||
class ProcessReaderModulesChild final : public MachMultiprocess {
|
||||
|
@ -918,27 +912,45 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
|
|||
void MachMultiprocessParent() override {
|
||||
ProcessReaderMac process_reader;
|
||||
ASSERT_TRUE(process_reader.Initialize(ChildTask()));
|
||||
|
||||
const std::vector<ProcessReaderMac::Module>& 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,
|
||||
// and for dyld.
|
||||
ASSERT_GE(modules.size(), 3u);
|
||||
ASSERT_GE(actual_modules.size(), 3u);
|
||||
|
||||
FileHandle read_handle = ReadPipeHandle();
|
||||
|
||||
uint32_t expect_modules;
|
||||
uint32_t expect_modules_size;
|
||||
CheckedReadFileExactly(
|
||||
read_handle, &expect_modules, sizeof(expect_modules));
|
||||
read_handle, &expect_modules_size, sizeof(expect_modules_size));
|
||||
|
||||
ASSERT_EQ(modules.size(), 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()));
|
||||
ASSERT_EQ(actual_modules.size(), expect_modules_size);
|
||||
ModuleSet expect_modules;
|
||||
|
||||
for (size_t index = 0; index < expect_modules_size; ++index) {
|
||||
uint32_t expect_name_length;
|
||||
CheckedReadFileExactly(
|
||||
read_handle, &expect_name_length, sizeof(expect_name_length));
|
||||
|
@ -946,40 +958,18 @@ class ProcessReaderModulesChild final : public MachMultiprocess {
|
|||
// The NUL terminator is not read.
|
||||
std::string expect_name(expect_name_length, '\0');
|
||||
CheckedReadFileExactly(read_handle, &expect_name[0], expect_name_length);
|
||||
EXPECT_EQ(modules[index].name, expect_name);
|
||||
|
||||
mach_vm_address_t expect_address;
|
||||
CheckedReadFileExactly(
|
||||
read_handle, &expect_address, sizeof(expect_address));
|
||||
ASSERT_TRUE(modules[index].reader);
|
||||
EXPECT_EQ(modules[index].reader->Address(), expect_address);
|
||||
|
||||
bool expect_timestamp;
|
||||
if ((index == 0 && MacOSVersionNumber() < 12'00'00) ||
|
||||
index == modules.size() - 1) {
|
||||
// Pre-dyld4, dyld didn’t 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 doesn’t 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 didn’t change on disk.
|
||||
VerifyImageExistenceAndTimestamp(expect_name.c_str(),
|
||||
modules[index].timestamp);
|
||||
expect_modules.insert(std::make_pair(expect_name, expect_address));
|
||||
if (cl_kernel_names.find(expect_name) == cl_kernel_names.end()) {
|
||||
VerifyImageExistence(expect_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(found_cl_kernels,
|
||||
EXPECT_EQ(cl_kernel_names.size() > 0,
|
||||
ExpectCLKernels() && ensure_cl_kernels_success_);
|
||||
EXPECT_EQ(expect_modules, actual_modules);
|
||||
}
|
||||
|
||||
void MachMultiprocessChild() override {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/notreached.h"
|
||||
#include "minidump/minidump_extensions.h"
|
||||
|
|
|
@ -346,7 +346,7 @@ bool ProcessSnapshotMinidump::InitializeMiscInfo() {
|
|||
switch (stream_it->second->DataSize) {
|
||||
case sizeof(MINIDUMP_MISC_INFO_5):
|
||||
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);
|
||||
#else
|
||||
full_version_ = base::UTF16ToUTF8(info.BuildString);
|
||||
|
|
|
@ -868,8 +868,9 @@ TEST(ProcessSnapshotMinidump, ThreadsWithNames) {
|
|||
}
|
||||
|
||||
TEST(ProcessSnapshotMinidump, System) {
|
||||
const char* cpu_info = "GenuineIntel";
|
||||
const uint32_t* cpu_info_bytes = reinterpret_cast<const uint32_t*>(cpu_info);
|
||||
const char cpu_info[] = "GenuineIntel";
|
||||
uint32_t cpu_info_bytes[3];
|
||||
memcpy(cpu_info_bytes, cpu_info, sizeof(cpu_info_bytes));
|
||||
StringFile string_file;
|
||||
|
||||
MINIDUMP_HEADER header = {};
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "snapshot/sanitized/module_snapshot_sanitized.h"
|
||||
|
||||
#include "base/strings/pattern.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
|
@ -22,7 +24,7 @@ namespace {
|
|||
bool KeyIsAllowed(const std::string& name,
|
||||
const std::vector<std::string>& allowed_keys) {
|
||||
for (const auto& key : allowed_keys) {
|
||||
if (name == key) {
|
||||
if (base::MatchPattern(name, key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ class ProcessSnapshotSanitized final : public ProcessSnapshot {
|
|||
//! \param[in] allowed_annotations A list of annotations names to allow to
|
||||
//! be returned by AnnotationsSimpleMap() or from this object's module
|
||||
//! 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
|
||||
//! accessible via Memory(), or `nullptr` to allow all ranges.
|
||||
//! \param[in] target_module_address An address in the target process'
|
||||
|
|
|
@ -79,6 +79,8 @@ class ExceptionGenerator {
|
|||
};
|
||||
|
||||
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 kNonAllowedAnnotationName[] = "non_allowed_anno";
|
||||
constexpr char kNonAllowedAnnotationValue[] = "private_annotation";
|
||||
|
@ -99,6 +101,10 @@ void ChildTestFunction() {
|
|||
static StringAnnotation<32> allowed_annotation(kAllowedAnnotationName);
|
||||
allowed_annotation.Set(kAllowedAnnotationValue);
|
||||
|
||||
static StringAnnotation<32> allowed_matched_annotation(
|
||||
kAllowedAnnotationNamePatternActual);
|
||||
allowed_matched_annotation.Set(kAllowedAnnotationValue);
|
||||
|
||||
static StringAnnotation<32> non_allowed_annotation(kNonAllowedAnnotationName);
|
||||
non_allowed_annotation.Set(kNonAllowedAnnotationValue);
|
||||
|
||||
|
@ -129,11 +135,15 @@ CRASHPAD_CHILD_TEST_MAIN(ChildToBeSanitized) {
|
|||
|
||||
void ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) {
|
||||
bool found_allowed = false;
|
||||
bool found_matched_allowed = false;
|
||||
bool found_non_allowed = false;
|
||||
for (auto module : snapshot->Modules()) {
|
||||
for (const auto& anno : module->AnnotationObjects()) {
|
||||
if (anno.name == kAllowedAnnotationName) {
|
||||
found_allowed = true;
|
||||
}
|
||||
if (anno.name == kAllowedAnnotationNamePatternActual) {
|
||||
found_matched_allowed = true;
|
||||
} else if (anno.name == kNonAllowedAnnotationName) {
|
||||
found_non_allowed = true;
|
||||
}
|
||||
|
@ -141,6 +151,7 @@ void ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) {
|
|||
}
|
||||
|
||||
EXPECT_TRUE(found_allowed);
|
||||
EXPECT_TRUE(found_matched_allowed);
|
||||
if (sanitized) {
|
||||
EXPECT_FALSE(found_non_allowed);
|
||||
} else {
|
||||
|
@ -279,6 +290,7 @@ class SanitizeTest : public MultiprocessExec {
|
|||
|
||||
auto allowed_annotations = std::make_unique<std::vector<std::string>>();
|
||||
allowed_annotations->push_back(kAllowedAnnotationName);
|
||||
allowed_annotations->push_back(kAllowedAnnotationNamePattern);
|
||||
|
||||
auto allowed_memory_ranges =
|
||||
std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
|
@ -173,9 +174,12 @@ void DoStackWalk(ProcessReaderWin::Thread* thread,
|
|||
// ctx = (LPVOID)ctx_;
|
||||
// }
|
||||
|
||||
// TODO: we dont support this right away, maybe in the future
|
||||
// #elif defined(ARCH_CPU_ARM64)
|
||||
// machine_type = IMAGE_FILE_MACHINE_ARM64;
|
||||
#elif defined(ARCH_CPU_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
|
||||
#error Unsupported Windows Arch
|
||||
#endif // ARCH_CPU_X86
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
#include "base/logging.h"
|
||||
|
@ -329,7 +330,7 @@ void ProcessSnapshotWin::InitializeUnloadedModules() {
|
|||
uet.SizeOfImage,
|
||||
uet.CheckSum,
|
||||
uet.TimeDateStamp,
|
||||
base::WideToUTF8(base::WStringPiece(
|
||||
base::WideToUTF8(std::wstring_view(
|
||||
uet.ImageName,
|
||||
wcsnlen(uet.ImageName, std::size(uet.ImageName))))));
|
||||
}
|
||||
|
|
|
@ -65,8 +65,11 @@ bundle_data("crashy_module_bundle") {
|
|||
}
|
||||
|
||||
ios_app_bundle("ios_crash_xcuitests") {
|
||||
info_plist = "Info.plist"
|
||||
testonly = true
|
||||
info_plist = "Info.plist"
|
||||
if (crashpad_is_in_chromium) {
|
||||
bundle_identifier = shared_bundle_id_for_test_apps
|
||||
}
|
||||
deps = [
|
||||
":app_host_sources",
|
||||
":crashy_module_bundle",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${IOS_BUNDLE_ID_PREFIX}.googletest.${EXECUTABLE_NAME:rfc1034identifier}</string>
|
||||
<string>${BUNDLE_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <malloc/malloc.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "base/apple/mach_logging.h"
|
||||
#include "base/check_op.h"
|
||||
#include "client/crashpad_client.h"
|
||||
#include "util/ios/raw_logging.h"
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
#include "util/misc/paths.h"
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
|
|
|
@ -15,7 +15,7 @@ mc_append_sources(
|
|||
check.h
|
||||
check_op.h
|
||||
compiler_specific.h
|
||||
cxx17_backports.h
|
||||
containers/span.h
|
||||
debug/alias.cc
|
||||
debug/alias.h
|
||||
files/file_path.cc
|
||||
|
@ -52,6 +52,9 @@ mc_append_sources(
|
|||
scoped_generic.h
|
||||
strings/pattern.cc
|
||||
strings/pattern.h
|
||||
strings/strcat.cc
|
||||
strings/strcat.h
|
||||
strings/strcat_internal.h
|
||||
strings/string_number_conversions.cc
|
||||
strings/string_number_conversions.h
|
||||
strings/string_piece.h
|
||||
|
@ -72,6 +75,7 @@ mc_append_sources(
|
|||
third_party/icu/icu_utf.h
|
||||
threading/thread_local_storage.cc
|
||||
threading/thread_local_storage.h
|
||||
types/cxx23_to_underlying.h
|
||||
)
|
||||
|
||||
if(NOT MINGW)
|
||||
|
@ -84,26 +88,7 @@ else()
|
|||
)
|
||||
endif()
|
||||
|
||||
if(APPLE AND NOT IOS)
|
||||
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)
|
||||
if(APPLE)
|
||||
mc_append_sources(
|
||||
apple/bridging.h
|
||||
apple/foundation_util.h
|
||||
|
@ -120,6 +105,13 @@ elseif(IOS)
|
|||
apple/scoped_typeref.h
|
||||
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()
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
@ -14,6 +14,7 @@ static_library("base") {
|
|||
"check.h",
|
||||
"check_op.h",
|
||||
"compiler_specific.h",
|
||||
"containers/span.h",
|
||||
"debug/alias.cc",
|
||||
"debug/alias.h",
|
||||
"files/file_path.cc",
|
||||
|
@ -50,6 +51,9 @@ static_library("base") {
|
|||
"scoped_generic.h",
|
||||
"strings/pattern.cc",
|
||||
"strings/pattern.h",
|
||||
"strings/strcat.cc",
|
||||
"strings/strcat.h",
|
||||
"strings/strcat_internal.h",
|
||||
"strings/string_number_conversions.cc",
|
||||
"strings/string_number_conversions.h",
|
||||
"strings/string_piece.h",
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "base/apple/foundation_util.h"
|
||||
|
||||
#include "base/check.h"
|
||||
|
||||
#if !BUILDFLAG(IS_IOS)
|
||||
extern "C" {
|
||||
CFTypeID SecACLGetTypeID();
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/check.h"
|
||||
|
||||
// Use ScopedMachVM to supervise ownership of pages in the current process
|
||||
// through the Mach VM subsystem. Pages allocated with vm_allocate can be
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef 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"
|
||||
|
||||
namespace base {
|
||||
|
@ -17,68 +17,125 @@ struct ScopedTypeRefTraits;
|
|||
template <typename T, typename Traits = ScopedTypeRefTraits<T>>
|
||||
class ScopedTypeRef {
|
||||
public:
|
||||
typedef T element_type;
|
||||
using element_type = T;
|
||||
|
||||
ScopedTypeRef(
|
||||
T object = Traits::InvalidValue(),
|
||||
// Construction from underlying type
|
||||
|
||||
explicit constexpr ScopedTypeRef(
|
||||
element_type object = Traits::InvalidValue(),
|
||||
base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
|
||||
: object_(object) {
|
||||
if (object_ && policy == base::scoped_policy::RETAIN)
|
||||
if (object_ != Traits::InvalidValue() &&
|
||||
policy == base::scoped_policy::RETAIN) {
|
||||
object_ = Traits::Retain(object_);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) : object_(that.object_) {
|
||||
if (object_)
|
||||
// Copy construction
|
||||
|
||||
ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) : object_(that.get()) {
|
||||
if (object_ != Traits::InvalidValue()) {
|
||||
object_ = Traits::Retain(object_);
|
||||
}
|
||||
}
|
||||
|
||||
~ScopedTypeRef() {
|
||||
if (object_)
|
||||
Traits::Release(object_);
|
||||
template <typename R, typename RTraits>
|
||||
ScopedTypeRef(const ScopedTypeRef<R, RTraits>& that) : object_(that.get()) {
|
||||
if (object_ != Traits::InvalidValue()) {
|
||||
object_ = Traits::Retain(object_);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy assignment
|
||||
|
||||
ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) {
|
||||
reset(that.get(), base::scoped_policy::RETAIN);
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] T* InitializeInto() {
|
||||
DCHECK(!object_);
|
||||
return &object_;
|
||||
template <typename R, typename RTraits>
|
||||
ScopedTypeRef& operator=(const ScopedTypeRef<R, RTraits>& that) {
|
||||
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::ASSUME) {
|
||||
if (object && policy == base::scoped_policy::RETAIN)
|
||||
if (object != Traits::InvalidValue() &&
|
||||
policy == base::scoped_policy::RETAIN) {
|
||||
object = Traits::Retain(object);
|
||||
if (object_)
|
||||
}
|
||||
if (object_ != Traits::InvalidValue()) {
|
||||
Traits::Release(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) {
|
||||
T temp = that.object_;
|
||||
element_type temp = that.object_;
|
||||
that.object_ = object_;
|
||||
object_ = temp;
|
||||
}
|
||||
|
||||
[[nodiscard]] T release() {
|
||||
T temp = object_;
|
||||
[[nodiscard]] element_type release() {
|
||||
element_type temp = object_;
|
||||
object_ = Traits::InvalidValue();
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
T object_;
|
||||
element_type object_;
|
||||
};
|
||||
|
||||
} // namespace apple
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#ifndef MINI_CHROMIUM_BASE_CHECK_OP_H_
|
||||
#define MINI_CHROMIUM_BASE_CHECK_OP_H_
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace logging {
|
||||
|
|
|
@ -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_
|
|
@ -6,8 +6,15 @@
|
|||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "base/check.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 {
|
||||
|
||||
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
|
||||
|
@ -284,6 +291,10 @@ void FilePath::StripTrailingSeparatorsInternal() {
|
|||
|
||||
} // namespace base
|
||||
|
||||
void PrintTo(const base::FilePath& path, std::ostream* out) {
|
||||
*out << path.value().c_str();
|
||||
std::ostream& operator<<(std::ostream& os, const base::FilePath& file_path) {
|
||||
#ifdef FILE_PATH_USES_WIDE_CHARACTERS
|
||||
return os << base::WideToUTF8(file_path.value());
|
||||
#else
|
||||
return os << file_path.value();
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -116,6 +116,7 @@
|
|||
#if BUILDFLAG(IS_WIN)
|
||||
#define FILE_PATH_USES_DRIVE_LETTERS
|
||||
#define FILE_PATH_USES_WIN_SEPARATORS
|
||||
#define FILE_PATH_USES_WIDE_CHARACTERS
|
||||
#endif // BUILDFLAG(IS_WIN)
|
||||
|
||||
namespace base {
|
||||
|
@ -225,8 +226,11 @@ class FilePath {
|
|||
|
||||
} // namespace base
|
||||
|
||||
// This is required by googletest to print a readable output on test failures.
|
||||
extern void PrintTo(const base::FilePath& path, std::ostream* out);
|
||||
// Streams `file_path`'s value to a byte stream, converting from wide
|
||||
// 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
|
||||
// using a FilePath::CharType[] in a printf-style format string.
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#if BUILDFLAG(IS_POSIX)
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#endif
|
||||
|
||||
|
|
|
@ -23,25 +23,18 @@
|
|||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if BUILDFLAG(IS_POSIX)
|
||||
|
||||
#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
|
||||
#if BUILDFLAG(IS_POSIX) && \
|
||||
(defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
|
||||
#error "inttypes.h has already been included before this header file, but "
|
||||
#error "without __STDC_FORMAT_MACROS defined."
|
||||
#endif
|
||||
|
||||
#if !defined(__STDC_FORMAT_MACROS)
|
||||
#if BUILDFLAG(IS_POSIX) && !defined(__STDC_FORMAT_MACROS)
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#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)
|
||||
#define PRIuS "zu"
|
||||
#endif
|
||||
|
@ -74,28 +67,4 @@
|
|||
#endif
|
||||
#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_
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include <lib/syslog/global.h>
|
||||
#endif
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "base/check.h"
|
||||
#include "base/check_op.h"
|
||||
#include "base/notreached.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace logging {
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if BUILDFLAG(IS_FUCHSIA)
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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_
|
|
@ -160,7 +160,6 @@ std::ostream& operator<<(std::ostream& ostream,
|
|||
|
||||
typedef BasicStringPiece<std::string> StringPiece;
|
||||
typedef BasicStringPiece<std::u16string> StringPiece16;
|
||||
typedef BasicStringPiece<std::wstring> WStringPiece;
|
||||
|
||||
inline bool operator==(const StringPiece& x, const StringPiece& y) {
|
||||
if (x.size() != y.size())
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "base/strings/sys_string_conversions.h"
|
||||
|
||||
#include "base/apple/bridging.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/check_op.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
|
|
|
@ -32,10 +32,13 @@ bool ReadUnicodeCharacter(const char* src,
|
|||
int32_t src_len,
|
||||
int32_t* char_index,
|
||||
uint32_t* code_point_out) {
|
||||
int32_t code_point;
|
||||
CBU8_NEXT(src, *char_index, src_len, code_point);
|
||||
*code_point_out = static_cast<uint32_t>(code_point);
|
||||
base_icu::UChar32 code_point;
|
||||
CBU8_NEXT(reinterpret_cast<const uint8_t*>(src), *char_index, src_len,
|
||||
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)--;
|
||||
|
||||
return IsValidCodepoint(code_point);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "base/strings/utf_string_conversion_utils.h"
|
||||
#include "build/build_config.h"
|
||||
|
@ -58,8 +59,8 @@ std::string UTF16ToUTF8(const StringPiece16& utf16) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if defined(WCHAR_T_IS_UTF16)
|
||||
std::string WideToUTF8(WStringPiece wide) {
|
||||
#if defined(WCHAR_T_IS_16_BIT)
|
||||
std::string WideToUTF8(std::wstring_view wide) {
|
||||
std::string ret;
|
||||
UTF16ToUTF8(
|
||||
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()),
|
||||
utf16.size());
|
||||
}
|
||||
#endif // defined(WCHAR_T_IS_UTF16)
|
||||
#endif // defined(WCHAR_T_IS_16_BIT)
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define MINI_CHROMIUM_BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "base/strings/string_piece.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);
|
||||
std::string UTF16ToUTF8(const StringPiece16& utf16);
|
||||
|
||||
#if defined(WCHAR_T_IS_UTF16)
|
||||
std::string WideToUTF8(WStringPiece wide);
|
||||
#if defined(WCHAR_T_IS_16_BIT)
|
||||
std::string WideToUTF8(std::wstring_view wide);
|
||||
std::wstring UTF8ToWide(StringPiece utf8);
|
||||
#endif // defined(WCHAR_T_IS_UTF16)
|
||||
#endif // defined(WCHAR_T_IS_16_BIT)
|
||||
|
||||
} // namespace
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "base/synchronization/condition_variable.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/check_op.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include "base/synchronization/lock.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/check_op.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <errno.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/check_op.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
|
|
@ -5,123 +5,48 @@
|
|||
#ifndef MINI_CHROMIUM_BASE_TEMPLATE_UTIL_H_
|
||||
#define MINI_CHROMIUM_BASE_TEMPLATE_UTIL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include <type_traits>
|
||||
|
||||
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 {
|
||||
|
||||
// Types YesType and NoType are guaranteed such that sizeof(YesType) <
|
||||
// sizeof(NoType).
|
||||
typedef char YesType;
|
||||
// Helper to express preferences in an overload set. If more than one overload
|
||||
// are available for a given set of parameters the overload with the higher
|
||||
// priority will be chosen.
|
||||
template <size_t I>
|
||||
struct priority_tag : priority_tag<I - 1> {};
|
||||
|
||||
struct NoType {
|
||||
YesType dummy[2];
|
||||
};
|
||||
|
||||
// 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(...);
|
||||
};
|
||||
template <>
|
||||
struct priority_tag<0> {};
|
||||
|
||||
} // 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
|
||||
// of whether or not the conversion would emit a warning.
|
||||
template <typename From, typename To>
|
||||
struct is_convertible
|
||||
: integral_constant<bool,
|
||||
sizeof(internal::ConvertHelper::Test<To>(
|
||||
internal::ConvertHelper::Create<From>())) ==
|
||||
sizeof(internal::YesType)> {
|
||||
};
|
||||
|
||||
// References:
|
||||
// - https://en.cppreference.com/w/cpp/types/remove_cvref
|
||||
// - https://wg21.link/meta.trans.other#lib:remove_cvref
|
||||
template <typename T>
|
||||
struct is_class
|
||||
: integral_constant<bool,
|
||||
sizeof(internal::IsClassHelper::Test<T>(0)) ==
|
||||
sizeof(internal::YesType)> {
|
||||
struct remove_cvref {
|
||||
using type = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
};
|
||||
|
||||
template<bool B, class T = void>
|
||||
struct enable_if {};
|
||||
// Implementation of C++20's std::remove_cvref_t.
|
||||
//
|
||||
// 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>
|
||||
struct enable_if<true, T> { typedef T type; };
|
||||
// Simplified implementation of C++20's std::iter_reference_t.
|
||||
// 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
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue