├── .gitignore ├── CMakeLists.txt ├── cmake ├── Findnumactl.cmake └── Findpfm.cmake ├── examples ├── .gitignore ├── CMakeLists.txt ├── example.c ├── example2.c └── showevtinfo.c ├── include ├── numap.h └── numap_config.h.in ├── pkg-config.pc.cmake ├── readme.md └── src ├── .gitignore ├── CMakeLists.txt ├── numap.c └── numap_analyse.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | install -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | INCLUDE (CheckLibraryExists) 3 | find_package(PkgConfig) 4 | 5 | 6 | project (numap C) 7 | set (NUMAP_VERSION_MAJOR 0) 8 | set (NUMAP_VERSION_MINOR 1) 9 | 10 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") 11 | 12 | SET(PKG_CONFIG_LIBDIR "\${prefix}/lib" ) 13 | SET(PKG_CONFIG_INCLUDEDIR "\${prefix}/include" ) 14 | SET(PKG_CONFIG_LIBS "-L\${libdir} -lnumap" ) 15 | SET(PKG_CONFIG_CFLAGS "-I\${includedir}" ) 16 | 17 | find_package (pfm) 18 | if (PFM_FOUND) 19 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${PFM_INCLUDE_DIR}") 20 | set(PKG_CONFIG_CFLAGS "${PKG_CONFIG_CFLAGS} -I${PFM_INCLUDE_DIR}") 21 | set(PKG_CONFIG_LIBS "${PKG_CONFIG_LIBS} ${PFM_LIBRARIES}") 22 | link_directories(${PFM_LIB_DIR}) 23 | else (PFM_FOUND) 24 | message(FATAL_ERROR "libpfm not found") 25 | endif (PFM_FOUND) 26 | 27 | find_package (numactl) 28 | if (NUMACTL_FOUND) 29 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${NUMACTL_INCLUDE_DIRS}") 30 | set(PKG_CONFIG_CFLAGS "${PKG_CONFIG_CFLAGS} -I${NUMACTL_INCLUDE_DIRS}") 31 | set(PKG_CONFIG_LIBS "${PKG_CONFIG_LIBS} ${NUMACTL_LIBRARIES}") 32 | link_directories(${NUMACTL_LIB_DIR}) 33 | else (NUMACTL_FOUND) 34 | message(FATAL_ERROR "numactl not found") 35 | endif (NUMACTL_FOUND) 36 | 37 | # configure a header file to pass some of the CMake settings 38 | # to the source code 39 | configure_file ( 40 | "${PROJECT_SOURCE_DIR}/include/numap_config.h.in" 41 | "${PROJECT_BINARY_DIR}/include/numap_config.h" 42 | ) 43 | 44 | # use, i.e. don't skip the full RPATH for the build tree 45 | SET(CMAKE_SKIP_BUILD_RPATH FALSE) 46 | 47 | # when building, don't use the install RPATH already 48 | # (but later on when installing) 49 | SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 50 | 51 | SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 52 | 53 | # add the automatically determined parts of the RPATH 54 | # which point to directories outside the build tree to the install RPATH 55 | SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 56 | 57 | 58 | # the RPATH to be used when installing, but only if it's not a system directory 59 | LIST(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) 60 | IF("${isSystemDir}" STREQUAL "-1") 61 | SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 62 | ENDIF("${isSystemDir}" STREQUAL "-1") 63 | 64 | # add the binary tree to the search path for include files 65 | # so that we will find TutorialConfig.h 66 | include_directories("${PROJECT_BINARY_DIR}/include") 67 | 68 | # add the sub directories 69 | include_directories("${PROJECT_SOURCE_DIR}/include") 70 | add_subdirectory ("src") 71 | add_subdirectory ("examples") 72 | 73 | CONFIGURE_FILE( 74 | "${CMAKE_CURRENT_SOURCE_DIR}/pkg-config.pc.cmake" 75 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" 76 | ) 77 | 78 | install(TARGETS DESTINATION bin) 79 | install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc" 80 | DESTINATION lib/pkgconfig) 81 | -------------------------------------------------------------------------------- /cmake/Findnumactl.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Find the NUMACTL libraries and include dir 3 | # 4 | 5 | # NUMACTL_INCLUDE_DIRS - Directories to include to use NUMACTL 6 | # NUMACTL_LIBRARIES - Files to link against to use NUMACTL 7 | # NUMACTL_LIB_DIR - The directory containing NUMACTL_LIBRARIES 8 | # NUMACTL_FOUND - When false, don't try to use NUMACTL 9 | # 10 | # NUMACTL_DIR can be used to make it simpler to find the various include 11 | # directories and compiled libraries when NUMACTL was not installed in the 12 | # usual/well-known directories (e.g. because you made an in tree-source 13 | # compilation or because you installed it in an "unusual" directory). 14 | # Just set NUMACTL_DIR it to your specific installation directory 15 | # 16 | FIND_PATH( NUMACTL_INCLUDE_DIR numa.h 17 | HINTS ${NUMACTL_DIR}/include 18 | ) 19 | 20 | find_library(NUMACTL_LIBRARY 21 | NAMES numa 22 | HINTS ${NUMACTL_DIR}/lib 23 | ) 24 | 25 | include(FindPackageHandleStandardArgs) 26 | # handle the QUIETLY and REQUIRED arguments and set LIBXML2_FOUND to TRUE 27 | # if all listed variables are TRUE 28 | find_package_handle_standard_args(numactl DEFAULT_MSG 29 | NUMACTL_LIBRARY NUMACTL_INCLUDE_DIR) 30 | 31 | IF( NUMACTL_INCLUDE_DIR ) 32 | IF( NUMACTL_LIBRARY ) 33 | SET( NUMACTL_FOUND "YES" ) 34 | MARK_AS_ADVANCED( NUMACTL_INCLUDE_DIR NUMACTL_LIBRARY ) 35 | set(NUMACTL_LIBRARIES ${NUMACTL_LIBRARY} ) 36 | get_filename_component(NUMACTL_LIB_DIR ${NUMACTL_LIBRARY} DIRECTORY) 37 | set(NUMACTL_INCLUDE_DIRS ${NUMACTL_INCLUDE_DIR} ) 38 | ENDIF( NUMACTL_LIBRARY ) 39 | ENDIF( NUMACTL_INCLUDE_DIR ) 40 | 41 | IF( NOT NUMACTL_FOUND ) 42 | MESSAGE("NUMACTL installation was not found. Please provide NUMACTL_DIR:") 43 | MESSAGE(" - through the GUI when working with ccmake, ") 44 | MESSAGE(" - as a command line argument when working with cmake e.g. ") 45 | MESSAGE(" cmake .. -DNUMACTL_DIR:PATH=/usr/local/numactl ") 46 | MESSAGE("Note: the following message is triggered by cmake on the first ") 47 | MESSAGE(" undefined necessary PATH variable (e.g. NUMACTL_INCLUDE_DIR).") 48 | MESSAGE(" Providing NUMACTL_DIR (as above described) is probably the") 49 | MESSAGE(" simplest solution unless you have a really customized/odd") 50 | MESSAGE(" NUMACTL installation...") 51 | SET(NUMACTL_DIR "" CACHE PATH "Root of NUMACTL install tree." ) 52 | ENDIF( NOT NUMACTL_FOUND ) 53 | -------------------------------------------------------------------------------- /cmake/Findpfm.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Find the PFM libraries and include dir 3 | # 4 | 5 | # PFM_INCLUDE_DIR - Directories to include to use PFM 6 | # PFM_LIBRARIES - Files to link against to use PFM 7 | # PFM_LIB_DIR - The directory containing PFM_LIBRARIES 8 | # PFM_FOUND - When false, don't try to use PFM 9 | # 10 | # PFM_DIR can be used to make it simpler to find the various include 11 | # directories and compiled libraries when PFM was not installed in the 12 | # usual/well-known directories (e.g. because you made an in tree-source 13 | # compilation or because you installed it in an "unusual" directory). 14 | # Just set PFM_DIR it to your specific installation directory 15 | # 16 | FIND_PATH( PFM_INCLUDE_DIR perfmon/pfmlib_perf_event.h 17 | HINTS ${PFM_DIR}/include 18 | ) 19 | 20 | find_library(PFM_LIBRARY 21 | NAMES pfm 22 | HINTS ${PFM_DIR}/lib 23 | ) 24 | 25 | include(FindPackageHandleStandardArgs) 26 | # handle the QUIETLY and REQUIRED arguments and set LIBXML2_FOUND to TRUE 27 | # if all listed variables are TRUE 28 | find_package_handle_standard_args(pfm DEFAULT_MSG 29 | PFM_LIBRARY PFM_INCLUDE_DIR) 30 | 31 | IF( PFM_INCLUDE_DIR ) 32 | IF( PFM_LIBRARY ) 33 | SET( PFM_FOUND "YES" ) 34 | MARK_AS_ADVANCED( PFM_DIR PFM_INCLUDE_DIR PFM_LIBRARY ) 35 | set(PFM_LIBRARIES ${PFM_LIBRARY} ) 36 | get_filename_component(PFM_LIB_DIR ${PFM_LIBRARY} DIRECTORY) 37 | set(PFM_INCLUDE_DIRS ${PFM_INCLUDE_DIR} ) 38 | ENDIF( PFM_LIBRARY ) 39 | ENDIF( PFM_INCLUDE_DIR ) 40 | 41 | IF( NOT PFM_FOUND ) 42 | MESSAGE("PFM installation was not found. Please provide PFM_DIR:") 43 | MESSAGE(" - through the GUI when working with ccmake, ") 44 | MESSAGE(" - as a command line argument when working with cmake e.g. ") 45 | MESSAGE(" cmake .. -DPFM_DIR:PATH=/usr/local/pfm ") 46 | MESSAGE("Note: the following message is triggered by cmake on the first ") 47 | MESSAGE(" undefined necessary PATH variable (e.g. PFM_INCLUDE_DIR).") 48 | MESSAGE(" Providing PFM_DIR (as above described) is probably the") 49 | MESSAGE(" simplest solution unless you have a really customized/odd") 50 | MESSAGE(" PFM installation...") 51 | SET(PFM_DIR "" CACHE PATH "Root of PFM install tree." ) 52 | ENDIF( NOT PFM_FOUND ) 53 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | example 3 | mem-bdw-top 4 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${NUMACTL_LIB_DIR} -L${PFM_LIB_DIR}") 2 | 3 | add_executable (example example.c) 4 | target_link_libraries (example numap pthread) 5 | set_target_properties(example PROPERTIES COMPILE_FLAGS "-D_REENTRANT -DLinux -D_GNU_SOURCE") 6 | 7 | add_executable (example2 example2.c) 8 | target_link_libraries (example2 numap pthread) 9 | set_target_properties(example2 PROPERTIES COMPILE_FLAGS "-D_REENTRANT -DLinux -D_GNU_SOURCE") 10 | 11 | add_executable (showevtinfo showevtinfo.c) 12 | target_link_libraries (showevtinfo numap pthread) 13 | 14 | -------------------------------------------------------------------------------- /examples/example.c: -------------------------------------------------------------------------------- 1 | #include "numap.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | pthread_barrier_t barrier; 12 | pid_t tid_0, tid_1; 13 | 14 | void to_be_profiled() { 15 | long size = 50000000; 16 | int *data = malloc(sizeof(int) * size); 17 | if (data == NULL) { 18 | printf("malloc failed\n"); 19 | exit(-1); 20 | } 21 | int res = 0; 22 | for (long i = 0; i < size; i++) { 23 | data[i] = i; 24 | res += data[i]; 25 | } 26 | } 27 | 28 | void *thread_0_f(void *p) { 29 | tid_0 = syscall(SYS_gettid); 30 | pthread_barrier_wait(&barrier); 31 | pthread_barrier_wait(&barrier); 32 | to_be_profiled(); 33 | pthread_barrier_wait(&barrier); 34 | return NULL; 35 | } 36 | 37 | void *thread_1_f(void *p) { 38 | tid_1 = syscall(SYS_gettid); 39 | pthread_barrier_wait(&barrier); 40 | pthread_barrier_wait(&barrier); 41 | to_be_profiled(); 42 | pthread_barrier_wait(&barrier); 43 | return NULL; 44 | } 45 | 46 | #define T0_CPU 2 47 | #define T1_CPU 3 48 | 49 | int main() { 50 | 51 | // Init numap 52 | int res = numap_init(); 53 | if(res < 0) { 54 | fprintf(stderr, "numap_init : %s\n", numap_error_message(res)); 55 | return -1; 56 | } 57 | 58 | int num_cores = sysconf(_SC_NPROCESSORS_ONLN); 59 | if (T0_CPU >= num_cores || T1_CPU >= num_cores) { 60 | fprintf(stderr, "Can't set affinity to thread 0 to %d or to thread 1 to %d\n", T0_CPU, T1_CPU); 61 | return -1; 62 | } 63 | 64 | // Create threads 65 | res = pthread_barrier_init(&barrier, NULL, 3); 66 | if (res) { 67 | fprintf(stderr, "Error creating barrier: %d\n", res); 68 | return -1; 69 | } 70 | pthread_attr_t attr; 71 | pthread_attr_init(&attr); 72 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 73 | cpu_set_t mask; 74 | CPU_ZERO(&mask); 75 | CPU_SET(T0_CPU, &mask); 76 | res = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &mask); 77 | if (res != 0) { 78 | fprintf(stderr, "Error setting affinity to thread 0: %s\n", strerror(res)); 79 | return -1; 80 | } 81 | pthread_t thread_0; 82 | if ((res = pthread_create(&thread_0, &attr, thread_0_f, (void *)NULL)) < 0) { 83 | fprintf(stderr, "Error creating thread 0: %d\n", res); 84 | return -1; 85 | } 86 | CPU_ZERO(&mask); 87 | CPU_SET(T1_CPU, &mask); 88 | res = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &mask); 89 | if (res != 0) { 90 | fprintf(stderr, "Error setting affinity to thread 1: %s\n", strerror(res)); 91 | return -1; 92 | } 93 | pthread_t thread_1; 94 | if ((res = pthread_create(&thread_1, &attr, thread_1_f, (void *)NULL)) < 0) { 95 | fprintf(stderr, "Error creating thread 1: %d\n", res); 96 | return -1; 97 | } 98 | 99 | // Init read sampling 100 | struct numap_sampling_measure sm; 101 | int sampling_rate = 1000; 102 | res = numap_sampling_init_measure(&sm, 2, sampling_rate, 64); 103 | if(res < 0) { 104 | fprintf(stderr, "numap_sampling_init error : %s\n", numap_error_message(res)); 105 | return -1; 106 | } 107 | pthread_barrier_wait(&barrier); 108 | sm.tids[0] = tid_0; 109 | sm.tids[1] = tid_1; 110 | 111 | // Start memory read access sampling 112 | printf("\nStarting memory read sampling"); 113 | fflush(stdout); 114 | // has to be called after tids set and before start 115 | res = numap_sampling_read_start(&sm); 116 | if(res < 0) { 117 | fprintf(stderr, " -> numap_sampling_start error : %s\n", numap_error_message(res)); 118 | return -1; 119 | } 120 | pthread_barrier_wait(&barrier); 121 | pthread_barrier_wait(&barrier); 122 | 123 | // Stop memory read access sampling 124 | res = numap_sampling_read_stop(&sm); 125 | if(res < 0) { 126 | printf("numap_sampling_stop error : %s\n", numap_error_message(res)); 127 | return -1; 128 | } 129 | 130 | // Print memory read sampling results 131 | printf("\nMemory read sampling results\n"); 132 | numap_sampling_read_print(&sm, 0); 133 | 134 | if ((res = pthread_create(&thread_0, &attr, thread_0_f, (void *)NULL)) < 0) { 135 | fprintf(stderr, "Error creating thread 0: %d\n", res); 136 | return -1; 137 | } 138 | CPU_ZERO(&mask); 139 | CPU_SET(T1_CPU, &mask); 140 | res = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &mask); 141 | if (res != 0) { 142 | fprintf(stderr, "Error setting affinity to thread 1: %s\n", strerror(res)); 143 | return -1; 144 | } 145 | if ((res = pthread_create(&thread_1, &attr, thread_1_f, (void *)NULL)) < 0) { 146 | fprintf(stderr, "Error creating thread 1: %d\n", res); 147 | return -1; 148 | } 149 | 150 | // Init write sampling 151 | res = numap_sampling_init_measure(&sm, 2, sampling_rate, 64); 152 | if(res < 0) { 153 | fprintf(stderr, "numap_sampling_init error : %s\n", numap_error_message(res)); 154 | return -1; 155 | } 156 | pthread_barrier_wait(&barrier); 157 | sm.tids[0] = tid_0; 158 | sm.tids[1] = tid_1; 159 | 160 | // Start memory write access sampling 161 | printf("\nStarting memory write sampling"); 162 | fflush(stdout); 163 | res = numap_sampling_write_start(&sm); 164 | if(res < 0) { 165 | fprintf(stderr, " -> numap_sampling_start error : %s\n", numap_error_message(res)); 166 | return -1; 167 | } 168 | pthread_barrier_wait(&barrier); 169 | pthread_barrier_wait(&barrier); 170 | 171 | // Stop memory write access sampling 172 | res = numap_sampling_write_stop(&sm); 173 | if(res < 0) { 174 | printf("numap_sampling_stop error : %s\n", numap_error_message(res)); 175 | return -1; 176 | } 177 | 178 | // Print memory write sampling results 179 | printf("\nMemory write sampling results\n"); 180 | numap_sampling_write_print(&sm, 0); 181 | 182 | /* // Start memory controler read and writes counting */ 183 | /* struct numap_bdw_measure m; */ 184 | /* res = numap_bdw_init_measure(&m); */ 185 | /* if(res < 0) { */ 186 | /* fprintf(stderr, "numap_bdw_init error : %s\n", numap_error_message(res)); */ 187 | /* return -1; */ 188 | /* } */ 189 | /* res = numap_bdw_start(&m); */ 190 | /* if(res < 0) { */ 191 | /* fprintf(stderr, "numap_bdw_start error : %s\n", numap_error_message(res)); */ 192 | /* return -1; */ 193 | /* } */ 194 | 195 | /* // Stop memory controler read and writes counting */ 196 | /* res = numap_stop(&m); */ 197 | /* if(res < 0) { */ 198 | /* printf("numap_stop error : %s\n", numap_error_message(res)); */ 199 | /* return -1; */ 200 | /* } */ 201 | 202 | /* // Print memory counting results */ 203 | /* printf("Memory counting results\n"); */ 204 | /* for(int i = 0; i < m.nb_nodes; i++) { */ 205 | /* printf("Memory reads count for node %d is %lld\n", i, m.reads_count[i]); */ 206 | /* printf("Memory writes count for node %d is %lld\n", i, m.writes_count[i]); */ 207 | /* } */ 208 | 209 | return 0; 210 | } 211 | -------------------------------------------------------------------------------- /examples/example2.c: -------------------------------------------------------------------------------- 1 | #include "numap.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | pthread_barrier_t barrier; 13 | pid_t tid_0, tid_1; 14 | 15 | void to_be_profiled() { 16 | long size = 70000000; 17 | int *data = malloc(sizeof(int) * size); 18 | if (data == NULL) { 19 | printf("malloc failed\n"); 20 | exit(-1); 21 | } 22 | int res = 0; 23 | for (long i = 0; i < size; i++) { 24 | data[i] = i; 25 | res += data[i]; 26 | } 27 | } 28 | 29 | void *thread_0_f(void *p) { 30 | tid_0 = syscall(SYS_gettid); 31 | pthread_barrier_wait(&barrier); 32 | pthread_barrier_wait(&barrier); 33 | to_be_profiled(); 34 | pthread_barrier_wait(&barrier); 35 | return NULL; 36 | } 37 | 38 | void *thread_1_f(void *p) { 39 | tid_1 = syscall(SYS_gettid); 40 | pthread_barrier_wait(&barrier); 41 | pthread_barrier_wait(&barrier); 42 | to_be_profiled(); 43 | pthread_barrier_wait(&barrier); 44 | return NULL; 45 | } 46 | 47 | struct mem_sampling_backed { 48 | struct mem_sampling_backed* next; 49 | int fd; // corresponding fd 50 | void* buffer; // buffer where data is backed up 51 | size_t buffer_size; 52 | }; 53 | 54 | pthread_mutex_t msb_lock = PTHREAD_MUTEX_INITIALIZER; 55 | struct mem_sampling_backed *msb; 56 | 57 | void handler(struct numap_sampling_measure* measure, int fd) 58 | { 59 | // search metadata 60 | int tid_i=-1; // search tid 61 | for (int i = 0 ; i < measure->nb_threads ; i++) 62 | { 63 | if (measure->fd_per_tid[i] == fd) 64 | tid_i = i; 65 | } 66 | if (tid_i == -1) 67 | { 68 | fprintf(stderr, "No tid associated with fd %d\n", fd); 69 | exit(EXIT_FAILURE); 70 | } 71 | struct perf_event_mmap_page *metadata_page = measure->metadata_pages_per_tid[tid_i]; 72 | 73 | // wrap data_head 74 | if (metadata_page->data_head > metadata_page->data_size) 75 | { 76 | metadata_page->data_head = (metadata_page->data_head % metadata_page->data_size); 77 | } 78 | uint64_t head = metadata_page->data_head; 79 | rmb(); 80 | uint64_t tail = metadata_page->data_tail; 81 | size_t sample_size; 82 | if (head > tail) { 83 | sample_size = head - tail; 84 | } else { 85 | sample_size = (metadata_page->data_size - tail) + head; 86 | } 87 | struct mem_sampling_backed *new_msb = malloc(sizeof(struct mem_sampling_backed)); 88 | if (new_msb == NULL) { 89 | fprintf(stderr, "could not malloc mem_sampling_backed\n"); 90 | exit(EXIT_FAILURE); 91 | } 92 | new_msb->fd = fd; 93 | new_msb->buffer_size = sample_size; 94 | new_msb->buffer = malloc(new_msb->buffer_size); 95 | if (new_msb->buffer == NULL) { 96 | fprintf(stderr, "could not malloc buffer\n"); 97 | exit(EXIT_FAILURE); 98 | } 99 | // TODO : Save the data here 100 | //struct perf_event_header *header = (struct perf_event_header *)((char *)metadata_page + measure->page_size + tail); 101 | uint8_t* start_addr = (uint8_t *)metadata_page; 102 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) 103 | start_addr += metadata_page->data_offset; 104 | #else 105 | static size_t page_size = 0; 106 | if(page_size == 0) 107 | page_size = (size_t)sysconf(_SC_PAGESIZE); 108 | start_addr += page_size; 109 | #endif 110 | //void* start_address = (char*)metadata_page+measure->page_size+tail; 111 | if (head > tail) { 112 | memcpy(new_msb->buffer, start_addr+tail, new_msb->buffer_size); 113 | } else { 114 | memcpy(new_msb->buffer, start_addr+tail, (metadata_page->data_size - tail)); 115 | memcpy((char*)new_msb->buffer + (metadata_page->data_size - tail), start_addr, head); 116 | } 117 | pthread_mutex_lock(&msb_lock); 118 | new_msb->next = msb; 119 | msb = new_msb; 120 | pthread_mutex_unlock(&msb_lock); 121 | 122 | metadata_page->data_tail = head; 123 | } 124 | 125 | void free_mem_sampling_backed(struct mem_sampling_backed* to_clean) 126 | { 127 | struct mem_sampling_backed* current = NULL; 128 | while (to_clean != NULL) 129 | { 130 | current = to_clean; 131 | to_clean = to_clean->next; 132 | free(current->buffer); 133 | free(current); 134 | } 135 | } 136 | 137 | int numap_sampling_print_backed(struct numap_sampling_measure *measure, struct mem_sampling_backed *msb, char print_samples) { 138 | int thread; 139 | if (msb == NULL) return 0; 140 | for (thread = 0; thread < measure->nb_threads; thread++) { 141 | struct mem_sampling_backed* current_msb = msb; 142 | int fd = measure->fd_per_tid[thread]; 143 | 144 | struct perf_event_header *header; 145 | uint64_t consumed = 0; 146 | int na_miss_count = 0; 147 | int cache1_count = 0; 148 | int cache2_count = 0; 149 | int cache3_count = 0; 150 | int lfb_count = 0; 151 | int memory_count = 0; 152 | int remote_memory_count = 0; 153 | int remote_cache_count = 0; 154 | int total_count = 0; 155 | while (current_msb != NULL) { 156 | header = (struct perf_event_header *)(current_msb->buffer); 157 | int head = 0; 158 | if (current_msb->fd == fd) { 159 | while (head < current_msb->buffer_size) { 160 | if (header->size == 0) { 161 | fprintf(stderr, "Error: invalid header size = 0\n"); 162 | return -1; 163 | } 164 | if (header -> type == PERF_RECORD_SAMPLE) { 165 | struct sample *sample = (struct sample *)((char *)(header) + 8); 166 | if (is_served_by_local_NA_miss(sample->data_src)) { 167 | na_miss_count++; 168 | } 169 | if (is_served_by_local_cache1(sample->data_src)) { 170 | cache1_count++; 171 | } 172 | if (is_served_by_local_cache2(sample->data_src)) { 173 | cache2_count++; 174 | } 175 | if (is_served_by_local_cache3(sample->data_src)) { 176 | cache3_count++; 177 | } 178 | if (is_served_by_local_lfb(sample->data_src)) { 179 | lfb_count++; 180 | } 181 | if (is_served_by_local_memory(sample->data_src)) { 182 | memory_count++; 183 | } 184 | if (is_served_by_remote_memory(sample->data_src)) { 185 | remote_memory_count++; 186 | } 187 | if (is_served_by_remote_cache_or_local_memory(sample->data_src)) { 188 | remote_cache_count++; 189 | } 190 | total_count++; 191 | if (print_samples) { 192 | printf("pc=%" PRIx64 ", @=%" PRIx64 ", src level=%s, latency=%" PRIu64 "\n", sample->ip, sample->addr, get_data_src_level(sample->data_src), sample->weight); 193 | } 194 | } 195 | head += header->size; 196 | header = (struct perf_event_header *)((char*)current_msb->buffer+head); 197 | } 198 | } 199 | current_msb = current_msb->next; 200 | } 201 | printf("\n"); 202 | //printf("head = %" PRIu64 " compared to max = %zu\n", head, measure->mmap_len); 203 | printf("Thread %d: %-8d samples\n", thread, total_count); 204 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, cache1_count, "local cache 1", (100.0 * cache1_count / total_count)); 205 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, cache2_count, "local cache 2", (100.0 * cache2_count / total_count)); 206 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, cache3_count, "local cache 3", (100.0 * cache3_count / total_count)); 207 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, lfb_count, "local cache LFB", (100.0 * lfb_count / total_count)); 208 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, memory_count, "local memory", (100.0 * memory_count / total_count)); 209 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, remote_cache_count, "remote cache or local memory", (100.0 * remote_cache_count / total_count)); 210 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, remote_memory_count, "remote memory", (100.0 * remote_memory_count / total_count)); 211 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, na_miss_count, "unknown l3 miss", (100.0 * na_miss_count / total_count)); 212 | } 213 | 214 | return 0; 215 | } 216 | 217 | #define T0_CPU 2 218 | #define T1_CPU 3 219 | 220 | int main() { 221 | 222 | // Init numap 223 | int res = numap_init(); 224 | if(res < 0) { 225 | fprintf(stderr, "numap_init : %s\n", numap_error_message(res)); 226 | return -1; 227 | } 228 | 229 | int num_cores = sysconf(_SC_NPROCESSORS_ONLN); 230 | if (T0_CPU >= num_cores || T1_CPU >= num_cores) { 231 | fprintf(stderr, "Can't set affinity to thread 0 to %d or to thread 1 to %d\n", T0_CPU, T1_CPU); 232 | return -1; 233 | } 234 | 235 | // Create threads 236 | res = pthread_barrier_init(&barrier, NULL, 3); 237 | if (res) { 238 | fprintf(stderr, "Error creating barrier: %d\n", res); 239 | return -1; 240 | } 241 | pthread_attr_t attr; 242 | pthread_attr_init(&attr); 243 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 244 | cpu_set_t mask; 245 | CPU_ZERO(&mask); 246 | CPU_SET(T0_CPU, &mask); 247 | res = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &mask); 248 | if (res != 0) { 249 | fprintf(stderr, "Error setting affinity to thread 0: %s\n", strerror(res)); 250 | return -1; 251 | } 252 | pthread_t thread_0; 253 | if ((res = pthread_create(&thread_0, &attr, thread_0_f, (void *)NULL)) < 0) { 254 | fprintf(stderr, "Error creating thread 0: %d\n", res); 255 | return -1; 256 | } 257 | CPU_ZERO(&mask); 258 | CPU_SET(T1_CPU, &mask); 259 | res = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &mask); 260 | if (res != 0) { 261 | fprintf(stderr, "Error setting affinity to thread 1: %s\n", strerror(res)); 262 | return -1; 263 | } 264 | pthread_t thread_1; 265 | if ((res = pthread_create(&thread_1, &attr, thread_1_f, (void *)NULL)) < 0) { 266 | fprintf(stderr, "Error creating thread 1: %d\n", res); 267 | return -1; 268 | } 269 | 270 | // Init read sampling 271 | struct numap_sampling_measure sm; 272 | int sampling_rate = 1000; 273 | res = numap_sampling_init_measure(&sm, 2, sampling_rate, 64); 274 | if(res < 0) { 275 | fprintf(stderr, "numap_sampling_init error : %s\n", numap_error_message(res)); 276 | return -1; 277 | } 278 | pthread_barrier_wait(&barrier); 279 | sm.tids[0] = tid_0; 280 | sm.tids[1] = tid_1; 281 | 282 | // set data backup 283 | if (numap_sampling_set_measure_handler(&sm, handler, 1000) != 0) 284 | { 285 | fprintf(stderr, "numap_sampling_set_measure_handler error : %s\n", numap_error_message(res)); 286 | return -1; 287 | } 288 | 289 | // Start memory read access sampling 290 | printf("\nStarting memory read sampling"); 291 | fflush(stdout); 292 | // has to be called after tids set and before start 293 | res = numap_sampling_read_start(&sm); 294 | if(res < 0) { 295 | fprintf(stderr, " -> numap_sampling_start error : %s\n", numap_error_message(res)); 296 | return -1; 297 | } 298 | pthread_barrier_wait(&barrier); 299 | pthread_barrier_wait(&barrier); 300 | 301 | // Stop memory read access sampling 302 | res = numap_sampling_read_stop(&sm); 303 | if(res < 0) { 304 | printf("numap_sampling_stop error : %s\n", numap_error_message(res)); 305 | return -1; 306 | } 307 | 308 | // Print memory read sampling results 309 | printf("\nMemory read sampling results\n"); 310 | //numap_sampling_read_print(&sm, 0); 311 | numap_sampling_print_backed(&sm, msb, 00); 312 | free_mem_sampling_backed(msb); 313 | msb = NULL; 314 | 315 | if ((res = pthread_create(&thread_0, &attr, thread_0_f, (void *)NULL)) < 0) { 316 | fprintf(stderr, "Error creating thread 0: %d\n", res); 317 | return -1; 318 | } 319 | CPU_ZERO(&mask); 320 | CPU_SET(T1_CPU, &mask); 321 | res = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &mask); 322 | if (res != 0) { 323 | fprintf(stderr, "Error setting affinity to thread 1: %s\n", strerror(res)); 324 | return -1; 325 | } 326 | if ((res = pthread_create(&thread_1, &attr, thread_1_f, (void *)NULL)) < 0) { 327 | fprintf(stderr, "Error creating thread 1: %d\n", res); 328 | return -1; 329 | } 330 | 331 | // Init write sampling 332 | res = numap_sampling_init_measure(&sm, 2, sampling_rate, 64); 333 | if(res < 0) { 334 | fprintf(stderr, "numap_sampling_init error : %s\n", numap_error_message(res)); 335 | return -1; 336 | } 337 | pthread_barrier_wait(&barrier); 338 | sm.tids[0] = tid_0; 339 | sm.tids[1] = tid_1; 340 | 341 | // Start memory write access sampling 342 | printf("\nStarting memory write sampling"); 343 | fflush(stdout); 344 | if (numap_sampling_set_measure_handler(&sm, handler, 1000) != 0) 345 | { 346 | fprintf(stderr, "numap_sampling_set_measure_handler error : %s\n", numap_error_message(res)); 347 | return -1; 348 | } 349 | res = numap_sampling_write_start(&sm); 350 | if(res < 0) { 351 | fprintf(stderr, " -> numap_sampling_start error : %s\n", numap_error_message(res)); 352 | return -1; 353 | } 354 | pthread_barrier_wait(&barrier); 355 | pthread_barrier_wait(&barrier); 356 | 357 | // Stop memory write access sampling 358 | res = numap_sampling_write_stop(&sm); 359 | if(res < 0) { 360 | printf("numap_sampling_stop error : %s\n", numap_error_message(res)); 361 | return -1; 362 | } 363 | 364 | // Print memory write sampling results 365 | printf("\nMemory write sampling results\n"); 366 | //numap_sampling_write_print(&sm, 0); 367 | numap_sampling_print_backed(&sm, msb, 0); 368 | free_mem_sampling_backed(msb); 369 | msb = NULL; 370 | 371 | /* // Start memory controler read and writes counting */ 372 | /* struct numap_bdw_measure m; */ 373 | /* res = numap_bdw_init_measure(&m); */ 374 | /* if(res < 0) { */ 375 | /* fprintf(stderr, "numap_bdw_init error : %s\n", numap_error_message(res)); */ 376 | /* return -1; */ 377 | /* } */ 378 | /* res = numap_bdw_start(&m); */ 379 | /* if(res < 0) { */ 380 | /* fprintf(stderr, "numap_bdw_start error : %s\n", numap_error_message(res)); */ 381 | /* return -1; */ 382 | /* } */ 383 | 384 | /* // Stop memory controler read and writes counting */ 385 | /* res = numap_stop(&m); */ 386 | /* if(res < 0) { */ 387 | /* printf("numap_stop error : %s\n", numap_error_message(res)); */ 388 | /* return -1; */ 389 | /* } */ 390 | 391 | /* // Print memory counting results */ 392 | /* printf("Memory counting results\n"); */ 393 | /* for(int i = 0; i < m.nb_nodes; i++) { */ 394 | /* printf("Memory reads count for node %d is %lld\n", i, m.reads_count[i]); */ 395 | /* printf("Memory writes count for node %d is %lld\n", i, m.writes_count[i]); */ 396 | /* } */ 397 | 398 | return 0; 399 | } 400 | -------------------------------------------------------------------------------- /examples/showevtinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * showevtinfo.c - show event information 3 | * 4 | * Copyright (c) 2010 Google, Inc 5 | * Contributed by Stephane Eranian 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | * of the Software, and to permit persons to whom the Software is furnished to do so, 12 | * subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 18 | * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 19 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 21 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | * 24 | * This file is part of libpfm, a performance monitoring support library for 25 | * applications on Linux. 26 | */ 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | #define MAXBUF 1024 40 | #define COMBO_MAX 18 41 | 42 | static struct { 43 | int compact; 44 | int sort; 45 | int encode; 46 | int combo; 47 | int combo_lim; 48 | int desc; 49 | char *csv_sep; 50 | pfm_event_info_t efilter; 51 | pfm_event_attr_info_t ufilter; 52 | pfm_os_t os; 53 | uint64_t mask; 54 | } options; 55 | 56 | typedef struct { 57 | uint64_t code; 58 | int idx; 59 | } code_info_t; 60 | 61 | static void show_event_info_compact(pfm_event_info_t *info); 62 | 63 | static const char *srcs[PFM_ATTR_CTRL_MAX]={ 64 | [PFM_ATTR_CTRL_UNKNOWN] = "???", 65 | [PFM_ATTR_CTRL_PMU] = "PMU", 66 | [PFM_ATTR_CTRL_PERF_EVENT] = "perf_event", 67 | }; 68 | 69 | #ifdef PFMLIB_WINDOWS 70 | int set_env_var(const char *var, const char *value, int ov) 71 | { 72 | size_t len; 73 | char *str; 74 | int ret; 75 | 76 | len = strlen(var) + 1 + strlen(value) + 1; 77 | 78 | str = malloc(len); 79 | if (!str) 80 | return PFM_ERR_NOMEM; 81 | 82 | sprintf(str, "%s=%s", var, value); 83 | 84 | ret = putenv(str); 85 | 86 | free(str); 87 | 88 | return ret ? PFM_ERR_INVAL : PFM_SUCCESS; 89 | } 90 | #else 91 | static inline int 92 | set_env_var(const char *var, const char *value, int ov) 93 | { 94 | return setenv(var, value, ov); 95 | } 96 | #endif 97 | 98 | static int 99 | event_has_pname(char *s) 100 | { 101 | char *p; 102 | return (p = strchr(s, ':')) && *(p+1) == ':'; 103 | } 104 | 105 | static int 106 | print_codes(char *buf, int plm, int max_encoding) 107 | { 108 | uint64_t *codes = NULL; 109 | int j, ret, count = 0; 110 | 111 | ret = pfm_get_event_encoding(buf, PFM_PLM0|PFM_PLM3, NULL, NULL, &codes, &count); 112 | if (ret != PFM_SUCCESS) { 113 | if (ret == PFM_ERR_NOTFOUND) 114 | fprintf(stderr, "encoding failed, try setting env variable LIBPFM_ENCODE_INACTIVE=1"); 115 | return -1; 116 | } 117 | for(j = 0; j < max_encoding; j++) { 118 | if (j < count) 119 | printf("0x%"PRIx64, codes[j]); 120 | printf("%s", options.csv_sep); 121 | } 122 | free(codes); 123 | return 0; 124 | } 125 | 126 | static int 127 | check_valid(char *buf, int plm) 128 | { 129 | uint64_t *codes = NULL; 130 | int ret, count = 0; 131 | 132 | ret = pfm_get_event_encoding(buf, PFM_PLM0|PFM_PLM3, NULL, NULL, &codes, &count); 133 | if (ret != PFM_SUCCESS) 134 | return -1; 135 | free(codes); 136 | return 0; 137 | } 138 | 139 | static int 140 | match_ufilters(pfm_event_attr_info_t *info) 141 | { 142 | uint32_t ufilter1 = 0; 143 | uint32_t ufilter2 = 0; 144 | 145 | if (options.ufilter.is_dfl) 146 | ufilter1 |= 0x1; 147 | 148 | if (info->is_dfl) 149 | ufilter2 |= 0x1; 150 | 151 | if (options.ufilter.is_precise) 152 | ufilter1 |= 0x2; 153 | 154 | if (info->is_precise) 155 | ufilter2 |= 0x2; 156 | 157 | if (!ufilter1) 158 | return 1; 159 | 160 | /* at least one filter matches */ 161 | return ufilter1 & ufilter2; 162 | } 163 | 164 | static int 165 | match_efilters(pfm_event_info_t *info) 166 | { 167 | pfm_event_attr_info_t ainfo; 168 | int n = 0; 169 | int i, ret; 170 | 171 | if (options.efilter.is_precise && !info->is_precise) 172 | return 0; 173 | 174 | memset(&ainfo, 0, sizeof(ainfo)); 175 | ainfo.size = sizeof(ainfo); 176 | 177 | pfm_for_each_event_attr(i, info) { 178 | ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo); 179 | if (ret != PFM_SUCCESS) 180 | continue; 181 | if (match_ufilters(&ainfo)) 182 | return 1; 183 | if (ainfo.type == PFM_ATTR_UMASK) 184 | n++; 185 | } 186 | return n ? 0 : 1; 187 | } 188 | 189 | static void 190 | show_event_info_combo(pfm_event_info_t *info) 191 | { 192 | pfm_event_attr_info_t *ainfo; 193 | pfm_pmu_info_t pinfo; 194 | char buf[MAXBUF]; 195 | size_t len; 196 | int numasks = 0; 197 | int i, j, ret; 198 | uint64_t total, m, u; 199 | 200 | memset(&pinfo, 0, sizeof(pinfo)); 201 | 202 | pinfo.size = sizeof(pinfo); 203 | 204 | ret = pfm_get_pmu_info(info->pmu, &pinfo); 205 | if (ret != PFM_SUCCESS) { 206 | fprintf(stderr, "cannot get PMU info"); 207 | abort(); 208 | } 209 | 210 | ainfo = calloc(info->nattrs, sizeof(*ainfo)); 211 | if (!ainfo) { 212 | fprintf(stderr, "event %s : ", info->name); 213 | abort(); 214 | } 215 | 216 | /* 217 | * extract attribute information and count number 218 | * of umasks 219 | * 220 | * we cannot just drop non umasks because we need 221 | * to keep attributes in order for the enumeration 222 | * of 2^n 223 | */ 224 | pfm_for_each_event_attr(i, info) { 225 | ainfo[i].size = sizeof(*ainfo); 226 | 227 | ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo[i]); 228 | if (ret != PFM_SUCCESS) { 229 | fprintf(stderr, "cannot get attribute info: %s", pfm_strerror(ret)); 230 | abort(); 231 | } 232 | 233 | if (ainfo[i].type == PFM_ATTR_UMASK) 234 | numasks++; 235 | } 236 | if (numasks > options.combo_lim) { 237 | fprintf(stderr, "event %s has too many umasks to print all combinations, dropping to simple enumeration", info->name); 238 | free(ainfo); 239 | show_event_info_compact(info); 240 | return; 241 | } 242 | 243 | if (numasks) { 244 | if (info->nattrs > (int)((sizeof(total)<<3))) { 245 | fprintf(stderr, "too many umasks, cannot show all combinations for event %s", info->name); 246 | goto end; 247 | } 248 | total = 1ULL << info->nattrs; 249 | 250 | for (u = 1; u < total; u++) { 251 | len = sizeof(buf); 252 | size_t bytes_written = snprintf(buf, len, "%s::%s", pinfo.name, info->name); 253 | if (bytes_written >= len) { 254 | fprintf(stderr, "event name too long%s", info->name); 255 | goto end; 256 | } 257 | len -= bytes_written; 258 | 259 | for(m = u, j = 0; m; m >>=1, j++) { 260 | if (m & 0x1ULL) { 261 | /* we have hit a non umasks attribute, skip */ 262 | if (ainfo[j].type != PFM_ATTR_UMASK) 263 | break; 264 | 265 | if (len < (1 + strlen(ainfo[j].name))) { 266 | fprintf(stderr, "umasks combination too long for event %s", buf); 267 | break; 268 | } 269 | strncat(buf, ":", len-1);buf[len-1] = '\0'; len--; 270 | strncat(buf, ainfo[j].name, len-1);buf[len-1] = '\0'; 271 | len -= strlen(ainfo[j].name); 272 | } 273 | } 274 | /* if found a valid umask combination, check encoding */ 275 | if (m == 0) { 276 | if (options.encode) 277 | ret = print_codes(buf, PFM_PLM0|PFM_PLM3, pinfo.max_encoding); 278 | else 279 | ret = check_valid(buf, PFM_PLM0|PFM_PLM3); 280 | if (!ret) 281 | printf("%s\n", buf); 282 | } 283 | } 284 | } else { 285 | snprintf(buf, sizeof(buf)-1, "%s::%s", pinfo.name, info->name); 286 | buf[sizeof(buf)-1] = '\0'; 287 | 288 | ret = options.encode ? print_codes(buf, PFM_PLM0|PFM_PLM3, pinfo.max_encoding) : 0; 289 | if (!ret) 290 | printf("%s\n", buf); 291 | } 292 | end: 293 | free(ainfo); 294 | } 295 | 296 | static void 297 | show_event_info_compact(pfm_event_info_t *info) 298 | { 299 | pfm_event_attr_info_t ainfo; 300 | pfm_pmu_info_t pinfo; 301 | char buf[MAXBUF]; 302 | int i, ret, um = 0; 303 | 304 | memset(&ainfo, 0, sizeof(ainfo)); 305 | memset(&pinfo, 0, sizeof(pinfo)); 306 | 307 | pinfo.size = sizeof(pinfo); 308 | ainfo.size = sizeof(ainfo); 309 | 310 | ret = pfm_get_pmu_info(info->pmu, &pinfo); 311 | if (ret != PFM_SUCCESS) { 312 | fprintf(stderr, "cannot get pmu info: %s", pfm_strerror(ret)); 313 | abort(); 314 | } 315 | 316 | pfm_for_each_event_attr(i, info) { 317 | ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo); 318 | if (ret != PFM_SUCCESS) { 319 | fprintf(stderr, "cannot get attribute info: %s", pfm_strerror(ret)); 320 | abort(); 321 | } 322 | 323 | if (ainfo.type != PFM_ATTR_UMASK) 324 | continue; 325 | 326 | if (!match_ufilters(&ainfo)) 327 | continue; 328 | 329 | snprintf(buf, sizeof(buf)-1, "%s::%s:%s", pinfo.name, info->name, ainfo.name); 330 | buf[sizeof(buf)-1] = '\0'; 331 | 332 | ret = 0; 333 | if (options.encode) { 334 | ret = print_codes(buf, PFM_PLM0|PFM_PLM3, pinfo.max_encoding); 335 | } 336 | if (!ret) { 337 | printf("%s", buf); 338 | if (options.desc) { 339 | printf("%s", options.csv_sep); 340 | printf("\"%s. %s.\"", info->desc, ainfo.desc); 341 | } 342 | putchar('\n'); 343 | } 344 | um++; 345 | } 346 | if (um == 0) { 347 | if (!match_efilters(info)) 348 | return; 349 | 350 | snprintf(buf, sizeof(buf)-1, "%s::%s", pinfo.name, info->name); 351 | buf[sizeof(buf)-1] = '\0'; 352 | if (options.encode) { 353 | ret = print_codes(buf, PFM_PLM0|PFM_PLM3, pinfo.max_encoding); 354 | if (ret) 355 | return; 356 | } 357 | printf("%s", buf); 358 | if (options.desc) { 359 | printf("%s", options.csv_sep); 360 | printf("\"%s.\"", info->desc); 361 | } 362 | putchar('\n'); 363 | } 364 | } 365 | 366 | int compare_codes(const void *a, const void *b) 367 | { 368 | const code_info_t *aa = a; 369 | const code_info_t *bb = b; 370 | uint64_t m = options.mask; 371 | 372 | if ((aa->code & m) < (bb->code &m)) 373 | return -1; 374 | if ((aa->code & m) == (bb->code & m)) 375 | return 0; 376 | return 1; 377 | } 378 | 379 | static void 380 | print_event_flags(pfm_event_info_t *info) 381 | { 382 | int n = 0; 383 | 384 | if (info->is_precise) { 385 | printf("[precise] "); 386 | n++; 387 | } 388 | if (!n) 389 | printf("None"); 390 | } 391 | 392 | static void 393 | print_attr_flags(pfm_event_attr_info_t *info) 394 | { 395 | int n = 0; 396 | 397 | if (info->is_dfl) { 398 | printf("[default] "); 399 | n++; 400 | } 401 | 402 | if (info->is_precise) { 403 | printf("[precise] "); 404 | n++; 405 | } 406 | 407 | if (!n) 408 | printf("None "); 409 | } 410 | 411 | 412 | static void 413 | show_event_info(pfm_event_info_t *info) 414 | { 415 | pfm_event_attr_info_t ainfo; 416 | pfm_pmu_info_t pinfo; 417 | int mod = 0, um = 0; 418 | int i, ret; 419 | const char *src; 420 | 421 | memset(&ainfo, 0, sizeof(ainfo)); 422 | memset(&pinfo, 0, sizeof(pinfo)); 423 | 424 | pinfo.size = sizeof(pinfo); 425 | ainfo.size = sizeof(ainfo); 426 | 427 | if (!match_efilters(info)) 428 | return; 429 | ret = pfm_get_pmu_info(info->pmu, &pinfo); 430 | if (ret) { 431 | fprintf(stderr, "cannot get pmu info: %s", pfm_strerror(ret)); 432 | abort(); 433 | } 434 | 435 | printf("#-----------------------------\n" 436 | "IDX : %d\n" 437 | "PMU name : %s (%s)\n" 438 | "Name : %s\n" 439 | "Equiv : %s\n", 440 | info->idx, 441 | pinfo.name, 442 | pinfo.desc, 443 | info->name, 444 | info->equiv ? info->equiv : "None"); 445 | 446 | printf("Flags : "); 447 | print_event_flags(info); 448 | putchar('\n'); 449 | 450 | printf("Desc : %s\n", info->desc ? info->desc : "no description available"); 451 | printf("Code : 0x%"PRIx64"\n", info->code); 452 | 453 | pfm_for_each_event_attr(i, info) { 454 | ret = pfm_get_event_attr_info(info->idx, i, options.os, &ainfo); 455 | if (ret != PFM_SUCCESS) { 456 | fprintf(stderr, "cannot retrieve event %s attribute info: %s", info->name, pfm_strerror(ret)); 457 | abort(); 458 | } 459 | 460 | if (ainfo.ctrl >= PFM_ATTR_CTRL_MAX) { 461 | fprintf(stderr, "event: %s has unsupported attribute source %d", info->name, ainfo.ctrl); 462 | ainfo.ctrl = PFM_ATTR_CTRL_UNKNOWN; 463 | } 464 | src = srcs[ainfo.ctrl]; 465 | switch(ainfo.type) { 466 | case PFM_ATTR_UMASK: 467 | if (!match_ufilters(&ainfo)) 468 | continue; 469 | 470 | printf("Umask-%02u : 0x%02"PRIx64" : %s : [%s] : ", 471 | um, 472 | ainfo.code, 473 | src, 474 | ainfo.name); 475 | 476 | print_attr_flags(&ainfo); 477 | 478 | putchar(':'); 479 | 480 | if (ainfo.equiv) 481 | printf(" Alias to %s", ainfo.equiv); 482 | else 483 | printf(" %s", ainfo.desc); 484 | 485 | putchar('\n'); 486 | um++; 487 | break; 488 | case PFM_ATTR_MOD_BOOL: 489 | printf("Modif-%02u : 0x%02"PRIx64" : %s : [%s] : %s (boolean)\n", mod, ainfo.code, src, ainfo.name, ainfo.desc); 490 | mod++; 491 | break; 492 | case PFM_ATTR_MOD_INTEGER: 493 | printf("Modif-%02u : 0x%02"PRIx64" : %s : [%s] : %s (integer)\n", mod, ainfo.code, src, ainfo.name, ainfo.desc); 494 | mod++; 495 | break; 496 | default: 497 | printf("Attr-%02u : 0x%02"PRIx64" : %s : [%s] : %s\n", i, ainfo.code, ainfo.name, src, ainfo.desc); 498 | } 499 | } 500 | } 501 | 502 | 503 | static int 504 | show_info(char *event, regex_t *preg) 505 | { 506 | pfm_pmu_info_t pinfo; 507 | pfm_event_info_t info; 508 | int i, j, ret, match = 0, pname; 509 | size_t len, l = 0; 510 | char *fullname = NULL; 511 | 512 | memset(&pinfo, 0, sizeof(pinfo)); 513 | memset(&info, 0, sizeof(info)); 514 | 515 | pinfo.size = sizeof(pinfo); 516 | info.size = sizeof(info); 517 | 518 | pname = event_has_pname(event); 519 | 520 | /* 521 | * scan all supported events, incl. those 522 | * from undetected PMU models 523 | */ 524 | pfm_for_all_pmus(j) { 525 | 526 | ret = pfm_get_pmu_info(j, &pinfo); 527 | if (ret != PFM_SUCCESS) 528 | continue; 529 | 530 | /* no pmu prefix, just look for detected PMU models */ 531 | if (!pname && !pinfo.is_present) 532 | continue; 533 | 534 | for (i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) { 535 | ret = pfm_get_event_info(i, options.os, &info); 536 | if (ret != PFM_SUCCESS) { 537 | fprintf(stderr, "cannot get event info: %s", pfm_strerror(ret)); 538 | abort(); 539 | } 540 | 541 | len = strlen(info.name) + strlen(pinfo.name) + 1 + 2; 542 | if (len > l) { 543 | l = len; 544 | fullname = realloc(fullname, l); 545 | if (!fullname) { 546 | fprintf(stderr, "cannot allocate memory"); 547 | abort(); 548 | } 549 | } 550 | sprintf(fullname, "%s::%s", pinfo.name, info.name); 551 | 552 | if (regexec(preg, fullname, 0, NULL, 0) == 0) { 553 | if (options.compact) 554 | if (options.combo) 555 | show_event_info_combo(&info); 556 | else 557 | show_event_info_compact(&info); 558 | else 559 | show_event_info(&info); 560 | match++; 561 | } 562 | } 563 | } 564 | if (fullname) 565 | free(fullname); 566 | 567 | return match; 568 | } 569 | 570 | static int 571 | show_info_sorted(char *event, regex_t *preg) 572 | { 573 | pfm_pmu_info_t pinfo; 574 | pfm_event_info_t info; 575 | unsigned int j; 576 | int i, ret, n, match = 0; 577 | size_t len, l = 0; 578 | char *fullname = NULL; 579 | code_info_t *codes; 580 | 581 | memset(&pinfo, 0, sizeof(pinfo)); 582 | memset(&info, 0, sizeof(info)); 583 | 584 | pinfo.size = sizeof(pinfo); 585 | info.size = sizeof(info); 586 | 587 | pfm_for_all_pmus(j) { 588 | 589 | ret = pfm_get_pmu_info(j, &pinfo); 590 | if (ret != PFM_SUCCESS) 591 | continue; 592 | 593 | codes = malloc(pinfo.nevents * sizeof(*codes)); 594 | if (!codes) { 595 | fprintf(stderr, "cannot allocate memory\n"); 596 | abort(); 597 | } 598 | 599 | /* scans all supported events */ 600 | n = 0; 601 | for (i = pinfo.first_event; i != -1; i = pfm_get_event_next(i)) { 602 | 603 | ret = pfm_get_event_info(i, options.os, &info); 604 | if (ret != PFM_SUCCESS) { 605 | fprintf(stderr, "cannot gt event info: %s", pfm_strerror(ret)); 606 | abort(); 607 | } 608 | 609 | if (info.pmu != j) 610 | continue; 611 | 612 | codes[n].idx = info.idx; 613 | codes[n].code = info.code; 614 | n++; 615 | } 616 | qsort(codes, n, sizeof(*codes), compare_codes); 617 | for(i=0; i < n; i++) { 618 | ret = pfm_get_event_info(codes[i].idx, options.os, &info); 619 | if (ret != PFM_SUCCESS) { 620 | fprintf(stderr, "cannot get event info: %s", pfm_strerror(ret)); 621 | abort(); 622 | } 623 | 624 | len = strlen(info.name) + strlen(pinfo.name) + 1 + 2; 625 | if (len > l) { 626 | l = len; 627 | fullname = realloc(fullname, l); 628 | if (!fullname) { 629 | fprintf(stderr, "cannot allocate memory"); 630 | abort(); 631 | } 632 | } 633 | sprintf(fullname, "%s::%s", pinfo.name, info.name); 634 | 635 | if (regexec(preg, fullname, 0, NULL, 0) == 0) { 636 | if (options.compact) 637 | show_event_info_compact(&info); 638 | else 639 | show_event_info(&info); 640 | match++; 641 | } 642 | } 643 | free(codes); 644 | } 645 | if (fullname) 646 | free(fullname); 647 | 648 | return match; 649 | } 650 | 651 | static void 652 | usage(void) 653 | { 654 | printf("showevtinfo [-L] [-E] [-h] [-s] [-m mask]\n" 655 | "-L\t\tlist one event per line (compact mode)\n" 656 | "-E\t\tlist one event per line with encoding (compact mode)\n" 657 | "-M\t\tdisplay all valid unit masks combination (use with -L or -E)\n" 658 | "-h\t\tget help\n" 659 | "-s\t\tsort event by PMU and by code based on -m mask\n" 660 | "-l\t\tmaximum number of umasks to list all combinations (default: %d)\n" 661 | "-F\t\tshow only events and attributes with certain flags (precise,...)\n" 662 | "-m mask\t\thexadecimal event code mask, bits to match when sorting\n" 663 | "-x sep\t\tuse sep as field separator in compact mode\n" 664 | "-D\t\t\tprint event description in compact mode\n" 665 | "-O os\t\tshow attributes for the specific operating system\n", 666 | COMBO_MAX); 667 | } 668 | 669 | /* 670 | * keep: [pmu::]event 671 | * drop everything else 672 | */ 673 | static void 674 | drop_event_attributes(char *str) 675 | { 676 | char *p; 677 | 678 | p = strchr(str, ':'); 679 | if (!p) 680 | return; 681 | 682 | str = p+1; 683 | /* keep PMU name */ 684 | if (*str == ':') 685 | str++; 686 | 687 | /* stop string at 1st attribute */ 688 | p = strchr(str, ':'); 689 | if (p) 690 | *p = '\0'; 691 | } 692 | 693 | #define EVENT_FLAGS(n, f, l) { .name = n, .ebit = f, .ubit = l } 694 | struct attr_flags { 695 | const char *name; 696 | int ebit; /* bit position in pfm_event_info_t.flags, -1 means ignore */ 697 | int ubit; /* bit position in pfm_event_attr_info_t.flags, -1 means ignore */ 698 | }; 699 | 700 | static const struct attr_flags event_flags[]={ 701 | EVENT_FLAGS("precise", 0, 1), 702 | EVENT_FLAGS("pebs", 0, 1), 703 | EVENT_FLAGS("default", -1, 0), 704 | EVENT_FLAGS("dfl", -1, 0), 705 | EVENT_FLAGS(NULL, 0, 0) 706 | }; 707 | 708 | static void 709 | parse_filters(char *arg) 710 | { 711 | const struct attr_flags *attr; 712 | char *p; 713 | 714 | while (arg) { 715 | p = strchr(arg, ','); 716 | if (p) 717 | *p++ = 0; 718 | 719 | for (attr = event_flags; attr->name; attr++) { 720 | if (!strcasecmp(attr->name, arg)) { 721 | switch(attr->ebit) { 722 | case 0: 723 | options.efilter.is_precise = 1; 724 | break; 725 | case -1: 726 | break; 727 | default: 728 | fprintf(stderr, "unknown event flag %d", attr->ebit); 729 | abort(); 730 | } 731 | switch (attr->ubit) { 732 | case 0: 733 | options.ufilter.is_dfl = 1; 734 | break; 735 | case 1: 736 | options.ufilter.is_precise = 1; 737 | break; 738 | case -1: 739 | break; 740 | default: 741 | fprintf(stderr, "unknown umaks flag %d", attr->ubit); 742 | abort(); 743 | } 744 | break; 745 | } 746 | } 747 | arg = p; 748 | } 749 | } 750 | 751 | static const struct { 752 | char *name; 753 | pfm_os_t os; 754 | } supported_oses[]={ 755 | { .name = "none", .os = PFM_OS_NONE }, 756 | { .name = "raw", .os = PFM_OS_NONE }, 757 | { .name = "pmu", .os = PFM_OS_NONE }, 758 | 759 | { .name = "perf", .os = PFM_OS_PERF_EVENT}, 760 | { .name = "perf_ext", .os = PFM_OS_PERF_EVENT_EXT}, 761 | { .name = NULL, } 762 | }; 763 | 764 | static const char *pmu_types[]={ 765 | "unknown type", 766 | "core", 767 | "uncore", 768 | "OS generic", 769 | }; 770 | 771 | static void 772 | setup_os(char *ostr) 773 | { 774 | int i; 775 | 776 | for (i = 0; supported_oses[i].name; i++) { 777 | if (!strcmp(supported_oses[i].name, ostr)) { 778 | options.os = supported_oses[i].os; 779 | return; 780 | } 781 | } 782 | fprintf(stderr, "unknown OS layer %s, choose from:", ostr); 783 | for (i = 0; supported_oses[i].name; i++) { 784 | if (i) 785 | fputc(',', stderr); 786 | fprintf(stderr, " %s", supported_oses[i].name); 787 | } 788 | fputc('\n', stderr); 789 | exit(1); 790 | } 791 | 792 | int 793 | main(int argc, char **argv) 794 | { 795 | static char *argv_all[2] = { ".*", NULL }; 796 | pfm_pmu_info_t pinfo; 797 | char *endptr = NULL; 798 | char default_sep[2] = "\t"; 799 | char *ostr = NULL; 800 | char **args; 801 | int i, match; 802 | regex_t preg; 803 | int ret, c; 804 | 805 | memset(&pinfo, 0, sizeof(pinfo)); 806 | 807 | pinfo.size = sizeof(pinfo); 808 | 809 | while ((c=getopt(argc, argv,"hELsm:Ml:F:x:DO:")) != -1) { 810 | switch(c) { 811 | case 'L': 812 | options.compact = 1; 813 | break; 814 | case 'F': 815 | parse_filters(optarg); 816 | break; 817 | case 'E': 818 | options.compact = 1; 819 | options.encode = 1; 820 | break; 821 | case 'M': 822 | options.combo = 1; 823 | break; 824 | case 's': 825 | options.sort = 1; 826 | break; 827 | case 'D': 828 | options.desc = 1; 829 | break; 830 | case 'l': 831 | options.combo_lim = atoi(optarg); 832 | break; 833 | case 'x': 834 | options.csv_sep = optarg; 835 | break; 836 | case 'O': 837 | ostr = optarg; 838 | break; 839 | case 'm': 840 | options.mask = strtoull(optarg, &endptr, 16); 841 | if (*endptr) { 842 | fprintf(stderr, "mask must be in hexadecimal\n"); 843 | abort(); 844 | } 845 | break; 846 | case 'h': 847 | usage(); 848 | exit(0); 849 | default: 850 | fprintf(stderr, "unknown option error"); 851 | abort(); 852 | } 853 | } 854 | /* to allow encoding of events from non detected PMU models */ 855 | ret = set_env_var("LIBPFM_ENCODE_INACTIVE", "1", 1); 856 | if (ret != PFM_SUCCESS) { 857 | fprintf(stderr, "cannot force inactive encoding"); 858 | abort(); 859 | } 860 | 861 | 862 | ret = pfm_initialize(); 863 | if (ret != PFM_SUCCESS) { 864 | fprintf(stderr, "cannot initialize libpfm: %s", pfm_strerror(ret)); 865 | abort(); 866 | } 867 | 868 | if (options.mask == 0) 869 | options.mask = ~0; 870 | 871 | if (optind == argc) { 872 | args = argv_all; 873 | } else { 874 | args = argv + optind; 875 | } 876 | if (!options.csv_sep) 877 | options.csv_sep = default_sep; 878 | 879 | /* avoid combinatorial explosion */ 880 | if (options.combo_lim == 0) 881 | options.combo_lim = COMBO_MAX; 882 | 883 | if (ostr) 884 | setup_os(ostr); 885 | else 886 | options.os = PFM_OS_NONE; 887 | 888 | if (!options.compact) { 889 | int total_supported_events = 0; 890 | int total_available_events = 0; 891 | 892 | printf("Supported PMU models:\n"); 893 | pfm_for_all_pmus(i) { 894 | ret = pfm_get_pmu_info(i, &pinfo); 895 | if (ret != PFM_SUCCESS) 896 | continue; 897 | 898 | printf("\t[%d, %s, \"%s\"]\n", i, pinfo.name, pinfo.desc); 899 | } 900 | 901 | printf("Detected PMU models:\n"); 902 | pfm_for_all_pmus(i) { 903 | ret = pfm_get_pmu_info(i, &pinfo); 904 | if (ret != PFM_SUCCESS) 905 | continue; 906 | 907 | if (pinfo.is_present) { 908 | if (pinfo.type >= PFM_PMU_TYPE_MAX) 909 | pinfo.type = PFM_PMU_TYPE_UNKNOWN; 910 | 911 | printf("\t[%d, %s, \"%s\", %d events, %d max encoding, %d counters, %s PMU]\n", 912 | i, 913 | pinfo.name, 914 | pinfo.desc, 915 | pinfo.nevents, 916 | pinfo.max_encoding, 917 | pinfo.num_cntrs + pinfo.num_fixed_cntrs, 918 | pmu_types[pinfo.type]); 919 | 920 | total_supported_events += pinfo.nevents; 921 | } 922 | total_available_events += pinfo.nevents; 923 | } 924 | printf("Total events: %d available, %d supported\n", total_available_events, total_supported_events); 925 | } 926 | 927 | while(*args) { 928 | /* drop umasks and modifiers */ 929 | drop_event_attributes(*args); 930 | if (regcomp(&preg, *args, REG_ICASE)) { 931 | fprintf(stderr, "error in regular expression for event \"%s\"", *argv); 932 | abort(); 933 | } 934 | 935 | if (options.sort) 936 | match = show_info_sorted(*args, &preg); 937 | else 938 | match = show_info(*args, &preg); 939 | 940 | if (match == 0) { 941 | fprintf(stderr, "event %s not found", *args); 942 | abort(); 943 | } 944 | 945 | args++; 946 | } 947 | 948 | regfree(&preg); 949 | 950 | pfm_terminate(); 951 | 952 | return 0; 953 | } 954 | -------------------------------------------------------------------------------- /include/numap.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_NB_NUMA_NODES 16 7 | #define MAX_NB_THREADS 24 8 | #define MAX_NB_CPUS 32 9 | 10 | #define ERROR_PERF_EVENT_OPEN -3 11 | #define ERROR_NUMAP_NOT_NUMA -4 12 | #define ERROR_NUMAP_ALREADY_STARTED -5 13 | #define ERROR_NUMAP_STOP_BEFORE_START -6 14 | #define ERROR_NUMAP_ARCH_NOT_SUPPORTED -7 15 | #define ERROR_NUMAP_READ_SAMPLING_ARCH_NOT_SUPPORTED -9 16 | #define ERROR_NUMAP_WRITE_SAMPLING_ARCH_NOT_SUPPORTED -10 17 | #define ERROR_PFM -11 18 | #define ERROR_READ -12 19 | 20 | #define rmb() asm volatile("lfence" ::: "memory") 21 | 22 | /** 23 | * Structure representing a measurement of counting the load of controlers. 24 | */ 25 | struct numap_counting_measure { 26 | char started; 27 | int nb_nodes; 28 | int is_valid[MAX_NB_NUMA_NODES]; 29 | long fd_reads[MAX_NB_NUMA_NODES]; 30 | long fd_writes[MAX_NB_NUMA_NODES]; 31 | long long reads_count[MAX_NB_NUMA_NODES]; 32 | long long writes_count[MAX_NB_NUMA_NODES]; 33 | }; 34 | 35 | /** 36 | * Structure representing a measurement of memory read or write sampling. 37 | */ 38 | struct numap_sampling_measure { 39 | 40 | /* 41 | * Fields to be written and/or read by user code. 42 | */ 43 | unsigned int nb_threads; 44 | unsigned int sampling_rate; 45 | unsigned int mmap_pages_count; // Must be power of two as specified in the man page of perf_event_open (mmap size should be 1+2^n pages) 46 | pid_t tids[MAX_NB_THREADS]; 47 | struct perf_event_mmap_page *metadata_pages_per_tid[MAX_NB_THREADS]; 48 | 49 | /* 50 | * Fields to be written and/or read by library code. 51 | */ 52 | size_t page_size; 53 | size_t mmap_len; 54 | char started; 55 | long fd_per_tid[MAX_NB_THREADS]; 56 | // overflow related fields 57 | void (*handler)(struct numap_sampling_measure*, int); // handler called each nb_refresh samples 58 | int total_samples; // after record, contains the total number of samples % nb_refresh 59 | int nb_refresh; // default value : 1000 60 | }; 61 | 62 | /** 63 | * Structure representing a read sample gathered with the library in 64 | * sampling mode. 65 | */ 66 | struct __attribute__ ((__packed__)) sample { 67 | uint64_t ip; 68 | uint64_t addr; 69 | uint64_t weight; 70 | union perf_mem_data_src data_src; 71 | }; 72 | 73 | /** 74 | * Structure representing a mmap sample gathered with the library in 75 | * sampling mode. 76 | */ 77 | struct __attribute__ ((__packed__)) mmap_sample { 78 | uint32_t pid; 79 | uint32_t tid; 80 | uint64_t addr; 81 | uint64_t len; 82 | uint64_t pgoff; 83 | char filename[100]; 84 | }; 85 | 86 | char* concat(const char *s1, const char *s2); 87 | 88 | /** 89 | * Numap initialization function 90 | */ 91 | int numap_init(void); 92 | 93 | /** 94 | * Memory counting. 95 | */ 96 | int numap_counting_init_measure(struct numap_counting_measure *measure); 97 | int numap_counting_start(struct numap_counting_measure *measure); 98 | int numap_counting_stop(struct numap_counting_measure *measure); 99 | 100 | /** 101 | * Memory read and write sampling. 102 | */ 103 | int numap_sampling_set_measure_handler(struct numap_sampling_measure *measure, void(*)(struct numap_sampling_measure*,int), int); 104 | int numap_sampling_init_measure(struct numap_sampling_measure *measure, int nb_threads, int sampling_rate, int mmap_pages_count); 105 | int numap_sampling_read_start_generic(struct numap_sampling_measure *measure, uint64_t sample_type); 106 | int numap_sampling_read_start(struct numap_sampling_measure *measure); 107 | int numap_sampling_read_stop(struct numap_sampling_measure *measure); 108 | int numap_sampling_read_print(struct numap_sampling_measure *measure, char print_samples); 109 | int numap_sampling_write_supported(); 110 | int numap_sampling_write_start_generic(struct numap_sampling_measure *measure, uint64_t sample_type); 111 | int numap_sampling_write_start(struct numap_sampling_measure *measure); 112 | int numap_sampling_write_stop(struct numap_sampling_measure *measure); 113 | int numap_sampling_write_print(struct numap_sampling_measure *measure, char print_samples); 114 | int numap_sampling_print(struct numap_sampling_measure *measure, char print_samples); 115 | int numap_sampling_end(struct numap_sampling_measure *measure); 116 | int numap_sampling_resume(struct numap_sampling_measure *measure); 117 | 118 | /** 119 | * Error handling. 120 | */ 121 | const char *numap_error_message(int error); 122 | 123 | /** 124 | * Functions to analyse results 125 | */ 126 | int is_served_by_local_cache1(union perf_mem_data_src data_src); 127 | int is_served_by_local_cache2(union perf_mem_data_src data_src); 128 | int is_served_by_local_cache3(union perf_mem_data_src data_src); 129 | int is_served_by_local_lfb(union perf_mem_data_src data_src); 130 | int is_served_by_local_cache(union perf_mem_data_src data_src); 131 | int is_served_by_local_memory(union perf_mem_data_src data_src); 132 | int is_served_by_remote_cache_or_local_memory(union perf_mem_data_src data_src); 133 | int is_served_by_remote_memory(union perf_mem_data_src data_src); 134 | int is_served_by_local_NA_miss(union perf_mem_data_src data_src); 135 | char *get_data_src_opcode(union perf_mem_data_src data_src); 136 | char *get_data_src_level(union perf_mem_data_src data_src); 137 | -------------------------------------------------------------------------------- /include/numap_config.h.in: -------------------------------------------------------------------------------- 1 | #ifndef NUMAP_CONFIG_H 2 | #define NUMAP_CONFIG_H 3 | 4 | #define INSTALL_PREFIX @CMAKE_INSTALL_PREFIX@ 5 | #endif 6 | -------------------------------------------------------------------------------- /pkg-config.pc.cmake: -------------------------------------------------------------------------------- 1 | Name: ${PROJECT_NAME} 2 | Description: ${PROJECT_DESCRIPTION} 3 | Version: ${PROJECT_VERSION} 4 | Requires: ${PKG_CONFIG_REQUIRES} 5 | prefix=${CMAKE_INSTALL_PREFIX} 6 | includedir=${PKG_CONFIG_INCLUDEDIR} 7 | libdir=${PKG_CONFIG_LIBDIR} 8 | Libs: ${PKG_CONFIG_LIBS} 9 | Cflags: ${PKG_CONFIG_CFLAGS} 10 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | 3 | numap is a Linux library dedicated to memory profiling based on 4 | hardware performance monitoring unit (PMU). The main objective for the 5 | library is to provide high level abstraction for: 6 | 7 | - Cores load requests sampling 8 | - Cores store requests sampling 9 | 10 | ## Supported processors 11 | 12 | ### Intel processors with family_model information (decimal notation) 13 | 14 | - Nehalem (06_26, 06_30, 06_31, 06_46) 15 | - Sandy Bridge (06_42, 06_45) 16 | - Westmere (06_37, 06_44, 06_44) 17 | - Ivy Bridge (06_58, 06_62) 18 | - Haswell (06_60, 06_63, 06_69, 06_70) 19 | - Broadwell (06_61, 06_71, 06_79, 06_86) 20 | - Kaby Lake (06_142, 06_158) 21 | - Sky Lake (06_94, 06_78) 22 | - Cannon Lake (06-102) 23 | - Ice Lake (06_126) 24 | 25 | ### Not implemented Intel processors: 26 | 27 | - Knights Ferry (11_00) 28 | - Knights Corner (11_01) 29 | - Knights Mill (06_133) 30 | - Knights Landing (06_87) 31 | 32 | ### AMD processors 33 | 34 | - On going development 35 | 36 | ## Folders Organization 37 | 38 | - `examples`: contains some examples showing how to use numap. 39 | 40 | - `include`: contains numap headers 41 | 42 | - `src`: contains numap implementation files 43 | 44 | - `Makefile`: is a Makefile building both the library and the examples 45 | 46 | ## Dependencies 47 | 48 | - libpfm4 49 | - libnuma 50 | 51 | ## Howto: extend numap in ordre to take your processor model into account. 52 | 53 | 54 | ### Intro 55 | 56 | The goal is to tell numap which read/write events to use on a specific architecture. The `get_archi` function specifies for each architecture which events to use: 57 | 58 | ``` C 59 | switch(archi_id) { 60 | /* ... */ 61 | case CPU_MODEL(6, 158): 62 | case CPU_MODEL(6, 142): 63 | snprintf(arch->name, 256, "Kaby Lake micro arch"); 64 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 65 | snprintf(arch->sampling_write_event, 256, "MEM_INST_RETIRED:ALL_STORES"); 66 | break; 67 | ``` 68 | 69 | You can add a new architecture by adding a new case. 70 | 71 | ### Getting the correct info 72 | 73 | On the machine considered, type 74 | 75 | ``` 76 | less /proc/cpuinfo 77 | ``` 78 | 79 | This file contains info in the following form: 80 | 81 | ``` 82 | processor : 0 83 | vendor_id : GenuineIntel 84 | cpu family : 6 85 | model : 45 86 | model name : Intel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz 87 | stepping : 7 88 | microcode : 0x710 89 | cpu MHz : 1339.121 90 | cache size : 15360 KB 91 | physical id : 0 92 | siblings : 12 93 | core id : 0 94 | cpu cores : 6 95 | apicid : 0 96 | initial apicid : 0 97 | fpu : yes 98 | fpu_exception : yes 99 | cpuid level : 13 100 | wp : yes 101 | flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx lahf_lm ida arat epb xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid 102 | bogomips : 4599.76 103 | clflush size : 64 104 | cache_alignment : 64 105 | address sizes : 46 bits physical, 48 bits virtual 106 | power management: 107 | ``` 108 | 109 | Amongst this info, you are interested in the lines "cpu family" and 110 | "model". Using them, you can add a new case: 111 | 112 | ``` C 113 | case CPU_MODEL(cpu_family, model): 114 | ``` 115 | 116 | In our case, we get 117 | 118 | ``` C 119 | case CPU_MODEL(06, 45): 120 | ``` 121 | 122 | In the Intel documentations, this will be noted as 06_2DH (H for 123 | ... hexa) 124 | 125 | Now, open the Intel documentation called "64, IA, 32 Architectures 126 | Software Developer Manual", and search for the string FAMILY_MODEL (in 127 | our example 06_2D). This brings you, among others into a section of 128 | chapter 19. Chapter 19 is called Performance Monitoring Events. In our 129 | case, we find that 06_2DH is described in section 19.6 PERFORMANCE 130 | MONITORING EVENTS FOR 2ND GENERATION INTEL® CORETM I7-2XXX, INTEL® 131 | CORETM I5-2XXX, INTEL® CORETM I3-2XXX PROCESSOR SERIES 132 | 133 | 134 | In the table provided in this section, find the lines corresponding to 135 | the requried info. In particular, in this example, we fill in the 136 | values for sampling_read_event and sampling_write_event. We leave out 137 | thos for counting_read_event and counting_write_event 138 | 139 | #### .sampling_read_event 140 | 141 | For the sampling of memory reads, you need something like: 142 | 143 | ``` 144 | | CDH | 01H | MEM_TRANS_RETIRED.LOAD_LATENCY | Randomly sampled loads whose latency is above a user defined threshold. A small fraction of the overall loads are sampled due to randomization. PMC3 only. | Specify threshold in MSR 3F6H. | 145 | ``` 146 | 147 | 148 | #### .sampling_write_event 149 | 150 | ``` 151 | | CDH | 02H | MEM_TRANS_RETIRED.PRECISE_STORE | Sample stores and collect precise store operation via PEBS record. PMC3 only. | See Section 18.9.4.3. | 152 | ``` 153 | 154 | ### Filling up numap's struct archi for your machine 155 | 156 | On some architectures, the info provided in the general documentation 157 | is INCORRECT. To get the correct naming of the sampling_read_event, 158 | one can use the `examples/showevtinfo` program provided by numap. This 159 | program prints the list of available events. 160 | 161 | For our example architecture, we find that the exact latency-fixing 162 | parameter is called LATENCY_ABOVE_THRESHOLD instead of LOAD_LATENCY. 163 | So be it! 164 | 165 | 166 | Thus, we modify get_archi to add these lines: 167 | 168 | ``` C 169 | case CPU_MODEL(6, 45): 170 | snprintf(arch->name, 256, "Sandy Bridge micro arch"); 171 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LATENCY_ABOVE_THRESHOLD:ldlat=3"); 172 | snprintf(arch->sampling_write_event, 256, "MEM_TRANS_RETIRED:PRECISE_STORE"); 173 | break; 174 | ``` 175 | 176 | ### Testing 177 | 178 | When this is done go to numap's root directory, type 179 | 180 | ``` 181 | $ cmake 182 | $ make 183 | ``` 184 | 185 | Then try the example binary in examples: 186 | 187 | ``` 188 | $ examples/example 189 | ``` 190 | 191 | This program should output something looking like: 192 | 193 | ``` 194 | root@taurus-8 ~/numap:-)examples/example 195 | 196 | Starting memory read sampling 197 | Memory read sampling results 198 | 199 | head = 192200 compared to max = 266240 200 | Thread 0: 4805 samples 201 | Thread 0: 4805 local cache 1 100.000% 202 | Thread 0: 0 local cache 2 0.000% 203 | Thread 0: 0 local cache 3 0.000% 204 | Thread 0: 0 local cache LFB 0.000% 205 | Thread 0: 0 local memory 0.000% 206 | Thread 0: 0 remote cache or local memory 0.000% 207 | Thread 0: 0 remote memory 0.000% 208 | Thread 0: 0 unknown l3 miss 0.000% 209 | 210 | head = 193240 compared to max = 266240 211 | Thread 1: 4831 samples 212 | Thread 1: 4831 local cache 1 100.000% 213 | Thread 1: 0 local cache 2 0.000% 214 | Thread 1: 0 local cache 3 0.000% 215 | Thread 1: 0 local cache LFB 0.000% 216 | Thread 1: 0 local memory 0.000% 217 | Thread 1: 0 remote cache or local memory 0.000% 218 | Thread 1: 0 remote memory 0.000% 219 | Thread 1: 0 unknown l3 miss 0.000% 220 | 221 | Starting memory write sampling 222 | Memory write sampling results 223 | 224 | head = 262112 compared to max = 266240 225 | Thread 0: 6452 samples 226 | Thread 0: 6442 local cache 1 99.845% 227 | Thread 0: 0 local cache 2 0.000% 228 | Thread 0: 0 local cache 3 0.000% 229 | Thread 0: 0 local cache LFB 0.000% 230 | Thread 0: 0 local memory 0.000% 231 | Thread 0: 0 remote cache or local memory 0.000% 232 | Thread 0: 0 remote memory 0.000% 233 | Thread 0: 0 unknown l3 miss 0.000% 234 | 235 | head = 262136 compared to max = 266240 236 | Thread 1: 6451 samples 237 | Thread 1: 6436 local cache 1 99.767% 238 | Thread 1: 0 local cache 2 0.000% 239 | Thread 1: 0 local cache 3 0.000% 240 | Thread 1: 0 local cache LFB 0.000% 241 | Thread 1: 0 local memory 0.000% 242 | Thread 1: 0 remote cache or local memory 0.000% 243 | Thread 1: 0 remote memory 0.000% 244 | Thread 1: 0 unknown l3 miss 0.000% 245 | ``` 246 | 247 | Congrats, numap is set up for your machine! 248 | 249 | Don't forget to push your modifications to github of course :) 250 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | overhead 3 | libnumap.so 4 | libnumap.a -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(numap SHARED 2 | numap.c 3 | numap_analyse.c 4 | ) 5 | target_link_libraries(numap LINK_PUBLIC numa pfm) 6 | 7 | configure_file ( 8 | "${PROJECT_SOURCE_DIR}/include/numap_config.h.in" 9 | "${PROJECT_BINARY_DIR}/include/numap_config.h" 10 | ) 11 | 12 | install(TARGETS numap DESTINATION lib) 13 | install ( 14 | DIRECTORY ${CMAKE_SOURCE_DIR}/include/ 15 | DESTINATION include 16 | FILES_MATCHING PATTERN "*.h") 17 | -------------------------------------------------------------------------------- /src/numap.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "numap.h" 22 | 23 | #define PERF_EVENT_MLOCK_KB_FILE "/proc/sys/kernel/perf_event_mlock_kb" 24 | 25 | #define NOT_SUPPORTED "NOT_SUPPORTED" 26 | 27 | struct archi { 28 | unsigned int id; 29 | char name[256]; 30 | char sampling_read_event[256]; 31 | char sampling_write_event[256]; 32 | char counting_read_event[256]; 33 | char counting_write_event[256]; 34 | }; 35 | 36 | /* If your Intel CPU is not supported by Numap, you need to add a new architecture: 37 | * .id can be found by running lscpu (CPU Familly | Model <<8) 38 | * .sampling_read_event and .sampling_write_event can be found in the Intel Software Developpers Manuel (Chapter 19) 39 | */ 40 | 41 | 42 | #define CPU_MODEL(family, model) ((family) | (model) <<8) 43 | 44 | static void get_archi(unsigned int archi_id, struct archi * arch) { 45 | arch->id=archi_id; 46 | 47 | snprintf(arch->name, 256, "Unknown architecture"); 48 | snprintf(arch->sampling_read_event, 256, NOT_SUPPORTED); 49 | snprintf(arch->sampling_write_event, 256, NOT_SUPPORTED); 50 | snprintf(arch->counting_read_event, 256, NOT_SUPPORTED); 51 | snprintf(arch->counting_write_event, 256, NOT_SUPPORTED); 52 | 53 | switch (archi_id) { 54 | case CPU_MODEL(6, 151): 55 | case CPU_MODEL(6, 154): 56 | snprintf(arch->name, 256, "Alder Lake micro arch"); 57 | /* Not tested. Let's assume these events are the same as the previous cpu generation */ 58 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 59 | snprintf(arch->sampling_write_event, 256, "MEM_INST_RETIRED:ALL_STORES"); 60 | break; 61 | 62 | case CPU_MODEL(6, 167): 63 | snprintf(arch->name, 256, "Rocket Lake micro arch"); 64 | /* Not tested. Let's assume these events are the same as the previous cpu generation */ 65 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 66 | snprintf(arch->sampling_write_event, 256, "MEM_INST_RETIRED:ALL_STORES"); 67 | break; 68 | 69 | case CPU_MODEL(6, 143): 70 | snprintf(arch->name, 256, "Sapphire Rapids micro arch"); 71 | /* Not tested. Let's assume these events are the same as the previous cpu generation */ 72 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 73 | snprintf(arch->sampling_write_event, 256, "MEM_INST_RETIRED:ALL_STORES"); 74 | break; 75 | 76 | case CPU_MODEL(6, 141): 77 | case CPU_MODEL(6, 140): 78 | snprintf(arch->name, 256, "Tiger Lake micro arch"); 79 | /* Not tested. Let's assume these events are the same as the previous cpu generation */ 80 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 81 | snprintf(arch->sampling_write_event, 256, "MEM_INST_RETIRED:ALL_STORES"); 82 | break; 83 | 84 | case CPU_MODEL(6, 106): 85 | case CPU_MODEL(6, 125): 86 | case CPU_MODEL(6, 126): 87 | snprintf(arch->name, 256, "Ice Lake micro arch"); 88 | /* Not tested. Let's assume these events are the same as the previous cpu generation */ 89 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 90 | snprintf(arch->sampling_write_event, 256, "MEM_INST_RETIRED:ALL_STORES"); 91 | break; 92 | 93 | case CPU_MODEL(6, 102): 94 | snprintf(arch->name, 256, "Cannon Lake micro arch"); 95 | /* Not tested. Let's assume these events are the same as the previous cpu generation */ 96 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 97 | snprintf(arch->sampling_write_event, 256, "MEM_INST_RETIRED:ALL_STORES"); 98 | break; 99 | 100 | case CPU_MODEL(6, 158): 101 | case CPU_MODEL(6, 142): 102 | snprintf(arch->name, 256, "Kaby Lake micro arch"); 103 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 104 | snprintf(arch->sampling_write_event, 256, "MEM_INST_RETIRED:ALL_STORES"); 105 | break; 106 | 107 | case CPU_MODEL(6, 94): 108 | case CPU_MODEL(6, 78): 109 | case CPU_MODEL(6, 85): 110 | snprintf(arch->name, 256, "Skylake micro arch"); 111 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 112 | snprintf(arch->sampling_write_event, 256, "MEM_UOPS_RETIRED:ALL_STORES"); 113 | break; 114 | 115 | case CPU_MODEL(6, 79): 116 | case CPU_MODEL(6, 86): 117 | case CPU_MODEL(6, 71): 118 | case CPU_MODEL(6, 61): 119 | snprintf(arch->name, 256, "Broadwell micro arch"); 120 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 121 | snprintf(arch->sampling_write_event, 256, "MEM_UOPS_RETIRED:ALL_STORES"); 122 | break; 123 | 124 | case CPU_MODEL(6, 60): 125 | case CPU_MODEL(6, 63): 126 | case CPU_MODEL(6, 69): 127 | case CPU_MODEL(6, 70): 128 | snprintf(arch->name, 256, "Haswell micro arch"); 129 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 130 | snprintf(arch->sampling_write_event, 256, "MEM_UOPS_RETIRED:ALL_STORES"); 131 | break; 132 | 133 | case CPU_MODEL(6, 58): 134 | case CPU_MODEL(6, 62): 135 | snprintf(arch->name, 256, "Ivy Bridge micro arch"); 136 | // NOTE: in the Intel SDM, read sampling event is MEM_TRANS_RETIRED:LOAD_LATENCY. 137 | // In practice this event does not work. As a consequence we use the event below 138 | // which is the one used by perf mem record and reported by the pfm library 139 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LATENCY_ABOVE_THRESHOLD:ldlat=3"); 140 | snprintf(arch->sampling_write_event, 256, "MEM_TRANS_RETIRED:PRECISE_STORE"); 141 | //snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LOAD_LATENCY:ldlat=3"); 142 | //snprintf(arch->sampling_write_event, 256, "MEM_TRANS_RETIRED:PRECISE_STORE"); 143 | break; 144 | 145 | case CPU_MODEL(6, 42): 146 | case CPU_MODEL(6, 45): 147 | snprintf(arch->name, 256, "Sandy Bridge micro arch"); 148 | // NOTE: in the Intel SDM, read sampling event is MEM_TRANS_RETIRED:LOAD_LATENCY. 149 | // In practice this event does not work. As a consequence we use the event below 150 | // which is the one used by perf mem record and reported by the pfm library 151 | snprintf(arch->sampling_read_event, 256, "MEM_TRANS_RETIRED:LATENCY_ABOVE_THRESHOLD:ldlat=3"); 152 | snprintf(arch->sampling_write_event, 256, "MEM_TRANS_RETIRED:PRECISE_STORE"); 153 | break; 154 | 155 | case CPU_MODEL(6,46): 156 | case CPU_MODEL(6,30): 157 | case CPU_MODEL(6,26): 158 | case CPU_MODEL(6,31): 159 | snprintf(arch->name, 256, "Nehalem micro arch"); 160 | snprintf(arch->sampling_read_event, 256, "MEM_INST_RETIRED:LATENCY_ABOVE_THRESHOLD:ldlat=3"); 161 | break; 162 | 163 | case CPU_MODEL(6, 44): 164 | case CPU_MODEL(6, 47): 165 | case CPU_MODEL(6, 37): 166 | snprintf(arch->name, 256, "Westmere micro arch"); 167 | snprintf(arch->sampling_read_event, 256, "MEM_INST_RETIRED:LATENCY_ABOVE_THRESHOLD:ldlat=3"); 168 | break; 169 | 170 | 171 | // Intel Xeon Phi CPUs (currently not supported) 172 | // I'm not sure if PEBS is available on these cpus 173 | case CPU_MODEL(6, 133): 174 | snprintf(arch->name, 256, "Knights Mill micro arch"); 175 | break; 176 | 177 | case CPU_MODEL(6, 87): 178 | snprintf(arch->name, 256, "Knights Landing micro arch"); 179 | break; 180 | 181 | // Old Intel Xeon Phi CPUs 182 | case CPU_MODEL(11, 0): 183 | snprintf(arch->name, 256, "Knights Ferry micro arch"); 184 | break; 185 | case CPU_MODEL(11, 1): 186 | snprintf(arch->name, 256, "Knights Corner micro arch"); 187 | break; 188 | 189 | // Some old Intel arch (will probably never be supported) 190 | case CPU_MODEL(15, 6): 191 | snprintf(arch->name, 256, "Netburst micro arch"); 192 | break; 193 | 194 | case CPU_MODEL(15, 4): 195 | case CPU_MODEL(15, 3): 196 | snprintf(arch->name, 256, "Prescott micro arch"); 197 | break; 198 | 199 | case CPU_MODEL(15, 2): 200 | snprintf(arch->name, 256, "Northwood micro arch"); 201 | break; 202 | case CPU_MODEL(15, 1): 203 | snprintf(arch->name, 256, "Willamette micro arch"); 204 | break; 205 | 206 | case CPU_MODEL(6, 29): 207 | case CPU_MODEL(6, 23): 208 | snprintf(arch->name, 256, "Penryn micro arch"); 209 | break; 210 | 211 | case CPU_MODEL(6, 15): 212 | case CPU_MODEL(6, 22): 213 | snprintf(arch->name, 256, "Core micro arch"); 214 | break; 215 | 216 | case CPU_MODEL(6, 14): 217 | snprintf(arch->name, 256, "Modified Pentium M micro arch"); 218 | break; 219 | 220 | case CPU_MODEL(6, 21): 221 | case CPU_MODEL(6, 13): 222 | case CPU_MODEL(6, 9): 223 | snprintf(arch->name, 256, "Pentium M micro arch"); 224 | break; 225 | 226 | // Intel Atom/ Celeron CPUs (will probably never be supported) 227 | case CPU_MODEL(6, 134): 228 | snprintf(arch->name, 256, "Tremont micro arch"); 229 | break; 230 | 231 | case CPU_MODEL(6, 122): 232 | snprintf(arch->name, 256, "Goldmont Plus micro arch"); 233 | break; 234 | 235 | case CPU_MODEL(6, 95): 236 | case CPU_MODEL(6, 92): 237 | snprintf(arch->name, 256, "Goldmont micro arch"); 238 | break; 239 | 240 | case CPU_MODEL(6, 76): 241 | snprintf(arch->name, 256, "Airmont micro arch"); 242 | break; 243 | 244 | case CPU_MODEL(6, 55): 245 | case CPU_MODEL(6, 74): 246 | case CPU_MODEL(6, 77): 247 | case CPU_MODEL(6, 93): 248 | snprintf(arch->name, 256, "Silvermont micro arch"); 249 | break; 250 | 251 | case CPU_MODEL(6, 39): 252 | case CPU_MODEL(6, 53): 253 | case CPU_MODEL(6, 54): 254 | snprintf(arch->name, 256, "Saltwell micro arch"); 255 | break; 256 | 257 | case CPU_MODEL(6, 28): 258 | case CPU_MODEL(6, 38): 259 | snprintf(arch->name, 256, "Bonnell micro arch"); 260 | break; 261 | } 262 | } 263 | 264 | unsigned char get_family(unsigned int archi_id) { 265 | return (archi_id >> (8*0)) & 0xff; 266 | } 267 | 268 | unsigned char get_model(unsigned int archi_id) { 269 | return (archi_id >> (8*1)) & 0xff; 270 | } 271 | 272 | /** 273 | * Globals in a shared lib are handled in such a way that each process 274 | * mapping the lib has its own copy of these globals. 275 | */ 276 | unsigned int nb_numa_nodes; 277 | int numa_node_to_cpu[MAX_NB_NUMA_NODES]; 278 | unsigned int perf_event_mlock_kb; 279 | struct archi *current_archi; 280 | char *model_name = NULL; 281 | int curr_err; 282 | 283 | struct link_fd_measure { 284 | struct link_fd_measure *next; 285 | int fd; 286 | struct numap_sampling_measure* measure; 287 | }; 288 | 289 | struct link_fd_measure *link_fd_measure = NULL; 290 | pthread_mutex_t link_fd_lock; 291 | 292 | /** 293 | * Special function called each time a process using the lib is 294 | * started. 295 | */ 296 | __attribute__((constructor)) void init(void) { 297 | 298 | int node; 299 | int cpu; 300 | 301 | // Supported_archs 302 | 303 | // Check architecture 304 | FILE *cpuinfo = fopen("/proc/cpuinfo", "rb"); 305 | char *arg = NULL; 306 | size_t size = 0; 307 | unsigned char family = 0; 308 | unsigned char model = 0; 309 | const char *family_string = "cpu family\t"; 310 | const char *model_string = "model\t"; 311 | const char *model_name_string = "model name\t"; 312 | while(getline(&arg, &size, cpuinfo) != -1 && (!family || !model || !model_name)) { 313 | if (strncmp(family_string, arg, strlen(family_string)) == 0) { 314 | strtok(arg, ":"); 315 | family = atoi(strtok(NULL, "\n")); 316 | } else if (strncmp(model_string, arg, strlen(model_string)) == 0) { 317 | strtok(arg, ":"); 318 | model = atoi(strtok(NULL, "\n")); 319 | } else if (strncmp(model_name_string, arg, strlen(model_name_string)) == 0) { 320 | strtok(arg, ":"); 321 | char * model_name_strtok = strtok(NULL, "\n"); 322 | int len = strlen(model_name_strtok); 323 | model_name = malloc((len + 1) * sizeof(char)); 324 | strcpy(model_name, model_name_strtok); 325 | } 326 | } 327 | free(arg); 328 | fclose(cpuinfo); 329 | current_archi = malloc(sizeof(struct archi)); 330 | get_archi(CPU_MODEL(family, model), current_archi); 331 | 332 | // Get numa configuration 333 | int available = numa_available(); 334 | if (available == -1) { 335 | nb_numa_nodes = -1; 336 | } else { 337 | nb_numa_nodes = numa_num_configured_nodes(); 338 | int nb_cpus = numa_num_configured_cpus(); 339 | for (node = 0; node < nb_numa_nodes; node++) { 340 | struct bitmask *mask = numa_allocate_cpumask(); 341 | numa_node_to_cpus(node, mask); 342 | numa_node_to_cpu[node] = -1; 343 | for (cpu = 0; cpu < nb_cpus; cpu++) { 344 | if (*(mask->maskp) & (1 << cpu)) { 345 | numa_node_to_cpu[node] = cpu; 346 | break; 347 | } 348 | } 349 | numa_bitmask_free(mask); 350 | } 351 | } 352 | 353 | // Get perf config 354 | FILE *f = fopen(PERF_EVENT_MLOCK_KB_FILE, "r"); 355 | if (!f) { 356 | fprintf(stderr, "Failed to open file: `%s'\n", PERF_EVENT_MLOCK_KB_FILE); 357 | exit(-1); 358 | } 359 | if (fscanf(f, "%u", &perf_event_mlock_kb) != 1) { 360 | fprintf(stderr, "Failed to read perf_event_mlock_kb from file: `%s'\n", PERF_EVENT_MLOCK_KB_FILE); 361 | fclose(f); 362 | exit(-1); 363 | } 364 | fclose(f); 365 | } 366 | 367 | char* concat(const char *s1, const char *s2) { 368 | char *result = malloc(strlen(s1) + strlen(s2) + 1); 369 | if (result == NULL) { 370 | return "malloc failed in concat\n"; 371 | } 372 | strcpy(result, s1); 373 | strcat(result, s2); 374 | return result; 375 | } 376 | 377 | char* build_string(const char *fmt, ...) { 378 | va_list args; 379 | va_start(args, fmt); 380 | char* result = NULL; 381 | vasprintf(&result, fmt, args); 382 | return result; 383 | } 384 | 385 | const char *numap_error_message(int error) { 386 | switch (error) { 387 | case ERROR_NUMAP_NOT_NUMA: 388 | return "libnumap: numa lib not available"; 389 | case ERROR_NUMAP_STOP_BEFORE_START: 390 | return "libnumap: stop called before start"; 391 | case ERROR_NUMAP_ALREADY_STARTED: 392 | return "libnumap: start called again before stop"; 393 | case ERROR_NUMAP_ARCH_NOT_SUPPORTED: 394 | return build_string("libnumap: architecture not supported: %s (family %d, model %d)", 395 | model_name, get_family(current_archi->id), get_model(current_archi->id)); 396 | case ERROR_NUMAP_READ_SAMPLING_ARCH_NOT_SUPPORTED: 397 | return build_string("libnumap: read sampling not supported on architecture: %s (family %d, model %d)", 398 | model_name, get_family(current_archi->id), get_model(current_archi->id)); 399 | case ERROR_NUMAP_WRITE_SAMPLING_ARCH_NOT_SUPPORTED: 400 | return build_string("libnumap: write sampling not supported on architecture: %s (family %d, model %d)", 401 | model_name, get_family(current_archi->id), get_model(current_archi->id)); 402 | case ERROR_PERF_EVENT_OPEN: 403 | return build_string("libnumap: error when calling perf_event_open: %s", strerror(errno)); 404 | case ERROR_PFM: 405 | return build_string("libnumap: error when initializing pfm: %s", pfm_strerror(curr_err)); 406 | case ERROR_READ: 407 | return "libnumap: error while trying to read counter"; 408 | default: 409 | return "libnumap: unknown error"; 410 | } 411 | } 412 | 413 | int set_signal_handler(void(*handler)(int, siginfo_t*,void*)) { 414 | struct sigaction sigoverflow; 415 | memset(&sigoverflow, 0, sizeof(struct sigaction)); 416 | sigoverflow.sa_sigaction = handler; 417 | sigoverflow.sa_flags = SA_SIGINFO; 418 | 419 | if (sigaction(SIGIO, &sigoverflow, NULL) < 0) 420 | { 421 | fprintf(stderr, "could not set up signal handler\n"); 422 | perror("sigaction"); 423 | exit(EXIT_FAILURE); 424 | } 425 | return 0; 426 | } 427 | 428 | int numap_init(void) { 429 | 430 | if (nb_numa_nodes == -1) { 431 | return ERROR_NUMAP_NOT_NUMA; 432 | } 433 | 434 | if (current_archi == NULL) { 435 | return ERROR_NUMAP_ARCH_NOT_SUPPORTED; 436 | } 437 | 438 | curr_err = pfm_initialize(); 439 | if (curr_err != PFM_SUCCESS) { 440 | return ERROR_PFM; 441 | } 442 | 443 | link_fd_measure = NULL; 444 | pthread_mutex_init(&link_fd_lock, NULL); 445 | 446 | return 0; 447 | } 448 | 449 | int numap_counting_init_measure(struct numap_counting_measure *measure) { 450 | 451 | measure->nb_nodes = nb_numa_nodes; 452 | for (int node = 0; node < nb_numa_nodes; node++) { 453 | measure->is_valid[node] = (numa_node_to_cpu[node] != -1); 454 | } 455 | measure->started = 0; 456 | return 0; 457 | } 458 | 459 | void refresh_wrapper_handler(int signum, siginfo_t *info, void* ucontext) { 460 | if (info->si_code == POLL_HUP) { 461 | /* TODO: copy the samples */ 462 | 463 | int fd = info->si_fd; 464 | 465 | // search for corresponding measure 466 | struct link_fd_measure* current_lfm = link_fd_measure; 467 | while (current_lfm != NULL && current_lfm->fd != fd) { 468 | current_lfm = current_lfm->next; 469 | } 470 | if (current_lfm == NULL) 471 | { 472 | fprintf(stderr, "No measure associated with fd %d\n", fd); 473 | abort(); 474 | exit(EXIT_FAILURE); 475 | } 476 | struct numap_sampling_measure* measure = current_lfm->measure; 477 | 478 | if (measure->handler) { 479 | measure->handler(measure, fd); 480 | } 481 | measure->total_samples += measure->nb_refresh; 482 | 483 | ioctl(info->si_fd, PERF_EVENT_IOC_REFRESH, measure->nb_refresh); 484 | } 485 | } 486 | 487 | int numap_sampling_set_measure_handler(struct numap_sampling_measure *measure, void(*handler)(struct numap_sampling_measure*,int), int nb_refresh) 488 | { 489 | // Has to be called before the measure starts 490 | if (measure->started != 0) 491 | { 492 | return ERROR_NUMAP_ALREADY_STARTED; 493 | } 494 | // Set signal handler 495 | // may be given as function argument later 496 | measure->handler = handler; 497 | if (nb_refresh <= 0) 498 | { 499 | fprintf(stderr, "Undefined behaviour : nb_refresh %d <= 0\n", nb_refresh); 500 | exit(EXIT_FAILURE); 501 | } 502 | measure->nb_refresh = nb_refresh; 503 | 504 | return 0; 505 | } 506 | 507 | int __numap_counting_start(struct numap_counting_measure *measure, struct perf_event_attr *pe_attr_read, struct perf_event_attr *pe_attr_write) { 508 | 509 | /** 510 | * Check everything is ok 511 | */ 512 | if (measure->started != 0) { 513 | return ERROR_NUMAP_ALREADY_STARTED; 514 | } else { 515 | measure->started++; 516 | } 517 | 518 | // Open the events on each NUMA node with Linux system call 519 | for (int node = 0; node < measure->nb_nodes; node++) { 520 | if (measure->is_valid[node]) { 521 | measure->fd_reads[node] = perf_event_open(pe_attr_read, -1, numa_node_to_cpu[node], -1, 0); 522 | if (measure->fd_reads[node] == -1) { 523 | return ERROR_PERF_EVENT_OPEN; 524 | } 525 | measure->fd_writes[node] = perf_event_open(pe_attr_write, -1, numa_node_to_cpu[node], -1, 0); 526 | if (measure->fd_writes[node] == -1) { 527 | return ERROR_PERF_EVENT_OPEN; 528 | } 529 | } 530 | } 531 | 532 | // Starts measure 533 | for (int node = 0; node < measure->nb_nodes; node++) { 534 | if (measure->is_valid[node]) { 535 | ioctl(measure->fd_reads[node], PERF_EVENT_IOC_RESET, 0); 536 | ioctl(measure->fd_reads[node], PERF_EVENT_IOC_ENABLE, 0); 537 | ioctl(measure->fd_writes[node], PERF_EVENT_IOC_RESET, 0); 538 | ioctl(measure->fd_writes[node], PERF_EVENT_IOC_ENABLE, 0); 539 | } 540 | } 541 | 542 | return 0; 543 | } 544 | 545 | int numap_counting_start(struct numap_counting_measure *measure) { 546 | 547 | // Set attribute parameter for counting reads using pfmlib 548 | struct perf_event_attr pe_attr_read; 549 | memset(&pe_attr_read, 0, sizeof(pe_attr_read)); 550 | pe_attr_read.size = sizeof(pe_attr_read); 551 | pfm_perf_encode_arg_t arg; 552 | memset(&arg, 0, sizeof(arg)); 553 | arg.size = sizeof(pfm_perf_encode_arg_t); 554 | arg.attr = &pe_attr_read; 555 | char *fstr; 556 | arg.fstr = &fstr; 557 | curr_err = pfm_get_os_event_encoding(current_archi->counting_read_event, PFM_PLM0 | PFM_PLM3, PFM_OS_PERF_EVENT, &arg); 558 | if (curr_err != PFM_SUCCESS) { 559 | return ERROR_PFM; 560 | } 561 | 562 | // Set attribute parameter for counting writes using pfmlib 563 | struct perf_event_attr pe_attr_write; 564 | 565 | // Other parameters 566 | pe_attr_read.disabled = 1; 567 | pe_attr_read.exclude_kernel = 1; 568 | pe_attr_read.exclude_hv = 1; 569 | 570 | return __numap_counting_start(measure, &pe_attr_read, &pe_attr_write); 571 | } 572 | 573 | int numap_counting_stop(struct numap_counting_measure *measure) { 574 | 575 | // Check everything is ok 576 | if (measure->started == 0) { 577 | return ERROR_NUMAP_STOP_BEFORE_START; 578 | } else { 579 | measure->started = 0; 580 | } 581 | 582 | for (int node = 0; node < nb_numa_nodes; node++) { 583 | if (measure->is_valid[node]) { 584 | ioctl(measure->fd_reads[node], PERF_EVENT_IOC_DISABLE, 0); 585 | ioctl(measure->fd_writes[node], PERF_EVENT_IOC_DISABLE, 0); 586 | if(read(measure->fd_reads[node], &measure->reads_count[node], 587 | sizeof(long long)) == -1) { 588 | return ERROR_READ; 589 | } 590 | if (read(measure->fd_writes[node], &measure->writes_count[node], 591 | sizeof(long long)) == -1) { 592 | return ERROR_READ; 593 | } 594 | close(measure->fd_reads[node]); 595 | close(measure->fd_writes[node]); 596 | } 597 | } 598 | 599 | return 0; 600 | } 601 | 602 | int numap_sampling_init_measure(struct numap_sampling_measure *measure, int nb_threads, int sampling_rate, int mmap_pages_count) { 603 | 604 | int thread; 605 | measure->started = 0; 606 | measure->page_size = (size_t)sysconf(_SC_PAGESIZE); 607 | measure->mmap_pages_count = mmap_pages_count; 608 | measure->mmap_len = measure->page_size + measure->page_size * measure->mmap_pages_count; 609 | measure->nb_threads = nb_threads; 610 | measure->sampling_rate = sampling_rate; 611 | for (thread = 0; thread < measure->nb_threads; thread++) { 612 | measure->fd_per_tid[thread] = 0; 613 | measure->metadata_pages_per_tid[thread] = 0; 614 | } 615 | measure->handler = NULL; 616 | measure->total_samples = 0; 617 | set_signal_handler(refresh_wrapper_handler); 618 | measure->nb_refresh = 1000; // default refresh 619 | 620 | return 0; 621 | } 622 | 623 | 624 | static int __numap_sampling_resume(struct numap_sampling_measure *measure) { 625 | int thread; 626 | for (thread = 0; thread < measure->nb_threads; thread++) { 627 | ioctl(measure->fd_per_tid[thread], PERF_EVENT_IOC_RESET, 0); 628 | fcntl(measure->fd_per_tid[thread], F_SETFL, O_ASYNC|O_NONBLOCK); 629 | fcntl(measure->fd_per_tid[thread], F_SETSIG, SIGIO); 630 | fcntl(measure->fd_per_tid[thread], F_SETOWN, measure->tids[thread]); 631 | ioctl(measure->fd_per_tid[thread], PERF_EVENT_IOC_REFRESH, measure->nb_refresh); 632 | ioctl(measure->fd_per_tid[thread], PERF_EVENT_IOC_ENABLE, 0); 633 | } 634 | return 0; 635 | } 636 | 637 | int numap_sampling_resume(struct numap_sampling_measure *measure) { 638 | /** 639 | * Check everything is ok 640 | */ 641 | if (measure->started != 0) { 642 | return ERROR_NUMAP_ALREADY_STARTED; 643 | } else { 644 | measure->started++; 645 | } 646 | 647 | return __numap_sampling_resume(measure); 648 | } 649 | 650 | int __numap_sampling_start(struct numap_sampling_measure *measure, struct perf_event_attr *pe_attr) { 651 | 652 | /** 653 | * Check everything is ok 654 | */ 655 | if (measure->started != 0) { 656 | return ERROR_NUMAP_ALREADY_STARTED; 657 | } else { 658 | measure->started++; 659 | } 660 | 661 | /** 662 | * Open the events for each thread in measure with Linux system call: we do per 663 | * thread monitoring by giving the system call the thread id and a 664 | * cpu = -1, this way the kernel handles the migration of counters 665 | * when threads are migrated. Then we mmap the result. 666 | */ 667 | int cpu = -1; 668 | int thread; 669 | for (thread = 0; thread < measure->nb_threads; thread++) { 670 | if(measure->metadata_pages_per_tid[thread]) { 671 | /* Already open, we can skip this one */ 672 | continue; 673 | } 674 | 675 | measure->fd_per_tid[thread] = perf_event_open(pe_attr, measure->tids[thread], cpu, 676 | -1, 0); 677 | if (measure->fd_per_tid[thread] == -1) { 678 | return ERROR_PERF_EVENT_OPEN; 679 | } 680 | measure->metadata_pages_per_tid[thread] = mmap(NULL, measure->mmap_len, PROT_WRITE, MAP_SHARED, measure->fd_per_tid[thread], 0); 681 | if (measure->metadata_pages_per_tid[thread] == MAP_FAILED) { 682 | if (errno == EPERM) { 683 | fprintf(stderr, "Permission error mapping pages.\n" 684 | "Consider increasing /proc/sys/kernel/perf_event_mlock_kb,\n" 685 | "(mmap length parameter = %zd > perf_event_mlock_kb = %u)\n", measure->mmap_len, (perf_event_mlock_kb * 1024)); 686 | } else { 687 | fprintf (stderr, "Couldn't mmap file descriptor: %s - errno = %d\n", 688 | strerror (errno), errno); 689 | } 690 | exit (EXIT_FAILURE); 691 | } 692 | struct link_fd_measure* new_lfm = malloc(sizeof(struct link_fd_measure)); 693 | new_lfm->fd = measure->fd_per_tid[thread]; 694 | new_lfm->measure = measure; 695 | 696 | pthread_mutex_lock(&link_fd_lock); 697 | new_lfm->next = link_fd_measure; 698 | link_fd_measure = new_lfm; 699 | pthread_mutex_unlock(&link_fd_lock); 700 | } 701 | __numap_sampling_resume(measure); 702 | 703 | return 0; 704 | } 705 | 706 | int numap_sampling_read_supported() { 707 | 708 | if (strcmp(current_archi->sampling_read_event, NOT_SUPPORTED) == 0) { 709 | return 0; 710 | } 711 | return 1; 712 | } 713 | 714 | int numap_sampling_read_start_generic(struct numap_sampling_measure *measure, uint64_t sample_type) { 715 | 716 | // Checks that read sampling is supported before calling pfm 717 | if (!numap_sampling_read_supported()) { 718 | return ERROR_NUMAP_READ_SAMPLING_ARCH_NOT_SUPPORTED; 719 | } 720 | 721 | // Set attribute parameter for perf_event_open using pfmlib 722 | struct perf_event_attr pe_attr; 723 | memset(&pe_attr, 0, sizeof(pe_attr)); 724 | pe_attr.size = sizeof(pe_attr); 725 | pfm_perf_encode_arg_t arg; 726 | memset(&arg, 0, sizeof(arg)); 727 | arg.size = sizeof(pfm_perf_encode_arg_t); 728 | arg.attr = &pe_attr; 729 | char *fstr; 730 | arg.fstr = &fstr; 731 | 732 | curr_err = pfm_get_os_event_encoding(current_archi->sampling_read_event, PFM_PLM0 | PFM_PLM3, PFM_OS_PERF_EVENT, &arg); 733 | if (curr_err != PFM_SUCCESS) { 734 | return ERROR_PFM; 735 | } 736 | 737 | // Sampling parameters 738 | pe_attr.sample_period = measure->sampling_rate; 739 | pe_attr.sample_type = sample_type; 740 | pe_attr.mmap = 1; 741 | pe_attr.task = 1; 742 | pe_attr.precise_ip = 2; 743 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) 744 | pe_attr.use_clockid=1; 745 | pe_attr.clockid = CLOCK_MONOTONIC_RAW; 746 | #else 747 | #warning NUMAP: using clockid is not possible on kernel version < 4.1. This feature will be disabled. 748 | #endif 749 | // Other parameters 750 | pe_attr.disabled = 1; 751 | pe_attr.exclude_kernel = 1; 752 | pe_attr.exclude_hv = 1; 753 | 754 | return __numap_sampling_start(measure, &pe_attr); 755 | 756 | } 757 | 758 | int numap_sampling_read_start(struct numap_sampling_measure *measure) { 759 | return numap_sampling_read_start_generic(measure, PERF_SAMPLE_IP | PERF_SAMPLE_ADDR | PERF_SAMPLE_WEIGHT | PERF_SAMPLE_DATA_SRC); 760 | } 761 | 762 | int numap_sampling_read_stop(struct numap_sampling_measure *measure) { 763 | 764 | // Check everything is ok 765 | if (measure->started == 0) { 766 | return ERROR_NUMAP_STOP_BEFORE_START; 767 | } else { 768 | measure->started = 0; 769 | } 770 | int thread; 771 | for (thread = 0; thread < measure->nb_threads; thread++) { 772 | ioctl(measure->fd_per_tid[thread], PERF_EVENT_IOC_DISABLE, 0); 773 | } 774 | return 0; 775 | } 776 | 777 | int numap_sampling_write_supported() { 778 | 779 | if (strcmp(current_archi->sampling_write_event, NOT_SUPPORTED) == 0) { 780 | return 0; 781 | } 782 | return 1; 783 | } 784 | 785 | int numap_sampling_write_start_generic(struct numap_sampling_measure *measure, uint64_t sample_type) { 786 | // Checks that write sampling is supported before calling pfm 787 | if (!numap_sampling_write_supported()) { 788 | return ERROR_NUMAP_WRITE_SAMPLING_ARCH_NOT_SUPPORTED; 789 | } 790 | 791 | // Set attribute parameter for perf_event_open using pfmlib 792 | struct perf_event_attr pe_attr; 793 | memset(&pe_attr, 0, sizeof(pe_attr)); 794 | pe_attr.size = sizeof(pe_attr); 795 | pfm_perf_encode_arg_t arg; 796 | memset(&arg, 0, sizeof(arg)); 797 | arg.size = sizeof(pfm_perf_encode_arg_t); 798 | arg.attr = &pe_attr; 799 | char *fstr; 800 | arg.fstr = &fstr; 801 | curr_err = pfm_get_os_event_encoding(current_archi->sampling_write_event, PFM_PLM0 | PFM_PLM3, PFM_OS_PERF_EVENT, &arg); 802 | if (curr_err != PFM_SUCCESS) { 803 | return ERROR_PFM; 804 | } 805 | 806 | // Sampling parameters 807 | pe_attr.sample_period = measure->sampling_rate; 808 | pe_attr.sample_type = sample_type; 809 | pe_attr.mmap = 1; 810 | pe_attr.task = 1; 811 | pe_attr.precise_ip = 2; 812 | 813 | // Other parameters 814 | pe_attr.disabled = 1; 815 | pe_attr.exclude_kernel = 1; 816 | pe_attr.exclude_hv = 1; 817 | 818 | return __numap_sampling_start(measure, &pe_attr); 819 | } 820 | 821 | 822 | int numap_sampling_write_start(struct numap_sampling_measure *measure) { 823 | return numap_sampling_write_start_generic(measure, PERF_SAMPLE_IP | PERF_SAMPLE_ADDR | PERF_SAMPLE_WEIGHT | PERF_SAMPLE_DATA_SRC); 824 | } 825 | 826 | int numap_sampling_write_stop(struct numap_sampling_measure *measure) { 827 | return numap_sampling_read_stop(measure); 828 | } 829 | 830 | int numap_sampling_end(struct numap_sampling_measure *measure) { 831 | int thread; 832 | 833 | struct link_fd_measure* current_lfm; 834 | while (link_fd_measure != NULL) { 835 | current_lfm = link_fd_measure; 836 | link_fd_measure = link_fd_measure->next; 837 | free(current_lfm); 838 | } 839 | 840 | for (thread = 0; thread < measure->nb_threads; thread++) { 841 | munmap(measure->metadata_pages_per_tid[thread], measure->mmap_len); 842 | close(measure->fd_per_tid[thread]); 843 | } 844 | return 0; 845 | } 846 | -------------------------------------------------------------------------------- /src/numap_analyse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "numap.h" 5 | 6 | int is_served_by_local_cache1(union perf_mem_data_src data_src) { 7 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT) { 8 | if (data_src.mem_lvl & PERF_MEM_LVL_L1) { 9 | return 1; 10 | } 11 | } 12 | return 0; 13 | } 14 | 15 | int is_served_by_local_cache2(union perf_mem_data_src data_src) { 16 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT) { 17 | if (data_src.mem_lvl & PERF_MEM_LVL_L2) { 18 | return 1; 19 | } 20 | } 21 | return 0; 22 | } 23 | 24 | int is_served_by_local_cache3(union perf_mem_data_src data_src) { 25 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT) { 26 | if (data_src.mem_lvl & PERF_MEM_LVL_L3) { 27 | return 1; 28 | } 29 | } 30 | return 0; 31 | } 32 | 33 | int is_served_by_local_lfb(union perf_mem_data_src data_src) { 34 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT) { 35 | if (data_src.mem_lvl & PERF_MEM_LVL_LFB) { 36 | return 1; 37 | } 38 | } 39 | return 0; 40 | } 41 | 42 | 43 | int is_served_by_local_cache(union perf_mem_data_src data_src) { 44 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT) { 45 | if (data_src.mem_lvl & PERF_MEM_LVL_L1) { 46 | return 1; 47 | } 48 | if (data_src.mem_lvl & PERF_MEM_LVL_LFB) { 49 | return 1; 50 | } 51 | if (data_src.mem_lvl & PERF_MEM_LVL_L2) { 52 | return 1; 53 | } 54 | if (data_src.mem_lvl & PERF_MEM_LVL_L3) { 55 | return 1; 56 | } 57 | } 58 | return 0; 59 | } 60 | 61 | int is_served_by_local_memory(union perf_mem_data_src data_src) { 62 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT) { 63 | if (data_src.mem_lvl & PERF_MEM_LVL_LOC_RAM) { 64 | return 1; 65 | } 66 | } 67 | return 0; 68 | } 69 | 70 | int is_served_by_remote_cache_or_local_memory(union perf_mem_data_src data_src) { 71 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT && data_src.mem_lvl & PERF_MEM_LVL_REM_CCE1) { 72 | return 1; 73 | } 74 | return 0; 75 | } 76 | 77 | int is_served_by_remote_memory(union perf_mem_data_src data_src) { 78 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT) { 79 | if (data_src.mem_lvl & PERF_MEM_LVL_REM_RAM1) { 80 | return 1; 81 | } else if (data_src.mem_lvl & PERF_MEM_LVL_REM_RAM2) { 82 | return 1; 83 | } 84 | } 85 | return 0; 86 | } 87 | 88 | int is_served_by_local_NA_miss(union perf_mem_data_src data_src) { 89 | if (data_src.mem_lvl & PERF_MEM_LVL_NA) { 90 | return 1; 91 | } 92 | if (data_src.mem_lvl & PERF_MEM_LVL_MISS && data_src.mem_lvl & PERF_MEM_LVL_L3) { 93 | return 1; 94 | } 95 | return 0; 96 | } 97 | 98 | char *get_data_src_opcode(union perf_mem_data_src data_src) { 99 | char * res = concat("", ""); 100 | char * old_res; 101 | if (data_src.mem_op & PERF_MEM_OP_NA) { 102 | old_res = res; 103 | res = concat(res, "NA"); 104 | free(old_res); 105 | } 106 | if (data_src.mem_op & PERF_MEM_OP_LOAD) { 107 | old_res = res; 108 | res = concat(res, "Load"); 109 | free(old_res); 110 | } 111 | if (data_src.mem_op & PERF_MEM_OP_STORE) { 112 | old_res = res; 113 | res = concat(res, "Store"); 114 | free(old_res); 115 | } 116 | if (data_src.mem_op & PERF_MEM_OP_PFETCH) { 117 | old_res = res; 118 | res = concat(res, "Prefetch"); 119 | free(old_res); 120 | } 121 | if (data_src.mem_op & PERF_MEM_OP_EXEC) { 122 | old_res = res; 123 | res = concat(res, "Exec code"); 124 | free(old_res); 125 | } 126 | return res; 127 | } 128 | 129 | char *get_data_src_level(union perf_mem_data_src data_src) { 130 | char * res = concat("", ""); 131 | char * old_res; 132 | if (data_src.mem_lvl & PERF_MEM_LVL_NA) { 133 | old_res = res; 134 | res = concat(res, "NA"); 135 | free(old_res); 136 | } 137 | if (data_src.mem_lvl & PERF_MEM_LVL_L1) { 138 | old_res = res; 139 | res = concat(res, "L1"); 140 | free(old_res); 141 | } else if (data_src.mem_lvl & PERF_MEM_LVL_LFB) { 142 | old_res = res; 143 | res = concat(res, "LFB"); 144 | free(old_res); 145 | } else if (data_src.mem_lvl & PERF_MEM_LVL_L2) { 146 | old_res = res; 147 | res = concat(res, "L2"); 148 | free(old_res); 149 | } else if (data_src.mem_lvl & PERF_MEM_LVL_L3) { 150 | old_res = res; 151 | res = concat(res, "L3"); 152 | free(old_res); 153 | } else if (data_src.mem_lvl & PERF_MEM_LVL_LOC_RAM) { 154 | old_res = res; 155 | res = concat(res, "Local_RAM"); 156 | free(old_res); 157 | } else if (data_src.mem_lvl & PERF_MEM_LVL_REM_RAM1) { 158 | old_res = res; 159 | res = concat(res, "Remote_RAM_1_hop"); 160 | free(old_res); 161 | } else if (data_src.mem_lvl & PERF_MEM_LVL_REM_RAM2) { 162 | old_res = res; 163 | res = concat(res, "Remote_RAM_2_hops"); 164 | free(old_res); 165 | } else if (data_src.mem_lvl & PERF_MEM_LVL_REM_CCE1) { 166 | old_res = res; 167 | res = concat(res, "Remote_Cache_1_hop"); 168 | free(old_res); 169 | } else if (data_src.mem_lvl & PERF_MEM_LVL_REM_CCE2) { 170 | old_res = res; 171 | res = concat(res, "Remote_Cache_2_hops"); 172 | free(old_res); 173 | } else if (data_src.mem_lvl & PERF_MEM_LVL_IO) { 174 | old_res = res; 175 | res = concat(res, "I/O_Memory"); 176 | free(old_res); 177 | } else if (data_src.mem_lvl & PERF_MEM_LVL_UNC) { 178 | old_res = res; 179 | res = concat(res, "Uncached_Memory"); 180 | free(old_res); 181 | } 182 | if (data_src.mem_lvl & PERF_MEM_LVL_HIT) { 183 | old_res = res; 184 | res = concat(res, "_Hit"); 185 | free(old_res); 186 | } else if (data_src.mem_lvl & PERF_MEM_LVL_MISS) { 187 | old_res = res; 188 | res = concat(res, "_Miss"); 189 | free(old_res); 190 | } 191 | return res; 192 | } 193 | 194 | int get_index(uint32_t tid, struct numap_sampling_measure *measure) { 195 | uint32_t thread; 196 | int index = 0; 197 | for (thread = 0; thread < measure->nb_threads; thread++) { 198 | if (measure->tids[index] == tid) { 199 | return index; 200 | } 201 | index++; 202 | } 203 | return -1; 204 | } 205 | 206 | int numap_sampling_print(struct numap_sampling_measure *measure, char print_samples) { 207 | int thread; 208 | for (thread = 0; thread < measure->nb_threads; thread++) { 209 | 210 | struct perf_event_mmap_page *metadata_page = measure->metadata_pages_per_tid[thread]; 211 | uint64_t head = metadata_page -> data_head; 212 | rmb(); 213 | struct perf_event_header *header = (struct perf_event_header *)((char *)metadata_page + measure->page_size); 214 | uint64_t consumed = 0; 215 | int na_miss_count = 0; 216 | int cache1_count = 0; 217 | int cache2_count = 0; 218 | int cache3_count = 0; 219 | int lfb_count = 0; 220 | int memory_count = 0; 221 | int remote_memory_count = 0; 222 | int remote_cache_count = 0; 223 | int total_count = 0; 224 | while (consumed < head) { 225 | if (header->size == 0) { 226 | fprintf(stderr, "Error: invalid header size = 0\n"); 227 | return -1; 228 | } 229 | if (header -> type == PERF_RECORD_SAMPLE) { 230 | struct sample *sample = (struct sample *)((char *)(header) + 8); 231 | if (is_served_by_local_NA_miss(sample->data_src)) { 232 | na_miss_count++; 233 | } 234 | if (is_served_by_local_cache1(sample->data_src)) { 235 | cache1_count++; 236 | } 237 | if (is_served_by_local_cache2(sample->data_src)) { 238 | cache2_count++; 239 | } 240 | if (is_served_by_local_cache3(sample->data_src)) { 241 | cache3_count++; 242 | } 243 | if (is_served_by_local_lfb(sample->data_src)) { 244 | lfb_count++; 245 | } 246 | if (is_served_by_local_memory(sample->data_src)) { 247 | memory_count++; 248 | } 249 | if (is_served_by_remote_memory(sample->data_src)) { 250 | remote_memory_count++; 251 | } 252 | if (is_served_by_remote_cache_or_local_memory(sample->data_src)) { 253 | remote_cache_count++; 254 | } 255 | total_count++; 256 | if (print_samples) { 257 | printf("pc=%" PRIx64 ", @=%" PRIx64 ", src level=%s, latency=%" PRIu64 "\n", sample->ip, sample->addr, get_data_src_level(sample->data_src), sample->weight); 258 | } 259 | } 260 | consumed += header->size; 261 | header = (struct perf_event_header *)((char *)header + header -> size); 262 | } 263 | printf("\n"); 264 | printf("head = %" PRIu64 " compared to max = %zu\n", head, measure->mmap_len); 265 | printf("Thread %d: %-8d samples\n", thread, total_count); 266 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, cache1_count, "local cache 1", (100.0 * cache1_count / total_count)); 267 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, cache2_count, "local cache 2", (100.0 * cache2_count / total_count)); 268 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, cache3_count, "local cache 3", (100.0 * cache3_count / total_count)); 269 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, lfb_count, "local cache LFB", (100.0 * lfb_count / total_count)); 270 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, memory_count, "local memory", (100.0 * memory_count / total_count)); 271 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, remote_cache_count, "remote cache or local memory", (100.0 * remote_cache_count / total_count)); 272 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, remote_memory_count, "remote memory", (100.0 * remote_memory_count / total_count)); 273 | printf("Thread %d: %-8d %-30s %0.3f%%\n", thread, na_miss_count, "unknown l3 miss", (100.0 * na_miss_count / total_count)); 274 | if (measure->nb_refresh > 0) { 275 | measure->total_samples += (total_count % measure->nb_refresh); 276 | } 277 | } 278 | printf("\nTotal sample number : %d\n", measure->total_samples); 279 | 280 | return 0; 281 | } 282 | 283 | int numap_sampling_read_print(struct numap_sampling_measure *measure, char print_samples) { 284 | return numap_sampling_print(measure, print_samples); 285 | } 286 | 287 | int numap_sampling_write_print(struct numap_sampling_measure *measure, char print_samples) { 288 | return numap_sampling_print(measure, print_samples); 289 | } 290 | --------------------------------------------------------------------------------