1#!/usr/local/bin/perl 2# promdatagen - generate promdata.[ch] files 3 4# Copyright (c) 1994-2017 Carnegie Mellon University. All rights reserved. 5 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in 15# the documentation and/or other materials provided with the 16# distribution. 17 18# 3. The name "Carnegie Mellon University" must not be used to 19# endorse or promote products derived from this software without 20# prior written permission. For permission or any legal 21# details, please contact 22# Carnegie Mellon University 23# Center for Technology Transfer and Enterprise Creation 24# 4615 Forbes Avenue 25# Suite 302 26# Pittsburgh, PA 15213 27# (412) 268-7393, fax: (412) 268-7395 28# innovation@andrew.cmu.edu 29 30# 4. Redistributions of any form whatsoever must retain the following 31# acknowledgment: 32# "This product includes software developed by Computing Services 33# at Carnegie Mellon University (http://www.cmu.edu/computing/)." 34 35# CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 36# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 37# AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 38# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 39# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 40# AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 41# OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 42 43use warnings; 44use strict; 45 46use Data::Dumper; 47use Getopt::Std; 48 49my %types = ( counter => 'PROM_METRIC_COUNTER', gauge => 'PROM_METRIC_GAUGE' ); 50 51my %options; 52my @metrics; 53my @labels; 54 55sub output_header; 56sub output_source; 57 58die "usage\n" if not getopts("h:c:v", \%options); 59 60my $lineno = 0; 61while (my $line = <>) { 62 $lineno ++; 63 64 chomp $line; 65 $line =~ s{#.*$}{}; # eat comments 66 $line =~ s{\s+$}{}; # eat trailing whitespace 67 next if $line =~ m{^\s*$}; # eat empty lines 68 69 if ($line =~ m{^\s*metric\s}) { 70 # parse a metric: 71 # metric counter imap_connections_total The total number of IMAP connections 72 $line =~ s{^\s*metric\s+}{}; 73 my ($type, $name, $help) = split /\s+/, $line, 3; 74 75 if (not exists $types{$type}) { 76 die "\"$type\" is not a valid type at line $lineno\n"; 77 } 78 79 if ($name !~ m{^[a-z][a-z0-9_]*$}) { 80 die "\"$name\" is not a valid metric name at line $lineno\n"; 81 } 82 83 push @metrics, { type => $type, name => $name, help => $help }; 84 85 } 86 elsif ($line =~ m{^\s*label\s}) { 87 # parse a label: 88 # label imap_authenticate_count result yes no 89 $line =~ s{^\s*label\s+}{}; 90 my ($name, $label, @values) = split /\s+/, $line; 91 92 if (not scalar grep { $_->{name} eq $name } @metrics) { 93 die "cannot define label \"$label\" for unknown metric \"$name\" at line $lineno\n"; 94 } 95 96 if ($label !~ m{^[a-z][a-z0-9_]*$}) { 97 die "\"$label\" is not a valid label at line $lineno\n"; 98 } 99 100 foreach my $v (@values) { 101 if ($v !~ m{^[a-z][a-z0-9_]*$}) { 102 die "\"$v\" is not a valid value at line $lineno\n"; 103 } 104 } 105 106 foreach my $metric (@metrics) { 107 if ($metric->{name} eq $name) { 108 if (exists $metric->{label} ) { 109 die "cannot define more than one label for metric \"$name\" at line $lineno\n"; 110 } 111 $metric->{label} = { name => $name, label => $label, values => [ @values ] }; 112 push @labels, $metric->{label}; 113 last; 114 } 115 } 116 } 117 else { 118 warn "skipping unparseable line at line $lineno: $line\n"; 119 next; 120 } 121} 122 123output_header($options{h}, \@metrics, \@labels) if $options{h}; 124output_source($options{c}, \@metrics, \@labels) if $options{c}; 125 126exit 0; 127 128sub output_header 129{ 130 my ($fname, $metrics, $labels) = @_; 131 132 open my $header, '>', $fname or die "$fname: $!\n"; 133 print $header "#ifndef INCLUDE_PROMDATA_H\n#define INCLUDE_PROMDATA_H\n"; 134 print $header "/* generated from $ARGV */\n"; 135 136 print $header <<OKAY; 137 138#include <sys/types.h> 139 140#include <stdint.h> 141 142enum prom_metric_type { 143 PROM_METRIC_COUNTER = 0, 144 PROM_METRIC_GAUGE = 1, 145 PROM_METRIC_HISTOGRAM = 2, /* unused */ 146 PROM_METRIC_SUMMARY = 3, /* unused */ 147 PROM_METRIC_CONTINUED = 4, /* internal use only */ 148}; 149extern const char *prom_metric_type_names[]; 150 151OKAY 152 153 print $header "enum prom_metric_id {\n"; 154 my $first = 1; 155 foreach my $metric (@{$metrics}) { 156 if (exists $metric->{label}) { 157 foreach my $v (@{$metric->{label}->{values}}) { 158 print $header " \U$metric->{name}_$metric->{label}->{label}_$v\E"; 159 print $header q{ = 0} if $first; 160 $first = 0; 161 print $header qq{,\n}; 162 } 163 } 164 else { 165 print $header q{ }, uc($metric->{name}); 166 print $header q{ = 0} if $first; 167 $first = 0; 168 print $header qq{,\n}; 169 } 170 } 171 print $header "\n PROM_NUM_METRICS /* n.b. leave last! */\n"; 172 print $header "};\n\n"; 173 174 print $header "enum prom_labelled_metric {\n"; 175 $first = 1; 176 foreach my $label (@{$labels}) { 177 print $header " \U$label->{name}\E"; 178 print $header q{ = 0} if $first; 179 $first = 0; 180 print $header qq{,\n}; 181 } 182 print $header "\n PROM_NUM_LABELLED_METRICS /* n.b. leave last! */\n"; 183 print $header "};\n"; 184 185print $header <<OKAY; 186 187struct prom_label_lookup_value { 188 const char *value; 189 enum prom_metric_id id; 190}; 191 192extern const struct prom_label_lookup_value *prom_label_lookup_table[]; 193 194OKAY 195 196 print $header <<OKAY; 197 198struct prom_metric_desc { 199 const char *name; 200 enum prom_metric_type type; 201 const char *help; 202 const char *label; 203}; 204extern const struct prom_metric_desc prom_metric_descs[]; 205 206struct prom_metric { 207 double value; 208 int64_t last_updated; 209}; 210 211struct prom_stats { 212 char ident[512]; /* XXX places upper limit on service names */ 213 struct prom_metric metrics[PROM_NUM_METRICS]; 214}; 215#define PROM_STATS_INITIALIZER { {0}, {{0, 0}} } 216 217OKAY 218 219 print $header "#endif\n"; 220 close $header; 221} 222 223sub output_source 224{ 225 my ($fname, $metrics, $labels) = @_; 226 227 open my $source, '>', $fname or die "$fname: $!\n"; 228 229 print $source <<OKAY; 230/* generated from $ARGV */ 231 232#include <config.h> 233 234#include "imap/promdata.h" /* XXX */ 235 236EXPORTED const char *prom_metric_type_names[] = { 237 "counter", 238 "gauge", 239 "histogram", 240 "summary", 241 NULL, 242}; 243 244OKAY 245 246 print $source "EXPORTED const struct prom_metric_desc prom_metric_descs[] = {\n"; 247 foreach my $metric (@{$metrics}) { 248 if (exists $metric->{label}) { 249 my $first = 1; 250 foreach my $v (@{$metric->{label}->{values}}) { 251 printf $source ' { "%s", %s, ', 252 $metric->{name}, 253 ($first ? $types{$metric->{type}} : "PROM_METRIC_CONTINUED"); 254 if ($first && defined $metric->{help}) { 255 printf $source '"%s", ', $metric->{help}; 256 } 257 else { 258 print $source "NULL, "; 259 } 260 printf $source '"%s=\\"%s\\""', $metric->{label}->{label}, $v; 261 print $source " },\n"; 262 $first = 0; 263 } 264 } 265 else { 266 printf $source ' { "%s", %s, ', 267 $metric->{name}, 268 $types{$metric->{type}}; 269 if (defined $metric->{help}) { 270 printf $source '"%s",', $metric->{help}; 271 } 272 else { 273 print $source "NULL,"; 274 } 275 print $source " NULL },\n"; 276 } 277 } 278 print $source " { NULL, 0, NULL, NULL },\n"; 279 print $source "};\n\n"; 280 281 foreach my $label(@{$labels}) { 282 print $source "static const struct prom_label_lookup_value "; 283 print $source "\U$label->{name}_$label->{label}\E_values[] = {\n"; 284 foreach my $value(sort @{$label->{values}}) { 285 print $source " { \"$value\", \U$label->{name}_$label->{label}_$value\E },\n"; 286 } 287 print $source " { NULL, 0 },\n"; 288 print $source "};\n\n"; 289 } 290 291 print $source "EXPORTED const struct prom_label_lookup_value *prom_label_lookup_table[] = {\n"; 292 foreach my $label (@{$labels}) { 293 print $source " \U$label->{name}_$label->{label}\E_values,\n"; 294 } 295 print $source "};\n"; 296 297 close $source; 298} 299