xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/xray/xray_utils.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
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