/* * This file is part of the libsigrokdecode project. * * Copyright (C) 2013 Bert Vermeulen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "../libsigrokdecode.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __LINUX__ #include #endif #include "../config.h" int debug = FALSE; int statistics = FALSE; struct probe { char *name; int probe; }; struct option { char *key; char *value; }; struct pd { char *name; GSList *probes; GSList *options; }; struct output { char *pd; int type; char *class; int class_idx; char *outfile; int outfd; }; void logmsg(char *prefix, FILE *out, const char *format, va_list args) { if (prefix) fprintf(out, "%s", prefix); vfprintf(out, format, args); fprintf(out, "\n"); } void DBG(const char *format, ...) { va_list args; if (!debug) return; va_start(args, format); logmsg("DBG: ", stdout, format, args); va_end(args); } void ERR(const char *format, ...) { va_list args; va_start(args, format); logmsg("Error: ", stderr, format, args); va_end(args); } int sr_log(void *cb_data, int loglevel, const char *format, va_list args) { (void)cb_data; if (loglevel == SR_LOG_ERR || loglevel == SR_LOG_WARN) logmsg("Error: sr: ", stderr, format, args); else if (debug) logmsg("DBG: sr: ", stdout, format, args); return SRD_OK; } int srd_log(void *cb_data, int loglevel, const char *format, va_list args) { (void)cb_data; if (loglevel == SRD_LOG_ERR || loglevel == SRD_LOG_WARN) logmsg("Error: srd: ", stderr, format, args); else if (debug) logmsg("DBG: srd: ", stdout, format, args); return SRD_OK; } void usage(char *msg) { if (msg) fprintf(stderr, "%s\n", msg); printf("Usage: runtc [-dPpoiOf]\n"); printf(" -d Debug\n"); printf(" -P \n"); printf(" -p (optional)\n"); printf(" -o (optional)\n"); printf(" -i \n"); printf(" -O \n"); printf(" -f (optional)\n"); exit(msg ? 1 : 0); } /* This is a neutered version of libsigrokdecode's py_str_as_str(). It * does no error checking, but then the only strings it processes are * generated by Python's repr(), so are known good. */ char *py_str_as_str(const PyObject *py_str) { PyObject *py_encstr; char *str, *outstr; py_encstr = PyUnicode_AsEncodedString((PyObject *)py_str, "utf-8", NULL); str = PyBytes_AS_STRING(py_encstr); outstr = g_strdup(str); Py_DecRef(py_encstr); return outstr; } static void srd_cb_py(struct srd_proto_data *pdata, void *cb_data) { struct output *op; PyObject *pydata, *pyrepr; GString *out; char *s; DBG("Python output from %s", pdata->pdo->di->inst_id); op = cb_data; pydata = pdata->data; DBG("ptr %p", pydata); if (strcmp(pdata->pdo->di->inst_id, op->pd)) /* This is not the PD selected for output. */ return; if (!(pyrepr = PyObject_Repr(pydata))) { ERR("Invalid Python object."); return; } s = py_str_as_str(pyrepr); Py_DecRef(pyrepr); /* Output format for testing is '- : \n' */ out = g_string_sized_new(128); g_string_printf(out, "%"PRIu64"-%"PRIu64" %s: %s\n", pdata->start_sample, pdata->end_sample, pdata->pdo->di->inst_id, s); g_free(s); if (write(op->outfd, out->str, out->len) == -1) ERR("SRD_OUTPUT_PYTHON callback write failure!"); DBG("wrote '%s'", out->str); g_string_free(out, TRUE); } static void srd_cb_bin(struct srd_proto_data *pdata, void *cb_data) { struct srd_proto_data_binary *pdb; struct output *op; DBG("Binary output from %s", pdata->pdo->di->inst_id); op = cb_data; pdb = pdata->data; if (strcmp(pdata->pdo->di->inst_id, op->pd)) /* This is not the PD selected for output. */ return; if (op->class_idx != -1 && op->class_idx != pdb->bin_class) /* * This output takes a specific binary class, * but not the one that just came in. */ return; if (write(op->outfd, pdb->data, pdb->size) == -1) ERR("SRD_OUTPUT_BINARY callback write failure!"); } static void srd_cb_ann(struct srd_proto_data *pdata, void *cb_data) { struct srd_decoder *dec; struct srd_proto_data_annotation *pda; struct output *op; GString *line; int i; char **dec_ann; DBG("Annotation output from %s", pdata->pdo->di->inst_id); op = cb_data; pda = pdata->data; dec = pdata->pdo->di->decoder; if (strcmp(pdata->pdo->di->inst_id, op->pd)) /* This is not the PD selected for output. */ return; if (op->class_idx != -1 && op->class_idx != pda->ann_format) /* * This output takes a specific annotation class, * but not the one that just came in. */ return; dec_ann = g_slist_nth_data(dec->annotations, pda->ann_format); line = g_string_sized_new(256); g_string_printf(line, "%"PRIu64"-%"PRIu64" %s: %s:", pdata->start_sample, pdata->end_sample, pdata->pdo->di->inst_id, dec_ann[0]); for (i = 0; pda->ann_text[i]; i++) g_string_append_printf(line, " \"%s\"", pda->ann_text[i]); g_string_append(line, "\n"); if (write(op->outfd, line->str, line->len) == -1) ERR("SRD_OUTPUT_ANN callback write failure!"); g_string_free(line, TRUE); } static void sr_cb(const struct sr_dev_inst *sdi, const struct sr_datafeed_packet *packet, void *cb_data) { const struct sr_datafeed_logic *logic; struct srd_session *sess; GVariant *gvar; uint64_t samplerate; int num_samples; static int samplecnt = 0; sess = cb_data; switch (packet->type) { case SR_DF_HEADER: DBG("Received SR_DF_HEADER"); if (sr_config_get(sdi->driver, sdi, NULL, SR_CONF_SAMPLERATE, &gvar) != SR_OK) { ERR("Getting samplerate failed"); break; } samplerate = g_variant_get_uint64(gvar); g_variant_unref(gvar); if (srd_session_metadata_set(sess, SRD_CONF_SAMPLERATE, g_variant_new_uint64(samplerate)) != SRD_OK) { ERR("Setting samplerate failed"); break; } if (srd_session_start(sess) != SRD_OK) { ERR("Session start failed"); break; } break; case SR_DF_LOGIC: logic = packet->payload; num_samples = logic->length / logic->unitsize; DBG("Received SR_DF_LOGIC: %d samples", num_samples); srd_session_send(sess, samplecnt, samplecnt + num_samples, logic->data, logic->length); samplecnt += logic->length / logic->unitsize; break; case SR_DF_END: DBG("Received SR_DF_END"); break; } } int get_stats(int stats[2]) { FILE *f; size_t len; int tmp; char *buf; stats[0] = stats[1] = -1; if (!(f = fopen("/proc/self/status", "r"))) return FALSE; len = 128; buf = malloc(len); while (getline(&buf, &len, f) != -1) { if (strcasestr(buf, "vmpeak:")) { stats[0] = strtoul(buf + 10, NULL, 10); } else if (strcasestr(buf, "vmsize:")) { tmp = strtoul(buf + 10, NULL, 10); if (tmp > stats[0]) stats[0] = tmp; } else if (strcasestr(buf, "vmhwm:")) { stats[1] = strtoul(buf + 6, NULL, 10); } else if (strcasestr(buf, "vmrss:")) { tmp = strtoul(buf + 10, NULL, 10); if (tmp > stats[0]) stats[0] = tmp; } } free(buf); fclose(f); return TRUE; } static int run_testcase(char *infile, GSList *pdlist, struct output *op) { struct srd_session *sess; struct srd_decoder *dec; struct srd_decoder_inst *di, *prev_di; srd_pd_output_callback_t cb; struct pd *pd; struct probe *probe; struct option *option; GVariant *gvar; GHashTable *probes, *opts; GSList *pdl, *l; int idx; char **decoder_class; if (op->outfile) { if ((op->outfd = open(op->outfile, O_CREAT|O_WRONLY, 0600)) == -1) { ERR("Unable to open %s for writing: %s", op->outfile, strerror(errno)); return FALSE; } } if (sr_session_load(infile) != SR_OK) return FALSE; if (srd_session_new(&sess) != SRD_OK) return FALSE; sr_session_datafeed_callback_add(sr_cb, sess); switch (op->type) { case SRD_OUTPUT_ANN: cb = srd_cb_ann; break; case SRD_OUTPUT_BINARY: cb = srd_cb_bin; break; case SRD_OUTPUT_PYTHON: cb = srd_cb_py; break; default: return FALSE; } srd_pd_output_callback_add(sess, op->type, cb, op); prev_di = NULL; pd = NULL; for (pdl = pdlist; pdl; pdl = pdl->next) { pd = pdl->data; if (srd_decoder_load(pd->name) != SRD_OK) return FALSE; /* Instantiate decoder and pass in options. */ opts = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_variant_unref); for (l = pd->options; l; l = l->next) { option = l->data; g_hash_table_insert(opts, option->key, option->value); } if (!(di = srd_inst_new(sess, pd->name, opts))) return FALSE; g_hash_table_destroy(opts); /* Map probes. */ if (pd->probes) { probes = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_variant_unref); for (l = pd->probes; l; l = l->next) { probe = l->data; gvar = g_variant_new_int32(probe->probe); g_variant_ref_sink(gvar); g_hash_table_insert(probes, probe->name, gvar); } if (srd_inst_probe_set_all(di, probes) != SRD_OK) return FALSE; g_hash_table_destroy(probes); } /* If this is not the first decoder in the list, stack it * on top of the previous one. */ if (prev_di) { if (srd_inst_stack(sess, prev_di, di) != SRD_OK) { ERR("Failed to stack decoder instances."); return FALSE; } } prev_di = di; } /* Resolve top decoder's class index, so we can match. */ dec = srd_decoder_get_by_id(pd->name); if (op->class) { if (op->type == SRD_OUTPUT_ANN) l = dec->annotations; else if (op->type == SRD_OUTPUT_BINARY) l = dec->binary; else /* Only annotations and binary can have a class. */ return FALSE; idx = 0; while(l) { decoder_class = l->data; if (!strcmp(decoder_class[0], op->class)) { op->class_idx = idx; break; } else idx++; l = l->next; } if (op->class_idx == -1) { ERR("Output class '%s' not found in decoder %s.", op->class, pd->name); return FALSE; } else DBG("Class %s index is %d", op->class, op->class_idx); } sr_session_start(); sr_session_run(); sr_session_stop(); srd_session_destroy(sess); if (op->outfile) close(op->outfd); return TRUE; } int main(int argc, char **argv) { struct sr_context *ctx; GSList *pdlist; struct pd *pd; struct probe *probe; struct option *option; struct output *op; int ret; char c, *opt_infile, **kv, **opstr; op = malloc(sizeof(struct output)); op->pd = NULL; op->type = -1; op->class = NULL; op->class_idx = -1; op->outfd = 1; pdlist = NULL; opt_infile = NULL; pd = NULL; while((c = getopt(argc, argv, "dP:p:o:i:O:f:S")) != -1) { switch(c) { case 'd': debug = TRUE; break; case 'P': pd = g_malloc(sizeof(struct pd)); pd->name = g_strdup(optarg); pd->probes = pd->options = NULL; pdlist = g_slist_append(pdlist, pd); break; case 'p': case 'o': if (g_slist_length(pdlist) == 0) { /* No previous -P. */ ERR("Syntax error at '%s'", optarg); usage(NULL); } kv = g_strsplit(optarg, "=", 0); if (!kv[0] || (!kv[1] || kv[2])) { /* Need x=y. */ ERR("Syntax error at '%s'", optarg); g_strfreev(kv); usage(NULL); } if (c == 'p') { probe = malloc(sizeof(struct probe)); probe->name = g_strdup(kv[0]); probe->probe = strtoul(kv[1], 0, 10); /* Apply to last PD. */ pd->probes = g_slist_append(pd->probes, probe); } else { option = malloc(sizeof(struct option)); option->key = g_strdup(kv[0]); option->value = g_strdup(kv[1]); /* Apply to last PD. */ pd->options = g_slist_append(pd->options, option); } break; case 'i': opt_infile = optarg; break; case 'O': opstr = g_strsplit(optarg, ":", 0); if (!opstr[0] || !opstr[1]) { /* Need at least abc:def. */ ERR("Syntax error at '%s'", optarg); g_strfreev(opstr); usage(NULL); } op->pd = g_strdup(opstr[0]); if (!strcmp(opstr[1], "annotation")) op->type = SRD_OUTPUT_ANN; else if (!strcmp(opstr[1], "binary")) op->type = SRD_OUTPUT_BINARY; else if (!strcmp(opstr[1], "python")) op->type = SRD_OUTPUT_PYTHON; else { ERR("Unknown output type '%s'", opstr[1]); g_strfreev(opstr); usage(NULL); } if (opstr[2]) op->class = g_strdup(opstr[2]); g_strfreev(opstr); break; case 'f': op->outfile = g_strdup(optarg); op->outfd = -1; break; case 'S': statistics = TRUE; break; default: usage(NULL); } } if (argc > optind) usage(NULL); if (g_slist_length(pdlist) == 0) usage(NULL); if (!opt_infile) usage(NULL); if (!op->pd || op->type == -1) usage(NULL); sr_log_callback_set(sr_log, NULL); if (sr_init(&ctx) != SR_OK) return 1; srd_log_callback_set(srd_log, NULL); if (srd_init(DECODERS_DIR) != SRD_OK) return 1; ret = 0; if (!run_testcase(opt_infile, pdlist, op)) ret = 1; srd_exit(); sr_exit(ctx); return ret; }