Skip to content

Commit

Permalink
Set up timer based on available platform features
Browse files Browse the repository at this point in the history
  • Loading branch information
laffra committed Jul 30, 2023
1 parent f436b8b commit 8bb34c6
Showing 1 changed file with 40 additions and 6 deletions.
46 changes: 40 additions & 6 deletions microlog/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,45 @@ def start(self) -> None:
self.track_logging()
self.track_gc()
self.track_files()
self.lastSample = 0
self.lastStatus = 0
self.sampleCount = 0
self.running = True
self.setupTimer()

def setupTimer(self):
try:
# The peferred choice as this is the least amount of overhead
# Only works on Linux, UNIX, and MacOSs
signal.signal(signal.SIGVTALRM, self.signal_handler)
signal.setitimer(signal.ITIMER_VIRTUAL, self.delay)
except:
info(f"Microlog: Using a signal timer with a sample delay of {self.delay}s")
return
except Exception as e:
error(f"Microlog: Cannot use a signal timer: {e}")

try:
# The second-best choice, use a thread that sleeps
# Does not work on PyScript/PyOdide as WebAssembly makes threads hard
threading.Thread.start(self) # use thread if signals do not work
info(f"Microlog: Using a background thread with a sample delay of {self.delay}s")
return
except Exception as e:
error(f"Microlog: Cannot use a background thread timer: {e}")

try:
# The final choice, this is needed for PyScript.
# This has serious overhead, making Python run around 4X slower.
sys.setprofile(lambda frame, event, arg: self.sample() if event == "call" else None)
info(f"Microlog: Using sys.setprofile with a sample delay of {self.delay}s")
return
except Exception as e:
error(f"Microlog: Cannot use a sys.profile timer: {e}")


def signal_handler(self, sig, frame):
try:
self.sample()
self.generateStatus(log.log.now())
signal.setitimer(signal.ITIMER_VIRTUAL, self.delay)
except:
pass
Expand All @@ -142,7 +169,6 @@ def profile(self, frame, event, arg, delay=config.sampleDelay):
now = time()
if now - self.lastProfile > delay:
self.sample()
self.generateStatus(log.log.now())
self.lastProfile = time()
return self.profile

Expand Down Expand Up @@ -255,9 +281,10 @@ def tick(self, when) -> None:
self.generateStatus(when)

def generateStatus(self, when):
if when - self.lastStatus > config.statusDelay:
self.lastStatus = when
self.statusGenerator.tick(when)
if when - self.lastStatus < config.statusDelay:
return
self.lastStatus = when
self.statusGenerator.tick(when)

def sample(self, function=None) -> None:
"""
Expand All @@ -269,6 +296,10 @@ def sample(self, function=None) -> None:
if not self.running:
return
when = log.log.now()
if when - self.lastSample < config.sampleDelay:
return
self.sampleCount += 1
self.lastSample = when
frames = sys._current_frames()
for threadId, frame in frames.items():
if threadId != self.ident:
Expand All @@ -277,6 +308,7 @@ def sample(self, function=None) -> None:
if threadId not in frames:
self.merge(threadId, Stack(log.log.now(), threadId))
del self.stacks[threadId]
self.generateStatus(when)

def getStack(self, when, threadId, frame, function):
"""
Expand Down Expand Up @@ -443,6 +475,8 @@ def showStats(self):
percentage = self.gc_info["duration"] / now * 100
average = self.gc_info["duration"] / self.gc_info["count"] if self.gc_info["count"] else 0.0
(error if leaks else debug)(f"""
# General Statistics
- Performed {self.sampleCount} samples
# GC Statistics
GC ran {count} for {duration:.3f}s ({percentage:.1f}% of {now:.3f}s), averaging {average:.3f}s per collection.
In total, {self.gc_info["collected"]:,} objects were collected.
Expand Down

0 comments on commit 8bb34c6

Please sign in to comment.