1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
|
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#include <sys/sysmacros.h>
#endif
#include <time.h>
#include <unistd.h>
#include "igt_perf.h"
static char *bus_address(int i915, char *path, int pathlen)
{
struct stat st;
int len = -1;
int dir;
char *s;
if (fstat(i915, &st) || !S_ISCHR(st.st_mode))
return NULL;
snprintf(path, pathlen, "/sys/dev/char/%d:%d",
major(st.st_rdev), minor(st.st_rdev));
dir = open(path, O_RDONLY);
if (dir != -1) {
len = readlinkat(dir, "device", path, pathlen - 1);
close(dir);
}
if (len < 0)
return NULL;
path[len] = '\0';
/* strip off the relative path */
s = strrchr(path, '/');
if (s)
memmove(path, s + 1, len - (s - path) + 1);
return path;
}
const char *i915_perf_device(int i915, char *buf, int buflen)
{
char *s;
#define prefix "i915_"
#define plen strlen(prefix)
if (!buf || buflen < plen)
return "i915";
memcpy(buf, prefix, plen);
if (!bus_address(i915, buf + plen, buflen - plen) ||
strcmp(buf + plen, "0000:00:02.0") == 0) /* legacy name for igfx */
buf[plen - 1] = '\0';
/* Convert all colons in the address to '_', thanks perf! */
for (s = buf; *s; s++)
if (*s == ':')
*s = '_';
return buf;
}
const char *xe_perf_device(int xe, char *buf, int buflen)
{
char *s;
char pref[] = "xe_";
int len = strlen(pref);
if (!buf || buflen < len)
return "xe";
memcpy(buf, pref, len);
if (!bus_address(xe, buf + len, buflen - len))
buf[len - 1] = '\0';
/* Convert all colons in the address to '_', thanks perf! */
for (s = buf; *s; s++)
if (*s == ':')
*s = '_';
return buf;
}
/**
* perf_event_format: Returns the shift for format param
* @device: PMU device
* @param: Parameter for which you need the format
* @shift: bit shift
*
* Returns: 0 on success or negative error code
*/
int perf_event_format(const char *device, const char *param, uint32_t *shift)
{
char buf[NAME_MAX];
ssize_t bytes;
uint32_t end;
int ret, fd;
snprintf(buf, sizeof(buf),
"/sys/bus/event_source/devices/%s/format/%s",
device, param);
fd = open(buf, O_RDONLY | O_CLOEXEC);
if (fd < 0)
return -errno;
bytes = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (bytes < 1)
return -EINVAL;
buf[bytes] = '\0';
ret = sscanf(buf, "config:%u-%u", shift, &end);
if (ret != 2)
return -EINVAL;
return 0;
}
/**
* perf_event_config:
* @device: Device string in driver:pci format
* @event: The event name
* @config: Pointer to the config
* Returns: 0 for success, negative value on error
*/
int perf_event_config(const char *device, const char *event, uint64_t *config)
{
char buf[NAME_MAX];
ssize_t bytes;
int ret;
int fd;
snprintf(buf, sizeof(buf),
"/sys/bus/event_source/devices/%s/events/%s",
device,
event);
fd = open(buf, O_RDONLY);
if (fd < 0)
return -errno;
bytes = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (bytes < 1)
return -EINVAL;
buf[bytes] = '\0';
ret = sscanf(buf, "event=0x%lx", config);
if (ret != 1)
return -EINVAL;
return 0;
}
uint64_t xe_perf_type_id(int xe)
{
char buf[80];
return igt_perf_type_id(xe_perf_device(xe, buf, sizeof(buf)));
}
uint64_t i915_perf_type_id(int i915)
{
char buf[80];
return igt_perf_type_id(i915_perf_device(i915, buf, sizeof(buf)));
}
uint64_t igt_perf_type_id(const char *device)
{
char buf[64];
ssize_t ret;
int fd;
snprintf(buf, sizeof(buf),
"/sys/bus/event_source/devices/%s/type", device);
fd = open(buf, O_RDONLY);
if (fd < 0)
return 0;
ret = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (ret < 1)
return 0;
buf[ret] = '\0';
return strtoull(buf, NULL, 0);
}
int igt_perf_events_dir(int i915)
{
char buf[80];
char path[PATH_MAX];
i915_perf_device(i915, buf, sizeof(buf));
snprintf(path, sizeof(path), "/sys/bus/event_source/devices/%s/events", buf);
return open(path, O_RDONLY);
}
static int
_perf_open(uint64_t type, uint64_t config, int group, uint64_t format)
{
struct perf_event_attr attr = { };
int nr_cpus = get_nprocs_conf();
int cpu = 0, ret;
attr.type = type;
if (attr.type == 0)
return -ENOENT;
if (group >= 0)
format &= ~PERF_FORMAT_GROUP;
attr.read_format = format;
attr.config = config;
attr.use_clockid = 1;
attr.clockid = CLOCK_MONOTONIC;
do {
ret = perf_event_open(&attr, -1, cpu++, group, 0);
} while ((ret < 0 && errno == EINVAL) && (cpu < nr_cpus));
return ret;
}
int perf_igfx_open(uint64_t config)
{
return _perf_open(igt_perf_type_id("i915"), config, -1,
PERF_FORMAT_TOTAL_TIME_ENABLED);
}
int perf_igfx_open_group(uint64_t config, int group)
{
return _perf_open(igt_perf_type_id("i915"), config, group,
PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_GROUP);
}
int perf_xe_open(int xe, uint64_t config)
{
return _perf_open(xe_perf_type_id(xe), config, -1,
PERF_FORMAT_TOTAL_TIME_ENABLED);
}
int perf_i915_open(int i915, uint64_t config)
{
return _perf_open(i915_perf_type_id(i915), config, -1,
PERF_FORMAT_TOTAL_TIME_ENABLED);
}
int perf_i915_open_group(int i915, uint64_t config, int group)
{
return _perf_open(i915_perf_type_id(i915), config, group,
PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_GROUP);
}
int igt_perf_open(uint64_t type, uint64_t config)
{
return _perf_open(type, config, -1,
PERF_FORMAT_TOTAL_TIME_ENABLED);
}
int igt_perf_open_group(uint64_t type, uint64_t config, int group)
{
return _perf_open(type, config, group,
PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_GROUP);
}
|