static int test_variable_renames(void) { fprintf(stderr, "Creating chart\n"); RRDSET *st = rrdset_create("chart", "ID", NULL, "family", "context", "Unit Testing", "a value", 1, 1, RRDSET_TYPE_LINE); fprintf(stderr, "Created chart with id '%s', name '%s'\n", st->id, st->name); fprintf(stderr, "Creating dimension DIM1\n"); RRDDIM *rd1 = rrddim_add(st, "DIM1", NULL, 1, 1, RRDDIM_INCREMENTAL); fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd1->id, rd1->name); fprintf(stderr, "Creating dimension DIM2\n"); RRDDIM *rd2 = rrddim_add(st, "DIM2", NULL, 1, 1, RRDDIM_INCREMENTAL); fprintf(stderr, "Created dimension with id '%s', name '%s'\n", rd2->id, rd2->name); fprintf(stderr, "Renaming chart to CHARTNAME1\n"); rrdset_set_name(st, "CHARTNAME1"); fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", st->id, st->name); fprintf(stderr, "Renaming chart to CHARTNAME2\n"); rrdset_set_name(st, "CHARTNAME2"); fprintf(stderr, "Renamed chart with id '%s' to name '%s'\n", st->id, st->name); fprintf(stderr, "Renaming dimension DIM1 to DIM1NAME1\n"); rrddim_set_name(st, rd1, "DIM1NAME1"); fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd1->id, rd1->name); fprintf(stderr, "Renaming dimension DIM1 to DIM1NAME2\n"); rrddim_set_name(st, rd1, "DIM1NAME2"); fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd1->id, rd1->name); fprintf(stderr, "Renaming dimension DIM2 to DIM2NAME1\n"); rrddim_set_name(st, rd2, "DIM2NAME1"); fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd2->id, rd2->name); fprintf(stderr, "Renaming dimension DIM2 to DIM2NAME2\n"); rrddim_set_name(st, rd2, "DIM2NAME2"); fprintf(stderr, "Renamed dimension with id '%s' to name '%s'\n", rd2->id, rd2->name); BUFFER *buf = buffer_create(1); health_api_v1_chart_variables2json(st, buf); fprintf(stderr, "%s", buffer_tostring(buf)); buffer_free(buf); return 1; }
static inline void tc_device_commit(struct tc_device *d) { static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1, enable_tokens = -1, enable_ctokens = -1, enabled_all_classes_qdiscs = -1; if(unlikely(enable_new_interfaces == -1)) { enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_BOOLEAN_YES); enable_bytes = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_BOOLEAN_AUTO); enable_packets = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_BOOLEAN_AUTO); enable_dropped = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_BOOLEAN_AUTO); enable_tokens = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_BOOLEAN_NO); enable_ctokens = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_BOOLEAN_NO); enabled_all_classes_qdiscs = config_get_boolean_ondemand("plugin:tc", "enable show all classes and qdiscs for all interfaces", CONFIG_BOOLEAN_NO); } if(unlikely(d->enabled == (char)-1)) { char var_name[CONFIG_MAX_NAME + 1]; snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id); d->enabled = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces); snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id); d->enabled_bytes = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id); d->enabled_packets = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_packets); snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id); d->enabled_dropped = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped); snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id); d->enabled_tokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens); snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id); d->enabled_ctokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", d->id); d->enabled_all_classes_qdiscs = (char)config_get_boolean_ondemand("plugin:tc", var_name, enabled_all_classes_qdiscs); } // we only need to add leaf classes struct tc_class *c, *x /*, *root = NULL */; unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0, tokens_sum = 0, ctokens_sum = 0; int active_nodes = 0, updated_classes = 0, updated_qdiscs = 0; // prepare all classes // we set reasonable defaults for the rest of the code below for(c = d->classes ; c ; c = c->next) { c->render = 0; // do not render this class c->isleaf = 1; // this is a leaf class c->hasparent = 0; // without a parent if(unlikely(!c->updated)) c->unupdated++; // increase its unupdated counter else { c->unupdated = 0; // reset its unupdated counter // count how many of each kind if(c->isqdisc) updated_qdiscs++; else updated_classes++; } } if(unlikely(!d->enabled || (!updated_classes && !updated_qdiscs))) { debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. It is not enabled/updated.", d->name?d->name:d->id); tc_device_classes_cleanup(d); return; } if(unlikely(updated_classes && updated_qdiscs)) { error("TC: device '%s' has active both classes (%d) and qdiscs (%d). Will render only qdiscs.", d->id, updated_classes, updated_qdiscs); // set all classes to !updated for(c = d->classes ; c ; c = c->next) if(unlikely(!c->isqdisc && c->updated)) c->updated = 0; updated_classes = 0; } // mark the classes as leafs and parents // // TC is hierarchical: // - classes can have other classes in them // - the same is true for qdiscs (i.e. qdiscs have classes, that have other qdiscs) // // we need to present a chart with leaf nodes only, so that the sum // of all dimensions of the chart, will be the total utilization // of the interface. // // here we try to find the ones we need to report // by default all nodes are marked with: isleaf = 1 (see above) // // so, here we remove the isleaf flag from nodes in the middle // and we add the hasparent flag to leaf nodes we found their parent if(likely(!d->enabled_all_classes_qdiscs)) { for(c = d->classes; c; c = c->next) { if(unlikely(!c->updated)) continue; //debug(D_TC_LOOP, "TC: In device '%s', %s '%s' has leafid: '%s' and parentid '%s'.", // d->id, // c->isqdisc?"qdisc":"class", // c->id, // c->leafid?c->leafid:"NULL", // c->parentid?c->parentid:"NULL"); // find if c is leaf or not for(x = d->classes; x; x = x->next) { if(unlikely(!x->updated || c == x || !x->parentid)) continue; // classes have both parentid and leafid // qdiscs have only parentid // the following works for both (it is an OR) if((c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) || (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0)) { // debug(D_TC_LOOP, "TC: In device '%s', %s '%s' (leafid: '%s') has as leaf %s '%s' (parentid: '%s').", d->name?d->name:d->id, c->isqdisc?"qdisc":"class", c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->isqdisc?"qdisc":"class", x->name?x->name:x->id, x->parentid?x->parentid:x->id); c->isleaf = 0; x->hasparent = 1; } } } } for(c = d->classes ; c ; c = c->next) { if(unlikely(!c->updated)) continue; // debug(D_TC_LOOP, "TC: device '%s', %s '%s' isleaf=%d, hasparent=%d", d->id, (c->isqdisc)?"qdisc":"class", c->id, c->isleaf, c->hasparent); if(unlikely((c->isleaf && c->hasparent) || d->enabled_all_classes_qdiscs)) { c->render = 1; active_nodes++; bytes_sum += c->bytes; packets_sum += c->packets; dropped_sum += c->dropped; tokens_sum += c->tokens; ctokens_sum += c->ctokens; } //if(unlikely(!c->hasparent)) { // if(root) error("TC: multiple root class/qdisc for device '%s' (old: '%s', new: '%s')", d->id, root->id, c->id); // root = c; // debug(D_TC_LOOP, "TC: found root class/qdisc '%s'", root->id); //} } #ifdef NETDATA_INTERNAL_CHECKS // dump all the list to see what we know if(unlikely(debug_flags & D_TC_LOOP)) { for(c = d->classes ; c ; c = c->next) { if(c->render) debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, OK", d->name, c->id); else debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, IGNORE (updated: %d, isleaf: %d, hasparent: %d, parent: %s)", d->name?d->name:d->id, c->id, c->updated, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)"); } } #endif if(unlikely(!active_nodes)) { debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No useful classes/qdiscs.", d->name?d->name:d->id); tc_device_classes_cleanup(d); return; } debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d, all_classes_qdiscs: %d/%d), classes: (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).", d->name?d->name:d->id, d->enabled, enable_new_interfaces, d->enabled_bytes, enable_bytes, d->enabled_packets, enable_packets, d->enabled_dropped, enable_dropped, d->enabled_tokens, enable_tokens, d->enabled_ctokens, enable_ctokens, d->enabled_all_classes_qdiscs, enabled_all_classes_qdiscs, bytes_sum, packets_sum, dropped_sum, tokens_sum, ctokens_sum ); // -------------------------------------------------------------------- // bytes if(d->enabled_bytes == CONFIG_BOOLEAN_YES || (d->enabled_bytes == CONFIG_BOOLEAN_AUTO && bytes_sum)) { d->enabled_bytes = CONFIG_BOOLEAN_YES; if(unlikely(!d->st_bytes)) d->st_bytes = rrdset_create_localhost( RRD_TYPE_TC , d->id , d->name ? d->name : d->id , d->family ? d->family : d->id , RRD_TYPE_TC ".qos" , "Class Usage" , "kilobits/s" , PLUGIN_TC_NAME , NULL , NETDATA_CHART_PRIO_TC_QOS , localhost->rrd_update_every , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED ); else { rrdset_next(d->st_bytes); if(unlikely(d->name_updated)) rrdset_set_name(d->st_bytes, d->name); // TODO // update the family } for(c = d->classes ; c ; c = c->next) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_bytes)) c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, BITS_IN_A_KILOBIT, RRD_ALGORITHM_INCREMENTAL); else if(unlikely(c->name_updated)) rrddim_set_name(d->st_bytes, c->rd_bytes, c->name); rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes); } rrdset_done(d->st_bytes); } // -------------------------------------------------------------------- // packets if(d->enabled_packets == CONFIG_BOOLEAN_YES || (d->enabled_packets == CONFIG_BOOLEAN_AUTO && packets_sum)) { d->enabled_packets = CONFIG_BOOLEAN_YES; if(unlikely(!d->st_packets)) { char id[RRD_ID_LENGTH_MAX + 1]; char name[RRD_ID_LENGTH_MAX + 1]; snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id); d->st_packets = rrdset_create_localhost( RRD_TYPE_TC , id , name , d->family ? d->family : d->id , RRD_TYPE_TC ".qos_packets" , "Class Packets" , "packets/s" , PLUGIN_TC_NAME , NULL , NETDATA_CHART_PRIO_TC_QOS_PACKETS , localhost->rrd_update_every , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED ); } else { rrdset_next(d->st_packets); if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id); rrdset_set_name(d->st_packets, name); } // TODO // update the family } for(c = d->classes ; c ; c = c->next) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_packets)) c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL); else if(unlikely(c->name_updated)) rrddim_set_name(d->st_packets, c->rd_packets, c->name); rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets); } rrdset_done(d->st_packets); } // -------------------------------------------------------------------- // dropped if(d->enabled_dropped == CONFIG_BOOLEAN_YES || (d->enabled_dropped == CONFIG_BOOLEAN_AUTO && dropped_sum)) { d->enabled_dropped = CONFIG_BOOLEAN_YES; if(unlikely(!d->st_dropped)) { char id[RRD_ID_LENGTH_MAX + 1]; char name[RRD_ID_LENGTH_MAX + 1]; snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id); d->st_dropped = rrdset_create_localhost( RRD_TYPE_TC , id , name , d->family ? d->family : d->id , RRD_TYPE_TC ".qos_dropped" , "Class Dropped Packets" , "packets/s" , PLUGIN_TC_NAME , NULL , NETDATA_CHART_PRIO_TC_QOS_DROPPED , localhost->rrd_update_every , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE : RRDSET_TYPE_STACKED ); } else { rrdset_next(d->st_dropped); if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id); rrdset_set_name(d->st_dropped, name); } // TODO // update the family } for(c = d->classes ; c ; c = c->next) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_dropped)) c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL); else if(unlikely(c->name_updated)) rrddim_set_name(d->st_dropped, c->rd_dropped, c->name); rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped); } rrdset_done(d->st_dropped); } // -------------------------------------------------------------------- // tokens if(d->enabled_tokens == CONFIG_BOOLEAN_YES || (d->enabled_tokens == CONFIG_BOOLEAN_AUTO && tokens_sum)) { d->enabled_tokens = CONFIG_BOOLEAN_YES; if(unlikely(!d->st_tokens)) { char id[RRD_ID_LENGTH_MAX + 1]; char name[RRD_ID_LENGTH_MAX + 1]; snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id); d->st_tokens = rrdset_create_localhost( RRD_TYPE_TC , id , name , d->family ? d->family : d->id , RRD_TYPE_TC ".qos_tokens" , "Class Tokens" , "tokens" , PLUGIN_TC_NAME , NULL , NETDATA_CHART_PRIO_TC_QOS_TOCKENS , localhost->rrd_update_every , RRDSET_TYPE_LINE ); } else { rrdset_next(d->st_tokens); if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id); rrdset_set_name(d->st_tokens, name); } // TODO // update the family } for(c = d->classes ; c ; c = c->next) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_tokens)) { c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE); } else if(unlikely(c->name_updated)) rrddim_set_name(d->st_tokens, c->rd_tokens, c->name); rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens); } rrdset_done(d->st_tokens); } // -------------------------------------------------------------------- // ctokens if(d->enabled_ctokens == CONFIG_BOOLEAN_YES || (d->enabled_ctokens == CONFIG_BOOLEAN_AUTO && ctokens_sum)) { d->enabled_ctokens = CONFIG_BOOLEAN_YES; if(unlikely(!d->st_ctokens)) { char id[RRD_ID_LENGTH_MAX + 1]; char name[RRD_ID_LENGTH_MAX + 1]; snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id); d->st_ctokens = rrdset_create_localhost( RRD_TYPE_TC , id , name , d->family ? d->family : d->id , RRD_TYPE_TC ".qos_ctokens" , "Class cTokens" , "ctokens" , PLUGIN_TC_NAME , NULL , NETDATA_CHART_PRIO_TC_QOS_CTOCKENS , localhost->rrd_update_every , RRDSET_TYPE_LINE ); } else { debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id); rrdset_next(d->st_ctokens); if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id); rrdset_set_name(d->st_ctokens, name); } // TODO // update the family } for(c = d->classes ; c ; c = c->next) { if(unlikely(!c->render)) continue; if(unlikely(!c->rd_ctokens)) c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE); else if(unlikely(c->name_updated)) rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name); rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens); } rrdset_done(d->st_ctokens); } tc_device_classes_cleanup(d); }
static inline void tc_device_commit(struct tc_device *d) { static int enable_new_interfaces = -1; if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean("plugin:tc", "enable new interfaces detected at runtime", 1); // we only need to add leaf classes struct tc_class *c, *x; // set all classes for(c = d->classes ; c ; c = c->next) { c->isleaf = 1; c->hasparent = 0; } // mark the classes as leafs and parents for(c = d->classes ; c ; c = c->next) { if(!c->updated) continue; for(x = d->classes ; x ; x = x->next) { if(!x->updated) continue; if(c == x) continue; if(x->parentid && ( ( c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) || (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0))) { // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has as leaf class '%s' (parentid: '%s').", d->name?d->name:d->id, c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->name?x->name:x->id, x->parentid?x->parentid:x->id); c->isleaf = 0; x->hasparent = 1; } } } // debugging: /* for ( c = d->classes ; c ; c = c->next) { if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id); else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid); } */ // we need at least a class for(c = d->classes ; c ; c = c->next) { // debug(D_TC_LOOP, "TC: Device '%s', class '%s', isLeaf=%d, HasParent=%d, Seen=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf, c->hasparent, c->seen); if(!c->updated) continue; if(c->isleaf && c->hasparent) break; } if(!c) { debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name?d->name:d->id); tc_device_classes_cleanup(d); return; } char var_name[CONFIG_MAX_NAME + 1]; snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id); if(config_get_boolean("plugin:tc", var_name, enable_new_interfaces)) { RRDSET *st = rrdset_find_bytype(RRD_TYPE_TC, d->id); if(!st) { debug(D_TC_LOOP, "TC: Creating new chart for device '%s'", d->name?d->name:d->id); st = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, RRD_TYPE_TC ".qos", "Class Usage", "kilobits/s", 7000, rrd_update_every, RRDSET_TYPE_STACKED); for(c = d->classes ; c ; c = c->next) { if(!c->updated) continue; if(c->isleaf && c->hasparent) rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL); } } else { debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id); rrdset_next_plugins(st); if(d->name && strcmp(d->id, d->name) != 0) rrdset_set_name(st, d->name); } for(c = d->classes ; c ; c = c->next) { if(!c->updated) continue; if(c->isleaf && c->hasparent) { RRDDIM *rd = rrddim_find(st, c->id); if(!rd) { debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s'", st->id, c->id, c->name); // new class, we have to add it rd = rrddim_add(st, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL); } else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", st->id, c->id); rrddim_set_by_pointer(st, rd, c->bytes); // if it has a name, different to the id if(c->name) { // update the rrd dimension with the new name debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", st->id, rd->id, c->name); rrddim_set_name(st, rd, c->name); free(c->name); c->name = NULL; } } } rrdset_done(st); } tc_device_classes_cleanup(d); }
int do_proc_interrupts(int update_every, usec_t dt) { (void)dt; static procfile *ff = NULL; static int cpus = -1, do_per_core = -1; struct interrupt *irrs = NULL; if(unlikely(do_per_core == -1)) do_per_core = config_get_boolean("plugin:proc:/proc/interrupts", "interrupts per core", 1); if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", global_host_prefix, "/proc/interrupts"); ff = procfile_open(config_get("plugin:proc:/proc/interrupts", "filename to monitor", filename), " \t", PROCFILE_FLAG_DEFAULT); } if(unlikely(!ff)) return 1; ff = procfile_readall(ff); if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time uint32_t lines = procfile_lines(ff), l; uint32_t words = procfile_linewords(ff, 0); if(unlikely(!lines)) { error("Cannot read /proc/interrupts, zero lines reported."); return 1; } // find how many CPUs are there if(unlikely(cpus == -1)) { uint32_t w; cpus = 0; for(w = 0; w < words ; w++) { if(likely(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)) cpus++; } } if(unlikely(!cpus)) { error("PLUGIN: PROC_INTERRUPTS: Cannot find the number of CPUs in /proc/interrupts"); return 1; } // allocate the size we need; irrs = get_interrupts_array(lines, cpus); irrs[0].used = 0; // loop through all lines for(l = 1; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); irr->used = 0; irr->total = 0; words = procfile_linewords(ff, l); if(unlikely(!words)) continue; irr->id = procfile_lineword(ff, l, 0); if(unlikely(!irr->id || !irr->id[0])) continue; int idlen = strlen(irr->id); if(unlikely(irr->id[idlen - 1] == ':')) irr->id[idlen - 1] = '\0'; int c; for(c = 0; c < cpus ;c++) { if(likely((c + 1) < (int)words)) irr->cpu[c].value = strtoull(procfile_lineword(ff, l, (uint32_t)(c + 1)), NULL, 10); else irr->cpu[c].value = 0; irr->total += irr->cpu[c].value; } if(unlikely(isdigit(irr->id[0]) && (uint32_t)(cpus + 2) < words)) { strncpyz(irr->name, procfile_lineword(ff, l, words - 1), MAX_INTERRUPT_NAME); int nlen = strlen(irr->name); int idlen = strlen(irr->id); if(likely(nlen + 1 + idlen <= MAX_INTERRUPT_NAME)) { irr->name[nlen] = '_'; strncpyz(&irr->name[nlen + 1], irr->id, MAX_INTERRUPT_NAME - nlen - 1); } else { irr->name[MAX_INTERRUPT_NAME - idlen - 1] = '_'; strncpyz(&irr->name[MAX_INTERRUPT_NAME - idlen], irr->id, idlen); } } else { strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME); } irr->used = 1; } RRDSET *st; // -------------------------------------------------------------------- st = rrdset_find_bytype("system", "interrupts"); if(unlikely(!st)) st = rrdset_create("system", "interrupts", NULL, "interrupts", NULL, "System interrupts", "interrupts/s", 1000, update_every, RRDSET_TYPE_STACKED); else rrdset_next(st); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); if(unlikely(!irr->used)) continue; // some interrupt may have changed without changing the total number of lines // if the same number of interrupts have been added and removed between two // calls of this function. if(unlikely(!irr->rd || strncmp(irr->rd->name, irr->name, MAX_INTERRUPT_NAME) != 0)) { irr->rd = rrddim_find(st, irr->id); if(unlikely(!irr->rd)) irr->rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); else rrddim_set_name(st, irr->rd, irr->name); // also reset per cpu RRDDIMs to avoid repeating strncmp() in the per core loop if(likely(do_per_core)) { int c; for (c = 0; c < cpus ;c++) irr->cpu[c].rd = NULL; } } rrddim_set_by_pointer(st, irr->rd, irr->total); } rrdset_done(st); if(likely(do_per_core)) { int c; for(c = 0; c < cpus ;c++) { char id[50+1]; snprintfz(id, 50, "cpu%d_interrupts", c); st = rrdset_find_bytype("cpu", id); if(unlikely(!st)) { char title[100+1]; snprintfz(title, 100, "CPU%d Interrupts", c); st = rrdset_create("cpu", id, NULL, "interrupts", "cpu.interrupts", title, "interrupts/s", 1100 + c, update_every, RRDSET_TYPE_STACKED); } else rrdset_next(st); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); if(unlikely(!irr->used)) continue; if(unlikely(!irr->cpu[c].rd)) { irr->cpu[c].rd = rrddim_find(st, irr->id); if(unlikely(!irr->cpu[c].rd)) irr->cpu[c].rd = rrddim_add(st, irr->id, irr->name, 1, 1, RRDDIM_INCREMENTAL); else rrddim_set_name(st, irr->cpu[c].rd, irr->name); } rrddim_set_by_pointer(st, irr->cpu[c].rd, irr->cpu[c].value); } rrdset_done(st); } } return 0; }
int do_proc_softirqs(int update_every, usec_t dt) { (void)dt; static procfile *ff = NULL; static int cpus = -1, do_per_core = -1; struct interrupt *irrs = NULL; if(unlikely(do_per_core == -1)) do_per_core = config_get_boolean("plugin:proc:/proc/softirqs", "interrupts per core", 1); if(unlikely(!ff)) { char filename[FILENAME_MAX + 1]; snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/softirqs"); ff = procfile_open(config_get("plugin:proc:/proc/softirqs", "filename to monitor", filename), " \t:", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; } ff = procfile_readall(ff); if(unlikely(!ff)) return 0; // we return 0, so that we will retry to open it next time size_t lines = procfile_lines(ff), l; size_t words = procfile_linewords(ff, 0); if(unlikely(!lines)) { error("Cannot read /proc/softirqs, zero lines reported."); return 1; } // find how many CPUs are there if(unlikely(cpus == -1)) { uint32_t w; cpus = 0; for(w = 0; w < words ; w++) { if(likely(strncmp(procfile_lineword(ff, 0, w), "CPU", 3) == 0)) cpus++; } } if(unlikely(!cpus)) { error("PLUGIN: PROC_SOFTIRQS: Cannot find the number of CPUs in /proc/softirqs"); return 1; } // allocate the size we need; irrs = get_interrupts_array(lines, cpus); irrs[0].used = 0; // loop through all lines for(l = 1; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); irr->used = 0; irr->total = 0; words = procfile_linewords(ff, l); if(unlikely(!words)) continue; irr->id = procfile_lineword(ff, l, 0); if(unlikely(!irr->id || !irr->id[0])) continue; int c; for(c = 0; c < cpus ;c++) { if(likely((c + 1) < (int)words)) irr->cpu[c].value = str2ull(procfile_lineword(ff, l, (uint32_t)(c + 1))); else irr->cpu[c].value = 0; irr->total += irr->cpu[c].value; } strncpyz(irr->name, irr->id, MAX_INTERRUPT_NAME); irr->used = 1; } // -------------------------------------------------------------------- static RRDSET *st_system_softirqs = NULL; if(unlikely(!st_system_softirqs)) st_system_softirqs = rrdset_create_localhost( "system" , "softirqs" , NULL , "softirqs" , NULL , "System softirqs" , "softirqs/s" , PLUGIN_PROC_NAME , PLUGIN_PROC_MODULE_SOFTIRQS_NAME , NETDATA_CHART_PRIO_SYSTEM_SOFTIRQS , update_every , RRDSET_TYPE_STACKED ); else rrdset_next(st_system_softirqs); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); if(unlikely(!irr->used)) continue; // some interrupt may have changed without changing the total number of lines // if the same number of interrupts have been added and removed between two // calls of this function. if(unlikely(!irr->rd || strncmp(irr->name, irr->rd->name, MAX_INTERRUPT_NAME) != 0)) { irr->rd = rrddim_find(st_system_softirqs, irr->id); if(unlikely(!irr->rd)) irr->rd = rrddim_add(st_system_softirqs, irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); else rrddim_set_name(st_system_softirqs, irr->rd, irr->name); // also reset per cpu RRDDIMs to avoid repeating strncmp() in the per core loop if(likely(do_per_core)) { int c; for (c = 0; c < cpus ;c++) irr->cpu[c].rd = NULL; } } rrddim_set_by_pointer(st_system_softirqs, irr->rd, irr->total); } rrdset_done(st_system_softirqs); // -------------------------------------------------------------------- if(do_per_core) { static RRDSET **core_st = NULL; static int old_cpus = 0; if(old_cpus < cpus) { core_st = reallocz(core_st, sizeof(RRDSET *) * cpus); memset(&core_st[old_cpus], 0, sizeof(RRDSET *) * (cpus - old_cpus)); old_cpus = cpus; } int c; for(c = 0; c < cpus ; c++) { if(unlikely(!core_st[c])) { // find if everything is just zero unsigned long long core_sum = 0; for (l = 0; l < lines; l++) { struct interrupt *irr = irrindex(irrs, l, cpus); if (unlikely(!irr->used)) continue; core_sum += irr->cpu[c].value; } if (unlikely(core_sum == 0)) continue; // try next core char id[50 + 1]; snprintfz(id, 50, "cpu%d_softirqs", c); char title[100 + 1]; snprintfz(title, 100, "CPU%d softirqs", c); core_st[c] = rrdset_create_localhost( "cpu" , id , NULL , "softirqs" , "cpu.softirqs" , title , "softirqs/s" , PLUGIN_PROC_NAME , PLUGIN_PROC_MODULE_SOFTIRQS_NAME , NETDATA_CHART_PRIO_SOFTIRQS_PER_CORE + c , update_every , RRDSET_TYPE_STACKED ); } else rrdset_next(core_st[c]); for(l = 0; l < lines ;l++) { struct interrupt *irr = irrindex(irrs, l, cpus); if(unlikely(!irr->used)) continue; if(unlikely(!irr->cpu[c].rd)) { irr->cpu[c].rd = rrddim_find(core_st[c], irr->id); if(unlikely(!irr->cpu[c].rd)) irr->cpu[c].rd = rrddim_add(core_st[c], irr->id, irr->name, 1, 1, RRD_ALGORITHM_INCREMENTAL); else rrddim_set_name(core_st[c], irr->cpu[c].rd, irr->name); } rrddim_set_by_pointer(core_st[c], irr->cpu[c].rd, irr->cpu[c].value); } rrdset_done(core_st[c]); } } return 0; }