• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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