66#include <errno.h>
77#include <sys/ioctl.h>
88#include <stdint.h>
9+ #include <stdlib.h>
910
1011struct counter
1112{
12- uint64_t instruction_count ;
1313 uint64_t time_enabled_ns ;
14+ uint64_t instruction_count ;
15+ uint64_t branch_misses ;
16+ uint64_t page_faults ;
17+ };
18+
19+ struct read_format
20+ {
21+ uint64_t nr ;
22+ uint64_t time_enabled ;
23+ uint64_t time_running ;
24+ struct
25+ {
26+ uint64_t value ;
27+ } values [];
28+ };
29+
30+ struct perf_event_config
31+ {
32+ uint64_t type ;
33+ uint64_t config ;
34+ };
35+
36+ struct perf_event_config events [] = {
37+ {PERF_TYPE_HARDWARE , PERF_COUNT_HW_INSTRUCTIONS },
38+ {PERF_TYPE_HARDWARE , PERF_COUNT_HW_BRANCH_MISSES },
39+ {PERF_TYPE_SOFTWARE , PERF_COUNT_SW_PAGE_FAULTS },
40+ // {PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES}, For whatever reason, these two always show 0
41+ // {PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS},
1442};
1543
44+ const int NUM_EVENTS = sizeof (events ) / sizeof (events [0 ]);
45+
1646int start ()
1747{
48+ // Construct base perf_event_attr struct
1849 struct perf_event_attr attr ;
1950 memset (& attr , 0 , sizeof (attr ));
20- attr .type = PERF_TYPE_HARDWARE ;
2151 attr .size = sizeof (attr );
52+ attr .disabled = 1 ;
2253 attr .exclude_kernel = 1 ;
2354 attr .exclude_hv = 1 ;
2455 attr .sample_period = 0 ;
25- attr .config = PERF_COUNT_HW_INSTRUCTIONS ;
26- attr .read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING ;
56+ attr .read_format = PERF_FORMAT_GROUP | PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING ;
57+
58+ int group = -1 ;
59+ int leader_fd ;
60+
61+ // Enable every event in perf_event_config
62+ for (int i = 0 ; i < NUM_EVENTS ; i ++ )
63+ {
64+ attr .type = events [i ].type ;
65+ attr .config = events [i ].config ;
66+
67+ int fd = syscall (SYS_perf_event_open , & attr , 0 , -1 , group , 0 );
68+ if (fd == -1 )
69+ {
70+ fprintf (stderr , "Failed to open event %lu: %s.\n" , events [i ].config , strerror (errno ));
71+ return -1 ;
72+ }
73+
74+ if (i == 0 )
75+ {
76+ group = fd ;
77+ leader_fd = fd ;
78+ }
79+ }
2780
28- int fd = syscall ( SYS_perf_event_open , & attr , 0 , -1 , -1 , 0 );
29- if (fd == -1 )
81+ // Enable the event group
82+ if (ioctl ( leader_fd , PERF_EVENT_IOC_ENABLE , PERF_IOC_FLAG_GROUP ) == -1 )
3083 {
31- fprintf (stderr , "Failed to perf_event_open: %s.\n" , strerror (errno ));
84+ fprintf (stderr , "Failed to enable perf events: %s.\n" , strerror (errno ));
85+ // Consider cleaning up previously opened file descriptors here
3286 return -1 ;
3387 }
3488
35- return fd ;
89+ return leader_fd ;
3690}
3791
3892int end (int fd , struct counter * out )
@@ -43,24 +97,50 @@ int end(int fd, struct counter *out)
4397 return -1 ;
4498 }
4599
100+ // Disable the event group
46101 if (ioctl (fd , PERF_EVENT_IOC_DISABLE , PERF_IOC_FLAG_GROUP ) == -1 )
47102 {
48103 fprintf (stderr , "Error disabling perf event (fd: %d): %s\n" , fd , strerror (errno ));
49104 return -1 ;
50105 }
51106
52- uint64_t read_format [5 ];
53- if (read (fd , read_format , sizeof (read_format )) == -1 )
107+ // Allocate buffer for reading results
108+ int size = sizeof (struct read_format ) + (sizeof (uint64_t ) * NUM_EVENTS );
109+ struct read_format * buffer = (struct read_format * )malloc (size );
110+ if (!buffer )
111+ {
112+ fprintf (stderr , "Failed to allocate memory for read buffer.\n" );
113+ return -1 ;
114+ }
115+
116+ // Read results
117+ int ret_val = read (fd , buffer , size );
118+ if (ret_val == -1 )
119+ {
120+ fprintf (stderr , "Error reading perf event results: %s\n" , strerror (errno ));
121+ free (buffer );
122+ return -1 ;
123+ }
124+ else if (ret_val != size )
54125 {
55- fprintf (stderr , "Error reading perf event results (fd: %d): %s\n" , fd , strerror (errno ));
126+ fprintf (stderr , "Error reading perf event results: read %d bytes, expected %d\n" , ret_val , size );
127+ free (buffer );
56128 return -1 ;
57129 }
58- else
130+
131+ // Assign time_enabled_ns
132+ out -> time_enabled_ns = buffer -> time_enabled ;
133+
134+ // Directly assign values to struct fields treating them as an array 8)
135+ uint64_t * counter_ptr = (uint64_t * )out ;
136+ counter_ptr ++ ; // Now points to instruction_count, the first counter field
137+
138+ for (int i = 0 ; i < NUM_EVENTS ; i ++ )
59139 {
60- close (fd );
61- out -> instruction_count = read_format [0 ];
62- out -> time_enabled_ns = read_format [1 ];
140+ counter_ptr [i ] = buffer -> values [i ].value ;
63141 }
64142
143+ close (fd );
144+ free (buffer );
65145 return 0 ;
66146}
0 commit comments