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 (at) 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. 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 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 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 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 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 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 381 int op_major_version(void) 382 { 383 return OP_MAJOR_VERSION; 384 } 385 386 int op_minor_version(void) 387 { 388 return OP_MINOR_VERSION; 389 } 390