MythTV  master
jitterometer.cpp
Go to the documentation of this file.
1 // MythTV
2 #include "mythlogging.h"
3 #include "jitterometer.h"
4 
5 // Std
6 #include <cmath>
7 #include <cstdlib>
8 #include <utility>
9 
10 #define UNIX_PROC_STAT "/proc/stat"
11 #define MAX_CORES 8
12 
13 #ifdef Q_OS_MACOS
14 #include <mach/mach_init.h>
15 #include <mach/mach_error.h>
16 #include <mach/mach_host.h>
17 #include <mach/vm_map.h>
18 #endif
19 
20 Jitterometer::Jitterometer(QString nname, int ncycles)
21  : m_numCycles(ncycles), m_name(std::move(nname))
22 {
23  m_times.resize(m_numCycles);
24 
25  if (m_name.isEmpty())
26  m_name = "Jitterometer";
27 
28 #if defined(__linux__) || defined(Q_OS_ANDROID)
29  // N.B. Access to /proc/stat was revoked on Android for API >=26 (Oreo)
30  if (QFile::exists(UNIX_PROC_STAT))
31  {
32  m_cpuStat = new QFile(UNIX_PROC_STAT);
33  if (m_cpuStat)
34  {
35  if (!m_cpuStat->open(QIODevice::ReadOnly))
36  {
37  delete m_cpuStat;
38  m_cpuStat = nullptr;
39  }
40  else
41  {
42  m_lastStats = new unsigned long long[MAX_CORES * 9];
43  memset(m_lastStats, 0, sizeof(unsigned long long) * MAX_CORES * 9);
44  }
45  }
46  }
47 #endif
48 
49 #ifdef Q_OS_MACOS
50  m_lastStats = new unsigned long long[MAX_CORES * 2];
51  memset(m_lastStats, 0, sizeof(unsigned long long) * MAX_CORES * 2);
52 #endif
53 }
54 
56 {
57  if (m_cpuStat)
58  m_cpuStat->close();
59  delete m_cpuStat;
60  delete [] m_lastStats;
61 }
62 
64 {
65  m_numCycles = cycles;
66  m_times.resize(m_numCycles);
67  m_count = 0;
68 }
69 
71 {
72  if (!m_numCycles)
73  return false;
74  bool ret = RecordEndTime();
76  return ret;
77 }
78 
80 {
81  if (!m_numCycles)
82  return false;
83 
84  int cycles = m_numCycles;
85  struct timeval timenow {};
86  gettimeofday(&timenow, nullptr);
87 
88  if (m_starttimeValid)
89  {
90  m_times[m_count] = (timenow.tv_sec - m_starttime.tv_sec ) * 1000000 +
91  (timenow.tv_usec - m_starttime.tv_usec) ;
92  m_count++;
93  }
94 
95  m_starttimeValid = false;
96 
97  if (m_count >= cycles)
98  {
99  /* compute and display stuff, reset count to -1 */
100  double mean = 0;
101  double sum_of_squared_deviations=0;
102 
103  /* compute the mean */
104  for (int i = 0; i < cycles; i++)
105  mean += m_times[i];
106 
107  double tottime = mean;
108  mean /= cycles;
109 
110  if (tottime > 0)
111  m_lastFps = cycles / tottime * 1000000;
112 
113  /* compute the sum of the squares of each deviation from the mean */
114  for (int i = 0; i < cycles; i++)
115  sum_of_squared_deviations += (mean - m_times[i]) * (mean - m_times[i]);
116 
117  /* compute standard deviation */
118  double standard_deviation = sqrt(sum_of_squared_deviations / (cycles - 1));
119  if (mean > 0)
120  m_lastSd = standard_deviation / mean;
121 
122  /* retrieve load if available */
123  QString extra;
125  if (!m_lastCpuStats.isEmpty())
126  extra = QString("CPUs: ") + m_lastCpuStats;
127 
128  LOG(VB_GENERAL, LOG_INFO,
129  m_name + QString("FPS: %1 Mean: %2 Std.Dev: %3 ")
130  .arg(m_lastFps, 7, 'f', 2).arg((int)mean, 5)
131  .arg((int)standard_deviation, 5) + extra);
132 
133  m_count = 0;
134  return true;
135  }
136  return false;
137 }
138 
140 {
141  if (!m_numCycles)
142  return;
143  gettimeofday(&m_starttime, nullptr);
144  m_starttimeValid = true;
145 }
146 
148 {
149  QString result = "N/A";
150 
151 #if defined(__linux__) || defined(Q_OS_ANDROID)
152  if (m_cpuStat)
153  {
154  m_cpuStat->seek(0);
155  m_cpuStat->flush();
156 
157  QByteArray line = m_cpuStat->readLine(256);
158  if (line.isEmpty())
159  return result;
160 
161  result = "";
162  int cores = 0;
163  int ptr = 0;
164  line = m_cpuStat->readLine(256);
165  while (!line.isEmpty() && cores < MAX_CORES)
166  {
167  static constexpr int kSize = sizeof(unsigned long long) * 9;
168  unsigned long long stats[9];
169  memset(stats, 0, kSize);
170  int num = 0;
171  if (sscanf(line.constData(),
172  "cpu%30d %30llu %30llu %30llu %30llu %30llu "
173  "%30llu %30llu %30llu %30llu %*5000s\n",
174  &num, &stats[0], &stats[1], &stats[2], &stats[3],
175  &stats[4], &stats[5], &stats[6], &stats[7], &stats[8]) >= 4)
176  {
177  float load = stats[0] + stats[1] + stats[2] + stats[4] +
178  stats[5] + stats[6] + stats[7] + stats[8] -
179  m_lastStats[ptr + 0] - m_lastStats[ptr + 1] -
180  m_lastStats[ptr + 2] - m_lastStats[ptr + 4] -
181  m_lastStats[ptr + 5] - m_lastStats[ptr + 6] -
182  m_lastStats[ptr + 7] - m_lastStats[ptr + 8];
183  float total = load + stats[3] - m_lastStats[ptr + 3];
184  if (total > 0)
185  result += QString("%1% ").arg(load / total * 100, 0, 'f', 0);
186  memcpy(&m_lastStats[ptr], stats, kSize);
187  }
188  line = m_cpuStat->readLine(256);
189  cores++;
190  ptr += 9;
191  }
192  }
193 #endif
194 
195 #ifdef Q_OS_MACOS
196  processor_cpu_load_info_t load;
197  mach_msg_type_number_t msgcount;
198  natural_t processorcount;
199 
200  if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &processorcount, (processor_info_array_t *)&load, &msgcount) == KERN_SUCCESS)
201  {
202  result = "";
203  int ptr = 0;
204  for (natural_t i = 0; i < processorcount && i < MAX_CORES; i++)
205  {
206  unsigned long long stats[9];
207  memset(stats, 0, sizeof(unsigned long long) * 2);
208  stats[0] = load[i].cpu_ticks[CPU_STATE_IDLE];
209  stats[1] = load[i].cpu_ticks[CPU_STATE_IDLE] + load[i].cpu_ticks[CPU_STATE_USER] +
210  load[i].cpu_ticks[CPU_STATE_SYSTEM] + load[i].cpu_ticks[CPU_STATE_NICE];
211  double idledelta = stats[0] - m_lastStats[ptr];
212  double totaldelta = stats[1] - m_lastStats[ptr + 1];
213  if (totaldelta > 0)
214  result += QString("%1% ").arg(((totaldelta - idledelta) / totaldelta) * 100.0, 0, 'f', 0);
215  memcpy(&m_lastStats[ptr], stats, sizeof(unsigned long long) * 2);
216  ptr += 2;
217  }
218  }
219 #endif
220  return result;
221 }
bool RecordEndTime()
Jitterometer(QString nname, int ncycles=0)
QFile * m_cpuStat
Definition: jitterometer.h:71
#define UNIX_PROC_STAT
QVector< uint > m_times
Definition: jitterometer.h:67
QString m_name
Definition: jitterometer.h:70
float m_lastSd
Definition: jitterometer.h:69
QString m_lastCpuStats
Definition: jitterometer.h:73
void SetNumCycles(int cycles)
bool m_starttimeValid
Definition: jitterometer.h:66
bool RecordCycleTime()
#define MAX_CORES
float m_lastFps
Definition: jitterometer.h:68
QString GetCPUStat(void)
const char * m_name
Definition: ParseText.cpp:329
void RecordStartTime()
unsigned long long * m_lastStats
Definition: jitterometer.h:72
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23