Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry-pick #22146 to 7.10: Perfmon - Fix regular expressions to comply to multiple parentheses in instance name and object #22233

Merged
merged 1 commit into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ field. You can revert this change by configuring tags for the module and omittin
- Fix retrieving resources by ID for the azure module. {pull}21711[21711] {issue}21707[21707]
- Use timestamp from CloudWatch API when creating events. {pull}21498[21498]
- Report the correct windows events for system/filesystem {pull}21758[21758]
- Fix regular expression in windows/permfon. {pull}22146[22146] {issue}21125[21125]
- Fix azure storage event format. {pull}21845[21845]
- Fix panic in kubernetes autodiscover related to keystores {issue}21843[21843] {pull}21880[21880]
- [Kubernetes] Remove redundant dockersock volume mount {pull}22009[22009]
Expand Down
44 changes: 38 additions & 6 deletions metricbeat/helper/windows/pdh/pdh_query_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
)

var (
instanceNameRegexp = regexp.MustCompile(`.*?\((.*?)\).*`)
instanceNameRegexp = regexp.MustCompile(`(\(.+\))\\`)
objectNameRegexp = regexp.MustCompile(`(?:^\\\\[^\\]+\\|^\\)([^\\]+)`)
)

Expand Down Expand Up @@ -86,7 +86,7 @@ func (q *Query) AddCounter(counterPath string, instance string, format string, w
var instanceName string
// Extract the instance name from the counterPath.
if instance == "" || wildcard {
instanceName, err = MatchInstanceName(counterPath)
instanceName, err = matchInstanceName(counterPath)
if err != nil {
return err
}
Expand Down Expand Up @@ -233,18 +233,50 @@ func (q *Query) Close() error {
return PdhCloseQuery(q.Handle)
}

// MatchInstanceName will check first for instance and then for any objects names.
func MatchInstanceName(counterPath string) (string, error) {
// matchInstanceName will check first for instance and then for any objects names.
func matchInstanceName(counterPath string) (string, error) {
matches := instanceNameRegexp.FindStringSubmatch(counterPath)
if len(matches) != 2 {
matches = objectNameRegexp.FindStringSubmatch(counterPath)
if len(matches) == 2 {
return returnLastInstance(matches[1]), nil
}
matches = objectNameRegexp.FindStringSubmatch(counterPath)
if len(matches) == 2 {
return matches[1], nil
}
return "", errors.New("query doesn't contain an instance name. In this case you have to define 'instance_name'")
}

// returnLastInstance will return the content from the last parentheses, this covers cases as `\WF (System.Workflow) 4.0.0.0(*)\Workflows Created`.
func returnLastInstance(match string) string {
var openedParanth int
var innerMatch string
var matches []string
runeMatch := []rune(match)
for i := 0; i < len(runeMatch); i++ {
char := string(runeMatch[i])

// check if string ends between parentheses
if char == ")" {
openedParanth -= 1
}
if openedParanth > 0 {
innerMatch += char
}
if openedParanth == 0 && innerMatch != "" {
matches = append(matches, innerMatch)
innerMatch = ""
}
// check if string starts between parentheses
if char == "(" {
openedParanth += 1
}
}
if len(matches) > 0 {
return matches[len(matches)-1]
}
return match
}

// getCounterValue will retrieve the counter value based on the format applied in the config options
func getCounterValue(counter *Counter) CounterValue {
counterValue := CounterValue{Instance: counter.instanceName, Err: CounterValueError{CStatus: 0}}
Expand Down
66 changes: 65 additions & 1 deletion metricbeat/helper/windows/pdh/pdh_query_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,56 @@ func TestSuccessfulQuery(t *testing.T) {
assert.NotNil(t, list)
}

func TestMatchInstanceName(t *testing.T) {
query := "\\SQLServer:Databases(*)\\Log File(s) Used Size (KB)"
match, err := matchInstanceName(query)
assert.NoError(t, err)
assert.Equal(t, match, "*")

query = " \\\\desktop-rfooe09\\per processor network interface card activity(3, microsoft wi-fi directvirtual (gyfyg) adapter #2)\\dpcs queued/sec"
match, err = matchInstanceName(query)
assert.NoError(t, err)
assert.Equal(t, match, "3, microsoft wi-fi directvirtual (gyfyg) adapter #2")

query = " \\\\desktop-rfooe09\\ (test this scenario) per processor network interface card activity(3, microsoft wi-fi directvirtual (gyfyg) adapter #2)\\dpcs queued/sec"
match, err = matchInstanceName(query)
assert.NoError(t, err)
assert.Equal(t, match, "3, microsoft wi-fi directvirtual (gyfyg) adapter #2")

query = "\\RAS\\Bytes Received By Disconnected Clients"
match, err = matchInstanceName(query)
assert.NoError(t, err)
assert.Equal(t, match, "RAS")

query = `\\Process (chrome.exe#4)\\Bytes Received By Disconnected Clients`
match, err = matchInstanceName(query)
assert.NoError(t, err)
assert.Equal(t, match, "chrome.exe#4")

query = "\\BranchCache\\Local Cache: Cache complete file segments"
match, err = matchInstanceName(query)
assert.NoError(t, err)
assert.Equal(t, match, "BranchCache")

query = `\Synchronization(*)\Exec. Resource no-Waits AcqShrdStarveExcl/sec`
match, err = matchInstanceName(query)
assert.NoError(t, err)
assert.Equal(t, match, "*")

query = `\.NET CLR Exceptions(test hellp (dsdsd) #rfsfs #3)\# of Finallys / sec`
match, err = matchInstanceName(query)
assert.NoError(t, err)
assert.Equal(t, match, "test hellp (dsdsd) #rfsfs #3")
}

// TestInstanceNameRegexp tests regular expression for instance.
func TestInstanceNameRegexp(t *testing.T) {
queryPaths := []string{`\SQLServer:Databases(*)\Log File(s) Used Size (KB)`, `\Search Indexer(*)\L0 Indexes (Wordlists)`,
`\Search Indexer(*)\L0 Merges (flushes) Now.`, `\NUMA Node Memory(*)\Free & Zero Page List MBytes`}
for _, path := range queryPaths {
matches := instanceNameRegexp.FindStringSubmatch(path)
if assert.Len(t, matches, 2, "regular expression did not return any matches") {
assert.Equal(t, matches[1], "*")
assert.Equal(t, matches[1], "(*)")
}
}
}
Expand All @@ -114,6 +156,28 @@ func TestObjectNameRegexp(t *testing.T) {
}
}

func TestReturnLastInstance(t *testing.T) {
query := "(*)"
match := returnLastInstance(query)
assert.Equal(t, match, "*")

query = "(3, microsoft wi-fi directvirtual (gyfyg) adapter #2)"
match = returnLastInstance(query)
assert.Equal(t, match, "3, microsoft wi-fi directvirtual (gyfyg) adapter #2")

query = "(test this scenario) per processor network interface card activity(3, microsoft wi-fi directvirtual (gyfyg) adapter #2)"
match = returnLastInstance(query)
assert.Equal(t, match, "3, microsoft wi-fi directvirtual (gyfyg) adapter #2")

query = `(chrome.exe#4)`
match = returnLastInstance(query)
assert.Equal(t, match, "chrome.exe#4")

query = `(test hellp (dsdsd) #rfsfs #3)`
match = returnLastInstance(query)
assert.Equal(t, match, "test hellp (dsdsd) #rfsfs #3")
}

func TestUTF16ToStringArray(t *testing.T) {
var array = []string{
"\\\\DESKTOP-RFOOE09\\Physikalischer Datenträger(0 C:)\\Schreibvorgänge/s",
Expand Down
4 changes: 2 additions & 2 deletions metricbeat/module/windows/perfmon/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
"github.com/elastic/beats/v7/metricbeat/mb"
)

var processRegexp = regexp.MustCompile(`(.+?)#[1-9]+`)
var processRegexp = regexp.MustCompile(`(.+?[^\s])(?:#\d+|$)`)

func (re *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.Event {
eventMap := make(map[string]*mb.Event)
Expand Down Expand Up @@ -73,7 +73,7 @@ func (re *Reader) groupToEvents(counters map[string][]pdh.CounterValue) []mb.Eve
Error: errors.Wrapf(val.Err.Error, "failed on query=%v", counterPath),
}
if val.Instance != "" {
//will ignore instance counter
//will ignore instance index
if ok, match := matchesParentProcess(val.Instance); ok {
eventMap[eventKey].MetricSetFields.Put(counter.InstanceField, match)
} else {
Expand Down
6 changes: 5 additions & 1 deletion metricbeat/module/windows/perfmon/data_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,13 @@ func TestGroupToSingleEvent(t *testing.T) {

func TestMatchesParentProcess(t *testing.T) {
ok, val := matchesParentProcess("svchost")
assert.False(t, ok)
assert.True(t, ok)
assert.Equal(t, val, "svchost")
ok, val = matchesParentProcess("svchost#54")
assert.True(t, ok)
assert.Equal(t, val, "svchost")

ok, val = matchesParentProcess("svchost (test) another #54")
assert.True(t, ok)
assert.Equal(t, val, "svchost (test) another #54")
}