234 lines
		
	
	
	
		
			4.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			234 lines
		
	
	
	
		
			4.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								#include <unistd.h>
							 | 
						||
| 
								 | 
							
								#include <sys/syscall.h>
							 | 
						||
| 
								 | 
							
								#include <sys/types.h>
							 | 
						||
| 
								 | 
							
								#include <sys/mman.h>
							 | 
						||
| 
								 | 
							
								#include <pthread.h>
							 | 
						||
| 
								 | 
							
								#include <stdlib.h>
							 | 
						||
| 
								 | 
							
								#include <stdio.h>
							 | 
						||
| 
								 | 
							
								#include "debug.h"
							 | 
						||
| 
								 | 
							
								#include "tests.h"
							 | 
						||
| 
								 | 
							
								#include "machine.h"
							 | 
						||
| 
								 | 
							
								#include "thread_map.h"
							 | 
						||
| 
								 | 
							
								#include "symbol.h"
							 | 
						||
| 
								 | 
							
								#include "thread.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define THREADS 4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int go_away;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								struct thread_data {
							 | 
						||
| 
								 | 
							
									pthread_t	pt;
							 | 
						||
| 
								 | 
							
									pid_t		tid;
							 | 
						||
| 
								 | 
							
									void		*map;
							 | 
						||
| 
								 | 
							
									int		ready[2];
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static struct thread_data threads[THREADS];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int thread_init(struct thread_data *td)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									void *map;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									map = mmap(NULL, page_size,
							 | 
						||
| 
								 | 
							
										   PROT_READ|PROT_WRITE|PROT_EXEC,
							 | 
						||
| 
								 | 
							
										   MAP_SHARED|MAP_ANONYMOUS, -1, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (map == MAP_FAILED) {
							 | 
						||
| 
								 | 
							
										perror("mmap failed");
							 | 
						||
| 
								 | 
							
										return -1;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									td->map = map;
							 | 
						||
| 
								 | 
							
									td->tid = syscall(SYS_gettid);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									pr_debug("tid = %d, map = %p\n", td->tid, map);
							 | 
						||
| 
								 | 
							
									return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void *thread_fn(void *arg)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct thread_data *td = arg;
							 | 
						||
| 
								 | 
							
									ssize_t ret;
							 | 
						||
| 
								 | 
							
									int go;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (thread_init(td))
							 | 
						||
| 
								 | 
							
										return NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* Signal thread_create thread is initialized. */
							 | 
						||
| 
								 | 
							
									ret = write(td->ready[1], &go, sizeof(int));
							 | 
						||
| 
								 | 
							
									if (ret != sizeof(int)) {
							 | 
						||
| 
								 | 
							
										pr_err("failed to notify\n");
							 | 
						||
| 
								 | 
							
										return NULL;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									while (!go_away) {
							 | 
						||
| 
								 | 
							
										/* Waiting for main thread to kill us. */
							 | 
						||
| 
								 | 
							
										usleep(100);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									munmap(td->map, page_size);
							 | 
						||
| 
								 | 
							
									return NULL;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int thread_create(int i)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct thread_data *td = &threads[i];
							 | 
						||
| 
								 | 
							
									int err, go;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (pipe(td->ready))
							 | 
						||
| 
								 | 
							
										return -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = pthread_create(&td->pt, NULL, thread_fn, td);
							 | 
						||
| 
								 | 
							
									if (!err) {
							 | 
						||
| 
								 | 
							
										/* Wait for thread initialization. */
							 | 
						||
| 
								 | 
							
										ssize_t ret = read(td->ready[0], &go, sizeof(int));
							 | 
						||
| 
								 | 
							
										err = ret != sizeof(int);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									close(td->ready[0]);
							 | 
						||
| 
								 | 
							
									close(td->ready[1]);
							 | 
						||
| 
								 | 
							
									return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int threads_create(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct thread_data *td0 = &threads[0];
							 | 
						||
| 
								 | 
							
									int i, err = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									go_away = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* 0 is main thread */
							 | 
						||
| 
								 | 
							
									if (thread_init(td0))
							 | 
						||
| 
								 | 
							
										return -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for (i = 1; !err && i < THREADS; i++)
							 | 
						||
| 
								 | 
							
										err = thread_create(i);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int threads_destroy(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct thread_data *td0 = &threads[0];
							 | 
						||
| 
								 | 
							
									int i, err = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* cleanup the main thread */
							 | 
						||
| 
								 | 
							
									munmap(td0->map, page_size);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									go_away = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for (i = 1; !err && i < THREADS; i++)
							 | 
						||
| 
								 | 
							
										err = pthread_join(threads[i].pt, NULL);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef int (*synth_cb)(struct machine *machine);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int synth_all(struct machine *machine)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									return perf_event__synthesize_threads(NULL,
							 | 
						||
| 
								 | 
							
													      perf_event__process,
							 | 
						||
| 
								 | 
							
													      machine, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int synth_process(struct machine *machine)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct thread_map *map;
							 | 
						||
| 
								 | 
							
									int err;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									map = thread_map__new_by_pid(getpid());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = perf_event__synthesize_thread_map(NULL, map,
							 | 
						||
| 
								 | 
							
														perf_event__process,
							 | 
						||
| 
								 | 
							
														machine, 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									thread_map__delete(map);
							 | 
						||
| 
								 | 
							
									return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int mmap_events(synth_cb synth)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct machines machines;
							 | 
						||
| 
								 | 
							
									struct machine *machine;
							 | 
						||
| 
								 | 
							
									int err, i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/*
							 | 
						||
| 
								 | 
							
									 * The threads_create will not return before all threads
							 | 
						||
| 
								 | 
							
									 * are spawned and all created memory map.
							 | 
						||
| 
								 | 
							
									 *
							 | 
						||
| 
								 | 
							
									 * They will loop until threads_destroy is called, so we
							 | 
						||
| 
								 | 
							
									 * can safely run synthesizing function.
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									TEST_ASSERT_VAL("failed to create threads", !threads_create());
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									machines__init(&machines);
							 | 
						||
| 
								 | 
							
									machine = &machines.host;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dump_trace = verbose > 1 ? 1 : 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									err = synth(machine);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									dump_trace = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy());
							 | 
						||
| 
								 | 
							
									TEST_ASSERT_VAL("failed to synthesize maps", !err);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/*
							 | 
						||
| 
								 | 
							
									 * All data is synthesized, try to find map for each
							 | 
						||
| 
								 | 
							
									 * thread object.
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									for (i = 0; i < THREADS; i++) {
							 | 
						||
| 
								 | 
							
										struct thread_data *td = &threads[i];
							 | 
						||
| 
								 | 
							
										struct addr_location al;
							 | 
						||
| 
								 | 
							
										struct thread *thread;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										thread = machine__findnew_thread(machine, getpid(), td->tid);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										pr_debug("looking for map %p\n", td->map);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										thread__find_addr_map(thread, machine,
							 | 
						||
| 
								 | 
							
												      PERF_RECORD_MISC_USER, MAP__FUNCTION,
							 | 
						||
| 
								 | 
							
												      (unsigned long) (td->map + 1), &al);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (!al.map) {
							 | 
						||
| 
								 | 
							
											pr_debug("failed, couldn't find map\n");
							 | 
						||
| 
								 | 
							
											err = -1;
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									machine__delete_threads(machine);
							 | 
						||
| 
								 | 
							
									machines__exit(&machines);
							 | 
						||
| 
								 | 
							
									return err;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * This test creates 'THREADS' number of threads (including
							 | 
						||
| 
								 | 
							
								 * main thread) and each thread creates memory map.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * When threads are created, we synthesize them with both
							 | 
						||
| 
								 | 
							
								 * (separate tests):
							 | 
						||
| 
								 | 
							
								 *   perf_event__synthesize_thread_map (process based)
							 | 
						||
| 
								 | 
							
								 *   perf_event__synthesize_threads    (global)
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * We test we can find all memory maps via:
							 | 
						||
| 
								 | 
							
								 *   thread__find_addr_map
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * by using all thread objects.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								int test__mmap_thread_lookup(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									/* perf_event__synthesize_threads synthesize */
							 | 
						||
| 
								 | 
							
									TEST_ASSERT_VAL("failed with sythesizing all",
							 | 
						||
| 
								 | 
							
											!mmap_events(synth_all));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* perf_event__synthesize_thread_map synthesize */
							 | 
						||
| 
								 | 
							
									TEST_ASSERT_VAL("failed with sythesizing process",
							 | 
						||
| 
								 | 
							
											!mmap_events(synth_process));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return 0;
							 | 
						||
| 
								 | 
							
								}
							 |