cpufreq.chart.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. # -*- coding: utf-8 -*-
  2. # Description: cpufreq netdata python.d module
  3. # Author: Pawel Krupa (paulfantom)
  4. # Author: Steven Noonan (tycho)
  5. # SPDX-License-Identifier: GPL-3.0-or-later
  6. import glob
  7. import os
  8. from bases.FrameworkServices.SimpleService import SimpleService
  9. # default module values (can be overridden per job in `config`)
  10. # update_every = 2
  11. ORDER = ['cpufreq']
  12. CHARTS = {
  13. 'cpufreq': {
  14. 'options': [None, 'CPU Clock', 'MHz', 'cpufreq', 'cpufreq.cpufreq', 'line'],
  15. 'lines': [
  16. # lines are created dynamically in `check()` method
  17. ]
  18. }
  19. }
  20. class Service(SimpleService):
  21. def __init__(self, configuration=None, name=None):
  22. prefix = os.getenv('NETDATA_HOST_PREFIX', "")
  23. if prefix.endswith('/'):
  24. prefix = prefix[:-1]
  25. self.sys_dir = prefix + "/sys/devices"
  26. SimpleService.__init__(self, configuration=configuration, name=name)
  27. self.order = ORDER
  28. self.definitions = CHARTS
  29. self.fake_name = 'cpu'
  30. self.assignment = {}
  31. self.accurate_exists = True
  32. self.accurate_last = {}
  33. def _get_data(self):
  34. data = {}
  35. if self.accurate_exists:
  36. accurate_ok = True
  37. for name, paths in self.assignment.items():
  38. last = self.accurate_last[name]
  39. current = {}
  40. deltas = {}
  41. ticks_since_last = 0
  42. for line in open(paths['accurate'], 'r'):
  43. line = list(map(int, line.split()))
  44. current[line[0]] = line[1]
  45. ticks = line[1] - last.get(line[0], 0)
  46. ticks_since_last += ticks
  47. deltas[line[0]] = line[1] - last.get(line[0], 0)
  48. avg_freq = 0
  49. if ticks_since_last != 0:
  50. for frequency, ticks in deltas.items():
  51. avg_freq += frequency * ticks
  52. avg_freq /= ticks_since_last
  53. data[name] = avg_freq
  54. self.accurate_last[name] = current
  55. if avg_freq == 0 or ticks_since_last == 0:
  56. # Delta is either too large or nonexistent, fall back to
  57. # less accurate reading. This can happen if we switch
  58. # to/from the 'schedutil' governor, which doesn't report
  59. # stats.
  60. accurate_ok = False
  61. if accurate_ok:
  62. return data
  63. for name, paths in self.assignment.items():
  64. data[name] = open(paths['inaccurate'], 'r').read()
  65. return data
  66. def check(self):
  67. try:
  68. self.sys_dir = str(self.configuration['sys_dir'])
  69. except (KeyError, TypeError):
  70. self.error("No path specified. Using: '" + self.sys_dir + "'")
  71. for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/stats/time_in_state'):
  72. path_elem = path.split('/')
  73. cpu = path_elem[-4]
  74. if cpu not in self.assignment:
  75. self.assignment[cpu] = {}
  76. self.assignment[cpu]['accurate'] = path
  77. self.accurate_last[cpu] = {}
  78. if not self.assignment:
  79. self.accurate_exists = False
  80. for path in glob.glob(self.sys_dir + '/system/cpu/cpu*/cpufreq/scaling_cur_freq'):
  81. path_elem = path.split('/')
  82. cpu = path_elem[-3]
  83. if cpu not in self.assignment:
  84. self.assignment[cpu] = {}
  85. self.assignment[cpu]['inaccurate'] = path
  86. if not self.assignment:
  87. self.error("couldn't find a method to read cpufreq statistics")
  88. return False
  89. for name in sorted(self.assignment, key=lambda v: int(v[3:])):
  90. self.definitions[ORDER[0]]['lines'].append([name, name, 'absolute', 1, 1000])
  91. return True