clang-tidy: add Chrome-inspired checks
[lttng-tools.git] / src / bin / lttng-sessiond / lttng-syscall.cpp
... / ...
CommitLineData
1/*
2 * Copyright (C) 2014 David Goulet <dgoulet@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8#define _LGPL_SOURCE
9#include "kernel.hpp"
10#include "lttng-sessiond.hpp"
11#include "lttng-syscall.hpp"
12#include "utils.hpp"
13
14#include <common/common.hpp>
15#include <common/kernel-ctl/kernel-ctl.hpp>
16
17#include <stdbool.h>
18
19/* Global syscall table. */
20struct syscall *syscall_table;
21
22/* Number of entry in the syscall table. */
23static size_t syscall_table_nb_entry;
24
25/*
26 * Populate the system call table using the kernel tracer.
27 *
28 * Return 0 on success and the syscall table is allocated. On error, a negative
29 * value is returned.
30 */
31int syscall_init_table(int tracer_fd)
32{
33 int ret, fd, err;
34 size_t nbmem;
35 FILE *fp;
36 /* Syscall data from the kernel. */
37 size_t index = 0;
38 bool at_least_one_syscall = false;
39 uint32_t bitness;
40 char name[SYSCALL_NAME_LEN];
41
42#if (SYSCALL_NAME_LEN == 255)
43#define SYSCALL_NAME_LEN_SCANF_IS_A_BROKEN_API "254"
44#endif
45
46 DBG3("Syscall init system call table");
47
48 fd = kernctl_syscall_list(tracer_fd);
49 if (fd < 0) {
50 ret = fd;
51 PERROR("kernelctl syscall list");
52 goto error_ioctl;
53 }
54
55 fp = fdopen(fd, "r");
56 if (!fp) {
57 ret = -errno;
58 PERROR("syscall list fdopen");
59 goto error_fp;
60 }
61
62 nbmem = SYSCALL_TABLE_INIT_SIZE;
63 syscall_table = calloc<struct syscall>(nbmem);
64 if (!syscall_table) {
65 ret = -errno;
66 PERROR("syscall list zmalloc");
67 goto error;
68 }
69
70 while (fscanf(fp,
71 "syscall { index = %zu; \
72 name = %" SYSCALL_NAME_LEN_SCANF_IS_A_BROKEN_API "[^;]; \
73 bitness = %u; };\n",
74 &index,
75 name,
76 &bitness) == 3) {
77 at_least_one_syscall = true;
78 if (index >= nbmem) {
79 struct syscall *new_list;
80 size_t new_nbmem;
81
82 /* Double memory size. */
83 new_nbmem = std::max(index + 1, nbmem << 1);
84 if (new_nbmem > (SIZE_MAX / sizeof(*new_list))) {
85 /* Overflow, stop everything, something went really wrong. */
86 ERR("Syscall listing memory size overflow. Stopping");
87 free(syscall_table);
88 syscall_table = nullptr;
89 ret = -EINVAL;
90 goto error;
91 }
92
93 DBG("Reallocating syscall table from %zu to %zu entries", nbmem, new_nbmem);
94 new_list = (struct syscall *) realloc(syscall_table,
95 new_nbmem * sizeof(*new_list));
96 if (!new_list) {
97 ret = -errno;
98 PERROR("syscall list realloc");
99 goto error;
100 }
101
102 /* Zero out the new memory. */
103 memset(new_list + nbmem, 0, (new_nbmem - nbmem) * sizeof(*new_list));
104 nbmem = new_nbmem;
105 syscall_table = new_list;
106 }
107 syscall_table[index].index = index;
108 syscall_table[index].bitness = bitness;
109 if (lttng_strncpy(
110 syscall_table[index].name, name, sizeof(syscall_table[index].name))) {
111 ret = -EINVAL;
112 free(syscall_table);
113 syscall_table = nullptr;
114 goto error;
115 }
116 /*
117 DBG("Syscall name '%s' at index %" PRIu32 " of bitness %u",
118 syscall_table[index].name,
119 syscall_table[index].index,
120 syscall_table[index].bitness);
121 */
122 }
123
124 /* Index starts at 0. */
125 if (at_least_one_syscall) {
126 syscall_table_nb_entry = index + 1;
127 }
128
129 ret = 0;
130
131error:
132 err = fclose(fp);
133 if (err) {
134 PERROR("syscall list fclose");
135 }
136 return ret;
137
138error_fp:
139 err = close(fd);
140 if (err) {
141 PERROR("syscall list close");
142 }
143
144error_ioctl:
145 return ret;
146}
147
148/*
149 * Helper function for the list syscalls command that empty the temporary
150 * syscall hashtable used to track duplicate between 32 and 64 bit arch.
151 *
152 * This empty the hash table and destroys it after. After this, the pointer is
153 * unsuable. RCU read side lock MUST be acquired before calling this.
154 */
155static void destroy_syscall_ht(struct lttng_ht *ht)
156{
157 struct lttng_ht_iter iter;
158 struct syscall *ksyscall;
159
160 ASSERT_RCU_READ_LOCKED();
161
162 DBG3("Destroying syscall hash table.");
163
164 if (!ht) {
165 return;
166 }
167
168 cds_lfht_for_each_entry (ht->ht, &iter.iter, ksyscall, node.node) {
169 int ret;
170
171 ret = lttng_ht_del(ht, &iter);
172 LTTNG_ASSERT(!ret);
173 free(ksyscall);
174 }
175 lttng_ht_destroy(ht);
176}
177
178/*
179 * Allocate the given hashtable pointer.
180 *
181 * Return 0 on success else a negative LTTNG error value.
182 */
183static int init_syscall_ht(struct lttng_ht **ht)
184{
185 int ret;
186
187 *ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING);
188 if (!*ht) {
189 ret = -LTTNG_ERR_NOMEM;
190 } else {
191 ret = 0;
192 }
193
194 return ret;
195}
196
197/*
198 * Lookup a syscall in the given hash table by name.
199 *
200 * Return syscall object if found or else NULL.
201 */
202static struct syscall *lookup_syscall(struct lttng_ht *ht, const char *name)
203{
204 struct lttng_ht_node_str *node;
205 struct lttng_ht_iter iter;
206 struct syscall *ksyscall = nullptr;
207
208 LTTNG_ASSERT(ht);
209 LTTNG_ASSERT(name);
210
211 lttng_ht_lookup(ht, (void *) name, &iter);
212 node = lttng_ht_iter_get_node_str(&iter);
213 if (node) {
214 ksyscall = lttng::utils::container_of(node, &syscall::node);
215 }
216
217 return ksyscall;
218}
219
220/*
221 * Using the given syscall object in the events array with the bitness of the
222 * syscall at index in the syscall table.
223 */
224static void update_event_syscall_bitness(struct lttng_event *events,
225 unsigned int index,
226 unsigned int syscall_index)
227{
228 LTTNG_ASSERT(events);
229
230 if (syscall_table[index].bitness == 32) {
231 events[syscall_index].flags = (lttng_event_flag) (events[syscall_index].flags |
232 LTTNG_EVENT_FLAG_SYSCALL_32);
233 } else {
234 events[syscall_index].flags = (lttng_event_flag) (events[syscall_index].flags |
235 LTTNG_EVENT_FLAG_SYSCALL_64);
236 }
237}
238
239/*
240 * Allocate and initialize syscall object and add it to the given hashtable.
241 *
242 * Return 0 on success else -LTTNG_ERR_NOMEM.
243 */
244static int add_syscall_to_ht(struct lttng_ht *ht, unsigned int index, unsigned int syscall_index)
245{
246 int ret;
247 struct syscall *ksyscall;
248
249 LTTNG_ASSERT(ht);
250
251 ksyscall = zmalloc<struct syscall>();
252 if (!ksyscall) {
253 ret = -LTTNG_ERR_NOMEM;
254 goto error;
255 }
256
257 strncpy(ksyscall->name, syscall_table[index].name, sizeof(ksyscall->name));
258 ksyscall->bitness = syscall_table[index].bitness;
259 ksyscall->index = syscall_index;
260 lttng_ht_node_init_str(&ksyscall->node, ksyscall->name);
261 lttng_ht_add_unique_str(ht, &ksyscall->node);
262 ret = 0;
263
264error:
265 return ret;
266}
267
268/*
269 * List syscalls present in the kernel syscall global array, allocate and
270 * populate the events structure with them. Skip the empty syscall name.
271 *
272 * Return the number of entries in the array else a negative value.
273 */
274ssize_t syscall_table_list(struct lttng_event **_events)
275{
276 int i, index = 0;
277 ssize_t ret;
278 struct lttng_event *events;
279 /* Hash table used to filter duplicate out. */
280 struct lttng_ht *syscalls_ht = nullptr;
281
282 LTTNG_ASSERT(_events);
283
284 DBG("Syscall table listing.");
285
286 rcu_read_lock();
287
288 /*
289 * Allocate at least the number of total syscall we have even if some of
290 * them might not be valid. The count below will make sure to return the
291 * right size of the events array.
292 */
293 events = calloc<lttng_event>(syscall_table_nb_entry);
294 if (!events) {
295 PERROR("syscall table list zmalloc");
296 ret = -LTTNG_ERR_NOMEM;
297 goto error;
298 }
299
300 ret = init_syscall_ht(&syscalls_ht);
301 if (ret < 0) {
302 goto error;
303 }
304
305 for (i = 0; i < syscall_table_nb_entry; i++) {
306 struct syscall *ksyscall;
307
308 /* Skip empty syscalls. */
309 if (*syscall_table[i].name == '\0') {
310 continue;
311 }
312
313 ksyscall = lookup_syscall(syscalls_ht, syscall_table[i].name);
314 if (ksyscall) {
315 update_event_syscall_bitness(events, i, ksyscall->index);
316 continue;
317 }
318
319 ret = add_syscall_to_ht(syscalls_ht, i, index);
320 if (ret < 0) {
321 goto error;
322 }
323
324 /* Copy the event information in the event's array. */
325 strncpy(events[index].name, syscall_table[i].name, sizeof(events[index].name));
326 update_event_syscall_bitness(events, i, index);
327 events[index].type = LTTNG_EVENT_SYSCALL;
328 /* This makes the command line not print the enabled/disabled field. */
329 events[index].enabled = -1;
330 index++;
331 }
332
333 destroy_syscall_ht(syscalls_ht);
334 *_events = events;
335 rcu_read_unlock();
336 return index;
337
338error:
339 destroy_syscall_ht(syscalls_ht);
340 free(events);
341 rcu_read_unlock();
342 return ret;
343}
This page took 0.023174 seconds and 4 git commands to generate.