Coverage for /opt/cloud/slavespace/usr1/096471637100f3de0fcfc01072822a80/ut/utils/show_kernel_debug_data/show_kernel_debug_data/dump_parser.py: 95%
916 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-27 14:38 +0800
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-27 14:38 +0800
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3# ----------------------------------------------------------------------------------------------------------
4# Copyright (c) 2025 Huawei Technologies Co., Ltd.
5# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6# CANN Open Software License Agreement Version 2.0 (the "License").
7# Please refer to the License for details. You may not use this file except in compliance with the License.
8# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10# See LICENSE in the root of the software repository for the full text of the License.
11# ----------------------------------------------------------------------------------------------------------
13import glob
14import json
15import os
16import stat
17import struct
18import subprocess
19from dataclasses import dataclass, field
20from enum import Enum, auto
21from typing import List, Dict, Any
22from .data_converter import decode_bfloat16
23from .dump_logger import DUMP_PARSER_LOG
25FILE_FLAG = os.O_WRONLY | os.O_CREAT
26FILE_MODE_640 = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP
28ONE_MEGA_BYTE = 1024 * 1024
31class TimeStampId(Enum):
32 TIME_STAMP_WRAP_FIRST = 0x000
33 TIME_STAMP_WRAP_MC2_CTX = auto()
34 TIME_STAMP_WRAP_WK_SPACE = auto()
35 TIME_STAMP_WRAP_INIT_DUMP = auto()
36 TIME_STAMP_WRAP_FFTS_ADDR = auto()
37 TIME_STAMP_WRAP_CLEAR_WK_SPAC = auto()
39 TIME_STAMP_TPIPE = 0x030
40 TIME_STAMP_BUFFER = auto()
42 TIME_STAMP_MATMUL_SERVER = 0x060
43 TIME_STAMP_MATMUL_SERVER_INIT = auto()
44 TIME_STAMP_MATMUL_SERVER_OBJ = auto()
45 TIME_STAMP_MATMUL_MATRIX_KFC = auto()
46 TIME_STAMP_MATMUL_CLIENT_KFC = auto()
47 TIME_STAMP_MATMUL_WAIT_EVE = auto()
48 TIME_STAMP_MATMUL_OBJ = auto()
50 TIME_STAMP_TILING_DATA = 0x090
51 TIME_STAMP_TILING_DATA_STRUCT = auto()
52 TIME_STAMP_TILING_DATA_MEMBER = auto()
54 TIME_STAMP_MAX = 0xfff
57def get_enum_member_name(enum_type, value):
58 for member in enum_type:
59 if member.value == value:
60 return member.name
61 return value
64@dataclass(repr=False)
65class TLV:
66 tag: int = 0
67 length: int = 0
68 value: bytes = None
70 def read(self, f):
71 tl_fmt = self.get_tl_format()
72 tl_size = self.get_tl_size()
73 self.tag, self.length = struct.unpack(tl_fmt, f.read(tl_size))
74 self.value = f.read(self.length)
76 def write_to(self, buffer, offset):
77 tl_fmt = self.get_tl_format()
78 struct.pack_into(tl_fmt, buffer, offset, self.tag, self.length)
79 value_offset = offset + self.get_tl_size()
80 val_fmt = f'{self.length}s'
81 struct.pack_into(val_fmt, buffer, value_offset, self.value)
82 return self.get_tl_size() + self.length
84 def __repr__(self):
85 return f'TLV(tag={self.tag}, length={self.length})'
87 @classmethod
88 def get_tl_format(cls):
89 # TLV header uses uint32_t type/length.
90 return 'II'
92 @classmethod
93 def get_tl_size(cls):
94 return struct.calcsize(cls.get_tl_format())
96 def get_size(self):
97 return self.get_tl_size() + self.length
100@dataclass
101class DumpMessageHeader:
102 addr: int = 0
103 data_type: int = 0
104 desc: int = 0
105 buffer_id: int = 0
106 position: int = 0
107 reserved: int = 0
109 @classmethod
110 def get_format(cls):
111 return 'iiiiii'
113 @classmethod
114 def get_size(cls):
115 fmt = cls.get_format()
116 return struct.calcsize(fmt)
118 def unpack(self, buffer):
119 fmt = self.get_format()
120 self.addr, self.data_type, self.desc, self.buffer_id, self.position, self.reserved = struct.unpack(
121 fmt, buffer)
123 def pack(self):
124 fmt = self.get_format()
125 return struct.pack(fmt, self.addr, self.data_type, self.desc, self.buffer_id, self.position, self.reserved)
128@dataclass
129class ShapeInfo:
130 dim: int = 0
131 shape: List[Any] = field(default_factory=list)
132 rsv: int = 0
133 total_ele_num = 0
135 @classmethod
136 def get_format(cls):
137 # value format of ShapeInfo bin: dim, shape0, ..., shape7, rsv
138 return 'iiiiiiiiii'
140 @classmethod
141 def get_size(cls):
142 fmt = cls.get_format()
143 return struct.calcsize(fmt)
145 def unpack(self, buffer):
146 fmt = self.get_format()
147 unpacked_data = struct.unpack(fmt, buffer)
148 self.dim = unpacked_data[0]
149 self.total_ele_num = 1
150 for i in range(self.dim):
151 self.shape.append(unpacked_data[i + 1])
152 self.total_ele_num *= self.shape[-1]
154 def parse_from(self, tlv: TLV):
155 self.unpack(tlv.value)
158@dataclass
159class MetaInfo:
160 blk_dim: int = 0
161 core_type: int = 0
162 task_ration: int = 0
163 rsv: int = 0
164 content: str = ''
167 @classmethod
168 def get_format(cls):
169 return 'HbbI'
171 @classmethod
172 def get_size(cls):
173 fmt = cls.get_format()
174 return struct.calcsize(fmt)
176 def unpack(self, buffer):
177 core_type_convert = {
178 0: "MIX",
179 1: "AIC",
180 2: "VEC"
181 }
182 fmt = self.get_format()
183 self.blk_dim, self.core_type, self.task_ration, self.rsv = struct.unpack(fmt, buffer)
184 self.content = "[Meta Info] block num: {}, core type: {}, isMix: {}\n".format(self.blk_dim,
185 core_type_convert.get(self.core_type, "Unknown"), bool(self.task_ration))
187 def parse_from(self, tlv: TLV):
188 self.unpack(tlv.value)
191@dataclass
192class TimeStampInfo:
193 desc_id: int = 0
194 rsv: int = 0
195 sys_cycle: int = 0
196 pc_ptr: int = 0
198 @classmethod
199 def get_format(cls):
200 # value format of TimeStampInfo bin:desc_id(u32), rsv(u32), sys_cycle(u64)
201 return 'IIQQ'
203 @classmethod
204 def get_size(cls):
205 fmt = cls.get_format()
206 return struct.calcsize(fmt)
208 def unpack(self, buffer):
209 fmt = self.get_format()
210 unpacked_data = struct.unpack(fmt, buffer)
211 self.desc_id = unpacked_data[0]
212 self.rsv = unpacked_data[1]
213 self.sys_cycle = unpacked_data[2]
214 self.pc_ptr = unpacked_data[3]
216 def parse_from(self, tlv: TLV):
217 self.unpack(tlv.value)
220@dataclass
221class FifoTimeStampInfo:
222 desc_id: int = 0
223 block_idx: int = 0
224 rsv: int = 0
225 sys_cycle: int = 0
226 pc_ptr: int = 0
227 entry: int = 0
228 resv_mem0: int = 0
229 resv_mem1: int = 0
231 @classmethod
232 def get_format(cls):
233 # value format of ringbuf TimeStampTlvInfo:
234 return 'IHHQQQII'
236 @classmethod
237 def get_size(cls):
238 fmt = cls.get_format()
239 return struct.calcsize(fmt)
241 def unpack(self, buffer):
242 fmt = self.get_format()
243 unpacked_data = struct.unpack(fmt, buffer)
244 self.desc_id = unpacked_data[0]
245 self.block_idx = unpacked_data[1]
246 self.rsv = unpacked_data[2]
247 self.sys_cycle = unpacked_data[3]
248 self.pc_ptr = unpacked_data[4]
249 self.entry = unpacked_data[5]
250 self.resv_mem0 = unpacked_data[6]
251 self.resv_mem1 = unpacked_data[7]
253 def parse_from(self, tlv: TLV):
254 self.unpack(tlv.value)
257dtype_to_fmt = {
258 0: 'f', # DT_FLOAT
259 1: 'e', # DT_FLOAT16
260 2: 'b', # DT_INT8
261 3: 'i', # DT_INT32
262 4: 'B', # DT_UINT8
263 6: 'h', # DT_INT16
264 7: 'H', # DT_UINT16
265 8: 'I', # DT_UINT32
266 9: 'q', # DT_UINT64
267 10: 'Q', # DT_UINT64
268 27: "H", # DT_BF16
269 33: '' # DT_MAX
270}
272dtype_to_data_type = {
273 0: 'float32', # DT_FLOAT
274 1: 'float16', # DT_FLOAT16
275 2: 'int8', # DT_INT8
276 3: 'int32', # DT_INT32
277 4: 'uint8', # DT_UINT8
278 6: 'int16', # DT_INT16
279 7: 'uint16', # DT_UINT16
280 8: 'uint32', # DT_UINT32
281 9: 'int64', # DT_INT64
282 10: 'uint64', # DT_UINT64
283 27: 'bfloat16', # DT_BF16
284 33: '' # DT_MAX
285}
287special_data_type = {
288 27: decode_bfloat16, # 'bfloat16'
289}
291@dataclass(repr=False)
292class DumpTensor:
293 tag: int = 0
294 length: int = 0
295 dump_header: DumpMessageHeader = None
296 dump_data: bytes = b''
297 dump_value: List[Any] = field(default_factory=list)
298 dump_shape: List[int] = field(default_factory=list)
300 def parse_from(self, tlv):
301 self.tag = tlv.tag
302 self.length = tlv.length
304 head_size = DumpMessageHeader.get_size()
305 head_buffer = tlv.value[0: head_size]
306 self.dump_header = DumpMessageHeader()
307 self.dump_header.unpack(head_buffer)
308 self.dump_data = tlv.value[head_size:]
309 self._parse_dump_data()
311 def parse_to(self):
312 tlv = TLV()
313 tlv.tag = self.tag
314 tlv.length = self.length
315 head_buffer = self.dump_header.pack()
317 tlv.value = head_buffer
318 tlv.value += self.dump_data
319 return tlv
321 def __repr__(self):
322 return f'DumpTensor(tag={self.tag}, length={self.length}, dump_header={self.dump_header},\
323 tensor_shape={self.dump_shape}, dump_data_size={len(self.dump_data)})'
325 def _parse_dump_data(self):
326 fmt = dtype_to_fmt.get(self.dump_header.data_type, '')
327 if not fmt:
328 DUMP_PARSER_LOG.debug(f'data type {self.dump_header.data_type} is not supported')
329 return
330 if self.dump_header.data_type in special_data_type.keys():
331 converter = special_data_type[self.dump_header.data_type]
332 special_data = [converter(value[0]) for value in struct.iter_unpack(fmt, self.dump_data)]
333 self.dump_value.extend(special_data)
334 else:
335 self.dump_value.extend(
336 value[0] for value in struct.iter_unpack(fmt, self.dump_data))
339@dataclass(repr=False)
340class FifoDumpTensor(DumpTensor):
341 def parse_from(self, tlv):
342 self.tag = tlv.tag
343 self.length = tlv.length
345 head_size = struct.calcsize('IIIIHHI8III')
346 if len(tlv.value) < head_size:
347 raise RuntimeError('FifoDumpTensor: invalid TLV value length')
349 unpacked = struct.unpack('IIIIHHI8III', tlv.value[:head_size])
350 tensor_addr = unpacked[0]
351 data_type = unpacked[1]
352 desc = unpacked[2]
353 buffer_id = unpacked[3]
354 position = unpacked[4]
355 # unpacked[5] is blockIdx, [6] is dim, [7:15] shape, [15] resv1
356 dump_size = unpacked[16]
358 self.dump_header = DumpMessageHeader(
359 addr=tensor_addr,
360 data_type=data_type,
361 desc=desc,
362 buffer_id=buffer_id,
363 position=position,
364 reserved=0
365 )
367 data_start = head_size
368 data_end = data_start + dump_size
369 if data_end > len(tlv.value):
370 raise RuntimeError('FifoDumpTensor: dump_size out of range')
371 self.dump_data = tlv.value[data_start:data_end]
372 self._parse_dump_data()
375@dataclass(repr=False)
376class PrintStruct:
377 tag: int = 0
378 length: int = 0
379 fmt: str = ''
380 args: List[Any] = field(default_factory=list)
381 content: str = None
383 def parse_from(self, tlv):
384 self.tag = tlv.tag
385 self.length = tlv.length
387 args_start, args_end = self._read_fmt(tlv.value) # fmt
388 self._read_args(tlv.value, args_start, args_end) # args
390 self.fmt = self.fmt.replace('%p', '0x%x') # python not support %p
391 self.content = self.fmt % tuple(self.args)
393 def _read_fmt(self, buffer):
394 self.fmt, fmt_offset = self._read_arg_str(buffer, 0)
395 return (8, fmt_offset)
397 def _all_fmt_placehold(self):
398 import re
399 pattern = r"%[a-zA-Z]{1,2}"
400 matches = re.findall(pattern, self.fmt)
401 fmt_placehold_list = []
402 for x in matches:
403 if not (len(x) == 3 and '%l' not in x.lower()):
404 fmt_placehold_list.append(x)
405 return fmt_placehold_list
407 def _read_args(self, buffer, args_start, args_end):
408 args_plds = self._all_fmt_placehold()
409 args_num = len(args_plds)
411 for i in range(0, args_num):
412 offset = args_start + i * 8
413 if offset + 8 > args_end:
414 raise RuntimeError(
415 f'arg {i} at [{offset}:{offset + 8}] over arg end({args_end}): \"{self.fmt}\"')
416 pld = args_plds[i]
417 self._read_arg(buffer, offset, pld)
419 def _read_arg(self, buffer, offset, pld):
420 if pld == '%d' or pld == '%i':
421 arg = self._read_arg_long(buffer, offset)
422 elif pld == '%ld':
423 arg = self._read_arg_long(buffer, offset)
424 elif pld == '%f' or pld == '%F':
425 arg = self._read_arg_float(buffer, offset)
426 elif pld == '%lf' or pld == '%LF':
427 arg = self._read_arg_double(buffer, offset)
428 elif pld == '%x' or pld == '%X':
429 arg = self._read_arg_hex(buffer, offset)
430 elif pld == '%s':
431 arg, _ = self._read_arg_str(buffer, offset)
432 elif pld == '%p':
433 arg = self._read_arg_point(buffer, offset)
434 elif pld == '%u':
435 arg = self._read_arg_unsigned_long(buffer, offset)
436 else:
437 raise RuntimeError(f'not support pld({pld}), fmt: {self.fmt}')
438 self.args.append(arg)
440 @staticmethod
441 def _read_arg_unsigned_long(buffer, offset):
442 return struct.unpack('Q', buffer[offset: offset + 8])[0]
444 @staticmethod
445 def _read_arg_long(buffer, offset):
446 return struct.unpack('q', buffer[offset: offset + 8])[0]
448 @staticmethod
449 def _read_arg_float(buffer, offset):
450 is_double = False
451 for buf_val in buffer[offset + 4: offset + 8]:
452 if int(buf_val) != 0:
453 is_double = True
454 break
455 if is_double:
456 return struct.unpack('d', buffer[offset: offset + 8])[0]
457 return struct.unpack('f', buffer[offset: offset + 4])[0]
459 @staticmethod
460 def _read_arg_double(buffer, offset):
461 return struct.unpack('d', buffer[offset: offset + 8])[0]
463 @staticmethod
464 def _read_arg_hex(buffer, offset):
465 return struct.unpack('Q', buffer[offset: offset + 8])[0]
467 @staticmethod
468 def _read_arg_str(buffer, offset):
469 relv_offset = struct.unpack('Q', buffer[offset: offset + 8])[0]
470 abs_offset = offset + relv_offset
471 return (PrintStruct._read_string(buffer, abs_offset), relv_offset)
473 @staticmethod
474 def _read_arg_point(buffer, offset):
475 return struct.unpack('P', buffer[offset: offset + 8])[0]
477 @staticmethod
478 def _read_string(buffer: bytes, offset: int):
479 max_length = len(buffer)
480 if offset > max_length:
481 raise RuntimeError(
482 f'read str offset {offset} over max buffer length {max_length}')
483 s: str = ''
484 for i in range(offset, max_length):
485 b = struct.unpack('1s', buffer[i: i + 1])[0]
486 if b == b'\x00':
487 return s
488 s = s + b.decode('utf-8')
489 return s
492@dataclass(repr=False)
493class FifoPrintStruct(PrintStruct):
494 block_idx: int = 0
495 resv: int = 0
497 def parse_from(self, tlv):
498 self.tag = tlv.tag
499 self.length = tlv.length
501 if len(tlv.value) < 16:
502 raise RuntimeError('FifoPrintStruct: invalid TLV value length')
503 self.block_idx, self.resv, _fmt_offset = struct.unpack('IIQ', tlv.value[:16])
505 # Skip blockIdx/resv (8 bytes) so buffer starts at fmtOffset field.
506 fifo_buffer = tlv.value[8:]
507 args_start, args_end = self._read_fmt(fifo_buffer) # fmt
508 self._read_args(fifo_buffer, args_start, args_end) # args
510 self.fmt = self.fmt.replace('%p', '0x%x') # python not support %p
511 self.content = self.fmt % tuple(self.args)
514@dataclass(repr=False)
515class FifoSimtPrintStruct(PrintStruct):
516 block_idx: List[int] = field(default_factory=lambda: [0, 0, 0])
517 thread_idx: List[int] = field(default_factory=lambda: [0, 0, 0])
518 resv: List[int] = field(default_factory=lambda: [0, 0, 0, 0])
520 def parse_from(self, tlv):
521 self.tag = tlv.tag
522 self.length = tlv.length
524 header_size = struct.calcsize('3I3I4IQ')
525 if len(tlv.value) < header_size:
526 raise RuntimeError('FifoSimtPrintStruct: invalid TLV value length')
528 unpacked = struct.unpack('3I3I4IQ', tlv.value[:header_size])
529 self.block_idx = list(unpacked[0:3])
530 self.thread_idx = list(unpacked[3:6])
531 self.resv = list(unpacked[6:10])
533 # Buffer starts from fmtOffset field.
534 simt_buffer = tlv.value[40:]
535 args_start, args_end = self._read_fmt(simt_buffer) # fmt
536 self._read_args(simt_buffer, args_start, args_end) # args
538 self.fmt = self.fmt.replace('%p', '0x%x') # python not support %p
539 self.content = self.fmt % tuple(self.args)
542@dataclass
543class BlockInfo:
544 total_size: int = 0
545 block_id: int = 0
546 block_num: int = 0
547 remain_size: int = 0
548 magic_num: int = 0
549 reserved: int = 0
550 dump_addr: int = 0 # 8 Byte
552 @classmethod
553 def get_format(cls):
554 return 'iiiiiiQ'
556 @classmethod
557 def get_size(cls):
558 fmt = cls.get_format()
559 return struct.calcsize(fmt)
561 def unpack(self, buffer):
562 fmt = self.get_format()
563 self.total_size, self.block_id, self.block_num, self.remain_size, \
564 self.magic_num, self.reserved, self.dump_addr = struct.unpack(
565 fmt, buffer)
567 def pack_into(self, buffer, offset):
568 fmt = self.get_format()
569 struct.pack_into(fmt, buffer, offset, self.total_size, self.block_id, self.block_num,
570 self.remain_size, self.magic_num, self.reserved, self.dump_addr)
571 return self.get_size()
573 def is_valid(self):
574 block_info_magic = 0x5aa5bccd
575 return block_info_magic == self.magic_num
578@dataclass
579class FifoBlockInfo:
580 length: int = 0
581 core_id: int = 0
582 block_num: int = 0
583 remain_len: int = 0
584 magic: int = 0
585 flag: int = 0
586 rsv: int = 0
587 dump_addr: int = 0
588 resv: List[int] = field(default_factory=list)
590 def __repr__(self):
591 return (
592 f'FifoBlockInfo(length={self.length}, core_id={self.core_id}, block_num={self.block_num}, '
593 f'remain_len={self.remain_len}, magic=0x{self.magic:04X}, flag={self.flag}, rsv={self.rsv}, '
594 f'dump_addr=0x{self.dump_addr:X}, resv={self.resv})'
595 )
597 @classmethod
598 def get_format(cls):
599 return 'IIIIHHIQ6I'
601 @classmethod
602 def get_size(cls):
603 fmt = cls.get_format()
604 return struct.calcsize(fmt)
606 def unpack(self, buffer):
607 fmt = self.get_format()
608 unpacked = struct.unpack(fmt, buffer)
609 self.length = unpacked[0]
610 self.core_id = unpacked[1]
611 self.block_num = unpacked[2]
612 self.remain_len = unpacked[3]
613 self.magic = unpacked[4]
614 self.flag = unpacked[5]
615 self.rsv = unpacked[6]
616 self.dump_addr = unpacked[7]
617 self.resv = list(unpacked[8:14])
619 def is_valid(self):
620 return self.magic == 0xAE86
623@dataclass
624class DumpCoreContent:
625 block_info: BlockInfo = None
626 dump_tensor_map: Dict[int, List[DumpTensor]] = field(default_factory=dict)
627 print_list: List[str] = field(default_factory=list)
628 simt_print_map: Dict[str, List[str]] = field(default_factory=dict)
629 time_stamp_list: List[TimeStampInfo] = field(default_factory=list)
630 index_dtype_dt = {}
631 shape: List[int] = field(default_factory=list)
633 @staticmethod
634 def _write_dump_tensor_data(dump_tensor, dump_data_path):
635 with open(dump_data_path, 'wb+') as f:
636 f.write(dump_tensor.dump_data)
638 @staticmethod
639 def _write_dump_tensor_value(dump_tensor, dump_value_path):
640 write_content = ""
641 total_ele_num = 0
642 if dump_tensor.dump_shape:
643 total_ele_num = 1
644 for ele in dump_tensor.dump_shape:
645 total_ele_num = total_ele_num * ele
646 if total_ele_num != 0:
647 value_len = len(dump_tensor.dump_value)
648 if total_ele_num > value_len:
649 DUMP_PARSER_LOG.warning(
650 f'tensor shape {dump_tensor.dump_shape} needs {total_ele_num} elements but only '
651 f'{value_len} dumped, missing values will be shown as "-"')
652 elif total_ele_num < value_len:
653 DUMP_PARSER_LOG.warning(
654 f'tensor shape {dump_tensor.dump_shape} needs {total_ele_num} elements but '
655 f'{value_len} dumped, extra dumped values will be ignored')
657 shape = dump_tensor.dump_shape.copy()
658 write_content = "[" * len(shape)
659 for i in range(len(shape) - 2, -1, -1):
660 shape[i] *= shape[i + 1]
661 for i in range(total_ele_num):
662 cnt = 0
663 for s in shape:
664 if (i + 1) % s == 0:
665 cnt += 1
666 if i < value_len:
667 write_content += str(dump_tensor.dump_value[i])
668 else:
669 write_content += "-"
670 if cnt:
671 write_content += "]" * cnt
672 if i != total_ele_num - 1:
673 write_content += ",\n"
674 write_content += "[" * cnt
675 elif i != total_ele_num - 1:
676 write_content += ","
677 if not write_content:
678 line_count = 0
679 for value in dump_tensor.dump_value:
680 write_content += str(value) + ","
681 line_count += 1
682 if line_count == 8:
683 write_content += "\n"
684 line_count = 0
685 with open(dump_value_path, 'w+') as f:
686 f.write(write_content)
688 @classmethod
689 def _flow_name(self):
690 return 'legacy'
692 @classmethod
693 def _create_dump_tensor(self):
694 return DumpTensor()
696 @classmethod
697 def _create_print_struct(self, _tlv_tag=None):
698 return PrintStruct()
700 @classmethod
701 def _create_time_stamp_info(self):
702 return TimeStampInfo()
704 def add_tlv_data(self, tlv):
705 if tlv.tag == DumpType.TENSOR_TYPE.value:
706 dump_tensor = self._create_dump_tensor()
707 dump_tensor.parse_from(tlv)
708 index = dump_tensor.dump_header.desc
709 data_type = dump_tensor.dump_header.data_type
710 if self.shape:
711 dump_tensor.dump_shape = self.shape.copy()
712 self.shape.clear()
713 self.index_dtype_dt[index] = dtype_to_data_type.get(data_type, '')
714 self._add_dump_tensor(dump_tensor)
715 elif tlv.tag in (
716 DumpType.SCALAR_TYPE.value,
717 DumpType.ASSERT_TYPE.value,
718 DumpType.SIMT_PRINTF_TYPE.value,
719 DumpType.SIMT_ASSERT_TYPE.value
720 ):
721 print_struct = self._create_print_struct(tlv.tag)
722 print_struct.parse_from(tlv)
723 self.print_list.append(print_struct.content)
724 if isinstance(print_struct, FifoSimtPrintStruct):
725 thread_id = '_'.join([str(x) for x in print_struct.thread_idx])
726 if thread_id not in self.simt_print_map:
727 self.simt_print_map[thread_id] = []
728 self.simt_print_map[thread_id].append(print_struct.content)
729 elif tlv.tag == DumpType.SHAPE_TYPE.value:
730 shape_info = ShapeInfo()
731 shape_info.parse_from(tlv)
732 self.shape = shape_info.shape
733 elif tlv.tag == DumpType.TIME_STAMP.value:
734 time_stamp_info = self._create_time_stamp_info()
735 time_stamp_info.parse_from(tlv)
736 self.time_stamp_list.append(time_stamp_info)
737 elif tlv.tag == DumpType.META_TYPE.value:
738 meta_info = MetaInfo()
739 meta_info.parse_from(tlv)
740 self.print_list.append(meta_info.content)
741 else:
742 DUMP_PARSER_LOG.error(f'Invalid dump Type: {tlv.tag}')
744 def get_core_id(self):
745 if self.block_info is None:
746 return 'unknown'
747 if hasattr(self.block_info, 'core_id'):
748 return str(self.block_info.core_id)
749 return str(self.block_info.block_id)
751 def write_dump_tensor_data(self, dump_tensor, dump_data_path):
752 self._write_dump_tensor_data(dump_tensor, dump_data_path)
754 def write_dump_tensor_value(self, dump_tensor, dump_value_path):
755 self._write_dump_tensor_value(dump_tensor, dump_value_path)
757 def write_time_stamp(self, core_output_dir):
758 self._write_time_stamp(core_output_dir)
760 def write_to_dir(self, output_dir):
761 core_id = self.get_core_id()
762 core_output_dir = os.path.join(output_dir, str(core_id))
763 os.makedirs(core_output_dir, exist_ok=True)
764 DUMP_PARSER_LOG.info(f'write core {core_id} dump data to dir: {core_output_dir}')
765 if self.dump_tensor_map:
766 self._write_dump_tensor_by_index(core_output_dir)
767 if self.time_stamp_list:
768 self._write_time_stamp(core_output_dir)
770 def show_print(self):
771 if len(self.print_list) > 0:
772 core_id = self.get_core_id()
773 print(f"================ block.{core_id} begin ==============")
774 print(''.join(self.print_list), end='', flush=True)
775 print(f"================ block.{core_id} end ================")
776 DUMP_PARSER_LOG.info(f"================ block.{core_id} begin ==============")
777 DUMP_PARSER_LOG.info(''.join(self.print_list))
778 DUMP_PARSER_LOG.info(f"================ block.{core_id} end ================")
780 def _add_dump_tensor(self, dump_tensor):
781 index = dump_tensor.dump_header.desc
782 if index not in self.dump_tensor_map:
783 self.dump_tensor_map[index] = []
784 DUMP_PARSER_LOG.debug(f'Tensor[{index}][{len(self.dump_tensor_map[index])}] = {dump_tensor}')
785 self.dump_tensor_map[index].append(dump_tensor)
787 def _write_dump_tensor_by_loop(self, index, index_output_dir):
788 loop_cnt = len(self.dump_tensor_map[index])
789 core_id = self.get_core_id()
790 for loop in range(0, loop_cnt):
791 dump_tensor = self.dump_tensor_map[index][loop]
792 dump_file_name = f'core_{core_id}_index_{index}_loop_{loop}.bin'
793 dump_file_path = os.path.join(index_output_dir, dump_file_name)
794 self._write_dump_tensor_data(dump_tensor, dump_file_path)
796 parsed_dump_file_name = f'core_{core_id}_index_{index}_loop_{loop}.txt'
797 parsed_dump_file_path = os.path.join(index_output_dir, parsed_dump_file_name)
798 self._write_dump_tensor_value(dump_tensor, parsed_dump_file_path)
800 def _write_dump_tensor_by_index(self, core_output_dir):
801 for index in self.dump_tensor_map.keys():
802 index_output_dir = os.path.join(core_output_dir, f'index_{index}')
803 os.makedirs(index_output_dir, exist_ok=True)
804 DUMP_PARSER_LOG.info(f'write index {index} tensor to dir: {index_output_dir}')
805 self._write_dump_tensor_by_loop(index, index_output_dir)
807 def _write_time_stamp(self, core_output_dir):
808 import csv
809 os.makedirs(core_output_dir, exist_ok=True)
810 DUMP_PARSER_LOG.info(f'write time_stamp data to dir: {core_output_dir}')
811 core_id = self.get_core_id()
812 dump_file_name = f'time_stamp_core_{core_id}.csv'
813 parsed_dump_file_path = os.path.join(core_output_dir, dump_file_name)
814 with open(parsed_dump_file_path, 'w', encoding='utf-8-sig', newline="") as f:
815 csv_write = csv.writer(f)
816 csv_write.writerow(['打点标识', 'Cycle', 'Cycle间隔', 'PC指针'])
817 last_cycle = 0
818 for time_stamp in self.time_stamp_list:
819 csv_write.writerow([str(get_enum_member_name(TimeStampId, int(time_stamp.desc_id))),
820 str(time_stamp.sys_cycle),
821 int(time_stamp.sys_cycle) - last_cycle, int(time_stamp.pc_ptr)]
822 )
823 last_cycle = int(time_stamp.sys_cycle)
826@dataclass
827class FifoDumpCoreContent(DumpCoreContent):
828 def _flow_name(self):
829 return 'fifo'
831 def _create_dump_tensor(self):
832 return FifoDumpTensor()
834 def _create_print_struct(self, tlv_tag=None):
835 if tlv_tag in (DumpType.SIMT_PRINTF_TYPE.value, DumpType.SIMT_ASSERT_TYPE.value):
836 return FifoSimtPrintStruct()
837 return FifoPrintStruct()
839 def _create_time_stamp_info(self):
840 return FifoTimeStampInfo()
843class DumpType(Enum):
844 DEFAULT_TYPE = 0
845 SCALAR_TYPE = 1
846 TENSOR_TYPE = 2
847 SHAPE_TYPE = 3
848 ASSERT_TYPE = 4
849 META_TYPE = 5
850 TIME_STAMP = 6
851 SIMT_PRINTF_TYPE = 0xF0E00F0E
852 SIMT_ASSERT_TYPE = 0xF0F00F0F
855class DumpBinFile:
856 def __init__(self, dump_bin):
857 self.dump_bin = self._pre_process(dump_bin)
858 self.dump_core_contents = [] # 每个core dump 内容
859 self.index_dtype_dt = {}
861 def _pre_process(self, dump_bin: str):
862 dump_dir = os.path.dirname(dump_bin)
863 temp_dir = os.path.join(dump_dir, "predump")
864 dump_file_name = os.path.basename(dump_bin)
865 install_path = get_install_path()
866 search_re = f"{install_path}/**/operator_cmp/compare/msaccucmp.py"
867 search_result = glob.glob(search_re, recursive=True)
868 if not search_result or not os.path.exists(search_result[0]):
869 return dump_bin
870 msaccucmp_file = os.path.realpath(search_result[0])
871 cmd = f'python3 "{msaccucmp_file}" convert -d "{dump_bin}" -t bin -out "{temp_dir}"'
872 log_file_tmp = DUMP_PARSER_LOG.get_log_file()
873 with open(log_file_tmp, "a+") as f:
874 try:
875 process = subprocess.run(cmd, stdout=f, stderr=subprocess.STDOUT, shell=True, encoding='utf-8',
876 timeout=120)
877 except subprocess.TimeoutExpired as e:
878 DUMP_PARSER_LOG.error(f'Command {cmd} TIME OUT.')
879 dump_result_re = os.path.join(temp_dir, f"{dump_file_name}.space.*.bin")
880 dump_result = glob.glob(dump_result_re, recursive=True)
881 if dump_result and os.path.exists(dump_result[0]):
882 DUMP_PARSER_LOG.info(f'Find new dump_bin use {dump_result[0]}')
883 return dump_result[0]
884 return dump_bin
886 def parse(self):
887 file_size = os.path.getsize(self.dump_bin)
888 with open(self.dump_bin, 'rb') as bin_file:
889 self.get_dump_core_contents(bin_file, file_size)
891 def get_dump_core_contents(self, bin_file, file_size):
892 block_id = 0
893 read_pos = 0
894 total_block_size = ONE_MEGA_BYTE
895 block_info_size = BlockInfo.get_size()
897 while read_pos + block_info_size < file_size:
898 DUMP_PARSER_LOG.debug(f'block.{block_id} read from: {read_pos}')
899 core_content = DumpCoreContent()
901 # read block info
902 block_info_buffer = bin_file.read(block_info_size)
903 block_info = BlockInfo()
904 block_info.unpack(block_info_buffer)
906 if block_info.reserved == 7:
907 DUMP_PARSER_LOG.warning(
908 f"block.{block_id} remain space is NOT enough for last dump !!!")
910 if not block_info.is_valid():
911 DUMP_PARSER_LOG.debug(
912 f'block.{block_id} block info is not valid, skip this block...')
913 read_pos += total_block_size
914 bin_file.seek(read_pos)
915 block_id += 1
916 continue
918 DUMP_PARSER_LOG.debug(block_info)
919 core_content.block_info = block_info
920 total_block_size = block_info.total_size
922 # read tlv data
923 tlv_offset = 0
924 core_dump_size = block_info.total_size - \
925 block_info_size - block_info.remain_size
926 while tlv_offset < core_dump_size:
927 tlv = TLV()
928 tlv.read(bin_file)
929 core_content.add_tlv_data(tlv)
930 tlv_offset += tlv.get_size()
931 self.index_dtype_dt.update(core_content.index_dtype_dt)
932 self.dump_core_contents.append(core_content)
934 # read remain data
935 read_pos += block_info.total_size
936 bin_file.seek(read_pos)
937 block_id += 1
939 def write_result(self, output_dir):
940 if len(self.dump_core_contents) == 0:
941 DUMP_PARSER_LOG.debug('no dump data, exit...')
942 return ''
944 parse_output_dir = os.path.join(output_dir, 'dump_data')
945 DUMP_PARSER_LOG.info(f'write dump workspace result: {parse_output_dir}')
946 os.makedirs(parse_output_dir, exist_ok=True)
947 for core_content in self.dump_core_contents:
948 core_content.write_to_dir(parse_output_dir)
949 return parse_output_dir
951 def write_index_dtype(self, output_dir):
952 if len(self.index_dtype_dt) == 0:
953 DUMP_PARSER_LOG.debug('no dump index, exit...')
954 # remove index_dtype.json
955 return
956 json_path = os.path.join(output_dir, 'dump_data', 'index_dtype.json')
957 with os.fdopen(os.open(json_path, FILE_FLAG, FILE_MODE_640), 'w') as f:
958 json.dump(self.index_dtype_dt, f, indent=4)
960 def show_print(self):
961 for core_content in self.dump_core_contents:
962 core_content.show_print()
965def get_install_path() -> str:
966 ascend_home = os.environ.get('ASCEND_HOME_PATH')
967 if not ascend_home:
968 raise RuntimeError(f'get install path env failed, Please set environment variables')
969 return ascend_home
972class FifoDumpBinFile:
973 def __init__(self, dump_bin, core_type, core_id):
974 self.dump_bin = dump_bin
975 self.core_type = core_type
976 self.core_id = core_id
977 self.dump_core_contents = []
978 self.index_dtype_dt = {}
980 @staticmethod
981 def _iter_core_tensors(core_content):
982 for index, tensors in core_content.dump_tensor_map.items():
983 for loop, dump_tensor in enumerate(tensors):
984 yield index, loop, dump_tensor
986 def parse(self):
987 file_size = os.path.getsize(self.dump_bin)
988 core_content = FifoDumpCoreContent()
990 with open(self.dump_bin, 'rb') as bin_file:
991 block_info_size = FifoBlockInfo.get_size()
992 block_info_buffer = bin_file.read(block_info_size)
993 if len(block_info_buffer) < block_info_size:
994 raise RuntimeError('FifoDumpBinFile: incomplete BlockInfo header')
995 block_info = FifoBlockInfo()
996 block_info.unpack(block_info_buffer)
997 if not block_info.is_valid():
998 raise RuntimeError('FifoDumpBinFile: invalid BlockInfo magic')
999 core_content.block_info = block_info
1000 while True:
1001 tl_head = bin_file.read(TLV.get_tl_size())
1002 if not tl_head:
1003 break
1004 if len(tl_head) < TLV.get_tl_size():
1005 raise RuntimeError('FifoDumpBinFile: incomplete TLV header')
1006 tlv = TLV()
1007 tlv.tag, tlv.length = struct.unpack(tlv.get_tl_format(), tl_head)
1008 if tlv.length > file_size:
1009 raise RuntimeError(f'FifoDumpBinFile: TLV length overflow, length={tlv.length}')
1010 remain_size = file_size - bin_file.tell()
1011 if tlv.length > remain_size:
1012 raise RuntimeError(
1013 f'FifoDumpBinFile: TLV length overflow, length={tlv.length}, remain={remain_size}'
1014 )
1015 tlv.value = bin_file.read(tlv.length)
1016 if len(tlv.value) < tlv.length:
1017 raise RuntimeError('FifoDumpBinFile: incomplete TLV value')
1019 if tlv.tag in (
1020 DumpType.TENSOR_TYPE.value,
1021 DumpType.SCALAR_TYPE.value,
1022 DumpType.ASSERT_TYPE.value,
1023 DumpType.SIMT_PRINTF_TYPE.value,
1024 DumpType.SIMT_ASSERT_TYPE.value,
1025 DumpType.TIME_STAMP.value,
1026 DumpType.SHAPE_TYPE.value
1027 ):
1028 core_content.add_tlv_data(tlv)
1029 self.index_dtype_dt.update(core_content.index_dtype_dt)
1030 self.dump_core_contents.append(core_content)
1032 def write_result(self, output_dir):
1033 if len(self.dump_core_contents) == 0:
1034 DUMP_PARSER_LOG.debug('no dump data, exit...')
1035 return ''
1037 parse_output_dir = os.path.join(output_dir, 'dump_data')
1038 DUMP_PARSER_LOG.info(f'write dump workspace result: {parse_output_dir}')
1039 os.makedirs(parse_output_dir, exist_ok=True)
1040 for core_content in self.dump_core_contents:
1041 core_output_dir = os.path.join(parse_output_dir, str(core_content.get_core_id()))
1042 os.makedirs(core_output_dir, exist_ok=True)
1043 for index, loop, dump_tensor in self._iter_core_tensors(core_content):
1044 name_prefix = f'asc_kernel_data_{self.core_type}_{self.core_id}_index_{index}_loop_{loop}'
1045 dump_file_path = os.path.join(core_output_dir, f'{name_prefix}.bin')
1046 core_content.write_dump_tensor_data(dump_tensor, dump_file_path)
1048 parsed_dump_file_path = os.path.join(core_output_dir, f'{name_prefix}.txt')
1049 core_content.write_dump_tensor_value(dump_tensor, parsed_dump_file_path)
1050 if self.core_type == 'simt' and core_content.simt_print_map:
1051 self._write_simt_print_by_thread(core_content, core_output_dir)
1052 if core_content.time_stamp_list:
1053 core_content.write_time_stamp(core_output_dir)
1054 return parse_output_dir
1056 def write_index_dtype(self, output_dir):
1057 if len(self.index_dtype_dt) == 0:
1058 DUMP_PARSER_LOG.debug('no dump index, exit...')
1059 return
1060 json_path = os.path.join(output_dir, 'dump_data', 'index_dtype.json')
1061 with os.fdopen(os.open(json_path, FILE_FLAG, FILE_MODE_640), 'w') as f:
1062 json.dump(self.index_dtype_dt, f, indent=4)
1064 def show_print(self):
1065 for core_content in self.dump_core_contents:
1066 core_content.show_print()
1068 def _write_simt_print_by_thread(self, core_content, parse_output_dir):
1069 for thread_id, print_list in core_content.simt_print_map.items():
1070 name_prefix = f'asc_kernel_data_{self.core_type}_{self.core_id}_thread_{thread_id}'
1071 parsed_dump_file_path = os.path.join(parse_output_dir, f'{name_prefix}.txt')
1072 with open(parsed_dump_file_path, 'w+') as f:
1073 f.write(''.join(print_list))
1076def _make_parser_output_dir(output_path):
1077 from datetime import datetime, timezone
1078 output_dir = os.path.abspath(output_path)
1079 cur_time_str = datetime.now(tz=timezone.utc).strftime('%Y%m%d%H%M%S%f')
1080 output_dir = os.path.join(output_dir, f"PARSER_{cur_time_str}")
1081 os.makedirs(output_dir, exist_ok=True)
1082 return output_dir
1085def _core_type_from_fifo_flag(flag):
1086 flag_to_core_type = {
1087 0: 'aic',
1088 1: 'aiv',
1089 2: 'simt'
1090 }
1091 return flag_to_core_type.get(flag, 'fifo')
1094def _read_block_magic_core_id_and_flag(dump_bin):
1095 raw_magic = None
1096 fifo_core_id = None
1097 fifo_flag = None
1098 with open(dump_bin, 'rb') as bin_file:
1099 header = bin_file.read(FifoBlockInfo.get_size())
1100 if len(header) >= 20:
1101 raw_magic = struct.unpack('I', header[16:20])[0]
1102 unpacked = struct.unpack(FifoBlockInfo.get_format(), header)
1103 fifo_core_id = unpacked[1]
1104 fifo_flag = unpacked[5]
1105 return raw_magic, fifo_core_id, fifo_flag
1108def _collect_bin_files(input_path):
1109 input_path = os.path.abspath(input_path)
1110 if os.path.isfile(input_path):
1111 return [input_path]
1112 if os.path.isdir(input_path):
1113 search_re = os.path.join(input_path, '**', '*.bin')
1114 return sorted([bin_file for bin_file in glob.glob(search_re, recursive=True) if os.path.isfile(bin_file)])
1115 return []
1118def parse_dump_bin(bin_file_path, output_path, parse_output_dir=None, init_logger=True):
1119 dump_bin = os.path.abspath(bin_file_path)
1120 output_dir = parse_output_dir if parse_output_dir else _make_parser_output_dir(output_path)
1121 output_dir = os.path.abspath(output_dir)
1122 os.makedirs(output_dir, exist_ok=True)
1123 if init_logger:
1124 DUMP_PARSER_LOG.set_log_file(os.path.join(output_dir, "parser.log"))
1125 DUMP_PARSER_LOG.set_log_level(os.environ.get('ASCEND_GLOBAL_LOG_LEVEL', '3'))
1126 try:
1127 name_no_ext, ext = os.path.splitext(os.path.basename(dump_bin))
1128 parts = name_no_ext.split('_')
1129 is_asc_kernel_data = (
1130 ext == '.bin'
1131 and len(parts) == 5
1132 and parts[0] == 'asc'
1133 and parts[1] == 'kernel'
1134 and parts[2] == 'data'
1135 )
1136 raw_magic, fifo_core_id, fifo_flag = _read_block_magic_core_id_and_flag(dump_bin)
1138 if raw_magic is not None and (raw_magic & 0xFFFF) == 0xAE86:
1139 if is_asc_kernel_data:
1140 core_type = parts[3]
1141 core_id = parts[4]
1142 else:
1143 core_type = _core_type_from_fifo_flag(fifo_flag)
1144 core_id = str(fifo_core_id) if fifo_core_id is not None else 'unknown'
1145 dump_file = FifoDumpBinFile(dump_bin, core_type, core_id)
1146 elif raw_magic is None or raw_magic == 0x5aa5bccd:
1147 if os.path.getsize(dump_bin) < BlockInfo.get_size():
1148 raise RuntimeError(
1149 f'file too small ({os.path.getsize(dump_bin)} bytes), '
1150 f'at least {BlockInfo.get_size()} bytes required for a valid workspace dump')
1151 dump_file = DumpBinFile(dump_bin)
1152 else:
1153 raise RuntimeError(f'unknown block magic: 0x{raw_magic:08X}')
1155 dump_file.parse()
1156 dump_file.write_result(output_dir)
1157 dump_file.write_index_dtype(output_dir)
1158 dump_file.show_print()
1159 return 0
1160 except Exception as e:
1161 print(f"parse dump workspace bin occur exception, bin_file: {dump_bin}")
1162 import traceback
1163 traceback.print_exc()
1164 return 255
1167def _validate_and_prepare(bin_file_path, output_path):
1168 """Validate input/output paths and collect bin files.
1169 Returns (dump_bins, output_path, parser_output_dir) on success.
1170 parser_output_dir is None when bin_file_path points to a single file.
1171 """
1172 if not bin_file_path or not os.path.exists(bin_file_path):
1173 if bin_file_path and not all(ord(c) < 128 for c in bin_file_path):
1174 raise RuntimeError(
1175 f'({bin_file_path}) path contains non-ASCII characters (e.g. Chinese), '
1176 f'which may cause encoding issues. Please use an ASCII-only path.')
1177 raise RuntimeError(f'({bin_file_path}) file does not exist or permission denied!!!')
1179 if not output_path:
1180 raise RuntimeError(f'({output_path}) directory does not exist or permission denied!!!')
1181 if not all(ord(c) < 128 for c in output_path):
1182 raise RuntimeError(
1183 f'({output_path}) output path contains non-ASCII characters (e.g. Chinese), '
1184 f'which may cause encoding issues. Please use an ASCII-only path.')
1185 if os.path.exists(output_path):
1186 if not os.path.isdir(output_path):
1187 raise RuntimeError(f'({output_path}) is not a directory!!!')
1188 else:
1189 try:
1190 os.makedirs(output_path, exist_ok=True)
1191 except OSError as err:
1192 raise RuntimeError(
1193 f'({output_path}) failed to create directory: {err}') from err
1195 if not os.path.isfile(bin_file_path) and not os.path.isdir(bin_file_path):
1196 raise RuntimeError(f'({bin_file_path}) is neither a file nor a directory!!!')
1198 dump_bins = _collect_bin_files(bin_file_path)
1199 if not dump_bins:
1200 raise RuntimeError(f'({bin_file_path}) does not contain any .bin file!!!')
1202 parser_output_dir = None
1203 if os.path.isdir(bin_file_path):
1204 parser_output_dir = _make_parser_output_dir(output_path)
1205 DUMP_PARSER_LOG.set_log_file(os.path.join(parser_output_dir, "parser.log"))
1206 DUMP_PARSER_LOG.set_log_level(os.environ.get('ASCEND_GLOBAL_LOG_LEVEL', '3'))
1208 return dump_bins, output_path, parser_output_dir
1211def execute_parse():
1212 import sys
1213 param_len = len(sys.argv[1:])
1214 bin_file_path = ''
1215 output_path = os.getcwd()
1216 help_info = "show_kernel_debug_data is a tool that parses dump binary data from AscendC debug API.\n"\
1217 "It takes two inputs:\n First mandatory param is binary file or directory\n"\
1218 " Second optional param is output path that stores result file," \
1219 " by default saving path is current directory.\n"\
1220 " ex: show_kernel_debug_data ./dump.bin ./output_dir"
1221 if param_len == 2:
1222 bin_file_path, output_path = sys.argv[1:]
1223 elif param_len == 1 and sys.argv[1] in ['-h', '--help']:
1224 print(help_info)
1225 return 0
1226 elif param_len == 1:
1227 bin_file_path = sys.argv[1]
1228 else:
1229 print(help_info)
1230 raise RuntimeError("parameters invalid, please check tool introduction.")
1232 dump_bins, output_path, parser_output_dir = _validate_and_prepare(bin_file_path, output_path)
1234 if parser_output_dir is not None:
1235 for dump_bin in dump_bins:
1236 ret = parse_dump_bin(dump_bin, output_path, parse_output_dir=parser_output_dir, init_logger=False)
1237 if ret != 0:
1238 return ret
1239 else:
1240 ret = parse_dump_bin(dump_bins[0], output_path)
1241 if ret != 0:
1242 return ret
1243 return 0