@@ -23,6 +23,49 @@ def _format_db_error(combined_error: str, database_path: str) -> str:
2323 )
2424
2525
26+ def _stream_subprocess (
27+ cmd : List [str ],
28+ log_file : Path ,
29+ timeout : int ,
30+ ) -> Tuple [int , str , str ]:
31+ """执行子进程,实时将 stdout/stderr tee 到控制台与日志文件,返回 (returncode, stdout, stderr)。
32+
33+ 多个 CodeQL 执行函数共用此逻辑(此前每处都内联了一份相同的读取线程)。
34+ 与原内联实现行为一致:subprocess.TimeoutExpired / FileNotFoundError 会向外传播,
35+ 由调用方的 try/except 处理。
36+ """
37+ import threading
38+
39+ process = subprocess .Popen (
40+ cmd ,
41+ stdout = subprocess .PIPE ,
42+ stderr = subprocess .PIPE ,
43+ text = True ,
44+ bufsize = 1 ,
45+ )
46+
47+ stdout_lines : List [str ] = []
48+ stderr_lines : List [str ] = []
49+
50+ def _pump (stream , sink : List [str ], out ) -> None :
51+ for line in stream :
52+ sink .append (line )
53+ print (line , end = '' , file = out , flush = True )
54+ with open (log_file , 'a' , encoding = 'utf-8' ) as f :
55+ f .write (line )
56+
57+ stdout_thread = threading .Thread (target = _pump , args = (process .stdout , stdout_lines , sys .stdout ))
58+ stderr_thread = threading .Thread (target = _pump , args = (process .stderr , stderr_lines , sys .stderr ))
59+ stdout_thread .start ()
60+ stderr_thread .start ()
61+
62+ returncode = process .wait (timeout = timeout )
63+ stdout_thread .join ()
64+ stderr_thread .join ()
65+
66+ return returncode , '' .join (stdout_lines ), '' .join (stderr_lines )
67+
68+
2669# 功能:
2770def detect_language_from_query (query_content : str ) -> str :
2871 content = (query_content or '' ).lower ()
@@ -487,51 +530,17 @@ def run_simple_query(
487530 start_time = time .time ()
488531
489532 # 使用`codeql query run`执行简单查询,添加实时日志输出
490- import threading
491- process = subprocess .Popen (
533+ returncode , stdout , stderr = _stream_subprocess (
492534 [
493535 'codeql' , 'query' , 'run' ,
494536 str (query_file ),
495537 '--database' , resolved_database_path ,
496538 f'--output={ str (bqrs_path )} ' ,
497539 ],
498- stdout = subprocess .PIPE ,
499- stderr = subprocess .PIPE ,
500- text = True ,
501- bufsize = 1 ,
540+ log_file ,
541+ timeout = 600 ,
502542 )
503543
504- stdout_lines = []
505- stderr_lines = []
506-
507- # 实时读取并输出 stdout 和 stderr
508- def read_stdout ():
509- for line in process .stdout :
510- stdout_lines .append (line )
511- print (line , end = '' , flush = True )
512- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
513- f .write (line )
514-
515- def read_stderr ():
516- for line in process .stderr :
517- stderr_lines .append (line )
518- print (line , end = '' , file = sys .stderr , flush = True )
519- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
520- f .write (line )
521-
522- stdout_thread = threading .Thread (target = read_stdout )
523- stderr_thread = threading .Thread (target = read_stderr )
524- stdout_thread .start ()
525- stderr_thread .start ()
526-
527- # 等待进程完成
528- returncode = process .wait (timeout = 600 )
529- stdout_thread .join ()
530- stderr_thread .join ()
531-
532- stdout = '' .join (stdout_lines )
533- stderr = '' .join (stderr_lines )
534-
535544 # 计算并显示实际执行时间
536545 execution_time = time .time () - start_time
537546 print (f"✅ CodeQL简单查询执行完成! 用时: { execution_time :.2f} 秒" )
@@ -569,49 +578,16 @@ def read_stderr():
569578 }
570579
571580 # 解码BQRS文件为JSON格式,以便detect_breakpoints方法能够正确解析
572- decode_process = subprocess . Popen (
581+ decode_returncode , decode_stdout , decode_stderr = _stream_subprocess (
573582 [
574583 'codeql' , 'bqrs' , 'decode' ,
575584 '--format=json' ,
576585 str (bqrs_path ),
577586 ],
578- stdout = subprocess .PIPE ,
579- stderr = subprocess .PIPE ,
580- text = True ,
581- bufsize = 1 ,
587+ log_file ,
588+ timeout = 300 ,
582589 )
583590
584- decode_stdout_lines = []
585- decode_stderr_lines = []
586-
587- # 实时读取并输出解码过程的 stdout 和 stderr
588- def read_decode_stdout ():
589- for line in decode_process .stdout :
590- decode_stdout_lines .append (line )
591- print (line , end = '' , flush = True )
592- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
593- f .write (line )
594-
595- def read_decode_stderr ():
596- for line in decode_process .stderr :
597- decode_stderr_lines .append (line )
598- print (line , end = '' , file = sys .stderr , flush = True )
599- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
600- f .write (line )
601-
602- decode_stdout_thread = threading .Thread (target = read_decode_stdout )
603- decode_stderr_thread = threading .Thread (target = read_decode_stderr )
604- decode_stdout_thread .start ()
605- decode_stderr_thread .start ()
606-
607- # 等待解码进程完成
608- decode_returncode = decode_process .wait (timeout = 300 )
609- decode_stdout_thread .join ()
610- decode_stderr_thread .join ()
611-
612- decode_stdout = '' .join (decode_stdout_lines )
613- decode_stderr = '' .join (decode_stderr_lines )
614-
615591 if decode_returncode != 0 :
616592 # 合并stderr和stdout以捕获所有错误信息
617593 error_output = []
@@ -766,8 +742,7 @@ def execute_codeql_query(
766742 f .write (f"{ '=' * 80 } \n " )
767743
768744 # 使用`codeql database analyze`执行查询,并使用SARIF v2.1.0输出,添加 --verbose 参数
769- import threading
770- process = subprocess .Popen (
745+ returncode , stdout , stderr = _stream_subprocess (
771746 [
772747 'codeql' , 'database' , 'analyze' ,
773748 '--verbose' ,
@@ -777,43 +752,10 @@ def execute_codeql_query(
777752 '--format=sarifv2.1.0' ,
778753 f'--output={ str (sarif_path )} ' ,
779754 ],
780- stdout = subprocess .PIPE ,
781- stderr = subprocess .PIPE ,
782- text = True ,
783- bufsize = 1 ,
755+ log_file ,
756+ timeout = 600 ,
784757 )
785758
786- stdout_lines = []
787- stderr_lines = []
788-
789- # 实时读取并输出 stdout 和 stderr
790- def read_stdout ():
791- for line in process .stdout :
792- stdout_lines .append (line )
793- print (line , end = '' , flush = True )
794- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
795- f .write (line )
796-
797- def read_stderr ():
798- for line in process .stderr :
799- stderr_lines .append (line )
800- print (line , end = '' , file = sys .stderr , flush = True )
801- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
802- f .write (line )
803-
804- stdout_thread = threading .Thread (target = read_stdout )
805- stderr_thread = threading .Thread (target = read_stderr )
806- stdout_thread .start ()
807- stderr_thread .start ()
808-
809- # 等待进程完成
810- returncode = process .wait (timeout = 600 )
811- stdout_thread .join ()
812- stderr_thread .join ()
813-
814- stdout = '' .join (stdout_lines )
815- stderr = '' .join (stderr_lines )
816-
817759 # 计算并显示实际执行时间
818760 execution_time = time .time () - start_time
819761 print (f"✅ CodeQL查询执行完成! 用时: { execution_time :.2f} 秒" )
@@ -937,52 +879,18 @@ def run_query_and_decode_to_text(
937879 f .write (f"{ '=' * 80 } \n " )
938880
939881 # 执行 codeql query run,添加 --verbose 参数
940- import threading
941- process = subprocess .Popen (
882+ returncode , stdout , stderr = _stream_subprocess (
942883 [
943884 'codeql' , 'query' , 'run' ,
944885 '--verbose' ,
945886 str (query_file ),
946887 '--database' , resolved_database_path ,
947888 f'--output={ str (bqrs_path )} ' ,
948889 ],
949- stdout = subprocess .PIPE ,
950- stderr = subprocess .PIPE ,
951- text = True ,
952- bufsize = 1 ,
890+ log_file ,
891+ timeout = 600 ,
953892 )
954893
955- stdout_lines = []
956- stderr_lines = []
957-
958- # 实时读取并输出 stdout 和 stderr
959- def read_stdout ():
960- for line in process .stdout :
961- stdout_lines .append (line )
962- print (line , end = '' , flush = True )
963- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
964- f .write (line )
965-
966- def read_stderr ():
967- for line in process .stderr :
968- stderr_lines .append (line )
969- print (line , end = '' , file = sys .stderr , flush = True )
970- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
971- f .write (line )
972-
973- stdout_thread = threading .Thread (target = read_stdout )
974- stderr_thread = threading .Thread (target = read_stderr )
975- stdout_thread .start ()
976- stderr_thread .start ()
977-
978- # 等待进程完成
979- returncode = process .wait (timeout = 600 )
980- stdout_thread .join ()
981- stderr_thread .join ()
982-
983- stdout = '' .join (stdout_lines )
984- stderr = '' .join (stderr_lines )
985-
986894 if returncode != 0 :
987895 parts : List [str ] = []
988896 if stderr :
@@ -1014,50 +922,17 @@ def read_stderr():
1014922 f .write (f"[{ log_timestamp } ] 执行 codeql bqrs decode\n " )
1015923 f .write (f"{ '=' * 80 } \n " )
1016924
1017- decode_process = subprocess . Popen (
925+ decode_returncode , decode_stdout , decode_stderr = _stream_subprocess (
1018926 [
1019927 'codeql' , 'bqrs' , 'decode' ,
1020928 '--verbose' ,
1021929 '--format=table' ,
1022930 str (bqrs_path ),
1023931 ],
1024- stdout = subprocess .PIPE ,
1025- stderr = subprocess .PIPE ,
1026- text = True ,
1027- bufsize = 1 ,
932+ log_file ,
933+ timeout = 300 ,
1028934 )
1029935
1030- decode_stdout_lines = []
1031- decode_stderr_lines = []
1032-
1033- # 实时读取并输出 stdout 和 stderr
1034- def read_decode_stdout ():
1035- for line in decode_process .stdout :
1036- decode_stdout_lines .append (line )
1037- print (line , end = '' , flush = True )
1038- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
1039- f .write (line )
1040-
1041- def read_decode_stderr ():
1042- for line in decode_process .stderr :
1043- decode_stderr_lines .append (line )
1044- print (line , end = '' , file = sys .stderr , flush = True )
1045- with open (log_file , 'a' , encoding = 'utf-8' ) as f :
1046- f .write (line )
1047-
1048- decode_stdout_thread = threading .Thread (target = read_decode_stdout )
1049- decode_stderr_thread = threading .Thread (target = read_decode_stderr )
1050- decode_stdout_thread .start ()
1051- decode_stderr_thread .start ()
1052-
1053- # 等待进程完成
1054- decode_returncode = decode_process .wait (timeout = 300 )
1055- decode_stdout_thread .join ()
1056- decode_stderr_thread .join ()
1057-
1058- decode_stdout = '' .join (decode_stdout_lines )
1059- decode_stderr = '' .join (decode_stderr_lines )
1060-
1061936 if decode_returncode != 0 :
1062937 parts : List [str ] = []
1063938 if decode_stderr :
0 commit comments