如何匹配自定义 Instruments 启动模式中的字符串?
How do you match strings in custom Instruments start-pattern?
我在 Xcode 的 Instruments 中匹配 start-pattern
in a custom instrument (see WWDC 2018 video Creating Custom Instruments) 中的字符串文字时遇到问题。
例如,这个 start-pattern
有效...
<start-pattern>
<message>?name ?value</message>
</start-pattern>
<column>
<mnemonic>name</mnemonic>
<title>Name</title>
<type>string</type>
<expression>?name</expression>
</column>
<column>
<mnemonic>value</mnemonic>
<title>Value</title>
<type>uint64</type>
<expression>?value</expression>
</column>
...用这个 OSSignpost
:
let id = OSSignpostID(log: log)
os_signpost(.begin, log: log, name: "Interval", signpostID: id, "Foo %d", 42)
...
os_signpost(.end, log: log, name: "Interval", signpostID: id)
?name ?value
模式有效,捕获 "Foo"
作为 name
和 42
作为 value
。很好。
但是当我尝试在消息中使用字符串文字时,它不起作用,例如这个start-pattern
...
<start-pattern>
<message>"Name:" ?name ",Value:" ?value</message>
</start-pattern>
... 有了这个 os_signpost
...
let id = OSSignpostID(log: log)
os_signpost(.begin, log: log, name: "Interval", signpostID: id, "Name:Foo,Value:%d", 42)
...
os_signpost(.end, log: log, name: "Interval", signpostID: id)
这个 "Name:" ?name ",Value:" ?value
模式不起作用,尽管 the documentation 建议它应该。
我做错了什么?
如果在 start-pattern
中使用字符串文字,则必须使用 printf-style
格式字符串。
因此,这将不起作用:
os_signpost(.begin,
log: log,
name: "Interval",
signpostID: id,
"Name:Foo,Value:%d",
42)
但是如果我们将 "Foo"
值移出格式字符串,并将其作为参数,它将起作用:
os_signpost(.begin,
log: log,
name: "Interval",
signpostID: id,
"Name:%{public}@,Value:%d",
"Foo",
42)
问题在于 os_signpost
调用中格式字符串的特殊细节。人们可能假设 start-pattern
/message
解析了 os_signpost
最终输出的结果,但它似乎(至少在 message
键中使用字符串文字时)它实际上是在解析格式字符串本身。
FWIW,这是我最后的,公认的基本音程乐器:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Instruments Developer Help: https://help.apple.com/instruments/developer/mac/current/ -->
<package>
<id>com.robertmryan.CustomInterval</id>
<version>0.1</version>
<title>Custom OS Signpost Interval</title>
<owner>
<name>Robert Ryan</name>
</owner>
<import-schema>os-signpost</import-schema>
<!-- See https://help.apple.com/instruments/developer/mac/current/#/dev536412616 -->
<os-signpost-interval-schema>
<id>custom-interval-schema</id>
<title>Interval</title>
<owner>
<name>Robert Ryan</name>
</owner>
<purpose>Provide mechanism for multicolored intervals posted by `os_signpost`; The string generated by `os_signpost` must be in form of "Label:%d,Concept:%{public}@", where "Label" is string that will control what text appears in the interval, and "Concept" is one of the strings listed in https://help.apple.com/instruments/developer/mac/current/#/dev66257045 that dictates the color of the interval. No spaces after the commas within this string.</purpose>
<note>That message must use that printf-style format, not embedding the values in the format string literal.</note>
<!-- you can constrain this to a particular subsystem if you'd like:
<subsystem>"com.domain.MyApp"</subsystem>
-->
<category>"Interval"</category>
<name>?name</name>
<start-pattern>
<message>"Label:" ?label ",Concept:" ?concept</message>
</start-pattern>
<column>
<mnemonic>name</mnemonic>
<title>Name</title>
<type>string</type>
<expression>?name</expression>
</column>
<column>
<mnemonic>label</mnemonic>
<title>Label</title>
<type>string</type>
<expression>?label</expression>
</column>
<column>
<mnemonic>concept</mnemonic>
<title>Concept</title>
<type>event-concept</type>
<expression>?concept</expression>
</column>
</os-signpost-interval-schema>
<instrument>
<id>com.robertmryan.CustomInterval.instrument</id>
<title>Custom OS Signpost Interval</title>
<category>Behavior</category>
<purpose>Provide multi-colored intervals as dictated by the "event-concept" parsed from the `start-pattern` string.</purpose>
<icon>Generic</icon>
<create-table>
<id>custom-interval-table</id>
<schema-ref>custom-interval-schema</schema-ref>
</create-table>
<graph>
<title>Custom Interval Graph</title>
<lane>
<title>Interval</title>
<table-ref>custom-interval-table</table-ref>
<plot-template>
<instance-by>name</instance-by>
<label-format>%s</label-format>
<value-from>name</value-from>
<color-from>concept</color-from>
<label-from>label</label-from>
<qualified-by>layout-qualifier</qualified-by>
</plot-template>
</lane>
</graph>
<list>
<title>Custom Interval List</title>
<table-ref>custom-interval-table</table-ref>
<column>name</column>
<column>label</column>
<column>concept</column>
<column>start</column>
<column>duration</column>
</list>
</instrument>
</package>
以及我为该工具发布自定义间隔的类型:
//
// InstrumentsInterval.swift
//
// Created by Robert Ryan on 6/5/21.
//
import Foundation
import os.log
/// EventConcept enumeration
///
/// This is used to dictate the color of the intervals in our custom instrument.
/// See [Event Concept Engineering Type](https://help.apple.com/instruments/developer/mac/current/#/dev66257045).
enum EventConcept: String {
case success = "Success"
case failure = "Failure"
case fault = "Fault"
case critical = "Critical"
case error = "Error"
case debug = "Debug"
case pedantic = "Pedantic"
case info = "Info"
case signpost = "Signpost"
case veryLow = "Very Low"
case low = "Low"
case moderate = "Moderate"
case high = "High"
case red = "Red"
case orange = "Orange"
case blue = "Blue"
case purple = "Purple"
case green = "Green"
}
/// Interval to be shown in custom instrument when profiling app
struct InstrumentsInterval {
static let category = "Interval"
let name: StaticString
let label: String
let concept: EventConcept
let log: OSLog
let id: OSSignpostID
init(name: StaticString, label: String, concept: EventConcept = .debug, log: OSLog) {
self.name = name
self.concept = concept
self.label = label
self.log = log
self.id = OSSignpostID(log: log)
}
/// Block based interval
func perform<T>(block: () throws -> T) rethrows -> T {
begin()
defer { end() }
return try block()
}
/// Manually begin an interval
func begin() {
os_signpost(.begin, log: log, name: name, signpostID: id, "Label:%{public}@,Concept:%{public}@", label, concept.rawValue)
}
/// Manually end an interval
func end() {
os_signpost(.end, log: log, name: name, signpostID: id)
}
}
然后你可以像这样使用:
let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: InstrumentsInterval.category)
let interval = InstrumentsInterval(name: "Foo", label: "1", concept: .red, log: log)
interval.perform {
...
}
这可以在 Instruments 中产生以下结果(在这个例子中,我将应用程序限制为同时执行四个并发任务):
我在 Xcode 的 Instruments 中匹配 start-pattern
in a custom instrument (see WWDC 2018 video Creating Custom Instruments) 中的字符串文字时遇到问题。
例如,这个 start-pattern
有效...
<start-pattern>
<message>?name ?value</message>
</start-pattern>
<column>
<mnemonic>name</mnemonic>
<title>Name</title>
<type>string</type>
<expression>?name</expression>
</column>
<column>
<mnemonic>value</mnemonic>
<title>Value</title>
<type>uint64</type>
<expression>?value</expression>
</column>
...用这个 OSSignpost
:
let id = OSSignpostID(log: log)
os_signpost(.begin, log: log, name: "Interval", signpostID: id, "Foo %d", 42)
...
os_signpost(.end, log: log, name: "Interval", signpostID: id)
?name ?value
模式有效,捕获 "Foo"
作为 name
和 42
作为 value
。很好。
但是当我尝试在消息中使用字符串文字时,它不起作用,例如这个start-pattern
...
<start-pattern>
<message>"Name:" ?name ",Value:" ?value</message>
</start-pattern>
... 有了这个 os_signpost
...
let id = OSSignpostID(log: log)
os_signpost(.begin, log: log, name: "Interval", signpostID: id, "Name:Foo,Value:%d", 42)
...
os_signpost(.end, log: log, name: "Interval", signpostID: id)
这个 "Name:" ?name ",Value:" ?value
模式不起作用,尽管 the documentation 建议它应该。
我做错了什么?
如果在 start-pattern
中使用字符串文字,则必须使用 printf-style
格式字符串。
因此,这将不起作用:
os_signpost(.begin,
log: log,
name: "Interval",
signpostID: id,
"Name:Foo,Value:%d",
42)
但是如果我们将 "Foo"
值移出格式字符串,并将其作为参数,它将起作用:
os_signpost(.begin,
log: log,
name: "Interval",
signpostID: id,
"Name:%{public}@,Value:%d",
"Foo",
42)
问题在于 os_signpost
调用中格式字符串的特殊细节。人们可能假设 start-pattern
/message
解析了 os_signpost
最终输出的结果,但它似乎(至少在 message
键中使用字符串文字时)它实际上是在解析格式字符串本身。
FWIW,这是我最后的,公认的基本音程乐器:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Instruments Developer Help: https://help.apple.com/instruments/developer/mac/current/ -->
<package>
<id>com.robertmryan.CustomInterval</id>
<version>0.1</version>
<title>Custom OS Signpost Interval</title>
<owner>
<name>Robert Ryan</name>
</owner>
<import-schema>os-signpost</import-schema>
<!-- See https://help.apple.com/instruments/developer/mac/current/#/dev536412616 -->
<os-signpost-interval-schema>
<id>custom-interval-schema</id>
<title>Interval</title>
<owner>
<name>Robert Ryan</name>
</owner>
<purpose>Provide mechanism for multicolored intervals posted by `os_signpost`; The string generated by `os_signpost` must be in form of "Label:%d,Concept:%{public}@", where "Label" is string that will control what text appears in the interval, and "Concept" is one of the strings listed in https://help.apple.com/instruments/developer/mac/current/#/dev66257045 that dictates the color of the interval. No spaces after the commas within this string.</purpose>
<note>That message must use that printf-style format, not embedding the values in the format string literal.</note>
<!-- you can constrain this to a particular subsystem if you'd like:
<subsystem>"com.domain.MyApp"</subsystem>
-->
<category>"Interval"</category>
<name>?name</name>
<start-pattern>
<message>"Label:" ?label ",Concept:" ?concept</message>
</start-pattern>
<column>
<mnemonic>name</mnemonic>
<title>Name</title>
<type>string</type>
<expression>?name</expression>
</column>
<column>
<mnemonic>label</mnemonic>
<title>Label</title>
<type>string</type>
<expression>?label</expression>
</column>
<column>
<mnemonic>concept</mnemonic>
<title>Concept</title>
<type>event-concept</type>
<expression>?concept</expression>
</column>
</os-signpost-interval-schema>
<instrument>
<id>com.robertmryan.CustomInterval.instrument</id>
<title>Custom OS Signpost Interval</title>
<category>Behavior</category>
<purpose>Provide multi-colored intervals as dictated by the "event-concept" parsed from the `start-pattern` string.</purpose>
<icon>Generic</icon>
<create-table>
<id>custom-interval-table</id>
<schema-ref>custom-interval-schema</schema-ref>
</create-table>
<graph>
<title>Custom Interval Graph</title>
<lane>
<title>Interval</title>
<table-ref>custom-interval-table</table-ref>
<plot-template>
<instance-by>name</instance-by>
<label-format>%s</label-format>
<value-from>name</value-from>
<color-from>concept</color-from>
<label-from>label</label-from>
<qualified-by>layout-qualifier</qualified-by>
</plot-template>
</lane>
</graph>
<list>
<title>Custom Interval List</title>
<table-ref>custom-interval-table</table-ref>
<column>name</column>
<column>label</column>
<column>concept</column>
<column>start</column>
<column>duration</column>
</list>
</instrument>
</package>
以及我为该工具发布自定义间隔的类型:
//
// InstrumentsInterval.swift
//
// Created by Robert Ryan on 6/5/21.
//
import Foundation
import os.log
/// EventConcept enumeration
///
/// This is used to dictate the color of the intervals in our custom instrument.
/// See [Event Concept Engineering Type](https://help.apple.com/instruments/developer/mac/current/#/dev66257045).
enum EventConcept: String {
case success = "Success"
case failure = "Failure"
case fault = "Fault"
case critical = "Critical"
case error = "Error"
case debug = "Debug"
case pedantic = "Pedantic"
case info = "Info"
case signpost = "Signpost"
case veryLow = "Very Low"
case low = "Low"
case moderate = "Moderate"
case high = "High"
case red = "Red"
case orange = "Orange"
case blue = "Blue"
case purple = "Purple"
case green = "Green"
}
/// Interval to be shown in custom instrument when profiling app
struct InstrumentsInterval {
static let category = "Interval"
let name: StaticString
let label: String
let concept: EventConcept
let log: OSLog
let id: OSSignpostID
init(name: StaticString, label: String, concept: EventConcept = .debug, log: OSLog) {
self.name = name
self.concept = concept
self.label = label
self.log = log
self.id = OSSignpostID(log: log)
}
/// Block based interval
func perform<T>(block: () throws -> T) rethrows -> T {
begin()
defer { end() }
return try block()
}
/// Manually begin an interval
func begin() {
os_signpost(.begin, log: log, name: name, signpostID: id, "Label:%{public}@,Concept:%{public}@", label, concept.rawValue)
}
/// Manually end an interval
func end() {
os_signpost(.end, log: log, name: name, signpostID: id)
}
}
然后你可以像这样使用:
let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: InstrumentsInterval.category)
let interval = InstrumentsInterval(name: "Foo", label: "1", concept: .red, log: log)
interval.perform {
...
}
这可以在 Instruments 中产生以下结果(在这个例子中,我将应用程序限制为同时执行四个并发任务):