@@ -151,6 +151,45 @@ def create_threads(n):
151151 time.sleep(0.05)
152152'''
153153
154+ ASYNC_CODE = '''\
155+ import asyncio
156+ import contextlib
157+ import math
158+
159+ def compute_slice(seed):
160+ result = 0.0
161+ for i in range(2000):
162+ result += math.sin(seed + i) * math.sqrt(i + 1)
163+ return result
164+
165+ async def leaf_task(seed):
166+ total = 0.0
167+ while True:
168+ total += compute_slice(seed)
169+ await asyncio.sleep(0)
170+
171+ async def parent_task(seed):
172+ child = asyncio.create_task(leaf_task(seed + 1000), name=f"leaf-{seed}")
173+ try:
174+ while True:
175+ compute_slice(seed)
176+ await asyncio.sleep(0.001)
177+ finally:
178+ child.cancel()
179+ with contextlib.suppress(asyncio.CancelledError):
180+ await child
181+
182+ async def main():
183+ tasks = [
184+ asyncio.create_task(parent_task(i), name=f"parent-{i}")
185+ for i in range(8)
186+ ]
187+ await asyncio.gather(*tasks)
188+
189+ if __name__ == "__main__":
190+ asyncio.run(main())
191+ '''
192+
154193CODE_EXAMPLES = {
155194 "basic" : {
156195 "code" : CODE ,
@@ -164,22 +203,44 @@ def create_threads(n):
164203 "code" : CODE_WITH_TONS_OF_THREADS ,
165204 "description" : "Tons of threads doing mixed CPU/IO work" ,
166205 },
206+ "asyncio" : {
207+ "code" : ASYNC_CODE ,
208+ "description" : "Asyncio tasks with active and awaited coroutine chains" ,
209+ },
210+ }
211+
212+ OPERATIONS = {
213+ "stack_trace" : {
214+ "method" : "get_stack_trace" ,
215+ "label" : "get_stack_trace()" ,
216+ },
217+ "async_stack_trace" : {
218+ "method" : "get_async_stack_trace" ,
219+ "label" : "get_async_stack_trace()" ,
220+ },
221+ "all_awaited_by" : {
222+ "method" : "get_all_awaited_by" ,
223+ "label" : "get_all_awaited_by()" ,
224+ },
167225}
168226
169227
170- def benchmark (unwinder , duration_seconds = 10 , blocking = False ):
228+ def benchmark (unwinder , duration_seconds = 10 , blocking = False , operation = "stack_trace" ):
171229 """Benchmark mode - measure raw sampling speed for specified duration"""
172230 sample_count = 0
173231 fail_count = 0
174232 total_work_time = 0.0
175233 start_time = time .perf_counter ()
176234 end_time = start_time + duration_seconds
177235 total_attempts = 0
236+ operation_info = OPERATIONS [operation ]
237+ operation_method = getattr (unwinder , operation_info ["method" ])
178238
179239 colors = get_colors (can_colorize ())
180240
181241 print (
182- f"{ colors .BOLD_BLUE } Benchmarking sampling speed for { duration_seconds } seconds...{ colors .RESET } "
242+ f"{ colors .BOLD_BLUE } Benchmarking { operation_info ['label' ]} speed "
243+ f"for { duration_seconds } seconds...{ colors .RESET } "
183244 )
184245
185246 try :
@@ -190,8 +251,8 @@ def benchmark(unwinder, duration_seconds=10, blocking=False):
190251 if blocking :
191252 unwinder .pause_threads ()
192253 try :
193- stack_trace = unwinder . get_stack_trace ()
194- if stack_trace :
254+ sample = operation_method ()
255+ if sample :
195256 sample_count += 1
196257 finally :
197258 if blocking :
@@ -239,6 +300,7 @@ def benchmark(unwinder, duration_seconds=10, blocking=False):
239300 (sample_count / total_attempts ) * 100 if total_attempts > 0 else 0
240301 ),
241302 "total_work_time" : total_work_time ,
303+ "operation" : operation_info ["label" ],
242304 "avg_work_time_us" : (
243305 (total_work_time / total_attempts ) * 1e6 if total_attempts > 0 else 0
244306 ),
@@ -252,7 +314,7 @@ def print_benchmark_results(results):
252314 colors = get_colors (can_colorize ())
253315
254316 print (f"\n { colors .BOLD_GREEN } { '=' * 60 } { colors .RESET } " )
255- print (f"{ colors .BOLD_GREEN } get_stack_trace() Benchmark Results{ colors .RESET } " )
317+ print (f"{ colors .BOLD_GREEN } { results [ 'operation' ] } Benchmark Results{ colors .RESET } " )
256318 print (f"{ colors .BOLD_GREEN } { '=' * 60 } { colors .RESET } " )
257319
258320 # Basic statistics
@@ -329,6 +391,8 @@ def parse_arguments():
329391 %(prog)s -d 60 # Run basic benchmark for 60 seconds
330392 %(prog)s --code deep_static # Run deep static call stack benchmark
331393 %(prog)s --code deep_static -d 30 # Run deep static benchmark for 30 seconds
394+ %(prog)s --operation async_stack_trace
395+ %(prog)s --operation all_awaited_by
332396
333397Available code examples:
334398{ examples_desc }
@@ -348,8 +412,15 @@ def parse_arguments():
348412 "--code" ,
349413 "-c" ,
350414 choices = list (CODE_EXAMPLES .keys ()),
351- default = "basic" ,
352- help = "Code example to benchmark (default: basic)" ,
415+ default = None ,
416+ help = "Code example to benchmark (default: basic, or asyncio for async operations)" ,
417+ )
418+
419+ parser .add_argument (
420+ "--operation" ,
421+ choices = list (OPERATIONS .keys ()),
422+ default = "stack_trace" ,
423+ help = "Remote unwinder operation to benchmark (default: stack_trace)" ,
353424 )
354425
355426 parser .add_argument (
@@ -365,7 +436,10 @@ def parse_arguments():
365436 help = "Stop all threads before sampling for consistent snapshots" ,
366437 )
367438
368- return parser .parse_args ()
439+ args = parser .parse_args ()
440+ if args .code is None :
441+ args .code = "asyncio" if args .operation != "stack_trace" else "basic"
442+ return args
369443
370444
371445def create_target_process (temp_file , code_example = "basic" ):
@@ -420,6 +494,9 @@ def main():
420494 print (
421495 f"{ colors .CYAN } Benchmark Duration:{ colors .RESET } { colors .YELLOW } { args .duration } { colors .RESET } seconds"
422496 )
497+ print (
498+ f"{ colors .CYAN } Operation:{ colors .RESET } { colors .GREEN } { OPERATIONS [args .operation ]['label' ]} { colors .RESET } "
499+ )
423500 print (
424501 f"{ colors .CYAN } Blocking Mode:{ colors .RESET } { colors .GREEN if args .blocking else colors .YELLOW } { 'enabled' if args .blocking else 'disabled' } { colors .RESET } "
425502 )
@@ -451,7 +528,12 @@ def main():
451528 unwinder = _remote_debugging .RemoteUnwinder (
452529 process .pid , cache_frames = True , ** kwargs
453530 )
454- results = benchmark (unwinder , duration_seconds = args .duration , blocking = args .blocking )
531+ results = benchmark (
532+ unwinder ,
533+ duration_seconds = args .duration ,
534+ blocking = args .blocking ,
535+ operation = args .operation ,
536+ )
455537 finally :
456538 cleanup_process (process , temp_file_path )
457539
0 commit comments