win32pdhutil.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. """Utilities for the win32 Performance Data Helper module
  2. Example:
  3. To get a single bit of data:
  4. >>> import win32pdhutil
  5. >>> win32pdhutil.GetPerformanceAttributes("Memory", "Available Bytes")
  6. 6053888
  7. >>> win32pdhutil.FindPerformanceAttributesByName("python", counter="Virtual Bytes")
  8. [22278144]
  9. First example returns data which is not associated with any specific instance.
  10. The second example reads data for a specific instance - hence the list return -
  11. it would return one result for each instance of Python running.
  12. In general, it can be tricky finding exactly the "name" of the data you wish to query.
  13. Although you can use <om win32pdh.EnumObjectItems>(None,None,(eg)"Memory", -1) to do this,
  14. the easiest way is often to simply use PerfMon to find out the names.
  15. """
  16. import win32pdh, time
  17. error = win32pdh.error
  18. # Handle some localization issues.
  19. # see http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/Q287/1/59.asp&NoWebContent=1
  20. # Build a map of english_counter_name: counter_id
  21. counter_english_map = {}
  22. def find_pdh_counter_localized_name(english_name, machine_name = None):
  23. if not counter_english_map:
  24. import win32api, win32con
  25. counter_reg_value = win32api.RegQueryValueEx(win32con.HKEY_PERFORMANCE_DATA,
  26. "Counter 009")
  27. counter_list = counter_reg_value[0]
  28. for i in range(0, len(counter_list) - 1, 2):
  29. try:
  30. counter_id = int(counter_list[i])
  31. except ValueError:
  32. continue
  33. counter_english_map[counter_list[i+1].lower()] = counter_id
  34. return win32pdh.LookupPerfNameByIndex(machine_name, counter_english_map[english_name.lower()])
  35. def GetPerformanceAttributes(object, counter, instance = None, inum=-1,
  36. format = win32pdh.PDH_FMT_LONG, machine=None):
  37. # NOTE: Many counters require 2 samples to give accurate results,
  38. # including "% Processor Time" (as by definition, at any instant, a
  39. # thread's CPU usage is either 0 or 100). To read counters like this,
  40. # you should copy this function, but keep the counter open, and call
  41. # CollectQueryData() each time you need to know.
  42. # See http://support.microsoft.com/default.aspx?scid=kb;EN-US;q262938
  43. # and http://msdn.microsoft.com/library/en-us/dnperfmo/html/perfmonpt2.asp
  44. # My older explanation for this was that the "AddCounter" process forced
  45. # the CPU to 100%, but the above makes more sense :)
  46. path = win32pdh.MakeCounterPath( (machine,object,instance, None, inum,counter) )
  47. hq = win32pdh.OpenQuery()
  48. try:
  49. hc = win32pdh.AddCounter(hq, path)
  50. try:
  51. win32pdh.CollectQueryData(hq)
  52. type, val = win32pdh.GetFormattedCounterValue(hc, format)
  53. return val
  54. finally:
  55. win32pdh.RemoveCounter(hc)
  56. finally:
  57. win32pdh.CloseQuery(hq)
  58. def FindPerformanceAttributesByName(instanceName, object = None,
  59. counter = None,
  60. format = win32pdh.PDH_FMT_LONG,
  61. machine = None, bRefresh=0):
  62. """Find peformance attributes by (case insensitive) instance name.
  63. Given a process name, return a list with the requested attributes.
  64. Most useful for returning a tuple of PIDs given a process name.
  65. """
  66. if object is None: object = find_pdh_counter_localized_name("Process", machine)
  67. if counter is None: counter = find_pdh_counter_localized_name("ID Process", machine)
  68. if bRefresh: # PDH docs say this is how you do a refresh.
  69. win32pdh.EnumObjects(None, machine, 0, 1)
  70. instanceName = instanceName.lower()
  71. items, instances = win32pdh.EnumObjectItems(None,None,object, -1)
  72. # Track multiple instances.
  73. instance_dict = {}
  74. for instance in instances:
  75. try:
  76. instance_dict[instance] = instance_dict[instance] + 1
  77. except KeyError:
  78. instance_dict[instance] = 0
  79. ret = []
  80. for instance, max_instances in instance_dict.items():
  81. for inum in range(max_instances+1):
  82. if instance.lower() == instanceName:
  83. ret.append(GetPerformanceAttributes(object, counter,
  84. instance, inum, format,
  85. machine))
  86. return ret
  87. def ShowAllProcesses():
  88. object = find_pdh_counter_localized_name("Process")
  89. items, instances = win32pdh.EnumObjectItems(None,None,object,
  90. win32pdh.PERF_DETAIL_WIZARD)
  91. # Need to track multiple instances of the same name.
  92. instance_dict = {}
  93. for instance in instances:
  94. try:
  95. instance_dict[instance] = instance_dict[instance] + 1
  96. except KeyError:
  97. instance_dict[instance] = 0
  98. # Bit of a hack to get useful info.
  99. items = [find_pdh_counter_localized_name("ID Process")] + items[:5]
  100. print("Process Name", ",".join(items))
  101. for instance, max_instances in instance_dict.items():
  102. for inum in range(max_instances+1):
  103. hq = win32pdh.OpenQuery()
  104. hcs = []
  105. for item in items:
  106. path = win32pdh.MakeCounterPath( (None,object,instance,
  107. None, inum, item) )
  108. hcs.append(win32pdh.AddCounter(hq, path))
  109. win32pdh.CollectQueryData(hq)
  110. # as per http://support.microsoft.com/default.aspx?scid=kb;EN-US;q262938, some "%" based
  111. # counters need two collections
  112. time.sleep(0.01)
  113. win32pdh.CollectQueryData(hq)
  114. print("%-15s\t" % (instance[:15]), end=' ')
  115. for hc in hcs:
  116. type, val = win32pdh.GetFormattedCounterValue(hc, win32pdh.PDH_FMT_LONG)
  117. print("%5d" % (val), end=' ')
  118. win32pdh.RemoveCounter(hc)
  119. print()
  120. win32pdh.CloseQuery(hq)
  121. # NOTE: This BrowseCallback doesn't seem to work on Vista for markh.
  122. # XXX - look at why!?
  123. # Some counters on Vista require elevation, and callback would previously
  124. # clear exceptions without printing them.
  125. def BrowseCallBackDemo(counters):
  126. ## BrowseCounters can now return multiple counter paths
  127. for counter in counters:
  128. machine, object, instance, parentInstance, index, counterName = \
  129. win32pdh.ParseCounterPath(counter)
  130. result = GetPerformanceAttributes(object, counterName, instance, index,
  131. win32pdh.PDH_FMT_DOUBLE, machine)
  132. print("Value of '%s' is" % counter, result)
  133. print("Added '%s' on object '%s' (machine %s), instance %s(%d)-parent of %s" \
  134. % (counterName, object, machine, instance, index, parentInstance))
  135. return 0
  136. def browse(callback = BrowseCallBackDemo, title="Python Browser",
  137. level=win32pdh.PERF_DETAIL_WIZARD):
  138. win32pdh.BrowseCounters(None,0, callback, level, title, ReturnMultiple=True)
  139. if __name__=='__main__':
  140. ShowAllProcesses()
  141. # Show how to get a couple of attributes by name.
  142. counter = find_pdh_counter_localized_name("Virtual Bytes")
  143. print("Virtual Bytes = ", FindPerformanceAttributesByName("python",
  144. counter=counter))
  145. print("Available Bytes = ", GetPerformanceAttributes(
  146. find_pdh_counter_localized_name("Memory"),
  147. find_pdh_counter_localized_name("Available Bytes")))
  148. # And a browser.
  149. print("Browsing for counters...")
  150. browse()