<think>我们需要完成一个复杂的脚本,主要功能如下:
1. 创建项目目录和result目录。
2. 检查ref和dut目录下是否存在bugreport文件(可能是压缩包或目录?),没有则报错。
3. 在result目录中创建一个以项目名和当日日期命名的Excel工作簿(.xlsx)。
4. 分别处理ref和dut目录下的bugreport文件,根据平台(MTK、QCOM、SPRD)使用对应的关键字文件进行搜索,将结果记录到Excel的指定工作表。
5. 根据dut和ref是否为同一平台,决定如何汇总数据到第一张表,并计算时间差和差值。
6. 特殊处理几个关键字(如"Booting Linux on physical"等),计算时间差(单位为毫秒)。
7. 将第六列中大于300的正值标红。
步骤:
1. 解析命令行参数:项目名,ref平台,dut平台(使用argparse)。
2. 创建目录结构。
3. 查找ref和dut目录下的bugreport文件(我们假设bugreport文件可能是zip、tar.gz等压缩格式,也可能是目录。但题目要求解压,所以如果是压缩文件则解压,然后查找以bugreport开头的文件。注意:解压后可能是一个目录,里面包含文件,也可能直接是文件。我们这里简化:先解压,然后在解压后的目录中递归查找以"bugreport"开头的文件,取第一个?或者要求只有一个?)
4. 根据平台读取关键字文件(mtk_keyword.txt, qcom_keyword.txt, sprd_keyword.txt, common_keyword.txt),每个关键字一行。
5. 处理每个bugreport文件(解压后):
- 解压(如果已经是目录则不需要解压,如果是压缩文件则解压到临时目录?)
- 在解压后的目录中搜索以"bugreport"开头的文件(可能是文本文件),我们假设这个文件就是我们要分析的日志文件。
6. 对于每个日志文件,按照关键字列表顺序搜索,记录每个关键字在日志中第一次出现的时间(转换为时间戳,以便计算差值)。注意:特殊关键字需要特殊处理(如记录时间差)。
7. 将记录的数据写入Excel的对应工作表(ref的写入第三张表,dut的写入第二张表)。
8. 创建第一张表,根据平台是否相同选择不同的关键字列表(相同则用对应平台的关键字,不同则用common_keyword),然后从第二张表和第三张表中提取数据(按关键字匹配),并计算各阶段时间差和总差值。
9. 将第一张表的第六列(差值列)中大于300的正值标红。
注意:
- 时间格式:日志中的时间格式为 "MM-DD HH:mm:ss.SSS",我们需要将其转换为时间戳(注意年份,由于bugreport不包含年份,我们可以假设为当前年?但要注意跨年问题。由于是启动日志,我们可以认为在收集日志的当天,所以用当前年。但注意:如果日志是去年的,而今年当前日期已经跨年,则会有问题。但题目没有说明,我们假设为当前年)。
- 特殊关键字的处理:
* "Booting Linux on physical": 如果找到,记录其时间戳(记为T0);如果没找到,则T0=0(注意:后续关键字计算相对时间时需要用到T0,所以没找到T0则后续关键字也无法计算相对时间?但题目要求即使没找到也要记录0,所以对于第一个关键字,我们直接记录0(如果没找到)或者时间戳(如果找到,但后续计算相对时间时,我们使用这个时间戳作为基准)。
* "init first stage started!":记录与T0的时间差(毫秒)。所以需要先找到T0,然后计算当前关键字的时间戳与T0的差值。如果T0没找到(为0),那么这个关键字即使找到了也不能计算?但题目要求:如果没找到T0,那么这个关键字也记录0?还是记录与T0的差值(由于T0为0,那么差值就是当前时间戳?)——根据题意:如果没找到T0,则这个关键字记录0。同样,如果找到了T0,但没找到这个关键字,也记录0。
* "init second stage started!":记录与上一个关键字(init first stage started!)的时间差。所以需要先找到第一个阶段的时间(T1),然后计算当前关键字时间戳与T1的差值。同样,如果T1没找到(为0)或者当前关键字没找到,则记录0。
因此,我们需要一个函数来处理一个日志文件,并按照给定的关键字列表提取时间信息,返回一个字典(关键字: 时间值(毫秒数,对于非特殊关键字,我们记录的是绝对时间戳(毫秒)?但是题目要求记录的是相对时间差,而且后面还要做差值计算。然而,在写入第二张表和第三张表时,对于非特殊关键字,我们记录的是从日志中提取的时间戳(转换为从0开始的相对时间?)还是直接记录时间戳?)
重新阅读要求:
对于每个关键字,在日志中搜索并记录一个时间值。对于特殊关键字,记录的是相对时间差(相对于前一个事件),而对于普通关键字,记录的是绝对时间?但是后面在第四、第五列又要求计算各阶段数据(相邻关键字的差值)。所以,实际上,我们应该将所有关键字都记录为相对于第一个事件(T0)的毫秒数。这样,在第四列和第五列,我们就可以通过相邻两个关键字的差值来计算各阶段时间。
但是,题目要求:
如果关键字是“Booting Linux on physical”,不管有没有找到都记录为了0(如没找到就记录0)——这个0表示时间0点?
如果关键字是“init first stage started!”,记录为与“Booting Linux on physical”的log时间差值(如果找到了“Booting Linux on physical”和当前关键字,则计算差值;如果其中一个没找到,则当前关键字记录0)。
同理,“init second stage started!”记录为与“init first stage started!”的差值。
然而,在后面的步骤6中,要求计算各阶段数据(相邻行的差值),所以实际上我们在记录每个关键字的时候,应该记录的是相对于T0的绝对时间(毫秒)。这样,相邻两个关键字的差值就是阶段时间。
所以,我们这样处理:
1. 首先,在整个日志中,我们找到“Booting Linux on physical”的时间(如果有),作为基准时间T0(毫秒时间戳)。
2. 然后,对于每个关键字(包括特殊关键字和普通关键字),我们记录的是:如果该关键字在日志中出现了,则记录其时间戳减去T0(如果T0不存在,则记录0?但是题目要求:如果没找到T0,那么所有关键字都记录0?)——不,题目只要求特殊关键字按照特定方式处理,普通关键字呢?题目没有明确,但为了统一,我们这样规定:
- 对于关键字列表中的每一个关键字,我们都会在日志中查找其第一次出现的时间戳。
- 如果找到了“Booting Linux on physical”(T0),那么对于每个关键字,我们记录: (当前关键字的时间戳 - T0) 的毫秒数。
- 如果没有找到T0,那么所有关键字都记录0(即使它们出现了)?这似乎不合理。但题目要求:对于“Booting Linux on physical”如果没有找到就记录0,而对于其他关键字,如果没找到当然也记录0。但是,如果T0没找到,但其他关键字出现了,我们是否应该记录它们相对于日志开始的时间?题目没有说明,我们按照题目要求:对于特殊关键字,按照特定规则(依赖于T0)记录,而对于普通关键字,我们直接记录相对于T0的差值(如果T0不存在,则无法计算,所以记录0)。
但是,注意步骤7中,要求计算相邻阶段的时间差(第四列和第五列),所以如果T0不存在,那么所有关键字记录0,那么相邻阶段差值都是0,最后差值也是0。
因此,我们可以这样设计函数:
def extract_times(log_file, keywords, platform):
# 步骤1:读取日志文件内容
# 步骤2:在整个日志中搜索"Booting Linux on physical"(注意:这个关键字可能不在关键字列表中,但它是基准。所以我们需要单独处理,即使它不在关键字列表里?)——但是,注意关键字列表里应该包含它吗?根据要求,关键字列表是从文件中读取的,所以我们需要确保在平台关键字文件中包含这个关键字吗?题目没有说,但根据步骤7的描述,这个关键字是必须的。所以,我们要求每个平台的关键字文件都必须包含这个关键字?但题目说“如果关键字是...”,说明这个关键字是硬编码的?还是说它也在关键字文件中?
重新阅读:步骤4、5中,平台关键字文件中应该包含这个关键字,因为它是按关键字文件中的关键字来搜索的。所以,我们不需要单独处理,它会作为关键字列表中的一个。
但是,在提取时间时,我们需要知道T0(即“Booting Linux on physical”的时间),所以我们需要先找到这个关键字的时间,然后对于每个关键字,计算相对于T0的时间差(毫秒)。所以,在关键字列表中,这个关键字必须存在,并且我们将其作为基准。
因此,函数流程:
1. 读取日志文件,按行处理。
2. 创建一个字典来存储每个关键字第一次出现的时间戳(毫秒时间戳,从日志时间字符串转换而来,注意我们假设年份为当前年)。
3. 对于每一行,检查是否包含关键字列表中的任何一个关键字(注意:一个关键字只记录第一次出现)。
4. 但是,我们需要先确定T0(即“Booting Linux on physical”)的时间戳,然后才能计算其他关键字相对于T0的差值。所以,我们可以先找到所有关键字的时间戳(绝对时间),然后再统一计算相对时间。
具体步骤:
a) 初始化一个字典:keyword_times = {keyword: None for keyword in keywords}
b) 遍历日志每一行,对于每个关键字,如果还没有记录过,且当前行包含该关键字,则提取该行的时间字符串,转换为时间戳(毫秒)。
c) 然后,检查T0(即关键字“Booting Linux on physical”)是否被找到。如果找到了,则T0_time = keyword_times["Booting Linux on physical"];如果没有找到,则T0_time = None。
d) 然后,对于每个关键字,计算其相对于T0的差值:
如果T0_time不为None,并且当前关键字的时间戳不为None,则记录 (当前关键字时间戳 - T0_time) 的毫秒数。
否则,记录0(表示未找到或无法计算)。
e) 但是,注意:对于“init first stage started!”和“init second stage started!”,题目要求记录的是相对于前一个事件的时间差,而不是相对于T0?但是,步骤7中又要求计算各阶段数据(相邻关键字的差值),所以如果我们记录的是相对于T0的绝对时间,那么相邻关键字的差值就是阶段时间。
然而,题目要求:
对于“init first stage started!”:记录为与“Booting Linux on physical”的差值。
对于“init second stage started!”:记录为与“init first stage started!”的差值。
这似乎和我们的方案(所有关键字都记录相对于T0的绝对时间)不一致。但是,如果我们记录的是相对于T0的绝对时间,那么:
“Booting Linux on physical”的相对时间就是0(因为自己减自己)。
“init first stage started!”的相对时间就是它相对于T0的时间差(记为T1)。
“init second stage started!”的相对时间就是它相对于T0的时间差(记为T2)。
那么,相邻阶段:
T0到T1:阶段时间 = T1 - 0 = T1
T1到T2:阶段时间 = T2 - T1
所以,在后续计算各阶段数据(第四列和第五列)时,我们可以通过相邻行的差值得到阶段时间。
因此,我们不需要在提取数据的时候计算阶段时间,而是在写入Excel表的第一张表时,通过相邻行的差值来计算(步骤6要求)。
所以,我们统一记录每个关键字相对于T0的绝对时间(毫秒)。如果T0不存在,则所有关键字都记录0(表示无效)。
但是,注意:在步骤7中,要求特殊关键字的处理方式,实际上与我们统一记录相对于T0的绝对时间是一致的。所以,我们按统一方式处理即可。
所以,提取函数返回一个字典:{keyword: relative_time},其中relative_time是相对于T0的毫秒数(如果T0不存在,则为0)。
8. 将提取的数据写入Excel:
- 第二张表(dut_bootimg):关键字列(按顺序),第二列为提取的时间(毫秒)
- 第三张表(ref_bootimg):同样
- 第一张表(未命名,我们命名为Summary):
如果dut和ref平台相同,则使用对应平台的关键字列表(如mtk_keyword)
如果不同,则使用common_keyword
第一列:关键字(按顺序)
第二列:dut数据(从dut_bootimg表中按关键字取,如果没有则0?)
第三列:ref数据(同理)
第四列:dut各阶段数据(算法:第一行0,第二行=第二行第二列的值 - 第一行第二列的值,以此类推)
第五列:ref各阶段数据(同理)
第六列:dut各阶段数据 - ref各阶段数据(注意:可能是负值,我们只标红大于300的正值)
9. 在写入第一张表的第六列时,对每个单元格判断:如果值>300,则设置单元格字体为红色。
10. 注意:解压操作。我们假设bugreport文件可能是zip、tar.gz等,我们可以使用shutil.unpack_archive?它支持多种格式。但注意:如果bugreport是一个目录,则我们直接使用。
实现步骤:
我们将分步骤实现:
步骤1:解析命令行参数
项目名(必选),ref平台,dut平台,以及ref目录和dut目录(题目要求在当前目录下查找ref和dut目录?)所以我们可以固定为当前目录下的ref和dut目录。
参数设计:
-p, --project: 项目名(必选)
--ref_platform: ref平台(MTK/QCOM/SPRD)
--dut_platform: dut平台
步骤2:创建目录
当前目录下创建项目名目录,然后在项目名目录下创建result目录。
步骤3:检查ref和dut目录
在项目目录下是否有ref和dut目录?题目要求是“脚本目录下查找ref、dut目录”,所以我们的脚本运行时,当前目录就是脚本所在目录。我们在当前目录下查找ref和dut目录。
注意:题目说“ref、dut下没有文件则直接报错”,所以检查这两个目录下是否存在文件(任何文件)?但是题目要求是bugreport文件,所以我们具体检查是否有bugreport文件(可能以bugreport开头)。如果没有,则报错。
步骤4:创建Excel工作簿
使用openpyxl库。
步骤5:处理ref和dut的bugreport文件
对于每个目录(ref和dut):
遍历目录下的文件,找到以"bugreport"开头的文件(可能是压缩文件,也可能是目录)。如果是压缩文件,则解压到一个临时目录(为了避免混乱,我们可以在项目目录下创建一个临时目录,处理完删除)。然后递归查找解压后的目录(或文件)中所有以"bugreport"开头的文件(注意:可能是文本文件)。我们取第一个找到的文件?还是取最新的?题目没有说明,我们假设只有一个。
解压后,我们得到一个日志文件(可能是文本文件),然后根据平台读取关键字文件,提取时间数据。
步骤6:写入Excel
将提取的数据写入第二张表(dut)和第三张表(ref)。
然后根据平台是否相同,选择关键字列表,创建第一张表,并计算阶段时间和差值。
步骤7:保存Excel,并清理临时目录。
由于代码较长,我们分函数实现。
注意:时间转换函数
日志时间格式: "03-04 03:33:57.152"
我们假设年份为当前年,但要注意:如果当前日期(月日)小于日志中的月日,则可能是去年(例如当前是2023年1月1日,而日志是12月31日)。但启动日志通常是最近收集的,我们假设为当年。如果跨年,则可能会出错(时间差为负),但这种情况较少,我们暂不考虑。
函数:parse_log_time(time_str, base_year=None)
如果不指定base_year,则使用当前年。然后转换成时间戳(毫秒)。
但是,注意:日志时间可能没有年份,而且一天内多次收集?我们假设日志收集的日期和运行脚本的日期在同一年。
实现:
由于代码量较大,我们开始编写代码。
我们将使用以下库:
- argparse:解析命令行参数
- os:文件操作
- shutil:解压
- openpyxl:操作Excel
- datetime:处理日期和时间
- tempfile:创建临时目录(可选)
注意:解压时,我们使用shutil.unpack_archive,它可以解压zip, tar等格式。
步骤:
1. 解析参数
2. 创建项目目录和result目录(如果已存在,则跳过或报错?题目没有说,我们直接创建,如果已存在则使用)
3. 检查ref和dut目录:
ref_dir = os.path.join(os.getcwd(), 'ref')
dut_dir = os.path.join(os.getcwd(), 'dut')
然后检查这两个目录下是否存在以'bugreport'开头的文件(或目录)。如果没有,则报错退出。
4. 创建Excel文件:在result目录下,文件名:项目名+当前日期(格式如:ProjectA_20240520.xlsx)
5. 处理ref目录:
- 找到bugreport文件(可能是多个,我们取第一个找到的,或者按创建时间取最新的?题目没有说,我们取第一个)
- 如果是压缩文件(根据扩展名判断?或者直接用unpack_archive尝试解压),则解压到临时目录(临时目录我们创建在项目目录下,以便后续清理)。如果解压成功,则在解压后的目录中递归查找以'bugreport'开头的文本文件(非压缩文件)。如果本来就是文本文件,则直接使用。
- 调用extract_times函数,传入日志文件路径和对应平台的关键字列表(从对应平台关键字文件读取),返回一个字典{keyword: time_in_ms}
6. 同样处理dut目录。
7. 将ref和dut提取的数据分别写入工作簿的第三张表(ref_bootimg)和第二张表(dut_bootimg)
8. 创建第一张表(Summary):
- 判断ref_platform和dut_platform是否相同:
相同:使用对应平台的关键字文件(如mtk_keyword.txt)中的关键字列表
不同:使用common_keyword.txt中的关键字列表
- 然后,对于每个关键字,从dut_bootimg表和ref_bootimg表中获取时间值(如果该关键字在表中没有,则取0)
- 写入第一列:关键字
- 写入第二列:dut数据
- 写入第三列:ref数据
- 计算第四列(dut阶段时间):
第一行:0
第二行:dut_data[1] - dut_data[0] (注意:dut_data是第二列的数据列表)
第三行:dut_data[2] - dut_data[1]
...
- 同样计算第五列(ref阶段时间)
- 第六列:第四列 - 第五列(即dut阶段时间 - ref阶段时间)
9. 遍历第六列,如果值>300,则设置字体为红色(使用openpyxl的样式)
10. 保存工作簿。
注意:我们假设关键字文件位于脚本同目录下(即当前目录),文件名为:mtk_keyword.txt, qcom_keyword.txt, sprd_keyword.txt, common_keyword.txt
由于代码较长,我们逐步实现。
开始编写代码:
</think>
以下是一个完整的Python脚本,满足您的需求。该脚本会创建项目目录、处理bugreport文件、提取关键字数据、生成Excel报告并标注异常值。
```python
import os
import sys
import argparse
import zipfile
import tarfile
import gzip
import shutil
import re
from datetime import datetime
import openpyxl
from openpyxl.styles import PatternFill
from openpyxl.utils import get_column_letter
# 常量定义
KEYWORD_FILES = {
"MTK": "mtk_keyword.txt",
"QCOM": "qcom_keyword.txt",
"SPRD": "sprd_keyword.txt",
"COMMON": "common_keyword.txt"
}
SPECIAL_KEYWORDS = [
"Booting Linux on physical",
"init first stage started!",
"init second stage started!"
]
def create_directories(project_name):
"""创建项目目录和result子目录"""
project_dir = os.path.join(os.getcwd(), project_name)
result_dir = os.path.join(project_dir, "result")
os.makedirs(result_dir, exist_ok=True)
return project_dir, result_dir
def find_bugreport_files(dir_path):
"""在指定目录中查找bugreport文件"""
bugreport_files = []
for root, _, files in os.walk(dir_path):
for file in files:
if file.startswith("bugreport"):
bugreport_files.append(os.path.join(root, file))
return bugreport_files
def extract_archive(file_path, extract_dir):
"""解压各种格式的压缩文件"""
os.makedirs(extract_dir, exist_ok=True)
if file_path.endswith(".zip"):
with zipfile.ZipFile(file_path, 'r') as zip_ref:
zip_ref.extractall(extract_dir)
elif file_path.endswith(".tar.gz") or file_path.endswith(".tgz"):
with tarfile.open(file_path, 'r:gz') as tar_ref:
tar_ref.extractall(extract_dir)
elif file_path.endswith(".gz"):
with gzip.open(file_path, 'rb') as gz_ref:
with open(os.path.join(extract_dir, os.path.basename(file_path)[:-3]), 'wb') as out_file:
shutil.copyfileobj(gz_ref, out_file)
else:
shutil.copy(file_path, extract_dir)
return extract_dir
def find_main_log_file(extract_dir):
"""在解压目录中查找主日志文件"""
for root, _, files in os.walk(extract_dir):
for file in files:
if file.startswith("bugreport") and (file.endswith(".txt") or '.' not in file):
return os.path.join(root, file)
return None
def parse_time(time_str):
"""将日志时间字符串转换为毫秒时间戳(假设为当前年)"""
try:
current_year = datetime.now().year
full_time_str = f"{current_year}-{time_str.strip()}"
dt = datetime.strptime(full_time_str, "%Y-%m-%d %H:%M:%S.%f")
return dt.timestamp() * 1000 # 转换为毫秒
except:
return 0
def extract_keyword_data(log_file, platform):
"""从日志文件中提取关键字数据"""
# 读取关键字文件
keyword_file = KEYWORD_FILES.get(platform.upper())
if not keyword_file or not os.path.exists(keyword_file):
raise FileNotFoundError(f"Keyword file not found: {keyword_file}")
with open(keyword_file, 'r') as f:
keywords = [line.strip() for line in f.readlines() if line.strip()]
# 初始化结果字典
results = {kw: 0 for kw in keywords}
special_times = {kw: 0 for kw in SPECIAL_KEYWORDS}
# 第一次扫描:收集所有特殊关键字的时间
with open(log_file, 'r', errors='ignore') as f:
for line in f:
for kw in SPECIAL_KEYWORDS:
if kw in line and special_times[kw] == 0:
time_match = re.search(r'(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})', line)
if time_match:
special_times[kw] = parse_time(time_match.group(1))
# 计算特殊关键字的相对时间
if special_times["Booting Linux on physical"] > 0:
if special_times["init first stage started!"] > 0:
special_times["init first stage started!"] -= special_times["Booting Linux on physical"]
if special_times["init second stage started!"] > 0 and special_times["init first stage started!"] > 0:
special_times["init second stage started!"] -= (
special_times["init first stage started!"] + special_times["Booting Linux on physical"]
)
# 第二次扫描:处理所有关键字
with open(log_file, 'r', errors='ignore') as f:
for line in f:
for kw in keywords:
if kw in line and results[kw] == 0:
if kw in SPECIAL_KEYWORDS:
results[kw] = special_times.get(kw, 0)
else:
time_match = re.search(r'(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})', line)
if time_match:
log_time = parse_time(time_match.group(1))
results[kw] = log_time - special_times["Booting Linux on physical"] \
if special_times["Booting Linux on physical"] > 0 else 0
return keywords, results
def create_excel_report(result_dir, project_name, dut_data, ref_data, same_platform, dut_platform, ref_platform):
"""创建Excel报告并填充数据"""
# 创建Excel文件
date_str = datetime.now().strftime("%Y%m%d")
excel_file = os.path.join(result_dir, f"{project_name}_{date_str}.xlsx")
wb = openpyxl.Workbook()
# 创建工作表
summary_sheet = wb.active
summary_sheet.title = "Summary"
dut_sheet = wb.create_sheet(title="dut_bootimg")
ref_sheet = wb.create_sheet(title="ref_bootimg")
# 确定用于汇总的关键字
if same_platform:
platform_key = dut_platform.upper()
keyword_file = KEYWORD_FILES.get(platform_key)
else:
keyword_file = KEYWORD_FILES["COMMON"]
with open(keyword_file, 'r') as f:
summary_keywords = [line.strip() for line in f.readlines() if line.strip()]
# 填充DUT表
dut_sheet.append(["Keyword", "Time (ms)"])
for kw in dut_data["keywords"]:
dut_sheet.append([kw, dut_data["results"].get(kw, 0)])
# 填充REF表
ref_sheet.append(["Keyword", "Time (ms)"])
for kw in ref_data["keywords"]:
ref_sheet.append([kw, ref_data["results"].get(kw, 0)])
# 填充汇总表
summary_sheet.append(["Keyword", "DUT (ms)", "REF (ms)", "DUT Phase", "REF Phase", "Difference"])
dut_values = [dut_data["results"].get(kw, 0) for kw in summary_keywords]
ref_values = [ref_data["results"].get(kw, 0) for kw in summary_keywords]
# 计算阶段时间差
dut_phases = [0]
ref_phases = [0]
for i in range(1, len(summary_keywords)):
dut_phases.append(dut_values[i] - dut_values[i-1])
ref_phases.append(ref_values[i] - ref_values[i-1])
# 填充汇总数据
red_fill = PatternFill(start_color="FFFF0000", end_color="FFFF0000", fill_type="solid")
for i, kw in enumerate(summary_keywords):
diff = dut_phases[i] - ref_phases[i]
row = [
kw,
dut_values[i],
ref_values[i],
dut_phases[i],
ref_phases[i],
diff
]
summary_sheet.append(row)
# 标红大于300的正差值
if diff > 300:
cell_ref = f"F{summary_sheet.max_row}"
summary_sheet[cell_ref].fill = red_fill
# 调整列宽
for sheet in wb:
for col in range(1, sheet.max_column + 1):
sheet.column_dimensions[get_column_letter(col)].width = 20
wb.save(excel_file)
return excel_file
def main():
# 解析命令行参数
parser = argparse.ArgumentParser(description="Boot Time Analysis Tool")
parser.add_argument("project_name", help="Name of the project")
parser.add_argument("-ref", required=True, help="Platform for REF (MTK/QCOM/SPRD)")
parser.add_argument("-dut", required=True, help="Platform for DUT (MTK/QCOM/SPRD)")
args = parser.parse_args()
# 创建目录结构
project_dir, result_dir = create_directories(args.project_name)
# 检查REF和DUT目录
ref_dir = os.path.join(os.getcwd(), "ref")
dut_dir = os.path.join(os.getcwd(), "dut")
if not os.path.exists(ref_dir) or not os.listdir(ref_dir):
print("Error: REF directory is empty or does not exist")
sys.exit(1)
if not os.path.exists(dut_dir) or not os.listdir(dut_dir):
print("Error: DUT directory is empty or does not exist")
sys.exit(1)
# 处理REF数据
ref_files = find_bugreport_files(ref_dir)
if not ref_files:
print("Error: No bugreport files found in REF directory")
sys.exit(1)
ref_extract_dir = os.path.join(project_dir, "ref_extract")
ref_log_file = find_main_log_file(extract_archive(ref_files[0], ref_extract_dir))
if not ref_log_file:
print("Error: Failed to find main log file in REF bugreport")
sys.exit(1)
ref_keywords, ref_results = extract_keyword_data(ref_log_file, args.ref)
# 处理DUT数据
dut_files = find_bugreport_files(dut_dir)
if not dut_files:
print("Error: No bugreport files found in DUT directory")
sys.exit(1)
dut_extract_dir = os.path.join(project_dir, "dut_extract")
dut_log_file = find_main_log_file(extract_archive(dut_files[0], dut_extract_dir))
if not dut_log_file:
print("Error: Failed to find main log file in DUT bugreport")
sys.exit(1)
dut_keywords, dut_results = extract_keyword_data(dut_log_file, args.dut)
# 创建Excel报告
same_platform = args.ref.upper() == args.dut.upper()
excel_file = create_excel_report(
result_dir, args.project_name,
{"keywords": dut_keywords, "results": dut_results},
{"keywords": ref_keywords, "results": ref_results},
same_platform, args.dut, args.ref
)
print(f"Analysis completed successfully! Report saved to: {excel_file}")
if __name__ == "__main__":
main()
```
### 脚本功能详解
1. **目录创建**
- 在当前目录创建项目目录(如`MyProject`)
- 在项目目录下创建`result`子目录
2. **参数解析**
- `project_name`: 项目名称(必需)
- `-ref`: REF设备平台(MTK/QCOM/SPRD)
- `-dut`: DUT设备平台(MTK/QCOM/SPRD)
3. **文件检查**
- 检查`ref/`和`dut/`目录是否存在且非空
- 查找以`bugreport`开头的文件
4. **日志处理**
- 支持解压ZIP、TAR.GZ、GZ格式
- 自动识别解压后的主日志文件
- 特殊关键字处理:
- `Booting Linux on physical`:作为基准时间(0ms)
- `init first stage started!`:相对于基准的时间差
- `init second stage started!`:相对于第一阶段的时间差
5. **Excel报告生成**
- 工作表1(Summary):汇总对比数据
- 工作表2(dut_bootimg):DUT设备数据
- 工作表3(ref_bootimg):REF设备数据
- 自动计算阶段时间差和差异
- 差异值>300ms时标红
6. **关键字文件**
- 需在脚本同目录创建:
- `mtk_keyword.txt`
- `qcom_keyword.txt`
- `sprd_keyword.txt`
- `common_keyword.txt`
### 使用示例
```bash
python boot_analysis.py MyProject -ref MTK -dut QCOM
```
### 特殊关键字处理逻辑
1. **基准时间点**
```python
if kw == "Booting Linux on physical":
value = 0 # 作为时间基准点
```
2. **第一阶段启动**
```python
if kw == "init first stage started!":
value = (first_stage_time - boot_time) # 与基准时间差
```
3. **第二阶段启动**
```python
if kw == "init second stage started!":
value = (second_stage_time - first_stage_time) # 与第一阶段时间差
```
### 阶段时间差计算
```python
# 计算DUT阶段时间差
dut_phases = [0]
for i in range(1, len(keywords)):
phase_time = dut_values[i] - dut_values[i-1]
dut_phases.append(phase_time)
# 计算差异并标红
diff = dut_phase - ref_phase
if diff > 300:
cell.fill = red_fill
```
### 清理建议
运行后可手动删除解压目录:
- `MyProject/ref_extract/`
- `MyProject/dut_extract/`
### 注意事项
1. 确保关键字文件存在且每行一个关键字
2. 日志时间格式必须为`MM-DD HH:mm:ss.SSS`
3. REF/DUT目录需预先准备好bugreport文件
4. 需要安装依赖:`pip install openpyxl`