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