| 1 | //===-- xray_utils.cpp ------------------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file is a part of XRay, a dynamic runtime instrumentation system. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | #include "xray_utils.h" |
| 13 | |
| 14 | #include "sanitizer_common/sanitizer_allocator_internal.h" |
| 15 | #include "sanitizer_common/sanitizer_common.h" |
| 16 | #include "xray_allocator.h" |
| 17 | #include "xray_defs.h" |
| 18 | #include "xray_flags.h" |
| 19 | #include <cstdio> |
| 20 | #include <errno.h> |
| 21 | #include <fcntl.h> |
| 22 | #include <iterator> |
| 23 | #include <new> |
| 24 | #include <stdlib.h> |
| 25 | #include <sys/types.h> |
| 26 | #include <tuple> |
| 27 | #include <unistd.h> |
| 28 | #include <utility> |
| 29 | |
| 30 | #if SANITIZER_FUCHSIA |
| 31 | #include "sanitizer_common/sanitizer_symbolizer_markup_constants.h" |
| 32 | |
| 33 | #include <inttypes.h> |
| 34 | #include <zircon/process.h> |
| 35 | #include <zircon/sanitizer.h> |
| 36 | #include <zircon/status.h> |
| 37 | #include <zircon/syscalls.h> |
| 38 | #endif |
| 39 | |
| 40 | namespace __xray { |
| 41 | |
| 42 | #if SANITIZER_FUCHSIA |
| 43 | constexpr const char* ProfileSinkName = "llvm-xray" ; |
| 44 | |
| 45 | LogWriter::~LogWriter() { |
| 46 | _zx_handle_close(Vmo); |
| 47 | } |
| 48 | |
| 49 | void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT { |
| 50 | if (Begin == End) |
| 51 | return; |
| 52 | auto TotalBytes = std::distance(Begin, End); |
| 53 | |
| 54 | const size_t PageSize = flags()->xray_page_size_override > 0 |
| 55 | ? flags()->xray_page_size_override |
| 56 | : GetPageSizeCached(); |
| 57 | if (RoundUpTo(Offset, PageSize) != RoundUpTo(Offset + TotalBytes, PageSize)) { |
| 58 | // Resize the VMO to ensure there's sufficient space for the data. |
| 59 | zx_status_t Status = _zx_vmo_set_size(Vmo, Offset + TotalBytes); |
| 60 | if (Status != ZX_OK) { |
| 61 | Report("Failed to resize VMO: %s\n" , _zx_status_get_string(Status)); |
| 62 | return; |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | // Write the data into VMO. |
| 67 | zx_status_t Status = _zx_vmo_write(Vmo, Begin, Offset, TotalBytes); |
| 68 | if (Status != ZX_OK) { |
| 69 | Report("Failed to write: %s\n" , _zx_status_get_string(Status)); |
| 70 | return; |
| 71 | } |
| 72 | Offset += TotalBytes; |
| 73 | |
| 74 | // Record the data size as a property of the VMO. |
| 75 | _zx_object_set_property(Vmo, ZX_PROP_VMO_CONTENT_SIZE, |
| 76 | &Offset, sizeof(Offset)); |
| 77 | } |
| 78 | |
| 79 | void LogWriter::Flush() XRAY_NEVER_INSTRUMENT { |
| 80 | // Nothing to do here since WriteAll writes directly into the VMO. |
| 81 | } |
| 82 | |
| 83 | LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT { |
| 84 | // Create VMO to hold the profile data. |
| 85 | zx_handle_t Vmo; |
| 86 | zx_status_t Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &Vmo); |
| 87 | if (Status != ZX_OK) { |
| 88 | Report("XRay: cannot create VMO: %s\n" , _zx_status_get_string(Status)); |
| 89 | return nullptr; |
| 90 | } |
| 91 | |
| 92 | // Get the KOID of the current process to use in the VMO name. |
| 93 | zx_info_handle_basic_t Info; |
| 94 | Status = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info, |
| 95 | sizeof(Info), NULL, NULL); |
| 96 | if (Status != ZX_OK) { |
| 97 | Report("XRay: cannot get basic info about current process handle: %s\n" , |
| 98 | _zx_status_get_string(Status)); |
| 99 | return nullptr; |
| 100 | } |
| 101 | |
| 102 | // Give the VMO a name including our process KOID so it's easy to spot. |
| 103 | char VmoName[ZX_MAX_NAME_LEN]; |
| 104 | internal_snprintf(VmoName, sizeof(VmoName), "%s.%zu" , ProfileSinkName, |
| 105 | Info.koid); |
| 106 | _zx_object_set_property(Vmo, ZX_PROP_NAME, VmoName, strlen(VmoName)); |
| 107 | |
| 108 | // Duplicate the handle since __sanitizer_publish_data consumes it and |
| 109 | // LogWriter needs to hold onto it. |
| 110 | zx_handle_t Handle; |
| 111 | Status =_zx_handle_duplicate(Vmo, ZX_RIGHT_SAME_RIGHTS, &Handle); |
| 112 | if (Status != ZX_OK) { |
| 113 | Report("XRay: cannot duplicate VMO handle: %s\n" , |
| 114 | _zx_status_get_string(Status)); |
| 115 | return nullptr; |
| 116 | } |
| 117 | |
| 118 | // Publish the VMO that receives the logging. Note the VMO's contents can |
| 119 | // grow and change after publication. The contents won't be read out until |
| 120 | // after the process exits. |
| 121 | __sanitizer_publish_data(ProfileSinkName, Handle); |
| 122 | |
| 123 | // Use the dumpfile symbolizer markup element to write the name of the VMO. |
| 124 | Report("XRay: " FORMAT_DUMPFILE "\n" , ProfileSinkName, VmoName); |
| 125 | |
| 126 | LogWriter *LW = reinterpret_cast<LogWriter *>(InternalAlloc(sizeof(LogWriter))); |
| 127 | new (LW) LogWriter(Vmo); |
| 128 | return LW; |
| 129 | } |
| 130 | |
| 131 | void LogWriter::Close(LogWriter *LW) { |
| 132 | LW->~LogWriter(); |
| 133 | InternalFree(LW); |
| 134 | } |
| 135 | #else // SANITIZER_FUCHSIA |
| 136 | LogWriter::~LogWriter() { |
| 137 | internal_close(fd: Fd); |
| 138 | } |
| 139 | |
| 140 | void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT { |
| 141 | if (Begin == End) |
| 142 | return; |
| 143 | auto TotalBytes = std::distance(first: Begin, last: End); |
| 144 | while (auto Written = write(fd: Fd, buf: Begin, n: TotalBytes)) { |
| 145 | if (Written < 0) { |
| 146 | if (errno == EINTR) |
| 147 | continue; // Try again. |
| 148 | Report(format: "Failed to write; errno = %d\n" , errno); |
| 149 | return; |
| 150 | } |
| 151 | TotalBytes -= Written; |
| 152 | if (TotalBytes == 0) |
| 153 | break; |
| 154 | Begin += Written; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | void LogWriter::Flush() XRAY_NEVER_INSTRUMENT { |
| 159 | fsync(fd: Fd); |
| 160 | } |
| 161 | |
| 162 | LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT { |
| 163 | // Open a temporary file once for the log. |
| 164 | char TmpFilename[256] = {}; |
| 165 | char TmpWildcardPattern[] = "XXXXXX" ; |
| 166 | auto **Argv = GetArgv(); |
| 167 | const char *Progname = !Argv ? "(unknown)" : Argv[0]; |
| 168 | const char *LastSlash = internal_strrchr(s: Progname, c: '/'); |
| 169 | |
| 170 | if (LastSlash != nullptr) |
| 171 | Progname = LastSlash + 1; |
| 172 | |
| 173 | int NeededLength = internal_snprintf( |
| 174 | buffer: TmpFilename, length: sizeof(TmpFilename), format: "%s%s.%s" , |
| 175 | flags()->xray_logfile_base, Progname, TmpWildcardPattern); |
| 176 | if (NeededLength > int(sizeof(TmpFilename))) { |
| 177 | Report(format: "XRay log file name too long (%d): %s\n" , NeededLength, TmpFilename); |
| 178 | return nullptr; |
| 179 | } |
| 180 | int Fd = mkstemp(template: TmpFilename); |
| 181 | if (Fd == -1) { |
| 182 | Report(format: "XRay: Failed opening temporary file '%s'; not logging events.\n" , |
| 183 | TmpFilename); |
| 184 | return nullptr; |
| 185 | } |
| 186 | if (Verbosity()) |
| 187 | Report(format: "XRay: Log file in '%s'\n" , TmpFilename); |
| 188 | |
| 189 | LogWriter *LW = allocate<LogWriter>(); |
| 190 | new (LW) LogWriter(Fd); |
| 191 | return LW; |
| 192 | } |
| 193 | |
| 194 | void LogWriter::Close(LogWriter *LW) { |
| 195 | LW->~LogWriter(); |
| 196 | deallocate(B: LW); |
| 197 | } |
| 198 | #endif // SANITIZER_FUCHSIA |
| 199 | |
| 200 | } // namespace __xray |
| 201 | |