2 * udebug - debug ring buffer library
4 * Copyright (C) 2023 Felix Fietkau <nbd@nbd.name>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include "udebug-priv.h"
21 udebug_remote_get_handle(struct udebug
*ctx
)
23 struct udebug_client_msg
*msg
;
24 struct udebug_client_msg send_msg
= {
25 .type
= CL_MSG_GET_HANDLE
,
28 if (ctx
->poll_handle
>= 0 || !udebug_is_connected(ctx
))
31 msg
= udebug_send_and_wait(ctx
, &send_msg
, NULL
);
35 ctx
->poll_handle
= msg
->id
;
39 struct udebug_remote_buf
*udebug_remote_buf_get(struct udebug
*ctx
, uint32_t id
)
41 struct udebug_remote_buf
*rb
;
42 void *key
= (void *)(uintptr_t)id
;
44 return avl_find_element(&ctx
->remote_rings
, key
, rb
, node
);
47 int udebug_remote_buf_map(struct udebug
*ctx
, struct udebug_remote_buf
*rb
, uint32_t id
)
49 void *key
= (void *)(uintptr_t)id
;
50 struct udebug_client_msg
*msg
;
51 struct udebug_client_msg send_msg
= {
52 .type
= CL_MSG_RING_GET
,
57 if (rb
->buf
.data
|| !udebug_is_connected(ctx
))
60 msg
= udebug_send_and_wait(ctx
, &send_msg
, &fd
);
64 if (udebug_buf_open(&rb
->buf
, fd
, msg
->ring_size
, msg
->data_size
)) {
65 fprintf(stderr
, "failed to open fd %d, ring_size=%d, data_size=%d\n", fd
, msg
->ring_size
, msg
->data_size
);
72 avl_insert(&ctx
->remote_rings
, &rb
->node
);
77 void udebug_remote_buf_unmap(struct udebug
*ctx
, struct udebug_remote_buf
*rb
)
82 avl_delete(&ctx
->remote_rings
, &rb
->node
);
83 udebug_buf_free(&rb
->buf
);
89 int udebug_remote_buf_set_poll(struct udebug
*ctx
, struct udebug_remote_buf
*rb
, bool val
)
103 handle
= udebug_remote_get_handle(ctx
);
107 __atomic_fetch_or(&rb
->buf
.hdr
->notify
, 1UL << handle
, __ATOMIC_RELAXED
);
112 rbuf_advance_read_head(struct udebug_remote_buf
*rb
, uint32_t head
,
113 uint32_t *data_start
)
115 struct udebug_hdr
*hdr
= rb
->buf
.hdr
;
116 uint32_t min_head
= head
+ 1 - rb
->buf
.ring_size
;
117 uint32_t min_data
= u32_get(&hdr
->data_used
) - rb
->buf
.data_size
;
118 struct udebug_ptr
*last_ptr
= udebug_ring_ptr(hdr
, head
- 1);
120 if (!u32_get(&hdr
->head_hi
) && u32_sub(0, min_head
) > 0)
123 /* advance head to skip over any entries that are guaranteed
124 * to be overwritten now. final check will be performed after
127 if (u32_sub(rb
->head
, min_head
) < 0)
130 for (size_t i
= 0; i
< rb
->buf
.ring_size
; i
++) {
131 struct udebug_ptr
*ptr
= udebug_ring_ptr(hdr
, rb
->head
);
134 *data_start
= u32_get(&ptr
->start
);
135 __sync_synchronize();
138 if (ptr
->timestamp
> last_ptr
->timestamp
)
141 if (u32_sub(ptr
->start
, min_data
) > 0)
148 void udebug_remote_buf_set_start_time(struct udebug_remote_buf
*rb
, uint64_t ts
)
150 struct udebug_hdr
*hdr
= rb
->buf
.hdr
;
151 uint32_t head
= u32_get(&hdr
->head
);
152 uint32_t start
= rb
->head
, end
= head
;
158 rbuf_advance_read_head(rb
, head
, NULL
);
159 while ((diff
= u32_sub(end
, start
)) > 0) {
160 uint32_t cur
= start
+ diff
/ 2;
161 struct udebug_ptr
*ptr
;
163 ptr
= udebug_ring_ptr(hdr
, cur
);
164 if (ptr
->timestamp
> ts
)
173 void udebug_remote_buf_set_start_offset(struct udebug_remote_buf
*rb
, uint32_t idx
)
178 rb
->head
= rb
->buf
.hdr
->head
- idx
;
181 void udebug_remote_buf_set_flags(struct udebug_remote_buf
*rb
, uint64_t mask
, uint64_t set
)
183 struct udebug_hdr
*hdr
= rb
->buf
.hdr
;
189 __atomic_and_fetch(&hdr
->flags
[0], (uintptr_t)~mask
, __ATOMIC_RELAXED
);
191 __atomic_or_fetch(&hdr
->flags
[0], (uintptr_t)set
, __ATOMIC_RELAXED
);
193 if (sizeof(mask
) == sizeof(unsigned long))
198 __atomic_and_fetch(&hdr
->flags
[1], (uintptr_t)~mask
, __ATOMIC_RELAXED
);
200 __atomic_or_fetch(&hdr
->flags
[1], (uintptr_t)set
, __ATOMIC_RELAXED
);
203 struct udebug_snapshot
*
204 udebug_remote_buf_snapshot(struct udebug_remote_buf
*rb
)
206 struct udebug_hdr
*hdr
= rb
->buf
.hdr
;
207 struct udebug_ptr
*last_ptr
;
208 uint32_t data_start
, data_end
, data_used
;
209 struct udebug_snapshot
*s
= NULL
;
210 struct udebug_ptr
*ptr_buf
, *first_ptr
;
211 uint32_t data_size
, ptr_size
;
212 uint32_t head
, first_idx
;
213 uint32_t prev_read_head
= rb
->head
;
219 head
= u32_get(&hdr
->head
);
220 rbuf_advance_read_head(rb
, head
, &data_start
);
221 if (rb
->head
== head
)
224 first_idx
= rb
->head
;
225 first_ptr
= udebug_ring_ptr(hdr
, first_idx
);
226 last_ptr
= udebug_ring_ptr(hdr
, head
- 1);
227 data_end
= last_ptr
->start
+ last_ptr
->len
;
229 data_size
= data_end
- data_start
;
230 ptr_size
= head
- rb
->head
;
231 if (data_size
> rb
->buf
.data_size
|| ptr_size
> rb
->buf
.ring_size
) {
232 fprintf(stderr
, "Invalid data size: %x > %x, %x > %x\n", data_size
, (int)rb
->buf
.data_size
, ptr_size
, (int)rb
->buf
.ring_size
);
236 s
= calloc_a(sizeof(*s
),
237 &ptr_buf
, ptr_size
* sizeof(*ptr_buf
),
238 &data_buf
, data_size
);
240 s
->data
= memcpy(data_buf
, udebug_buf_ptr(&rb
->buf
, data_start
), data_size
);
241 s
->data_size
= data_size
;
242 s
->entries
= ptr_buf
;
243 s
->dropped
= rb
->head
- prev_read_head
;
245 if (first_ptr
> last_ptr
) {
246 struct udebug_ptr
*start_ptr
= udebug_ring_ptr(hdr
, 0);
247 struct udebug_ptr
*end_ptr
= udebug_ring_ptr(hdr
, rb
->buf
.ring_size
- 1) + 1;
248 uint32_t size
= end_ptr
- first_ptr
;
249 memcpy(s
->entries
, first_ptr
, size
* sizeof(*s
->entries
));
250 memcpy(s
->entries
+ size
, start_ptr
, (last_ptr
+ 1 - start_ptr
) * sizeof(*s
->entries
));
252 memcpy(s
->entries
, first_ptr
, (last_ptr
+ 1 - first_ptr
) * sizeof(*s
->entries
));
255 /* get a snapshot of the counter that indicates how much data has been
256 * clobbered by newly added entries */
257 __sync_synchronize();
258 data_used
= u32_get(&hdr
->data_used
) - rb
->buf
.data_size
;
260 s
->n_entries
= head
- first_idx
;
262 rbuf_advance_read_head(rb
, head
, NULL
);
263 if (s
->n_entries
< rb
->head
- first_idx
) {
269 s
->entries
+= rb
->head
- first_idx
;
270 s
->n_entries
-= rb
->head
- first_idx
;
271 while (s
->n_entries
> 0 &&
272 u32_sub(s
->entries
[0].start
, data_used
) < 0) {
278 for (size_t i
= 0; i
< s
->n_entries
; i
++)
279 s
->entries
[i
].start
-= data_start
;
281 s
->format
= hdr
->format
;
282 s
->sub_format
= hdr
->sub_format
;
283 s
->rbuf_idx
= (uint32_t)(uintptr_t)rb
->node
.key
;
290 bool udebug_snapshot_get_entry(struct udebug_snapshot
*s
, struct udebug_iter
*it
, unsigned int entry
)
292 struct udebug_ptr
*ptr
;
295 if (entry
>= s
->n_entries
)
298 ptr
= &s
->entries
[entry
];
299 if (ptr
->start
> s
->data_size
|| ptr
->len
> s
->data_size
||
300 ptr
->start
+ ptr
->len
> s
->data_size
)
304 it
->data
= s
->data
+ ptr
->start
;
306 it
->timestamp
= ptr
->timestamp
;
314 void udebug_iter_start(struct udebug_iter
*it
, struct udebug_snapshot
**s
, size_t n
)
316 memset(it
, 0, sizeof(*it
));
321 for (size_t i
= 0; i
< it
->n
; i
++)
322 it
->list
[i
]->iter_idx
= 0;
325 bool udebug_iter_next(struct udebug_iter
*it
)
328 struct udebug_snapshot
*s
;
332 for (size_t i
= 0; i
< it
->n
; i
++) {
333 struct udebug_ptr
*ptr
;
336 if (s
->iter_idx
>= s
->n_entries
)
339 ptr
= &s
->entries
[s
->iter_idx
];
340 if (cur
>= 0 && ptr
->timestamp
> cur_ts
)
344 cur_ts
= ptr
->timestamp
;
352 if (!udebug_snapshot_get_entry(s
, it
, s
->iter_idx
++))