问题描述
我无法在 Xcode 的 Instruments 的自定义仪器中匹配 start-pattern
中的字符串文字(请参阅 WWDC 2018 视频 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,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,"Name:Foo,Value:%d",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,"Name:%{public}@,"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,name: name,"Label:%{public}@,label,concept.rawValue)
}
/// Manually end an interval
func end() {
os_signpost(.end,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 中产生以下结果(在本例中,我将应用程序限制为一次执行四个并发任务):