1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "berberis/runtime_primitives/profiler_interface.h"
18
19 #include <fcntl.h> // open
20 #include <unistd.h> // write
21
22 #include <array>
23 #include <cstring> // str*
24
25 #include "berberis/base/config_globals.h"
26 #include "berberis/base/format_buffer.h"
27 #include "berberis/base/gettid.h"
28 #include "berberis/base/maps_snapshot.h"
29 #include "berberis/base/scoped_errno.h"
30 #include "berberis/base/tracing.h"
31 #include "berberis/guest_state/guest_addr.h"
32
33 namespace berberis {
34
35 namespace {
36
ProfilerOpenLogFile()37 int ProfilerOpenLogFile() {
38 auto env = GetProfilingConfig();
39 if (!env) {
40 TRACE("Profiling: None");
41 return -1;
42 }
43
44 auto app = GetAppPackageName();
45
46 if (strcmp(env, "1") == 0) {
47 // Special case - profile everything.
48 } else if (app) {
49 // Running an app - must match package name.
50 if (strcmp(app, env) != 0) {
51 TRACE("Profiling: Skipping: app %s doesn't match filter %s", app, env);
52 return -1;
53 }
54 } else if (auto exe = GetMainExecutableRealPath()) {
55 // Running a standalone program - must somehow match main executable path.
56 if (!strstr(exe, env)) {
57 TRACE("Profiling: Skipping: executable %s doesn't match filter %s", exe, env);
58 return -1;
59 }
60 } else {
61 // Running a unit test, or some other non-app, non-executable case.
62 return -1;
63 }
64
65 ScopedErrno scoped_errno;
66
67 char buf[160];
68
69 int pid = GetpidSyscall();
70 if (app) {
71 FormatBuffer(buf, sizeof(buf), "/data/data/%s/perf-%u.map", app, pid);
72 } else {
73 FormatBuffer(buf, sizeof(buf), "/data/local/tmp/perf-%u.map", pid);
74 }
75
76 int fd = open(buf, O_WRONLY | O_CREAT | O_CLOEXEC, S_IWUSR);
77 if (fd == -1) {
78 TRACE("Profiling Error: Failed to open map file %s", buf);
79 } else {
80 TRACE("Probfiling to %s", buf);
81 }
82 return fd;
83 }
84
85 constexpr size_t kMaxMappedNameLen = 16;
86 // Name c-string + terminating null + underscore.
87 using MappedNameBuffer = std::array<char, kMaxMappedNameLen + 2>;
88
89 // Malloc-free implementation.
ConstructMappedNameBuffer(GuestAddr guest_addr)90 MappedNameBuffer ConstructMappedNameBuffer(GuestAddr guest_addr) {
91 MappedNameBuffer buf;
92 auto* maps_snapshot = MapsSnapshot::GetInstance();
93
94 auto mapped_name = maps_snapshot->FindMappedObjectName(guest_addr);
95 if (!mapped_name.has_value()) {
96 // If no mapping is found renew the snapshot and try again.
97 maps_snapshot->Update();
98 auto updated_mapped_name = maps_snapshot->FindMappedObjectName(guest_addr);
99 if (!updated_mapped_name.has_value()) {
100 TRACE("Guest addr %p not found in /proc/self/maps", ToHostAddr<void>(guest_addr));
101 buf[0] = '\0';
102 return buf;
103 }
104 mapped_name.emplace(std::move(updated_mapped_name.value()));
105 }
106
107 // We can use more clever logic here and try to extract the basename, but the parent directory
108 // name may also be interesting (e.g. <guest_arch>/libc.so) so we just take the last
109 // kMaxMappedNameLen symbols for simplicity until it's proven we need something more advanced.
110 // An added benefit of this approach is that symbols look well aligned in the profile.
111 auto& result = mapped_name.value();
112 size_t terminator_pos;
113 if (result.length() > kMaxMappedNameLen) {
114 // In this case it should be safe to call strcpy, but we still use strncpy to be extra careful.
115 strncpy(buf.data(), result.c_str() + result.length() - kMaxMappedNameLen, kMaxMappedNameLen);
116 terminator_pos = kMaxMappedNameLen;
117 } else {
118 strncpy(buf.data(), result.c_str(), kMaxMappedNameLen);
119 terminator_pos = result.length();
120 }
121 buf[terminator_pos] = '_';
122 buf[terminator_pos + 1] = '\0';
123
124 return buf;
125 }
126
127 } // namespace
128
ProfilerLogGeneratedCode(const void * start,size_t size,GuestAddr guest_start,size_t guest_size,const char * jit_suffix)129 void ProfilerLogGeneratedCode(const void* start,
130 size_t size,
131 GuestAddr guest_start,
132 size_t guest_size,
133 const char* jit_suffix) {
134 static int fd = ProfilerOpenLogFile();
135 if (fd == -1) {
136 return;
137 }
138
139 MappedNameBuffer mapped_name_buf = ConstructMappedNameBuffer(guest_start);
140
141 char buf[128];
142 // start size symbol-name
143 size_t n = FormatBuffer(buf,
144 sizeof(buf),
145 "%p 0x%zx %s%s_0x%lx+%zu\n",
146 start,
147 size,
148 mapped_name_buf.data(),
149 jit_suffix,
150 guest_start,
151 guest_size);
152 UNUSED(write(fd, buf, n));
153 }
154
155 } // namespace berberis
156