1*0b57cec5SDimitry Andric //===-- OProfileWrapper.cpp - OProfile JIT API Wrapper implementation -----===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // This file implements the interface in OProfileWrapper.h. It is responsible
10*0b57cec5SDimitry Andric // for loading the opagent dynamic library when the first call to an op_
11*0b57cec5SDimitry Andric // function occurs.
12*0b57cec5SDimitry Andric //
13*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
14*0b57cec5SDimitry Andric
15*0b57cec5SDimitry Andric #include "llvm/ExecutionEngine/OProfileWrapper.h"
16*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
17*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
18*0b57cec5SDimitry Andric #include "llvm/Support/DynamicLibrary.h"
19*0b57cec5SDimitry Andric #include "llvm/Support/Mutex.h"
20*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
21*0b57cec5SDimitry Andric #include <cstring>
22*0b57cec5SDimitry Andric #include <dirent.h>
23*0b57cec5SDimitry Andric #include <fcntl.h>
24*0b57cec5SDimitry Andric #include <mutex>
25*0b57cec5SDimitry Andric #include <stddef.h>
26*0b57cec5SDimitry Andric #include <sys/stat.h>
27*0b57cec5SDimitry Andric #include <unistd.h>
28*0b57cec5SDimitry Andric
29*0b57cec5SDimitry Andric #define DEBUG_TYPE "oprofile-wrapper"
30*0b57cec5SDimitry Andric
31*0b57cec5SDimitry Andric namespace {
32*0b57cec5SDimitry Andric
33*0b57cec5SDimitry Andric // Global mutex to ensure a single thread initializes oprofile agent.
34*0b57cec5SDimitry Andric llvm::sys::Mutex OProfileInitializationMutex;
35*0b57cec5SDimitry Andric
36*0b57cec5SDimitry Andric } // anonymous namespace
37*0b57cec5SDimitry Andric
38*0b57cec5SDimitry Andric namespace llvm {
39*0b57cec5SDimitry Andric
OProfileWrapper()40*0b57cec5SDimitry Andric OProfileWrapper::OProfileWrapper()
41*0b57cec5SDimitry Andric : Agent(0),
42*0b57cec5SDimitry Andric OpenAgentFunc(0),
43*0b57cec5SDimitry Andric CloseAgentFunc(0),
44*0b57cec5SDimitry Andric WriteNativeCodeFunc(0),
45*0b57cec5SDimitry Andric WriteDebugLineInfoFunc(0),
46*0b57cec5SDimitry Andric UnloadNativeCodeFunc(0),
47*0b57cec5SDimitry Andric MajorVersionFunc(0),
48*0b57cec5SDimitry Andric MinorVersionFunc(0),
49*0b57cec5SDimitry Andric IsOProfileRunningFunc(0),
50*0b57cec5SDimitry Andric Initialized(false) {
51*0b57cec5SDimitry Andric }
52*0b57cec5SDimitry Andric
initialize()53*0b57cec5SDimitry Andric bool OProfileWrapper::initialize() {
54*0b57cec5SDimitry Andric using namespace llvm;
55*0b57cec5SDimitry Andric using namespace llvm::sys;
56*0b57cec5SDimitry Andric
57*0b57cec5SDimitry Andric std::lock_guard<sys::Mutex> Guard(OProfileInitializationMutex);
58*0b57cec5SDimitry Andric
59*0b57cec5SDimitry Andric if (Initialized)
60*0b57cec5SDimitry Andric return OpenAgentFunc != 0;
61*0b57cec5SDimitry Andric
62*0b57cec5SDimitry Andric Initialized = true;
63*0b57cec5SDimitry Andric
64*0b57cec5SDimitry Andric // If the oprofile daemon is not running, don't load the opagent library
65*0b57cec5SDimitry Andric if (!isOProfileRunning()) {
66*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "OProfile daemon is not detected.\n");
67*0b57cec5SDimitry Andric return false;
68*0b57cec5SDimitry Andric }
69*0b57cec5SDimitry Andric
70*0b57cec5SDimitry Andric std::string error;
71*0b57cec5SDimitry Andric if(!DynamicLibrary::LoadLibraryPermanently("libopagent.so", &error)) {
72*0b57cec5SDimitry Andric LLVM_DEBUG(
73*0b57cec5SDimitry Andric dbgs()
74*0b57cec5SDimitry Andric << "OProfile connector library libopagent.so could not be loaded: "
75*0b57cec5SDimitry Andric << error << "\n");
76*0b57cec5SDimitry Andric }
77*0b57cec5SDimitry Andric
78*0b57cec5SDimitry Andric // Get the addresses of the opagent functions
79*0b57cec5SDimitry Andric OpenAgentFunc = (op_open_agent_ptr_t)(intptr_t)
80*0b57cec5SDimitry Andric DynamicLibrary::SearchForAddressOfSymbol("op_open_agent");
81*0b57cec5SDimitry Andric CloseAgentFunc = (op_close_agent_ptr_t)(intptr_t)
82*0b57cec5SDimitry Andric DynamicLibrary::SearchForAddressOfSymbol("op_close_agent");
83*0b57cec5SDimitry Andric WriteNativeCodeFunc = (op_write_native_code_ptr_t)(intptr_t)
84*0b57cec5SDimitry Andric DynamicLibrary::SearchForAddressOfSymbol("op_write_native_code");
85*0b57cec5SDimitry Andric WriteDebugLineInfoFunc = (op_write_debug_line_info_ptr_t)(intptr_t)
86*0b57cec5SDimitry Andric DynamicLibrary::SearchForAddressOfSymbol("op_write_debug_line_info");
87*0b57cec5SDimitry Andric UnloadNativeCodeFunc = (op_unload_native_code_ptr_t)(intptr_t)
88*0b57cec5SDimitry Andric DynamicLibrary::SearchForAddressOfSymbol("op_unload_native_code");
89*0b57cec5SDimitry Andric MajorVersionFunc = (op_major_version_ptr_t)(intptr_t)
90*0b57cec5SDimitry Andric DynamicLibrary::SearchForAddressOfSymbol("op_major_version");
91*0b57cec5SDimitry Andric MinorVersionFunc = (op_major_version_ptr_t)(intptr_t)
92*0b57cec5SDimitry Andric DynamicLibrary::SearchForAddressOfSymbol("op_minor_version");
93*0b57cec5SDimitry Andric
94*0b57cec5SDimitry Andric // With missing functions, we can do nothing
95*0b57cec5SDimitry Andric if (!OpenAgentFunc
96*0b57cec5SDimitry Andric || !CloseAgentFunc
97*0b57cec5SDimitry Andric || !WriteNativeCodeFunc
98*0b57cec5SDimitry Andric || !WriteDebugLineInfoFunc
99*0b57cec5SDimitry Andric || !UnloadNativeCodeFunc) {
100*0b57cec5SDimitry Andric OpenAgentFunc = 0;
101*0b57cec5SDimitry Andric CloseAgentFunc = 0;
102*0b57cec5SDimitry Andric WriteNativeCodeFunc = 0;
103*0b57cec5SDimitry Andric WriteDebugLineInfoFunc = 0;
104*0b57cec5SDimitry Andric UnloadNativeCodeFunc = 0;
105*0b57cec5SDimitry Andric return false;
106*0b57cec5SDimitry Andric }
107*0b57cec5SDimitry Andric
108*0b57cec5SDimitry Andric return true;
109*0b57cec5SDimitry Andric }
110*0b57cec5SDimitry Andric
isOProfileRunning()111*0b57cec5SDimitry Andric bool OProfileWrapper::isOProfileRunning() {
112*0b57cec5SDimitry Andric if (IsOProfileRunningFunc != 0)
113*0b57cec5SDimitry Andric return IsOProfileRunningFunc();
114*0b57cec5SDimitry Andric return checkForOProfileProcEntry();
115*0b57cec5SDimitry Andric }
116*0b57cec5SDimitry Andric
checkForOProfileProcEntry()117*0b57cec5SDimitry Andric bool OProfileWrapper::checkForOProfileProcEntry() {
118*0b57cec5SDimitry Andric DIR* ProcDir;
119*0b57cec5SDimitry Andric
120*0b57cec5SDimitry Andric ProcDir = opendir("/proc");
121*0b57cec5SDimitry Andric if (!ProcDir)
122*0b57cec5SDimitry Andric return false;
123*0b57cec5SDimitry Andric
124*0b57cec5SDimitry Andric // Walk the /proc tree looking for the oprofile daemon
125*0b57cec5SDimitry Andric struct dirent* Entry;
126*0b57cec5SDimitry Andric while (0 != (Entry = readdir(ProcDir))) {
127*0b57cec5SDimitry Andric if (Entry->d_type == DT_DIR) {
128*0b57cec5SDimitry Andric // Build a path from the current entry name
129*0b57cec5SDimitry Andric SmallString<256> CmdLineFName;
130*0b57cec5SDimitry Andric raw_svector_ostream(CmdLineFName) << "/proc/" << Entry->d_name
131*0b57cec5SDimitry Andric << "/cmdline";
132*0b57cec5SDimitry Andric
133*0b57cec5SDimitry Andric // Open the cmdline file
134*0b57cec5SDimitry Andric int CmdLineFD = open(CmdLineFName.c_str(), S_IRUSR);
135*0b57cec5SDimitry Andric if (CmdLineFD != -1) {
136*0b57cec5SDimitry Andric char ExeName[PATH_MAX+1];
137*0b57cec5SDimitry Andric char* BaseName = 0;
138*0b57cec5SDimitry Andric
139*0b57cec5SDimitry Andric // Read the cmdline file
140*0b57cec5SDimitry Andric ssize_t NumRead = read(CmdLineFD, ExeName, PATH_MAX+1);
141*0b57cec5SDimitry Andric close(CmdLineFD);
142*0b57cec5SDimitry Andric ssize_t Idx = 0;
143*0b57cec5SDimitry Andric
144*0b57cec5SDimitry Andric if (ExeName[0] != '/') {
145*0b57cec5SDimitry Andric BaseName = ExeName;
146*0b57cec5SDimitry Andric }
147*0b57cec5SDimitry Andric
148*0b57cec5SDimitry Andric // Find the terminator for the first string
149*0b57cec5SDimitry Andric while (Idx < NumRead-1 && ExeName[Idx] != 0) {
150*0b57cec5SDimitry Andric Idx++;
151*0b57cec5SDimitry Andric }
152*0b57cec5SDimitry Andric
153*0b57cec5SDimitry Andric // Go back to the last non-null character
154*0b57cec5SDimitry Andric Idx--;
155*0b57cec5SDimitry Andric
156*0b57cec5SDimitry Andric // Find the last path separator in the first string
157*0b57cec5SDimitry Andric while (Idx > 0) {
158*0b57cec5SDimitry Andric if (ExeName[Idx] == '/') {
159*0b57cec5SDimitry Andric BaseName = ExeName + Idx + 1;
160*0b57cec5SDimitry Andric break;
161*0b57cec5SDimitry Andric }
162*0b57cec5SDimitry Andric Idx--;
163*0b57cec5SDimitry Andric }
164*0b57cec5SDimitry Andric
165*0b57cec5SDimitry Andric // Test this to see if it is the oprofile daemon
166*0b57cec5SDimitry Andric if (BaseName != 0 && (!strcmp("oprofiled", BaseName) ||
167*0b57cec5SDimitry Andric !strcmp("operf", BaseName))) {
168*0b57cec5SDimitry Andric // If it is, we're done
169*0b57cec5SDimitry Andric closedir(ProcDir);
170*0b57cec5SDimitry Andric return true;
171*0b57cec5SDimitry Andric }
172*0b57cec5SDimitry Andric }
173*0b57cec5SDimitry Andric }
174*0b57cec5SDimitry Andric }
175*0b57cec5SDimitry Andric
176*0b57cec5SDimitry Andric // We've looked through all the files and didn't find the daemon
177*0b57cec5SDimitry Andric closedir(ProcDir);
178*0b57cec5SDimitry Andric return false;
179*0b57cec5SDimitry Andric }
180*0b57cec5SDimitry Andric
op_open_agent()181*0b57cec5SDimitry Andric bool OProfileWrapper::op_open_agent() {
182*0b57cec5SDimitry Andric if (!Initialized)
183*0b57cec5SDimitry Andric initialize();
184*0b57cec5SDimitry Andric
185*0b57cec5SDimitry Andric if (OpenAgentFunc != 0) {
186*0b57cec5SDimitry Andric Agent = OpenAgentFunc();
187*0b57cec5SDimitry Andric return Agent != 0;
188*0b57cec5SDimitry Andric }
189*0b57cec5SDimitry Andric
190*0b57cec5SDimitry Andric return false;
191*0b57cec5SDimitry Andric }
192*0b57cec5SDimitry Andric
op_close_agent()193*0b57cec5SDimitry Andric int OProfileWrapper::op_close_agent() {
194*0b57cec5SDimitry Andric if (!Initialized)
195*0b57cec5SDimitry Andric initialize();
196*0b57cec5SDimitry Andric
197*0b57cec5SDimitry Andric int ret = -1;
198*0b57cec5SDimitry Andric if (Agent && CloseAgentFunc) {
199*0b57cec5SDimitry Andric ret = CloseAgentFunc(Agent);
200*0b57cec5SDimitry Andric if (ret == 0) {
201*0b57cec5SDimitry Andric Agent = 0;
202*0b57cec5SDimitry Andric }
203*0b57cec5SDimitry Andric }
204*0b57cec5SDimitry Andric return ret;
205*0b57cec5SDimitry Andric }
206*0b57cec5SDimitry Andric
isAgentAvailable()207*0b57cec5SDimitry Andric bool OProfileWrapper::isAgentAvailable() {
208*0b57cec5SDimitry Andric return Agent != 0;
209*0b57cec5SDimitry Andric }
210*0b57cec5SDimitry Andric
op_write_native_code(const char * Name,uint64_t Addr,void const * Code,const unsigned int Size)211*0b57cec5SDimitry Andric int OProfileWrapper::op_write_native_code(const char* Name,
212*0b57cec5SDimitry Andric uint64_t Addr,
213*0b57cec5SDimitry Andric void const* Code,
214*0b57cec5SDimitry Andric const unsigned int Size) {
215*0b57cec5SDimitry Andric if (!Initialized)
216*0b57cec5SDimitry Andric initialize();
217*0b57cec5SDimitry Andric
218*0b57cec5SDimitry Andric if (Agent && WriteNativeCodeFunc)
219*0b57cec5SDimitry Andric return WriteNativeCodeFunc(Agent, Name, Addr, Code, Size);
220*0b57cec5SDimitry Andric
221*0b57cec5SDimitry Andric return -1;
222*0b57cec5SDimitry Andric }
223*0b57cec5SDimitry Andric
op_write_debug_line_info(void const * Code,size_t NumEntries,struct debug_line_info const * Info)224*0b57cec5SDimitry Andric int OProfileWrapper::op_write_debug_line_info(
225*0b57cec5SDimitry Andric void const* Code,
226*0b57cec5SDimitry Andric size_t NumEntries,
227*0b57cec5SDimitry Andric struct debug_line_info const* Info) {
228*0b57cec5SDimitry Andric if (!Initialized)
229*0b57cec5SDimitry Andric initialize();
230*0b57cec5SDimitry Andric
231*0b57cec5SDimitry Andric if (Agent && WriteDebugLineInfoFunc)
232*0b57cec5SDimitry Andric return WriteDebugLineInfoFunc(Agent, Code, NumEntries, Info);
233*0b57cec5SDimitry Andric
234*0b57cec5SDimitry Andric return -1;
235*0b57cec5SDimitry Andric }
236*0b57cec5SDimitry Andric
op_major_version()237*0b57cec5SDimitry Andric int OProfileWrapper::op_major_version() {
238*0b57cec5SDimitry Andric if (!Initialized)
239*0b57cec5SDimitry Andric initialize();
240*0b57cec5SDimitry Andric
241*0b57cec5SDimitry Andric if (Agent && MajorVersionFunc)
242*0b57cec5SDimitry Andric return MajorVersionFunc();
243*0b57cec5SDimitry Andric
244*0b57cec5SDimitry Andric return -1;
245*0b57cec5SDimitry Andric }
246*0b57cec5SDimitry Andric
op_minor_version()247*0b57cec5SDimitry Andric int OProfileWrapper::op_minor_version() {
248*0b57cec5SDimitry Andric if (!Initialized)
249*0b57cec5SDimitry Andric initialize();
250*0b57cec5SDimitry Andric
251*0b57cec5SDimitry Andric if (Agent && MinorVersionFunc)
252*0b57cec5SDimitry Andric return MinorVersionFunc();
253*0b57cec5SDimitry Andric
254*0b57cec5SDimitry Andric return -1;
255*0b57cec5SDimitry Andric }
256*0b57cec5SDimitry Andric
op_unload_native_code(uint64_t Addr)257*0b57cec5SDimitry Andric int OProfileWrapper::op_unload_native_code(uint64_t Addr) {
258*0b57cec5SDimitry Andric if (!Initialized)
259*0b57cec5SDimitry Andric initialize();
260*0b57cec5SDimitry Andric
261*0b57cec5SDimitry Andric if (Agent && UnloadNativeCodeFunc)
262*0b57cec5SDimitry Andric return UnloadNativeCodeFunc(Agent, Addr);
263*0b57cec5SDimitry Andric
264*0b57cec5SDimitry Andric return -1;
265*0b57cec5SDimitry Andric }
266*0b57cec5SDimitry Andric
267*0b57cec5SDimitry Andric } // namespace llvm
268