// This program reads in output from the trace_alsa shared object library // ALSA interceptor and replays the ALSA commands. // // Compile with: gcc -o retrace_alsa retrace_alsa.c -lasound -lrt #include #include #include #include typedef struct HwParams { unsigned long long id; snd_pcm_hw_params_t *params; struct HwParams *next; } HwParams; typedef struct SwParams { unsigned long long id; snd_pcm_sw_params_t *params; struct SwParams *next; } SwParams; typedef enum { Func_snd_pcm_close, Func_snd_pcm_drop, Func_snd_pcm_open, Func_snd_pcm_prepare, Func_snd_pcm_writei, Func_snd_pcm_hw_params, Func_snd_pcm_hw_params_any, Func_snd_pcm_hw_params_set_access, Func_snd_pcm_hw_params_set_channels, Func_snd_pcm_hw_params_set_rate_near, Func_snd_pcm_hw_params_set_format, Func_snd_pcm_sw_params, Func_snd_pcm_sw_params_current, Func_snd_pcm_sw_params_set_silence_size, Func_snd_pcm_sw_params_set_silence_threshold, Func_snd_pcm_sw_params_set_start_threshold, Func_snd_pcm_sw_params_set_stop_threshold } Func; typedef struct Instruction { unsigned int line_number; uint64_t time_offset; Func func; unsigned int pcm_index; int result; // possible additional params char pcm_name[256]; int stream; int mode; unsigned long size; unsigned long byte_count; uint8_t *data; snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; int access; int channels; int rate; int dir; int dir_value; int format; unsigned long silence_size; unsigned long silence_threshold; unsigned long start_threshold; unsigned long stop_threshold; struct Instruction *next; } Instruction; static snd_pcm_t *pcmsG[16]; static HwParams *hwParamsG, *hwParamsTailG; static SwParams *swParamsG, *swParamsTailG; static uint64_t Now() { struct timespec ts; (void) clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * (1000 * 1000)) + (ts.tv_nsec / 1000); } static snd_pcm_hw_params_t *FindHwParams(unsigned long long id) { HwParams *hwp = hwParamsG; while (hwp) { if (hwp->id == id) { break; } hwp = hwp->next; } if (hwp) { return hwp->params; } hwp = (HwParams *) malloc(sizeof(HwParams)); hwp->id = id; (void) snd_pcm_hw_params_malloc(&(hwp->params)); hwp->next = 0; if (hwParamsTailG) { hwParamsTailG->next = hwp; hwParamsTailG = hwp; } else { hwParamsG = hwParamsTailG = hwp; } return hwp->params; } static snd_pcm_sw_params_t *FindSwParams(unsigned long long id) { SwParams *swp = swParamsG; while (swp) { if (swp->id == id) { break; } swp = swp->next; } if (swp) { return swp->params; } swp = (SwParams *) malloc(sizeof(SwParams)); swp->id = id; (void) snd_pcm_sw_params_malloc(&(swp->params)); swp->next = 0; if (swParamsTailG) { swParamsTailG->next = swp; swParamsTailG = swp; } else { swParamsG = swParamsTailG = swp; } return swp->params; } static uint8_t tohex(char c, unsigned int line_number) { if ((c >= '0') && (c <= '9')) { return c - '0'; } if ((c >= 'a') && (c <= 'f')) { return (c - 'a') + 10; } fprintf(stderr, "ERROR: Bad data on line %u\n", line_number); exit(-1); return 0; } static int no_args(Instruction *instruction, const char *c, const char *name, Func func, unsigned int line_number) { char buf[256]; if ((sscanf(c, "%d: %u: %s\n", &(instruction->result), &(instruction->pcm_index), buf) != 3) || strcmp(buf, name)) { return 0; } if (instruction->pcm_index > 15) { fprintf(stderr, "ERROR: Invalid pcm index on line %u\n", line_number); exit(-1); } instruction->func = func; return 1; } // Reads from stdin int main() { Instruction *head = 0, *tail = 0; // Read line by line, building up a set of instructions char line[1024]; unsigned long last_time_offset = 0; unsigned int line_number = 0; while (fgets(line, sizeof(line), stdin)) { line_number += 1; char *c = line; unsigned long time_offset; if (sscanf(c, "%09lu: ", &time_offset) != 1) { fprintf(stderr, "ERROR: Missing timestamp on line %u\n", line_number); exit(-1); } c += 11; if (time_offset < last_time_offset) { fprintf(stderr, "WARNING: Time goes backwards on line %u\n", line_number); time_offset = last_time_offset; } last_time_offset = time_offset; if (!strcmp(c, "start\n")) { if (head) { fprintf(stderr, "ERROR: Duplicate start on line %u\n", line_number); exit(-1); } if (time_offset != 0) { fprintf(stderr, "ERROR: Invalid timestamp on line %u\n", line_number); exit(-1); } continue; } if (!strcmp(c, "finish\n")) { // All done break; } Instruction *instruction = (Instruction *) malloc(sizeof(Instruction)); instruction->line_number = line_number; instruction->time_offset = time_offset; instruction->next = 0; if (tail) { tail->next = instruction; tail = instruction; } else { head = tail = instruction; } if (no_args(instruction, c, "snd_pcm_close", Func_snd_pcm_close, line_number) || no_args(instruction, c, "snd_pcm_drop", Func_snd_pcm_drop, line_number) || no_args(instruction, c, "snd_pcm_prepare", Func_snd_pcm_prepare, line_number) || no_args(instruction, c, "snd_pcm_close", Func_snd_pcm_close, line_number)) { continue; } char cmdname[256]; if ((sscanf(c, "%d: %u: %s %s %d %d\n", &(instruction->result), &(instruction->pcm_index), cmdname, instruction->pcm_name, &(instruction->stream), &(instruction->mode)) == 6) && !strcmp(cmdname, "snd_pcm_open")) { instruction->func = Func_snd_pcm_open; continue; } if ((sscanf(c, "%d: %u: %s %lu %lu\n", &(instruction->result), &(instruction->pcm_index), cmdname, &(instruction->size), &(instruction->byte_count)) == 5) && !strcmp(cmdname, "snd_pcm_writei")) { instruction->func = Func_snd_pcm_writei; // Now read the data instruction->data = (uint8_t *) malloc(instruction->byte_count); uint8_t *dp = instruction->data; unsigned int toread = instruction->byte_count; while (toread) { if (!fgets(line, sizeof(line), stdin)) { fprintf(stderr, "ERROR: Short data read after line %u\n", line_number); } line_number += 1; c = line; while (toread) { *dp++ = ((tohex(*c++, line_number) << 4) | tohex(*c++, line_number)); toread -= 1; if (*c == '\n') { break; } else if (*c == ' ') { c += 1; } else { fprintf(stderr, "ERROR: Malformed data on line %u\n", line_number); exit(-1); } } } continue; } unsigned long long params_addr = 0; if ((sscanf(c, "%d: %u: %s %llu\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr) == 4) && !strcmp(cmdname, "snd_pcm_hw_params")) { instruction->func = Func_snd_pcm_hw_params; // Look up the params instruction->hw_params = FindHwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr) == 4) && !strcmp(cmdname, "snd_pcm_hw_params_any")) { instruction->func = Func_snd_pcm_hw_params_any; // Look up the params instruction->hw_params = FindHwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu %d\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr, &(instruction->access)) == 5) && !strcmp(cmdname, "snd_pcm_hw_params_set_access")) { instruction->func = Func_snd_pcm_hw_params_set_access; // Look up the params instruction->hw_params = FindHwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu %d\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr, &(instruction->channels)) == 5) && !strcmp(cmdname, "snd_pcm_hw_params_set_channels")) { instruction->func = Func_snd_pcm_hw_params_set_channels; // Look up the params instruction->hw_params = FindHwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu %d %d %d\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr, &(instruction->rate), &(instruction->dir), &(instruction->dir_value)) == 7) && !strcmp(cmdname, "snd_pcm_hw_params_set_rate_near")) { instruction->func = Func_snd_pcm_hw_params_set_rate_near; // Look up the params instruction->hw_params = FindHwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu %d\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr, &(instruction->format)) == 5) && !strcmp(cmdname, "snd_pcm_hw_params_set_format")) { instruction->func = Func_snd_pcm_hw_params_set_format; // Look up the params instruction->hw_params = FindHwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr) == 4) && !strcmp(cmdname, "snd_pcm_sw_params")) { instruction->func = Func_snd_pcm_sw_params; // Look up the params instruction->sw_params = FindSwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr) == 4) && !strcmp(cmdname, "snd_pcm_sw_params_current")) { instruction->func = Func_snd_pcm_sw_params_current; // Look up the params instruction->sw_params = FindSwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu %lu\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr, &(instruction->silence_size)) == 5) && !strcmp(cmdname, "snd_pcm_sw_params_set_silence_size")) { instruction->func = Func_snd_pcm_sw_params_set_silence_size; // Look up the params instruction->sw_params = FindSwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu %lu\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr, &(instruction->silence_threshold)) == 5) && !strcmp(cmdname, "snd_pcm_sw_params_set_silence_threshold")) { instruction->func = Func_snd_pcm_sw_params_set_silence_threshold; // Look up the params instruction->sw_params = FindSwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu %lu\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr, &(instruction->start_threshold)) == 5) && !strcmp(cmdname, "snd_pcm_sw_params_set_start_threshold")) { instruction->func = Func_snd_pcm_sw_params_set_start_threshold; // Look up the params instruction->sw_params = FindSwParams(params_addr); continue; } if ((sscanf(c, "%d: %u: %s %llu %lu\n", &(instruction->result), &(instruction->pcm_index), cmdname, ¶ms_addr, &(instruction->stop_threshold)) == 5) && !strcmp(cmdname, "snd_pcm_sw_params_set_stop_threshold")) { instruction->func = Func_snd_pcm_sw_params_set_stop_threshold; // Look up the params instruction->sw_params = FindSwParams(params_addr); continue; } fprintf(stderr, "ERROR: Invalid line: %u\n", line_number); exit(-1); } uint64_t start = Now(); while (head) { // Busy wait until it is to be replayed while ((Now() - start) < head->time_offset) { } int result = 0; switch (head->func) { case Func_snd_pcm_close: result = snd_pcm_close(pcmsG[head->pcm_index]); break; case Func_snd_pcm_drop: result = snd_pcm_drop(pcmsG[head->pcm_index]); break; case Func_snd_pcm_open: { snd_pcm_t *dummy; snd_pcm_t **ppcm = ((head->pcm_index > 15) ? &dummy : &(pcmsG[head->pcm_index])); result = snd_pcm_open(ppcm, head->pcm_name, head->stream, head->mode); } break; case Func_snd_pcm_prepare: result = snd_pcm_prepare(pcmsG[head->pcm_index]); break; case Func_snd_pcm_writei: result = snd_pcm_writei(pcmsG[head->pcm_index], head->data, head->size); break; case Func_snd_pcm_hw_params: result = snd_pcm_hw_params(pcmsG[head->pcm_index], head->hw_params); break; case Func_snd_pcm_hw_params_any: result = snd_pcm_hw_params_any(pcmsG[head->pcm_index], head->hw_params); break; case Func_snd_pcm_hw_params_set_access: result = snd_pcm_hw_params_set_access(pcmsG[head->pcm_index], head->hw_params, head->access); break; case Func_snd_pcm_hw_params_set_channels: result = snd_pcm_hw_params_set_channels(pcmsG[head->pcm_index], head->hw_params, head->channels); break; case Func_snd_pcm_hw_params_set_rate_near: result = snd_pcm_hw_params_set_rate_near (pcmsG[head->pcm_index], head->hw_params, &(head->rate), head->dir ? &(head->dir_value) : 0); break; case Func_snd_pcm_hw_params_set_format: result = snd_pcm_hw_params_set_format(pcmsG[head->pcm_index], head->hw_params, head->format); break; case Func_snd_pcm_sw_params: result = snd_pcm_sw_params(pcmsG[head->pcm_index], head->sw_params); break; case Func_snd_pcm_sw_params_current: result = snd_pcm_sw_params_current(pcmsG[head->pcm_index], head->sw_params); break; case Func_snd_pcm_sw_params_set_silence_size: result = snd_pcm_sw_params_set_silence_size(pcmsG[head->pcm_index], head->sw_params, head->silence_size); break; case Func_snd_pcm_sw_params_set_silence_threshold: result = snd_pcm_sw_params_set_silence_size (pcmsG[head->pcm_index], head->sw_params, head->silence_threshold); break; case Func_snd_pcm_sw_params_set_start_threshold: result = snd_pcm_sw_params_set_silence_size (pcmsG[head->pcm_index], head->sw_params, head->start_threshold); case Func_snd_pcm_sw_params_set_stop_threshold: result = snd_pcm_sw_params_set_silence_size (pcmsG[head->pcm_index], head->sw_params, head->stop_threshold); break; } if (result != head->result) { fprintf(stderr, "ERROR: Mismatched result for line %u\n", head->line_number); exit(-1); } head = head->next; } return 0; }