log_system_usage.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #!/usr/bin/python3
  2. #
  3. import time
  4. import threading
  5. import pickle
  6. import gzip
  7. #
  8. PROC_STAT_HEADERS = ('user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq', 'steal', 'guest', 'guest_nice')
  9. #
  10. def get_cpu_stats(path='/proc/stat'):
  11. """
  12. Get CPU statistics from /proc/stat. Output is returned for the system and each CPU.
  13. Ex:
  14. {'system': {'user': 8696397, 'nice': 22431, ...},
  15. 'cpus': {
  16. 0: {'user': 4199206, 'nice': 11615, ...},
  17. 1: {'user': 4199308, 'nice': 10642, ...}
  18. }
  19. }
  20. """
  21. #
  22. with open(path, 'r') as f:
  23. lines = f.readlines()
  24. #
  25. cpu_lines = [l for l in lines if l.startswith('cpu')]
  26. stats = {'system':None, 'cpus':{}}
  27. #
  28. for l in cpu_lines:
  29. l_split = l.split()
  30. cpu_index = l_split[0][3:]
  31. cpu_stats = {x[0]: int(x[1]) for x in zip(PROC_STAT_HEADERS, l_split[1:])}
  32. #
  33. if cpu_index == '':
  34. stats['system'] = cpu_stats
  35. else:
  36. stats['cpus'][int(cpu_index)] = cpu_stats
  37. #
  38. #
  39. return stats
  40. #
  41. def calculate_cpu_usage(initial, current):
  42. """
  43. Calculation adapted from: https://stackoverflow.com/questions/23367857/accurate-calculation-of-cpu-usage-given-in-percentage-in-linux/
  44. """
  45. #
  46. initial_idle = initial['idle'] + initial['iowait']
  47. current_idle = current['idle'] + current['iowait']
  48. #
  49. initial_non_idle = initial['user'] + initial['nice'] + initial['system'] + initial['irq'] + initial['softirq'] + initial['steal']
  50. current_non_idle = current['user'] + current['nice'] + current['system'] + current['irq'] + current['softirq'] + current['steal']
  51. #
  52. initial_total = initial_idle + initial_non_idle
  53. current_total = current_idle + current_non_idle
  54. #
  55. return (current_non_idle-initial_non_idle)/(current_total-initial_total)
  56. #
  57. def calculate_cpu_usage_continuous(stats):
  58. cpu_usages = []
  59. for i in range(len(stats)-1):
  60. cpu_usages.append(calculate_cpu_usage(stats[i], stats[i+1]))
  61. #
  62. return cpu_usages
  63. #
  64. def log_cpu_stats(path, interval, stop_event):
  65. """
  66. Log the cpu stats to a gz compressed JSON file. Storing JSON
  67. seems to only use about 10% more disk space than storing
  68. bytes directly (4 bytes per value), so JSON is used for
  69. simplicity.
  70. path: file to save to
  71. interval: how many seconds to wait before getting more data
  72. stop_event: a threading.Event which stops the function
  73. """
  74. #
  75. stats = {'timestamps':[], 'stats':{'system':[], 'cpus':{}}}
  76. while not stop_event.is_set():
  77. stats['timestamps'].append(time.time())
  78. #stats['stats'].append(get_cpu_stats())
  79. current_stats = get_cpu_stats()
  80. stats['stats']['system'].append(current_stats['system'])
  81. for cpu in current_stats['cpus']:
  82. if cpu not in stats['stats']['cpus']:
  83. stats['stats']['cpus'][cpu] = []
  84. #
  85. stats['stats']['cpus'][cpu].append(current_stats['cpus'][cpu])
  86. #
  87. stop_event.wait(interval)
  88. #
  89. with gzip.GzipFile(path, 'wb') as f:
  90. pickle.dump(stats, f, protocol=4)
  91. #
  92. #
  93. def load_cpu_stats(path):
  94. with gzip.GzipFile(path, 'rb') as f:
  95. return pickle.load(f)
  96. #
  97. #
  98. '''
  99. def log_cpu_stats(path, interval, stop_event):
  100. path: file to save to
  101. interval: how many seconds to wait before getting more data
  102. stop_event: a threading.Event which stops the function
  103. #
  104. with gzip.GzipFile(path+'.2.gz', 'w') as f:
  105. f.write(' '.join(PROC_STAT_HEADERS).encode('utf-8'))
  106. f.write('\n\n'.encode('utf-8'))
  107. #
  108. while not stop_event.is_set():
  109. f.write(str(time.time()).encode('utf-8'))
  110. f.write('\n'.encode('utf-8'))
  111. stats = get_cpu_stats()
  112. f.write('cpu '.encode('utf-8'))
  113. #f.write(' '.join([str(stats['system'][x]) for x in PROC_STAT_HEADERS]).encode('utf-8'))
  114. f.write(b''.join([stats['system'][x].to_bytes(4, byteorder='big') for x in PROC_STAT_HEADERS]))
  115. f.write('\n'.encode('utf-8'))
  116. for cpu in stats['cpus']:
  117. f.write('cpu{} '.format(cpu).encode('utf-8'))
  118. #f.write(' '.join([str(stats['cpus'][cpu][x]) for x in PROC_STAT_HEADERS]).encode('utf-8'))
  119. f.write(b''.join([stats['cpus'][cpu][x].to_bytes(4, byteorder='big') for x in PROC_STAT_HEADERS]))
  120. f.write('\n'.encode('utf-8'))
  121. #
  122. f.write('\n'.encode('utf-8'))
  123. time.sleep(interval)
  124. #
  125. #
  126. #
  127. '''
  128. if __name__ == '__main__':
  129. stop_event = threading.Event()
  130. t = threading.Thread(target=log_cpu_stats, args=('/tmp/cpu_stats.pickle.gz', 0.5, stop_event))
  131. t.start()
  132. #
  133. try:
  134. while True:
  135. time.sleep(100)
  136. #
  137. except KeyboardInterrupt:
  138. stop_event.set()
  139. print()
  140. #
  141. t.join()
  142. #