Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tools: Add guest_code support

A common case for KVM test programs is that the test program acts as the
hypervisor, creating, running and destroying the virtual machine, and
providing the guest object code from its own object code. In this case,
the VM is not running an OS, but only the functions loaded into it by the
hypervisor test program, and conveniently, loaded at the same virtual
addresses.

Normally to resolve addresses, MMAP events are needed to map addresses
back to the object code and debug symbols for that object code.

Currently, there is no way to get such mapping information from guests
but, in the scenario described above, the guest has the same mappings
as the hypervisor, so support for that scenario can be achieved.

To support that, copy the host thread's maps to the guest thread's maps.
Note, we do not discover the guest until we encounter a guest event,
which works well because it is not until then that we know that the host
thread's maps have been set up.

Typically the main function for the guest object code is called
"guest_code", hence the name chosen for this feature. Note, that is just a
convention, the function could be named anything, and the tools do not
care.

This is primarily aimed at supporting Intel PT, or similar, where trace
data can be recorded for a guest. Refer to the final patch in this series
"perf intel-pt: Add guest_code support" for an example.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: kvm@vger.kernel.org
Link: https://lore.kernel.org/r/20220517131011.6117-4-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Adrian Hunter and committed by
Arnaldo Carvalho de Melo
096fc361 c98e064d

+103 -3
+5 -2
tools/perf/util/event.c
··· 683 683 int machine__resolve(struct machine *machine, struct addr_location *al, 684 684 struct perf_sample *sample) 685 685 { 686 - struct thread *thread = machine__findnew_thread(machine, sample->pid, 687 - sample->tid); 686 + struct thread *thread; 688 687 688 + if (symbol_conf.guest_code && !machine__is_host(machine)) 689 + thread = machine__findnew_guest_code(machine, sample->pid); 690 + else 691 + thread = machine__findnew_thread(machine, sample->pid, sample->tid); 689 692 if (thread == NULL) 690 693 return -1; 691 694
+87
tools/perf/util/machine.c
··· 392 392 return machine; 393 393 } 394 394 395 + /* 396 + * A common case for KVM test programs is that the test program acts as the 397 + * hypervisor, creating, running and destroying the virtual machine, and 398 + * providing the guest object code from its own object code. In this case, 399 + * the VM is not running an OS, but only the functions loaded into it by the 400 + * hypervisor test program, and conveniently, loaded at the same virtual 401 + * addresses. 402 + * 403 + * Normally to resolve addresses, MMAP events are needed to map addresses 404 + * back to the object code and debug symbols for that object code. 405 + * 406 + * Currently, there is no way to get such mapping information from guests 407 + * but, in the scenario described above, the guest has the same mappings 408 + * as the hypervisor, so support for that scenario can be achieved. 409 + * 410 + * To support that, copy the host thread's maps to the guest thread's maps. 411 + * Note, we do not discover the guest until we encounter a guest event, 412 + * which works well because it is not until then that we know that the host 413 + * thread's maps have been set up. 414 + * 415 + * This function returns the guest thread. Apart from keeping the data 416 + * structures sane, using a thread belonging to the guest machine, instead 417 + * of the host thread, allows it to have its own comm (refer 418 + * thread__set_guest_comm()). 419 + */ 420 + static struct thread *findnew_guest_code(struct machine *machine, 421 + struct machine *host_machine, 422 + pid_t pid) 423 + { 424 + struct thread *host_thread; 425 + struct thread *thread; 426 + int err; 427 + 428 + if (!machine) 429 + return NULL; 430 + 431 + thread = machine__findnew_thread(machine, -1, pid); 432 + if (!thread) 433 + return NULL; 434 + 435 + /* Assume maps are set up if there are any */ 436 + if (thread->maps->nr_maps) 437 + return thread; 438 + 439 + host_thread = machine__find_thread(host_machine, -1, pid); 440 + if (!host_thread) 441 + goto out_err; 442 + 443 + thread__set_guest_comm(thread, pid); 444 + 445 + /* 446 + * Guest code can be found in hypervisor process at the same address 447 + * so copy host maps. 448 + */ 449 + err = maps__clone(thread, host_thread->maps); 450 + thread__put(host_thread); 451 + if (err) 452 + goto out_err; 453 + 454 + return thread; 455 + 456 + out_err: 457 + thread__zput(thread); 458 + return NULL; 459 + } 460 + 461 + struct thread *machines__findnew_guest_code(struct machines *machines, pid_t pid) 462 + { 463 + struct machine *host_machine = machines__find(machines, HOST_KERNEL_ID); 464 + struct machine *machine = machines__findnew(machines, pid); 465 + 466 + return findnew_guest_code(machine, host_machine, pid); 467 + } 468 + 469 + struct thread *machine__findnew_guest_code(struct machine *machine, pid_t pid) 470 + { 471 + struct machines *machines = machine->machines; 472 + struct machine *host_machine; 473 + 474 + if (!machines) 475 + return NULL; 476 + 477 + host_machine = machines__find(machines, HOST_KERNEL_ID); 478 + 479 + return findnew_guest_code(machine, host_machine, pid); 480 + } 481 + 395 482 void machines__process_guests(struct machines *machines, 396 483 machine__process_t process, void *data) 397 484 {
+2
tools/perf/util/machine.h
··· 167 167 struct machine *machines__find(struct machines *machines, pid_t pid); 168 168 struct machine *machines__findnew(struct machines *machines, pid_t pid); 169 169 struct machine *machines__find_guest(struct machines *machines, pid_t pid); 170 + struct thread *machines__findnew_guest_code(struct machines *machines, pid_t pid); 171 + struct thread *machine__findnew_guest_code(struct machine *machine, pid_t pid); 170 172 171 173 void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size); 172 174 void machines__set_comm_exec(struct machines *machines, bool comm_exec);
+7
tools/perf/util/session.c
··· 1426 1426 else 1427 1427 pid = sample->pid; 1428 1428 1429 + /* 1430 + * Guest code machine is created as needed and does not use 1431 + * DEFAULT_GUEST_KERNEL_ID. 1432 + */ 1433 + if (symbol_conf.guest_code) 1434 + return machines__findnew(machines, pid); 1435 + 1429 1436 return machines__find_guest(machines, pid); 1430 1437 } 1431 1438
+2 -1
tools/perf/util/symbol_conf.h
··· 43 43 report_individual_block, 44 44 inline_name, 45 45 disable_add2line_warn, 46 - buildid_mmap2; 46 + buildid_mmap2, 47 + guest_code; 47 48 const char *vmlinux_name, 48 49 *kallsyms_name, 49 50 *source_prefix,