Skip to content

Commit d381449

Browse files
committed
[EventPipe] Cleanup Exhausted ThreadSessionStates
Throughout an EventPipe Session's lifetime, any new thread that writes an Event will induce a sequence of allocations including EventPipeThread, EventPipeThreadSessionStates, EventPipeBufferList, EventPipeBuffer, EventPipeSequencePoint, etc. Some of these objects are held throughout the lifetime of the session instead of the scope of their utility. Such behavior leads to a performance hit. For example, the buffer_manager's thread_session_state_list is used to discover new events, and without the proper cleanup, it will hold instances whose buffers have been exhausted and may never received a new event. To address a bloated thread_session_state_list, we add cleanup measures to remove instances whose buffer_list is both empty and the corresponding thread has been unregistered, indicating that the thread has exited and will not write new events.
1 parent 983f974 commit d381449

File tree

4 files changed

+143
-52
lines changed

4 files changed

+143
-52
lines changed

src/native/eventpipe/ep-buffer-manager.c

Lines changed: 98 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ buffer_manager_convert_buffer_to_read_only (
124124
EventPipeBufferManager *buffer_manager,
125125
EventPipeBuffer *new_read_buffer);
126126

127+
static
128+
void
129+
buffer_manager_remove_and_delete_thread_session_state (
130+
EventPipeBufferManager *buffer_manager,
131+
EventPipeThreadSessionState *thread_session_state);
132+
127133
/*
128134
* EventPipeBufferList.
129135
*/
@@ -258,6 +264,14 @@ ep_buffer_list_get_and_remove_head (EventPipeBufferList *buffer_list)
258264
return ret_buffer;
259265
}
260266

267+
bool
268+
ep_buffer_list_is_empty (const EventPipeBufferList *buffer_list)
269+
{
270+
EP_ASSERT (buffer_list != NULL);
271+
EP_ASSERT (ep_buffer_list_ensure_consistency ((EventPipeBufferList *)buffer_list));
272+
return buffer_list->head_buffer == NULL;
273+
}
274+
261275
bool
262276
buffer_manager_try_reserve_buffer (
263277
EventPipeBufferManager *buffer_manager,
@@ -701,6 +715,17 @@ buffer_manager_advance_to_non_empty_buffer (
701715

702716
// get the next buffer
703717
current_buffer = buffer_list->head_buffer;
718+
719+
if (!current_buffer && (ep_rt_volatile_load_uint32_t_without_barrier (ep_thread_get_unregistered_ref (ep_thread_session_state_get_thread (thread_session_state))) > 0)) {
720+
if (buffer_manager->sequence_point_alloc_budget != 0) {
721+
// The next sequence point needs to update the sequence number for this thread
722+
// Delay the thread session state removal until the next sequence point is written.
723+
ep_thread_session_state_set_delete_deferred (thread_session_state, true);
724+
} else {
725+
buffer_manager_remove_and_delete_thread_session_state (buffer_manager, thread_session_state);
726+
}
727+
}
728+
704729
if (!current_buffer || ep_buffer_get_creation_timestamp (current_buffer) >= before_timestamp) {
705730
// no more buffers in the list before this timestamp, we're done
706731
current_buffer = NULL;
@@ -1197,19 +1222,8 @@ ep_buffer_manager_write_all_buffers_to_file_v4 (
11971222

11981223
it = dn_list_it_next (it);
11991224

1200-
// if a session_state was exhausted during this sequence point, mark it for deletion
1201-
if (ep_thread_session_state_get_buffer_list (session_state)->head_buffer == NULL) {
1202-
// We don't hold the thread lock here, so it technically races with a thread getting unregistered. This is okay,
1203-
// because we will either not have passed the above if statement (there were events still in the buffers) or we
1204-
// will catch it at the next sequence point.
1205-
EventPipeThread* thread = ep_thread_session_state_get_thread (session_state);
1206-
EventPipeSession* session = ep_thread_session_state_get_session (session_state);
1207-
if (ep_rt_volatile_load_uint32_t_without_barrier (ep_thread_get_unregistered_ref (thread)) > 0) {
1208-
dn_vector_ptr_push_back (&session_states_to_delete, session_state);
1209-
dn_list_remove (buffer_manager->thread_session_state_list, session_state);
1210-
ep_thread_set_session_state (thread, session, NULL);
1211-
}
1212-
}
1225+
if (ep_thread_session_state_get_delete_deferred (session_state))
1226+
dn_vector_ptr_push_back (&session_states_to_delete, session_state);
12131227
}
12141228
EP_SPIN_LOCK_EXIT (&buffer_manager->rt_lock, section2)
12151229

@@ -1218,35 +1232,12 @@ ep_buffer_manager_write_all_buffers_to_file_v4 (
12181232

12191233
// we are done with the sequence point
12201234
EP_SPIN_LOCK_ENTER (&buffer_manager->rt_lock, section3)
1221-
buffer_manager_dequeue_sequence_point (buffer_manager);
1222-
EP_SPIN_LOCK_EXIT (&buffer_manager->rt_lock, section3)
1223-
}
1224-
1225-
// There are sequence points created during this flush and we've marked session states for deletion.
1226-
// We need to remove these from the internal maps of the subsequent Sequence Points
1227-
if (dn_vector_ptr_size (&session_states_to_delete) > 0) {
1228-
EP_SPIN_LOCK_ENTER (&buffer_manager->rt_lock, section4)
1229-
if (buffer_manager_try_peek_sequence_point (buffer_manager, &sequence_point)) {
1230-
DN_LIST_FOREACH_BEGIN (EventPipeSequencePoint *, current_sequence_point, buffer_manager->sequence_points) {
1231-
// foreach (session_state in session_states_to_delete)
1232-
DN_VECTOR_PTR_FOREACH_BEGIN (EventPipeThreadSessionState *, thread_session_state, &session_states_to_delete) {
1233-
dn_umap_it_t found = dn_umap_ptr_uint32_find (ep_sequence_point_get_thread_sequence_numbers (current_sequence_point), thread_session_state);
1234-
if (!dn_umap_it_end (found)) {
1235-
dn_umap_erase (found);
1236-
// every entry of this map was holding an extra ref to the thread (see: ep-event-instance.{h|c})
1237-
ep_thread_release (ep_thread_session_state_get_thread (thread_session_state));
1238-
}
1239-
} DN_VECTOR_PTR_FOREACH_END;
1240-
} DN_LIST_FOREACH_END;
1241-
}
1242-
1243-
// foreach (thread_session_state in session_states_to_delete)
12441235
DN_VECTOR_PTR_FOREACH_BEGIN (EventPipeThreadSessionState *, thread_session_state, &session_states_to_delete) {
1245-
EP_ASSERT (thread_session_state != NULL);
1246-
ep_thread_session_state_free (thread_session_state);
1236+
if (ep_thread_session_state_get_delete_deferred (thread_session_state))
1237+
buffer_manager_remove_and_delete_thread_session_state (buffer_manager, thread_session_state);
12471238
} DN_VECTOR_PTR_FOREACH_END;
1248-
1249-
EP_SPIN_LOCK_EXIT (&buffer_manager->rt_lock, section4)
1239+
buffer_manager_dequeue_sequence_point (buffer_manager);
1240+
EP_SPIN_LOCK_EXIT (&buffer_manager->rt_lock, section3)
12501241
}
12511242

12521243
ep_on_exit:
@@ -1330,6 +1321,73 @@ ep_buffer_manager_ensure_consistency (EventPipeBufferManager *buffer_manager)
13301321
}
13311322
#endif
13321323

1324+
static
1325+
void
1326+
buffer_manager_remove_thread_session_state_from_sequence_points (
1327+
EventPipeBufferManager *buffer_manager,
1328+
EventPipeThreadSessionState *thread_session_state)
1329+
{
1330+
EP_ASSERT (buffer_manager != NULL);
1331+
EP_ASSERT (thread_session_state != NULL);
1332+
1333+
ep_buffer_manager_requires_lock_held (buffer_manager);
1334+
1335+
DN_LIST_FOREACH_BEGIN (EventPipeSequencePoint *, current_sequence_point, buffer_manager->sequence_points) {
1336+
dn_umap_it_t found = dn_umap_ptr_uint32_find (ep_sequence_point_get_thread_sequence_numbers (current_sequence_point), thread_session_state);
1337+
if (!dn_umap_it_end (found)) {
1338+
dn_umap_erase (found);
1339+
// buffer_manager_init_sequence_point_thread_list - every entry of this map was holding an extra ref to the thread
1340+
ep_thread_release (ep_thread_session_state_get_thread (thread_session_state));
1341+
}
1342+
} DN_LIST_FOREACH_END;
1343+
}
1344+
1345+
static
1346+
void
1347+
buffer_manager_remove_and_delete_thread_session_state (
1348+
EventPipeBufferManager *buffer_manager,
1349+
EventPipeThreadSessionState *thread_session_state)
1350+
{
1351+
EP_ASSERT (buffer_manager != NULL);
1352+
EP_ASSERT (thread_session_state != NULL);
1353+
1354+
ep_buffer_manager_requires_lock_held (buffer_manager);
1355+
1356+
dn_list_remove (buffer_manager->thread_session_state_list, thread_session_state);
1357+
ep_thread_set_session_state (ep_thread_session_state_get_thread (thread_session_state), ep_thread_session_state_get_session (thread_session_state), NULL);
1358+
buffer_manager_remove_thread_session_state_from_sequence_points (buffer_manager, thread_session_state);
1359+
ep_thread_session_state_free (thread_session_state);
1360+
}
1361+
1362+
void
1363+
ep_buffer_manager_remove_and_delete_thread_session_state (
1364+
EventPipeBufferManager *buffer_manager,
1365+
EventPipeThreadSessionState *thread_session_state)
1366+
{
1367+
EP_ASSERT (buffer_manager != NULL);
1368+
EP_ASSERT (thread_session_state != NULL);
1369+
1370+
ep_buffer_manager_requires_lock_not_held (buffer_manager);
1371+
1372+
EP_SPIN_LOCK_ENTER (&buffer_manager->rt_lock, section1)
1373+
buffer_manager_remove_and_delete_thread_session_state (buffer_manager, thread_session_state);
1374+
EP_SPIN_LOCK_EXIT (&buffer_manager->rt_lock, section1)
1375+
1376+
ep_on_exit:
1377+
ep_buffer_manager_requires_lock_not_held (buffer_manager);
1378+
return;
1379+
1380+
ep_on_error:
1381+
ep_exit_error_handler ();
1382+
}
1383+
1384+
bool
1385+
ep_buffer_manager_uses_sequence_points (const EventPipeBufferManager *buffer_manager)
1386+
{
1387+
EP_ASSERT (buffer_manager != NULL);
1388+
return buffer_manager->sequence_point_alloc_budget != 0;
1389+
}
1390+
13331391
#endif /* !defined(EP_INCLUDE_SOURCE_FILES) || defined(EP_FORCE_INCLUDE_SOURCE_FILES) */
13341392
#endif /* ENABLE_PERFTRACING */
13351393

src/native/eventpipe/ep-buffer-manager.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ ep_buffer_list_insert_tail (
7474
EventPipeBuffer *
7575
ep_buffer_list_get_and_remove_head (EventPipeBufferList *buffer_list);
7676

77+
bool
78+
ep_buffer_list_is_empty (const EventPipeBufferList *buffer_list);
79+
7780
#ifdef EP_CHECKED_BUILD
7881
bool
7982
ep_buffer_list_ensure_consistency (EventPipeBufferList *buffer_list);
@@ -228,5 +231,13 @@ bool
228231
ep_buffer_manager_ensure_consistency (EventPipeBufferManager *buffer_manager);
229232
#endif
230233

234+
void
235+
ep_buffer_manager_remove_and_delete_thread_session_state (
236+
EventPipeBufferManager *buffer_manager,
237+
EventPipeThreadSessionState *thread_session_state);
238+
239+
bool
240+
ep_buffer_manager_uses_sequence_points (const EventPipeBufferManager *buffer_manager);
241+
231242
#endif /* ENABLE_PERFTRACING */
232243
#endif /* __EVENTPIPE_BUFFERMANAGER_H__ */

src/native/eventpipe/ep-thread.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,22 @@ ep_thread_unregister (EventPipeThread *thread)
119119

120120
ep_return_false_if_nok (thread != NULL);
121121

122+
for (uint32_t i = 0; i < EP_MAX_NUMBER_OF_SESSIONS; ++i) {
123+
EventPipeThreadSessionState *thread_session_state = (EventPipeThreadSessionState *)ep_rt_volatile_load_ptr ((volatile void **)(&thread->session_state [i]));
124+
if (thread_session_state == NULL)
125+
continue;
126+
127+
EventPipeBufferManager *buffer_manager = ep_session_get_buffer_manager (thread_session_state->session);
128+
EP_ASSERT (buffer_manager != NULL);
129+
130+
if (ep_buffer_list_is_empty (thread_session_state->buffer_list)) {
131+
if (ep_buffer_manager_uses_sequence_points (buffer_manager))
132+
thread_session_state->delete_deferred = true;
133+
else
134+
ep_buffer_manager_remove_and_delete_thread_session_state (buffer_manager, thread_session_state);
135+
}
136+
}
137+
122138
bool found = false;
123139
EP_SPIN_LOCK_ENTER (&_ep_threads_lock, section1)
124140
// Remove ourselves from the global list
@@ -321,9 +337,8 @@ ep_thread_session_state_alloc (
321337

322338
instance->buffer_list = ep_buffer_list_alloc (buffer_manager, thread);
323339
ep_raise_error_if_nok (instance->buffer_list != NULL);
324-
#ifdef EP_CHECKED_BUILD
325-
instance->buffer_manager = buffer_manager;
326-
#endif
340+
341+
instance->delete_deferred = false;
327342

328343
ep_on_exit:
329344
return instance;
@@ -367,7 +382,7 @@ EventPipeBuffer *
367382
ep_thread_session_state_get_write_buffer (const EventPipeThreadSessionState *thread_session_state)
368383
{
369384
EP_ASSERT (thread_session_state != NULL);
370-
ep_buffer_manager_requires_lock_held (thread_session_state->buffer_manager);
385+
ep_buffer_manager_requires_lock_held (ep_session_get_buffer_manager (thread_session_state->session));
371386

372387
EP_ASSERT ((thread_session_state->write_buffer == NULL) || (ep_buffer_get_volatile_state (thread_session_state->write_buffer) == EP_BUFFER_STATE_WRITABLE));
373388
return (EventPipeBuffer*) ep_rt_volatile_load_ptr_without_barrier ((volatile void **)&thread_session_state->write_buffer);
@@ -388,7 +403,7 @@ ep_thread_session_state_set_write_buffer (
388403
{
389404
EP_ASSERT (thread_session_state != NULL);
390405

391-
ep_buffer_manager_requires_lock_held (thread_session_state->buffer_manager);
406+
ep_buffer_manager_requires_lock_held (ep_session_get_buffer_manager (thread_session_state->session));
392407

393408
EP_ASSERT ((new_buffer == NULL) || (ep_buffer_get_volatile_state (new_buffer) == EP_BUFFER_STATE_WRITABLE));
394409
EP_ASSERT ((thread_session_state->write_buffer == NULL) || (ep_buffer_get_volatile_state (thread_session_state->write_buffer) == EP_BUFFER_STATE_WRITABLE));
@@ -401,7 +416,7 @@ ep_thread_session_state_get_buffer_list (const EventPipeThreadSessionState *thre
401416
{
402417
EP_ASSERT (thread_session_state != NULL);
403418

404-
ep_buffer_manager_requires_lock_held (thread_session_state->buffer_manager);
419+
ep_buffer_manager_requires_lock_held (ep_session_get_buffer_manager (thread_session_state->session));
405420

406421
return thread_session_state->buffer_list;
407422
}

src/native/eventpipe/ep-thread.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -273,10 +273,6 @@ struct _EventPipeThreadSessionState_Internal {
273273
// The sequence number of the last event that was read, only
274274
// updated/read by the reader thread.
275275
uint32_t last_read_sequence_number;
276-
#ifdef EP_CHECKED_BUILD
277-
// protected by the buffer manager lock.
278-
EventPipeBufferManager *buffer_manager;
279-
#endif
280276
// The number of events that were attempted to be written by this
281277
// thread. Each event was either successfully recorded in a buffer
282278
// or it was dropped.
@@ -297,6 +293,15 @@ struct _EventPipeThreadSessionState_Internal {
297293
// buffers are later read and there are fewer than X events timestamped
298294
// prior to the sequence point we can be certain the others were dropped.
299295
volatile uint32_t sequence_number;
296+
// An ThreadSessionState is no longer needed when the following are met:
297+
// 1. The thread is unregistered - No more events can be written.
298+
// 2. The buffers are empty - No more events need to be flushed.
299+
// 3. The following SequencePoint is written - No more mapped sequence
300+
// numbers need to be updated.
301+
// If this is set to true, that means we have detected 1 and 2 through
302+
// buffer_manager_advance_to_non_empty_buffer, but sequence points are active,
303+
// so we defer the deletion until the next sequence point updates its map.
304+
bool delete_deferred;
300305
};
301306

302307
#if !defined(EP_INLINE_GETTER_SETTER) && !defined(EP_IMPL_THREAD_GETTER_SETTER)
@@ -307,8 +312,10 @@ struct _EventPipeThreadSessionState {
307312

308313
EP_DEFINE_GETTER_REF(EventPipeThreadSessionState *, thread_session_state, EventPipeThreadHolder *, thread_holder)
309314
EP_DEFINE_GETTER(EventPipeThreadSessionState *, thread_session_state, EventPipeSession *, session)
310-
EP_DEFINE_GETTER(EventPipeThreadSessionState *, thread_session_state, uint32_t, last_read_sequence_number);
311-
EP_DEFINE_SETTER(EventPipeThreadSessionState *, thread_session_state, uint32_t, last_read_sequence_number);
315+
EP_DEFINE_GETTER(EventPipeThreadSessionState *, thread_session_state, uint32_t, last_read_sequence_number)
316+
EP_DEFINE_SETTER(EventPipeThreadSessionState *, thread_session_state, uint32_t, last_read_sequence_number)
317+
EP_DEFINE_GETTER(EventPipeThreadSessionState *, thread_session_state, bool, delete_deferred)
318+
EP_DEFINE_SETTER(EventPipeThreadSessionState *, thread_session_state, bool, delete_deferred)
312319

313320
EventPipeThreadSessionState *
314321
ep_thread_session_state_alloc (

0 commit comments

Comments
 (0)