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