168d75effSDimitry Andric //===-- xray_utils.cpp ------------------------------------------*- C++ -*-===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of XRay, a dynamic runtime instrumentation system. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric //===----------------------------------------------------------------------===// 1268d75effSDimitry Andric #include "xray_utils.h" 1368d75effSDimitry Andric 1468d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h" 1568d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h" 1668d75effSDimitry Andric #include "xray_allocator.h" 1768d75effSDimitry Andric #include "xray_defs.h" 1868d75effSDimitry Andric #include "xray_flags.h" 1968d75effSDimitry Andric #include <cstdio> 2068d75effSDimitry Andric #include <errno.h> 2168d75effSDimitry Andric #include <fcntl.h> 2268d75effSDimitry Andric #include <iterator> 2368d75effSDimitry Andric #include <stdlib.h> 2468d75effSDimitry Andric #include <sys/types.h> 2568d75effSDimitry Andric #include <tuple> 2668d75effSDimitry Andric #include <unistd.h> 2768d75effSDimitry Andric #include <utility> 2868d75effSDimitry Andric 2968d75effSDimitry Andric #if SANITIZER_FUCHSIA 3068d75effSDimitry Andric #include "sanitizer_common/sanitizer_symbolizer_fuchsia.h" 3168d75effSDimitry Andric 3268d75effSDimitry Andric #include <inttypes.h> 3368d75effSDimitry Andric #include <zircon/process.h> 3468d75effSDimitry Andric #include <zircon/sanitizer.h> 3568d75effSDimitry Andric #include <zircon/status.h> 3668d75effSDimitry Andric #include <zircon/syscalls.h> 3768d75effSDimitry Andric #endif 3868d75effSDimitry Andric 3968d75effSDimitry Andric namespace __xray { 4068d75effSDimitry Andric 4168d75effSDimitry Andric #if SANITIZER_FUCHSIA 4268d75effSDimitry Andric constexpr const char* ProfileSinkName = "llvm-xray"; 4368d75effSDimitry Andric 4468d75effSDimitry Andric LogWriter::~LogWriter() { 4568d75effSDimitry Andric _zx_handle_close(Vmo); 4668d75effSDimitry Andric } 4768d75effSDimitry Andric 4868d75effSDimitry Andric void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT { 4968d75effSDimitry Andric if (Begin == End) 5068d75effSDimitry Andric return; 5168d75effSDimitry Andric auto TotalBytes = std::distance(Begin, End); 5268d75effSDimitry Andric 5368d75effSDimitry Andric const size_t PageSize = flags()->xray_page_size_override > 0 5468d75effSDimitry Andric ? flags()->xray_page_size_override 5568d75effSDimitry Andric : GetPageSizeCached(); 5668d75effSDimitry Andric if (RoundUpTo(Offset, PageSize) != RoundUpTo(Offset + TotalBytes, PageSize)) { 5768d75effSDimitry Andric // Resize the VMO to ensure there's sufficient space for the data. 5868d75effSDimitry Andric zx_status_t Status = _zx_vmo_set_size(Vmo, Offset + TotalBytes); 5968d75effSDimitry Andric if (Status != ZX_OK) { 6068d75effSDimitry Andric Report("Failed to resize VMO: %s\n", _zx_status_get_string(Status)); 6168d75effSDimitry Andric return; 6268d75effSDimitry Andric } 6368d75effSDimitry Andric } 6468d75effSDimitry Andric 6568d75effSDimitry Andric // Write the data into VMO. 6668d75effSDimitry Andric zx_status_t Status = _zx_vmo_write(Vmo, Begin, Offset, TotalBytes); 6768d75effSDimitry Andric if (Status != ZX_OK) { 6868d75effSDimitry Andric Report("Failed to write: %s\n", _zx_status_get_string(Status)); 6968d75effSDimitry Andric return; 7068d75effSDimitry Andric } 7168d75effSDimitry Andric Offset += TotalBytes; 72*5ffd83dbSDimitry Andric 73*5ffd83dbSDimitry Andric // Record the data size as a property of the VMO. 74*5ffd83dbSDimitry Andric _zx_object_set_property(Vmo, ZX_PROP_VMO_CONTENT_SIZE, 75*5ffd83dbSDimitry Andric &Offset, sizeof(Offset)); 7668d75effSDimitry Andric } 7768d75effSDimitry Andric 7868d75effSDimitry Andric void LogWriter::Flush() XRAY_NEVER_INSTRUMENT { 7968d75effSDimitry Andric // Nothing to do here since WriteAll writes directly into the VMO. 8068d75effSDimitry Andric } 8168d75effSDimitry Andric 8268d75effSDimitry Andric LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT { 8368d75effSDimitry Andric // Create VMO to hold the profile data. 8468d75effSDimitry Andric zx_handle_t Vmo; 8568d75effSDimitry Andric zx_status_t Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &Vmo); 8668d75effSDimitry Andric if (Status != ZX_OK) { 8768d75effSDimitry Andric Report("XRay: cannot create VMO: %s\n", _zx_status_get_string(Status)); 8868d75effSDimitry Andric return nullptr; 8968d75effSDimitry Andric } 9068d75effSDimitry Andric 9168d75effSDimitry Andric // Get the KOID of the current process to use in the VMO name. 9268d75effSDimitry Andric zx_info_handle_basic_t Info; 9368d75effSDimitry Andric Status = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info, 9468d75effSDimitry Andric sizeof(Info), NULL, NULL); 9568d75effSDimitry Andric if (Status != ZX_OK) { 9668d75effSDimitry Andric Report("XRay: cannot get basic info about current process handle: %s\n", 9768d75effSDimitry Andric _zx_status_get_string(Status)); 9868d75effSDimitry Andric return nullptr; 9968d75effSDimitry Andric } 10068d75effSDimitry Andric 10168d75effSDimitry Andric // Give the VMO a name including our process KOID so it's easy to spot. 10268d75effSDimitry Andric char VmoName[ZX_MAX_NAME_LEN]; 10368d75effSDimitry Andric internal_snprintf(VmoName, sizeof(VmoName), "%s.%zu", ProfileSinkName, 10468d75effSDimitry Andric Info.koid); 10568d75effSDimitry Andric _zx_object_set_property(Vmo, ZX_PROP_NAME, VmoName, strlen(VmoName)); 10668d75effSDimitry Andric 10768d75effSDimitry Andric // Duplicate the handle since __sanitizer_publish_data consumes it and 10868d75effSDimitry Andric // LogWriter needs to hold onto it. 10968d75effSDimitry Andric zx_handle_t Handle; 11068d75effSDimitry Andric Status =_zx_handle_duplicate(Vmo, ZX_RIGHT_SAME_RIGHTS, &Handle); 11168d75effSDimitry Andric if (Status != ZX_OK) { 11268d75effSDimitry Andric Report("XRay: cannot duplicate VMO handle: %s\n", 11368d75effSDimitry Andric _zx_status_get_string(Status)); 11468d75effSDimitry Andric return nullptr; 11568d75effSDimitry Andric } 11668d75effSDimitry Andric 11768d75effSDimitry Andric // Publish the VMO that receives the logging. Note the VMO's contents can 11868d75effSDimitry Andric // grow and change after publication. The contents won't be read out until 11968d75effSDimitry Andric // after the process exits. 12068d75effSDimitry Andric __sanitizer_publish_data(ProfileSinkName, Handle); 12168d75effSDimitry Andric 12268d75effSDimitry Andric // Use the dumpfile symbolizer markup element to write the name of the VMO. 12368d75effSDimitry Andric Report("XRay: " FORMAT_DUMPFILE "\n", ProfileSinkName, VmoName); 12468d75effSDimitry Andric 12568d75effSDimitry Andric LogWriter *LW = reinterpret_cast<LogWriter *>(InternalAlloc(sizeof(LogWriter))); 12668d75effSDimitry Andric new (LW) LogWriter(Vmo); 12768d75effSDimitry Andric return LW; 12868d75effSDimitry Andric } 12968d75effSDimitry Andric 13068d75effSDimitry Andric void LogWriter::Close(LogWriter *LW) { 13168d75effSDimitry Andric LW->~LogWriter(); 13268d75effSDimitry Andric InternalFree(LW); 13368d75effSDimitry Andric } 13468d75effSDimitry Andric #else // SANITIZER_FUCHSIA 13568d75effSDimitry Andric LogWriter::~LogWriter() { 13668d75effSDimitry Andric internal_close(Fd); 13768d75effSDimitry Andric } 13868d75effSDimitry Andric 13968d75effSDimitry Andric void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT { 14068d75effSDimitry Andric if (Begin == End) 14168d75effSDimitry Andric return; 14268d75effSDimitry Andric auto TotalBytes = std::distance(Begin, End); 14368d75effSDimitry Andric while (auto Written = write(Fd, Begin, TotalBytes)) { 14468d75effSDimitry Andric if (Written < 0) { 14568d75effSDimitry Andric if (errno == EINTR) 14668d75effSDimitry Andric continue; // Try again. 14768d75effSDimitry Andric Report("Failed to write; errno = %d\n", errno); 14868d75effSDimitry Andric return; 14968d75effSDimitry Andric } 15068d75effSDimitry Andric TotalBytes -= Written; 15168d75effSDimitry Andric if (TotalBytes == 0) 15268d75effSDimitry Andric break; 15368d75effSDimitry Andric Begin += Written; 15468d75effSDimitry Andric } 15568d75effSDimitry Andric } 15668d75effSDimitry Andric 15768d75effSDimitry Andric void LogWriter::Flush() XRAY_NEVER_INSTRUMENT { 15868d75effSDimitry Andric fsync(Fd); 15968d75effSDimitry Andric } 16068d75effSDimitry Andric 16168d75effSDimitry Andric LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT { 16268d75effSDimitry Andric // Open a temporary file once for the log. 16368d75effSDimitry Andric char TmpFilename[256] = {}; 16468d75effSDimitry Andric char TmpWildcardPattern[] = "XXXXXX"; 16568d75effSDimitry Andric auto **Argv = GetArgv(); 16668d75effSDimitry Andric const char *Progname = !Argv ? "(unknown)" : Argv[0]; 16768d75effSDimitry Andric const char *LastSlash = internal_strrchr(Progname, '/'); 16868d75effSDimitry Andric 16968d75effSDimitry Andric if (LastSlash != nullptr) 17068d75effSDimitry Andric Progname = LastSlash + 1; 17168d75effSDimitry Andric 17268d75effSDimitry Andric int NeededLength = internal_snprintf( 17368d75effSDimitry Andric TmpFilename, sizeof(TmpFilename), "%s%s.%s", 17468d75effSDimitry Andric flags()->xray_logfile_base, Progname, TmpWildcardPattern); 17568d75effSDimitry Andric if (NeededLength > int(sizeof(TmpFilename))) { 17668d75effSDimitry Andric Report("XRay log file name too long (%d): %s\n", NeededLength, TmpFilename); 17768d75effSDimitry Andric return nullptr; 17868d75effSDimitry Andric } 17968d75effSDimitry Andric int Fd = mkstemp(TmpFilename); 18068d75effSDimitry Andric if (Fd == -1) { 18168d75effSDimitry Andric Report("XRay: Failed opening temporary file '%s'; not logging events.\n", 18268d75effSDimitry Andric TmpFilename); 18368d75effSDimitry Andric return nullptr; 18468d75effSDimitry Andric } 18568d75effSDimitry Andric if (Verbosity()) 18668d75effSDimitry Andric Report("XRay: Log file in '%s'\n", TmpFilename); 18768d75effSDimitry Andric 18868d75effSDimitry Andric LogWriter *LW = allocate<LogWriter>(); 18968d75effSDimitry Andric new (LW) LogWriter(Fd); 19068d75effSDimitry Andric return LW; 19168d75effSDimitry Andric } 19268d75effSDimitry Andric 19368d75effSDimitry Andric void LogWriter::Close(LogWriter *LW) { 19468d75effSDimitry Andric LW->~LogWriter(); 19568d75effSDimitry Andric deallocate(LW); 19668d75effSDimitry Andric } 19768d75effSDimitry Andric #endif // SANITIZER_FUCHSIA 19868d75effSDimitry Andric 19968d75effSDimitry Andric } // namespace __xray 200