1 #include <linux/types.h> 2 #include "event.h" 3 #include "debug.h" 4 #include "machine.h" 5 #include "sort.h" 6 #include "string.h" 7 #include "strlist.h" 8 #include "thread.h" 9 #include "thread_map.h" 10 11 static const char *perf_event__names[] = { 12 [0] = "TOTAL", 13 [PERF_RECORD_MMAP] = "MMAP", 14 [PERF_RECORD_LOST] = "LOST", 15 [PERF_RECORD_COMM] = "COMM", 16 [PERF_RECORD_EXIT] = "EXIT", 17 [PERF_RECORD_THROTTLE] = "THROTTLE", 18 [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", 19 [PERF_RECORD_FORK] = "FORK", 20 [PERF_RECORD_READ] = "READ", 21 [PERF_RECORD_SAMPLE] = "SAMPLE", 22 [PERF_RECORD_HEADER_ATTR] = "ATTR", 23 [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", 24 [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", 25 [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", 26 [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", 27 }; 28 29 const char *perf_event__name(unsigned int id) 30 { 31 if (id >= ARRAY_SIZE(perf_event__names)) 32 return "INVALID"; 33 if (!perf_event__names[id]) 34 return "UNKNOWN"; 35 return perf_event__names[id]; 36 } 37 38 static struct perf_sample synth_sample = { 39 .pid = -1, 40 .tid = -1, 41 .time = -1, 42 .stream_id = -1, 43 .cpu = -1, 44 .period = 1, 45 }; 46 47 static pid_t perf_event__get_comm_tgid(pid_t pid, char *comm, size_t len) 48 { 49 char filename[PATH_MAX]; 50 char bf[BUFSIZ]; 51 FILE *fp; 52 size_t size = 0; 53 pid_t tgid = -1; 54 55 snprintf(filename, sizeof(filename), "/proc/%d/status", pid); 56 57 fp = fopen(filename, "r"); 58 if (fp == NULL) { 59 pr_debug("couldn't open %s\n", filename); 60 return 0; 61 } 62 63 while (!comm[0] || (tgid < 0)) { 64 if (fgets(bf, sizeof(bf), fp) == NULL) { 65 pr_warning("couldn't get COMM and pgid, malformed %s\n", 66 filename); 67 break; 68 } 69 70 if (memcmp(bf, "Name:", 5) == 0) { 71 char *name = bf + 5; 72 while (*name && isspace(*name)) 73 ++name; 74 size = strlen(name) - 1; 75 if (size >= len) 76 size = len - 1; 77 memcpy(comm, name, size); 78 comm[size] = '\0'; 79 80 } else if (memcmp(bf, "Tgid:", 5) == 0) { 81 char *tgids = bf + 5; 82 while (*tgids && isspace(*tgids)) 83 ++tgids; 84 tgid = atoi(tgids); 85 } 86 } 87 88 fclose(fp); 89 90 return tgid; 91 } 92 93 static pid_t perf_event__synthesize_comm(struct perf_tool *tool, 94 union perf_event *event, pid_t pid, 95 int full, 96 perf_event__handler_t process, 97 struct machine *machine) 98 { 99 char filename[PATH_MAX]; 100 size_t size; 101 DIR *tasks; 102 struct dirent dirent, *next; 103 pid_t tgid; 104 105 memset(&event->comm, 0, sizeof(event->comm)); 106 107 tgid = perf_event__get_comm_tgid(pid, event->comm.comm, 108 sizeof(event->comm.comm)); 109 if (tgid < 0) 110 goto out; 111 112 event->comm.pid = tgid; 113 event->comm.header.type = PERF_RECORD_COMM; 114 115 size = strlen(event->comm.comm) + 1; 116 size = PERF_ALIGN(size, sizeof(u64)); 117 memset(event->comm.comm + size, 0, machine->id_hdr_size); 118 event->comm.header.size = (sizeof(event->comm) - 119 (sizeof(event->comm.comm) - size) + 120 machine->id_hdr_size); 121 if (!full) { 122 event->comm.tid = pid; 123 124 if (process(tool, event, &synth_sample, machine) != 0) 125 return -1; 126 127 goto out; 128 } 129 130 snprintf(filename, sizeof(filename), "/proc/%d/task", pid); 131 132 tasks = opendir(filename); 133 if (tasks == NULL) { 134 pr_debug("couldn't open %s\n", filename); 135 return 0; 136 } 137 138 while (!readdir_r(tasks, &dirent, &next) && next) { 139 char *end; 140 pid = strtol(dirent.d_name, &end, 10); 141 if (*end) 142 continue; 143 144 /* already have tgid; jut want to update the comm */ 145 (void) perf_event__get_comm_tgid(pid, event->comm.comm, 146 sizeof(event->comm.comm)); 147 148 size = strlen(event->comm.comm) + 1; 149 size = PERF_ALIGN(size, sizeof(u64)); 150 memset(event->comm.comm + size, 0, machine->id_hdr_size); 151 event->comm.header.size = (sizeof(event->comm) - 152 (sizeof(event->comm.comm) - size) + 153 machine->id_hdr_size); 154 155 event->comm.tid = pid; 156 157 if (process(tool, event, &synth_sample, machine) != 0) { 158 tgid = -1; 159 break; 160 } 161 } 162 163 closedir(tasks); 164 out: 165 return tgid; 166 } 167 168 static int perf_event__synthesize_mmap_events(struct perf_tool *tool, 169 union perf_event *event, 170 pid_t pid, pid_t tgid, 171 perf_event__handler_t process, 172 struct machine *machine) 173 { 174 char filename[PATH_MAX]; 175 FILE *fp; 176 int rc = 0; 177 178 snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); 179 180 fp = fopen(filename, "r"); 181 if (fp == NULL) { 182 /* 183 * We raced with a task exiting - just return: 184 */ 185 pr_debug("couldn't open %s\n", filename); 186 return -1; 187 } 188 189 event->header.type = PERF_RECORD_MMAP; 190 /* 191 * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c 192 */ 193 event->header.misc = PERF_RECORD_MISC_USER; 194 195 while (1) { 196 char bf[BUFSIZ]; 197 char prot[5]; 198 char execname[PATH_MAX]; 199 char anonstr[] = "//anon"; 200 size_t size; 201 202 if (fgets(bf, sizeof(bf), fp) == NULL) 203 break; 204 205 /* ensure null termination since stack will be reused. */ 206 strcpy(execname, ""); 207 208 /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ 209 sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", 210 &event->mmap.start, &event->mmap.len, prot, 211 &event->mmap.pgoff, execname); 212 213 if (prot[2] != 'x') 214 continue; 215 216 if (!strcmp(execname, "")) 217 strcpy(execname, anonstr); 218 219 size = strlen(execname) + 1; 220 memcpy(event->mmap.filename, execname, size); 221 size = PERF_ALIGN(size, sizeof(u64)); 222 event->mmap.len -= event->mmap.start; 223 event->mmap.header.size = (sizeof(event->mmap) - 224 (sizeof(event->mmap.filename) - size)); 225 memset(event->mmap.filename + size, 0, machine->id_hdr_size); 226 event->mmap.header.size += machine->id_hdr_size; 227 event->mmap.pid = tgid; 228 event->mmap.tid = pid; 229 230 if (process(tool, event, &synth_sample, machine) != 0) { 231 rc = -1; 232 break; 233 } 234 } 235 236 fclose(fp); 237 return rc; 238 } 239 240 int perf_event__synthesize_modules(struct perf_tool *tool, 241 perf_event__handler_t process, 242 struct machine *machine) 243 { 244 int rc = 0; 245 struct rb_node *nd; 246 struct map_groups *kmaps = &machine->kmaps; 247 union perf_event *event = zalloc((sizeof(event->mmap) + 248 machine->id_hdr_size)); 249 if (event == NULL) { 250 pr_debug("Not enough memory synthesizing mmap event " 251 "for kernel modules\n"); 252 return -1; 253 } 254 255 event->header.type = PERF_RECORD_MMAP; 256 257 /* 258 * kernel uses 0 for user space maps, see kernel/perf_event.c 259 * __perf_event_mmap 260 */ 261 if (machine__is_host(machine)) 262 event->header.misc = PERF_RECORD_MISC_KERNEL; 263 else 264 event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; 265 266 for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); 267 nd; nd = rb_next(nd)) { 268 size_t size; 269 struct map *pos = rb_entry(nd, struct map, rb_node); 270 271 if (pos->dso->kernel) 272 continue; 273 274 size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); 275 event->mmap.header.type = PERF_RECORD_MMAP; 276 event->mmap.header.size = (sizeof(event->mmap) - 277 (sizeof(event->mmap.filename) - size)); 278 memset(event->mmap.filename + size, 0, machine->id_hdr_size); 279 event->mmap.header.size += machine->id_hdr_size; 280 event->mmap.start = pos->start; 281 event->mmap.len = pos->end - pos->start; 282 event->mmap.pid = machine->pid; 283 284 memcpy(event->mmap.filename, pos->dso->long_name, 285 pos->dso->long_name_len + 1); 286 if (process(tool, event, &synth_sample, machine) != 0) { 287 rc = -1; 288 break; 289 } 290 } 291 292 free(event); 293 return rc; 294 } 295 296 static int __event__synthesize_thread(union perf_event *comm_event, 297 union perf_event *mmap_event, 298 pid_t pid, int full, 299 perf_event__handler_t process, 300 struct perf_tool *tool, 301 struct machine *machine) 302 { 303 pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, full, 304 process, machine); 305 if (tgid == -1) 306 return -1; 307 return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid, 308 process, machine); 309 } 310 311 int perf_event__synthesize_thread_map(struct perf_tool *tool, 312 struct thread_map *threads, 313 perf_event__handler_t process, 314 struct machine *machine) 315 { 316 union perf_event *comm_event, *mmap_event; 317 int err = -1, thread, j; 318 319 comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); 320 if (comm_event == NULL) 321 goto out; 322 323 mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size); 324 if (mmap_event == NULL) 325 goto out_free_comm; 326 327 err = 0; 328 for (thread = 0; thread < threads->nr; ++thread) { 329 if (__event__synthesize_thread(comm_event, mmap_event, 330 threads->map[thread], 0, 331 process, tool, machine)) { 332 err = -1; 333 break; 334 } 335 336 /* 337 * comm.pid is set to thread group id by 338 * perf_event__synthesize_comm 339 */ 340 if ((int) comm_event->comm.pid != threads->map[thread]) { 341 bool need_leader = true; 342 343 /* is thread group leader in thread_map? */ 344 for (j = 0; j < threads->nr; ++j) { 345 if ((int) comm_event->comm.pid == threads->map[j]) { 346 need_leader = false; 347 break; 348 } 349 } 350 351 /* if not, generate events for it */ 352 if (need_leader && 353 __event__synthesize_thread(comm_event, 354 mmap_event, 355 comm_event->comm.pid, 0, 356 process, tool, machine)) { 357 err = -1; 358 break; 359 } 360 } 361 } 362 free(mmap_event); 363 out_free_comm: 364 free(comm_event); 365 out: 366 return err; 367 } 368 369 int perf_event__synthesize_threads(struct perf_tool *tool, 370 perf_event__handler_t process, 371 struct machine *machine) 372 { 373 DIR *proc; 374 struct dirent dirent, *next; 375 union perf_event *comm_event, *mmap_event; 376 int err = -1; 377 378 comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size); 379 if (comm_event == NULL) 380 goto out; 381 382 mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size); 383 if (mmap_event == NULL) 384 goto out_free_comm; 385 386 proc = opendir("/proc"); 387 if (proc == NULL) 388 goto out_free_mmap; 389 390 while (!readdir_r(proc, &dirent, &next) && next) { 391 char *end; 392 pid_t pid = strtol(dirent.d_name, &end, 10); 393 394 if (*end) /* only interested in proper numerical dirents */ 395 continue; 396 /* 397 * We may race with exiting thread, so don't stop just because 398 * one thread couldn't be synthesized. 399 */ 400 __event__synthesize_thread(comm_event, mmap_event, pid, 1, 401 process, tool, machine); 402 } 403 404 err = 0; 405 closedir(proc); 406 out_free_mmap: 407 free(mmap_event); 408 out_free_comm: 409 free(comm_event); 410 out: 411 return err; 412 } 413 414 struct process_symbol_args { 415 const char *name; 416 u64 start; 417 }; 418 419 static int find_symbol_cb(void *arg, const char *name, char type, 420 u64 start) 421 { 422 struct process_symbol_args *args = arg; 423 424 /* 425 * Must be a function or at least an alias, as in PARISC64, where "_text" is 426 * an 'A' to the same address as "_stext". 427 */ 428 if (!(symbol_type__is_a(type, MAP__FUNCTION) || 429 type == 'A') || strcmp(name, args->name)) 430 return 0; 431 432 args->start = start; 433 return 1; 434 } 435 436 int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, 437 perf_event__handler_t process, 438 struct machine *machine, 439 const char *symbol_name) 440 { 441 size_t size; 442 const char *filename, *mmap_name; 443 char path[PATH_MAX]; 444 char name_buff[PATH_MAX]; 445 struct map *map; 446 int err; 447 /* 448 * We should get this from /sys/kernel/sections/.text, but till that is 449 * available use this, and after it is use this as a fallback for older 450 * kernels. 451 */ 452 struct process_symbol_args args = { .name = symbol_name, }; 453 union perf_event *event = zalloc((sizeof(event->mmap) + 454 machine->id_hdr_size)); 455 if (event == NULL) { 456 pr_debug("Not enough memory synthesizing mmap event " 457 "for kernel modules\n"); 458 return -1; 459 } 460 461 mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); 462 if (machine__is_host(machine)) { 463 /* 464 * kernel uses PERF_RECORD_MISC_USER for user space maps, 465 * see kernel/perf_event.c __perf_event_mmap 466 */ 467 event->header.misc = PERF_RECORD_MISC_KERNEL; 468 filename = "/proc/kallsyms"; 469 } else { 470 event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; 471 if (machine__is_default_guest(machine)) 472 filename = (char *) symbol_conf.default_guest_kallsyms; 473 else { 474 sprintf(path, "%s/proc/kallsyms", machine->root_dir); 475 filename = path; 476 } 477 } 478 479 if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) { 480 free(event); 481 return -ENOENT; 482 } 483 484 map = machine->vmlinux_maps[MAP__FUNCTION]; 485 size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), 486 "%s%s", mmap_name, symbol_name) + 1; 487 size = PERF_ALIGN(size, sizeof(u64)); 488 event->mmap.header.type = PERF_RECORD_MMAP; 489 event->mmap.header.size = (sizeof(event->mmap) - 490 (sizeof(event->mmap.filename) - size) + machine->id_hdr_size); 491 event->mmap.pgoff = args.start; 492 event->mmap.start = map->start; 493 event->mmap.len = map->end - event->mmap.start; 494 event->mmap.pid = machine->pid; 495 496 err = process(tool, event, &synth_sample, machine); 497 free(event); 498 499 return err; 500 } 501 502 size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) 503 { 504 return fprintf(fp, ": %s:%d\n", event->comm.comm, event->comm.tid); 505 } 506 507 int perf_event__process_comm(struct perf_tool *tool __maybe_unused, 508 union perf_event *event, 509 struct perf_sample *sample __maybe_unused, 510 struct machine *machine) 511 { 512 return machine__process_comm_event(machine, event); 513 } 514 515 int perf_event__process_lost(struct perf_tool *tool __maybe_unused, 516 union perf_event *event, 517 struct perf_sample *sample __maybe_unused, 518 struct machine *machine) 519 { 520 return machine__process_lost_event(machine, event); 521 } 522 523 size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) 524 { 525 return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", 526 event->mmap.pid, event->mmap.tid, event->mmap.start, 527 event->mmap.len, event->mmap.pgoff, event->mmap.filename); 528 } 529 530 int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, 531 union perf_event *event, 532 struct perf_sample *sample __maybe_unused, 533 struct machine *machine) 534 { 535 return machine__process_mmap_event(machine, event); 536 } 537 538 size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) 539 { 540 return fprintf(fp, "(%d:%d):(%d:%d)\n", 541 event->fork.pid, event->fork.tid, 542 event->fork.ppid, event->fork.ptid); 543 } 544 545 int perf_event__process_fork(struct perf_tool *tool __maybe_unused, 546 union perf_event *event, 547 struct perf_sample *sample __maybe_unused, 548 struct machine *machine) 549 { 550 return machine__process_fork_event(machine, event); 551 } 552 553 int perf_event__process_exit(struct perf_tool *tool __maybe_unused, 554 union perf_event *event, 555 struct perf_sample *sample __maybe_unused, 556 struct machine *machine) 557 { 558 return machine__process_exit_event(machine, event); 559 } 560 561 size_t perf_event__fprintf(union perf_event *event, FILE *fp) 562 { 563 size_t ret = fprintf(fp, "PERF_RECORD_%s", 564 perf_event__name(event->header.type)); 565 566 switch (event->header.type) { 567 case PERF_RECORD_COMM: 568 ret += perf_event__fprintf_comm(event, fp); 569 break; 570 case PERF_RECORD_FORK: 571 case PERF_RECORD_EXIT: 572 ret += perf_event__fprintf_task(event, fp); 573 break; 574 case PERF_RECORD_MMAP: 575 ret += perf_event__fprintf_mmap(event, fp); 576 break; 577 default: 578 ret += fprintf(fp, "\n"); 579 } 580 581 return ret; 582 } 583 584 int perf_event__process(struct perf_tool *tool __maybe_unused, 585 union perf_event *event, 586 struct perf_sample *sample __maybe_unused, 587 struct machine *machine) 588 { 589 return machine__process_event(machine, event); 590 } 591 592 void thread__find_addr_map(struct thread *self, 593 struct machine *machine, u8 cpumode, 594 enum map_type type, u64 addr, 595 struct addr_location *al) 596 { 597 struct map_groups *mg = &self->mg; 598 bool load_map = false; 599 600 al->thread = self; 601 al->addr = addr; 602 al->cpumode = cpumode; 603 al->filtered = false; 604 605 if (machine == NULL) { 606 al->map = NULL; 607 return; 608 } 609 610 if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { 611 al->level = 'k'; 612 mg = &machine->kmaps; 613 load_map = true; 614 } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { 615 al->level = '.'; 616 } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { 617 al->level = 'g'; 618 mg = &machine->kmaps; 619 load_map = true; 620 } else { 621 /* 622 * 'u' means guest os user space. 623 * TODO: We don't support guest user space. Might support late. 624 */ 625 if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) 626 al->level = 'u'; 627 else 628 al->level = 'H'; 629 al->map = NULL; 630 631 if ((cpumode == PERF_RECORD_MISC_GUEST_USER || 632 cpumode == PERF_RECORD_MISC_GUEST_KERNEL) && 633 !perf_guest) 634 al->filtered = true; 635 if ((cpumode == PERF_RECORD_MISC_USER || 636 cpumode == PERF_RECORD_MISC_KERNEL) && 637 !perf_host) 638 al->filtered = true; 639 640 return; 641 } 642 try_again: 643 al->map = map_groups__find(mg, type, al->addr); 644 if (al->map == NULL) { 645 /* 646 * If this is outside of all known maps, and is a negative 647 * address, try to look it up in the kernel dso, as it might be 648 * a vsyscall or vdso (which executes in user-mode). 649 * 650 * XXX This is nasty, we should have a symbol list in the 651 * "[vdso]" dso, but for now lets use the old trick of looking 652 * in the whole kernel symbol list. 653 */ 654 if ((long long)al->addr < 0 && 655 cpumode == PERF_RECORD_MISC_USER && 656 machine && mg != &machine->kmaps) { 657 mg = &machine->kmaps; 658 goto try_again; 659 } 660 } else { 661 /* 662 * Kernel maps might be changed when loading symbols so loading 663 * must be done prior to using kernel maps. 664 */ 665 if (load_map) 666 map__load(al->map, machine->symbol_filter); 667 al->addr = al->map->map_ip(al->map, al->addr); 668 } 669 } 670 671 void thread__find_addr_location(struct thread *thread, struct machine *machine, 672 u8 cpumode, enum map_type type, u64 addr, 673 struct addr_location *al) 674 { 675 thread__find_addr_map(thread, machine, cpumode, type, addr, al); 676 if (al->map != NULL) 677 al->sym = map__find_symbol(al->map, al->addr, 678 machine->symbol_filter); 679 else 680 al->sym = NULL; 681 } 682 683 int perf_event__preprocess_sample(const union perf_event *event, 684 struct machine *machine, 685 struct addr_location *al, 686 struct perf_sample *sample) 687 { 688 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 689 struct thread *thread = machine__findnew_thread(machine, sample->pid, 690 sample->pid); 691 692 if (thread == NULL) 693 return -1; 694 695 if (symbol_conf.comm_list && 696 !strlist__has_entry(symbol_conf.comm_list, thread->comm)) 697 goto out_filtered; 698 699 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); 700 /* 701 * Have we already created the kernel maps for this machine? 702 * 703 * This should have happened earlier, when we processed the kernel MMAP 704 * events, but for older perf.data files there was no such thing, so do 705 * it now. 706 */ 707 if (cpumode == PERF_RECORD_MISC_KERNEL && 708 machine->vmlinux_maps[MAP__FUNCTION] == NULL) 709 machine__create_kernel_maps(machine); 710 711 thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, 712 sample->ip, al); 713 dump_printf(" ...... dso: %s\n", 714 al->map ? al->map->dso->long_name : 715 al->level == 'H' ? "[hypervisor]" : "<not found>"); 716 al->sym = NULL; 717 al->cpu = sample->cpu; 718 719 if (al->map) { 720 struct dso *dso = al->map->dso; 721 722 if (symbol_conf.dso_list && 723 (!dso || !(strlist__has_entry(symbol_conf.dso_list, 724 dso->short_name) || 725 (dso->short_name != dso->long_name && 726 strlist__has_entry(symbol_conf.dso_list, 727 dso->long_name))))) 728 goto out_filtered; 729 730 al->sym = map__find_symbol(al->map, al->addr, 731 machine->symbol_filter); 732 } 733 734 if (symbol_conf.sym_list && 735 (!al->sym || !strlist__has_entry(symbol_conf.sym_list, 736 al->sym->name))) 737 goto out_filtered; 738 739 return 0; 740 741 out_filtered: 742 al->filtered = true; 743 return 0; 744 } 745