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