# KeilTools **Repository Path**: ronxia/keil-tools ## Basic Information - **Project Name**: KeilTools - **Description**: Keil‑MDK 工程分析小工具(Windows):把编译产物里的 Flash / RAM 占用、符号排行、调用图栈深、常见 C 代码风险 汇总成一份可交互的单文件 HTML 报告,顺手把几个"编译器不报、但一定会坑人"的写法挑出来。 - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 14 - **Forks**: 3 - **Created**: 2026-10-20 - **Last Updated**: 2026-11-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # KeilTools [![star](https://gitee.com/ronxia/keil-tools/badge/star.svg?theme=dark)](https://gitee.com/ronxia/keil-tools) [![fork](https://gitee.com/ronxia/keil-tools/badge/fork.svg?theme=dark)](https://gitee.com/ronxia/keil-tools) > A check-up for Keil MDK projects: **who is eating your Flash / RAM, how much stack to reserve, and which C patterns the compiler will not warn you about**. > One run produces an interactive, single-file HTML report that opens in your browser. One executable. No Python / Java, no network access, no changes to your project. It reads the project files, the linker `.map`, Keil's Callgraph `.htm` and your C sources, then writes `ResourceReport.html`. **What it is not**: not a compiler plugin (it does not take part in the build), not a simulator (no runtime data), and it never uploads your code. ## What it answers | You are probably… | How it used to be | What KeilTools shows | |---|---|---| | Adding features and Flash is almost full | The map is ordered by address; the big consumer is hard to spot | Function tab sorted by size, top 3 highlighted | | A new library added 4 KB of RAM — which one grew? | Diff two map files by eye | Variable tab with sizes / share; export CSV and compare two reports | | The RTOS task stack was a guess, and it HardFaults after a few days | Read Keil's callgraph and do the math yourself | Stack tab with a recommended size in bytes / words (50% headroom included) | | No idea who includes whom, so touching a header feels risky | Grep `#include` and follow it by hand | "Include chain" tab: a relation graph and a word cloud, click a node to see both directions | | Code review misses `if (a = 0)` or a missing `break` | Eyeballs, with compiler warnings usually disabled | 9 checks over the whole project, with file / line / suggestion | | You want to archive the numbers or share them | Screenshots or a hand-made spreadsheet | Single HTML file, copy rows, export CSV | ## Quick start > First, get the exe: the repository does not ship binaries, so build one with the steps under [Build](#build). ### 1. Let Keil produce the inputs (once) 1. `Options for Target` → `Listing` → check **Callgraph** under `Linker Listing`. 2. Run a full **Rebuild** (`Project → Rebuild all target files`). The Stack tab is simply empty without the callgraph. If the map and htm are not from the same build, the report shows a red warning at the top. Output directory, output name and memory sizes are read from the project file; both UTF-8 and GBK project files are handled — no project layout changes needed. ### 2. Run it inside the project folder Copy `KeilTools.exe` next to your `*.uvprojx` and double-click it, or use a shell: ```powershell cd D:\work\my_board\MDK-ARM E:\tools\KeilTools.exe ``` > ⚠️ It only analyses the **current working directory**; it will not ask you to pick a project. Run it from the project folder (or copy the exe there). A small `run.cmd` in the project folder saves the `cd`: ```bat @echo off cd /d "%~dp0" start "" "E:\tools\KeilTools.exe" ``` ### 3. Read the report `ResourceReport.html` opens automatically. Six tabs, sortable columns, live filter, copy-visible-rows and CSV export (UTF-8 with BOM, so Excel opens it cleanly). The report is a single file: rename it, move it or send it to a colleague and it still works. There is a theme switch in the top-right corner: **System / Dark / Light**. It follows your OS setting by default, the choice is remembered in the browser, and switching never reloads the page or rebuilds a table — even with tens of thousands of rows. ## What is in the report Three cards on top: **Total RO** (code + constants), **Flash** (Total ROM) and **RAM** (RW + ZI), each with capacity, free space and a usage bar. Utilisation at or above 85% / 95% raises a yellow / red alert. | Tab | Data | Typical use | |---|---|---| | Variables | RAM symbols: name / size / share / type (ZI·RW·OTHER) / object file | Find unexpectedly large arrays, buffers or unused tables | | Functions | Flash symbols: function / size / share / type (CODE·RODATA) / object file | See which module or library takes the most Flash, or whether an unused feature got linked in | | Stack | Function / Max Depth / recommended stack (bytes, words) / share / call chain | Size RTOS task stacks (use the task entry function's row), or check `Stack_Size` in the startup file | | Includes | File-level `#include` relations: relation graph, word cloud, cycles, unresolved includes | See module dependencies, estimate the blast radius of a header change, find headers nobody includes | | Risks | Severity / category / file / line / message / suggestion / code snippet | Run before code review; start with High | A few definitions, so the numbers match Keil's Build Output: - **Flash is not just Total RO**: Total ROM = RO + initialised RW, so the Flash number is slightly larger than RO; - **Recommended stack = `Max Depth × 1.5`, rounded up to 4 bytes**. Max Depth is the worst-case path from the static callgraph; the 1.5 factor is headroom; - The recommendation covers the call chain only. An RTOS task needs extra room for context switching and interrupt nesting; - A function with Max Depth ≥ 512 B is highlighted red; a share ≥ 10% is red, ≥ 5% is amber; - Rows are lazily built per tab, so even a symbol table with 10k+ rows does not freeze the browser (a tab switch takes ~0.1–0.2 s). > Deep link to a tab: `ResourceReport.html#tab-risk`. The include chain lives at `#tab-inc`. ## Reading the include chain Starting from the source files listed in the uvprojx, every `#include` is followed recursively to build a file-level graph. The tab has two views, switched with one click: - **Relation graph**: nodes are files, arrows mean "A includes B", and a node grows with the number of files including it. Wheel to zoom, drag the background to pan, drag a node to nudge it; click a node and a card on the right lists what it includes and what includes it (click those to jump), double-click the canvas to fit. - **Word cloud**: font size = how often the header is included. The "public base" headers (`stm32f4xx_hal.h`, …) are obvious at a glance; headers that almost nobody uses are tiny, and those are usually safe to delete. Details worth knowing: - **Headers outside the project are not drawn individually**: CMSIS, HAL and the C library are merged into one "external header" node per directory (e.g. `CMSIS`, `系统头`); open it to see which files it folded in. That keeps vendor headers from turning the graph into mush; - **Include cycles** (they exist even with include guards) are listed at the bottom of the tab and reported in the alert bar, e.g. `main.h → app.h → main.h`; - **Unresolved includes** (not found on the IncludePath) are folded into an external-header node (bare names go to `系统头`) and counted in the alert bar, which makes a missing include path easy to spot; - With very many nodes only the 700 most referenced are drawn (the tab says so); the search box filters by file name / path, and `工程内 only` keeps just the project's own `.c` / `.h` files; - **Export include-chain CSV** writes the edge list of the current filter to a file you can feed to Excel or another tool. ## The 9 checks This is the most useful tab: it targets patterns the compiler either accepts silently or warns about only behind a flag that is usually off. Every finding has a severity, file, line, message, suggestion and code snippet. | # | Category (filterable) | Severity | Looks like | Why it bites | |---|---|---|---|---| | 1 | `Assignment` | High | `if (a = 0)` | The condition is false; `a` is silently set to 0 | | 2 | `BitwiseLogic` | High / Warning | `if (a & b)` | Bitwise AND where logical AND was meant; no short-circuit | | 3 | `Precedence` | High | `if (flags & MASK == 0)` | `MASK == 0` is evaluated first; the branch is never taken | | 4 | `ShortCircuit` | High / Info | `if (p \|\| p->x)` | Dereferences `p` exactly when it is NULL | | 5 | `FloatCompare` | Warning | `if (f == 0.0f)` | Binary floats are almost never exactly equal | | 6 | `AssignCompare` | High | `if (a = f() != 0)` | Actually `a = (f() != 0)`, so `a` becomes 0 or 1 | | 7 | `SwitchRange` | High | `uint8_t` + `case 256` | The case is outside the type's range — dead code | | 8 | `DanglingElse` | Warning | nested `if` without braces | `else` binds to the inner `if`, not what the indentation says | | 9 | `SwitchBreak` | Warning | missing `break` at the end of a `case` | Execution falls through to the next branch | The rules prefer **missing a case over drowning you in noise**, so each has an explicit allow-list: literal / uppercase-macro bit masks, `p && p->x`, adjacent empty `case` labels, `/* fall through */`, and so on. Severity: **High** is almost certainly a bug, **Warning** is likely a bug but may be intentional, and **Info** needs a human decision. - Put `// risk-ignore` on a line to suppress all rules for that line; - Type a category (e.g. `Precedence`) in the search box to see only that class; - 71 regression cases (32 must fire, 39 must not) live in `KeilTools/KeilTools/testdata/risk-rules/`; `check-expected.py` (Python) compares expected vs actual after a rule change. > Per-rule background (why it bites, why the compiler stays quiet, what is allowed) is in the Chinese [detailed guide](KeilTools/README.md). ## Output and console By default the report is written to `\ResourceReport.html` and opened automatically. If that folder is not writable (read-only media, permissions, file lock), it falls back to the **exe folder**, then **`%TEMP%`**, and prints the real path. Analysis still succeeds. Result lines carry one of three prefixes: | Prefix | Meaning | |---|---| | `[OK]` | Done, followed by the report path | | `[WARNING]` | Can continue, but the result may be incomplete (e.g. no htm) | | `[ERROR]` | Failure reason with errno / Win32 error code | The three most common ones: - `MAP文件读取失败` (failed to read the map) → no build yet, or no map in the output / listing folder; - `map与htm文件时间相差超过1分钟` (map and htm differ by more than a minute) → the two files are from different builds; run `Rebuild`; - `工程目录不可写,报告已改存至 …` (project folder not writable, report saved to …) → not an analysis failure; the report is at the printed path. > Add `ResourceReport.html` to the project's `.gitignore` so it never gets committed by accident. ## FAQ **The Stack tab is empty.** Keil did not generate the callgraph (check `Callgraph` under `Options for Target → Listing`), or you ran `Compile` instead of `Build`. Check the option and do a full `Rebuild`. **The numbers differ from Keil's Build Output.** The report reads the `Total RO / RW / ROM` lines from the map; a stale map gives stale numbers. Also, Flash includes initialised RW, so it is larger than Total RO. **The window flashes and disappears.** That is success: generate report → open browser → exit. Run it from PowerShell / cmd if you want to read the log; it pauses only when it fails. **Windows SmartScreen blocks it.** The exe is not code-signed; click "More info → Run anyway". **Does it modify my project?** No. It only reads the project files and writes one `ResourceReport.html`. It does not add compiler options or touch sources. **Can I share the report?** Yes — it is self-contained and works offline. Note that the Risks tab contains source snippets, so check confidentiality before sending it outside. **C++ / IAR / GCC?** Only Keil MDK 5 project files (`.uvprojx` / `.uvoptx`) are recognised; the map / htm parsers are language-agnostic, but the 9 checks target C-style code and do not cover C++-specific issues. **Multiple targets?** Resource, stack and capacity figures come from the current target (the one with `IsCurrentTarget=1` in `.uvoptx`). Re-run after switching targets. **Several Keil projects in one folder?** Only the first `.uvprojx` and the first `.uvoptx` found are used, and they are not guaranteed to belong to the same project. Keep one project per folder, or put the exe in the project folder you want to analyse. ## Build Requirements: Windows 10/11, Visual Studio with the "Desktop development with C++" workload, C++20. No third-party dependencies. ```powershell git clone https://gitee.com/ronxia/keil-tools.git cd keil-tools # from a Developer PowerShell for VS msbuild KeilTools\KeilTools.slnx /p:Configuration=Release /p:Platform=x64 ``` Output: `KeilTools\x64\Release\KeilTools.exe`. - The project defaults to toolset `v145` (recent VS). With an older VS, change the platform toolset in all four configurations (Debug/Release, x64/x86) to the one you have, e.g. `v143`; - `.slnx` needs a recent VS / MSBuild; older versions can open `KeilTools\KeilTools\KeilTools.vcxproj` directly; - To hand the exe to someone else, switch **C/C++ → Code Generation → Runtime Library** to **Multi-threaded (/MT)** so they do not need the VC++ redistributable. ## Layout ```text keil-tools/ ├─ KeilTools/ │ ├─ KeilTools.slnx │ └─ KeilTools/ │ ├─ src/ │ │ ├─ keil/ uvprojx / uvoptx / map / callgraph htm readers │ │ ├─ analysis/ the 9 code-risk rules + the include-chain builder │ │ ├─ report/ single-file HTML report (style / tables / include chain / script) │ │ └─ app/ util/ core/ pipeline / utilities / types & constants │ └─ testdata/risk-rules/ regression cases (standalone minimal Keil project) ├─ LICENSE └─ README.md ``` Dependencies point one way: `main → app → {keil, analysis, report} → util → core`. ## Known limits These are deliberate trade-offs: - **Windows only** — uses Win32 APIs (console code page, file times, `ShellExecute`); - **Keil MDK 5 only** — relies on `.uvprojx` / `.uvoptx` and Keil's map / htm formats; - **Rules are text-level heuristics** — no full AST or type inference, hence the allow-lists and the occasional false negative; - **Condition rules cover `if` / `while` only** — the condition of a `for` loop and the ternary operator are not scanned; - **Stack figures come from the static callgraph** — no interrupt nesting, function pointers or dynamic recursion; - **The include chain is a text-level scan** — macros and conditional compilation are not evaluated (an `#include` under `#if 0` still counts), and the IncludePath comes from the active target's C compiler settings only; headers outside the project are merged per directory and not expanded further, and the graph is capped at 4000 nodes; - **The report is GBK-encoded HTML** — fine in a browser, mojibake if opened directly in an editor (VS Code defaults to UTF-8). ## Feedback and license - Bugs and rule requests: open an [Issue](https://gitee.com/ronxia/keil-tools/issues) with your Keil version and a minimal code snippet (sanitised); - PRs are welcome; if you change a rule, add cases under `testdata/risk-rules/` and run the regression; - License: **Mulan Permissive Software License, Version 2 (Mulan PSL v2)** — see [LICENSE](LICENSE); - If this tool saves you half a day, a star is enough.