Coverage Report

Created: 2026-08-09 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/plugins/in_gpu_metrics/amd_gpu.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*  Fluent Bit
4
 *  ==========
5
 *  Copyright (C) 2015-2026 The Fluent Bit Authors
6
 *
7
 *  Licensed under the Apache License, Version 2.0 (the "License");
8
 *  you may not use this file except in compliance with the License.
9
 *  You may obtain a copy of the License at
10
 *
11
 *      http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *  Unless required by applicable law or agreed to in writing, software
14
 *  distributed under the License is distributed on an "AS IS" BASIS,
15
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *  See the License for the specific language governing permissions and
17
 *  limitations under the License.
18
 */
19
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <string.h>
23
#include <dirent.h>
24
#include <limits.h>
25
#include <unistd.h>
26
27
#include <fluent-bit/flb_log.h>
28
#include <fluent-bit/flb_input_plugin.h>
29
#include <fluent-bit/flb_mem.h>
30
#include <fluent-bit/flb_sds.h>
31
32
#include "gpu_common.h"
33
#include "amd_gpu.h"
34
35
0
#define AMD_VENDOR_ID "0x1002"
36
37
static const char *sysfs_path = "/sys";
38
39
static flb_sds_t build_path(int card_id, const char *file)
40
0
{
41
0
    flb_sds_t path = NULL;
42
0
    path = flb_sds_create_size(256); /* Allocate initial buffer */
43
0
    if (!path) {
44
0
        return NULL;
45
0
    }
46
0
    path = flb_sds_printf(&path, "%s/class/drm/card%d/device/%s", sysfs_path, card_id, file);
47
0
    return path;
48
0
}
49
50
static void free_cards(struct in_gpu_metrics *ctx)
51
0
{
52
0
    struct cfl_list *tmp;
53
0
    struct cfl_list *head;
54
0
    struct gpu_card *card;
55
56
0
    cfl_list_foreach_safe(head, tmp, &ctx->cards) {
57
0
        card = cfl_list_entry(head, struct gpu_card, _head);
58
0
        cfl_list_del(&card->_head);
59
0
        if (card->hwmon_path) {
60
0
            flb_sds_destroy(card->hwmon_path);
61
0
        }
62
0
        flb_free(card);
63
0
    }
64
0
}
65
66
static flb_sds_t find_hwmon_path(int card_id)
67
0
{
68
0
    char name[64];
69
0
    flb_sds_t path = NULL;
70
0
    flb_sds_t name_path = NULL;
71
0
    flb_sds_t hwmon_path = NULL;
72
0
    DIR *dir;
73
0
    struct dirent *entry;
74
75
0
    path = flb_sds_create_size(256); /* Allocate initial buffer */
76
0
    if (!path) {
77
0
        return NULL;
78
0
    }
79
0
    path = flb_sds_printf(&path, "%s/class/drm/card%d/device/hwmon", sysfs_path, card_id);
80
0
    if (!path) {
81
0
        return NULL;
82
0
    }
83
0
    dir = opendir(path);
84
0
    if (!dir) {
85
0
        flb_sds_destroy(path);
86
0
        return NULL;
87
0
    }
88
89
0
    while ((entry = readdir(dir))) {
90
0
        if (strncmp(entry->d_name, "hwmon", 5) != 0) {
91
0
            continue;
92
0
        }
93
0
        flb_sds_destroy(name_path);
94
0
        name_path = flb_sds_create_size(256); /* Allocate initial buffer */
95
0
        if (!name_path) {
96
0
            continue;
97
0
        }
98
0
        name_path = flb_sds_printf(&name_path, "%s/class/drm/card%d/device/hwmon/%s/name",
99
0
                                   sysfs_path, card_id, entry->d_name);
100
0
        if (!name_path) {
101
0
            continue;
102
0
        }
103
0
        if (gpu_read_line(name_path, name, sizeof(name)) == 0) {
104
0
            if (strncmp(name, "amdgpu", 6) == 0) {
105
0
                hwmon_path = flb_sds_create_size(256); /* Allocate initial buffer */
106
0
                if (!hwmon_path) {
107
0
                    flb_sds_destroy(name_path);
108
0
                    flb_sds_destroy(path);
109
0
                    closedir(dir);
110
0
                    return NULL;
111
0
                }
112
0
                hwmon_path = flb_sds_printf(&hwmon_path, "%s/class/drm/card%d/device/hwmon/%s",
113
0
                                           sysfs_path, card_id, entry->d_name);
114
0
                flb_sds_destroy(name_path);
115
0
                flb_sds_destroy(path);
116
0
                closedir(dir);
117
0
                return hwmon_path;
118
0
            }
119
0
        }
120
0
        flb_sds_destroy(name_path);
121
0
        name_path = NULL;
122
0
    }
123
0
    flb_sds_destroy(path);
124
0
    closedir(dir);
125
0
    return NULL;
126
0
}
127
128
int amd_gpu_detect_cards(struct in_gpu_metrics *ctx)
129
0
{
130
0
    DIR *dir;
131
0
    int id;
132
0
    char vendor[32];
133
0
    struct dirent *entry;
134
0
    flb_sds_t path = NULL;
135
0
    flb_sds_t vendor_path = NULL;
136
0
    struct gpu_card *card;
137
138
    /* Check if cards have already been detected */
139
0
    if (ctx->cards_detected) {
140
0
        flb_plg_debug(ctx->ins, "Cards already detected, skipping detection");
141
0
        return 0;
142
0
    }
143
144
0
    sysfs_path = ctx->path_sysfs ? ctx->path_sysfs : "/sys";
145
0
    flb_plg_info(ctx->ins, "Starting AMD GPU detection in %s", sysfs_path);
146
147
0
    path = flb_sds_create_size(256); /* Allocate initial buffer */
148
0
    if (!path) {
149
0
        flb_plg_warn(ctx->ins, "failed to allocate path buffer");
150
0
        return -1;
151
0
    }
152
0
    path = flb_sds_printf(&path, "%s/class/drm", sysfs_path);
153
0
    if (!path) {
154
0
        flb_plg_warn(ctx->ins, "failed to create path for class/drm");
155
0
        return -1;
156
0
    }
157
0
    if (access(path, F_OK) != 0) {
158
0
        flb_plg_warn(ctx->ins, "%s not found", path);
159
0
        flb_sds_destroy(path);
160
0
        return 0;
161
0
    }
162
163
0
    dir = opendir(path);
164
0
    if (!dir) {
165
0
        flb_plg_warn(ctx->ins, "could not open %s", path);
166
0
        flb_sds_destroy(path);
167
0
        return -1;
168
0
    }
169
170
0
    while ((entry = readdir(dir))) {
171
        /* Only match exact "cardX" pattern, not "cardX-*" */
172
0
        if (strncmp(entry->d_name, "card", 4) != 0) {
173
0
            continue;
174
0
        }
175
176
        /* Skip entries that have additional parts after the card number */
177
0
        if (strlen(entry->d_name) > 5 && entry->d_name[5] != '\0') {
178
0
            continue;
179
0
        }
180
0
        id = atoi(entry->d_name + 4);
181
0
        flb_plg_info(ctx->ins, "Found card%d", id);
182
183
0
        flb_sds_destroy(vendor_path);
184
0
        vendor_path = flb_sds_create_size(256);
185
0
        if (!vendor_path) {
186
0
            flb_plg_debug(ctx->ins, "failed to allocate vendor path buffer");
187
0
            continue;
188
0
        }
189
190
0
        vendor_path = flb_sds_printf(&vendor_path, "%s/class/drm/%s/device/vendor", sysfs_path, entry->d_name);
191
0
        if (!vendor_path) {
192
0
            flb_plg_debug(ctx->ins, "failed to create path for vendor file");
193
0
            continue;
194
0
        }
195
196
0
        if (gpu_read_line(vendor_path, vendor, sizeof(vendor)) != 0) {
197
0
            flb_plg_debug(ctx->ins, "could not read %s", vendor_path);
198
0
            continue;
199
0
        }
200
201
0
        flb_plg_info(ctx->ins, "Card%d vendor: %s", id, vendor);
202
0
        if (strncmp(vendor, AMD_VENDOR_ID, strlen(AMD_VENDOR_ID)) != 0) {
203
0
            flb_plg_info(ctx->ins, "Card%d is not AMD (vendor: %s)", id, vendor);
204
0
            continue;
205
0
        }
206
207
0
        flb_plg_info(ctx->ins, "Checking if card%d should be included", id);
208
0
        if (!gpu_should_include_card(ctx, id)) {
209
0
            flb_plg_info(ctx->ins, "Card%d excluded by filter", id);
210
0
            continue;
211
0
        }
212
0
        flb_plg_info(ctx->ins, "Card%d passed filter check", id);
213
0
        card = flb_calloc(1, sizeof(struct gpu_card));
214
0
        if (!card) {
215
0
            flb_errno();
216
0
            closedir(dir);
217
0
            flb_sds_destroy(path);
218
0
            flb_sds_destroy(vendor_path);
219
0
            free_cards(ctx);
220
0
            return -1;
221
0
        }
222
0
        card->id = id;
223
0
        card->backend_type = GPU_BACKEND_AMD;
224
0
        card->hwmon_path = NULL;
225
0
        card->uuid = NULL;
226
0
        card->parent_uuid = NULL;
227
0
        card->gpu_instance_id = -1;
228
0
        card->compute_instance_id = -1;
229
0
        card->hwmon_path = find_hwmon_path(id);
230
0
        if (!card->hwmon_path) {
231
0
            flb_plg_debug(ctx->ins, "no hwmon path for card%d", id);
232
0
        }
233
0
        cfl_list_add(&card->_head, &ctx->cards);
234
0
        flb_plg_info(ctx->ins, "detected AMD GPU card%d", id);
235
0
    }
236
0
    closedir(dir);
237
0
    flb_sds_destroy(path);
238
0
    flb_sds_destroy(vendor_path);
239
240
    /* Mark that cards have been detected */
241
0
    ctx->cards_detected = 1;
242
0
    return 0;
243
0
}
244
245
int amd_gpu_read_utilization(struct in_gpu_metrics *ctx, int card_id, double *utilization)
246
0
{
247
0
    flb_sds_t path = NULL;
248
0
    uint64_t val;
249
250
0
    path = build_path(card_id, "gpu_busy_percent");
251
0
    if (!path) {
252
0
        flb_plg_debug(ctx->ins, "failed to build path for gpu_busy_percent");
253
0
        return -1;
254
0
    }
255
0
    if (gpu_read_uint64(path, &val) != 0) {
256
0
        flb_plg_debug(ctx->ins, "failed to read %s", path);
257
0
        flb_sds_destroy(path);
258
0
        return -1;
259
0
    }
260
0
    flb_sds_destroy(path);
261
0
    *utilization = (double) val;
262
0
    return 0;
263
0
}
264
265
int amd_gpu_read_memory_info(struct in_gpu_metrics *ctx, int card_id, uint64_t *used, uint64_t *total)
266
0
{
267
0
    flb_sds_t path = NULL;
268
269
0
    path = build_path(card_id, "mem_info_vram_used");
270
0
    if (!path) {
271
0
        flb_plg_debug(ctx->ins, "failed to build path for mem_info_vram_used");
272
0
        return -1;
273
0
    }
274
0
    if (gpu_read_uint64(path, used) != 0) {
275
0
        flb_plg_debug(ctx->ins, "failed to read %s", path);
276
0
        flb_sds_destroy(path);
277
0
        return -1;
278
0
    }
279
0
    flb_sds_destroy(path);
280
281
0
    path = build_path(card_id, "mem_info_vram_total");
282
0
    if (!path) {
283
0
        flb_plg_debug(ctx->ins, "failed to build path for mem_info_vram_total");
284
0
        return -1;
285
0
    }
286
0
    if (gpu_read_uint64(path, total) != 0) {
287
0
        flb_plg_debug(ctx->ins, "failed to read %s", path);
288
0
        flb_sds_destroy(path);
289
0
        return -1;
290
0
    }
291
0
    flb_sds_destroy(path);
292
0
    return 0;
293
0
}
294
295
static int read_clock_file(struct in_gpu_metrics *ctx, int card_id, const char *file, double *clock)
296
0
{
297
0
    flb_sds_t path = NULL;
298
0
    char line[256];
299
0
    FILE *fp;
300
301
0
    path = build_path(card_id, file);
302
0
    if (!path) {
303
0
        flb_plg_debug(ctx->ins, "failed to build path for %s", file);
304
0
        return -1;
305
0
    }
306
0
    fp = fopen(path, "r");
307
0
    if (!fp) {
308
0
        flb_plg_debug(ctx->ins, "could not open %s", path);
309
0
        flb_sds_destroy(path);
310
0
        return -1;
311
0
    }
312
0
    while (fgets(line, sizeof(line), fp)) {
313
0
        if (strchr(line, '*')) {
314
0
            double freq;
315
0
            if (sscanf(line, "%*d: %lfMhz", &freq) == 1) {
316
0
                *clock = freq;
317
0
                fclose(fp);
318
0
                flb_sds_destroy(path);
319
0
                return 0;
320
0
            }
321
0
        }
322
0
    }
323
0
    fclose(fp);
324
0
    flb_plg_debug(ctx->ins, "no active clock in %s", path);
325
0
    flb_sds_destroy(path);
326
0
    return -1;
327
0
}
328
329
static int read_power_watts(struct in_gpu_metrics *ctx, struct gpu_card *card, double *power)
330
0
{
331
0
    flb_sds_t path = NULL;
332
333
0
    if (!card->hwmon_path) {
334
0
        return -1;
335
0
    }
336
0
    path = flb_sds_create_size(256); /* Allocate initial buffer */
337
0
    if (!path) {
338
0
        flb_plg_debug(ctx->ins, "failed to allocate path buffer");
339
0
        return -1;
340
0
    }
341
0
    path = flb_sds_printf(&path, "%s/power1_average", card->hwmon_path);
342
0
    if (!path) {
343
0
        flb_plg_debug(ctx->ins, "failed to create path for power1_average");
344
0
        return -1;
345
0
    }
346
0
    if (gpu_read_double(path, 1000000.0, power) != 0) {
347
0
        flb_plg_debug(ctx->ins, "failed to read %s", path);
348
0
        flb_sds_destroy(path);
349
0
        return -1;
350
0
    }
351
0
    flb_sds_destroy(path);
352
0
    return 0;
353
0
}
354
355
static int read_temp_celsius(struct in_gpu_metrics *ctx, struct gpu_card *card, double *temp)
356
0
{
357
0
    flb_sds_t path = NULL;
358
359
0
    if (!card->hwmon_path) {
360
0
        return -1;
361
0
    }
362
0
    path = flb_sds_create_size(256); /* Allocate initial buffer */
363
0
    if (!path) {
364
0
        flb_plg_debug(ctx->ins, "failed to allocate path buffer");
365
0
        return -1;
366
0
    }
367
0
    path = flb_sds_printf(&path, "%s/temp1_input", card->hwmon_path);
368
0
    if (!path) {
369
0
        flb_plg_debug(ctx->ins, "failed to create path for temp1_input");
370
0
        return -1;
371
0
    }
372
0
    if (gpu_read_double(path, 1000.0, temp) != 0) {
373
0
        flb_plg_debug(ctx->ins, "failed to read %s", path);
374
0
        flb_sds_destroy(path);
375
0
        return -1;
376
0
    }
377
0
    flb_sds_destroy(path);
378
0
    return 0;
379
0
}
380
381
static int read_fan_speed(struct in_gpu_metrics *ctx, struct gpu_card *card, double *speed)
382
0
{
383
0
    flb_sds_t path = NULL;
384
0
    uint64_t val;
385
386
0
    if (!card->hwmon_path) {
387
0
        return -1;
388
0
    }
389
0
    path = flb_sds_create_size(256); /* Allocate initial buffer */
390
0
    if (!path) {
391
0
        flb_plg_debug(ctx->ins, "failed to allocate path buffer");
392
0
        return -1;
393
0
    }
394
0
    path = flb_sds_printf(&path, "%s/fan1_input", card->hwmon_path);
395
0
    if (!path) {
396
0
        flb_plg_debug(ctx->ins, "failed to create path for fan1_input");
397
0
        return -1;
398
0
    }
399
0
    if (gpu_read_uint64(path, &val) != 0) {
400
0
        flb_plg_debug(ctx->ins, "failed to read %s", path);
401
0
        flb_sds_destroy(path);
402
0
        return -1;
403
0
    }
404
0
    flb_sds_destroy(path);
405
0
    *speed = (double) val;
406
0
    return 0;
407
0
}
408
409
static int read_fan_pwm(struct in_gpu_metrics *ctx, struct gpu_card *card, double *pwm)
410
0
{
411
0
    flb_sds_t path = NULL;
412
0
    uint64_t val;
413
414
0
    if (!card->hwmon_path) {
415
0
        return -1;
416
0
    }
417
0
    path = flb_sds_create_size(256); /* Allocate initial buffer */
418
0
    if (!path) {
419
0
        flb_plg_debug(ctx->ins, "failed to allocate path buffer");
420
0
        return -1;
421
0
    }
422
0
    path = flb_sds_printf(&path, "%s/pwm1", card->hwmon_path);
423
0
    if (!path) {
424
0
        flb_plg_debug(ctx->ins, "failed to create path for pwm1");
425
0
        return -1;
426
0
    }
427
0
    if (gpu_read_uint64(path, &val) != 0) {
428
0
        flb_plg_debug(ctx->ins, "failed to read %s", path);
429
0
        flb_sds_destroy(path);
430
0
        return -1;
431
0
    }
432
0
    flb_sds_destroy(path);
433
0
    *pwm = ((double) val * 100.0) / 255.0;
434
0
    return 0;
435
0
}
436
437
int amd_gpu_collect_metrics(struct in_gpu_metrics *ctx, struct gpu_card *card)
438
0
{
439
0
    double utilization;
440
0
    uint64_t used;
441
0
    uint64_t total;
442
0
    double clock;
443
0
    double power;
444
0
    double temp;
445
0
    double fan_speed;
446
0
    double fan_pwm;
447
0
    uint64_t ts;
448
0
    flb_sds_t card_id = NULL;
449
450
0
    ts = cfl_time_now();
451
0
    card_id = flb_sds_create_size(32); /* Allocate initial buffer for card ID */
452
0
    if (!card_id) {
453
0
        flb_plg_debug(ctx->ins, "failed to allocate card_id buffer");
454
0
        return -1;
455
0
    }
456
0
    card_id = flb_sds_printf(&card_id, "%d", card->id);
457
0
    if (!card_id) {
458
0
        flb_plg_debug(ctx->ins, "failed to create card_id string");
459
0
        return -1;
460
0
    }
461
462
0
    if (amd_gpu_read_utilization(ctx, card->id, &utilization) == 0) {
463
0
        cmt_gauge_set(ctx->g_utilization, ts, utilization, 2,
464
0
                      (char *[]) {card_id, "amd"});
465
0
    }
466
467
0
    if (amd_gpu_read_memory_info(ctx, card->id, &used, &total) == 0) {
468
0
        cmt_gauge_set(ctx->g_mem_used, ts, (double) used, 2,
469
0
                      (char *[]) {card_id, "amd"});
470
0
        cmt_gauge_set(ctx->g_mem_total, ts, (double) total, 2,
471
0
                      (char *[]) {card_id, "amd"});
472
0
    }
473
474
0
    if (read_clock_file(ctx, card->id, "pp_dpm_sclk", &clock) == 0) {
475
0
        cmt_gauge_set(ctx->g_clock, ts, clock, 3,
476
0
                      (char *[]) {card_id, "amd", "graphics"});
477
0
    }
478
0
    if (read_clock_file(ctx, card->id, "pp_dpm_mclk", &clock) == 0) {
479
0
        cmt_gauge_set(ctx->g_clock, ts, clock, 3,
480
0
                      (char *[]) {card_id, "amd", "memory"});
481
0
    }
482
0
    if (read_clock_file(ctx, card->id, "pp_dpm_socclk", &clock) == 0) {
483
0
        cmt_gauge_set(ctx->g_clock, ts, clock, 3,
484
0
                      (char *[]) {card_id, "amd", "soc"});
485
0
    }
486
487
0
    if (ctx->enable_power && read_power_watts(ctx, card, &power) == 0) {
488
0
        cmt_gauge_set(ctx->g_power, ts, power, 2,
489
0
                      (char *[]) {card_id, "amd"});
490
0
    }
491
492
0
    if (ctx->enable_temperature && read_temp_celsius(ctx, card, &temp) == 0) {
493
0
        cmt_gauge_set(ctx->g_temp, ts, temp, 2,
494
0
                      (char *[]) {card_id, "amd"});
495
0
    }
496
497
0
    if (read_fan_speed(ctx, card, &fan_speed) == 0) {
498
0
        cmt_gauge_set(ctx->g_fan_speed, ts, fan_speed, 2,
499
0
                      (char *[]) {card_id, "amd"});
500
0
    }
501
502
0
    if (read_fan_pwm(ctx, card, &fan_pwm) == 0) {
503
0
        cmt_gauge_set(ctx->g_fan_pwm, ts, fan_pwm, 2,
504
0
                      (char *[]) {card_id, "amd"});
505
0
    }
506
507
0
    flb_sds_destroy(card_id);
508
0
    return 0;
509
0
}