/* * Copyright 2009, Michael Lotz, mmlr@mlotz.ch * Distributed under the terms of the MIT License. */ #include #include #include #include static sem_id sRequestSem = -1; static char sCommandBuffer[1024]; static uint32 sCommandOffset = 0; static uint32 sCommandCount = 0; static int32 run_on_exit_loop(void *data) { while (true) { if (acquire_sem(sRequestSem) != B_OK) break; char *pointer = sCommandBuffer; while (sCommandCount > 0) { uint8 argCount = (uint8)pointer[0]; pointer++; const char *args[argCount]; for (uint8 i = 0; i < argCount; i++) { args[i] = pointer; uint32 length = strlen(pointer); pointer += length + 1; } thread_id thread = load_image(argCount, args, NULL); if (thread >= B_OK) resume_thread(thread); sCommandCount--; } sCommandOffset = 0; } return 0; } static int add_run_on_exit_command(int argc, char **argv) { if (argc < 2 || strcmp(argv[1], "--help") == 0) { print_debugger_command_usage(argv[0]); return 0; } if (argc > 256) { kprintf("too many arguments\n"); return 0; } size_t totalLength = 1; for (int32 i = 1; i < argc; i++) totalLength += strlen(argv[i]) + 1; if (sCommandOffset + totalLength > sizeof(sCommandBuffer)) { kprintf("no space left in command buffer\n"); return 0; } char *pointer = sCommandBuffer + sCommandOffset; *pointer++ = (char)(argc - 1); for (int32 i = 1; i < argc; i++) { strcpy(pointer, argv[i]); pointer += strlen(argv[i]) + 1; } sCommandOffset += totalLength; sCommandCount++; return 0; } static void exit_debugger() { if (sCommandCount > 0) release_sem_etc(sRequestSem, 1, B_DO_NOT_RESCHEDULE); } static status_t std_ops(int32 op, ...) { if (op == B_MODULE_INIT) { sRequestSem = create_sem(0, "run_on_exit_request"); if (sRequestSem < B_OK) return sRequestSem; thread_id thread = spawn_kernel_thread(&run_on_exit_loop, "run_on_exit_loop", B_NORMAL_PRIORITY, NULL); if (thread < B_OK) return thread; send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); add_debugger_command_etc("on_exit", &add_run_on_exit_command, "Adds a command to be run when leaving the kernel debugger", " []\n" "Adds a command to be run when leaving the kernel debugger.\n", 0); return B_OK; } else if (op == B_MODULE_UNINIT) { remove_debugger_command("on_exit", &add_run_on_exit_command); // deleting the sem will also cause the thread to exit delete_sem(sRequestSem); sRequestSem = -1; return B_OK; } return B_BAD_VALUE; } static struct debugger_module_info sModuleInfo = { { "debugger/run_on_exit/v1", B_KEEP_LOADED, &std_ops }, NULL, exit_debugger, NULL, NULL }; module_info *modules[] = { (module_info *)&sModuleInfo, NULL };