"""修复 issue 附件:重建 artifacts/2026-11-13/ 下全部场景 zip(此前为 0 字节空文件)。
每个 zip 内容:执行步骤说明.md(结果概览+复现步骤+附件清单)+ 该场景实际压测 CPP + plog + 执行日志。
上传:GitCode contents API(POST 创建 / PUT 带 sha 更新)。
链接:全部 issue 正文统一为 blob/main 格式(sha 无关,永远指向最新内容)。
"""
import base64
import io
import json
import os
import urllib.request
import zipfile
BASE = '/home/30039325/Vector_fuzz_cpp_testing'
cfg = json.load(open(os.path.join(BASE, 'config/issue_tracker.json')))
TOK, API = cfg['access_token'], cfg['api_base']
OWNER, REPO, BRANCH = cfg['issue_repo_owner'], cfg['issue_repo_name'], cfg.get('issue_repo_branch', 'main')
DATE = '2026-11-13'
ISSUE_MAP = [
(575, 'aclnnMoeFinalizeRoutingV3', 'non_contig'),
(576, 'aclnnMoeFinalizeRoutingV3', 'multithread'),
(577, 'aclnnMoeFinalizeRoutingV3', 'error'),
(578, 'aclnnMoeFinalizeRoutingV3', 'error'),
(579, 'aclnnInplaceRReluWithNoise', 'error'),
(580, 'aclnnRReluWithNoise', 'error'),
(581, 'aclnnSituMxQuant', 'error'),
(582, 'aclnnSituMxQuant', 'multithread'),
(583, 'aclnnLeftShift', 'error'),
(584, 'aclnnLeftShifts', 'error'),
(585, 'aclnnPartialRotaryMulQuant', 'error'),
(586, 'aclnnPartialRotaryMulQuant', 'error'),
]
def api(path, data=None, method=None):
req = urllib.request.Request(API + path, data=json.dumps(data).encode() if data else None,
method=method or ('PATCH' if data else 'GET'),
headers={'PRIVATE-TOKEN': TOK, 'Content-Type': 'application/json'})
for attempt in range(3):
try:
with urllib.request.urlopen(req, timeout=180) as r:
return r.status, json.loads(r.read().decode() or '{}')
except urllib.error.HTTPError as e:
body = e.read().decode()[:200]
if attempt == 2:
return e.code, {'error': body}
import time; time.sleep(2)
def trimmed(path, cap=2 * 1024 * 1024):
"""大日志截取:head 512KB + aicore/ERROR 关键行(≤512KB) + tail 256KB,标注截取。"""
size = os.path.getsize(path)
if size <= cap:
return path, None
import re
head_len, tail_len, ev_len = 512 * 1024, 256 * 1024, 512 * 1024
with open(path, 'rb') as f:
head = f.read(head_len)
f.seek(-tail_len, os.SEEK_END)
tail = f.read(tail_len)
evid = []
with open(path, 'rb') as f:
for line in f:
low = line[:400].lower()
if b'error' in low and (b'aivec' in low or b'kernel_name' in low or b'errno' in low):
evid.append(line[:400])
if sum(len(x) for x in evid) > ev_len:
break
out = path + '.trimmed'
with open(out, 'wb') as f:
f.write(b'===== [TRIMMED] original %d bytes; head 512KB + aicore/errno evidence + tail 256KB =====\n\n===== HEAD =====\n' % size)
f.write(head)
f.write(b'\n\n===== AICORE/ERRNO EVIDENCE (grep) =====\n')
f.write(b''.join(evid[:2000]))
f.write(b'\n\n===== TAIL =====\n')
f.write(tail)
return out, size
def scene_files(api_name, scenario):
d = os.path.join(BASE, 'output', api_name)
logs = os.path.join(d, 'fuzz_logs')
cpp_map = {
'non_contig': os.path.join(d, f'{api_name}_non_contig.cpp'),
'multithread': os.path.join(d, f'{api_name}_multithread.cpp'),
'error': os.path.join(d, f'{api_name}_error.cpp'),
}
log_map = {
'non_contig': [os.path.join(logs, f'{api_name}_non_contig_fuzz_output.log'),
os.path.join(logs, f'{api_name}_non_contig_fuzz_plog.log')],
'multithread': [os.path.join(logs, f'{api_name}_multithread_fuzz_output.log'),
os.path.join(logs, f'{api_name}_multithread_fuzz_plog.log')],
'error': [os.path.join(logs, f'{api_name}_error_fuzz_output.log'),
os.path.join(logs, f'{api_name}_error_fuzz_plog.log')],
}
out = [('该场景实际压测 CPP', cpp_map[scenario])]
out.append(('执行日志', log_map[scenario][0]))
out.append(('plog 回传', log_map[scenario][1]))
return out
def build_readme(api_name, scenario):
entry = {'non_contig': f'{api_name}_non_contig_fuzz 10000',
'multithread': 'aclnnAPI_fuzz_multithread 5 20000 0 5 0(5 线程)',
'error': f'{api_name}_error_fuzz 60'}[scenario]
return f"""# {api_name} {scenario} 场景 压测附件
## 结果概览
- 压测日期:{DATE};环境:Ascend 950(CANN 9.3.0)
- 场景:{scenario}
- 详细结果见 issue 正文与 执行日志/plog
## 复现步骤
1. 下载测试框架:https://gitcode.com/Alice_zhou/Vector_fuzz_cpp_testing/tree/main/turing_sec/aclnnAPI_fuzz/
2. 环境准备(清除原有内容,将附件 cpp 放入对应目录下):
rm -rf aclnnAPI_fuzz/src/op/*;将附件中的 CPP 放入 aclnnAPI_fuzz/src/op/ 下{'(multithread 场景放 src/op_multithread/,文件名改为 ' + api_name + '.cpp)' if scenario == 'multithread' else ''}
3. 编译:cd aclnnAPI_fuzz/ ; make op_fuzz{' TARGET=multithread' if scenario == 'multithread' else ''}
4. 执行:cd out ; export LD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/x86_64-linux/lib64:$LD_LIBRARY_PATH ; ./{'aclnnAPI_fuzz_multithread 5 20000 0 5 0' if scenario == 'multithread' else 'aclnnAPI_fuzz ' + entry}
## 附件清单
- 执行步骤说明.md(本文件)
- 该场景实际压测 CPP
- 执行日志(打屏输出,含 [FUZZ_PARAM]/[FUZZ_RESULT])
- plog 回传(设备侧日志,含 aicore_error 判定串)
"""
def main():
uploaded = []
for num, api_name, scenario in ISSUE_MAP:
repo_path = f'artifacts/{DATE}/{api_name}_{scenario}.zip'
key = (api_name, scenario)
if key in [u[1:] for u in uploaded]:
continue
bio = io.BytesIO()
with zipfile.ZipFile(bio, 'w', zipfile.ZIP_DEFLATED) as zf:
zf.writestr('执行步骤说明.md', build_readme(api_name, scenario))
for label, path in scene_files(api_name, scenario):
if os.path.isfile(path) and os.path.getsize(path) > 0:
src, orig = trimmed(path)
zf.write(src, arcname=os.path.basename(path) + ('' if src == path else '.trimmed'))
if src != path:
os.unlink(src)
else:
print(f'[WARN] 缺失: {path}')
content = base64.b64encode(bio.getvalue()).decode()
q = urllib.request.quote(repo_path)
st, cur = api(f'/repos/{OWNER}/{REPO}/contents/{q}?ref={BRANCH}')
if st == 200:
st2, _ = api(f'/repos/{OWNER}/{REPO}/contents/{q}',
{'content': content, 'message': f'rebuild artifact {repo_path} (修复空文件)',
'branch': BRANCH, 'sha': cur.get('sha'), 'ref': BRANCH}, method='PUT')
else:
st2, _ = api(f'/repos/{OWNER}/{REPO}/contents/{q}',
{'content': content, 'message': f'add artifact {repo_path}', 'branch': BRANCH}, method='POST')
st3, chk = api(f'/repos/{OWNER}/{REPO}/contents/{q}?ref={BRANCH}')
size = chk.get('size', 0)
print(f'[{num}] {repo_path} -> upload_rc={st2} 远端size={size}')
uploaded.append((num, api_name, scenario, size))
blob_prefix = f'https://gitcode.com/{OWNER}/{REPO}/blob/{BRANCH}/artifacts/{DATE}/'
import re
fixed = []
for num, api_name, scenario in ISSUE_MAP:
st, iss = api(f'/repos/{OWNER}/{REPO}/issues/{num}')
body = iss.get('body', '') or ''
newbody = re.sub(r'https?://[^\s\)\]】,]*?artifacts/[^\s\)\]】,]*?\.zip',
blob_prefix + f'{api_name}_{scenario}.zip', body)
newbody = newbody.replace(f'http://123.60.114.238:8899/download/{DATE}/{api_name}_{scenario}.zip',
blob_prefix + f'{api_name}_{scenario}.zip')
if newbody != body:
api(f'/repos/{OWNER}/{REPO}/issues/{num}', {'body': newbody})
fixed.append(num)
print('链接统一为 blob/main 的 issue:', fixed)
if __name__ == '__main__':
main()