ftrace_test.c (10946B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * User Events FTrace Test Program 4 * 5 * Copyright (c) 2021 Beau Belgrave <beaub@linux.microsoft.com> 6 */ 7 8#include <errno.h> 9#include <linux/user_events.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <fcntl.h> 13#include <sys/ioctl.h> 14#include <sys/stat.h> 15#include <unistd.h> 16 17#include "../kselftest_harness.h" 18 19const char *data_file = "/sys/kernel/debug/tracing/user_events_data"; 20const char *status_file = "/sys/kernel/debug/tracing/user_events_status"; 21const char *enable_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/enable"; 22const char *trace_file = "/sys/kernel/debug/tracing/trace"; 23const char *fmt_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/format"; 24 25static int trace_bytes(void) 26{ 27 int fd = open(trace_file, O_RDONLY); 28 char buf[256]; 29 int bytes = 0, got; 30 31 if (fd == -1) 32 return -1; 33 34 while (true) { 35 got = read(fd, buf, sizeof(buf)); 36 37 if (got == -1) 38 return -1; 39 40 if (got == 0) 41 break; 42 43 bytes += got; 44 } 45 46 close(fd); 47 48 return bytes; 49} 50 51static int skip_until_empty_line(FILE *fp) 52{ 53 int c, last = 0; 54 55 while (true) { 56 c = getc(fp); 57 58 if (c == EOF) 59 break; 60 61 if (last == '\n' && c == '\n') 62 return 0; 63 64 last = c; 65 } 66 67 return -1; 68} 69 70static int get_print_fmt(char *buffer, int len) 71{ 72 FILE *fp = fopen(fmt_file, "r"); 73 char *newline; 74 75 if (!fp) 76 return -1; 77 78 /* Read until empty line (Skip Common) */ 79 if (skip_until_empty_line(fp) < 0) 80 goto err; 81 82 /* Read until empty line (Skip Properties) */ 83 if (skip_until_empty_line(fp) < 0) 84 goto err; 85 86 /* Read in print_fmt: */ 87 if (fgets(buffer, len, fp) == NULL) 88 goto err; 89 90 newline = strchr(buffer, '\n'); 91 92 if (newline) 93 *newline = '\0'; 94 95 fclose(fp); 96 97 return 0; 98err: 99 fclose(fp); 100 101 return -1; 102} 103 104static int clear(void) 105{ 106 int fd = open(data_file, O_RDWR); 107 108 if (fd == -1) 109 return -1; 110 111 if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1) 112 if (errno != ENOENT) 113 return -1; 114 115 close(fd); 116 117 return 0; 118} 119 120static int check_print_fmt(const char *event, const char *expected) 121{ 122 struct user_reg reg = {0}; 123 char print_fmt[256]; 124 int ret; 125 int fd; 126 127 /* Ensure cleared */ 128 ret = clear(); 129 130 if (ret != 0) 131 return ret; 132 133 fd = open(data_file, O_RDWR); 134 135 if (fd == -1) 136 return fd; 137 138 reg.size = sizeof(reg); 139 reg.name_args = (__u64)event; 140 141 /* Register should work */ 142 ret = ioctl(fd, DIAG_IOCSREG, ®); 143 144 close(fd); 145 146 if (ret != 0) 147 return ret; 148 149 /* Ensure correct print_fmt */ 150 ret = get_print_fmt(print_fmt, sizeof(print_fmt)); 151 152 if (ret != 0) 153 return ret; 154 155 return strcmp(print_fmt, expected); 156} 157 158FIXTURE(user) { 159 int status_fd; 160 int data_fd; 161 int enable_fd; 162}; 163 164FIXTURE_SETUP(user) { 165 self->status_fd = open(status_file, O_RDONLY); 166 ASSERT_NE(-1, self->status_fd); 167 168 self->data_fd = open(data_file, O_RDWR); 169 ASSERT_NE(-1, self->data_fd); 170 171 self->enable_fd = -1; 172} 173 174FIXTURE_TEARDOWN(user) { 175 close(self->status_fd); 176 close(self->data_fd); 177 178 if (self->enable_fd != -1) { 179 write(self->enable_fd, "0", sizeof("0")); 180 close(self->enable_fd); 181 } 182 183 ASSERT_EQ(0, clear()); 184} 185 186TEST_F(user, register_events) { 187 struct user_reg reg = {0}; 188 int page_size = sysconf(_SC_PAGESIZE); 189 char *status_page; 190 191 reg.size = sizeof(reg); 192 reg.name_args = (__u64)"__test_event u32 field1; u32 field2"; 193 194 status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED, 195 self->status_fd, 0); 196 197 /* Register should work */ 198 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 199 ASSERT_EQ(0, reg.write_index); 200 ASSERT_NE(0, reg.status_index); 201 202 /* Multiple registers should result in same index */ 203 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 204 ASSERT_EQ(0, reg.write_index); 205 ASSERT_NE(0, reg.status_index); 206 207 /* Ensure disabled */ 208 self->enable_fd = open(enable_file, O_RDWR); 209 ASSERT_NE(-1, self->enable_fd); 210 ASSERT_NE(-1, write(self->enable_fd, "0", sizeof("0"))) 211 212 /* MMAP should work and be zero'd */ 213 ASSERT_NE(MAP_FAILED, status_page); 214 ASSERT_NE(NULL, status_page); 215 ASSERT_EQ(0, status_page[reg.status_index]); 216 217 /* Enable event and ensure bits updated in status */ 218 ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1"))) 219 ASSERT_EQ(EVENT_STATUS_FTRACE, status_page[reg.status_index]); 220 221 /* Disable event and ensure bits updated in status */ 222 ASSERT_NE(-1, write(self->enable_fd, "0", sizeof("0"))) 223 ASSERT_EQ(0, status_page[reg.status_index]); 224 225 /* File still open should return -EBUSY for delete */ 226 ASSERT_EQ(-1, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event")); 227 ASSERT_EQ(EBUSY, errno); 228 229 /* Delete should work only after close */ 230 close(self->data_fd); 231 self->data_fd = open(data_file, O_RDWR); 232 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event")); 233 234 /* Unmap should work */ 235 ASSERT_EQ(0, munmap(status_page, page_size)); 236} 237 238TEST_F(user, write_events) { 239 struct user_reg reg = {0}; 240 struct iovec io[3]; 241 __u32 field1, field2; 242 int before = 0, after = 0; 243 244 reg.size = sizeof(reg); 245 reg.name_args = (__u64)"__test_event u32 field1; u32 field2"; 246 247 field1 = 1; 248 field2 = 2; 249 250 io[0].iov_base = ®.write_index; 251 io[0].iov_len = sizeof(reg.write_index); 252 io[1].iov_base = &field1; 253 io[1].iov_len = sizeof(field1); 254 io[2].iov_base = &field2; 255 io[2].iov_len = sizeof(field2); 256 257 /* Register should work */ 258 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 259 ASSERT_EQ(0, reg.write_index); 260 ASSERT_NE(0, reg.status_index); 261 262 /* Write should fail on invalid slot with ENOENT */ 263 io[0].iov_base = &field2; 264 io[0].iov_len = sizeof(field2); 265 ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3)); 266 ASSERT_EQ(ENOENT, errno); 267 io[0].iov_base = ®.write_index; 268 io[0].iov_len = sizeof(reg.write_index); 269 270 /* Enable event */ 271 self->enable_fd = open(enable_file, O_RDWR); 272 ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1"))) 273 274 /* Write should make it out to ftrace buffers */ 275 before = trace_bytes(); 276 ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 3)); 277 after = trace_bytes(); 278 ASSERT_GT(after, before); 279} 280 281TEST_F(user, write_fault) { 282 struct user_reg reg = {0}; 283 struct iovec io[2]; 284 int l = sizeof(__u64); 285 void *anon; 286 287 reg.size = sizeof(reg); 288 reg.name_args = (__u64)"__test_event u64 anon"; 289 290 anon = mmap(NULL, l, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 291 ASSERT_NE(MAP_FAILED, anon); 292 293 io[0].iov_base = ®.write_index; 294 io[0].iov_len = sizeof(reg.write_index); 295 io[1].iov_base = anon; 296 io[1].iov_len = l; 297 298 /* Register should work */ 299 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 300 ASSERT_EQ(0, reg.write_index); 301 ASSERT_NE(0, reg.status_index); 302 303 /* Write should work normally */ 304 ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 2)); 305 306 /* Faulted data should zero fill and work */ 307 ASSERT_EQ(0, madvise(anon, l, MADV_DONTNEED)); 308 ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 2)); 309 ASSERT_EQ(0, munmap(anon, l)); 310} 311 312TEST_F(user, write_validator) { 313 struct user_reg reg = {0}; 314 struct iovec io[3]; 315 int loc, bytes; 316 char data[8]; 317 int before = 0, after = 0; 318 319 reg.size = sizeof(reg); 320 reg.name_args = (__u64)"__test_event __rel_loc char[] data"; 321 322 /* Register should work */ 323 ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, ®)); 324 ASSERT_EQ(0, reg.write_index); 325 ASSERT_NE(0, reg.status_index); 326 327 io[0].iov_base = ®.write_index; 328 io[0].iov_len = sizeof(reg.write_index); 329 io[1].iov_base = &loc; 330 io[1].iov_len = sizeof(loc); 331 io[2].iov_base = data; 332 bytes = snprintf(data, sizeof(data), "Test") + 1; 333 io[2].iov_len = bytes; 334 335 /* Undersized write should fail */ 336 ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 1)); 337 ASSERT_EQ(EINVAL, errno); 338 339 /* Enable event */ 340 self->enable_fd = open(enable_file, O_RDWR); 341 ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1"))) 342 343 /* Full in-bounds write should work */ 344 before = trace_bytes(); 345 loc = DYN_LOC(0, bytes); 346 ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 3)); 347 after = trace_bytes(); 348 ASSERT_GT(after, before); 349 350 /* Out of bounds write should fault (offset way out) */ 351 loc = DYN_LOC(1024, bytes); 352 ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3)); 353 ASSERT_EQ(EFAULT, errno); 354 355 /* Out of bounds write should fault (offset 1 byte out) */ 356 loc = DYN_LOC(1, bytes); 357 ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3)); 358 ASSERT_EQ(EFAULT, errno); 359 360 /* Out of bounds write should fault (size way out) */ 361 loc = DYN_LOC(0, bytes + 1024); 362 ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3)); 363 ASSERT_EQ(EFAULT, errno); 364 365 /* Out of bounds write should fault (size 1 byte out) */ 366 loc = DYN_LOC(0, bytes + 1); 367 ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3)); 368 ASSERT_EQ(EFAULT, errno); 369 370 /* Non-Null should fault */ 371 memset(data, 'A', sizeof(data)); 372 loc = DYN_LOC(0, bytes); 373 ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3)); 374 ASSERT_EQ(EFAULT, errno); 375} 376 377TEST_F(user, print_fmt) { 378 int ret; 379 380 ret = check_print_fmt("__test_event __rel_loc char[] data", 381 "print fmt: \"data=%s\", __get_rel_str(data)"); 382 ASSERT_EQ(0, ret); 383 384 ret = check_print_fmt("__test_event __data_loc char[] data", 385 "print fmt: \"data=%s\", __get_str(data)"); 386 ASSERT_EQ(0, ret); 387 388 ret = check_print_fmt("__test_event s64 data", 389 "print fmt: \"data=%lld\", REC->data"); 390 ASSERT_EQ(0, ret); 391 392 ret = check_print_fmt("__test_event u64 data", 393 "print fmt: \"data=%llu\", REC->data"); 394 ASSERT_EQ(0, ret); 395 396 ret = check_print_fmt("__test_event s32 data", 397 "print fmt: \"data=%d\", REC->data"); 398 ASSERT_EQ(0, ret); 399 400 ret = check_print_fmt("__test_event u32 data", 401 "print fmt: \"data=%u\", REC->data"); 402 ASSERT_EQ(0, ret); 403 404 ret = check_print_fmt("__test_event int data", 405 "print fmt: \"data=%d\", REC->data"); 406 ASSERT_EQ(0, ret); 407 408 ret = check_print_fmt("__test_event unsigned int data", 409 "print fmt: \"data=%u\", REC->data"); 410 ASSERT_EQ(0, ret); 411 412 ret = check_print_fmt("__test_event s16 data", 413 "print fmt: \"data=%d\", REC->data"); 414 ASSERT_EQ(0, ret); 415 416 ret = check_print_fmt("__test_event u16 data", 417 "print fmt: \"data=%u\", REC->data"); 418 ASSERT_EQ(0, ret); 419 420 ret = check_print_fmt("__test_event short data", 421 "print fmt: \"data=%d\", REC->data"); 422 ASSERT_EQ(0, ret); 423 424 ret = check_print_fmt("__test_event unsigned short data", 425 "print fmt: \"data=%u\", REC->data"); 426 ASSERT_EQ(0, ret); 427 428 ret = check_print_fmt("__test_event s8 data", 429 "print fmt: \"data=%d\", REC->data"); 430 ASSERT_EQ(0, ret); 431 432 ret = check_print_fmt("__test_event u8 data", 433 "print fmt: \"data=%u\", REC->data"); 434 ASSERT_EQ(0, ret); 435 436 ret = check_print_fmt("__test_event char data", 437 "print fmt: \"data=%d\", REC->data"); 438 ASSERT_EQ(0, ret); 439 440 ret = check_print_fmt("__test_event unsigned char data", 441 "print fmt: \"data=%u\", REC->data"); 442 ASSERT_EQ(0, ret); 443 444 ret = check_print_fmt("__test_event char[4] data", 445 "print fmt: \"data=%s\", REC->data"); 446 ASSERT_EQ(0, ret); 447} 448 449int main(int argc, char **argv) 450{ 451 return test_harness_run(argc, argv); 452}