@@ -38,7 +38,10 @@ type frameInfo struct {
3838 filename string
3939}
4040
41- const nativeLibraryAttrName = "shlib_name"
41+ const (
42+ nativeLibraryAttrName = "shlib_name"
43+ syscallAttrName = "syscall_name"
44+ )
4245
4346var (
4447 metricUser = metric {name : "samples.user.count" , desc : "Number of samples executing userspace code (self)" }
@@ -70,10 +73,14 @@ var (
7073 frameTypeBeam : metricBeam ,
7174 }
7275
76+ // match shared libraries
7377 rx = regexp .MustCompile (`(?:.*/)?(.+)\.so` )
78+
79+ // match syscalls
80+ syscallRx = regexp .MustCompile (`^__(?:x64|arm64)_sys_(\w+)` )
7481)
7582
76- func fetchFrameInfo (dictionary pprofile.ProfilesDictionary ,
83+ func fetchLeafFrameInfo (dictionary pprofile.ProfilesDictionary ,
7784 locationIndices pcommon.Int32Slice ,
7885 sampleLocationIndex int ,
7986) (frameInfo , error ) {
@@ -153,12 +160,12 @@ func classifyFrame(dictionary pprofile.ProfilesDictionary,
153160 counts map [metric ]int64 ,
154161 nativeCounts map [string ]int64 ,
155162) error {
156- fi , err := fetchFrameInfo (dictionary , locationIndices , 0 )
163+ leaf , err := fetchLeafFrameInfo (dictionary , locationIndices , 0 )
157164 if err != nil {
158165 return err
159166 }
160167
161- leafFrameType := fi .typ
168+ leafFrameType := leaf .typ
162169 // We don't need a separate metric for total number of samples, as this can always be
163170 // derived from summing the metricKernel and metricUser counts.
164171 metric := allowedFrameTypes [leafFrameType ]
@@ -177,7 +184,7 @@ func classifyFrame(dictionary pprofile.ProfilesDictionary,
177184 }
178185
179186 // Extract native library name and increment associated count
180- if sm := rx .FindStringSubmatch (fi .filename ); sm != nil {
187+ if sm := rx .FindStringSubmatch (leaf .filename ); sm != nil {
181188 nativeCounts [sm [1 ]]++
182189 } else {
183190 counts [metric ]++
@@ -186,13 +193,82 @@ func classifyFrame(dictionary pprofile.ProfilesDictionary,
186193 return nil
187194}
188195
196+ // identifySyscall walks the frames and extracts the syscall information.
197+ func identifySyscall (dictionary pprofile.ProfilesDictionary ,
198+ locationIndices pcommon.Int32Slice ,
199+ syscallCounts map [string ]int64 ,
200+ ) error {
201+ // TODO: Scale syscallCounts by number of events in each Sample. Currently,
202+ // this logic assumes 1 event per Sample (thus the increments by 1 below),
203+ // which isn't necessarily the case.
204+ attrTable := dictionary .AttributeTable ()
205+ locationTable := dictionary .LocationTable ()
206+ strTable := dictionary .StringTable ()
207+ funcTable := dictionary .FunctionTable ()
208+
209+ attrTblLen := attrTable .Len ()
210+ locTblLen := locationTable .Len ()
211+ strTblLen := strTable .Len ()
212+ funcTblLen := funcTable .Len ()
213+
214+ for _ , li := range locationIndices .All () {
215+ if li >= int32 (locTblLen ) {
216+ // log error
217+ continue
218+ }
219+ loc := locationTable .At (int (li ))
220+ for _ , attrIdx := range loc .AttributeIndices ().All () {
221+ if attrIdx >= int32 (attrTblLen ) {
222+ // log error
223+ continue
224+ }
225+ attr := attrTable .At (int (attrIdx ))
226+ if int (attr .KeyStrindex ()) >= strTblLen {
227+ // log error
228+ continue
229+ }
230+
231+ if strTable .At (int (attr .KeyStrindex ())) == string (semconv .ProfileFrameTypeKey ) {
232+ frameType := attr .Value ().Str ()
233+ if frameType == frameTypeKernel {
234+ for _ , ln := range loc .Line ().All () {
235+ if ln .FunctionIndex () >= int32 (funcTblLen ) {
236+ // log error
237+ continue
238+ }
239+ fn := funcTable .At (int (ln .FunctionIndex ()))
240+ if fn .NameStrindex () >= int32 (strTblLen ) {
241+ // log error
242+ continue
243+ }
244+ fnName := strTable .At (int (fn .NameStrindex ()))
245+
246+ // Avoid string allocations by using indices to string location.
247+ indices := syscallRx .FindStringSubmatchIndex (fnName )
248+ if len (indices ) == 4 {
249+ syscall := fnName [indices [2 ]:indices [3 ]]
250+ syscallCounts [syscall ]++
251+ return nil
252+ }
253+ }
254+
255+ }
256+ }
257+
258+ }
259+
260+ }
261+ return nil
262+ }
263+
189264func (c * profilesToMetricsConnector ) addFrameMetrics (dictionary pprofile.ProfilesDictionary ,
190265 profile pprofile.Profile , scopeMetrics pmetric.ScopeMetrics ,
191266) {
192267 stackTable := dictionary .StackTable ()
193268
194269 counts := make (map [metric ]int64 )
195270 nativeCounts := make (map [string ]int64 )
271+ syscallCounts := make (map [string ]int64 )
196272
197273 // Process all samples and extract metric counts
198274 for _ , sample := range profile .Sample ().All () {
@@ -201,7 +277,12 @@ func (c *profilesToMetricsConnector) addFrameMetrics(dictionary pprofile.Profile
201277 sample , counts , nativeCounts ); err != nil {
202278 // Should not happen with well-formed profile data
203279 // TODO: Add error metric or log error
204- continue
280+ }
281+
282+ if err := identifySyscall (dictionary , stack .LocationIndices (),
283+ syscallCounts ); err != nil {
284+ // Should not happen with well-formed profile data
285+ // TODO: Add error metric or log error
205286 }
206287 }
207288
@@ -236,4 +317,20 @@ func (c *profilesToMetricsConnector) addFrameMetrics(dictionary pprofile.Profile
236317 dp .SetIntValue (count )
237318 dp .Attributes ().PutStr (nativeLibraryAttrName , libraryName )
238319 }
320+
321+ for sysCall , count := range syscallCounts {
322+ m := scopeMetrics .Metrics ().AppendEmpty ()
323+ m .SetName (c .config .MetricsPrefix + metricNative .name )
324+ m .SetDescription (metricNative .desc )
325+ m .SetUnit ("1" )
326+
327+ sum := m .SetEmptySum ()
328+ sum .SetIsMonotonic (true )
329+ sum .SetAggregationTemporality (pmetric .AggregationTemporalityDelta )
330+
331+ dp := sum .DataPoints ().AppendEmpty ()
332+ dp .SetTimestamp (profile .Time ())
333+ dp .SetIntValue (count )
334+ dp .Attributes ().PutStr (syscallAttrName , sysCall )
335+ }
239336}
0 commit comments