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