Coverage Report

Created: 2026-04-01 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/opcache/jit/ir/ir_perf.c
Line
Count
Source
1
/*
2
 * IR - Lightweight JIT Compilation Framework
3
 * (Linux perf interface)
4
 * Copyright (C) 2022 Zend by Perforce.
5
 * Authors: Dmitry Stogov <dmitry@php.net>
6
 *
7
 * 1) Profile using perf-<pid>.map
8
 * perf record ./prog
9
 * perf report
10
 *
11
 * 2) Profile using jit-<pid>.dump
12
 * perf record -k 1 ./prog
13
 * perf inject -j -i perf.data -o perf.data.jitted
14
 * perf report -i perf.data.jitted
15
 */
16
17
#ifndef _GNU_SOURCE
18
# define _GNU_SOURCE
19
#endif
20
21
#include <stdio.h>
22
#include <unistd.h>
23
#include <time.h>
24
#include <sys/mman.h>
25
#include <sys/types.h>
26
#include <sys/stat.h>
27
#include <fcntl.h>
28
#include <string.h>
29
# include <limits.h>
30
31
#if defined(__linux__)
32
#include <sys/syscall.h>
33
#elif defined(__APPLE__)
34
# include <pthread.h>
35
#elif defined(__FreeBSD__)
36
# include <sys/thr.h>
37
# include <sys/sysctl.h>
38
#elif defined(__NetBSD__)
39
# include <lwp.h>
40
#elif defined(__DragonFly__)
41
# include <sys/lwp.h>
42
# include <sys/sysctl.h>
43
#elif defined(__sun)
44
// avoiding thread.h inclusion as it conflicts with vtunes types.
45
extern unsigned int thr_self(void);
46
#elif defined(__HAIKU__)
47
#include <FindDirectory.h>
48
#endif
49
50
#include "ir.h"
51
#include "ir_elf.h"
52
53
0
#define IR_PERF_JITDUMP_HEADER_MAGIC   0x4A695444
54
0
#define IR_PERF_JITDUMP_HEADER_VERSION 1
55
56
0
#define IR_PERF_JITDUMP_RECORD_LOAD       0
57
#define IR_PERF_JITDUMP_RECORD_MOVE       1
58
#define IR_PERF_JITDUMP_RECORD_DEBUG_INFO 2
59
0
#define IR_PERF_JITDUMP_RECORD_CLOSE      3
60
#define IR_PERF_JITDUMP_UNWINDING_UNFO    4
61
62
#define ALIGN8(size)   (((size) + 7) & ~7)
63
#define PADDING8(size) (ALIGN8(size) - (size))
64
65
typedef struct ir_perf_jitdump_header {
66
  uint32_t magic;
67
  uint32_t version;
68
  uint32_t size;
69
  uint32_t elf_mach_target;
70
  uint32_t reserved;
71
  uint32_t process_id;
72
  uint64_t time_stamp;
73
  uint64_t flags;
74
} ir_perf_jitdump_header;
75
76
typedef struct _ir_perf_jitdump_record {
77
  uint32_t event;
78
  uint32_t size;
79
  uint64_t time_stamp;
80
} ir_perf_jitdump_record;
81
82
typedef struct _ir_perf_jitdump_load_record {
83
  ir_perf_jitdump_record hdr;
84
  uint32_t process_id;
85
  uint32_t thread_id;
86
  uint64_t vma;
87
  uint64_t code_address;
88
  uint64_t code_size;
89
  uint64_t code_id;
90
} ir_perf_jitdump_load_record;
91
92
static int   jitdump_fd  = -1;
93
static void *jitdump_mem = MAP_FAILED;
94
95
static uint64_t ir_perf_timestamp(void)
96
0
{
97
0
  struct timespec ts;
98
99
0
  if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
100
0
    return 0;
101
0
  }
102
0
  return ((uint64_t)ts.tv_sec * 1000000000) + ts.tv_nsec;
103
0
}
104
105
int ir_perf_jitdump_open(void)
106
0
{
107
0
  char filename[64];
108
0
  int fd, ret;
109
0
  ir_elf_header elf_hdr;
110
0
  ir_perf_jitdump_header jit_hdr;
111
112
0
  snprintf(filename, sizeof(filename), "/tmp/jit-%d.dump", getpid());
113
0
  if (!ir_perf_timestamp()) {
114
0
    return 0;
115
0
  }
116
117
0
#if defined(__linux__)
118
0
  fd = open("/proc/self/exe", O_RDONLY);
119
#elif defined(__NetBSD__)
120
  fd = open("/proc/curproc/exe", O_RDONLY);
121
#elif defined(__FreeBSD__) || defined(__DragonFly__)
122
  char path[PATH_MAX];
123
  size_t pathlen = sizeof(path);
124
  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
125
  if (sysctl(mib, 4, path, &pathlen, NULL, 0) == -1) {
126
    return 0;
127
  }
128
  fd = open(path, O_RDONLY);
129
#elif defined(__sun)
130
  fd = open("/proc/self/path/a.out", O_RDONLY);
131
#elif defined(__HAIKU__)
132
  char path[PATH_MAX];
133
  if (find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH,
134
    NULL, path, sizeof(path)) != B_OK) {
135
    return 0;
136
  }
137
138
  fd = open(path, O_RDONLY);
139
#else
140
  fd = -1;
141
#endif
142
0
  if (fd < 0) {
143
0
    return 0;
144
0
  }
145
146
0
  ret = read(fd, &elf_hdr, sizeof(elf_hdr));
147
0
  close(fd);
148
149
0
  if (ret != sizeof(elf_hdr) ||
150
0
      elf_hdr.emagic[0] != 0x7f ||
151
0
      elf_hdr.emagic[1] != 'E' ||
152
0
      elf_hdr.emagic[2] != 'L' ||
153
0
      elf_hdr.emagic[3] != 'F') {
154
0
    return 0;
155
0
  }
156
157
0
  jitdump_fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666);
158
0
  if (jitdump_fd < 0) {
159
0
    return 0;
160
0
  }
161
162
0
  jitdump_mem = mmap(NULL,
163
0
      sysconf(_SC_PAGESIZE),
164
0
      PROT_READ|PROT_EXEC,
165
0
      MAP_PRIVATE, jitdump_fd, 0);
166
167
0
  if (jitdump_mem == MAP_FAILED) {
168
0
    close(jitdump_fd);
169
0
    jitdump_fd = -1;
170
0
    return 0;
171
0
  }
172
173
0
  memset(&jit_hdr, 0, sizeof(jit_hdr));
174
0
  jit_hdr.magic           = IR_PERF_JITDUMP_HEADER_MAGIC;
175
0
  jit_hdr.version         = IR_PERF_JITDUMP_HEADER_VERSION;
176
0
  jit_hdr.size            = sizeof(jit_hdr);
177
0
  jit_hdr.elf_mach_target = elf_hdr.machine;
178
0
  jit_hdr.process_id      = getpid();
179
0
  jit_hdr.time_stamp      = ir_perf_timestamp();
180
0
  jit_hdr.flags           = 0;
181
0
  if (write(jitdump_fd, &jit_hdr, sizeof(jit_hdr)) != sizeof(jit_hdr)) {
182
0
    return 0;
183
0
  }
184
0
  return 1;
185
0
}
186
187
int ir_perf_jitdump_close(void)
188
0
{
189
0
  int ret = 1;
190
191
0
  if (jitdump_fd >= 0) {
192
0
    ir_perf_jitdump_record rec;
193
194
0
    rec.event      = IR_PERF_JITDUMP_RECORD_CLOSE;
195
0
    rec.size       = sizeof(rec);
196
0
    rec.time_stamp = ir_perf_timestamp();
197
0
    if (write(jitdump_fd, &rec, sizeof(rec)) != sizeof(rec)) {
198
0
      ret = 0;
199
0
    }
200
0
    close(jitdump_fd);
201
202
0
    if (jitdump_mem != MAP_FAILED) {
203
0
      munmap(jitdump_mem, sysconf(_SC_PAGESIZE));
204
0
    }
205
0
  }
206
0
  return ret;
207
0
}
208
209
int ir_perf_jitdump_register(const char *name, const void *start, size_t size)
210
0
{
211
0
  if (jitdump_fd >= 0) {
212
0
    static uint64_t id = 1;
213
0
    ir_perf_jitdump_load_record rec;
214
0
    size_t len = strlen(name);
215
0
    uint32_t thread_id = 0;
216
0
#if defined(__linux__)
217
0
    thread_id = syscall(SYS_gettid);
218
#elif defined(__APPLE__)
219
    uint64_t thread_id_u64;
220
    pthread_threadid_np(NULL, &thread_id_u64);
221
    thread_id = (uint32_t) thread_id_u64;
222
#elif defined(__FreeBSD__)
223
    long tid;
224
    thr_self(&tid);
225
    thread_id = (uint32_t)tid;
226
#elif defined(__OpenBSD__)
227
    thread_id = getthrid();
228
#elif defined(__NetBSD__)
229
    thread_id = _lwp_self();
230
#elif defined(__DragonFly__)
231
    thread_id = lwp_gettid();
232
#elif defined(__sun)
233
    thread_id = thr_self();
234
#endif
235
236
0
    memset(&rec, 0, sizeof(rec));
237
0
    rec.hdr.event      = IR_PERF_JITDUMP_RECORD_LOAD;
238
0
    rec.hdr.size       = sizeof(rec) + len + 1 + size;
239
0
    rec.hdr.time_stamp = ir_perf_timestamp();
240
0
    rec.process_id     = getpid();
241
0
    rec.thread_id      = thread_id;
242
0
    rec.vma            = (uint64_t)(uintptr_t)start;
243
0
    rec.code_address   = (uint64_t)(uintptr_t)start;
244
0
    rec.code_size      = (uint64_t)size;
245
0
    rec.code_id        = id++;
246
247
0
    if (write(jitdump_fd, &rec, sizeof(rec)) != sizeof(rec)
248
0
     || write(jitdump_fd, name, len + 1) < 0
249
0
     || write(jitdump_fd, start, size) < 0) {
250
0
      return 0;
251
0
    }
252
0
  }
253
0
  return 1;
254
0
}
255
256
void ir_perf_map_register(const char *name, const void *start, size_t size)
257
0
{
258
0
  static FILE *fp = NULL;
259
260
0
  if (!fp) {
261
0
    char filename[64];
262
263
0
    snprintf(filename, sizeof(filename), "/tmp/perf-%d.map", getpid());
264
0
    fp = fopen(filename, "w");
265
0
    if (!fp) {
266
0
      return;
267
0
    }
268
0
      setlinebuf(fp);
269
0
  }
270
0
  fprintf(fp, "%zx %zx %s\n", (size_t)(uintptr_t)start, size, name);
271
0
}