1 /**
2 * @file opagent.c
3 * Interface to report symbol names and dynamically generated code to Oprofile
4 *
5 * @remark Copyright 2007 OProfile authors
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * @author Jens Wilke
22 * @Modifications Daniel Hansel
23 *
24 * Copyright IBM Corporation 2007
25 *
26 */
27
28 /******************************************************************
29 * ATTENTION:
30 * When adding new functions to this interface, you MUST update
31 * opagent_symbols.ver.
32 *
33 * If a change is made to an existing exported function, perform the
34 * the following steps. As an example, assume op_open_agent()
35 * is being updated to include a 'dump_code' parameter.
36 * 1. Update the opagent.ver file with a new version node, and
37 * add the op_open_agent to it. Note that op_open_agent
38 * is also still declared in the original version node.
39 * 2. Add '__asm__(".symver <blah>") directives to this .c source file.
40 * For this example, the directives would be as follows:
41 * __asm__(".symver op_open_agent_1_0,op_open_agent@OPAGENT_1.0");
42 * __asm__(".symver op_open_agent_2_0,op_open_agent@@OPAGENT_2.0");
43 * 3. Update the declaration of op_open_agent in the header file with
44 * the additional parameter.
45 * 4. Change the name of the original op_open_agent to "op_open_agent_1_0"
46 * in this .c source file.
47 * 5. Add the new op_open_agent_2_0(int dump_code) function in this
48 * .c source file.
49 *
50 * See libopagent/Makefile.am for more information.
51 *******************************************************************/
52
53 #include <stdio.h>
54 #include <errno.h>
55 #include <string.h>
56 #include <stdint.h>
57 #include <limits.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <fcntl.h>
61 #include <unistd.h>
62 #include <time.h>
63 #include <bfd.h>
64
65 #include "opagent.h"
66 #include "op_config.h"
67 #include "jitdump.h"
68
69 // Declare BFD-related global variables.
70 static char * _bfd_target_name;
71 static int _bfd_arch;
72 static unsigned int _bfd_mach;
73
74 // Define BFD-related global variables.
define_bfd_vars(void)75 static int define_bfd_vars(void)
76 {
77 bfd * bfd;
78 bfd_boolean r;
79 int len;
80 #define MAX_PATHLENGTH 2048
81 char mypath[MAX_PATHLENGTH];
82
83 len = readlink("/proc/self/exe", mypath, sizeof(mypath));
84
85 if (len < 0) {
86 fprintf(stderr, "libopagent: readlink /proc/self/exe failed\n");
87 return -1;
88 }
89 if (len >= MAX_PATHLENGTH) {
90 fprintf(stderr, "libopagent: readlink /proc/self/exe returned"
91 " path length longer than %d.\n", MAX_PATHLENGTH);
92
93 return -1;
94 }
95 mypath[len] = '\0';
96
97 bfd_init();
98 bfd = bfd_openr(mypath, NULL);
99 if (bfd == NULL) {
100 bfd_perror("bfd_openr error. Cannot get required BFD info");
101 return -1;
102 }
103 r = bfd_check_format(bfd, bfd_object);
104 if (!r) {
105 bfd_perror("bfd_get_arch error. Cannot get required BFD info");
106 return -1;
107 }
108 _bfd_target_name = bfd->xvec->name;
109 _bfd_arch = bfd_get_arch(bfd);
110 _bfd_mach = bfd_get_mach(bfd);
111
112 return 0;
113 }
114 /**
115 * Define the version of the opagent library.
116 */
117 #define OP_MAJOR_VERSION 1
118 #define OP_MINOR_VERSION 0
119
120 #define AGENT_DIR OP_SESSION_DIR_DEFAULT "jitdump"
121
122 #define MSG_MAXLEN 20
123
op_open_agent(void)124 op_agent_t op_open_agent(void)
125 {
126 char pad_bytes[7] = {0, 0, 0, 0, 0, 0, 0};
127 int pad_cnt;
128 char dump_path[PATH_MAX];
129 char err_msg[PATH_MAX + 16];
130 struct stat dirstat;
131 int rc;
132 struct jitheader header;
133 int fd;
134 struct timeval tv;
135 FILE * dumpfile = NULL;
136
137 rc = stat(AGENT_DIR, &dirstat);
138 if (rc || !S_ISDIR(dirstat.st_mode)) {
139 if (!rc)
140 errno = ENOTDIR;
141 fprintf(stderr,"libopagent: Jitdump agent directory %s "
142 "missing\n", AGENT_DIR);
143 fprintf(stderr,"libopagent: do opcontrol --setup or "
144 "opcontrol --reset, first\n");
145 return NULL;
146 }
147 snprintf(dump_path, PATH_MAX, "%s/%i.dump", AGENT_DIR, getpid());
148 snprintf(err_msg, PATH_MAX + 16, "Error opening %s\n", dump_path);
149 // make the dump file only accessible for the user for security reason.
150 fd = creat(dump_path, S_IRUSR|S_IWUSR);
151 if (fd == -1) {
152 fprintf(stderr, "%s\n", err_msg);
153 return NULL;
154 }
155 dumpfile = fdopen(fd, "w");
156 if (!dumpfile) {
157 fprintf(stderr, "%s\n", err_msg);
158 return NULL;
159 }
160 if (define_bfd_vars())
161 return NULL;
162 header.magic = JITHEADER_MAGIC;
163 header.version = JITHEADER_VERSION;
164 header.totalsize = sizeof(header) + strlen(_bfd_target_name) + 1;
165 /* calculate amount of padding '\0' */
166 pad_cnt = PADDING_8ALIGNED(header.totalsize);
167 header.totalsize += pad_cnt;
168 header.bfd_arch = _bfd_arch;
169 header.bfd_mach = _bfd_mach;
170 if (gettimeofday(&tv, NULL)) {
171 fprintf(stderr, "gettimeofday failed\n");
172 return NULL;
173 }
174
175 header.timestamp = tv.tv_sec;
176 snprintf(err_msg, PATH_MAX + 16, "Error writing to %s", dump_path);
177 if (!fwrite(&header, sizeof(header), 1, dumpfile)) {
178 fprintf(stderr, "%s\n", err_msg);
179 return NULL;
180 }
181 if (!fwrite(_bfd_target_name, strlen(_bfd_target_name) + 1, 1,
182 dumpfile)) {
183 fprintf(stderr, "%s\n", err_msg);
184 return NULL;
185 }
186 /* write padding '\0' if necessary */
187 if (pad_cnt && !fwrite(pad_bytes, pad_cnt, 1, dumpfile)) {
188 fprintf(stderr, "%s\n", err_msg);
189 return NULL;
190 }
191 fflush(dumpfile);
192 return (op_agent_t)dumpfile;
193 }
194
195
op_close_agent(op_agent_t hdl)196 int op_close_agent(op_agent_t hdl)
197 {
198 struct jr_code_close rec;
199 struct timeval tv;
200 FILE * dumpfile = (FILE *) hdl;
201 if (!dumpfile) {
202 errno = EINVAL;
203 return -1;
204 }
205 rec.id = JIT_CODE_CLOSE;
206 rec.total_size = sizeof(rec);
207 if (gettimeofday(&tv, NULL)) {
208 fprintf(stderr, "gettimeofday failed\n");
209 return -1;
210 }
211 rec.timestamp = tv.tv_sec;
212
213 if (!fwrite(&rec, sizeof(rec), 1, dumpfile))
214 return -1;
215 fclose(dumpfile);
216 dumpfile = NULL;
217 return 0;
218 }
219
220
op_write_native_code(op_agent_t hdl,char const * symbol_name,uint64_t vma,void const * code,unsigned int const size)221 int op_write_native_code(op_agent_t hdl, char const * symbol_name,
222 uint64_t vma, void const * code, unsigned int const size)
223 {
224 struct jr_code_load rec;
225 struct timeval tv;
226 size_t sz_symb_name;
227 char pad_bytes[7] = { 0, 0, 0, 0, 0, 0, 0 };
228 size_t padding_count;
229 FILE * dumpfile = (FILE *) hdl;
230
231 if (!dumpfile) {
232 errno = EINVAL;
233 fprintf(stderr, "Invalid hdl argument\n");
234 return -1;
235 }
236 sz_symb_name = strlen(symbol_name) + 1;
237
238 rec.id = JIT_CODE_LOAD;
239 rec.code_size = size;
240 rec.vma = vma;
241 rec.code_addr = (u64) (uintptr_t) code;
242 rec.total_size = code ? sizeof(rec) + sz_symb_name + size :
243 sizeof(rec) + sz_symb_name;
244 /* calculate amount of padding '\0' */
245 padding_count = PADDING_8ALIGNED(rec.total_size);
246 rec.total_size += padding_count;
247 if (gettimeofday(&tv, NULL)) {
248 fprintf(stderr, "gettimeofday failed\n");
249 return -1;
250 }
251
252 rec.timestamp = tv.tv_sec;
253
254 /* locking makes sure that we continuously write this record, if
255 * we are called within a multi-threaded context */
256 flockfile(dumpfile);
257 /* Write record, symbol name, code (optionally), and (if necessary)
258 * additonal padding \0 bytes.
259 */
260 if (fwrite_unlocked(&rec, sizeof(rec), 1, dumpfile) &&
261 fwrite_unlocked(symbol_name, sz_symb_name, 1, dumpfile)) {
262 if (code)
263 fwrite_unlocked(code, size, 1, dumpfile);
264 if (padding_count)
265 fwrite_unlocked(pad_bytes, padding_count, 1, dumpfile);
266 /* Always flush to ensure conversion code to elf will see
267 * data as soon as possible */
268 fflush_unlocked(dumpfile);
269 funlockfile(dumpfile);
270 return 0;
271 }
272 fflush_unlocked(dumpfile);
273 funlockfile(dumpfile);
274 return -1;
275 }
276
277
op_write_debug_line_info(op_agent_t hdl,void const * code,size_t nr_entry,struct debug_line_info const * compile_map)278 int op_write_debug_line_info(op_agent_t hdl, void const * code,
279 size_t nr_entry,
280 struct debug_line_info const * compile_map)
281 {
282 struct jr_code_debug_info rec;
283 long cur_pos, last_pos;
284 struct timeval tv;
285 size_t i;
286 size_t padding_count;
287 char padd_bytes[7] = {0, 0, 0, 0, 0, 0, 0};
288 int rc = -1;
289 FILE * dumpfile = (FILE *) hdl;
290
291 if (!dumpfile) {
292 errno = EINVAL;
293 fprintf(stderr, "Invalid hdl argument\n");
294 return -1;
295 }
296
297 /* write nothing if no entries are provided */
298 if (nr_entry == 0)
299 return 0;
300
301 rec.id = JIT_CODE_DEBUG_INFO;
302 rec.code_addr = (uint64_t)(uintptr_t)code;
303 /* will be fixed after writing debug line info */
304 rec.total_size = 0;
305 rec.nr_entry = nr_entry;
306 if (gettimeofday(&tv, NULL)) {
307 fprintf(stderr, "gettimeofday failed\n");
308 return -1;
309 }
310
311 rec.timestamp = tv.tv_sec;
312
313 flockfile(dumpfile);
314
315 if ((cur_pos = ftell(dumpfile)) == -1l)
316 goto error;
317 if (!fwrite_unlocked(&rec, sizeof(rec), 1, dumpfile))
318 goto error;
319 for (i = 0; i < nr_entry; ++i) {
320 if (!fwrite_unlocked(&compile_map[i].vma,
321 sizeof(compile_map[i].vma), 1,
322 dumpfile) ||
323 !fwrite_unlocked(&compile_map[i].lineno,
324 sizeof(compile_map[i].lineno), 1,
325 dumpfile) ||
326 !fwrite_unlocked(compile_map[i].filename,
327 strlen(compile_map[i].filename) + 1, 1,
328 dumpfile))
329 goto error;
330 }
331
332 if ((last_pos = ftell(dumpfile)) == -1l)
333 goto error;
334 rec.total_size = last_pos - cur_pos;
335 padding_count = PADDING_8ALIGNED(rec.total_size);
336 rec.total_size += padding_count;
337 if (padding_count && !fwrite(padd_bytes, padding_count, 1, dumpfile))
338 goto error;
339 if (fseek(dumpfile, cur_pos, SEEK_SET) == -1l)
340 goto error;
341 if (!fwrite_unlocked(&rec, sizeof(rec), 1, dumpfile))
342 goto error;
343 if (fseek(dumpfile, last_pos + padding_count, SEEK_SET) == -1)
344 goto error;
345
346 rc = 0;
347 error:
348 fflush_unlocked(dumpfile);
349 funlockfile(dumpfile);
350 return rc;
351 }
352
353
op_unload_native_code(op_agent_t hdl,uint64_t vma)354 int op_unload_native_code(op_agent_t hdl, uint64_t vma)
355 {
356 struct jr_code_unload rec;
357 struct timeval tv;
358 FILE * dumpfile = (FILE *) hdl;
359
360 if (!dumpfile) {
361 errno = EINVAL;
362 fprintf(stderr, "Invalid hdl argument\n");
363 return -1;
364 }
365
366 rec.id = JIT_CODE_UNLOAD;
367 rec.vma = vma;
368 rec.total_size = sizeof(rec);
369 if (gettimeofday(&tv, NULL)) {
370 fprintf(stderr, "gettimeofday failed\n");
371 return -1;
372 }
373 rec.timestamp = tv.tv_sec;
374
375 if (!fwrite(&rec, sizeof(rec), 1, dumpfile))
376 return -1;
377 fflush(dumpfile);
378 return 0;
379 }
380
op_major_version(void)381 int op_major_version(void)
382 {
383 return OP_MAJOR_VERSION;
384 }
385
op_minor_version(void)386 int op_minor_version(void)
387 {
388 return OP_MINOR_VERSION;
389 }
390