<commit_before>package collectors

import (
	"github.com/StackExchange/tcollector/opentsdb"
	"github.com/StackExchange/wmi"
	"regexp"
	"strings"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_windows_processes})
}

// These are silly processes but exist on my machine, will need to update KMB
var processInclusions = regexp.MustCompile("chrome|powershell|tcollector")

func c_windows_processes() opentsdb.MultiDataPoint {
	var dst []Win32_PerfRawData_PerfProc_Process
	var q = wmi.CreateQuery(&dst, `WHERE Name <> '_Total'`)
	err := wmi.Query(q, &dst)
	if err != nil {
		l.Println("cpu:", err)
		return nil
	}
	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		if !processInclusions.MatchString(v.Name) {
			continue
		}
		id := "0"
		raw_name := strings.Split(v.Name, "#")
		name := raw_name[0]
		if len(raw_name) == 2 {
			id = raw_name[1]
		}
		// If you have a hash sign in your process name you don't deserve monitoring ;-)
		if len(raw_name) > 2 {
			continue
		}
		Add(&md, "processes.elapsed_time", v.ElapsedTime, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.handle_count", v.HandleCount, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.io_bytes", v.IOOtherBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "other"})
		Add(&md, "processes.io_operations", v.IOOtherOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "other"})
		Add(&md, "processes.io_bytes", v.IOReadBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "read"})
		Add(&md, "processes.io_operations", v.IOReadOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "read"})
		Add(&md, "processes.io_bytes", v.IOWriteBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "write"})
		Add(&md, "processes.io_operations", v.IOWriteOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "write"})
		Add(&md, "processes.page_faults", v.PageFaultsPersec, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.pagefile_bytes", v.PageFileBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.pagefile_bytes_peak", v.PageFileBytesPeak, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.cpu_time", v.PercentPrivilegedTime, opentsdb.TagSet{"name": name, "id": id, "type": "privileged"})
		Add(&md, "processes.cpu_total", v.PercentProcessorTime, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.cpu_time", v.PercentUserTime, opentsdb.TagSet{"name": name, "id": id, "type": "user"})
		Add(&md, "processes.pool_nonpaged_bytes", v.PoolNonpagedBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.pool_paged_bytes", v.PoolPagedBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.priority_base", v.PriorityBase, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.private_bytes", v.PrivateBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.thread_count", v.ThreadCount, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.virtual_bytes", v.VirtualBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.virtual_bytespeak", v.VirtualBytesPeak, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.workingset", v.WorkingSet, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.workingset_peak", v.WorkingSetPeak, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.workingset_private", v.WorkingSetPrivate, opentsdb.TagSet{"name": name, "id": id})
	}
	return md
}

// Actually a CIM_StatisticalInformation Struct according to Reflection
type Win32_PerfRawData_PerfProc_Process struct {
	ElapsedTime             uint64
	HandleCount             uint32
	IOOtherBytesPersec      uint64
	IOOtherOperationsPersec uint64
	IOReadBytesPersec       uint64
	IOReadOperationsPersec  uint64
	IOWriteBytesPersec      uint64
	IOWriteOperationsPersec uint64
	Name                    string
	PageFaultsPersec        uint32
	PageFileBytes           uint64
	PageFileBytesPeak       uint64
	PercentPrivilegedTime   uint64
	PercentProcessorTime    uint64
	PercentUserTime         uint64
	PoolNonpagedBytes       uint32
	PoolPagedBytes          uint32
	PriorityBase            uint32
	PrivateBytes            uint64
	ThreadCount             uint32
	VirtualBytes            uint64
	VirtualBytesPeak        uint64
	WorkingSet              uint64
	WorkingSetPeak          uint64
	WorkingSetPrivate       uint64
}<commit_msg>cmd/scollector: Support windows process monitoring by service name<commit_after>package collectors

import (
	"github.com/StackExchange/tcollector/opentsdb"
	"github.com/StackExchange/wmi"
	"regexp"
	"strings"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_windows_processes})
}

// These are silly processes but exist on my machine, will need to update KMB
var processInclusions = regexp.MustCompile("chrome|powershell|tcollector")
var serviceInclusions = regexp.MustCompile("WinRM")

func c_windows_processes() opentsdb.MultiDataPoint {
	var dst []Win32_PerfRawData_PerfProc_Process
	var q = wmi.CreateQuery(&dst, `WHERE Name <> '_Total'`)
	err := wmi.Query(q, &dst)
	if err != nil {
		l.Println("processes:", err)
		return nil
	}
	var svc_dst []Win32_Service
	var svc_q = wmi.CreateQuery(&svc_dst, `WHERE Name <> '_Total'`)
	err = wmi.Query(svc_q, &svc_dst)
	if err != nil {
		l.Println("services:", err)
		return nil
	}

	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		var name string
		service_match := false
		process_match := processInclusions.MatchString(v.Name)

		id := "0"

		if process_match {
			raw_name := strings.Split(v.Name, "#")
			name = raw_name[0]
			if len(raw_name) == 2 {
				id = raw_name[1]
			}
			// If you have a hash sign in your process name you don't deserve monitoring ;-)
			if len(raw_name) > 2 {
				continue
			}
		}

		// A Service match could "overwrite" a process match, but that is probably what we would want
		for _, svc := range svc_dst {
			if serviceInclusions.MatchString(svc.Name) {
				// It is possible the pid has gone and been reused, but I think this unlikely
				// And I'm not aware of an atomic join we could do anyways
				if svc.ProcessId == v.IDProcess {
					id = "0"
					service_match = true
					name = svc.Name
					break
				}
			}
		}
		if !(service_match || process_match) {
			continue
		}

		Add(&md, "processes.elapsed_time", v.ElapsedTime, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.handle_count", v.HandleCount, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.io_bytes", v.IOOtherBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "other"})
		Add(&md, "processes.io_operations", v.IOOtherOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "other"})
		Add(&md, "processes.io_bytes", v.IOReadBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "read"})
		Add(&md, "processes.io_operations", v.IOReadOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "read"})
		Add(&md, "processes.io_bytes", v.IOWriteBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "write"})
		Add(&md, "processes.io_operations", v.IOWriteOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "write"})
		Add(&md, "processes.page_faults", v.PageFaultsPersec, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.pagefile_bytes", v.PageFileBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.pagefile_bytes_peak", v.PageFileBytesPeak, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.cpu_time", v.PercentPrivilegedTime, opentsdb.TagSet{"name": name, "id": id, "type": "privileged"})
		Add(&md, "processes.cpu_total", v.PercentProcessorTime, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.cpu_time", v.PercentUserTime, opentsdb.TagSet{"name": name, "id": id, "type": "user"})
		Add(&md, "processes.pool_nonpaged_bytes", v.PoolNonpagedBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.pool_paged_bytes", v.PoolPagedBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.priority_base", v.PriorityBase, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.private_bytes", v.PrivateBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.thread_count", v.ThreadCount, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.virtual_bytes", v.VirtualBytes, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.virtual_bytes_peak", v.VirtualBytesPeak, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.working_set", v.WorkingSet, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.working_set_peak", v.WorkingSetPeak, opentsdb.TagSet{"name": name, "id": id})
		Add(&md, "processes.working_set_private", v.WorkingSetPrivate, opentsdb.TagSet{"name": name, "id": id})
	}
	return md
}

// Actually a CIM_StatisticalInformation Struct according to Reflection
type Win32_PerfRawData_PerfProc_Process struct {
	ElapsedTime             uint64
	HandleCount             uint32
	IDProcess               uint32
	IOOtherBytesPersec      uint64
	IOOtherOperationsPersec uint64
	IOReadBytesPersec       uint64
	IOReadOperationsPersec  uint64
	IOWriteBytesPersec      uint64
	IOWriteOperationsPersec uint64
	Name                    string
	PageFaultsPersec        uint32
	PageFileBytes           uint64
	PageFileBytesPeak       uint64
	PercentPrivilegedTime   uint64
	PercentProcessorTime    uint64
	PercentUserTime         uint64
	PoolNonpagedBytes       uint32
	PoolPagedBytes          uint32
	PriorityBase            uint32
	PrivateBytes            uint64
	ThreadCount             uint32
	VirtualBytes            uint64
	VirtualBytesPeak        uint64
	WorkingSet              uint64
	WorkingSetPeak          uint64
	WorkingSetPrivate       uint64
}

//Actually a Win32_BaseServce
type Win32_Service struct {
	Name      string
	ProcessId uint32
}
{'fraction_non_alphanumeric': 0.14225266975619585, 'fraction_numerical': 0.012593189603062663, 'mean_word_length': 4.418668122270742, 'pattern_counts': {'":': 112, '<': 6, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '925709', 'n_tokens_mistral': 3741, 'n_tokens_neox': 3494, 'n_words': 784}