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