| //===-- xray_utils.cpp ------------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file is a part of XRay, a dynamic runtime instrumentation system. |
| // |
| //===----------------------------------------------------------------------===// |
| #include "xray_utils.h" |
| |
| #include "sanitizer_common/sanitizer_allocator_internal.h" |
| #include "sanitizer_common/sanitizer_common.h" |
| #include "xray_allocator.h" |
| #include "xray_defs.h" |
| #include "xray_flags.h" |
| #include <cstdio> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <iterator> |
| #include <new> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <tuple> |
| #include <unistd.h> |
| #include <utility> |
| |
| #if SANITIZER_FUCHSIA |
| #include "sanitizer_common/sanitizer_symbolizer_fuchsia.h" |
| |
| #include <inttypes.h> |
| #include <zircon/process.h> |
| #include <zircon/sanitizer.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| #endif |
| |
| namespace __xray { |
| |
| #if SANITIZER_FUCHSIA |
| constexpr const char* ProfileSinkName = "llvm-xray"; |
| |
| LogWriter::~LogWriter() { |
| _zx_handle_close(Vmo); |
| } |
| |
| void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT { |
| if (Begin == End) |
| return; |
| auto TotalBytes = std::distance(Begin, End); |
| |
| const size_t PageSize = flags()->xray_page_size_override > 0 |
| ? flags()->xray_page_size_override |
| : GetPageSizeCached(); |
| if (RoundUpTo(Offset, PageSize) != RoundUpTo(Offset + TotalBytes, PageSize)) { |
| // Resize the VMO to ensure there's sufficient space for the data. |
| zx_status_t Status = _zx_vmo_set_size(Vmo, Offset + TotalBytes); |
| if (Status != ZX_OK) { |
| Report("Failed to resize VMO: %s\n", _zx_status_get_string(Status)); |
| return; |
| } |
| } |
| |
| // Write the data into VMO. |
| zx_status_t Status = _zx_vmo_write(Vmo, Begin, Offset, TotalBytes); |
| if (Status != ZX_OK) { |
| Report("Failed to write: %s\n", _zx_status_get_string(Status)); |
| return; |
| } |
| Offset += TotalBytes; |
| |
| // Record the data size as a property of the VMO. |
| _zx_object_set_property(Vmo, ZX_PROP_VMO_CONTENT_SIZE, |
| &Offset, sizeof(Offset)); |
| } |
| |
| void LogWriter::Flush() XRAY_NEVER_INSTRUMENT { |
| // Nothing to do here since WriteAll writes directly into the VMO. |
| } |
| |
| LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT { |
| // Create VMO to hold the profile data. |
| zx_handle_t Vmo; |
| zx_status_t Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &Vmo); |
| if (Status != ZX_OK) { |
| Report("XRay: cannot create VMO: %s\n", _zx_status_get_string(Status)); |
| return nullptr; |
| } |
| |
| // Get the KOID of the current process to use in the VMO name. |
| zx_info_handle_basic_t Info; |
| Status = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info, |
| sizeof(Info), NULL, NULL); |
| if (Status != ZX_OK) { |
| Report("XRay: cannot get basic info about current process handle: %s\n", |
| _zx_status_get_string(Status)); |
| return nullptr; |
| } |
| |
| // Give the VMO a name including our process KOID so it's easy to spot. |
| char VmoName[ZX_MAX_NAME_LEN]; |
| internal_snprintf(VmoName, sizeof(VmoName), "%s.%zu", ProfileSinkName, |
| Info.koid); |
| _zx_object_set_property(Vmo, ZX_PROP_NAME, VmoName, strlen(VmoName)); |
| |
| // Duplicate the handle since __sanitizer_publish_data consumes it and |
| // LogWriter needs to hold onto it. |
| zx_handle_t Handle; |
| Status =_zx_handle_duplicate(Vmo, ZX_RIGHT_SAME_RIGHTS, &Handle); |
| if (Status != ZX_OK) { |
| Report("XRay: cannot duplicate VMO handle: %s\n", |
| _zx_status_get_string(Status)); |
| return nullptr; |
| } |
| |
| // Publish the VMO that receives the logging. Note the VMO's contents can |
| // grow and change after publication. The contents won't be read out until |
| // after the process exits. |
| __sanitizer_publish_data(ProfileSinkName, Handle); |
| |
| // Use the dumpfile symbolizer markup element to write the name of the VMO. |
| Report("XRay: " FORMAT_DUMPFILE "\n", ProfileSinkName, VmoName); |
| |
| LogWriter *LW = reinterpret_cast<LogWriter *>(InternalAlloc(sizeof(LogWriter))); |
| new (LW) LogWriter(Vmo); |
| return LW; |
| } |
| |
| void LogWriter::Close(LogWriter *LW) { |
| LW->~LogWriter(); |
| InternalFree(LW); |
| } |
| #else // SANITIZER_FUCHSIA |
| LogWriter::~LogWriter() { |
| internal_close(Fd); |
| } |
| |
| void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT { |
| if (Begin == End) |
| return; |
| auto TotalBytes = std::distance(Begin, End); |
| while (auto Written = write(Fd, Begin, TotalBytes)) { |
| if (Written < 0) { |
| if (errno == EINTR) |
| continue; // Try again. |
| Report("Failed to write; errno = %d\n", errno); |
| return; |
| } |
| TotalBytes -= Written; |
| if (TotalBytes == 0) |
| break; |
| Begin += Written; |
| } |
| } |
| |
| void LogWriter::Flush() XRAY_NEVER_INSTRUMENT { |
| fsync(Fd); |
| } |
| |
| LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT { |
| // Open a temporary file once for the log. |
| char TmpFilename[256] = {}; |
| char TmpWildcardPattern[] = "XXXXXX"; |
| auto **Argv = GetArgv(); |
| const char *Progname = !Argv ? "(unknown)" : Argv[0]; |
| const char *LastSlash = internal_strrchr(Progname, '/'); |
| |
| if (LastSlash != nullptr) |
| Progname = LastSlash + 1; |
| |
| int NeededLength = internal_snprintf( |
| TmpFilename, sizeof(TmpFilename), "%s%s.%s", |
| flags()->xray_logfile_base, Progname, TmpWildcardPattern); |
| if (NeededLength > int(sizeof(TmpFilename))) { |
| Report("XRay log file name too long (%d): %s\n", NeededLength, TmpFilename); |
| return nullptr; |
| } |
| int Fd = mkstemp(TmpFilename); |
| if (Fd == -1) { |
| Report("XRay: Failed opening temporary file '%s'; not logging events.\n", |
| TmpFilename); |
| return nullptr; |
| } |
| if (Verbosity()) |
| Report("XRay: Log file in '%s'\n", TmpFilename); |
| |
| LogWriter *LW = allocate<LogWriter>(); |
| new (LW) LogWriter(Fd); |
| return LW; |
| } |
| |
| void LogWriter::Close(LogWriter *LW) { |
| LW->~LogWriter(); |
| deallocate(LW); |
| } |
| #endif // SANITIZER_FUCHSIA |
| |
| } // namespace __xray |