добавлены комменты к debug_tools.c/.h
начата работа над поддержкой stm32 и кейл проектов
This commit is contained in:
parent
742c4e9e1b
commit
4de53090a1
BIN
DebugVarEdit.exe
BIN
DebugVarEdit.exe
Binary file not shown.
@ -5,7 +5,7 @@ import sys
|
||||
import os
|
||||
import subprocess
|
||||
import lxml.etree as ET
|
||||
from generate_debug_vars import type_map
|
||||
from generate_debug_vars import type_map, choose_type_map
|
||||
from enum import IntEnum
|
||||
import threading
|
||||
from generate_debug_vars import run_generate
|
||||
@ -21,8 +21,9 @@ from PySide2.QtWidgets import (
|
||||
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
||||
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
|
||||
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView
|
||||
)
|
||||
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView,
|
||||
QMenuBar, QMenu, QAction
|
||||
)
|
||||
from PySide2.QtGui import QTextCursor, QKeyEvent, QIcon, QFont
|
||||
from PySide2.QtCore import Qt, QProcess, QObject, Signal, QSettings
|
||||
|
||||
@ -58,6 +59,7 @@ class VarEditor(QWidget):
|
||||
self.output_path = None
|
||||
self._updating = False # Флаг блокировки рекурсии
|
||||
self._resizing = False # флаг блокировки повторного вызова
|
||||
self.target = 'TMS'
|
||||
self.initUI()
|
||||
|
||||
def initUI(self):
|
||||
@ -122,6 +124,23 @@ class VarEditor(QWidget):
|
||||
self.btn_update_vars = QPushButton(scan_title)
|
||||
self.btn_update_vars.clicked.connect(self.update_vars_data)
|
||||
|
||||
# Добавляем чекбокс для выбора типовой карты
|
||||
# --- Создаем верхнее меню ---
|
||||
menubar = QMenuBar(self)
|
||||
target_menu = QMenu("Target", menubar)
|
||||
# Создаем действия для выбора Target
|
||||
self.action_tms = QAction("TMS", self, checkable=True)
|
||||
self.action_stm = QAction("STM", self, checkable=True)
|
||||
# Группируем действия чтобы выбирался только один
|
||||
self.action_tms.setChecked(True) # по умолчанию TMS
|
||||
self.action_tms.triggered.connect(lambda: self.on_target_selected("tms"))
|
||||
self.action_stm.triggered.connect(lambda: self.on_target_selected("stm"))
|
||||
|
||||
target_menu.addAction(self.action_tms)
|
||||
target_menu.addAction(self.action_stm)
|
||||
|
||||
menubar.addMenu(target_menu)
|
||||
|
||||
# Кнопка сохранения
|
||||
btn_save = QPushButton(build_title)
|
||||
btn_save.clicked.connect(self.save_build)
|
||||
@ -137,6 +156,7 @@ class VarEditor(QWidget):
|
||||
self.table = VariableTableWidget()
|
||||
# Основной layout
|
||||
layout = QVBoxLayout()
|
||||
layout.setMenuBar(menubar) # прикрепляем menubar в layout сверху
|
||||
layout.addLayout(xml_layout)
|
||||
layout.addLayout(proj_layout)
|
||||
layout.addLayout(makefile_layout)
|
||||
@ -150,7 +170,18 @@ class VarEditor(QWidget):
|
||||
self.setLayout(layout)
|
||||
|
||||
|
||||
|
||||
def on_target_selected(self, target):
|
||||
self.target = target.lower()
|
||||
if target == "stm":
|
||||
choose_type_map(True)
|
||||
self.action_stm.setChecked(True)
|
||||
self.action_tms.setChecked(False)
|
||||
else:
|
||||
choose_type_map(False)
|
||||
self.action_tms.setChecked(True)
|
||||
self.action_stm.setChecked(False)
|
||||
|
||||
|
||||
def get_xml_path(self):
|
||||
xml_path = self.xml_output_edit.text().strip()
|
||||
return xml_path
|
||||
@ -358,15 +389,25 @@ class VarEditor(QWidget):
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def __browse_makefile(self):
|
||||
if self.target == 'stm':
|
||||
file_filter = "Makefile или Keil-проект (*.uvprojx *.uvproj makefile);;Все файлы (*)"
|
||||
dialog_title = "Выберите Makefile или Keil-проект"
|
||||
else: # 'TMS' или по умолчанию
|
||||
file_filter = "Makefile (makefile);;Все файлы (*)"
|
||||
dialog_title = "Выберите Makefile"
|
||||
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self, "Выберите Makefile", filter="Makefile (makefile);;All Files (*)"
|
||||
self,
|
||||
dialog_title,
|
||||
filter=file_filter
|
||||
)
|
||||
if file_path and self.proj_path:
|
||||
path = myXML.make_relative_path(file_path, self.proj_path)
|
||||
else:
|
||||
path = file_path
|
||||
self.makefile_edit.setText(path)
|
||||
self.makefile_path = path
|
||||
if file_path:
|
||||
if self.proj_path:
|
||||
path = myXML.make_relative_path(file_path, self.proj_path)
|
||||
else:
|
||||
path = file_path
|
||||
self.makefile_edit.setText(path)
|
||||
self.makefile_path = path
|
||||
|
||||
def __browse_source_output(self):
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "Выберите папку для debug_vars.c")
|
||||
@ -438,12 +479,12 @@ class VarEditor(QWidget):
|
||||
self.write_to_xml()
|
||||
|
||||
|
||||
def __open_variable_selector(self):
|
||||
def __open_variable_selector(self):
|
||||
self.update()
|
||||
if not self.vars_list:
|
||||
QMessageBox.warning(self, "Нет переменных", f"Сначала загрузите переменные ({scan_title}).")
|
||||
return
|
||||
|
||||
self.update()
|
||||
dlg = VariableSelectorDialog(self.table, self.vars_list, self.structs, self.typedef_map, self.xml_path, self)
|
||||
if dlg.exec_():
|
||||
self.write_to_xml()
|
||||
|
@ -14,7 +14,7 @@ import argparse
|
||||
|
||||
|
||||
# === Словарь соответствия типов XML → DebugVarType_t ===
|
||||
type_map = dict([
|
||||
type_map_tms = dict([
|
||||
*[(k, 'pt_int8') for k in ('signed char', 'char')],
|
||||
*[(k, 'pt_int16') for k in ('int', 'int16', 'short')],
|
||||
*[(k, 'pt_int32') for k in ('long', 'int32', '_iqx')],
|
||||
@ -52,6 +52,147 @@ type_map = dict([
|
||||
('struct[]', 'pt_arr_struct'),
|
||||
('union[]', 'pt_arr_union'),
|
||||
])
|
||||
# === Словарь соответствия типов XML → DebugVarType_t ===
|
||||
type_map_stm32 = dict([
|
||||
|
||||
*[(k, 'pt_int8') for k in (
|
||||
'int8_t', 'signed char', 'char'
|
||||
)],
|
||||
|
||||
# --- 8-bit unsigned ---
|
||||
*[(k, 'pt_uint8') for k in (
|
||||
'uint8_t', 'unsigned char'
|
||||
)],
|
||||
|
||||
# --- 16-bit signed ---
|
||||
*[(k, 'pt_int16') for k in (
|
||||
'int16_t', 'short', 'short int', 'signed short', 'signed short int'
|
||||
)],
|
||||
|
||||
# --- 16-bit unsigned ---
|
||||
*[(k, 'pt_uint16') for k in (
|
||||
'uint16_t', 'unsigned short', 'unsigned short int'
|
||||
)],
|
||||
|
||||
# --- 32-bit signed ---
|
||||
*[(k, 'pt_int32') for k in (
|
||||
'int32_t', 'int', 'signed', 'signed int'
|
||||
)],
|
||||
|
||||
# --- 32-bit unsigned ---
|
||||
*[(k, 'pt_uint32') for k in (
|
||||
'uint32_t', 'unsigned', 'unsigned int'
|
||||
)],
|
||||
|
||||
# --- 64-bit signed ---
|
||||
*[(k, 'pt_int64') for k in (
|
||||
'int64_t', 'long long', 'signed long long', 'signed long long int'
|
||||
)],
|
||||
|
||||
# --- 64-bit unsigned ---
|
||||
*[(k, 'pt_uint64') for k in (
|
||||
'uint64_t', 'unsigned long long', 'unsigned long long int'
|
||||
)],
|
||||
|
||||
# --- Float ---
|
||||
*[(k, 'pt_float') for k in (
|
||||
'float', 'float32_t'
|
||||
)],
|
||||
|
||||
# --- Struct and Union ---
|
||||
('struct', 'pt_struct'),
|
||||
('union', 'pt_union'),
|
||||
('struct*', 'pt_ptr_struct'),
|
||||
('union*', 'pt_ptr_union'),
|
||||
('struct[]', 'pt_arr_struct'),
|
||||
('union[]', 'pt_arr_union'),
|
||||
|
||||
# === POINTERS ===
|
||||
|
||||
# 8-bit
|
||||
*[(k, 'pt_ptr_int8') for k in (
|
||||
'int8_t*', 'signed char*', 'char*'
|
||||
)],
|
||||
*[(k, 'pt_ptr_uint8') for k in (
|
||||
'uint8_t*', 'unsigned char*'
|
||||
)],
|
||||
|
||||
# 16-bit
|
||||
*[(k, 'pt_ptr_int16') for k in (
|
||||
'int16_t*', 'short*', 'short int*', 'signed short*', 'signed short int*'
|
||||
)],
|
||||
*[(k, 'pt_ptr_uint16') for k in (
|
||||
'uint16_t*', 'unsigned short*', 'unsigned short int*'
|
||||
)],
|
||||
|
||||
# 32-bit
|
||||
*[(k, 'pt_ptr_int32') for k in (
|
||||
'int32_t*', 'int*', 'signed*', 'signed int*'
|
||||
)],
|
||||
*[(k, 'pt_ptr_uint32') for k in (
|
||||
'uint32_t*', 'unsigned*', 'unsigned int*'
|
||||
)],
|
||||
|
||||
# 64-bit
|
||||
*[(k, 'pt_ptr_int64') for k in (
|
||||
'int64_t*', 'long long*', 'signed long long*', 'signed long long int*'
|
||||
)],
|
||||
*[(k, 'pt_ptr_uint64') for k in (
|
||||
'uint64_t*', 'unsigned long long*', 'unsigned long long int*'
|
||||
)],
|
||||
|
||||
# float*
|
||||
*[(k, 'pt_ptr_float') for k in (
|
||||
'float*', 'float32_t*'
|
||||
)],
|
||||
|
||||
# === ARRAYS ===
|
||||
|
||||
# 8-bit
|
||||
*[(k, 'pt_arr_int8') for k in (
|
||||
'int8_t[]', 'signed char[]', 'char[]'
|
||||
)],
|
||||
*[(k, 'pt_arr_uint8') for k in (
|
||||
'uint8_t[]', 'unsigned char[]'
|
||||
)],
|
||||
|
||||
# 16-bit
|
||||
*[(k, 'pt_arr_int16') for k in (
|
||||
'int16_t[]', 'short[]', 'short int[]', 'signed short[]', 'signed short int[]'
|
||||
)],
|
||||
*[(k, 'pt_arr_uint16') for k in (
|
||||
'uint16_t[]', 'unsigned short[]', 'unsigned short int[]'
|
||||
)],
|
||||
|
||||
# 32-bit
|
||||
*[(k, 'pt_arr_int32') for k in (
|
||||
'int32_t[]', 'int[]', 'signed[]', 'signed int[]'
|
||||
)],
|
||||
*[(k, 'pt_arr_uint32') for k in (
|
||||
'uint32_t[]', 'unsigned[]', 'unsigned int[]'
|
||||
)],
|
||||
|
||||
# 64-bit
|
||||
*[(k, 'pt_arr_int64') for k in (
|
||||
'int64_t[]', 'long long[]', 'signed long long[]', 'signed long long int[]'
|
||||
)],
|
||||
*[(k, 'pt_arr_uint64') for k in (
|
||||
'uint64_t[]', 'unsigned long long[]', 'unsigned long long int[]'
|
||||
)],
|
||||
|
||||
# float[]
|
||||
*[(k, 'pt_arr_float') for k in (
|
||||
'float[]', 'float32_t[]'
|
||||
)],
|
||||
])
|
||||
type_map = type_map_tms
|
||||
|
||||
def choose_type_map(stm_flag):
|
||||
global type_map # объявляем, что будем менять глобальную переменную
|
||||
if stm_flag:
|
||||
type_map = type_map_stm32
|
||||
else:
|
||||
type_map = type_map_tms
|
||||
|
||||
def map_type_to_pt(typename, varname=None, typedef_map=None):
|
||||
typename_orig = typename.strip()
|
||||
|
@ -1,5 +1,6 @@
|
||||
import os
|
||||
import re
|
||||
from lxml import etree as ET
|
||||
|
||||
|
||||
def strip_single_line_comments(code):
|
||||
@ -66,89 +67,176 @@ def find_all_includes_recursive(c_files, include_dirs, processed_files=None):
|
||||
return include_files
|
||||
|
||||
|
||||
def parse_objects_list(objects_list_path, project_root):
|
||||
c_files = []
|
||||
include_dirs = set()
|
||||
|
||||
if not os.path.isfile(objects_list_path):
|
||||
return c_files, include_dirs
|
||||
|
||||
with open(objects_list_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
for line in lines:
|
||||
line = line.strip().strip('"').replace("\\", "/")
|
||||
if line.endswith(".o"):
|
||||
c_file = re.sub(r"\.o$", ".c", line)
|
||||
abs_path = os.path.normpath(os.path.join(project_root, c_file))
|
||||
if os.path.isfile(abs_path):
|
||||
if not any(x in abs_path for x in ["DebugTools", "v120", "v100"]):
|
||||
c_files.append(abs_path)
|
||||
include_dirs.add(os.path.dirname(abs_path))
|
||||
|
||||
return c_files, include_dirs
|
||||
|
||||
|
||||
def parse_uvprojx(uvprojx_path):
|
||||
import xml.etree.ElementTree as ET
|
||||
import os
|
||||
|
||||
tree = ET.parse(uvprojx_path)
|
||||
root = tree.getroot()
|
||||
|
||||
project_dir = os.path.dirname(os.path.abspath(uvprojx_path))
|
||||
|
||||
c_files = []
|
||||
include_dirs = set()
|
||||
defines = set()
|
||||
|
||||
# Найдём C-файлы и директории
|
||||
for file_elem in root.findall(".//FilePath"):
|
||||
file_path = file_elem.text
|
||||
if file_path:
|
||||
abs_path = os.path.normpath(os.path.join(project_dir, file_path))
|
||||
if os.path.isfile(abs_path):
|
||||
if abs_path.endswith(".c"):
|
||||
c_files.append(abs_path)
|
||||
include_dirs.add(os.path.dirname(abs_path))
|
||||
|
||||
# Включаем IncludePath
|
||||
for inc_path_elem in root.findall(".//IncludePath"):
|
||||
path_text = inc_path_elem.text
|
||||
if path_text:
|
||||
paths = path_text.split(';')
|
||||
for p in paths:
|
||||
p = p.strip()
|
||||
if p:
|
||||
abs_inc_path = os.path.normpath(os.path.join(project_dir, p))
|
||||
if os.path.isdir(abs_inc_path):
|
||||
include_dirs.add(abs_inc_path)
|
||||
|
||||
# Добавим <Define>
|
||||
for define_elem in root.findall(".//Define"):
|
||||
def_text = define_elem.text
|
||||
if def_text:
|
||||
for d in def_text.split(','):
|
||||
d = d.strip()
|
||||
if d:
|
||||
defines.add(d)
|
||||
|
||||
h_files = find_all_includes_recursive(c_files, include_dirs)
|
||||
|
||||
return sorted(c_files), sorted(h_files), sorted(include_dirs), sorted(defines)
|
||||
|
||||
|
||||
|
||||
def parse_makefile(makefile_path, proj_path):
|
||||
makefile_dir = os.path.dirname(makefile_path)
|
||||
project_root = proj_path
|
||||
import os
|
||||
import re
|
||||
|
||||
project_root = os.path.abspath(proj_path)
|
||||
c_files = []
|
||||
include_dirs = set()
|
||||
defines = [] # Заглушка: нет define-параметров из Makefile
|
||||
|
||||
with open(makefile_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
objs_lines = []
|
||||
raw_entries = []
|
||||
collecting = False
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("ORDERED_OBJS") and "+=" in stripped:
|
||||
parts = stripped.split("\\")
|
||||
first_part = parts[0]
|
||||
idx = first_part.find("+=")
|
||||
tail = first_part[idx+2:].strip()
|
||||
if tail:
|
||||
objs_lines.append(tail)
|
||||
|
||||
if (("ORDERED_OBJS" in stripped or "C_SOURCES" in stripped) and ("+=" in stripped or "=" in stripped)):
|
||||
collecting = True
|
||||
if len(parts) > 1:
|
||||
for p in parts[1:]:
|
||||
p = p.strip()
|
||||
if p:
|
||||
objs_lines.append(p)
|
||||
continue
|
||||
|
||||
if collecting:
|
||||
if stripped.endswith("\\"):
|
||||
objs_lines.append(stripped[:-1].strip())
|
||||
else:
|
||||
objs_lines.append(stripped)
|
||||
line_clean = stripped.rstrip("\\").strip()
|
||||
if line_clean:
|
||||
line_clean = re.sub(r"\$\([^)]+\)", "", line_clean)
|
||||
line_clean = re.sub(r"\$\{[^}]+\}", "", line_clean)
|
||||
raw_entries.append(line_clean)
|
||||
|
||||
if not stripped.endswith("\\"):
|
||||
collecting = False
|
||||
|
||||
objs_str = ' '.join(objs_lines)
|
||||
for entry in raw_entries:
|
||||
for token in entry.split():
|
||||
token = token.strip('"')
|
||||
if not token:
|
||||
continue
|
||||
|
||||
objs_str = re.sub(r"\$\([^)]+\)", "", objs_str)
|
||||
token = token.replace("\\", "/")
|
||||
|
||||
objs = []
|
||||
for part in objs_str.split():
|
||||
part = part.strip()
|
||||
if part.startswith('"') and part.endswith('"'):
|
||||
part = part[1:-1]
|
||||
if part:
|
||||
objs.append(part)
|
||||
if token.endswith(".obj"):
|
||||
token = re.sub(r"\.obj$", ".c", token)
|
||||
elif token.endswith(".o"):
|
||||
token = re.sub(r"\.o$", ".c", token)
|
||||
|
||||
c_files = []
|
||||
include_dirs = set()
|
||||
if token.endswith(".c"):
|
||||
abs_path = os.path.normpath(os.path.join(project_root, token))
|
||||
if os.path.isfile(abs_path):
|
||||
if not any(x in abs_path for x in ["DebugTools", "v120", "v100"]):
|
||||
c_files.append(abs_path)
|
||||
include_dirs.add(os.path.dirname(abs_path))
|
||||
|
||||
for obj_path in objs:
|
||||
if "DebugTools" in obj_path:
|
||||
continue
|
||||
if "v120" in obj_path:
|
||||
continue
|
||||
if "v100" in obj_path:
|
||||
continue
|
||||
if not c_files:
|
||||
makefile_dir = os.path.dirname(os.path.abspath(makefile_path))
|
||||
objects_list_path = os.path.join(makefile_dir, "objects.list")
|
||||
c_from_objects, inc_from_objects = parse_objects_list(objects_list_path, project_root)
|
||||
c_files.extend(c_from_objects)
|
||||
include_dirs.update(inc_from_objects)
|
||||
|
||||
if obj_path.startswith("Debug\\") or obj_path.startswith("Debug/"):
|
||||
rel_path = obj_path.replace("Debug\\", "Src\\").replace("Debug/", "Src/")
|
||||
else:
|
||||
rel_path = obj_path
|
||||
for line in lines:
|
||||
if "-I" in line or "C_INCLUDES" in line:
|
||||
matches = re.findall(r"-I\s*([^\s\\]+)", line)
|
||||
for match in matches:
|
||||
match = match.strip('"').replace("\\", "/")
|
||||
abs_include = os.path.normpath(os.path.join(project_root, match))
|
||||
if os.path.isdir(abs_include):
|
||||
include_dirs.add(abs_include)
|
||||
|
||||
abs_path = os.path.normpath(os.path.join(project_root, rel_path))
|
||||
|
||||
root, ext = os.path.splitext(abs_path)
|
||||
if ext.lower() == ".obj":
|
||||
c_path = root + ".c"
|
||||
else:
|
||||
c_path = abs_path
|
||||
|
||||
# Проверяем существование файла, если нет — пропускаем
|
||||
if not os.path.isfile(c_path):
|
||||
continue
|
||||
|
||||
# Сохраняем только .c файлы
|
||||
if c_path.lower().endswith(".c"):
|
||||
c_files.append(c_path)
|
||||
dir_path = os.path.dirname(c_path)
|
||||
if dir_path and "DebugTools" not in dir_path:
|
||||
include_dirs.add(dir_path)
|
||||
# Добавляем пути с заменой 'Src' на 'Inc', если путь заканчивается на 'Src'
|
||||
additional_includes = set()
|
||||
for inc in include_dirs:
|
||||
if inc.endswith(os.sep + "Src") or inc.endswith("/Src"):
|
||||
inc_inc = inc[:-3] + "Inc" # заменяем 'Src' на 'Inc'
|
||||
if os.path.isdir(inc_inc):
|
||||
additional_includes.add(inc_inc)
|
||||
|
||||
include_dirs.update(additional_includes)
|
||||
|
||||
h_files = find_all_includes_recursive(c_files, include_dirs)
|
||||
|
||||
return sorted(c_files), sorted(h_files), sorted(include_dirs), sorted(defines)
|
||||
|
||||
return sorted(c_files), sorted(h_files), sorted(include_dirs)
|
||||
|
||||
def parse_project(project_file_path, project_root=None):
|
||||
"""
|
||||
Выбирает парсер в зависимости от расширения project_file_path:
|
||||
- для *.uvprojx и *.uvproj вызывается парсер Keil
|
||||
- для остальных - parse_makefile
|
||||
|
||||
project_root нужен для parse_makefile, если не передан - берется из project_file_path
|
||||
"""
|
||||
ext = os.path.splitext(project_file_path)[1].lower()
|
||||
|
||||
if ext in ['.uvprojx', '.uvproj']:
|
||||
# Парсим Keil проект
|
||||
return parse_uvprojx(project_file_path)
|
||||
else:
|
||||
# Парсим makefile
|
||||
if project_root is None:
|
||||
project_root = os.path.dirname(os.path.abspath(project_file_path))
|
||||
return parse_makefile(project_file_path, project_root)
|
@ -11,7 +11,7 @@ from clang import cindex
|
||||
from clang.cindex import Config
|
||||
import lxml.etree as ET
|
||||
from xml.dom import minidom
|
||||
from makefile_parser import parse_makefile
|
||||
from makefile_parser import parse_project
|
||||
from collections import deque
|
||||
import argparse
|
||||
import myXML
|
||||
@ -118,11 +118,11 @@ def get_canonical_typedef_file(var_type, include_dirs):
|
||||
break
|
||||
return None
|
||||
|
||||
def analyze_variables_across_files(c_files, h_files, include_dirs):
|
||||
def analyze_variables_across_files(c_files, h_files, include_dirs, global_defs):
|
||||
optional_printf(PRINT_STATUS, "Starting analysis of variables across files...")
|
||||
index = clang.cindex.Index.create()
|
||||
args = [f"-I{inc}" for inc in include_dirs]
|
||||
|
||||
define_args = [f"-D{d}" for d in global_defs]
|
||||
args = [f"-I{inc}" for inc in include_dirs] + define_args
|
||||
unique_vars = {} # имя переменной → словарь с инфой
|
||||
h_files_needed = set()
|
||||
vars_need_extern = {} # имя переменной → словарь без поля 'extern'
|
||||
@ -295,6 +295,8 @@ def strip_ptr_and_array(typename):
|
||||
|
||||
return typename
|
||||
|
||||
|
||||
|
||||
def analyze_typedefs_and_struct(typedefs, structs):
|
||||
optional_printf(PRINT_STATUS, "Resolving typedefs and expanding struct field types...")
|
||||
|
||||
@ -422,10 +424,28 @@ def contains_anywhere_in_node(node, target: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
|
||||
|
||||
def try_guess_std_include():
|
||||
# Популярные места, где может лежать stdint.h
|
||||
guesses = [
|
||||
r"C:\Keil_v5\ARM\ARMCLANG\include",
|
||||
r"C:\Program Files (x86)\GNU Arm Embedded Toolchain",
|
||||
r"C:\Program Files (x86)\Arm GNU Toolchain"
|
||||
]
|
||||
|
||||
found = []
|
||||
for base in guesses:
|
||||
for root, dirs, files in os.walk(base):
|
||||
if "stdint.h" in files:
|
||||
found.append(root)
|
||||
return found
|
||||
|
||||
def analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs):
|
||||
optional_printf(PRINT_STATUS, "Starting analysis of typedefs and structs across files...")
|
||||
index = clang.cindex.Index.create()
|
||||
args = [f"-I{inc}" for inc in include_dirs]
|
||||
define_args = [f"-D{d}" for d in global_defs]
|
||||
extra_std_include_dirs = try_guess_std_include()
|
||||
args = [f"-I{inc}" for inc in include_dirs] + extra_std_include_dirs + define_args
|
||||
|
||||
unique_typedefs_raw = {}
|
||||
unique_structs_raw = {}
|
||||
@ -444,6 +464,8 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
|
||||
def visit(node):
|
||||
if node.kind == clang.cindex.CursorKind.TYPEDEF_DECL:
|
||||
name = node.spelling
|
||||
if 'ADC_HandleTypeDef' in name:
|
||||
a =1
|
||||
underlying = node.underlying_typedef_type.spelling
|
||||
typedefs[name] = underlying
|
||||
|
||||
@ -451,6 +473,8 @@ def analyze_typedefs_and_structs_across_files(c_files, include_dirs):
|
||||
prefix = "struct " if node.kind == clang.cindex.CursorKind.STRUCT_DECL else "union "
|
||||
|
||||
raw_name = node.spelling
|
||||
if 'struct (unnamed struct at F:\\Work\\Projects\\NIIET\\MZKT\\MZKT\\Drivers\\STM32F4xx_HAL_Driver\\Inc\\stm32f4xx_hal_adc.h:195:9)' in raw_name:
|
||||
a =1
|
||||
normalized_name = normalize_type_name(raw_name)
|
||||
|
||||
# struct_name всегда с префиксом
|
||||
@ -855,10 +879,10 @@ Usage example:
|
||||
print(f"Error: Makefile path '{makefile_path}' does not exist.")
|
||||
sys.exit(1)
|
||||
|
||||
c_files, h_files, include_dirs = parse_makefile(makefile_path, proj_path)
|
||||
c_files, h_files, include_dirs, global_defs = parse_project(makefile_path, proj_path)
|
||||
|
||||
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs)
|
||||
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)
|
||||
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs, global_defs)
|
||||
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs)
|
||||
|
||||
vars = dict(sorted(vars.items()))
|
||||
includes = get_sorted_headers(c_files, includes, include_dirs)
|
||||
@ -898,10 +922,10 @@ def run_scan(proj_path, makefile_path, output_xml, verbose=2):
|
||||
if not os.path.isfile(makefile_path):
|
||||
raise FileNotFoundError(f"Makefile path '{makefile_path}' does not exist.")
|
||||
|
||||
c_files, h_files, include_dirs = parse_makefile(makefile_path, proj_path)
|
||||
c_files, h_files, include_dirs, global_defs = parse_project(makefile_path, proj_path)
|
||||
|
||||
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs)
|
||||
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs)
|
||||
vars, includes, externs = analyze_variables_across_files(c_files, h_files, include_dirs, global_defs)
|
||||
typedefs, structs = analyze_typedefs_and_structs_across_files(c_files, include_dirs, global_defs)
|
||||
|
||||
vars = dict(sorted(vars.items()))
|
||||
includes = get_sorted_headers(c_files, includes, include_dirs)
|
||||
|
@ -1,18 +1,23 @@
|
||||
#include "debug_tools.h"
|
||||
#include "IQmathLib.h"
|
||||
|
||||
DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT;
|
||||
DebugLowLevel_t debug_ll = DEBUG_LOWLEVEL_INIT; ///< Структура отладки нижнего уровня (инициализация)
|
||||
|
||||
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var);
|
||||
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var);
|
||||
|
||||
///////////////////////////----EXAPLE-----//////////////////////////////
|
||||
long var_numb = 1;
|
||||
DebugVarName_t var_name;
|
||||
long return_var;
|
||||
long return_ll_var;
|
||||
int result;
|
||||
char ext_date[] = {7, 233, 11, 07, 16, 50};
|
||||
long var_numb = 1; ///< Пример переменной для отладки
|
||||
DebugVarName_t var_name; ///< Имя переменной
|
||||
long return_var; ///< Переменная для возврата результата
|
||||
long return_ll_var; ///< Возвращаемое значение с нижнего уровня
|
||||
int result; ///< Переменная результата
|
||||
char ext_date[] = {7, 233, 11, 07, 16, 50}; ///< Пример внешней даты сборки
|
||||
|
||||
/**
|
||||
* @brief Пример использования функций отладки.
|
||||
* @details Демонстрационная функция для работы с переменными отладки.
|
||||
*/
|
||||
void Debug_Test_Example(void)
|
||||
{
|
||||
result = Debug_ReadVar(var_numb, &return_var);
|
||||
@ -24,6 +29,14 @@ void Debug_Test_Example(void)
|
||||
}
|
||||
|
||||
///////////////////////////----PUBLIC-----//////////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Читает переменную по индексу.
|
||||
* @param var_ind – индекс переменной.
|
||||
* @param return_long – указатель для возврата результата.
|
||||
* @return int – 0: успех, 1: ошибка.
|
||||
* @details Используется для чтения значений переменных по их индексу.
|
||||
*/
|
||||
int Debug_ReadVar(int var_ind, long *return_long)
|
||||
{
|
||||
if(return_long == NULL)
|
||||
@ -41,6 +54,13 @@ int Debug_ReadVar(int var_ind, long *return_long)
|
||||
return convertDebugVarToIQx(&dbg_vars[var_numb], return_long);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Читает имя переменной по индексу.
|
||||
* @param var_ind – индекс переменной.
|
||||
* @param name_ptr – указатель на буфер имени (DebugVarName_t).
|
||||
* @return int – 0: успех, 1: ошибка.
|
||||
* @details Копирует имя переменной в предоставленный буфер.
|
||||
*/
|
||||
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr)
|
||||
{
|
||||
if(name_ptr == NULL)
|
||||
@ -64,7 +84,12 @@ int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Читает значение переменной отладки с нижнего уровня.
|
||||
* @param return_long – указатель на переменную, куда записывается результат.
|
||||
* @return int – 0: успех, 1: ошибка, 2: недопустимый адрес.
|
||||
* @details Использует адресс, передаваемый с терминалки для получения значения.
|
||||
*/
|
||||
int Debug_LowLevel_ReadVar(long *return_long)
|
||||
{
|
||||
if (return_long == NULL)
|
||||
@ -93,7 +118,12 @@ int Debug_LowLevel_ReadVar(long *return_long)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Инициализация отладки на нижнем уровне по дате сборки.
|
||||
* @param external_date – указатель на массив из 6 байт: {year_hi, year_lo, day, month, hour, minute}.
|
||||
* @return int – 0: совпадает, 1: не совпадает, -1: ошибка.
|
||||
* @details Сравнивает дату компиляции с запрашиваемой и инициализирует отладочную переменную.
|
||||
*/
|
||||
int Debug_LowLevel_Initialize(const char* external_date)
|
||||
{
|
||||
if (external_date == NULL) {
|
||||
@ -129,6 +159,12 @@ int Debug_LowLevel_Initialize(const char* external_date)
|
||||
|
||||
|
||||
/////////////////////----INTERNAL FUNCTIONS-----////////////////////////
|
||||
/**
|
||||
* @brief Преобразует тип IQ переменной в число битов для сдвига(Q-фактор).
|
||||
* @param t – тип IQ (перечисление DebugVarIQType_t).
|
||||
* @return int – Q-фактор (например, 24), 0: если t_iq_none, -1: ошибка.
|
||||
* @details Сопоставляет тип IQ переменной с соответствующим Q-значением.
|
||||
*/
|
||||
static int iqTypeToQ(DebugVarIQType_t t)
|
||||
{
|
||||
if (t == t_iq_none)
|
||||
@ -141,6 +177,13 @@ static int iqTypeToQ(DebugVarIQType_t t)
|
||||
return -1; // îøèáêà
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Преобразует переменную отладки в IQ формат.
|
||||
* @param var – указатель на переменную отладки.
|
||||
* @param ret_var – указатель для возврата значения в формате long.
|
||||
* @return int – 0: успех, 1: ошибка чтения, 2: неправильный формат, 3: переполнение.
|
||||
* @details Определяет формат IQ переменной, конвертирует её в long с учётом масштаба.
|
||||
*/
|
||||
static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var)
|
||||
{
|
||||
long iq_numb, iq_united, iq_final;
|
||||
@ -196,6 +239,14 @@ static int convertDebugVarToIQx(DebugVar_t *var, long *ret_var)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Прочитать значение переменной отладки.
|
||||
* @param var – указатель на структуру DebugVar.
|
||||
* @param int_var – указатель на переменную типа long для возврата целочисленного значения.
|
||||
* @param float_var – указатель на переменную типа float для возврата значения с плавающей точкой.
|
||||
* @return int – 0: успех, 1: ошибка указателей или неподдерживаемый тип, 3/4: ошибка выравнивания.
|
||||
* @details В зависимости от типа переменной считывает её значение и сохраняет в соответствующем указателе.
|
||||
*/
|
||||
static int getDebugVar(DebugVar_t *var, long *int_var, float *float_var)
|
||||
{
|
||||
if (!var || !int_var || !float_var || !var->Ptr)
|
||||
|
@ -3,6 +3,9 @@
|
||||
#include "IQmathLib.h"
|
||||
#include "DSP281x_Device.h"
|
||||
|
||||
/**
|
||||
* @brief Тип данных, на который указывает указатель переменной отладки.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
pt_unknown, // unknown
|
||||
@ -31,6 +34,9 @@ typedef enum
|
||||
// pt_arr_uint32, // unsigned long[]
|
||||
}DebugVarPtrType_t;
|
||||
|
||||
/**
|
||||
* @brief Типы IQ-представления переменной отладки.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
t_iq_none,
|
||||
@ -67,45 +73,65 @@ typedef enum
|
||||
t_iq30
|
||||
}DebugVarIQType_t;
|
||||
|
||||
typedef char DebugVarName_t[11];
|
||||
typedef char DebugVarName_t[11]; ///< Имя переменной отладки (до 10 символов + \0)
|
||||
|
||||
/**
|
||||
* @brief Описание переменной отладки.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char* Ptr;
|
||||
DebugVarPtrType_t ptr_type;
|
||||
DebugVarIQType_t iq_type;
|
||||
DebugVarIQType_t return_type;
|
||||
DebugVarName_t name;
|
||||
}DebugVar_t;
|
||||
char* Ptr; ///< Указатель на значение переменной
|
||||
DebugVarPtrType_t ptr_type; ///< Тип значения
|
||||
DebugVarIQType_t iq_type; ///< Тип IQ переменной (если есть)
|
||||
DebugVarIQType_t return_type;///< Тип IQ возвращаемого значения
|
||||
DebugVarName_t name; ///< Имя переменной
|
||||
} DebugVar_t;
|
||||
|
||||
/**
|
||||
* @brief Структура даты и времени.
|
||||
*/
|
||||
typedef struct {
|
||||
int year;
|
||||
char month;
|
||||
char day;
|
||||
char hour;
|
||||
char minute;
|
||||
int year; ///< Год (например, 2025)
|
||||
char month; ///< Месяц (1-12)
|
||||
char day; ///< День (1-31)
|
||||
char hour; ///< Часы (0-23)
|
||||
char minute; ///< Минуты (0-59)
|
||||
} DateTime_t;
|
||||
|
||||
/**
|
||||
* @brief Структура нижнего уровня отладки.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
DateTime_t build_date;
|
||||
unsigned int isVerified;
|
||||
DebugVar_t dbg_var;
|
||||
DateTime_t build_date; ///< Дата сборки
|
||||
unsigned int isVerified; ///< Флаг инициализации низкоуровненой отладки (0 — нет, 1 — успешно)
|
||||
DebugVar_t dbg_var; ///< Переменная для отладки
|
||||
}DebugLowLevel_t;
|
||||
extern DebugLowLevel_t debug_ll;
|
||||
extern DebugLowLevel_t debug_ll; ///< Глобальный экземпляр отладки нижнего уровня
|
||||
|
||||
|
||||
/** @brief Макрос инициализации даты */
|
||||
#define DATE_INIT {BUILD_YEAR, BUILD_MONTH, BUILD_DATA, BUILD_HOURS, BUILD_MINUTES}
|
||||
/** @brief Макрос инициализации переменной отладки */
|
||||
#define DEBUG_VAR_INIT {0, pt_uint16, t_iq_none, t_iq_none, "\0"}
|
||||
/** @brief Макрос инициализации нижнего уровня отладки */
|
||||
#define DEBUG_LOWLEVEL_INIT {DATE_INIT, 0, DEBUG_VAR_INIT}
|
||||
|
||||
|
||||
extern int DebugVar_Qnt;
|
||||
extern DebugVar_t dbg_vars[];
|
||||
extern int DebugVar_Qnt; ///< Количество переменных отладки
|
||||
extern DebugVar_t dbg_vars[]; ///< Массив переменных отладки
|
||||
|
||||
|
||||
/* Пример использования отладки */
|
||||
void Debug_Test_Example(void);
|
||||
|
||||
/* Читает значение переменной по индексу */
|
||||
int Debug_ReadVar(int var_ind, long *return_long);
|
||||
/* Читает имя переменной по индексу */
|
||||
int Debug_ReadVarName(int var_ind, DebugVarName_t name_ptr);
|
||||
/* Читает значение переменной с нижнего уровня */
|
||||
int Debug_LowLevel_ReadVar(long *return_long);
|
||||
/* Инициализирует отладку нижнего уровня */
|
||||
int Debug_LowLevel_Initialize(const char* external_date);
|
||||
|
||||
#endif //DEBUG_TOOLS
|
||||
|
Loading…
Reference in New Issue
Block a user