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)";
154 $thisThreadRecords = self::printCurrentThreadRecords();
156 $otherThreadRecordPromises = [];
157 if(self::$collectCallbacks !==
null){
158 foreach(self::$collectCallbacks as $callback){
159 $callbackPromises = $callback();
160 array_push($otherThreadRecordPromises, ...$callbackPromises);
166 Promise::all($otherThreadRecordPromises)->onCompletion(
167 function(array $promisedRecords) use ($resolver, $thisThreadRecords) :
void{
168 $resolver->resolve([...$thisThreadRecords, ...array_merge(...$promisedRecords), ...self::printFooter()]);
171 throw new \AssertionError(
"This promise is not expected to be rejected");
175 return $resolver->getPromise();
178 public static function isEnabled() : bool{
179 return self::$enabled;
182 public static function setEnabled(
bool $enable =
true) : void{
183 if($enable === self::$enabled){
186 self::$enabled = $enable;
187 self::internalReload();
188 if(self::$toggleCallbacks !==
null){
189 foreach(self::$toggleCallbacks as $callback){
195 public static function getStartTime() : float{
196 return self::$timingStart;
199 private static function internalReload() : void{
200 TimingsRecord::reset();
202 self::$timingStart = hrtime(
true);
206 public static function reload() : void{
207 self::internalReload();
208 if(self::$reloadCallbacks !==
null){
209 foreach(self::$reloadCallbacks as $callback){
215 public static function tick(
bool $measure =
true) : void{
217 TimingsRecord::tick($measure);
221 private ?TimingsRecord $rootRecord =
null;
222 private int $timingDepth = 0;
228 private array $recordsByParent = [];
230 public function __construct(
231 private string $name,
232 private ?TimingsHandler $parent =
null,
233 private string $group = Timings::GROUP_MINECRAFT
236 public function getName() : string{ return $this->name; }
238 public function getGroup() : string{ return $this->group; }
240 public function startTiming() : void{
242 $this->internalStartTiming(hrtime(
true));
246 private function internalStartTiming(
int $now) : void{
247 if(++$this->timingDepth === 1){
248 if($this->parent !==
null){
249 $this->parent->internalStartTiming($now);
252 $current = TimingsRecord::getCurrentRecord();
253 if($current !==
null){
254 $record = $this->recordsByParent[spl_object_id($current)] ??
null;
255 if($record ===
null){
256 $record =
new TimingsRecord($this, $current);
257 $this->recordsByParent[spl_object_id($current)] = $record;
260 if($this->rootRecord ===
null){
261 $this->rootRecord =
new TimingsRecord($this,
null);
263 $record = $this->rootRecord;
265 $record->startTiming($now);
269 public function stopTiming() : void{
271 $this->internalStopTiming(hrtime(
true));
275 private function internalStopTiming(
int $now) : void{
276 if($this->timingDepth === 0){
282 if(--$this->timingDepth !== 0){
286 $record = TimingsRecord::getCurrentRecord();
287 $timerId = spl_object_id($this);
288 for(; $record !==
null && $record->getTimerId() !== $timerId; $record = TimingsRecord::getCurrentRecord()){
289 \GlobalLogger::get()->error(
"Timer \"" . $record->getName() .
"\" should have been stopped before stopping timer \"" . $this->name .
"\"");
290 $record->stopTiming($now);
292 $record?->stopTiming($now);
293 if($this->parent !==
null){
294 $this->parent->internalStopTiming($now);
305 public function time(\Closure $closure){
306 $this->startTiming();
317 public function reset() : void{
318 $this->rootRecord = null;
319 $this->recordsByParent = [];
320 $this->timingDepth = 0;