| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "perflib-rrd.h" |
| 4 | |
| 5 | #define COLLECTED_NUMBER_PRECISION 10000 |
| 6 | |
| 7 | RRDDIM *perflib_rrddim_add( |
| 8 | RRDSET *st, |
| 9 | const char *id, |
| 10 | const char *name, |
| 11 | collected_number multiplier, |
| 12 | collected_number divider, |
| 13 | COUNTER_DATA *cd) |
| 14 | { |
| 15 | RRD_ALGORITHM algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 16 | |
| 17 | switch (cd->current.CounterType) { |
| 18 | case PERF_COUNTER_COUNTER: |
| 19 | case PERF_SAMPLE_COUNTER: |
| 20 | case PERF_COUNTER_BULK_COUNT: |
| 21 | // (N1 - N0) / ((D1 - D0) / F) |
| 22 | // multiplier *= cd->current.Frequency / 10000000; |
| 23 | // tested, the frequency is not that useful for netdata |
| 24 | // we get right results without it. |
| 25 | algorithm = RRD_ALGORITHM_INCREMENTAL; |
| 26 | break; |
| 27 | |
| 28 | case PERF_COUNTER_QUEUELEN_TYPE: |
| 29 | case PERF_COUNTER_100NS_QUEUELEN_TYPE: |
| 30 | case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE: |
| 31 | case PERF_COUNTER_LARGE_QUEUELEN_TYPE: |
| 32 | case PERF_AVERAGE_BULK: // normally not displayed |
| 33 | // (N1 - N0) / (D1 - D0) |
| 34 | algorithm = RRD_ALGORITHM_INCREMENTAL; |
| 35 | break; |
| 36 | |
| 37 | case PERF_OBJ_TIME_TIMER: |
| 38 | case PERF_COUNTER_TIMER: |
| 39 | case PERF_100NSEC_TIMER: |
| 40 | case PERF_PRECISION_SYSTEM_TIMER: |
| 41 | case PERF_PRECISION_100NS_TIMER: |
| 42 | case PERF_PRECISION_OBJECT_TIMER: |
| 43 | case PERF_SAMPLE_FRACTION: |
| 44 | // 100 * (N1 - N0) / (D1 - D0) |
| 45 | multiplier *= 100; |
| 46 | algorithm = RRD_ALGORITHM_INCREMENTAL; |
| 47 | break; |
| 48 | |
| 49 | case PERF_COUNTER_TIMER_INV: |
| 50 | case PERF_100NSEC_TIMER_INV: |
| 51 | // 100 * (1 - ((N1 - N0) / (D1 - D0))) |
| 52 | divider *= COLLECTED_NUMBER_PRECISION; |
| 53 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 54 | break; |
| 55 | |
| 56 | case PERF_COUNTER_MULTI_TIMER: |
| 57 | // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 |
| 58 | divider *= COLLECTED_NUMBER_PRECISION; |
| 59 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 60 | break; |
| 61 | |
| 62 | case PERF_100NSEC_MULTI_TIMER: |
| 63 | // 100 * ((N1 - N0) / (D1 - D0)) / B1 |
| 64 | divider *= COLLECTED_NUMBER_PRECISION; |
| 65 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 66 | break; |
| 67 | |
| 68 | case PERF_COUNTER_MULTI_TIMER_INV: |
| 69 | case PERF_100NSEC_MULTI_TIMER_INV: |
| 70 | // 100 * (B1 - ((N1 - N0) / (D1 - D0))) |
| 71 | divider *= COLLECTED_NUMBER_PRECISION; |
| 72 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 73 | break; |
| 74 | |
| 75 | case PERF_COUNTER_RAWCOUNT: |
| 76 | case PERF_COUNTER_LARGE_RAWCOUNT: |
| 77 | // N as decimal |
| 78 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 79 | break; |
| 80 | |
| 81 | case PERF_COUNTER_RAWCOUNT_HEX: |
| 82 | case PERF_COUNTER_LARGE_RAWCOUNT_HEX: |
| 83 | // N as hexadecimal |
| 84 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 85 | break; |
| 86 | |
| 87 | case PERF_COUNTER_DELTA: |
| 88 | case PERF_COUNTER_LARGE_DELTA: |
| 89 | // N1 - N0 |
| 90 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 91 | break; |
| 92 | |
| 93 | case PERF_RAW_FRACTION: |
| 94 | case PERF_LARGE_RAW_FRACTION: |
| 95 | // 100 * N / B |
| 96 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 97 | divider *= COLLECTED_NUMBER_PRECISION; |
| 98 | break; |
| 99 | |
| 100 | case PERF_AVERAGE_TIMER: |
| 101 | // ((N1 - N0) / TB) / (B1 - B0) |
| 102 | // divider *= cd->current.Frequency / 10000000; |
| 103 | algorithm = RRD_ALGORITHM_INCREMENTAL; |
| 104 | break; |
| 105 | |
| 106 | case PERF_ELAPSED_TIME: |
| 107 | // (D0 - N0) / F |
| 108 | algorithm = RRD_ALGORITHM_ABSOLUTE; |
| 109 | break; |
| 110 | |
| 111 | case PERF_COUNTER_TEXT: |
| 112 | case PERF_SAMPLE_BASE: |
| 113 | case PERF_AVERAGE_BASE: |
| 114 | case PERF_COUNTER_MULTI_BASE: |
| 115 | case PERF_RAW_BASE: |
| 116 | case PERF_COUNTER_NODATA: |
| 117 | case PERF_PRECISION_TIMESTAMP: |
| 118 | default: |
| 119 | break; |
| 120 | } |
| 121 | |
| 122 | return rrddim_add(st, id, name, multiplier, divider, algorithm); |
| 123 | } |
| 124 | |
| 125 | #define VALID_DELTA(cd) \ |
| 126 | ((cd)->previous.Time > 0 && (cd)->current.Data >= (cd)->previous.Data && (cd)->current.Time > (cd)->previous.Time) |
| 127 | |
| 128 | collected_number perflib_rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, COUNTER_DATA *cd) |
| 129 | { |
| 130 | ULONGLONG numerator = 0; |
| 131 | LONGLONG denominator = 0; |
| 132 | double doubleValue = 0.0; |
| 133 | collected_number value; |
| 134 | |
| 135 | switch (cd->current.CounterType) { |
| 136 | case PERF_COUNTER_COUNTER: |
| 137 | case PERF_SAMPLE_COUNTER: |
| 138 | case PERF_COUNTER_BULK_COUNT: |
| 139 | // (N1 - N0) / ((D1 - D0) / F) |
| 140 | value = (collected_number)cd->current.Data; |
| 141 | break; |
| 142 | |
| 143 | case PERF_COUNTER_QUEUELEN_TYPE: |
| 144 | case PERF_COUNTER_100NS_QUEUELEN_TYPE: |
| 145 | case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE: |
| 146 | case PERF_COUNTER_LARGE_QUEUELEN_TYPE: |
| 147 | case PERF_AVERAGE_BULK: // normally not displayed |
| 148 | // (N1 - N0) / (D1 - D0) |
| 149 | value = (collected_number)cd->current.Data; |
| 150 | break; |
| 151 | |
| 152 | case PERF_OBJ_TIME_TIMER: |
| 153 | case PERF_COUNTER_TIMER: |
| 154 | case PERF_100NSEC_TIMER: |
| 155 | case PERF_PRECISION_SYSTEM_TIMER: |
| 156 | case PERF_PRECISION_100NS_TIMER: |
| 157 | case PERF_PRECISION_OBJECT_TIMER: |
| 158 | case PERF_SAMPLE_FRACTION: |
| 159 | // 100 * (N1 - N0) / (D1 - D0) |
| 160 | value = (collected_number)cd->current.Data; |
| 161 | break; |
| 162 | |
| 163 | case PERF_COUNTER_TIMER_INV: |
| 164 | case PERF_100NSEC_TIMER_INV: |
| 165 | // 100 * (1 - ((N1 - N0) / (D1 - D0))) |
| 166 | if (!VALID_DELTA(cd)) |
| 167 | return 0; |
| 168 | numerator = cd->current.Data - cd->previous.Data; |
| 169 | denominator = cd->current.Time - cd->previous.Time; |
| 170 | doubleValue = 100.0 * (1.0 - ((double)numerator / (double)denominator)); |
| 171 | // printf("Display value is (timer-inv): %f%%\n", doubleValue); |
| 172 | value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); |
| 173 | break; |
| 174 | |
| 175 | case PERF_COUNTER_MULTI_TIMER: |
| 176 | // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 |
| 177 | if (!VALID_DELTA(cd) || !cd->current.Frequency || !cd->current.MultiCounterData) |
| 178 | return 0; |
| 179 | numerator = cd->current.Data - cd->previous.Data; |
| 180 | denominator = cd->current.Time - cd->previous.Time; |
| 181 | doubleValue = 100.0 * ((double)numerator / ((double)denominator / (double)cd->current.Frequency)) / |
| 182 | (double)cd->current.MultiCounterData; |
| 183 | // printf("Display value is (multi-timer): %f%%\n", doubleValue); |
| 184 | value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); |
| 185 | break; |
| 186 | |
| 187 | case PERF_100NSEC_MULTI_TIMER: |
| 188 | // 100 * ((N1 - N0) / (D1 - D0)) / B1 |
| 189 | if (!VALID_DELTA(cd) || !cd->current.MultiCounterData) |
| 190 | return 0; |
| 191 | numerator = cd->current.Data - cd->previous.Data; |
| 192 | denominator = cd->current.Time - cd->previous.Time; |
| 193 | doubleValue = 100.0 * ((double)numerator / (double)denominator) / (double)cd->current.MultiCounterData; |
| 194 | // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue); |
| 195 | value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); |
| 196 | break; |
| 197 | |
| 198 | case PERF_COUNTER_MULTI_TIMER_INV: |
| 199 | case PERF_100NSEC_MULTI_TIMER_INV: |
| 200 | // 100 * (B1 - ((N1 - N0) / (D1 - D0))) |
| 201 | if (!VALID_DELTA(cd) || !cd->current.MultiCounterData) |
| 202 | return 0; |
| 203 | numerator = cd->current.Data - cd->previous.Data; |
| 204 | denominator = cd->current.Time - cd->previous.Time; |
| 205 | doubleValue = 100.0 * ((double)cd->current.MultiCounterData - ((double)numerator / (double)denominator)); |
| 206 | // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue); |
| 207 | value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); |
| 208 | break; |
| 209 | |
| 210 | case PERF_COUNTER_RAWCOUNT: |
| 211 | case PERF_COUNTER_LARGE_RAWCOUNT: |
| 212 | // N as decimal |
| 213 | value = (collected_number)cd->current.Data; |
| 214 | break; |
| 215 | |
| 216 | case PERF_COUNTER_RAWCOUNT_HEX: |
| 217 | case PERF_COUNTER_LARGE_RAWCOUNT_HEX: |
| 218 | // N as hexadecimal |
| 219 | value = (collected_number)cd->current.Data; |
| 220 | break; |
| 221 | |
| 222 | case PERF_COUNTER_DELTA: |
| 223 | case PERF_COUNTER_LARGE_DELTA: |
| 224 | if (!VALID_DELTA(cd)) |
| 225 | return 0; |
| 226 | value = (collected_number)(cd->current.Data - cd->previous.Data); |
| 227 | break; |
| 228 | |
| 229 | case PERF_RAW_FRACTION: |
| 230 | case PERF_LARGE_RAW_FRACTION: |
| 231 | // 100 * N / B |
| 232 | if (!cd->current.Time) |
| 233 | return 0; |
| 234 | doubleValue = 100.0 * (double)cd->current.Data / (double)cd->current.Time; |
| 235 | // printf("Display value is (fraction): %f%%\n", doubleValue); |
| 236 | value = (collected_number)(doubleValue * COLLECTED_NUMBER_PRECISION); |
| 237 | break; |
| 238 | |
| 239 | default: |
| 240 | return 0; |
| 241 | } |
| 242 | |
| 243 | return rrddim_set_by_pointer(st, rd, value); |
| 244 | } |
| 245 | |
| 246 | /* |
| 247 | double perflibCalculateValue(RAW_DATA *current, RAW_DATA *previous) { |
| 248 | ULONGLONG numerator = 0; |
| 249 | LONGLONG denominator = 0; |
| 250 | double doubleValue = 0.0; |
| 251 | DWORD dwordValue = 0; |
| 252 | |
| 253 | if (NULL == previous) { |
| 254 | // Return error if the counter type requires two samples to calculate the value. |
| 255 | switch (current->CounterType) { |
| 256 | default: |
| 257 | if (PERF_DELTA_COUNTER != (current->CounterType & PERF_DELTA_COUNTER)) |
| 258 | break; |
| 259 | __fallthrough; |
| 260 | // fallthrough |
| 261 | |
| 262 | case PERF_AVERAGE_TIMER: // Special case. |
| 263 | case PERF_AVERAGE_BULK: // Special case. |
| 264 | // printf(" > The counter type requires two samples but only one sample was provided.\n"); |
| 265 | return NAN; |
| 266 | } |
| 267 | } |
| 268 | else { |
| 269 | if (current->CounterType != previous->CounterType) { |
| 270 | // printf(" > The samples have inconsistent counter types.\n"); |
| 271 | return NAN; |
| 272 | } |
| 273 | |
| 274 | // Check for integer overflow or bad data from provider (the data from |
| 275 | // sample 2 must be greater than the data from sample 1). |
| 276 | if (current->Data < previous->Data) |
| 277 | { |
| 278 | // Can happen for various reasons. Commonly occurs with the Process counterset when |
| 279 | // multiple processes have the same name and one of them starts or stops. |
| 280 | // Normally you'll just drop the older sample and continue. |
| 281 | // printf("> current (%llu) is smaller than previous (%llu).\n", current->Data, previous->Data); |
| 282 | return NAN; |
| 283 | } |
| 284 | } |
| 285 | |
| 286 | switch (current->CounterType) { |
| 287 | case PERF_COUNTER_COUNTER: |
| 288 | case PERF_SAMPLE_COUNTER: |
| 289 | case PERF_COUNTER_BULK_COUNT: |
| 290 | // (N1 - N0) / ((D1 - D0) / F) |
| 291 | numerator = current->Data - previous->Data; |
| 292 | denominator = current->Time - previous->Time; |
| 293 | dwordValue = (DWORD)(numerator / ((double)denominator / current->Frequency)); |
| 294 | //printf("Display value is (counter): %lu%s\n", (unsigned long)dwordValue, |
| 295 | // (previous->CounterType == PERF_SAMPLE_COUNTER) ? "" : "/sec"); |
| 296 | return (double)dwordValue; |
| 297 | |
| 298 | case PERF_COUNTER_QUEUELEN_TYPE: |
| 299 | case PERF_COUNTER_100NS_QUEUELEN_TYPE: |
| 300 | case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE: |
| 301 | case PERF_COUNTER_LARGE_QUEUELEN_TYPE: |
| 302 | case PERF_AVERAGE_BULK: // normally not displayed |
| 303 | // (N1 - N0) / (D1 - D0) |
| 304 | numerator = current->Data - previous->Data; |
| 305 | denominator = current->Time - previous->Time; |
| 306 | doubleValue = (double)numerator / denominator; |
| 307 | if (previous->CounterType != PERF_AVERAGE_BULK) { |
| 308 | // printf("Display value is (queuelen): %f\n", doubleValue); |
| 309 | return doubleValue; |
| 310 | } |
| 311 | return NAN; |
| 312 | |
| 313 | case PERF_OBJ_TIME_TIMER: |
| 314 | case PERF_COUNTER_TIMER: |
| 315 | case PERF_100NSEC_TIMER: |
| 316 | case PERF_PRECISION_SYSTEM_TIMER: |
| 317 | case PERF_PRECISION_100NS_TIMER: |
| 318 | case PERF_PRECISION_OBJECT_TIMER: |
| 319 | case PERF_SAMPLE_FRACTION: |
| 320 | // 100 * (N1 - N0) / (D1 - D0) |
| 321 | numerator = current->Data - previous->Data; |
| 322 | denominator = current->Time - previous->Time; |
| 323 | doubleValue = (double)(100 * numerator) / denominator; |
| 324 | // printf("Display value is (timer): %f%%\n", doubleValue); |
| 325 | return doubleValue; |
| 326 | |
| 327 | case PERF_COUNTER_TIMER_INV: |
| 328 | // 100 * (1 - ((N1 - N0) / (D1 - D0))) |
| 329 | numerator = current->Data - previous->Data; |
| 330 | denominator = current->Time - previous->Time; |
| 331 | doubleValue = 100 * (1 - ((double)numerator / denominator)); |
| 332 | // printf("Display value is (timer-inv): %f%%\n", doubleValue); |
| 333 | return doubleValue; |
| 334 | |
| 335 | case PERF_100NSEC_TIMER_INV: |
| 336 | // 100 * (1- (N1 - N0) / (D1 - D0)) |
| 337 | numerator = current->Data - previous->Data; |
| 338 | denominator = current->Time - previous->Time; |
| 339 | doubleValue = 100 * (1 - (double)numerator / denominator); |
| 340 | // printf("Display value is (100ns-timer-inv): %f%%\n", doubleValue); |
| 341 | return doubleValue; |
| 342 | |
| 343 | case PERF_COUNTER_MULTI_TIMER: |
| 344 | // 100 * ((N1 - N0) / ((D1 - D0) / TB)) / B1 |
| 345 | numerator = current->Data - previous->Data; |
| 346 | denominator = current->Time - previous->Time; |
| 347 | denominator /= current->Frequency; |
| 348 | doubleValue = 100 * ((double)numerator / denominator) / current->MultiCounterData; |
| 349 | // printf("Display value is (multi-timer): %f%%\n", doubleValue); |
| 350 | return doubleValue; |
| 351 | |
| 352 | case PERF_100NSEC_MULTI_TIMER: |
| 353 | // 100 * ((N1 - N0) / (D1 - D0)) / B1 |
| 354 | numerator = current->Data - previous->Data; |
| 355 | denominator = current->Time - previous->Time; |
| 356 | doubleValue = 100 * ((double)numerator / (double)denominator) / (double)current->MultiCounterData; |
| 357 | // printf("Display value is (100ns multi-timer): %f%%\n", doubleValue); |
| 358 | return doubleValue; |
| 359 | |
| 360 | case PERF_COUNTER_MULTI_TIMER_INV: |
| 361 | case PERF_100NSEC_MULTI_TIMER_INV: |
| 362 | // 100 * (B1 - ((N1 - N0) / (D1 - D0))) |
| 363 | numerator = current->Data - previous->Data; |
| 364 | denominator = current->Time - previous->Time; |
| 365 | doubleValue = 100.0 * ((double)current->MultiCounterData - ((double)numerator / (double)denominator)); |
| 366 | // printf("Display value is (multi-timer-inv): %f%%\n", doubleValue); |
| 367 | return doubleValue; |
| 368 | |
| 369 | case PERF_COUNTER_RAWCOUNT: |
| 370 | case PERF_COUNTER_LARGE_RAWCOUNT: |
| 371 | // N as decimal |
| 372 | // printf("Display value is (rawcount): %llu\n", current->Data); |
| 373 | return (double)current->Data; |
| 374 | |
| 375 | case PERF_COUNTER_RAWCOUNT_HEX: |
| 376 | case PERF_COUNTER_LARGE_RAWCOUNT_HEX: |
| 377 | // N as hexadecimal |
| 378 | // printf("Display value is (hex): 0x%llx\n", current->Data); |
| 379 | return (double)current->Data; |
| 380 | |
| 381 | case PERF_COUNTER_DELTA: |
| 382 | case PERF_COUNTER_LARGE_DELTA: |
| 383 | // N1 - N0 |
| 384 | // printf("Display value is (delta): %llu\n", current->Data - previous->Data); |
| 385 | return (double)(current->Data - previous->Data); |
| 386 | |
| 387 | case PERF_RAW_FRACTION: |
| 388 | case PERF_LARGE_RAW_FRACTION: |
| 389 | // 100 * N / B |
| 390 | doubleValue = 100.0 * (double)current->Data / (double)current->Time; |
| 391 | // printf("Display value is (fraction): %f%%\n", doubleValue); |
| 392 | return doubleValue; |
| 393 | |
| 394 | case PERF_AVERAGE_TIMER: |
| 395 | // ((N1 - N0) / TB) / (B1 - B0) |
| 396 | numerator = current->Data - previous->Data; |
| 397 | denominator = current->Time - previous->Time; |
| 398 | doubleValue = (double)numerator / (double)current->Frequency / (double)denominator; |
| 399 | // printf("Display value is (average timer): %f seconds\n", doubleValue); |
| 400 | return doubleValue; |
| 401 | |
| 402 | case PERF_ELAPSED_TIME: |
| 403 | // (D0 - N0) / F |
| 404 | doubleValue = (double)(current->Time - current->Data) / (double)current->Frequency; |
| 405 | // printf("Display value is (elapsed time): %f seconds\n", doubleValue); |
| 406 | return doubleValue; |
| 407 | |
| 408 | case PERF_COUNTER_TEXT: |
| 409 | case PERF_SAMPLE_BASE: |
| 410 | case PERF_AVERAGE_BASE: |
| 411 | case PERF_COUNTER_MULTI_BASE: |
| 412 | case PERF_RAW_BASE: |
| 413 | case PERF_COUNTER_NODATA: |
| 414 | case PERF_PRECISION_TIMESTAMP: |
| 415 | // printf(" > Non-printing counter type: 0x%08x\n", current->CounterType); |
| 416 | return NAN; |
| 417 | break; |
| 418 | |
| 419 | default: |
| 420 | // printf(" > Unrecognized counter type: 0x%08x\n", current->CounterType); |
| 421 | return NAN; |
| 422 | break; |
| 423 | } |
| 424 | } |
| 425 | */ |