1 /* 2 * probe-event.c : perf-probe definition to kprobe_events format converter 3 * 4 * Written by Masami Hiramatsu <mhiramat@redhat.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 * 20 */ 21 22 #define _GNU_SOURCE 23 #include <sys/utsname.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <fcntl.h> 27 #include <errno.h> 28 #include <stdio.h> 29 #include <unistd.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <stdarg.h> 33 #include <limits.h> 34 35 #undef _GNU_SOURCE 36 #include "event.h" 37 #include "string.h" 38 #include "strlist.h" 39 #include "debug.h" 40 #include "cache.h" 41 #include "color.h" 42 #include "parse-events.h" /* For debugfs_path */ 43 #include "probe-event.h" 44 45 #define MAX_CMDLEN 256 46 #define MAX_PROBE_ARGS 128 47 #define PERFPROBE_GROUP "probe" 48 49 #define semantic_error(msg ...) die("Semantic error :" msg) 50 51 /* If there is no space to write, returns -E2BIG. */ 52 static int e_snprintf(char *str, size_t size, const char *format, ...) 53 __attribute__((format(printf, 3, 4))); 54 55 static int e_snprintf(char *str, size_t size, const char *format, ...) 56 { 57 int ret; 58 va_list ap; 59 va_start(ap, format); 60 ret = vsnprintf(str, size, format, ap); 61 va_end(ap); 62 if (ret >= (int)size) 63 ret = -E2BIG; 64 return ret; 65 } 66 67 void parse_line_range_desc(const char *arg, struct line_range *lr) 68 { 69 const char *ptr; 70 char *tmp; 71 /* 72 * <Syntax> 73 * SRC:SLN[+NUM|-ELN] 74 * FUNC[:SLN[+NUM|-ELN]] 75 */ 76 ptr = strchr(arg, ':'); 77 if (ptr) { 78 lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); 79 if (*tmp == '+') 80 lr->end = lr->start + (unsigned int)strtoul(tmp + 1, 81 &tmp, 0); 82 else if (*tmp == '-') 83 lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); 84 else 85 lr->end = 0; 86 pr_debug("Line range is %u to %u\n", lr->start, lr->end); 87 if (lr->end && lr->start > lr->end) 88 semantic_error("Start line must be smaller" 89 " than end line."); 90 if (*tmp != '\0') 91 semantic_error("Tailing with invalid character '%d'.", 92 *tmp); 93 tmp = strndup(arg, (ptr - arg)); 94 } else 95 tmp = strdup(arg); 96 97 if (strchr(tmp, '.')) 98 lr->file = tmp; 99 else 100 lr->function = tmp; 101 } 102 103 /* Check the name is good for event/group */ 104 static bool check_event_name(const char *name) 105 { 106 if (!isalpha(*name) && *name != '_') 107 return false; 108 while (*++name != '\0') { 109 if (!isalpha(*name) && !isdigit(*name) && *name != '_') 110 return false; 111 } 112 return true; 113 } 114 115 /* Parse probepoint definition. */ 116 static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) 117 { 118 char *ptr, *tmp; 119 char c, nc = 0; 120 /* 121 * <Syntax> 122 * perf probe [EVENT=]SRC:LN 123 * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] 124 * 125 * TODO:Group name support 126 */ 127 128 ptr = strchr(arg, '='); 129 if (ptr) { /* Event name */ 130 *ptr = '\0'; 131 tmp = ptr + 1; 132 ptr = strchr(arg, ':'); 133 if (ptr) /* Group name is not supported yet. */ 134 semantic_error("Group name is not supported yet."); 135 if (!check_event_name(arg)) 136 semantic_error("%s is bad for event name -it must " 137 "follow C symbol-naming rule.", arg); 138 pp->event = strdup(arg); 139 arg = tmp; 140 } 141 142 ptr = strpbrk(arg, ":+@%"); 143 if (ptr) { 144 nc = *ptr; 145 *ptr++ = '\0'; 146 } 147 148 /* Check arg is function or file and copy it */ 149 if (strchr(arg, '.')) /* File */ 150 pp->file = strdup(arg); 151 else /* Function */ 152 pp->function = strdup(arg); 153 DIE_IF(pp->file == NULL && pp->function == NULL); 154 155 /* Parse other options */ 156 while (ptr) { 157 arg = ptr; 158 c = nc; 159 ptr = strpbrk(arg, ":+@%"); 160 if (ptr) { 161 nc = *ptr; 162 *ptr++ = '\0'; 163 } 164 switch (c) { 165 case ':': /* Line number */ 166 pp->line = strtoul(arg, &tmp, 0); 167 if (*tmp != '\0') 168 semantic_error("There is non-digit charactor" 169 " in line number."); 170 break; 171 case '+': /* Byte offset from a symbol */ 172 pp->offset = strtoul(arg, &tmp, 0); 173 if (*tmp != '\0') 174 semantic_error("There is non-digit charactor" 175 " in offset."); 176 break; 177 case '@': /* File name */ 178 if (pp->file) 179 semantic_error("SRC@SRC is not allowed."); 180 pp->file = strdup(arg); 181 DIE_IF(pp->file == NULL); 182 if (ptr) 183 semantic_error("@SRC must be the last " 184 "option."); 185 break; 186 case '%': /* Probe places */ 187 if (strcmp(arg, "return") == 0) { 188 pp->retprobe = 1; 189 } else /* Others not supported yet */ 190 semantic_error("%%%s is not supported.", arg); 191 break; 192 default: 193 DIE_IF("Program has a bug."); 194 break; 195 } 196 } 197 198 /* Exclusion check */ 199 if (pp->line && pp->offset) 200 semantic_error("Offset can't be used with line number."); 201 202 if (!pp->line && pp->file && !pp->function) 203 semantic_error("File always requires line number."); 204 205 if (pp->offset && !pp->function) 206 semantic_error("Offset requires an entry function."); 207 208 if (pp->retprobe && !pp->function) 209 semantic_error("Return probe requires an entry function."); 210 211 if ((pp->offset || pp->line) && pp->retprobe) 212 semantic_error("Offset/Line can't be used with return probe."); 213 214 pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", 215 pp->function, pp->file, pp->line, pp->offset, pp->retprobe); 216 } 217 218 /* Parse perf-probe event definition */ 219 void parse_perf_probe_event(const char *str, struct probe_point *pp, 220 bool *need_dwarf) 221 { 222 char **argv; 223 int argc, i; 224 225 *need_dwarf = false; 226 227 argv = argv_split(str, &argc); 228 if (!argv) 229 die("argv_split failed."); 230 if (argc > MAX_PROBE_ARGS + 1) 231 semantic_error("Too many arguments"); 232 233 /* Parse probe point */ 234 parse_perf_probe_probepoint(argv[0], pp); 235 if (pp->file || pp->line) 236 *need_dwarf = true; 237 238 /* Copy arguments and ensure return probe has no C argument */ 239 pp->nr_args = argc - 1; 240 pp->args = zalloc(sizeof(char *) * pp->nr_args); 241 for (i = 0; i < pp->nr_args; i++) { 242 pp->args[i] = strdup(argv[i + 1]); 243 if (!pp->args[i]) 244 die("Failed to copy argument."); 245 if (is_c_varname(pp->args[i])) { 246 if (pp->retprobe) 247 semantic_error("You can't specify local" 248 " variable for kretprobe"); 249 *need_dwarf = true; 250 } 251 } 252 253 argv_free(argv); 254 } 255 256 /* Parse kprobe_events event into struct probe_point */ 257 void parse_trace_kprobe_event(const char *str, struct probe_point *pp) 258 { 259 char pr; 260 char *p; 261 int ret, i, argc; 262 char **argv; 263 264 pr_debug("Parsing kprobe_events: %s\n", str); 265 argv = argv_split(str, &argc); 266 if (!argv) 267 die("argv_split failed."); 268 if (argc < 2) 269 semantic_error("Too less arguments."); 270 271 /* Scan event and group name. */ 272 ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", 273 &pr, (float *)(void *)&pp->group, 274 (float *)(void *)&pp->event); 275 if (ret != 3) 276 semantic_error("Failed to parse event name: %s", argv[0]); 277 pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); 278 279 pp->retprobe = (pr == 'r'); 280 281 /* Scan function name and offset */ 282 ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, 283 &pp->offset); 284 if (ret == 1) 285 pp->offset = 0; 286 287 /* kprobe_events doesn't have this information */ 288 pp->line = 0; 289 pp->file = NULL; 290 291 pp->nr_args = argc - 2; 292 pp->args = zalloc(sizeof(char *) * pp->nr_args); 293 for (i = 0; i < pp->nr_args; i++) { 294 p = strchr(argv[i + 2], '='); 295 if (p) /* We don't need which register is assigned. */ 296 *p = '\0'; 297 pp->args[i] = strdup(argv[i + 2]); 298 if (!pp->args[i]) 299 die("Failed to copy argument."); 300 } 301 302 argv_free(argv); 303 } 304 305 /* Synthesize only probe point (not argument) */ 306 int synthesize_perf_probe_point(struct probe_point *pp) 307 { 308 char *buf; 309 char offs[64] = "", line[64] = ""; 310 int ret; 311 312 pp->probes[0] = buf = zalloc(MAX_CMDLEN); 313 pp->found = 1; 314 if (!buf) 315 die("Failed to allocate memory by zalloc."); 316 if (pp->offset) { 317 ret = e_snprintf(offs, 64, "+%d", pp->offset); 318 if (ret <= 0) 319 goto error; 320 } 321 if (pp->line) { 322 ret = e_snprintf(line, 64, ":%d", pp->line); 323 if (ret <= 0) 324 goto error; 325 } 326 327 if (pp->function) 328 ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, 329 offs, pp->retprobe ? "%return" : "", line); 330 else 331 ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); 332 if (ret <= 0) { 333 error: 334 free(pp->probes[0]); 335 pp->probes[0] = NULL; 336 pp->found = 0; 337 } 338 return ret; 339 } 340 341 int synthesize_perf_probe_event(struct probe_point *pp) 342 { 343 char *buf; 344 int i, len, ret; 345 346 len = synthesize_perf_probe_point(pp); 347 if (len < 0) 348 return 0; 349 350 buf = pp->probes[0]; 351 for (i = 0; i < pp->nr_args; i++) { 352 ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", 353 pp->args[i]); 354 if (ret <= 0) 355 goto error; 356 len += ret; 357 } 358 pp->found = 1; 359 360 return pp->found; 361 error: 362 free(pp->probes[0]); 363 pp->probes[0] = NULL; 364 365 return ret; 366 } 367 368 int synthesize_trace_kprobe_event(struct probe_point *pp) 369 { 370 char *buf; 371 int i, len, ret; 372 373 pp->probes[0] = buf = zalloc(MAX_CMDLEN); 374 if (!buf) 375 die("Failed to allocate memory by zalloc."); 376 ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); 377 if (ret <= 0) 378 goto error; 379 len = ret; 380 381 for (i = 0; i < pp->nr_args; i++) { 382 ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", 383 pp->args[i]); 384 if (ret <= 0) 385 goto error; 386 len += ret; 387 } 388 pp->found = 1; 389 390 return pp->found; 391 error: 392 free(pp->probes[0]); 393 pp->probes[0] = NULL; 394 395 return ret; 396 } 397 398 static int open_kprobe_events(int flags, int mode) 399 { 400 char buf[PATH_MAX]; 401 int ret; 402 403 ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); 404 if (ret < 0) 405 die("Failed to make kprobe_events path."); 406 407 ret = open(buf, flags, mode); 408 if (ret < 0) { 409 if (errno == ENOENT) 410 die("kprobe_events file does not exist -" 411 " please rebuild with CONFIG_KPROBE_EVENT."); 412 else 413 die("Could not open kprobe_events file: %s", 414 strerror(errno)); 415 } 416 return ret; 417 } 418 419 /* Get raw string list of current kprobe_events */ 420 static struct strlist *get_trace_kprobe_event_rawlist(int fd) 421 { 422 int ret, idx; 423 FILE *fp; 424 char buf[MAX_CMDLEN]; 425 char *p; 426 struct strlist *sl; 427 428 sl = strlist__new(true, NULL); 429 430 fp = fdopen(dup(fd), "r"); 431 while (!feof(fp)) { 432 p = fgets(buf, MAX_CMDLEN, fp); 433 if (!p) 434 break; 435 436 idx = strlen(p) - 1; 437 if (p[idx] == '\n') 438 p[idx] = '\0'; 439 ret = strlist__add(sl, buf); 440 if (ret < 0) 441 die("strlist__add failed: %s", strerror(-ret)); 442 } 443 fclose(fp); 444 445 return sl; 446 } 447 448 /* Free and zero clear probe_point */ 449 static void clear_probe_point(struct probe_point *pp) 450 { 451 int i; 452 453 if (pp->event) 454 free(pp->event); 455 if (pp->group) 456 free(pp->group); 457 if (pp->function) 458 free(pp->function); 459 if (pp->file) 460 free(pp->file); 461 for (i = 0; i < pp->nr_args; i++) 462 free(pp->args[i]); 463 if (pp->args) 464 free(pp->args); 465 for (i = 0; i < pp->found; i++) 466 free(pp->probes[i]); 467 memset(pp, 0, sizeof(*pp)); 468 } 469 470 /* Show an event */ 471 static void show_perf_probe_event(const char *event, const char *place, 472 struct probe_point *pp) 473 { 474 int i, ret; 475 char buf[128]; 476 477 ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); 478 if (ret < 0) 479 die("Failed to copy event: %s", strerror(-ret)); 480 printf(" %-40s (on %s", buf, place); 481 482 if (pp->nr_args > 0) { 483 printf(" with"); 484 for (i = 0; i < pp->nr_args; i++) 485 printf(" %s", pp->args[i]); 486 } 487 printf(")\n"); 488 } 489 490 /* List up current perf-probe events */ 491 void show_perf_probe_events(void) 492 { 493 int fd; 494 struct probe_point pp; 495 struct strlist *rawlist; 496 struct str_node *ent; 497 498 setup_pager(); 499 500 memset(&pp, 0, sizeof(pp)); 501 fd = open_kprobe_events(O_RDONLY, 0); 502 rawlist = get_trace_kprobe_event_rawlist(fd); 503 close(fd); 504 505 strlist__for_each(ent, rawlist) { 506 parse_trace_kprobe_event(ent->s, &pp); 507 /* Synthesize only event probe point */ 508 synthesize_perf_probe_point(&pp); 509 /* Show an event */ 510 show_perf_probe_event(pp.event, pp.probes[0], &pp); 511 clear_probe_point(&pp); 512 } 513 514 strlist__delete(rawlist); 515 } 516 517 /* Get current perf-probe event names */ 518 static struct strlist *get_perf_event_names(int fd, bool include_group) 519 { 520 char buf[128]; 521 struct strlist *sl, *rawlist; 522 struct str_node *ent; 523 struct probe_point pp; 524 525 memset(&pp, 0, sizeof(pp)); 526 rawlist = get_trace_kprobe_event_rawlist(fd); 527 528 sl = strlist__new(true, NULL); 529 strlist__for_each(ent, rawlist) { 530 parse_trace_kprobe_event(ent->s, &pp); 531 if (include_group) { 532 if (e_snprintf(buf, 128, "%s:%s", pp.group, 533 pp.event) < 0) 534 die("Failed to copy group:event name."); 535 strlist__add(sl, buf); 536 } else 537 strlist__add(sl, pp.event); 538 clear_probe_point(&pp); 539 } 540 541 strlist__delete(rawlist); 542 543 return sl; 544 } 545 546 static void write_trace_kprobe_event(int fd, const char *buf) 547 { 548 int ret; 549 550 pr_debug("Writing event: %s\n", buf); 551 ret = write(fd, buf, strlen(buf)); 552 if (ret <= 0) 553 die("Failed to write event: %s", strerror(errno)); 554 } 555 556 static void get_new_event_name(char *buf, size_t len, const char *base, 557 struct strlist *namelist, bool allow_suffix) 558 { 559 int i, ret; 560 561 /* Try no suffix */ 562 ret = e_snprintf(buf, len, "%s", base); 563 if (ret < 0) 564 die("snprintf() failed: %s", strerror(-ret)); 565 if (!strlist__has_entry(namelist, buf)) 566 return; 567 568 if (!allow_suffix) { 569 pr_warning("Error: event \"%s\" already exists. " 570 "(Use -f to force duplicates.)\n", base); 571 die("Can't add new event."); 572 } 573 574 /* Try to add suffix */ 575 for (i = 1; i < MAX_EVENT_INDEX; i++) { 576 ret = e_snprintf(buf, len, "%s_%d", base, i); 577 if (ret < 0) 578 die("snprintf() failed: %s", strerror(-ret)); 579 if (!strlist__has_entry(namelist, buf)) 580 break; 581 } 582 if (i == MAX_EVENT_INDEX) 583 die("Too many events are on the same function."); 584 } 585 586 void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, 587 bool force_add) 588 { 589 int i, j, fd; 590 struct probe_point *pp; 591 char buf[MAX_CMDLEN]; 592 char event[64]; 593 struct strlist *namelist; 594 bool allow_suffix; 595 596 fd = open_kprobe_events(O_RDWR, O_APPEND); 597 /* Get current event names */ 598 namelist = get_perf_event_names(fd, false); 599 600 for (j = 0; j < nr_probes; j++) { 601 pp = probes + j; 602 if (!pp->event) 603 pp->event = strdup(pp->function); 604 if (!pp->group) 605 pp->group = strdup(PERFPROBE_GROUP); 606 DIE_IF(!pp->event || !pp->group); 607 /* If force_add is true, suffix search is allowed */ 608 allow_suffix = force_add; 609 for (i = 0; i < pp->found; i++) { 610 /* Get an unused new event name */ 611 get_new_event_name(event, 64, pp->event, namelist, 612 allow_suffix); 613 snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", 614 pp->retprobe ? 'r' : 'p', 615 pp->group, event, 616 pp->probes[i]); 617 write_trace_kprobe_event(fd, buf); 618 printf("Added new event:\n"); 619 /* Get the first parameter (probe-point) */ 620 sscanf(pp->probes[i], "%s", buf); 621 show_perf_probe_event(event, buf, pp); 622 /* Add added event name to namelist */ 623 strlist__add(namelist, event); 624 /* 625 * Probes after the first probe which comes from same 626 * user input are always allowed to add suffix, because 627 * there might be several addresses corresponding to 628 * one code line. 629 */ 630 allow_suffix = true; 631 } 632 } 633 /* Show how to use the event. */ 634 printf("\nYou can now use it on all perf tools, such as:\n\n"); 635 printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); 636 637 strlist__delete(namelist); 638 close(fd); 639 } 640 641 static void __del_trace_kprobe_event(int fd, struct str_node *ent) 642 { 643 char *p; 644 char buf[128]; 645 646 /* Convert from perf-probe event to trace-kprobe event */ 647 if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) 648 die("Failed to copy event."); 649 p = strchr(buf + 2, ':'); 650 if (!p) 651 die("Internal error: %s should have ':' but not.", ent->s); 652 *p = '/'; 653 654 write_trace_kprobe_event(fd, buf); 655 printf("Remove event: %s\n", ent->s); 656 } 657 658 static void del_trace_kprobe_event(int fd, const char *group, 659 const char *event, struct strlist *namelist) 660 { 661 char buf[128]; 662 struct str_node *ent, *n; 663 int found = 0; 664 665 if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) 666 die("Failed to copy event."); 667 668 if (strpbrk(buf, "*?")) { /* Glob-exp */ 669 strlist__for_each_safe(ent, n, namelist) 670 if (strglobmatch(ent->s, buf)) { 671 found++; 672 __del_trace_kprobe_event(fd, ent); 673 strlist__remove(namelist, ent); 674 } 675 } else { 676 ent = strlist__find(namelist, buf); 677 if (ent) { 678 found++; 679 __del_trace_kprobe_event(fd, ent); 680 strlist__remove(namelist, ent); 681 } 682 } 683 if (found == 0) 684 pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); 685 } 686 687 void del_trace_kprobe_events(struct strlist *dellist) 688 { 689 int fd; 690 const char *group, *event; 691 char *p, *str; 692 struct str_node *ent; 693 struct strlist *namelist; 694 695 fd = open_kprobe_events(O_RDWR, O_APPEND); 696 /* Get current event names */ 697 namelist = get_perf_event_names(fd, true); 698 699 strlist__for_each(ent, dellist) { 700 str = strdup(ent->s); 701 if (!str) 702 die("Failed to copy event."); 703 pr_debug("Parsing: %s\n", str); 704 p = strchr(str, ':'); 705 if (p) { 706 group = str; 707 *p = '\0'; 708 event = p + 1; 709 } else { 710 group = "*"; 711 event = str; 712 } 713 pr_debug("Group: %s, Event: %s\n", group, event); 714 del_trace_kprobe_event(fd, group, event, namelist); 715 free(str); 716 } 717 strlist__delete(namelist); 718 close(fd); 719 } 720 721 #define LINEBUF_SIZE 256 722 723 static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) 724 { 725 char buf[LINEBUF_SIZE]; 726 const char *color = PERF_COLOR_BLUE; 727 728 if (fgets(buf, LINEBUF_SIZE, fp) == NULL) 729 goto error; 730 if (!skip) { 731 if (show_num) 732 fprintf(stdout, "%7u %s", l, buf); 733 else 734 color_fprintf(stdout, color, " %s", buf); 735 } 736 737 while (strlen(buf) == LINEBUF_SIZE - 1 && 738 buf[LINEBUF_SIZE - 2] != '\n') { 739 if (fgets(buf, LINEBUF_SIZE, fp) == NULL) 740 goto error; 741 if (!skip) { 742 if (show_num) 743 fprintf(stdout, "%s", buf); 744 else 745 color_fprintf(stdout, color, "%s", buf); 746 } 747 } 748 return; 749 error: 750 if (feof(fp)) 751 die("Source file is shorter than expected."); 752 else 753 die("File read error: %s", strerror(errno)); 754 } 755 756 void show_line_range(struct line_range *lr) 757 { 758 unsigned int l = 1; 759 struct line_node *ln; 760 FILE *fp; 761 762 setup_pager(); 763 764 if (lr->function) 765 fprintf(stdout, "<%s:%d>\n", lr->function, 766 lr->start - lr->offset); 767 else 768 fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); 769 770 fp = fopen(lr->path, "r"); 771 if (fp == NULL) 772 die("Failed to open %s: %s", lr->path, strerror(errno)); 773 /* Skip to starting line number */ 774 while (l < lr->start) 775 show_one_line(fp, l++, true, false); 776 777 list_for_each_entry(ln, &lr->line_list, list) { 778 while (ln->line > l) 779 show_one_line(fp, (l++) - lr->offset, false, false); 780 show_one_line(fp, (l++) - lr->offset, false, true); 781 } 782 fclose(fp); 783 } 784