1/**
2 * \file
3 * \brief Barrelfish trace server, Version 2, NFS variant
4 */
5
6/*
7 * Copyright (c) 2017, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <errno.h>
18
19#include <barrelfish/barrelfish.h>
20#include <barrelfish/dispatch.h>
21#include <barrelfish/dispatcher_arch.h>
22#include <barrelfish/lmp_endpoints.h>
23#include <barrelfish/event_queue.h>
24#include <barrelfish/nameservice_client.h>
25#include <trace/trace.h>
26#include <vfs/vfs.h>
27
28#include <flounder/flounder.h>
29#include <if/monitor_defs.h>
30
31#include <if/empty_defs.h>
32
33#define DEBUG if (0) printf
34
35/// Buffer size for temp buffer during dumping: 128MB
36#define BFSCOPE_BUFLEN (128UL * 1024 * 1024)
37
38/// Use /bfscope as mount point for NFS share
39#define MOUNT_DIR "/bfscope"
40/// try to make NFS writes fit in ethernet frame, copied from lib/vfs/vfs_nfs.c
41//#define MAX_NFS_CHUNK 1420
42static vfs_handle_t dump_file_vh;
43
44/// If we are in autoflush is enabled, bfscope can itself determine to flush. In
45/// that case, we don't want to notify anyone after doing a locally initiated flush.
46static bool local_flush = false;
47
48static char *trace_buf = NULL;
49static bool dump_in_progress = false;
50
51struct bfscope_ack_send_state {
52    struct event_queue_node qnode;
53    struct monitor_binding *monitor_binding;
54};
55
56static void bfscope_send_flush_ack_cont(void* arg)
57{
58    errval_t err;
59
60    struct bfscope_ack_send_state *state = (struct bfscope_ack_send_state*) arg;
61    struct monitor_binding *monitor_binding = state->monitor_binding;
62
63    err = monitor_binding->tx_vtbl.bfscope_flush_ack(monitor_binding, MKCONT(free, state));
64
65    if (err_is_ok(err)) {
66        event_mutex_unlock(&monitor_binding->mutex);
67    } else if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
68        err = monitor_binding->register_send(monitor_binding,
69                monitor_binding->waitset,
70                MKCONT(&bfscope_send_flush_ack_cont, state));
71        assert(err_is_ok(err));
72    } else {
73        event_mutex_unlock(&monitor_binding->mutex);
74        //TODO: Error handling
75        USER_PANIC_ERR(err, "Could not send flush ack message to monitor of bfscope");
76    }
77}
78
79static void bfscope_send_flush_ack_to_monitor(void)
80{
81    struct bfscope_ack_send_state *state =
82        malloc(sizeof(struct bfscope_ack_send_state));
83
84    state->monitor_binding = get_monitor_binding();
85
86    event_mutex_enqueue_lock(&state->monitor_binding->mutex, &state->qnode,
87            MKCLOSURE(&bfscope_send_flush_ack_cont, state));
88}
89
90static void bfscope_trace_dump_finished(void)
91{
92    dump_in_progress = false;
93
94    if (!local_flush) {
95        bfscope_send_flush_ack_to_monitor();
96    } else {
97        // Locally initiated flush is finished.
98        local_flush = false;
99    }
100}
101
102static void bfscope_trace_dump(void)
103{
104    errval_t err;
105    int number_of_events = 0;
106    size_t trace_length = 0;
107
108    if(dump_in_progress) {
109        // Currently there is already a dump in progress, do nothing.
110        return;
111    }
112
113    // Acquire the trace buffer
114    trace_length = trace_dump(trace_buf, BFSCOPE_BUFLEN, &number_of_events);
115
116    DEBUG("bfscope: trace length %zu, nr. of events %d\n", trace_length, number_of_events);
117
118    if (trace_length <= 0 || number_of_events <= 0) {
119        DEBUG("bfscope: trace length too small, not dumping.\n");
120        goto finish;
121    }
122
123    dump_in_progress = true;
124
125    DEBUG("dumping %zu bytes to NFS share\n", trace_length);
126
127    size_t total_written = 0;
128    while (total_written < trace_length) {
129        size_t written = 0;
130        char *bufptr = trace_buf + total_written;
131        size_t bytes = trace_length - total_written;
132        // artificially limit nfs writes
133        //bytes = bytes > MAX_NFS_CHUNK ? MAX_NFS_CHUNK : bytes;
134        DEBUG("dumping to NFS share: trying to write %zu bytes\n", bytes);
135        err = vfs_write(dump_file_vh, bufptr, bytes, &written);
136        if (err_is_fail(err)) {
137            DEBUG_ERR(err, "vfs_write while dumping trace");
138        }
139        total_written += written;
140        DEBUG("dumping to NFS share: %zu/%zu bytes written\n", total_written, trace_length);
141    }
142    DEBUG("dump to NFS share done!\n");
143
144finish:
145    bfscope_trace_dump_finished();
146}
147
148static void bfscope_handle_flush_msg(struct monitor_binding *mb, iref_t iref)
149{
150    printf("bfscope flush request message received!\n");
151
152    bfscope_trace_dump();
153}
154
155static void export_cb(void *st, errval_t err, iref_t iref)
156{
157    if (err_is_fail(err)) {
158        USER_PANIC_ERR(err, "export failed");
159    }
160
161    printf("bfscope: exported at iref %"PRIuIREF"\n", iref);
162
163    // register this iref with the name service
164    err = nameservice_register("bfscope", iref);
165    if (err_is_fail(err)) {
166        USER_PANIC_ERR(err, "nameservice_register failed");
167    }
168}
169
170static errval_t connect_cb(void *st, struct empty_binding *b)
171{
172    USER_PANIC("bfscope: connect_cb got called");
173}
174
175int main(int argc, char**argv)
176{
177#ifndef CONFIG_TRACE
178    // bail - no tracing support
179    printf("%*s: Error, no tracing support, cannot start bfscope\n",
180           DISP_NAME_LEN, disp_name());
181    printf("%.*s: recompile with trace = TRUE in build/hake/Config.hs\n",
182           DISP_NAME_LEN, disp_name());
183    return -1;
184#endif
185
186    errval_t err;
187    /* connect to nfs server according to command line arguments */
188    vfs_init();
189
190    if(argc < 3) {
191        printf("Usage: %s mount-URL filepath\n", argv[0]);
192        printf("Example: %s nfs://10.110.4.4/mnt/local/nfs/gerbesim  /bfscope/trace.data\n\n"
193               "    This example mounts the nfs share /mnt/local/nfs/gerbesim\n"
194               "    on 10.110.4.4 (emmentaler1) in the hardcoded directory /bfscope\n"
195               "    in the barrelfish VFS, and then writes the trace data into the\n"
196               "    file trace.data in the NFS share.\n",
197                argv[0]);
198        exit(EXIT_FAILURE);
199    }
200
201    err = vfs_mount(MOUNT_DIR, argv[1]);
202    if(err_is_fail(err)) {
203        DEBUG_ERR(err, "vfs_mount");
204    }
205    assert(err_is_ok(err));
206    debug_printf("NFS mount done\n");
207
208    // open/create file
209    err = vfs_create(argv[2], &dump_file_vh);
210    if (err_is_fail(err)) {
211        DEBUG_ERR(err, "creating dump file");
212        return 1;
213    }
214
215    // do a write to the file, to check that we're able to write
216    char header[] = "# bfscope trace dump\n";
217    size_t written = 0;
218    // take sizeof(header)-1 to not write null byte to file
219    err = vfs_write(dump_file_vh, header, sizeof(header)-1, &written);
220    if (err_is_fail(err)) {
221        DEBUG_ERR(err, "writing header to trace dump file");
222        return 1;
223    }
224
225    // Allocate the outgoing buffer
226    if (trace_buf == NULL) {
227        trace_buf = malloc(BFSCOPE_BUFLEN);
228    }
229    assert(trace_buf);
230
231    // Disable tracing for bfscope
232    dispatcher_handle_t handle = curdispatcher();
233    struct dispatcher_generic *disp = get_dispatcher_generic(handle);
234    disp->trace_buf = NULL;
235
236    printf("%.*s running on core %d\n", DISP_NAME_LEN, disp_name(),
237           disp_get_core_id());
238
239
240    // Export our empty interface
241    err = empty_export(NULL /* state pointer for connect/export callbacks */,
242            export_cb, connect_cb, get_default_waitset(),
243            IDC_EXPORT_FLAGS_DEFAULT);
244    if (err_is_fail(err)) {
245        USER_PANIC_ERR(err, "export failed");
246    }
247
248    // Register our message handlers with the monitor
249    struct monitor_binding *monitor_binding;
250    monitor_binding = get_monitor_binding();
251    monitor_binding->rx_vtbl.bfscope_flush_send = &bfscope_handle_flush_msg;
252
253    // dispatch events, and check whether to autoflush periodically
254    while (1) {
255        err = event_dispatch_non_block(get_default_waitset());
256        if (err == LIB_ERR_NO_EVENT) {
257            // It is ok that no event is dispatched, we want to check whether
258            // to autoflush, anyway
259            err = SYS_ERR_OK;
260        }
261        if (err_is_fail(err)) {
262            DEBUG_ERR(err, "in event_dispatch");
263            break;
264        }
265
266        DEBUG("bfscope: dispatched event, autoflush: %d\n",
267                ((struct trace_buffer*) trace_buffer_master)->autoflush);
268
269        // Check if we are in autoflush mode
270        if(((struct trace_buffer*) trace_buffer_master)->autoflush) {
271            local_flush = true;
272            bfscope_trace_dump();
273        }
274
275        thread_yield_dispatcher(NULL_CAP);
276    }
277}
278