Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm-ep] Implement DiagnosticServer and startup sessions for WebAssembly #72482

Merged
merged 88 commits into from
Jul 25, 2022
Merged
Show file tree
Hide file tree
Changes from 70 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
e73537f
[wasm] Enable the tracing component if threading is supported
lambdageek Jun 17, 2022
8c3360a
WIP: add a way to specify EP sessions in the MonoConfig
lambdageek May 23, 2022
6334ee7
Add a mechanism to copy startup configs into the runtime and session
lambdageek May 23, 2022
4897241
WIP: C side startup provider copying
lambdageek May 31, 2022
43d6ec8
WIP checkpoint. Do more from JS
lambdageek Jun 2, 2022
b232adb
checkpoint: starting a session at startup works
lambdageek Jun 3, 2022
3d22fe1
WIP: checkpoint add a controller and a webworker for DS
lambdageek Jun 6, 2022
ee53f05
WIP checkpoint EventPipeIPCSession class skeleton
lambdageek Jun 8, 2022
6ddb829
WIP checkpoint: runtime crashes; but WS from JS works
lambdageek Jun 8, 2022
5c4aa81
WIP: diagnostic server
lambdageek Jun 14, 2022
dfcc613
XXX PrintfDebuggingHacks
lambdageek Jun 13, 2022
0daa0e0
WIP some bits of the websocket worker
lambdageek Jun 14, 2022
377a50a
WIP some notes on diagnostics and JS workers
lambdageek Jun 15, 2022
4a17d5d
fix eslint
lambdageek Jun 17, 2022
fd8df98
debug printfs etc
lambdageek Jun 17, 2022
f235f55
WIP: start moving the diagnostic server to a JS pthread
lambdageek Jun 21, 2022
431e2c6
WIP: move things around
lambdageek Jun 22, 2022
563b31a
cleanup
lambdageek Jun 23, 2022
5e999a3
notes
lambdageek Jun 22, 2022
2906e1e
[diagnostic_server] wasm-specific fn_table
lambdageek Jun 23, 2022
6c249f0
[wasm-ep] disable DS connect ports in C, too
lambdageek Jun 24, 2022
567484c
asyncify finalize_startup; make 1 diagnostics init function
lambdageek Jun 24, 2022
199cc80
(not implemented) set browser-eventpipe sample to start a DS server
lambdageek Jun 24, 2022
c3e7fe1
ping in the DS server (not functional yet)
lambdageek Jun 24, 2022
e64e539
Start diagnostic server pthread
lambdageek Jun 24, 2022
00f73ba
WIP try to start the server
lambdageek Jun 25, 2022
f803c3c
WIP diagnostic server server
lambdageek Jun 29, 2022
58851b4
Add a mock WebSocket connection to simulate the remote end
lambdageek Jul 1, 2022
c497c59
cleanup diagnostics.ts
lambdageek Jul 5, 2022
74d8443
wasm-mt: use a PThreadSelf struct instead of a raw MessagePort
lambdageek Jul 5, 2022
9ce7348
Move all the EP and diagnostic server modules to one directory
lambdageek Jul 5, 2022
e8fd671
Refactor; remove dead code; rationalize controller
lambdageek Jul 5, 2022
e8aace4
WIP more server pthread impl
lambdageek Jul 6, 2022
54e618b
WIP: start adding queue from streaming thread to DS thread
lambdageek Jul 6, 2022
c1c4c86
[wasm] Incremental build and rollup warnings cleanups
lambdageek Jul 7, 2022
2cca7f0
WIP: work on wiring up DS protocol commands (mock); resume hack
lambdageek Jul 7, 2022
0609be7
WIP: set up a WasmIpcStream, create EP sessions from DS
lambdageek Jul 8, 2022
9e00d8b
WIP: starting to stream works; needs PTHREAD_POOL_SIZE bump
lambdageek Jul 9, 2022
50e035c
cleanup browser-eventpipe sample
lambdageek Jul 10, 2022
69d0ca7
refactor to simplify and cleanup; rm duplicate code
lambdageek Jul 11, 2022
032e41e
call mono_wasm_event_pipe_early_startup_callback from event_pipe init
lambdageek Jul 11, 2022
637f88a
if diagnostics server isn't enabled, don't try to initialize it
lambdageek Jul 11, 2022
33c7d39
WIP: start parsing binary commands
lambdageek Jul 11, 2022
7c2d7a1
WIP: start wiring up binary protocol parsing to the websocket
lambdageek Jul 13, 2022
e015940
WIP: Can parse a CollectTracing2 command and attempt to create a
lambdageek Jul 14, 2022
182872c
[wasm-ep] use the new PromiseController<T>
lambdageek Jul 15, 2022
0e7a6ba
get back to the server loop quicker by queueing the parsing in the mi…
lambdageek Jul 15, 2022
6ef49c3
update mock for binary ADVR_V1 message
lambdageek Jul 15, 2022
76abc4d
sample: don't suspend, and use a mock url
lambdageek Jul 15, 2022
32dbdd9
use better values for parse results
lambdageek Jul 16, 2022
c417200
parse a few more binary protocol commands
lambdageek Jul 16, 2022
c3c9a25
wasm_ipc_stream: wire up close command
lambdageek Jul 18, 2022
07c0691
Send proper OK messages in replies to binary protocol commands
lambdageek Jul 18, 2022
ab5e247
(testing) turn off the file session for now
lambdageek Jul 18, 2022
bde9669
TODO: handle WS connection failures
lambdageek Jul 18, 2022
c4b8cc1
remove em_asm(console.log); simplify wasm EP init
lambdageek Jul 19, 2022
051e257
remove debug output
lambdageek Jul 19, 2022
c75ee2c
remove debug output in startup
lambdageek Jul 19, 2022
bca82f0
cleanup wasm ipc stream impl
lambdageek Jul 19, 2022
83e4d29
put diagnostics mocks behind a const flag
lambdageek Jul 19, 2022
7f95626
don't build wasm-specific DS if threads are disabled
lambdageek Jul 19, 2022
033b0c4
refactor and cleanup
lambdageek Jul 19, 2022
0d1eefe
help treeshaking
lambdageek Jul 19, 2022
be703f2
update DS design notes
lambdageek Jul 19, 2022
7a63e7e
remove more printfs
lambdageek Jul 19, 2022
e5d1838
use PromiseController in more places
lambdageek Jul 19, 2022
d92d45f
remove more console.debug in startup
lambdageek Jul 19, 2022
26751d6
Merge remote-tracking branch 'origin/main' into wasm-ep-on-startup
lambdageek Jul 19, 2022
530fa1c
fix Windows build
lambdageek Jul 19, 2022
ca2f204
add MONO_WASM prefix to console logging outputs
lambdageek Jul 19, 2022
d1d088a
fix sample logic when startup session is disabled
lambdageek Jul 20, 2022
e6979c1
improve debug output for DS server
lambdageek Jul 20, 2022
0bf7c0b
bugfix: don't confuse buf_addr for the value stored in it
lambdageek Jul 20, 2022
b3676b5
slight refactor of EventPipeSocketConnection and more logging
lambdageek Jul 20, 2022
2d22094
review feedback
lambdageek Jul 20, 2022
88ef693
Merge remote-tracking branch 'origin/main' into wasm-ep-on-startup
lambdageek Jul 20, 2022
b8b2148
merge fixup
lambdageek Jul 20, 2022
0de9719
fix bug in queue_push_sync main thread detection
lambdageek Jul 20, 2022
17d12e8
fix typo
lambdageek Jul 21, 2022
6abe463
Merge remote-tracking branch 'origin/main' into wasm-ep-on-startup
lambdageek Jul 22, 2022
d2f0acd
merge fixup
lambdageek Jul 22, 2022
2e135e7
fix rollup warning when making the crypto worker
lambdageek Jul 22, 2022
7da9d41
add MONO_WASM: prefix to logging
lambdageek Jul 22, 2022
f4219a6
make diagnostic server mocking friendlier
lambdageek Jul 21, 2022
18e199a
disable mocking in the sample project by default
lambdageek Jul 22, 2022
4bdbbfa
Merge remote-tracking branch 'origin/main' into wasm-ep-on-startup
lambdageek Jul 24, 2022
cbd4691
fixup after merge
lambdageek Jul 24, 2022
2fe9a3e
review feedback
lambdageek Jul 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/mono/mono.proj
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@
<ItemGroup Condition="'$(TargetsBrowser)' == 'true'">
<_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_PERFTRACING_LISTEN_PORTS=1"/>
<_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_DEFAULT_LISTEN_PORT=1"/>
<_MonoCMakeArgs Include="-DFEATURE_PERFTRACING_DISABLE_CONNECT_PORTS=1" />
lateralusX marked this conversation as resolved.
Show resolved Hide resolved
</ItemGroup>

<!-- Components -->
Expand Down
295 changes: 295 additions & 0 deletions src/mono/mono/component/diagnostics_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@
#include <mono/utils/mono-publib.h>
#include <mono/utils/mono-compiler.h>
#include <eventpipe/ds-server.h>
#ifdef HOST_WASM
lateralusX marked this conversation as resolved.
Show resolved Hide resolved
#include <eventpipe/ep-ipc-stream.h>
#include <mono/component/event_pipe-wasm.h>
#include <mono/utils/mono-coop-semaphore.h>
#include <mono/utils/mono-threads-wasm.h>
#include <emscripten/emscripten.h>
#include <emscripten/threading.h>
#endif

static bool
diagnostics_server_available (void);

#if !defined (HOST_WASM) || defined (DISABLE_THREADS)
static MonoComponentDiagnosticsServer fn_table = {
{ MONO_COMPONENT_ITF_VERSION, &diagnostics_server_available },
&ds_server_init,
Expand All @@ -19,6 +28,291 @@ static MonoComponentDiagnosticsServer fn_table = {
&ds_server_disable
};

#else /* !defined (HOST_WASM) || defined (DISABLE_THREADS) */

static bool
ds_server_wasm_init (void);

static bool
ds_server_wasm_shutdown (void);

static void
ds_server_wasm_pause_for_diagnostics_monitor (void);

static void
ds_server_wasm_disable (void);

static MonoComponentDiagnosticsServer fn_table = {
{ MONO_COMPONENT_ITF_VERSION, &diagnostics_server_available },
&ds_server_wasm_init,
&ds_server_wasm_shutdown,
&ds_server_wasm_pause_for_diagnostics_monitor,
&ds_server_wasm_disable,
};

typedef struct _MonoWasmDiagnosticServerOptions {
int32_t suspend; /* set from JS! */
MonoCoopSem suspend_resume;
} MonoWasmDiagnosticServerOptions;

static MonoWasmDiagnosticServerOptions wasm_ds_options;
static pthread_t ds_thread_id;

extern void
mono_wasm_diagnostic_server_on_runtime_server_init (MonoWasmDiagnosticServerOptions *out_options);

EMSCRIPTEN_KEEPALIVE void
mono_wasm_diagnostic_server_resume_runtime_startup (void);

static bool
ds_server_wasm_init (void)
{
/* called on the main thread when the runtime is sufficiently initialized */
mono_coop_sem_init (&wasm_ds_options.suspend_resume, 0);
mono_wasm_diagnostic_server_on_runtime_server_init(&wasm_ds_options);
return true;
}


static bool
ds_server_wasm_shutdown (void)
{
return true;
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
}

static void
ds_server_wasm_pause_for_diagnostics_monitor (void)
{
/* wait until the DS receives a resume */
if (wasm_ds_options.suspend) {
const guint timeout = 50;
const guint warn_threshold = 5000;
guint cumulative_timeout = 0;
while (true) {
MonoSemTimedwaitRet res = mono_coop_sem_timedwait (&wasm_ds_options.suspend_resume, timeout, MONO_SEM_FLAGS_ALERTABLE);
if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS || res == MONO_SEM_TIMEDWAIT_RET_ALERTED)
break;
else {
/* timed out */
cumulative_timeout += timeout;
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
if (cumulative_timeout > warn_threshold) {
cumulative_timeout = 0;
}
}
}
}
}


static void
ds_server_wasm_disable (void)
{
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
}

/* Allocated by mono_wasm_diagnostic_server_create_thread,
* then ownership passed to server_thread.
*/
static char*
ds_websocket_url;

extern void mono_wasm_diagnostic_server_on_server_thread_created (char *websocket_url);

static void*
server_thread (void* unused_arg G_GNUC_UNUSED)
{
char* ws_url = g_strdup (ds_websocket_url);
g_free (ds_websocket_url);
ds_websocket_url = NULL;
mono_wasm_diagnostic_server_on_server_thread_created (ws_url);
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
// "exit" from server_thread, but keep the pthread alive and responding to events
emscripten_exit_with_live_runtime ();
}

gboolean
mono_wasm_diagnostic_server_create_thread (const char *websocket_url, pthread_t *out_thread_id)
{
pthread_t thread;

g_assert (!ds_websocket_url);
ds_websocket_url = g_strdup (websocket_url);
if (!pthread_create (&thread, NULL, server_thread, NULL)) {
*out_thread_id = thread;
return TRUE;
}
memset(out_thread_id, 0, sizeof(pthread_t));
return FALSE;
}

void
mono_wasm_diagnostic_server_thread_attach_to_runtime (void)
{
ds_thread_id = pthread_self();
MonoThread *thread = mono_thread_internal_attach (mono_get_root_domain ());
mono_thread_set_state (thread, ThreadState_Background);
mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_SAMPLE);
/* diagnostic server thread is now in GC Unsafe mode */
}

void
mono_wasm_diagnostic_server_post_resume_runtime (void)
{
if (wasm_ds_options.suspend) {
/* wake the main thread */
mono_coop_sem_post (&wasm_ds_options.suspend_resume);
}
}

#define QUEUE_CLOSE_SENTINEL ((uint8_t*)(intptr_t)-1)

/* single-reader single-writer one-element queue. See
* src/mono/wasm/runtime/diagnostics/server_pthread/stream-queue.ts
*/
typedef struct WasmIpcStreamQueue {
uint8_t *buf; /* or QUEUE_CLOSE_SENTINEL */
int32_t count;
volatile int32_t buf_full;
} WasmIpcStreamQueue;

extern void
mono_wasm_diagnostic_server_stream_signal_work_available (WasmIpcStreamQueue *queue, int32_t current_thread);

static void
queue_wake_reader (void *ptr) {
/* asynchronously invoked on the ds server thread by the writer. */
WasmIpcStreamQueue *q = (WasmIpcStreamQueue *)ptr;
mono_wasm_diagnostic_server_stream_signal_work_available (q, 0);
}

static void
queue_wake_reader_now (WasmIpcStreamQueue *q)
{
// call only from the diagnostic server thread!
mono_wasm_diagnostic_server_stream_signal_work_available (q, 1);
}

static int32_t
queue_push_sync (WasmIpcStreamQueue *q, const uint8_t *buf, uint32_t buf_size, uint32_t *bytes_written)
{
/* to be called on the writing thread */
/* single-writer, so there is no write contention */
q->buf = (uint8_t*)buf;
q->count = buf_size;
/* there's one instance where a thread other than the
* streaming thread is writing: in ep_file_initialize_file
* (called from ep_session_start_streaming), there's a write
* from either the main thread (if the streaming was deferred
* until ep_finish_init is called) or the diagnostic thread if
* the session is started later.
*/
pthread_t cur = pthread_self ();
gboolean will_wait = TRUE;
mono_atomic_store_i32 (&q->buf_full, 1);
if (cur == ds_thread_id) {
queue_wake_reader_now (q);
/* doesn't return until the buffer is empty again; no need to wait */
will_wait = FALSE;
} else {
emscripten_dispatch_to_thread (ds_thread_id, EM_FUNC_SIG_VI, &queue_wake_reader, NULL, q);
}
// wait until the reader reads the value
int r = 0;
if (G_LIKELY (will_wait)) {
gboolean is_browser_thread_inited = FALSE;
gboolean is_browser_thread = FALSE;
while (mono_atomic_load_i32 (&q->buf_full) != 0) {
if (G_UNLIKELY (is_browser_thread_inited)) {
is_browser_thread = mono_threads_wasm_is_browser_thread ();
is_browser_thread_inited = TRUE;
}
if (G_UNLIKELY (is_browser_thread)) {
/* can't use memory.atomic.wait32 on the main thread, spin instead */
/* this lets Emscripten run queued calls on the main thread */
emscripten_thread_sleep (1);
} else {
r = mono_wasm_atomic_wait_i32 (&q->buf_full, 1, -1);
if (G_UNLIKELY (r == 2)) {
/* timed out with infinite wait?? */
return -1;
}
/* if r == 0 (blocked and woken) or r == 1 (not equal), go around again and check if buf_full is now 0 */
}
}
}
if (bytes_written)
*bytes_written = buf_size;
return 0;
}

typedef struct {
IpcStream stream;
WasmIpcStreamQueue queue;
} WasmIpcStream;

static void
wasm_ipc_stream_free (void *self);
static bool
wasm_ipc_stream_read (void *self, uint8_t *buffer, uint32_t bytes_to_read, uint32_t *bytes_read, uint32_t timeout_ms);
static bool
wasm_ipc_stream_write (void *self, const uint8_t *buffer, uint32_t bytes_to_write, uint32_t *bytes_written, uint32_t timeout_ms);
static bool
wasm_ipc_stream_flush (void *self);
static bool
wasm_ipc_stream_close (void *self);

static IpcStreamVtable wasm_ipc_stream_vtable = {
&wasm_ipc_stream_free,
&wasm_ipc_stream_read,
&wasm_ipc_stream_write,
&wasm_ipc_stream_flush,
&wasm_ipc_stream_close,
};

EMSCRIPTEN_KEEPALIVE IpcStream *
mono_wasm_diagnostic_server_create_stream (void)
{
g_assert (G_STRUCT_OFFSET(WasmIpcStream, queue) == 4); // keep in sync with mono_wasm_diagnostic_server_get_stream_queue
WasmIpcStream *stream = g_new0 (WasmIpcStream, 1);
ep_ipc_stream_init (&stream->stream, &wasm_ipc_stream_vtable);
return &stream->stream;
}

static void
wasm_ipc_stream_free (void *self)
{
g_free (self);
}
static bool
wasm_ipc_stream_read (void *self, uint8_t *buffer, uint32_t bytes_to_read, uint32_t *bytes_read, uint32_t timeout_ms)
{
/* our reader is in JS */
g_assert_not_reached();
}
static bool
wasm_ipc_stream_write (void *self, const uint8_t *buffer, uint32_t bytes_to_write, uint32_t *bytes_written, uint32_t timeout_ms)
{
WasmIpcStream *stream = (WasmIpcStream *)self;
g_assert (timeout_ms == EP_INFINITE_WAIT); // pass it down to the queue if the timeout param starts being used
int r = queue_push_sync (&stream->queue, buffer, bytes_to_write, bytes_written);
return r == 0;
}

static bool
wasm_ipc_stream_flush (void *self)
{
return true;
}

static bool
wasm_ipc_stream_close (void *self)
{
WasmIpcStream *stream = (WasmIpcStream*)self;
// push the special buf value -1 to signal stream close.
int r = queue_push_sync (&stream->queue, QUEUE_CLOSE_SENTINEL, 0, NULL);
return r == 0;
}

#endif /* !defined (HOST_WASM) || defined (DISABLE_THREADS) */

static bool
diagnostics_server_available (void)
{
Expand All @@ -29,4 +323,5 @@ MonoComponentDiagnosticsServer *
mono_component_diagnostics_server_init (void)
{
return &fn_table;

lambdageek marked this conversation as resolved.
Show resolved Hide resolved
}
6 changes: 1 addition & 5 deletions src/mono/mono/component/event_pipe-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
#include "mono/component/event_pipe.h"
#include "mono/component/event_pipe-wasm.h"
#include "mono/metadata/components.h"
#ifdef HOST_WASM
#include <emscripten/emscripten.h>
#endif

static EventPipeSessionID _dummy_session_id;

Expand Down Expand Up @@ -524,12 +521,12 @@ mono_component_event_pipe_init (void)

EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_event_pipe_enable (const ep_char8_t *output_path,
IpcStream *ipc_stream,
uint32_t circular_buffer_size_in_mb,
const ep_char8_t *providers,
/* EventPipeSessionType session_type = EP_SESSION_TYPE_FILE, */
/* EventPipieSerializationFormat format = EP_SERIALIZATION_FORMAT_NETTRACE_V4, */
/* bool */ gboolean rundown_requested,
/* IpcStream stream = NULL, */
/* EventPipeSessionSycnhronousCallback sync_callback = NULL, */
/* void *callback_additional_data, */
MonoWasmEventPipeSessionID *out_session_id)
Expand All @@ -551,5 +548,4 @@ mono_wasm_event_pipe_session_disable (MonoWasmEventPipeSessionID session_id)
{
g_assert_not_reached ();
}

#endif /* HOST_WASM */
15 changes: 14 additions & 1 deletion src/mono/mono/component/event_pipe-wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#ifdef HOST_WASM

#include <pthread.h>
#include <emscripten.h>

G_BEGIN_DECLS
Expand All @@ -27,12 +28,12 @@ typedef uint32_t MonoWasmEventPipeSessionID;

EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_event_pipe_enable (const ep_char8_t *output_path,
IpcStream *ipc_stream,
uint32_t circular_buffer_size_in_mb,
const ep_char8_t *providers,
/* EventPipeSessionType session_type = EP_SESSION_TYPE_FILE, */
/* EventPipieSerializationFormat format = EP_SERIALIZATION_FORMAT_NETTRACE_V4, */
/* bool */ gboolean rundown_requested,
/* IpcStream stream = NULL, */
/* EventPipeSessionSycnhronousCallback sync_callback = NULL, */
/* void *callback_additional_data, */
MonoWasmEventPipeSessionID *out_session_id);
Expand All @@ -43,6 +44,18 @@ mono_wasm_event_pipe_session_start_streaming (MonoWasmEventPipeSessionID session
EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_event_pipe_session_disable (MonoWasmEventPipeSessionID session_id);

EMSCRIPTEN_KEEPALIVE gboolean
mono_wasm_diagnostic_server_create_thread (const char *websocket_url, pthread_t *out_thread_id);

EMSCRIPTEN_KEEPALIVE void
mono_wasm_diagnostic_server_thread_attach_to_runtime (void);

EMSCRIPTEN_KEEPALIVE void
mono_wasm_diagnostic_server_post_resume_runtime (void);

EMSCRIPTEN_KEEPALIVE IpcStream *
mono_wasm_diagnostic_server_create_stream (void);

G_END_DECLS

#endif /* HOST_WASM */
Expand Down
Loading