42    private const FORMAT_VERSION = 3; 
 
   44    private static bool $enabled = 
false;
 
   45    private static int $timingStart = 0;
 
   48    private static ?
ObjectSet $toggleCallbacks = 
null;
 
   50    private static ?
ObjectSet $reloadCallbacks = 
null;
 
   52    private static ?
ObjectSet $collectCallbacks = 
null;
 
   85        $threadId = NativeThread::getCurrentThread()?->getThreadId();
 
   88        foreach(TimingsRecord::getAll() as $timings){
 
   89            $time = $timings->getTotalTime();
 
   90            $count = $timings->getCount();
 
   96            $avg = $time / $count;
 
   98            $group = $timings->getGroup() . ($threadId !== 
null ? 
" ThreadId: $threadId" : 
"");
 
   99            $groups[$group][] = implode(
" ", [
 
  104                "Violations: " . $timings->getViolations(),
 
  105                "RecordId: " . $timings->getId(),
 
  106                "ParentRecordId: " . ($timings->getParentId() ?? 
"none"),
 
  107                "TimerId: " . $timings->getTimerId(),
 
  108                "Ticks: " . $timings->getTicksActive(),
 
  109                "Peak: " . $timings->getPeakTime(),
 
  114        foreach(Utils::stringifyKeys($groups) as $groupName => $lines){
 
  115            $result[] = $groupName;
 
  116            foreach($lines as $line){
 
  117                $result[] = 
"    $line";
 
 
  128    private static function printFooter() : array{
 
  131        $result[] = 
"# Version " . Server::getInstance()->getVersion();
 
  132        $result[] = 
"# " . Server::getInstance()->getName() . 
" " . Server::getInstance()->getPocketMineVersion();
 
  134        $result[] = 
"# FormatVersion " . self::FORMAT_VERSION;
 
  136        $sampleTime = hrtime(
true) - self::$timingStart;
 
  137        $result[] = 
"Sample time $sampleTime (" . ($sampleTime / 1000000000) . 
"s)";
 
  149        $records = self::printCurrentThreadRecords();
 
  150        $footer = self::printFooter();
 
  152        return [...$records, ...$footer];
 
 
  167        $thisThreadRecords = self::printCurrentThreadRecords();
 
  169        $otherThreadRecordPromises = [];
 
  170        if(self::$collectCallbacks !== 
null){
 
  171            foreach(self::$collectCallbacks as $callback){
 
  172                $callbackPromises = $callback();
 
  173                array_push($otherThreadRecordPromises, ...$callbackPromises);
 
  179        Promise::all($otherThreadRecordPromises)->onCompletion(
 
  180            function(array $promisedRecords) use ($resolver, $thisThreadRecords) : 
void{
 
  181                $resolver->resolve([...$thisThreadRecords, ...array_merge(...$promisedRecords), ...self::printFooter()]);
 
  184                throw new \AssertionError(
"This promise is not expected to be rejected");
 
  188        return $resolver->getPromise();
 
 
  191    public static function isEnabled() : bool{
 
  192        return self::$enabled;
 
  195    public static function setEnabled(
bool $enable = 
true) : void{
 
  196        if($enable === self::$enabled){
 
  199        self::$enabled = $enable;
 
  200        self::internalReload();
 
  201        if(self::$toggleCallbacks !== 
null){
 
  202            foreach(self::$toggleCallbacks as $callback){
 
  208    public static function getStartTime() : float{
 
  209        return self::$timingStart;
 
  212    private static function internalReload() : void{
 
  213        TimingsRecord::reset();
 
  215            self::$timingStart = hrtime(
true);
 
  219    public static function reload() : void{
 
  220        self::internalReload();
 
  221        if(self::$reloadCallbacks !== 
null){
 
  222            foreach(self::$reloadCallbacks as $callback){
 
  228    public static function tick(
bool $measure = 
true) : void{
 
  230            TimingsRecord::tick($measure);
 
  234    private ?TimingsRecord $rootRecord = 
null;
 
  235    private int $timingDepth = 0;
 
  241    private array $recordsByParent = [];
 
  243    public function __construct(
 
  244        private string $name,
 
  245        private ?TimingsHandler $parent = 
null,
 
  246        private string $group = Timings::GROUP_MINECRAFT
 
  249    public function getName() : string{ return $this->name; }
 
  251    public function getGroup() : string{ return $this->group; }
 
  253    public function startTiming() : void{
 
  255            $this->internalStartTiming(hrtime(
true));
 
  259    private function internalStartTiming(
int $now) : void{
 
  260        if(++$this->timingDepth === 1){
 
  261            if($this->parent !== 
null){
 
  262                $this->parent->internalStartTiming($now);
 
  265            $current = TimingsRecord::getCurrentRecord();
 
  266            if($current !== 
null){
 
  267                $record = $this->recordsByParent[spl_object_id($current)] ?? 
null;
 
  268                if($record === 
null){
 
  269                    $record = 
new TimingsRecord($this, $current);
 
  270                    $this->recordsByParent[spl_object_id($current)] = $record;
 
  273                if($this->rootRecord === 
null){
 
  274                    $this->rootRecord = 
new TimingsRecord($this, 
null);
 
  276                $record = $this->rootRecord;
 
  278            $record->startTiming($now);
 
  282    public function stopTiming() : void{
 
  284            $this->internalStopTiming(hrtime(
true));
 
  288    private function internalStopTiming(
int $now) : void{
 
  289        if($this->timingDepth === 0){
 
  295        if(--$this->timingDepth !== 0){
 
  299        $record = TimingsRecord::getCurrentRecord();
 
  300        $timerId = spl_object_id($this);
 
  301        for(; $record !== 
null && $record->getTimerId() !== $timerId; $record = TimingsRecord::getCurrentRecord()){
 
  302            \GlobalLogger::get()->error(
"Timer \"" . $record->getName() . 
"\" should have been stopped before stopping timer \"" . $this->name . 
"\"");
 
  303            $record->stopTiming($now);
 
  305        $record?->stopTiming($now);
 
  306        if($this->parent !== 
null){
 
  307            $this->parent->internalStopTiming($now);
 
  318    public function time(\Closure $closure){
 
  319        $this->startTiming();
 
 
  330    public function reset() : void{
 
  331        $this->rootRecord = null;
 
  332        $this->recordsByParent = [];
 
  333        $this->timingDepth = 0;