LCOV - code coverage report
Current view: top level - plugins/lvm - vdo_stats.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 74.8 % 143 107
Test Date: 2026-01-23 09:12:16 Functions: 100.0 % 8 8
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2020  Red Hat, Inc.
       3              :  *
       4              :  * This library is free software; you can redistribute it and/or
       5              :  * modify it under the terms of the GNU Lesser General Public
       6              :  * License as published by the Free Software Foundation; either
       7              :  * version 2.1 of the License, or (at your option) any later version.
       8              :  *
       9              :  * This library is distributed in the hope that it will be useful,
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12              :  * Lesser General Public License for more details.
      13              :  *
      14              :  * You should have received a copy of the GNU Lesser General Public
      15              :  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
      16              :  *
      17              :  * Author: Vojtech Trefny <vtrefny@redhat.com>
      18              :  */
      19              : 
      20              : #include <glib.h>
      21              : #include <blockdev/utils.h>
      22              : #include <libdevmapper.h>
      23              : #include <yaml.h>
      24              : 
      25              : #include "vdo_stats.h"
      26              : #include "lvm.h"
      27              : 
      28              : 
      29              : G_GNUC_INTERNAL gboolean
      30           64 : get_stat_val64 (GHashTable *stats, const gchar *key, gint64 *val) {
      31              :     const gchar *s;
      32           64 :     gchar *endptr = NULL;
      33              : 
      34           64 :     s = g_hash_table_lookup (stats, key);
      35           64 :     if (s == NULL)
      36            4 :         return FALSE;
      37              : 
      38           60 :     *val = g_ascii_strtoll (s, &endptr, 0);
      39           60 :     if (endptr == NULL || *endptr != '\0')
      40            0 :         return FALSE;
      41              : 
      42           60 :     return TRUE;
      43              : }
      44              : 
      45              : G_GNUC_INTERNAL gboolean
      46           16 : get_stat_val64_default (GHashTable *stats, const gchar *key, gint64 *val, gint64 def) {
      47           16 :     if (!get_stat_val64 (stats, key, val))
      48            0 :         *val = def;
      49           16 :     return TRUE;
      50              : }
      51              : 
      52              : G_GNUC_INTERNAL gboolean
      53            2 : get_stat_val_double (GHashTable *stats, const gchar *key, gdouble *val) {
      54              :     const gchar *s;
      55            2 :     gchar *endptr = NULL;
      56              : 
      57            2 :     s = g_hash_table_lookup (stats, key);
      58            2 :     if (s == NULL)
      59            0 :         return FALSE;
      60              : 
      61            2 :     *val = g_ascii_strtod (s, &endptr);
      62            2 :     if (endptr == NULL || *endptr != '\0')
      63            0 :         return FALSE;
      64              : 
      65            2 :     return TRUE;
      66              : }
      67              : 
      68            4 : static void add_write_ampl_r_stats (GHashTable *stats) {
      69              :     gint64 bios_meta_write, bios_out_write, bios_in_write;
      70              : 
      71            8 :     if (! get_stat_val64 (stats, "biosMetaWrite", &bios_meta_write) ||
      72            8 :         ! get_stat_val64 (stats, "biosOutWrite", &bios_out_write) ||
      73            4 :         ! get_stat_val64 (stats, "biosInWrite", &bios_in_write))
      74            0 :         return;
      75              : 
      76            4 :     if (bios_in_write <= 0)
      77            4 :         g_hash_table_replace (stats, g_strdup ("writeAmplificationRatio"), g_strdup ("0.00"));
      78              :     else
      79            0 :         g_hash_table_replace (stats,
      80            0 :                               g_strdup ("writeAmplificationRatio"),
      81            0 :                               g_strdup_printf ("%.2f", (gfloat) (bios_meta_write + bios_out_write) / (gfloat) bios_in_write));
      82              : }
      83              : 
      84            4 : static void add_block_stats (GHashTable *stats) {
      85              :     gint64 physical_blocks, block_size, data_blocks_used, overhead_blocks_used, logical_blocks_used;
      86              :     gint64 savings;
      87              : 
      88            8 :     if (! get_stat_val64 (stats, "physicalBlocks", &physical_blocks) ||
      89            8 :         ! get_stat_val64 (stats, "blockSize", &block_size) ||
      90            8 :         ! get_stat_val64 (stats, "dataBlocksUsed", &data_blocks_used) ||
      91            8 :         ! get_stat_val64 (stats, "overheadBlocksUsed", &overhead_blocks_used) ||
      92            4 :         ! get_stat_val64 (stats, "logicalBlocksUsed", &logical_blocks_used))
      93            0 :         return;
      94              : 
      95            8 :     g_hash_table_replace (stats, g_strdup ("oneKBlocks"), g_strdup_printf ("%"G_GINT64_FORMAT, physical_blocks * block_size / 1024));
      96            8 :     g_hash_table_replace (stats, g_strdup ("oneKBlocksUsed"), g_strdup_printf ("%"G_GINT64_FORMAT, (data_blocks_used + overhead_blocks_used) * block_size / 1024));
      97            8 :     g_hash_table_replace (stats, g_strdup ("oneKBlocksAvailable"), g_strdup_printf ("%"G_GINT64_FORMAT, (physical_blocks - data_blocks_used - overhead_blocks_used) * block_size / 1024));
      98            8 :     g_hash_table_replace (stats, g_strdup ("usedPercent"), g_strdup_printf ("%.0f", 100.0 * (gfloat) (data_blocks_used + overhead_blocks_used) / (gfloat) physical_blocks + 0.5));
      99            4 :     savings = (logical_blocks_used > 0) ? (gint64) (100.0 * (gfloat) (logical_blocks_used - data_blocks_used) / (gfloat) logical_blocks_used) : 100;
     100            8 :     g_hash_table_replace (stats, g_strdup ("savings"), g_strdup_printf ("%"G_GINT64_FORMAT, savings));
     101            4 :     if (savings >= 0)
     102            8 :         g_hash_table_replace (stats, g_strdup ("savingPercent"), g_strdup_printf ("%"G_GINT64_FORMAT, savings));
     103              : }
     104              : 
     105            4 : static void add_journal_stats (GHashTable *stats) {
     106              :     gint64 journal_entries_committed, journal_entries_started, journal_entries_written;
     107              :     gint64 journal_blocks_committed, journal_blocks_started, journal_blocks_written;
     108              : 
     109            8 :     if (! get_stat_val64 (stats, "journalEntriesCommitted", &journal_entries_committed) ||
     110            8 :         ! get_stat_val64 (stats, "journalEntriesStarted", &journal_entries_started) ||
     111            8 :         ! get_stat_val64 (stats, "journalEntriesWritten", &journal_entries_written) ||
     112            4 :         ! get_stat_val64 (stats, "journalBlocksCommitted", &journal_blocks_committed) ||
     113            0 :         ! get_stat_val64 (stats, "journalBlocksStarted", &journal_blocks_started) ||
     114            0 :         ! get_stat_val64 (stats, "journalBlocksWritten", &journal_blocks_written))
     115            4 :         return;
     116              : 
     117            0 :     g_hash_table_replace (stats, g_strdup ("journalEntriesBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_started - journal_entries_written));
     118            0 :     g_hash_table_replace (stats, g_strdup ("journalEntriesWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_entries_written - journal_entries_committed));
     119            0 :     g_hash_table_replace (stats, g_strdup ("journalBlocksBatching"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_started - journal_blocks_written));
     120            0 :     g_hash_table_replace (stats, g_strdup ("journalBlocksWriting"), g_strdup_printf ("%"G_GINT64_FORMAT, journal_blocks_written - journal_blocks_committed));
     121              : }
     122              : 
     123            4 : static void add_computed_stats (GHashTable *stats) {
     124              :     const gchar *s;
     125              : 
     126            4 :     s = g_hash_table_lookup (stats, "logicalBlockSize");
     127            4 :     g_hash_table_replace (stats,
     128            4 :                           g_strdup ("fiveTwelveByteEmulation"),
     129            8 :                           g_strdup ((g_strcmp0 (s, "512") == 0) ? "true" : "false"));
     130              : 
     131            4 :     add_write_ampl_r_stats (stats);
     132            4 :     add_block_stats (stats);
     133            4 :     add_journal_stats (stats);
     134            4 : }
     135              : 
     136              : enum parse_flags {
     137              :   PARSE_NEXT_KEY,
     138              :   PARSE_NEXT_VAL,
     139              :   PARSE_NEXT_IGN,
     140              : };
     141              : 
     142              : G_GNUC_INTERNAL GHashTable *
     143            4 : vdo_get_stats_full (const gchar *name, GError **error) {
     144            4 :     struct dm_task *dmt = NULL;
     145            4 :     const gchar *response = NULL;
     146              :     yaml_parser_t parser;
     147              :     yaml_token_t token;
     148            4 :     GHashTable *stats = NULL;
     149            4 :     gchar *key = NULL;
     150            4 :     gsize len = 0;
     151            4 :     int next_token = PARSE_NEXT_IGN;
     152            4 :     gchar *prefix = NULL;
     153              : 
     154            4 :     dmt = dm_task_create (DM_DEVICE_TARGET_MSG);
     155            4 :     if (!dmt) {
     156            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     157              :                      "Failed to create DM task");
     158            0 :         return NULL;
     159              :     }
     160              : 
     161            4 :     if (!dm_task_set_name (dmt, name)) {
     162            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     163              :                      "Failed to set name for DM task");
     164            0 :         dm_task_destroy (dmt);
     165            0 :         return NULL;
     166              :     }
     167              : 
     168            4 :     if (!dm_task_set_message (dmt, "stats")) {
     169            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     170              :                      "Failed to set message for DM task");
     171            0 :         dm_task_destroy (dmt);
     172            0 :         return NULL;
     173              :     }
     174              : 
     175            4 :     if (!dm_task_run (dmt)) {
     176            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     177              :                      "Failed to run DM task");
     178            0 :         dm_task_destroy (dmt);
     179            0 :         return NULL;
     180              :     }
     181              : 
     182            4 :     response = dm_task_get_message_response (dmt);
     183            4 :     if (!response) {
     184            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     185              :                      "Failed to get response from the DM task");
     186            0 :         dm_task_destroy (dmt);
     187            0 :         return NULL;
     188              :     }
     189              : 
     190            4 :     if (!yaml_parser_initialize (&parser)) {
     191            0 :         g_set_error (error, BD_LVM_ERROR, BD_LVM_ERROR_DM_ERROR,
     192              :                      "Failed to get initialize YAML parser");
     193            0 :         dm_task_destroy (dmt);
     194            0 :         return NULL;
     195              :     }
     196              : 
     197            4 :     stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
     198            4 :     yaml_parser_set_input_string (&parser, (guchar *) response, strlen (response));
     199              : 
     200              :     do {
     201         3760 :         yaml_parser_scan (&parser, &token);
     202         3760 :         switch (token.type) {
     203              :             /* key */
     204          728 :             case YAML_KEY_TOKEN:
     205          728 :                 next_token = PARSE_NEXT_KEY;
     206          728 :                 break;
     207              :             /* value */
     208          728 :             case YAML_VALUE_TOKEN:
     209          728 :                 next_token = PARSE_NEXT_VAL;
     210          728 :                 break;
     211              :             /* block mapping */
     212            0 :             case YAML_BLOCK_MAPPING_START_TOKEN:
     213            0 :                 if (next_token == PARSE_NEXT_VAL)
     214              :                     /* we were expecting to read a key-value pair but this is actually
     215              :                        a block start, so we need to free the key we're not going to use */
     216            0 :                     g_free (key);
     217            0 :                 break;
     218              :             /* mapping */
     219          108 :             case YAML_FLOW_MAPPING_START_TOKEN:
     220              :                 /* start of flow mapping -> previously read key will be used as prefix
     221              :                    for all keys in the mapping:
     222              :                         previous key: biosInProgress
     223              :                         keys in the mapping: Read, Write...
     224              :                         with prefix: biosInProgressRead, biosInProgressWrite...
     225              :                 */
     226          108 :                 prefix = key;
     227          108 :                 break;
     228          108 :             case YAML_FLOW_MAPPING_END_TOKEN:
     229              :                 /* end of flow mapping, discard the prefix used */
     230          108 :                 g_free (prefix);
     231          108 :                 prefix = NULL;
     232          108 :                 break;
     233              :             /* actual data */
     234         1352 :             case YAML_SCALAR_TOKEN:
     235         1352 :                 if (next_token == PARSE_NEXT_KEY) {
     236          728 :                     if (prefix) {
     237          552 :                         key = g_strdup_printf ("%s%s", prefix, (const gchar *) token.data.scalar.value);
     238          552 :                         len = strlen (prefix);
     239              :                         /* make sure the key with the prefix is still camelCase */
     240          552 :                         key[len] = g_ascii_toupper (key[len]);
     241              :                     } else
     242          352 :                         key = g_strdup ((const gchar *) token.data.scalar.value);
     243          624 :                 } else if (next_token == PARSE_NEXT_VAL) {
     244          624 :                     gchar *val = g_strdup ((const gchar *) token.data.scalar.value);
     245          624 :                     g_hash_table_insert (stats, key, val);
     246              :                 }
     247         1352 :                 break;
     248          736 :             default:
     249          736 :                 break;
     250              :           }
     251              : 
     252         3760 :           if (token.type != YAML_STREAM_END_TOKEN)
     253         3756 :               yaml_token_delete (&token);
     254         3760 :     } while (token.type != YAML_STREAM_END_TOKEN);
     255              : 
     256            4 :     yaml_parser_delete (&parser);
     257            4 :     dm_task_destroy (dmt);
     258              : 
     259            4 :     if (stats != NULL)
     260            4 :         add_computed_stats (stats);
     261              : 
     262            4 :     return stats;
     263              : }
        

Generated by: LCOV version 2.0-1