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.
70static char * _bfd_target_name;
71static int _bfd_arch;
72static unsigned int _bfd_mach;
73
74// Define BFD-related global variables.
75static 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
124op_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
196int 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
221int 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
278int 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;
347error:
348	fflush_unlocked(dumpfile);
349	funlockfile(dumpfile);
350	return rc;
351}
352
353
354int 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
381int op_major_version(void)
382{
383	return OP_MAJOR_VERSION;
384}
385
386int op_minor_version(void)
387{
388	return OP_MINOR_VERSION;
389}
390