@@ -426,3 +426,128 @@ def test_circular_dependency_fix(self):
426426 # These calls should not trigger circular dependency
427427 pc = frame .GetPC ()
428428 self .assertNotEqual (pc , 0 , f"Frame { i } should have valid PC" )
429+
430+ def test_python_source_frames (self ):
431+ """Test that frames can point to Python source files and display properly."""
432+ self .build ()
433+ target , process , thread , bkpt = lldbutil .run_to_source_breakpoint (
434+ self , "Break here" , lldb .SBFileSpec (self .source ), only_one_thread = False
435+ )
436+
437+ # Get original frame count
438+ original_frame_count = thread .GetNumFrames ()
439+ self .assertGreaterEqual (
440+ original_frame_count , 2 , "Should have at least 2 real frames"
441+ )
442+
443+ # Import the provider
444+ script_path = os .path .join (self .getSourceDir (), "test_frame_providers.py" )
445+ self .runCmd ("command script import " + script_path )
446+
447+ # Register the PythonSourceFrameProvider
448+ error = lldb .SBError ()
449+ provider_id = target .RegisterScriptedFrameProvider (
450+ "test_frame_providers.PythonSourceFrameProvider" ,
451+ lldb .SBStructuredData (),
452+ error ,
453+ )
454+ self .assertTrue (error .Success (), f"Failed to register provider: { error } " )
455+ self .assertNotEqual (provider_id , 0 , "Provider ID should be non-zero" )
456+
457+ # Verify we have 3 more frames (Python frames)
458+ new_frame_count = thread .GetNumFrames ()
459+ self .assertEqual (
460+ new_frame_count ,
461+ original_frame_count + 3 ,
462+ "Should have original frames + 3 Python frames" ,
463+ )
464+
465+ # Verify first three frames are Python source frames
466+ frame0 = thread .GetFrameAtIndex (0 )
467+ self .assertIsNotNone (frame0 )
468+ self .assertEqual (
469+ frame0 .GetFunctionName (),
470+ "compute_fibonacci" ,
471+ "First frame should be compute_fibonacci" ,
472+ )
473+ self .assertTrue (frame0 .IsSynthetic (), "Frame should be marked as synthetic" )
474+ # PC-less frames should show invalid address
475+ self .assertEqual (
476+ frame0 .GetPC (),
477+ lldb .LLDB_INVALID_ADDRESS ,
478+ "PC-less frame should have LLDB_INVALID_ADDRESS" ,
479+ )
480+
481+ frame1 = thread .GetFrameAtIndex (1 )
482+ self .assertIsNotNone (frame1 )
483+ self .assertEqual (
484+ frame1 .GetFunctionName (),
485+ "process_data" ,
486+ "Second frame should be process_data" ,
487+ )
488+ self .assertTrue (frame1 .IsSynthetic (), "Frame should be marked as synthetic" )
489+
490+ frame2 = thread .GetFrameAtIndex (2 )
491+ self .assertIsNotNone (frame2 )
492+ self .assertEqual (
493+ frame2 .GetFunctionName (), "main" , "Third frame should be main"
494+ )
495+ self .assertTrue (frame2 .IsSynthetic (), "Frame should be marked as synthetic" )
496+
497+ # Verify line entry information is present
498+ line_entry0 = frame0 .GetLineEntry ()
499+ self .assertTrue (
500+ line_entry0 .IsValid (), "Frame 0 should have a valid line entry"
501+ )
502+ self .assertEqual (
503+ line_entry0 .GetLine (), 7 , "Frame 0 should point to line 7"
504+ )
505+ file_spec0 = line_entry0 .GetFileSpec ()
506+ self .assertTrue (file_spec0 .IsValid (), "Frame 0 should have valid file spec" )
507+ self .assertEqual (
508+ file_spec0 .GetFilename (),
509+ "python_helper.py" ,
510+ "Frame 0 should point to python_helper.py" ,
511+ )
512+
513+ line_entry1 = frame1 .GetLineEntry ()
514+ self .assertTrue (
515+ line_entry1 .IsValid (), "Frame 1 should have a valid line entry"
516+ )
517+ self .assertEqual (
518+ line_entry1 .GetLine (), 16 , "Frame 1 should point to line 16"
519+ )
520+
521+ line_entry2 = frame2 .GetLineEntry ()
522+ self .assertTrue (
523+ line_entry2 .IsValid (), "Frame 2 should have a valid line entry"
524+ )
525+ self .assertEqual (
526+ line_entry2 .GetLine (), 27 , "Frame 2 should point to line 27"
527+ )
528+
529+ # Verify the frames display properly in backtrace
530+ # This tests that PC-less frames don't show 0xffffffffffffffff
531+ self .runCmd ("bt" )
532+ output = self .res .GetOutput ()
533+
534+ # Should show function names
535+ self .assertIn ("compute_fibonacci" , output )
536+ self .assertIn ("process_data" , output )
537+ self .assertIn ("main" , output )
538+
539+ # Should show Python file
540+ self .assertIn ("python_helper.py" , output )
541+
542+ # Should show line numbers
543+ self .assertIn (":7" , output ) # compute_fibonacci line
544+ self .assertIn (":16" , output ) # process_data line
545+ self .assertIn (":27" , output ) # main line
546+
547+ # Should NOT show invalid address (0xffffffffffffffff)
548+ self .assertNotIn ("0xffffffffffffffff" , output .lower ())
549+
550+ # Verify frame 3 is the original real frame 0
551+ frame3 = thread .GetFrameAtIndex (3 )
552+ self .assertIsNotNone (frame3 )
553+ self .assertIn ("thread_func" , frame3 .GetFunctionName ())
0 commit comments