Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/tools/perf/util/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 19 kB image not shown  

Quelle  hwmon_pmu.c   Sprache: C

 
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
#include "counts.h"
#include "debug.h"
#include "evsel.h"
#include "hashmap.h"
#include "hwmon_pmu.h"
#include "pmu.h"
#include <internal/xyarray.h>
#include <internal/threadmap.h>
#include <perf/threadmap.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <api/fs/fs.h>
#include <api/io.h>
#include <api/io_dir.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/zalloc.h>

/** Strings that correspond to enum hwmon_type. */
static const char * const hwmon_type_strs[HWMON_TYPE_MAX] = {
 NULL,
 "cpu",
 "curr",
 "energy",
 "fan",
 "humidity",
 "in",
 "intrusion",
 "power",
 "pwm",
 "temp",
};
#define LONGEST_HWMON_TYPE_STR "intrusion"

/** Strings that correspond to enum hwmon_item. */
static const char * const hwmon_item_strs[HWMON_ITEM__MAX] = {
 NULL,
 "accuracy",
 "alarm",
 "auto_channels_temp",
 "average",
 "average_highest",
 "average_interval",
 "average_interval_max",
 "average_interval_min",
 "average_lowest",
 "average_max",
 "average_min",
 "beep",
 "cap",
 "cap_hyst",
 "cap_max",
 "cap_min",
 "crit",
 "crit_hyst",
 "div",
 "emergency",
 "emergency_hist",
 "enable",
 "fault",
 "freq",
 "highest",
 "input",
 "label",
 "lcrit",
 "lcrit_hyst",
 "lowest",
 "max",
 "max_hyst",
 "min",
 "min_hyst",
 "mod",
 "offset",
 "pulses",
 "rated_max",
 "rated_min",
 "reset_history",
 "target",
 "type",
 "vid",
};
#define LONGEST_HWMON_ITEM_STR "average_interval_max"

static const char *const hwmon_units[HWMON_TYPE_MAX] = {
 NULL,
 "V",   /* cpu */
 "A",   /* curr */
 "J",   /* energy */
 "rpm"/* fan */
 "%",   /* humidity */
 "V",   /* in */
 "",    /* intrusion */
 "W",   /* power */
 "Hz",  /* pwm */
 "'C",  /* temp */
};

struct hwmon_pmu {
 struct perf_pmu pmu;
 struct hashmap events;
 char *hwmon_dir;
};

/**
 * struct hwmon_pmu_event_value: Value in hwmon_pmu->events.
 *
 * Hwmon files are of the form <type><number>_<item> and may have a suffix
 * _alarm.
 */

struct hwmon_pmu_event_value {
 /** @items: which item files are present. */
 DECLARE_BITMAP(items, HWMON_ITEM__MAX);
 /** @alarm_items: which item files are present. */
 DECLARE_BITMAP(alarm_items, HWMON_ITEM__MAX);
 /** @label: contents of <type><number>_label if present. */
 char *label;
 /** @name: name computed from label of the form <type>_<label>. */
 char *name;
};

bool perf_pmu__is_hwmon(const struct perf_pmu *pmu)
{
 return pmu && pmu->type >= PERF_PMU_TYPE_HWMON_START &&
  pmu->type <= PERF_PMU_TYPE_HWMON_END;
}

bool evsel__is_hwmon(const struct evsel *evsel)
{
 return perf_pmu__is_hwmon(evsel->pmu);
}

static size_t hwmon_pmu__event_hashmap_hash(long key, void *ctx __maybe_unused)
{
 return ((union hwmon_pmu_event_key)key).type_and_num;
}

static bool hwmon_pmu__event_hashmap_equal(long key1, long key2, void *ctx __maybe_unused)
{
 return ((union hwmon_pmu_event_key)key1).type_and_num ==
        ((union hwmon_pmu_event_key)key2).type_and_num;
}

static int hwmon_strcmp(const void *a, const void *b)
{
 const char *sa = a;
 const char * const *sb = b;

 return strcmp(sa, *sb);
}

bool parse_hwmon_filename(const char *filename,
     enum hwmon_type *type,
     int *number,
     enum hwmon_item *item,
     bool *alarm)
{
 char fn_type[24];
 const char **elem;
 const char *fn_item = NULL;
 size_t fn_item_len;

 assert(strlen(LONGEST_HWMON_TYPE_STR) < sizeof(fn_type));
 strlcpy(fn_type, filename, sizeof(fn_type));
 for (size_t i = 0; fn_type[i] != '\0'; i++) {
  if (fn_type[i] >= '0' && fn_type[i] <= '9') {
   fn_type[i] = '\0';
   *number = strtoul(&filename[i], (char **)&fn_item, 10);
   if (*fn_item == '_')
    fn_item++;
   break;
  }
  if (fn_type[i] == '_') {
   fn_type[i] = '\0';
   *number = -1;
   fn_item = &filename[i + 1];
   break;
  }
 }
 if (fn_item == NULL || fn_type[0] == '\0' || (item != NULL && fn_item[0] == '\0')) {
  pr_debug3("hwmon_pmu: not a hwmon file '%s'\n", filename);
  return false;
 }
 elem = bsearch(&fn_type, hwmon_type_strs + 1, ARRAY_SIZE(hwmon_type_strs) - 1,
         sizeof(hwmon_type_strs[0]), hwmon_strcmp);
 if (!elem) {
  pr_debug3("hwmon_pmu: not a hwmon type '%s' in file name '%s'\n",
    fn_type, filename);
  return false;
 }

 *type = elem - &hwmon_type_strs[0];
 if (!item)
  return true;

 *alarm = false;
 fn_item_len = strlen(fn_item);
 if (fn_item_len > 6 && !strcmp(&fn_item[fn_item_len - 6], "_alarm")) {
  assert(strlen(LONGEST_HWMON_ITEM_STR) < sizeof(fn_type));
  strlcpy(fn_type, fn_item, fn_item_len - 5);
  fn_item = fn_type;
  *alarm = true;
 }
 elem = bsearch(fn_item, hwmon_item_strs + 1, ARRAY_SIZE(hwmon_item_strs) - 1,
         sizeof(hwmon_item_strs[0]), hwmon_strcmp);
 if (!elem) {
  pr_debug3("hwmon_pmu: not a hwmon item '%s' in file name '%s'\n",
    fn_item, filename);
  return false;
 }
 *item = elem - &hwmon_item_strs[0];
 return true;
}

static void fix_name(char *p)
{
 char *s = strchr(p, '\n');

 if (s)
  *s = '\0';

 while (*p != '\0') {
  if (strchr(" :,/\n\t", *p))
   *p = '_';
  else
   *p = tolower(*p);
  p++;
 }
}

static int hwmon_pmu__read_events(struct hwmon_pmu *pmu)
{
 int err = 0;
 struct hashmap_entry *cur, *tmp;
 size_t bkt;
 struct io_dirent64 *ent;
 struct io_dir dir;

 if (pmu->pmu.sysfs_aliases_loaded)
  return 0;

 /* Use openat so that the directory contents are refreshed. */
 io_dir__init(&dir, open(pmu->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY));

 if (dir.dirfd < 0)
  return -ENOENT;

 while ((ent = io_dir__readdir(&dir)) != NULL) {
  enum hwmon_type type;
  int number;
  enum hwmon_item item;
  bool alarm;
  union hwmon_pmu_event_key key = { .type_and_num = 0 };
  struct hwmon_pmu_event_value *value;

  if (ent->d_type != DT_REG)
   continue;

  if (!parse_hwmon_filename(ent->d_name, &type, &number, &item, &alarm)) {
   pr_debug3("Not a hwmon file '%s'\n", ent->d_name);
   continue;
  }
  key.num = number;
  key.type = type;
  if (!hashmap__find(&pmu->events, key.type_and_num, &value)) {
   value = zalloc(sizeof(*value));
   if (!value) {
    err = -ENOMEM;
    goto err_out;
   }
   err = hashmap__add(&pmu->events, key.type_and_num, value);
   if (err) {
    free(value);
    err = -ENOMEM;
    goto err_out;
   }
  }
  __set_bit(item, alarm ? value->alarm_items : value->items);
  if (item == HWMON_ITEM_LABEL) {
   char buf[128];
   int fd = openat(dir.dirfd, ent->d_name, O_RDONLY);
   ssize_t read_len;

   if (fd < 0)
    continue;

   read_len = read(fd, buf, sizeof(buf));

   while (read_len > 0 && buf[read_len - 1] == '\n')
    read_len--;

   if (read_len > 0)
    buf[read_len] = '\0';

   if (buf[0] == '\0') {
    pr_debug("hwmon_pmu: empty label file %s %s\n",
      pmu->pmu.name, ent->d_name);
    close(fd);
    continue;
   }
   value->label = strdup(buf);
   if (!value->label) {
    pr_debug("hwmon_pmu: memory allocation failure\n");
    close(fd);
    continue;
   }
   snprintf(buf, sizeof(buf), "%s_%s", hwmon_type_strs[type], value->label);
   fix_name(buf);
   value->name = strdup(buf);
   if (!value->name)
    pr_debug("hwmon_pmu: memory allocation failure\n");
   close(fd);
  }
 }
 if (hashmap__size(&pmu->events) == 0)
  pr_debug2("hwmon_pmu: %s has no events\n", pmu->pmu.name);

 hashmap__for_each_entry_safe((&pmu->events), cur, tmp, bkt) {
  union hwmon_pmu_event_key key = {
   .type_and_num = cur->key,
  };
  struct hwmon_pmu_event_value *value = cur->pvalue;

  if (!test_bit(HWMON_ITEM_INPUT, value->items)) {
   pr_debug("hwmon_pmu: %s removing event '%s%d' that has no input file\n",
    pmu->pmu.name, hwmon_type_strs[key.type], key.num);
   hashmap__delete(&pmu->events, key.type_and_num, &key, &value);
   zfree(&value->label);
   zfree(&value->name);
   free(value);
  }
 }
 pmu->pmu.sysfs_aliases_loaded = true;

err_out:
 close(dir.dirfd);
 return err;
}

struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, const char *hwmon_dir,
    const char *sysfs_name, const char *name)
{
 char buf[64];
 struct hwmon_pmu *hwm;
 __u32 type = PERF_PMU_TYPE_HWMON_START + strtoul(sysfs_name + 5, NULL, 10);

 if (type > PERF_PMU_TYPE_HWMON_END) {
  pr_err("Unable to encode hwmon type from %s in valid PMU type\n", sysfs_name);
  return NULL;
 }

 snprintf(buf, sizeof(buf), "hwmon_%s", name);
 fix_name(buf + 6);

 hwm = zalloc(sizeof(*hwm));
 if (!hwm)
  return NULL;

 if (perf_pmu__init(&hwm->pmu, type, buf) != 0) {
  perf_pmu__delete(&hwm->pmu);
  return NULL;
 }

 hwm->hwmon_dir = strdup(hwmon_dir);
 if (!hwm->hwmon_dir) {
  perf_pmu__delete(&hwm->pmu);
  return NULL;
 }
 hwm->pmu.alias_name = strdup(sysfs_name);
 if (!hwm->pmu.alias_name) {
  perf_pmu__delete(&hwm->pmu);
  return NULL;
 }
 hwm->pmu.cpus = perf_cpu_map__new("0");
 if (!hwm->pmu.cpus) {
  perf_pmu__delete(&hwm->pmu);
  return NULL;
 }
 INIT_LIST_HEAD(&hwm->pmu.format);
 INIT_LIST_HEAD(&hwm->pmu.caps);
 hashmap__init(&hwm->events, hwmon_pmu__event_hashmap_hash,
        hwmon_pmu__event_hashmap_equal, /*ctx=*/NULL);

 list_add_tail(&hwm->pmu.list, pmus);
 return &hwm->pmu;
}

void hwmon_pmu__exit(struct perf_pmu *pmu)
{
 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
 struct hashmap_entry *cur, *tmp;
 size_t bkt;

 hashmap__for_each_entry_safe((&hwm->events), cur, tmp, bkt) {
  struct hwmon_pmu_event_value *value = cur->pvalue;

  zfree(&value->label);
  zfree(&value->name);
  free(value);
 }
 hashmap__clear(&hwm->events);
 zfree(&hwm->hwmon_dir);
}

static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, size_t out_buf_len,
     union hwmon_pmu_event_key key,
     const unsigned long *items, bool is_alarm)
{
 size_t bit;
 char buf[64];
 size_t len = 0;
 int dir = open(hwm->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY);

 if (dir < 0)
  return 0;

 for_each_set_bit(bit, items, HWMON_ITEM__MAX) {
  int fd;

  if (bit == HWMON_ITEM_LABEL || bit == HWMON_ITEM_INPUT)
   continue;

  snprintf(buf, sizeof(buf), "%s%d_%s%s",
   hwmon_type_strs[key.type],
   key.num,
   hwmon_item_strs[bit],
   is_alarm ? "_alarm" : "");
  fd = openat(dir, buf, O_RDONLY);
  if (fd > 0) {
   ssize_t read_len = read(fd, buf, sizeof(buf));

   while (read_len > 0 && buf[read_len - 1] == '\n')
    read_len--;

   if (read_len > 0) {
    long long val;

    buf[read_len] = '\0';
    val = strtoll(buf, /*endptr=*/NULL, 10);
    len += snprintf(out_buf + len, out_buf_len - len, "%s%s%s=%g%s",
      len == 0 ? " " : ", ",
      hwmon_item_strs[bit],
      is_alarm ? "_alarm" : "",
      (double)val / 1000.0,
      hwmon_units[key.type]);
   }
   close(fd);
  }
 }
 close(dir);
 return len;
}

int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
{
 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
 struct hashmap_entry *cur;
 size_t bkt;

 if (hwmon_pmu__read_events(hwm))
  return false;

 hashmap__for_each_entry((&hwm->events), cur, bkt) {
  static const char *const hwmon_scale_units[HWMON_TYPE_MAX] = {
   NULL,
   "0.001V"/* cpu */
   "0.001A"/* curr */
   "0.001J"/* energy */
   "1rpm",   /* fan */
   "0.001%"/* humidity */
   "0.001V"/* in */
   NULL,     /* intrusion */
   "0.001W"/* power */
   "1Hz",    /* pwm */
   "0.001'C"/* temp */
  };
  static const char *const hwmon_desc[HWMON_TYPE_MAX] = {
   NULL,
   "CPU core reference voltage",   /* cpu */
   "Current",                      /* curr */
   "Cumulative energy use",        /* energy */
   "Fan",                          /* fan */
   "Humidity",                     /* humidity */
   "Voltage",                      /* in */
   "Chassis intrusion detection",  /* intrusion */
   "Power use",                    /* power */
   "Pulse width modulation fan control"/* pwm */
   "Temperature",                  /* temp */
  };
  char alias_buf[64];
  char desc_buf[256];
  char encoding_buf[128];
  union hwmon_pmu_event_key key = {
   .type_and_num = cur->key,
  };
  struct hwmon_pmu_event_value *value = cur->pvalue;
  struct pmu_event_info info = {
   .pmu = pmu,
   .name = value->name,
   .alias = alias_buf,
   .scale_unit = hwmon_scale_units[key.type],
   .desc = desc_buf,
   .long_desc = NULL,
   .encoding_desc = encoding_buf,
   .topic = "hwmon",
   .pmu_name = pmu->name,
   .event_type_desc = "Hwmon event",
  };
  int ret;
  size_t len;

  len = snprintf(alias_buf, sizeof(alias_buf), "%s%d",
          hwmon_type_strs[key.type], key.num);
  if (!info.name) {
   info.name = info.alias;
   info.alias = NULL;
  }

  len = snprintf(desc_buf, sizeof(desc_buf), "%s in unit %s named %s.",
   hwmon_desc[key.type],
   pmu->name + 6,
   value->label ?: info.name);

  len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len,
      key, value->items, /*is_alarm=*/false);

  len += hwmon_pmu__describe_items(hwm, desc_buf + len, sizeof(desc_buf) - len,
      key, value->alarm_items, /*is_alarm=*/true);

  snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%lx/",
    pmu->name, cur->key);

  ret = cb(state, &info);
  if (ret)
   return ret;
 }
 return 0;
}

size_t hwmon_pmu__num_events(struct perf_pmu *pmu)
{
 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);

 hwmon_pmu__read_events(hwm);
 return hashmap__size(&hwm->events);
}

bool hwmon_pmu__have_event(struct perf_pmu *pmu, const char *name)
{
 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
 enum hwmon_type type;
 int number;
 union hwmon_pmu_event_key key = { .type_and_num = 0 };
 struct hashmap_entry *cur;
 size_t bkt;

 if (!parse_hwmon_filename(name, &type, &number, /*item=*/NULL, /*is_alarm=*/NULL))
  return false;

 if (hwmon_pmu__read_events(hwm))
  return false;

 key.type = type;
 key.num = number;
 if (hashmap_find(&hwm->events, key.type_and_num, /*value=*/NULL))
  return true;
 if (key.num != -1)
  return false;
 /* Item is of form <type>_ which means we should match <type>_<label>. */
 hashmap__for_each_entry((&hwm->events), cur, bkt) {
  struct hwmon_pmu_event_value *value = cur->pvalue;

  key.type_and_num = cur->key;
  if (key.type == type && value->name && !strcasecmp(name, value->name))
   return true;
 }
 return false;
}

static int hwmon_pmu__config_term(const struct hwmon_pmu *hwm,
      struct perf_event_attr *attr,
      struct parse_events_term *term,
      struct parse_events_error *err)
{
 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
  enum hwmon_type type;
  int number;

  if (parse_hwmon_filename(term->config, &type, &number,
      /*item=*/NULL, /*is_alarm=*/NULL)) {
   if (number == -1) {
    /*
 * Item is of form <type>_ which means we should
 * match <type>_<label>.
 */

    struct hashmap_entry *cur;
    size_t bkt;

    attr->config = 0;
    hashmap__for_each_entry((&hwm->events), cur, bkt) {
     union hwmon_pmu_event_key key = {
      .type_and_num = cur->key,
     };
     struct hwmon_pmu_event_value *value = cur->pvalue;

     if (key.type == type && value->name &&
         !strcasecmp(term->config, value->name)) {
      attr->config = key.type_and_num;
      break;
     }
    }
    if (attr->config == 0)
     return -EINVAL;
   } else {
    union hwmon_pmu_event_key key = {
     .type_and_num = 0,
    };

    key.type = type;
    key.num = number;
    attr->config = key.type_and_num;
   }
   return 0;
  }
 }
 if (err) {
  char *err_str;

  parse_events_error__handle(err, term->err_val,
     asprintf(&err_str,
      "unexpected hwmon event term (%s) %s",
      parse_events__term_type_str(term->type_term),
      term->config) < 0
     ? strdup("unexpected hwmon event term")
     : err_str,
     NULL);
 }
 return -EINVAL;
}

int hwmon_pmu__config_terms(const struct perf_pmu *pmu,
       struct perf_event_attr *attr,
       struct parse_events_terms *terms,
       struct parse_events_error *err)
{
 struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
 struct parse_events_term *term;
 int ret;

 ret = hwmon_pmu__read_events(hwm);
 if (ret)
  return ret;

 list_for_each_entry(term, &terms->terms, list) {
  if (hwmon_pmu__config_term(hwm, attr, term, err))
   return -EINVAL;
 }

 return 0;

}

int hwmon_pmu__check_alias(struct parse_events_terms *terms, struct perf_pmu_info *info,
      struct parse_events_error *err)
{
 struct parse_events_term *term =
  list_first_entry(&terms->terms, struct parse_events_term, list);

 if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER) {
  enum hwmon_type type;
  int number;

  if (parse_hwmon_filename(term->config, &type, &number,
      /*item=*/NULL, /*is_alarm=*/NULL)) {
   info->unit = hwmon_units[type];
   if (type == HWMON_TYPE_FAN || type == HWMON_TYPE_PWM ||
       type == HWMON_TYPE_INTRUSION)
    info->scale = 1;
   else
    info->scale = 0.001;
  }
  return 0;
 }
 if (err) {
  char *err_str;

  parse_events_error__handle(err, term->err_val,
     asprintf(&err_str,
      "unexpected hwmon event term (%s) %s",
      parse_events__term_type_str(term->type_term),
      term->config) < 0
     ? strdup("unexpected hwmon event term")
     : err_str,
     NULL);
 }
 return -EINVAL;
}

int perf_pmus__read_hwmon_pmus(struct list_head *pmus)
{
 char *line = NULL;
 struct io_dirent64 *class_hwmon_ent;
 struct io_dir class_hwmon_dir;
 char buf[PATH_MAX];
 const char *sysfs = sysfs__mountpoint();

 if (!sysfs)
  return 0;

 scnprintf(buf, sizeof(buf), "%s/class/hwmon/", sysfs);
 io_dir__init(&class_hwmon_dir, open(buf, O_CLOEXEC | O_DIRECTORY | O_RDONLY));

 if (class_hwmon_dir.dirfd < 0)
  return 0;

 while ((class_hwmon_ent = io_dir__readdir(&class_hwmon_dir)) != NULL) {
  size_t line_len;
  int hwmon_dir, name_fd;
  struct io io;
  char buf2[128];

  if (class_hwmon_ent->d_type != DT_LNK)
   continue;

  scnprintf(buf, sizeof(buf), "%s/class/hwmon/%s", sysfs, class_hwmon_ent->d_name);
  hwmon_dir = open(buf, O_DIRECTORY);
  if (hwmon_dir == -1) {
   pr_debug("hwmon_pmu: not a directory: '%s/class/hwmon/%s'\n",
     sysfs, class_hwmon_ent->d_name);
   continue;
  }
  name_fd = openat(hwmon_dir, "name", O_RDONLY);
  if (name_fd == -1) {
   pr_debug("hwmon_pmu: failure to open '%s/class/hwmon/%s/name'\n",
      sysfs, class_hwmon_ent->d_name);
   close(hwmon_dir);
   continue;
  }
  io__init(&io, name_fd, buf2, sizeof(buf2));
  io__getline(&io, &line, &line_len);
  if (line_len > 0 && line[line_len - 1] == '\n')
   line[line_len - 1] = '\0';
  hwmon_pmu__new(pmus, buf, class_hwmon_ent->d_name, line);
  close(name_fd);
  close(hwmon_dir);
 }
 free(line);
 close(class_hwmon_dir.dirfd);
 return 0;
}

#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))

int evsel__hwmon_pmu_open(struct evsel *evsel,
     struct perf_thread_map *threads,
     int start_cpu_map_idx, int end_cpu_map_idx)
{
 struct hwmon_pmu *hwm = container_of(evsel->pmu, struct hwmon_pmu, pmu);
 union hwmon_pmu_event_key key = {
  .type_and_num = evsel->core.attr.config,
 };
 int idx = 0, thread = 0, nthreads, err = 0;
 int dir = open(hwm->hwmon_dir, O_CLOEXEC | O_DIRECTORY | O_RDONLY);

 if (dir < 0)
  return -errno;

 nthreads = perf_thread_map__nr(threads);
 for (idx = start_cpu_map_idx; idx < end_cpu_map_idx; idx++) {
  for (thread = 0; thread < nthreads; thread++) {
   char buf[64];
   int fd;

   snprintf(buf, sizeof(buf), "%s%d_input",
     hwmon_type_strs[key.type], key.num);

   fd = openat(dir, buf, O_RDONLY);
   FD(evsel, idx, thread) = fd;
   if (fd < 0) {
    err = -errno;
    goto out_close;
   }
  }
 }
 close(dir);
 return 0;
out_close:
 if (err)
  threads->err_thread = thread;

 do {
  while (--thread >= 0) {
   if (FD(evsel, idx, thread) >= 0)
    close(FD(evsel, idx, thread));
   FD(evsel, idx, thread) = -1;
  }
  thread = nthreads;
 } while (--idx >= 0);
 close(dir);
 return err;
}

int evsel__hwmon_pmu_read(struct evsel *evsel, int cpu_map_idx, int thread)
{
 char buf[32];
 int fd;
 ssize_t len;
 struct perf_counts_values *count, *old_count = NULL;

 if (evsel->prev_raw_counts)
  old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread);

 count = perf_counts(evsel->counts, cpu_map_idx, thread);
 fd = FD(evsel, cpu_map_idx, thread);
 len = pread(fd, buf, sizeof(buf), 0);
 if (len <= 0) {
  count->lost++;
  return -EINVAL;
 }
 buf[len] = '\0';
 if (old_count) {
  count->val = old_count->val + strtoll(buf, NULL, 10);
  count->run = old_count->run + 1;
  count->ena = old_count->ena + 1;
 } else {
  count->val = strtoll(buf, NULL, 10);
  count->run++;
  count->ena++;
 }
 return 0;
}

Messung V0.5
C=92 H=97 G=94

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.