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 | |