in prod/php/ElasticOTel/InferredSpans/InferredSpans.php [122:187]
private function compareStackTraces(array $stackTrace, int $durationMs, bool $topFrameIsInternalFunction, ?int $apmFramesFilteredOut): void
{
$this->stackTraceId++;
$identicalFramesCount = $this->getHowManyStackFramesAreIdenticalFromStackBottom($stackTrace);
self::logDebug("Same frames count: " . $identicalFramesCount); //, [$stackTrace, $this->lastStackTrace]);
$lastStackTraceCount = count($this->lastStackTrace);
$oldFramesCount = $lastStackTraceCount - $identicalFramesCount;
// on previous stack trace - end all spans above identical frames
$previousFrameStackTraceId = -1;
$forceParentChangeFailed = false;
for ($index = 0; $index < $oldFramesCount; $index++) {
$endEpochNanos = null;
// if last frame was internal function, so duration contains it's time, previous ones ended between sampling interval - they're shorter
if ($topFrameIsInternalFunction) {
$endEpochNanos = $this->getStartTime($durationMs);
}
$dropSpan = false;
if ($this->spanReductionEnabled) {
$dropSpan = $this->shouldReduceFrame($index, $oldFramesCount, $previousFrameStackTraceId, $forceParentChangeFailed);
}
$this->endFrameSpan($this->lastStackTrace[$index], $dropSpan, $endEpochNanos);
unset($this->lastStackTrace[$index]); // remove ended frame
}
// reindex array
$this->lastStackTrace = array_values($this->lastStackTrace);
$stackTraceCount = count($stackTrace);
if ($stackTraceCount == $identicalFramesCount) {
// no frames to start
return;
}
$first = true;
// start spans for all frames below identical frames
for ($index = $stackTraceCount - $identicalFramesCount - 1; $index >= 0; $index--) {
if ($first && $apmFramesFilteredOut && !empty($this->lastStackTrace)) {
self::logDebug("Going to start span in previous span context");
$newFrame = $this->startFrameSpan($stackTrace[$index], $durationMs, $this->lastStackTrace[0][self::METADATA_CONTEXT]->get(), $this->stackTraceId);
} else {
$newFrame = $this->startFrameSpan($stackTrace[$index], $durationMs, null, $this->stackTraceId);
}
$first = false;
if ($this->attachStackTrace) {
$newFrame[self::METADATA_SPAN]->get()?->setAttribute(TraceAttributes::CODE_STACKTRACE, $this->getStackTrace($this->lastStackTrace));
}
if ($index == 0 && $topFrameIsInternalFunction) {
/** @noinspection PhpRedundantOptionalArgumentInspection */
$this->endFrameSpan($newFrame, false, null); // we don't need to save the newest internal frame, it ended
} else {
array_unshift($this->lastStackTrace, $newFrame); // push-copy frame in front of last stack trace for next interruption processing
}
}
}