# # Copyright (c) 2003-2014, Ashok P. Nadkarni # All rights reserved. # # See the file LICENSE for license namespace eval twapi { } # # Return list of toplevel performance objects proc twapi::pdh_enumerate_objects {args} { array set opts [parseargs args { datasource.arg machine.arg {detail.arg wizard} refresh } -nulldefault] # TBD - PdhEnumObjects enables the SeDebugPrivilege the first time it # is called. Should we reset it if it was not already enabled? # This seems to only happen on the first call return [PdhEnumObjects $opts(datasource) $opts(machine) \ [_perf_detail_sym_to_val $opts(detail)] \ $opts(refresh)] } proc twapi::_pdh_enumerate_object_items_helper {selector objname args} { array set opts [parseargs args { datasource.arg machine.arg {detail.arg wizard} refresh } -nulldefault] if {$opts(refresh)} { _refresh_perf_objects $opts(machine) $opts(datasource) } return [PdhEnumObjectItems $opts(datasource) $opts(machine) \ $objname \ [_perf_detail_sym_to_val $opts(detail)] \ $selector] } interp alias {} twapi::pdh_enumerate_object_items {} twapi::_pdh_enumerate_object_items_helper 0 interp alias {} twapi::pdh_enumerate_object_counters {} twapi::_pdh_enumerate_object_items_helper 1 interp alias {} twapi::pdh_enumerate_object_instances {} twapi::_pdh_enumerate_object_items_helper 2 # # Construct a counter path proc twapi::pdh_counter_path {object counter args} { array set opts [parseargs args { machine.arg instance.arg parent.arg {instanceindex.int -1} {localized.bool false} } -nulldefault] if {$opts(instanceindex) == 0} { # For XP. For first instance (index 0), the path should not contain # "#0" but on XP it does. Reset it to -1 for Vista+ consistency set opts(instanceindex) -1 } if {! $opts(localized)} { # Need to localize the counter names set object [_pdh_localize $object] set counter [_pdh_localize $counter] # TBD - not sure we need to localize parent set opts(parent) [_pdh_localize $opts(parent)] } # TBD - add options PDH_PATH_WBEM as documented in PdhMakeCounterPath return [PdhMakeCounterPath $opts(machine) $object $opts(instance) \ $opts(parent) $opts(instanceindex) $counter 0] } # # Parse a counter path and return the individual elements proc twapi::pdh_parse_counter_path {counter_path} { return [twine {machine object instance parent instanceindex counter} [PdhParseCounterPath $counter_path 0]] } interp alias {} twapi::pdh_get_scalar {} twapi::_pdh_get 1 interp alias {} twapi::pdh_get_array {} twapi::_pdh_get 0 proc twapi::_pdh_get {scalar hcounter args} { array set opts [parseargs args { {format.arg large {long large double}} {scale.arg {} {{} none x1000 nocap100}} var.arg } -ignoreunknown -nulldefault] set flags [_pdh_fmt_sym_to_val $opts(format)] if {$opts(scale) ne ""} { set flags [expr {$flags | [_pdh_fmt_sym_to_val $opts(scale)]}] } set status 1 set result "" trap { if {$scalar} { set result [PdhGetFormattedCounterValue $hcounter $flags] } else { set result [PdhGetFormattedCounterArray $hcounter $flags] } } onerror {TWAPI_WIN32 0x800007d1} { # Error is that no such instance exists. # If result is being returned in a variable, then # we will not generate an error but pass back a return value # of 0 if {[string length $opts(var)] == 0} { rethrow } set status 0 } if {[string length $opts(var)]} { uplevel [list set $opts(var) $result] return $status } else { return $result } } # # Get the value of a counter identified by the path. # Should not be used to collect # rate based options. # TBD - document proc twapi::pdh_counter_path_value {counter_path args} { array set opts [parseargs args { {format.arg long} scale.arg datasource.arg var.arg full.bool } -nulldefault] # Open the query set hquery [pdh_query_open -datasource $opts(datasource)] trap { set hcounter [pdh_add_counter $hquery $counter_path] pdh_query_refresh $hquery if {[string length $opts(var)]} { # Need to pass up value in a variable if so requested upvar $opts(var) myvar set opts(var) myvar } set value [pdh_get_scalar $hcounter -format $opts(format) \ -scale $opts(scale) -full $opts(full) \ -var $opts(var)] } finally { pdh_query_close $hquery } return $value } # # Constructs one or more counter paths for getting process information. # Returned as a list of sublists. Each sublist corresponds to a counter path # and has the form {counteroptionname datatype counterpath rate} # datatype is the recommended format when retrieving counter value (eg. double) # rate is 0 or 1 depending on whether the counter is a rate based counter or # not (requires at least two readings when getting the value) proc twapi::get_perf_process_counter_paths {pids args} { variable _process_counter_opt_map if {![info exists _counter_opt_map]} { # "descriptive string" format rate array set _process_counter_opt_map { privilegedutilization {"% Privileged Time" double 1} processorutilization {"% Processor Time" double 1} userutilization {"% User Time" double 1} parent {"Creating Process ID" long 0} elapsedtime {"Elapsed Time" large 0} handlecount {"Handle Count" long 0} pid {"ID Process" long 0} iodatabytesrate {"IO Data Bytes/sec" large 1} iodataopsrate {"IO Data Operations/sec" large 1} iootherbytesrate {"IO Other Bytes/sec" large 1} iootheropsrate {"IO Other Operations/sec" large 1} ioreadbytesrate {"IO Read Bytes/sec" large 1} ioreadopsrate {"IO Read Operations/sec" large 1} iowritebytesrate {"IO Write Bytes/sec" large 1} iowriteopsrate {"IO Write Operations/sec" large 1} pagefaultrate {"Page Faults/sec" large 1} pagefilebytes {"Page File Bytes" large 0} pagefilebytespeak {"Page File Bytes Peak" large 0} poolnonpagedbytes {"Pool Nonpaged Bytes" large 0} poolpagedbytes {"Pool Paged Bytes" large 1} basepriority {"Priority Base" large 1} privatebytes {"Private Bytes" large 1} threadcount {"Thread Count" large 1} virtualbytes {"Virtual Bytes" large 1} virtualbytespeak {"Virtual Bytes Peak" large 1} workingset {"Working Set" large 1} workingsetpeak {"Working Set Peak" large 1} } } set optdefs { machine.arg datasource.arg all refresh } # Add counter names to option list foreach cntr [array names _process_counter_opt_map] { lappend optdefs $cntr } # Parse options array set opts [parseargs args $optdefs -nulldefault] # Force a refresh of object items if {$opts(refresh)} { # Silently ignore. The above counters are predefined and refreshing # is just a time-consuming no-op. Keep the option for backward # compatibility if {0} { _refresh_perf_objects $opts(machine) $opts(datasource) } } # TBD - could we not use get_perf_instance_counter_paths instead of rest of this code # Get the path to the process. set pid_paths [get_perf_counter_paths \ [_pdh_localize "Process"] \ [list [_pdh_localize "ID Process"]] \ $pids \ -machine $opts(machine) -datasource $opts(datasource) \ -all] if {[llength $pid_paths] == 0} { # No thread return [list ] } # Construct the requested counter paths set counter_paths [list ] foreach {pid pid_path} $pid_paths { # We have to filter out an entry for _Total which might be present # if pid includes "0" # TBD - does _Total need to be localized? if {$pid == 0 && [string match -nocase *_Total\#0* $pid_path]} { continue } # Break it down into components and store in array array set path_components [pdh_parse_counter_path $pid_path] # Construct counter paths for this pid foreach {opt counter_info} [array get _process_counter_opt_map] { if {$opts(all) || $opts($opt)} { lappend counter_paths \ [list -$opt $pid [lindex $counter_info 1] \ [pdh_counter_path $path_components(object) \ [_pdh_localize [lindex $counter_info 0]] \ -localized true \ -machine $path_components(machine) \ -parent $path_components(parent) \ -instance $path_components(instance) \ -instanceindex $path_components(instanceindex)] \ [lindex $counter_info 2] \ ] } } } return $counter_paths } # Returns the counter path for the process with the given pid. This includes # the pid counter path element proc twapi::get_perf_process_id_path {pid args} { return [get_unique_counter_path \ [_pdh_localize "Process"] \ [_pdh_localize "ID Process"] $pid] } # # Constructs one or more counter paths for getting thread information. # Returned as a list of sublists. Each sublist corresponds to a counter path # and has the form {counteroptionname datatype counterpath rate} # datatype is the recommended format when retrieving counter value (eg. double) # rate is 0 or 1 depending on whether the counter is a rate based counter or # not (requires at least two readings when getting the value) proc twapi::get_perf_thread_counter_paths {tids args} { variable _thread_counter_opt_map if {![info exists _thread_counter_opt_map]} { array set _thread_counter_opt_map { privilegedutilization {"% Privileged Time" double 1} processorutilization {"% Processor Time" double 1} userutilization {"% User Time" double 1} contextswitchrate {"Context Switches/sec" long 1} elapsedtime {"Elapsed Time" large 0} pid {"ID Process" long 0} tid {"ID Thread" long 0} basepriority {"Priority Base" long 0} priority {"Priority Current" long 0} startaddress {"Start Address" large 0} state {"Thread State" long 0} waitreason {"Thread Wait Reason" long 0} } } set optdefs { machine.arg datasource.arg all refresh } # Add counter names to option list foreach cntr [array names _thread_counter_opt_map] { lappend optdefs $cntr } # Parse options array set opts [parseargs args $optdefs -nulldefault] # Force a refresh of object items if {$opts(refresh)} { # Silently ignore. The above counters are predefined and refreshing # is just a time-consuming no-op. Keep the option for backward # compatibility if {0} { _refresh_perf_objects $opts(machine) $opts(datasource) } } # TBD - could we not use get_perf_instance_counter_paths instead of rest of this code # Get the path to the thread set tid_paths [get_perf_counter_paths \ [_pdh_localize "Thread"] \ [list [_pdh_localize "ID Thread"]] \ $tids \ -machine $opts(machine) -datasource $opts(datasource) \ -all] if {[llength $tid_paths] == 0} { # No thread return [list ] } # Now construct the requested counter paths set counter_paths [list ] foreach {tid tid_path} $tid_paths { # Break it down into components and store in array array set path_components [pdh_parse_counter_path $tid_path] foreach {opt counter_info} [array get _thread_counter_opt_map] { if {$opts(all) || $opts($opt)} { lappend counter_paths \ [list -$opt $tid [lindex $counter_info 1] \ [pdh_counter_path $path_components(object) \ [_pdh_localize [lindex $counter_info 0]] \ -localized true \ -machine $path_components(machine) \ -parent $path_components(parent) \ -instance $path_components(instance) \ -instanceindex $path_components(instanceindex)] \ [lindex $counter_info 2] ] } } } return $counter_paths } # Returns the counter path for the thread with the given tid. This includes # the tid counter path element proc twapi::get_perf_thread_id_path {tid args} { return [get_unique_counter_path [_pdh_localize"Thread"] [_pdh_localize "ID Thread"] $tid] } # # Constructs one or more counter paths for getting processor information. # Returned as a list of sublists. Each sublist corresponds to a counter path # and has the form {counteroptionname datatype counterpath rate} # datatype is the recommended format when retrieving counter value (eg. double) # rate is 0 or 1 depending on whether the counter is a rate based counter or # not (requires at least two readings when getting the value) # $processor should be the processor number or "" to get total proc twapi::get_perf_processor_counter_paths {processor args} { variable _processor_counter_opt_map if {![string is integer -strict $processor]} { if {[string length $processor]} { error "Processor id must be an integer or null to retrieve information for all processors" } set processor "_Total" } if {![info exists _processor_counter_opt_map]} { array set _processor_counter_opt_map { dpcutilization {"% DPC Time" double 1} interruptutilization {"% Interrupt Time" double 1} privilegedutilization {"% Privileged Time" double 1} processorutilization {"% Processor Time" double 1} userutilization {"% User Time" double 1} dpcrate {"DPC Rate" double 1} dpcqueuerate {"DPCs Queued/sec" double 1} interruptrate {"Interrupts/sec" double 1} } } set optdefs { machine.arg datasource.arg all refresh } # Add counter names to option list foreach cntr [array names _processor_counter_opt_map] { lappend optdefs $cntr } # Parse options array set opts [parseargs args $optdefs -nulldefault -maxleftover 0] # Force a refresh of object items if {$opts(refresh)} { # Silently ignore. The above counters are predefined and refreshing # is just a time-consuming no-op. Keep the option for backward # compatibility if {0} { _refresh_perf_objects $opts(machine) $opts(datasource) } } # Now construct the requested counter paths set counter_paths [list ] foreach {opt counter_info} [array get _processor_counter_opt_map] { if {$opts(all) || $opts($opt)} { lappend counter_paths \ [list $opt $processor [lindex $counter_info 1] \ [pdh_counter_path \ [_pdh_localize "Processor"] \ [_pdh_localize [lindex $counter_info 0]] \ -localized true \ -machine $opts(machine) \ -instance $processor] \ [lindex $counter_info 2] \ ] } } return $counter_paths } # # Returns a list comprising of the counter paths for counters with # names in the list $counters from those instance(s) whose counter # $key_counter matches the specified $key_counter_value proc twapi::get_perf_instance_counter_paths {object counters key_counter key_counter_values args} { # Parse options array set opts [parseargs args { machine.arg datasource.arg {matchop.arg "exact"} skiptotal.bool refresh } -nulldefault] # Force a refresh of object items if {$opts(refresh)} { _refresh_perf_objects $opts(machine) $opts(datasource) } # Get the list of instances that have the specified value for the # key counter set instance_paths [get_perf_counter_paths $object \ [list $key_counter] $key_counter_values \ -machine $opts(machine) \ -datasource $opts(datasource) \ -matchop $opts(matchop) \ -skiptotal $opts(skiptotal) \ -all] # Loop through all instance paths, and all counters to generate # We store in an array to get rid of duplicates array set counter_paths {} foreach {key_counter_value instance_path} $instance_paths { # Break it down into components and store in array array set path_components [pdh_parse_counter_path $instance_path] # Now construct the requested counter paths # TBD - what should -localized be here ? foreach counter $counters { set counter_path \ [pdh_counter_path $path_components(object) \ $counter \ -localized true \ -machine $path_components(machine) \ -parent $path_components(parent) \ -instance $path_components(instance) \ -instanceindex $path_components(instanceindex)] set counter_paths($counter_path) "" } } return [array names counter_paths] } # # Returns a list comprising of the counter paths for all counters # whose values match the specified criteria proc twapi::get_perf_counter_paths {object counters counter_values args} { array set opts [parseargs args { machine.arg datasource.arg {matchop.arg "exact"} skiptotal.bool all refresh } -nulldefault] if {$opts(refresh)} { _refresh_perf_objects $opts(machine) $opts(datasource) } set items [pdh_enum_object_items $object \ -machine $opts(machine) \ -datasource $opts(datasource)] lassign $items object_counters object_instances if {[llength $counters]} { set object_counters $counters } set paths [_make_counter_path_list \ $object $object_instances $object_counters \ -skiptotal $opts(skiptotal) -machine $opts(machine)] set result_paths [list ] trap { # Set up the query with the process id for all processes set hquery [pdh_query_open -datasource $opts(datasource)] foreach path $paths { set hcounter [pdh_add_counter $hquery $path] set lookup($hcounter) $path } # Now collect the info pdh_query_refresh $hquery # Now lookup each counter value to find a matching one foreach hcounter [array names lookup] { if {! [pdh_get_scalar $hcounter -var value]} { # Counter or instance no longer exists continue } set match_pos [lsearch -$opts(matchop) $counter_values $value] if {$match_pos >= 0} { lappend result_paths \ [lindex $counter_values $match_pos] $lookup($hcounter) if {! $opts(all)} { break } } } } finally { # TBD - should we have a catch to throw errors? pdh_query_close $hquery } return $result_paths } # # Returns the counter path for counter $counter with a value $value # for object $object. Returns "" on no matches but exception if more than one proc twapi::get_unique_counter_path {object counter value args} { set matches [get_perf_counter_paths $object [list $counter ] [list $value] {*}$args -all] if {[llength $matches] > 1} { error "Multiple counter paths found matching criteria object='$object' counter='$counter' value='$value" } return [lindex $matches 0] } # # Utilities # proc twapi::_refresh_perf_objects {machine datasource} { pdh_enumerate_objects -refresh return } # # Return the localized form of a counter name # TBD - assumes machine is local machine! proc twapi::_pdh_localize {name} { variable _perf_counter_ids variable _localized_perf_counter_names set name_index [string tolower $name] # If we already have a translation, return it if {[info exists _localized_perf_counter_names($name_index)]} { return $_localized_perf_counter_names($name_index) } # Didn't already have it. Go generate the mappings # Get the list of counter names in English if we don't already have it if {![info exists _perf_counter_ids]} { foreach {id label} [registry get {HKEY_PERFORMANCE_DATA} {Counter 009}] { set _perf_counter_ids([string tolower $label]) $id } } # If we have do not have id for the given name, we will just use # the passed name as the localized version if {! [info exists _perf_counter_ids($name_index)]} { # Does not seem to exist. Just set localized name to itself return [set _localized_perf_counter_names($name_index) $name] } # We do have an id. THen try to get a translated name if {[catch {PdhLookupPerfNameByIndex "" $_perf_counter_ids($name_index)} xname]} { set _localized_perf_counter_names($name_index) $name } else { set _localized_perf_counter_names($name_index) $xname } return $_localized_perf_counter_names($name_index) } # Given a list of instances and counters, return a cross product of the # corresponding counter paths. # The list is expected to be already localized # Example: _make_counter_path_list "Process" (instance list) {{ID Process} {...}} # TBD - bug - does not handle -parent in counter path proc twapi::_make_counter_path_list {object instance_list counter_list args} { array set opts [parseargs args { machine.arg skiptotal.bool } -nulldefault] array set instances {} foreach instance $instance_list { if {![info exists instances($instance)]} { set instances($instance) 1 } else { incr instances($instance) } } if {$opts(skiptotal)} { catch {array unset instances "*_Total"} } set counter_paths [list ] foreach {instance count} [array get instances] { while {$count} { incr count -1 foreach counter $counter_list { lappend counter_paths [pdh_counter_path \ $object $counter \ -localized true \ -machine $opts(machine) \ -instance $instance \ -instanceindex $count] } } } return $counter_paths } # # Given a set of counter paths in the format returned by # get_perf_thread_counter_paths, get_perf_processor_counter_paths etc. # return the counter information as a flat list of field value pairs proc twapi::get_perf_values_from_metacounter_info {metacounters args} { array set opts [parseargs args {{interval.int 100}}] set result [list ] set counters [list ] if {[llength $metacounters]} { set hquery [pdh_query_open] trap { set counter_info [list ] set need_wait 0 foreach counter_elem $metacounters { lassign $counter_elem pdh_opt key data_type counter_path wait incr need_wait $wait set hcounter [pdh_add_counter $hquery $counter_path] lappend counters $hcounter lappend counter_info $pdh_opt $key $counter_path $data_type $hcounter } pdh_query_refresh $hquery if {$need_wait} { after $opts(interval) pdh_query_refresh $hquery } foreach {pdh_opt key counter_path data_type hcounter} $counter_info { if {[pdh_get_scalar $hcounter -format $data_type -var value]} { lappend result $pdh_opt $key $value } } } onerror {} { #puts "Error: $msg" } finally { pdh_query_close $hquery } } return $result } proc twapi::pdh_query_open {args} { variable _pdh_queries array set opts [parseargs args { datasource.arg cookie.int } -nulldefault] set qh [PdhOpenQuery $opts(datasource) $opts(cookie)] set id pdh[TwapiId] dict set _pdh_queries($id) Qh $qh dict set _pdh_queries($id) Counters {} dict set _pdh_queries($id) Meta {} return $id } proc twapi::pdh_query_refresh {qid args} { variable _pdh_queries _pdh_query_check $qid PdhCollectQueryData [dict get $_pdh_queries($qid) Qh] return } proc twapi::pdh_query_close {qid} { variable _pdh_queries _pdh_query_check $qid dict for {ctrh -} [dict get $_pdh_queries($qid) Counters] { PdhRemoveCounter $ctrh } PdhCloseQuery [dict get $_pdh_queries($qid) Qh] unset _pdh_queries($qid) } proc twapi::pdh_add_counter {qid ctr_path args} { variable _pdh_queries _pdh_query_check $qid parseargs args { {format.arg large {long large double}} {scale.arg {} {{} none x1000 nocap100}} name.arg cookie.int array.bool } -nulldefault -maxleftover 0 -setvars if {$name eq ""} { set name $ctr_path } if {[dict exists $_pdh_queries($qid) Meta $name]} { error "A counter with name \"$name\" already present in the query." } set flags [_pdh_fmt_sym_to_val $format] if {$scale ne ""} { set flags [expr {$flags | [_pdh_fmt_sym_to_val $scale]}] } set hctr [PdhAddCounter [dict get $_pdh_queries($qid) Qh] $ctr_path $flags] dict set _pdh_queries($qid) Counters $hctr 1 dict set _pdh_queries($qid) Meta $name [list Counter $hctr FmtFlags $flags Array $array] return $hctr } proc twapi::pdh_remove_counter {qid ctrname} { variable _pdh_queries _pdh_query_check $qid if {![dict exists $_pdh_queries($qid) Meta $ctrname]} { badargs! "Counter \"$ctrname\" not present in query." } set hctr [dict get $_pdh_queries($qid) Meta $ctrname Counter] dict unset _pdh_queries($qid) Counters $hctr dict unset _pdh_queries($qid) Meta $ctrname PdhRemoveCounter $hctr return } proc twapi::pdh_query_get {qid args} { variable _pdh_queries _pdh_query_check $qid # Refresh the data PdhCollectQueryData [dict get $_pdh_queries($qid) Qh] set meta [dict get $_pdh_queries($qid) Meta] if {[llength $args] != 0} { set names $args } else { set names [dict keys $meta] } set result {} foreach name $names { if {[dict get $meta $name Array]} { lappend result $name [PdhGetFormattedCounterArray [dict get $meta $name Counter] [dict get $meta $name FmtFlags]] } else { lappend result $name [PdhGetFormattedCounterValue [dict get $meta $name Counter] [dict get $meta $name FmtFlags]] } } return $result } twapi::proc* twapi::pdh_system_performance_query args { variable _sysperf_defs set _sysperf_defs { event_count { {Objects Events} {} } mutex_count { {Objects Mutexes} {} } process_count { {Objects Processes} {} } section_count { {Objects Sections} {} } semaphore_count { {Objects Semaphores} {} } thread_count { {Objects Threads} {} } handle_count { {Process "Handle Count" -instance _Total} {-format long} } commit_limit { {Memory "Commit Limit"} {} } committed_bytes { {Memory "Committed Bytes"} {} } committed_percent { {Memory "% Committed Bytes In Use"} {-format double} } memory_free_mb { {Memory "Available MBytes"} {} } memory_free_kb { {Memory "Available KBytes"} {} } page_fault_rate { {Memory "Page Faults/sec"} {} } page_input_rate { {Memory "Pages Input/sec"} {} } page_output_rate { {Memory "Pages Output/sec"} {} } disk_bytes_rate { {PhysicalDisk "Disk Bytes/sec" -instance _Total} {} } disk_readbytes_rate { {PhysicalDisk "Disk Read Bytes/sec" -instance _Total} {} } disk_writebytes_rate { {PhysicalDisk "Disk Write Bytes/sec" -instance _Total} {} } disk_transfer_rate { {PhysicalDisk "Disk Transfers/sec" -instance _Total} {} } disk_read_rate { {PhysicalDisk "Disk Reads/sec" -instance _Total} {} } disk_write_rate { {PhysicalDisk "Disk Writes/sec" -instance _Total} {} } disk_idle_percent { {PhysicalDisk "% Idle Time" -instance _Total} {-format double} } } # Per-processor counters are based on above but the object name depends # on the system in order to support > 64 processors set obj_name [expr {[min_os_version 6 1] ? "Processor Information" : "Processor"}] dict for {key ctr_name} { interrupt_utilization "% Interrupt Time" privileged_utilization "% Privileged Time" processor_utilization "% Processor Time" user_utilization "% User Time" idle_utilization "% Idle Time" } { lappend _sysperf_defs $key \ [list \ [list $obj_name $ctr_name -instance _Total] \ [list -format double]] lappend _sysperf_defs ${key}_per_cpu \ [list \ [list $obj_name $ctr_name -instance *] \ [list -format double -array 1]] } } { variable _sysperf_defs if {[llength $args] == 0} { return [lsort -dictionary [dict keys $_sysperf_defs]] } set qid [pdh_query_open] trap { foreach arg $args { set def [dict! $_sysperf_defs $arg] set ctr_path [pdh_counter_path {*}[lindex $def 0]] pdh_add_counter $qid $ctr_path -name $arg {*}[lindex $def 1] } pdh_query_refresh $qid } onerror {} { pdh_query_close $qid rethrow } return $qid } # # Internal utility procedures proc twapi::_pdh_query_check {qid} { variable _pdh_queries if {![info exists _pdh_queries($qid)]} { error "Invalid query id $qid" } } proc twapi::_perf_detail_sym_to_val {sym} { # PERF_DETAIL_NOVICE 100 # PERF_DETAIL_ADVANCED 200 # PERF_DETAIL_EXPERT 300 # PERF_DETAIL_WIZARD 400 # PERF_DETAIL_COSTLY 0x00010000 # PERF_DETAIL_STANDARD 0x0000FFFF return [dict get {novice 100 advanced 200 expert 300 wizard 400 costly 0x00010000 standard 0x0000ffff } $sym] } proc twapi::_pdh_fmt_sym_to_val {sym} { # PDH_FMT_RAW 0x00000010 # PDH_FMT_ANSI 0x00000020 # PDH_FMT_UNICODE 0x00000040 # PDH_FMT_LONG 0x00000100 # PDH_FMT_DOUBLE 0x00000200 # PDH_FMT_LARGE 0x00000400 # PDH_FMT_NOSCALE 0x00001000 # PDH_FMT_1000 0x00002000 # PDH_FMT_NODATA 0x00004000 # PDH_FMT_NOCAP100 0x00008000 return [dict get { raw 0x00000010 ansi 0x00000020 unicode 0x00000040 long 0x00000100 double 0x00000200 large 0x00000400 noscale 0x00001000 none 0x00001000 1000 0x00002000 x1000 0x00002000 nodata 0x00004000 nocap100 0x00008000 nocap 0x00008000 } $sym] }