├── .gitee
├── ISSUE_TEMPLATE.zh-CN.md
└── PULL_REQUEST_TEMPLATE.zh-CN.md
├── .gitignore
├── LICENSE
├── README.md
├── README_EN.md
├── images
├── create_map.png
├── gcc.png
├── gcc_compile.png
├── keil_project_name.png
├── main.png
├── open_file.gif
├── path_config.png
├── select_listing_folder.png
├── space_example.png
└── user_command.png
├── keil-build-viewer.c
└── keil-build-viewer.h
/.gitee/ISSUE_TEMPLATE.zh-CN.md:
--------------------------------------------------------------------------------
1 | ### 该问题是怎么引起的?
2 |
3 |
4 |
5 | ### 重现步骤
6 |
7 |
8 |
9 | ### 报错信息
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md:
--------------------------------------------------------------------------------
1 | ### 一、内容说明(相关的Issue)
2 |
3 |
4 |
5 | ### 二、建议测试周期和提测地址
6 | 建议测试完成时间:xxxx.xx.xx
7 | 投产上线时间:xxxx.xx.xx
8 | 提测地址:CI环境/压测环境
9 | 测试账号:
10 |
11 | ### 三、变更内容
12 | * 3.1 关联PR列表
13 |
14 | * 3.2 数据库和部署说明
15 | 1. 常规更新
16 | 2. 重启unicorn
17 | 3. 重启sidekiq
18 | 4. 迁移任务:是否有迁移任务,没有写 "无"
19 | 5. rake脚本:`bundle exec xxx RAILS_ENV = production`;没有写 "无"
20 |
21 | * 3.4 其他技术优化内容(做了什么,变更了什么)
22 | - 重构了 xxxx 代码
23 | - xxxx 算法优化
24 |
25 |
26 | * 3.5 废弃通知(什么字段、方法弃用?)
27 |
28 |
29 |
30 | * 3.6 后向不兼容变更(是否有无法向后兼容的变更?)
31 |
32 |
33 |
34 | ### 四、研发自测点(自测哪些?冒烟用例全部自测?)
35 | 自测测试结论:
36 |
37 |
38 | ### 五、测试关注点(需要提醒QA重点关注的、可能会忽略的地方)
39 | 检查点:
40 |
41 | | 需求名称 | 是否影响xx公共模块 | 是否需要xx功能 | 需求升级是否依赖其他子产品 |
42 | |------|------------|----------|---------------|
43 | | xxx | 否 | 需要 | 不需要 |
44 | | | | | |
45 |
46 | 接口测试:
47 |
48 | 性能测试:
49 |
50 | 并发测试:
51 |
52 | 其他:
53 |
54 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Object files
5 | *.o
6 | *.ko
7 | *.obj
8 | *.elf
9 |
10 | # Linker output
11 | *.ilk
12 | *.map
13 | *.exp
14 |
15 | # Precompiled Headers
16 | *.gch
17 | *.pch
18 |
19 | # Libraries
20 | *.lib
21 | *.a
22 | *.la
23 | *.lo
24 |
25 | # Shared objects (inc. Windows DLLs)
26 | *.dll
27 | *.so
28 | *.so.*
29 | *.dylib
30 |
31 | # Executables
32 | *.exe
33 | *.out
34 | *.app
35 | *.i*86
36 | *.x86_64
37 | *.hex
38 |
39 | # Debug files
40 | *.dSYM/
41 | *.su
42 | *.idb
43 | *.pdb
44 |
45 | # Kernel Module Compile Results
46 | *.mod*
47 | *.cmd
48 | .tmp_versions/
49 | modules.order
50 | Module.symvers
51 | Mkfile.old
52 | dkms.conf
53 |
54 | # for keil
55 | *.bin
56 | *.dbgconf
57 | *.lst
58 | *.__i
59 | *.SBR
60 | *.Administrator
61 | *.uvguix.*
62 | *.axf
63 | *.htm
64 | *.lnp
65 | *.dep
66 | *.json
67 | *.log
68 | **/.vscode/
69 | /Keil_C51/list/
70 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # keil-build-viewer v1.6
2 |
3 | ## [English](./README_EN.md)
4 |
5 | 
6 |
7 | ## 1 介绍
8 | 这是一个 keil 的编译信息展示增强工具,支持芯片存储器的可视化展示,轻量且无任何依赖。具备以下功能:
9 | 1. 解析参与编译的每个文件对 RAM 和 flash 的占用情况
10 | - 自动忽略不被包含进编译的文件
11 | - 自动检索被 keil 改名的文件
12 | - **支持双击打开文件**
13 | - 支持关闭该信息的展示
14 | - 支持仅显示文件名
15 |
16 | 2. 分析芯片的 RAM 和 flash 的使用情况,使用进度条可视化展示
17 | - `■` 或 `#` 或 `X` 表示实际占用的区域
18 | - `□` 或 `O` 表示 zero initialize 的区域
19 | - `_` 表示未被使用的区域
20 |
21 | 3. 二次编译后新增与减少的数据量展示
22 | - 通过对比上次的编译结果,**显示本次编译新增或减少的数据量大小,单位是 byte**
23 | - 若是新增的文件,则会显示 `[NEW]`
24 |
25 | 4. 自动搜索本级目录的 keil 工程,因此可无参调用
26 | - 默认选择搜索到的最后一个 keil 工程
27 | - 支持输入绝对路径指定 keil 工程
28 | - 支持仅输入文件名指定 keil 工程(必须是同级目录,可不带文件扩展名)
29 | - **若路径或工程名有空格,则使用 `""` 括起来**
30 |
31 | 5. 支持输入参数修改选项
32 | - 如第 4 功能所描述的,指定 keil 工程
33 | - `-OBJ` 显示每个文件的 RAM 和 flash 的占用信息(默认)
34 | - `-NOOBJ` 不显示每个文件的 RAM 和 flash 的占用信息
35 | - `-PATH` 显示每个文件的相对路径(默认)
36 | - `-NOPATH` 仅显示每个文件的文件名
37 | - `以下为 v1.5 新增功能`
38 | - `-NOLOG` 不生成 log 文件
39 | - `-STYLE0` 进度条样式跟随系统(默认)
40 | - `-STYLE1` 进度条样式一: `|###OOO____|` (非中文环境时默认样式)
41 | - `-STYLE2` 进度条样式二: `|XXXOOO____|`
42 | - **以上命令不区分大小写**
43 |
44 | 6. 显示最大的栈使用
45 | - 数据来自 keil ,静态无法精确分析,数据仅供参考
46 |
47 | 7. 支持放置于公共目录后,可在任意目录调用本工具,无需跟随 keil uvproj(x) 工程
48 | - v1.4 新增功能
49 | - **必须设置好系统环境变量,并把 `keil-build-viewer.exe` 放置于系统环境变量所指定的目录中**,建议使用系统环境变量 `Path`
50 | - 可节省拷贝 `keil-build-viewer.exe` 至对应 keil uvproj(x) 工程的步骤,但 `after build` 仍需填写,详见 `2 在 keil 中使用`
51 |
52 | > **说明:** 本工具的所有参数可不按顺序输入,为空时表示选择默认值,但参数与参数之间需用**空格**隔开
53 |
54 | > **双击打开对应文件动画演示**
55 | 
56 |
57 | ## 2 在 keil 中使用
58 | 1. 在 keil 中调用方式很简单,下载[发行版](https://gitee.com/DinoHaw/keil-build-viewer/releases)中的 `keil-build-viewer.exe` 放在 keil 对应的 uvproj(x) 工程的同级目录,按下图进行配置即可。如需输入其他选项,则在 `keil-build-viewer.exe` 后跟随输入。如仅显示每个文件的文件名,则可填写:
59 | ```
60 | keil-build-viewer.exe -NOPATH
61 | ```
62 |
63 | 2. 在 cmd 或 powershell 中使用同理,仅需添加前缀 `.\` 即可。如:
64 | ```
65 | .\keil-build-viewer.exe
66 | ```
67 | 
68 |
69 |
70 | ## 3 我想自己编译这个工具
71 | **本代码仅支持 windows 系统**
72 | ### 3.1 预备操作
73 | 0. 如果你已经安装了 gcc ,请忽略本步骤
74 | 1. 下载 gcc 编译器,为了考虑兼容性,这里提供一个 32 位的 mingw 下载链接: [i686-13.1.0-release-posix-dwarf-ucrt-rt_v11-rev1.7z](https://github.com/niXman/mingw-builds-binaries/releases/download/13.1.0-rt_v11-rev1/i686-13.1.0-release-posix-dwarf-ucrt-rt_v11-rev1.7z)
75 | 2. 解压后放在任意路径,此处以 `C:\mingw32` 为例
76 | 3. 配置好环境变量
77 | 
78 |
79 | 4. 打开 `powershell` 或 `cmd` 输入 `gcc -v` ,出现下图内容表示配置成功
80 | 
81 |
82 | ### 3.2 编译
83 | 1. 打开 `powershell` 或 `cmd` 并定位至代码目录
84 | - 若使用 `powershell` ,可在代码目录空白处按住 `shift` 键同时单击鼠标右键选择打开 `powershell` ,将自动定位到代码目录
85 |
86 | 2. 执行以下 gcc 命令
87 | ```
88 | gcc .\keil-build-viewer.c -o .\keil-build-viewer.exe
89 | ```
90 | 3. 无任何提示信息,编译通过
91 | 
92 |
93 |
94 | ## 4 问题解答
95 | 1. 出现 `[ERROR] NO keil project found` 之类的提示
96 | > 确认 `keil-build-viewer.exe` 放在了你需要查看的 keil uvproj(x) 工程同级目录
97 |
98 | 2. 出现 `[ERROR] listing path is empty` 之类的提示
99 | > 在 keil 中选择你要放置的 listing 相关文件的文件夹
100 | 
101 |
102 | 3. 出现 `[ERROR] generate map file is not checked` 或 `[ERROR] Check if a map file exists` 之类的提示
103 | > 确认 keil 已经勾选了下图这些选项
104 | 
105 |
106 | 4. 若编译信息缺失或与实际有偏差
107 | > 确认解析的工程为目标工程(同级目录存在多个工程时)
108 | > 可通过解析出的前置信息核对当前工具所解析的工程,若发现不一致,可在 `keil-build-viewer.exe` 之后指定工程名,如:
109 | ```
110 | keil-build-viewer.exe TIMER
111 | 或
112 | keil-build-viewer.exe TIMER.uvprojx
113 | ```
114 | > 
115 |
116 | 5. 若工程目录或工程名有空格,将其使用 `""` 括起来
117 | > 
118 |
119 | 6. 其他问题请提 issues 或联系作者。
120 |
121 |
122 | ## 重要说明
123 | > **1. 目前仅支持 keil MDK。**
124 | >
125 | > **2. 不支持解析通过 RTE 添加的文件**
126 |
127 |
128 | ## 修改记录
129 | | 版本 | 日期 |修改者 |修改内容 |
130 | |:-----:|:----------:|--------------|---------------------------------------------------|
131 | | v1.0 | 2023-11-10 | Dino | 初版发布 |
132 | | v1.1 | 2023-11-11 | Dino | 1. 适配 RAM 和 ROM 的解析 |
133 | | v1.2 | 2023-11-11 | Dino | 1. 适配 keil4 的 map 文件
2. 增加检测到开启 LTO 后打印提示信息
3. 修复开启 LTO 后无打印 region 的问题 |
134 | | v1.3 | 2023-11-12 | Dino | 1. 修复工程存在多个 lib 时仅解析一个的问题 |
135 | | v1.4 | 2023-11-21 | Dino | 1. 增加将本工具放置于系统环境变量 Path 所含目录的功能 |
136 | | v1.5 | 2023-11-30 | Dino | 1. 新增更多的 progress bar 样式
2. 新增解析自定义的 memory area
3. 修复 RAM 和 ROM 信息缺失时显示异常的问题 |
137 | | v1.5a | 2023-11-30 | Dino | 1. 修复 object 数据溢出的问题
2. 修改进度条内存大小的显示策略,不再四舍五入 |
138 | | v1.5b | 2023-12-02 | Dino | 1. 修复保存文件路径内存动态分配过小的问题 |
139 | | v1.6 | 2024-12-11 | Dino | 1. 【修复】有多个 region 时导致显示回车多行的问题
2. 【修复】Execution Region 被错误识别的问题
3. 【修复】当 Execution Region 在其他 Loard Region 中使用会重复显示的问题
4. 【修改】将未使用的 memory 放在同一分类显示
5. 【修改】若 Execution Region Size 为 UINT32_MAX 时,则修改为对应 memory 的 Size |
140 |
141 |
142 | ## 参与贡献
143 | 1. Fork 本仓库
144 | 2. 新建 Feat_xxx 分支
145 | 3. 提交代码
146 | 4. 新建 Pull Request
147 |
148 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | # keil-build-viewer v1.6
2 |
3 | 
4 |
5 | ## 1 Introduction
6 | This is a keil compilation information display enhancement tool that supports the visualization of chip memory, lightweight and no dependencies. It has the following features:
7 | 1. Analyze the RAM and flash usage of each file involved in the compilation
8 | - Automatically ignore files that are not included in the compilation
9 | - Automatic retrieval of files renamed by keil
10 | - **Support for double-clicking to open documents**
11 | - Support for turning off the display of this information
12 | - Support for displaying only file names
13 |
14 | 2. Analyze the RAM and flash usage of the chip and visualize it with a progress bar
15 | - `■` or `#` or `X` Indicates physically occupied area
16 | - `□` or `O` Indicates the area of zero initialize
17 | - `_` Indicates unused areas
18 |
19 | 3. Demonstration of the amount of data added and subtracted after secondary compilation
20 | - By comparing the results of the last compilation **shows the size of the amount of data added or subtracted by this compilation, in byte**
21 | - If the file is new, `[NEW]` will be displayed
22 |
23 | 4. Automatically searches for keil projects in this level of the directory, so it can be called without parameters
24 | - The last keil project searched is selected by default
25 | - Support for entering absolute paths to specify keil projects
26 | - Support for specifying keil projects by filename only (must be a directory of the same level, no file extensions)
27 | - **If there are spaces in the path or project name, enclose them in `""`**
28 |
29 | 5. Support for input parameter modification options
30 | - Specify the keil project as described in function 4
31 | - `-OBJ` Display RAM and flash occupancy information for each file (default)
32 | - `-NOOBJ` Does not display RAM and flash usage information for each file
33 | - `-PATH` Displays the relative path to each file (default)
34 | - `-NOPATH` Shows only the filename of each file
35 | - `The following features are new in v1.5`
36 | - `-NOLOG` No log file is generated
37 | - `-STYLE0` Progress bar style following system (default)
38 | - `-STYLE1` Progress bar style 1: `|####OOO____|` (default style for non-Chinese environments)
39 | - `-STYLE2` Progress Bar Style 2: `|XXXOOOO____|`
40 | - **The above commands are not case-sensitive**
41 |
42 | 6. Show maximum stack usage
43 | - Data from keil, static can not be analyzed accurately, data for reference only
44 |
45 | 7. support placed in the public directory, you can call the tool in any directory, without following keil uvproj(x) project
46 | - v1.4 New Features
47 | - **You must set the system environment variable and place `keil-build-viewer.exe` in the directory specified by the system environment variable**. It is recommended that you use the system environment variable `Path`.
48 | - This saves copying `keil-build-viewer.exe` to the corresponding keil uvproj(x) project, but `after build` still needs to be filled in, see `2 Use in keil` for details.
49 |
50 | > **Description:** All parameters of this tool can be entered out of order, and when it is empty, it means that the default value is selected, but the parameters need to be separated from each other by **space**.
51 |
52 | > **Double-click to open the corresponding file animated presentation**
53 | 
54 |
55 | ## 2 Use in keil
56 | 1. The way to invoke in keil is very simple, download latest version of `keil-build-viewer.exe` from [releases](https://github.com/DinoHaw/keil-build-viewer/releases) and put it in the same level directory of the uvproj(x) project corresponding to keil, and configure it according to the following figure. The configuration is done as shown below. If you want to enter other commands, enter them after `keil-build-viewer.exe`. If you want to display only the filename of each file, you can fill in the following:
57 | ```
58 | keil-build-viewer.exe -NOPATH
59 | ```
60 |
61 | 2. In cmd or powershell use the same thing, just add the prefix `.\`. For example:
62 | ```
63 | .\keil-build-viewer.exe
64 | ```
65 | 
66 |
67 | ## 3 I want to compile this tool myself ##
68 | **This code is only supported on windows systems**.
69 | ### 3.1 Preparatory operations
70 | 0. If you already have gcc installed, ignore this step.
71 | 1. Download the gcc compiler, for compatibility, here is a 32-bit mingw download link: [i686-13.1.0-release-posix-dwarf-ucrt-rt_v11-rev1.7z](https://github.com/niXman/mingw-builds-binaries/releases/download/13.1.0-rt_v11-rev1/i686-13.1.0-release-posix-dwarf-ucrt-rt_v11-rev1.7z)
72 | 2. Unzip the program and put it in any path, take `C:\mingw32` as an example.
73 | 3. Configure system environment variables
74 | 
75 |
76 | 4. Open `powershell` or `cmd` and type `gcc -v`, and the following image appears to indicate successful configuration
77 | 
78 |
79 | ### 3.2 Compilation
80 | 1. Open `powershell` or `cmd` and locate the code directory.
81 | - If you are using `powershell`, hold down the `shift` key while clicking the right mouse button on an empty space in the code directory and select Open `powershell`, which will automatically locate the code directory.
82 |
83 | 2. Execute the following gcc command
84 | ```
85 | gcc .\keil-build-viewer.c -o .\keil-build-viewer.exe
86 | ```
87 | 3. Compilation passes without any message
88 | 
89 |
90 | ## 4 Questions answered
91 | 1. A prompt such as `[ERROR] NO keil project found` appears.
92 | > Confirm that `keil-build-viewer.exe` is placed in the same directory as the keil uvproj(x) project you need to view.
93 |
94 | 2. A prompt such as `[ERROR] listing path is empty` appears.
95 | > Select the folder in keil where you want to place the listing-related files.
96 | 
97 |
98 | 3. A prompt such as `[ERROR] generate map file is not checked` or `[ERROR] Check if a map file exists` appears.
99 | > Make sure that keil has checked the options shown below.
100 | 
101 |
102 | 4. If compilation information is missing or deviates from reality.
103 | > Confirm that the parsed project is the target project (when there are multiple projects in the same level of directory)
104 | > You can check the current project parsed by the tool with the parsed predecessor information, and if you find inconsistencies, you can specify the project name after `keil-build-viewer.exe`, for example:
105 | ```
106 | keil-build-viewer.exe TIMER
107 | or
108 | keil-build-viewer.exe TIMER.uvprojx
109 | ```
110 | > 
111 |
112 | 5. If there are spaces in the project directory or project name, enclose them in `""`.
113 | > 
114 |
115 | 6. For other questions, please raise issues or contact the authors.
116 |
117 | ## Important note
118 | > **1. Currently only the keil MDK is supported.**
119 | >
120 | > **2. Does not support parsing of files added via RTE**
121 |
122 | ## Modify the record
123 | | version | date | author | revise content |
124 | |:-----:|:----------:|--------------|---------------------------------------------------|
125 | | v1.0 | 2023-11-10 | Dino | Initial release |
126 | | v1.1 | 2023-11-11 | Dino | 1. Adaptation of RAM and ROM parsing |
127 | | v1.2 | 2023-11-11 | Dino | 1. Adapted map file for keil4
2. Added print message when LTO is detected to be on
3. Fixed the problem that no region is printed when LTO is enabled |
128 | | v1.3 | 2023-11-12 | Dino | 1. Fixed the issue that only one lib is parsed when there are multiple libs in the project |
129 | | v1.4 | 2023-11-21 | Dino | 1. Add the function of placing this tool in the directory contained in the system environment variable Path |
130 | | v1.5 | 2023-11-30 | Dino | 1. Add more progress bar styles
2. Add parsing customized memory area
3. Fix the problem of displaying an exception when the RAM and ROM information is missing |
131 | | v1.5a | 2023-11-30 | Dino | 1. Fix object data overflow problem
2. Change the display strategy of progress bar memory size, no longer round up |
132 | | v1.5b | 2023-12-02 | Dino | 1. Fix save file path memory dynamic allocation is too small |
133 | | v1.6 | 2024-12-11 | Dino | 1. [Fix] Problem with multiple regions causing multiple lines to be displayed
2. [Fix] Problem of Execution Region being recognized incorrectly.
3. [Fix] Problem of duplicate display when Execution Region is used in other Loard Region.
4. [Modified] Display unused memory in the same category.
5. [Modified] If the Execution Region Size is UINT32_MAX, it will be changed to the size of the corresponding memory. |
134 |
--------------------------------------------------------------------------------
/images/create_map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/create_map.png
--------------------------------------------------------------------------------
/images/gcc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/gcc.png
--------------------------------------------------------------------------------
/images/gcc_compile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/gcc_compile.png
--------------------------------------------------------------------------------
/images/keil_project_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/keil_project_name.png
--------------------------------------------------------------------------------
/images/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/main.png
--------------------------------------------------------------------------------
/images/open_file.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/open_file.gif
--------------------------------------------------------------------------------
/images/path_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/path_config.png
--------------------------------------------------------------------------------
/images/select_listing_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/select_listing_folder.png
--------------------------------------------------------------------------------
/images/space_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/space_example.png
--------------------------------------------------------------------------------
/images/user_command.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DinoHaw/keil-build-viewer/5b0ed7d2740851730c8b05e33e2c0cda266ad1d4/images/user_command.png
--------------------------------------------------------------------------------
/keil-build-viewer.c:
--------------------------------------------------------------------------------
1 | /**
2 | * \file keil-build-viewer.c
3 | * \brief main application
4 | */
5 |
6 | /*
7 | * Copyright (c) 2023 Dino Haw
8 | *
9 | * Permission is hereby granted, free of charge, to any person
10 | * obtaining a copy of this software and associated documentation
11 | * files (the "Software"), to deal in the Software without restriction,
12 | * including without limitation the rights to use, copy, modify, merge,
13 | * publish, distribute, sublicense, and/or sell copies of the Software,
14 | * and to permit persons to whom the Software is furnished to do so,
15 | * subject to the following conditions:
16 | *
17 | * The above copyright notice and this permission notice shall be
18 | * included in all copies or substantial portions of the Software.
19 | *
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 | * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 | * OTHER DEALINGS IN THE SOFTWARE.
28 | *
29 | * This file is keil-build-viewer.
30 | *
31 | * Author: Dino Haw <347341799@qq.com>
32 | * Version: v1.6
33 | * Change Logs:
34 | * Version Date Author Notes
35 | * v1.0 2023-11-10 Dino the first version
36 | * v1.1 2023-11-11 Dino 1. 适配 RAM 和 ROM 的解析
37 | * v1.2 2023-11-11 Dino 1. 适配 keil4 的 map 文件
38 | * 2. 增加检测到开启 LTO 后打印提示信息
39 | * 3. 修复开启 LTO 后无打印 region 的问题
40 | * v1.3 2023-11-12 Dino 1. 修复工程存在多个 lib 时仅解析一个的问题
41 | * v1.4 2023-11-21 Dino 1. 增加将本工具放置于系统环境变量 Path 所含目录的功能
42 | * v1.5 2023-11-30 Dino 1. 新增更多的 progress bar 样式
43 | * 2. 新增解析自定义的 memory area
44 | * 3. 修复 RAM 和 ROM 信息缺失时显示异常的问题
45 | * v1.5a 2023-11-30 Dino 1. 修复 object 数据溢出的问题
46 | * 2. 修改进度条内存大小的显示策略,不再四舍五入
47 | * v1.5b 2023-12-02 Dino 1. 【修复】保存文件路径内存动态分配过小的问题
48 | * v1.6 2024-12-11 Dino 1. 【修复】有多个 region 时导致显示回车多行的问题
49 | * 2. 【修复】Execution Region 被错误识别的问题
50 | * 3. 【修复】当 Execution Region 在其他 Loard Region 中使用会重复显示的问题
51 | * 4. 【修改】将未使用的 memory 放在同一分类显示
52 | * 5. 【修改】若 Execution Region Size 为 UINT32_MAX 时,则修改为对应 memory 的 Size
53 | */
54 |
55 | /* Includes ------------------------------------------------------------------*/
56 | #include "keil-build-viewer.h"
57 |
58 |
59 | /* Private variables ---------------------------------------------------------*/
60 | static FILE * _log_file;
61 | static bool _is_save_log = true;
62 | static bool _is_has_unused_region;
63 | static bool _is_display_object = true;
64 | static bool _is_display_path = true;
65 | static char _line_text[1024];
66 | static char * _current_dir;
67 | static ENCODING_TYPE _encoding_type = ENCODING_TYPE_GBK;
68 | static PROGRESS_STYLE _progress_style = PROGRESS_STYLE_0;
69 | static struct prj_path_list * _keil_prj_path_list;
70 | static struct memory_info * _memory_info_head;
71 | static struct file_path_list * _file_path_list_head;
72 | static const char * _keil_prj_extension[] =
73 | {
74 | ".uvprojx",
75 | ".uvproj"
76 | };
77 | static struct command_list _command_list[] =
78 | {
79 | {
80 | .cmd = "-NOLOG",
81 | .desc = "NOT save log file",
82 | },
83 | {
84 | .cmd = "-OBJ",
85 | .desc = "Display the ram and flash occupancy of each object file (default)",
86 | },
87 | {
88 | .cmd = "-NOOBJ",
89 | .desc = "NOT display the ram and flash occupancy of each object file",
90 | },
91 | {
92 | .cmd = "-PATH",
93 | .desc = "Display each object file path (default)",
94 | },
95 | {
96 | .cmd = "-NOPATH",
97 | .desc = "NOT display each object file path",
98 | },
99 | {
100 | .cmd = "-STYLE0",
101 | .desc = "Progress bar style: following system (default)",
102 | },
103 | {
104 | .cmd = "-STYLE1",
105 | .desc = "Progress bar style: |###OOO____| (when non-Chinese and not specified progress bar style)",
106 | },
107 | {
108 | .cmd = "-STYLE2",
109 | .desc = "Progress bar style: |XXXOOO____|",
110 | },
111 | };
112 |
113 |
114 | /**
115 | * @brief 主程序
116 | * @note
117 | * @param argc: 参数数量
118 | * @param argv[]: 参数列表
119 | * @retval 0: 正常 | -x: 错误
120 | */
121 | int main(int argc, char *argv[])
122 | {
123 | clock_t run_time = clock();
124 |
125 | struct load_region *load_region_head = NULL;
126 | struct object_info *object_info_head = NULL;
127 | struct load_region *record_load_region_head = NULL;
128 | struct object_info *record_object_info_head = NULL;
129 |
130 | /* 获取编码格式 */
131 | UINT acp = GetACP();
132 | if (acp == 936) {
133 | _encoding_type = ENCODING_TYPE_GBK;
134 | }
135 | else if (acp == 950) {
136 | _encoding_type = ENCODING_TYPE_BIG5;
137 | }
138 | else {
139 | _encoding_type = ENCODING_TYPE_OTHER;
140 | }
141 |
142 | /* 1. 获取程序运行的工作目录 */
143 | int result = 0;
144 | DWORD buff_len = GetCurrentDirectory(0, NULL);
145 | if (buff_len == 0)
146 | {
147 | printf("\n[ERROR] %s %s\n", APP_NAME, APP_VERSION);
148 | printf("[ERROR] Get current directory length failed (code: %d)\n", GetLastError());
149 | result = -20;
150 | goto __exit;
151 | }
152 |
153 | _current_dir = (char *)malloc(buff_len + 1);
154 | if (_current_dir == NULL)
155 | {
156 | printf("\n[ERROR] %s %s\n", APP_NAME, APP_VERSION);
157 | printf("[ERROR] Failed to allocate current directory memory\n");
158 | result = -21;
159 | goto __exit;
160 | }
161 |
162 | buff_len = GetCurrentDirectory(buff_len, _current_dir);
163 | if (buff_len == 0)
164 | {
165 | printf("\n[ERROR] %s %s\n", APP_NAME, APP_VERSION);
166 | printf("[ERROR] Get current directory failed. (code: %d)\n", GetLastError());
167 | result = -22;
168 | goto __exit;
169 | }
170 |
171 | /* 创建 log 文件 */
172 | char *file_path = NULL;
173 | size_t file_path_size = 0;
174 |
175 | if (buff_len < MAX_PATH) {
176 | file_path_size = MAX_PATH * 2;
177 | } else {
178 | file_path_size = buff_len * 2;
179 | }
180 | file_path = (char *)malloc(file_path_size);
181 | if (file_path == NULL)
182 | {
183 | printf("\n[ERROR] %s %s\n", APP_NAME, APP_VERSION);
184 | printf("[ERROR] Failed to allocate file path memory\n");
185 | result = -23;
186 | goto __exit;
187 | }
188 |
189 | /* 2. 搜索同级目录或指定目录下的所有 keil 工程并打印 */
190 | _keil_prj_path_list = prj_path_list_init(MAX_PATH_QTY);
191 |
192 | search_files_by_extension(_current_dir,
193 | buff_len,
194 | _keil_prj_extension,
195 | sizeof(_keil_prj_extension) / sizeof(char *),
196 | _keil_prj_path_list);
197 |
198 | snprintf(file_path, file_path_size, "%s\\%s.log", _current_dir, APP_NAME);
199 |
200 | /* 3. 参数处理 */
201 | char input_param[MAX_PATH] = {0};
202 | char keil_prj_name[MAX_PRJ_NAME_SIZE] = {0};
203 | if (argc > 1)
204 | {
205 | int err_param = 0;
206 | int res = parameter_process(argc,
207 | argv,
208 | keil_prj_name,
209 | sizeof(keil_prj_name),
210 | input_param,
211 | sizeof(input_param),
212 | &err_param);
213 |
214 | if (_is_save_log) {
215 | _log_file = fopen(file_path, "w+");
216 | }
217 |
218 | if (res == -1)
219 | {
220 | log_print(_log_file, "\n[ERROR] INVALID INPUT (code: %d): %s\n", GetLastError(), argv[1]);
221 | result = -1;
222 | goto __exit;
223 | }
224 | else if (res == -2)
225 | {
226 | log_print(_log_file, "\n[ERROR] INVALID INPUT: %s\n", argv[1]);
227 | log_print(_log_file, "[ERROR] Please enter the absolute path or keil project name with extension\n");
228 | result = -2;
229 | goto __exit;
230 | }
231 | else if (res == -3)
232 | {
233 | log_print(_log_file, "\n[ERROR] INVALID INPUT: %s\n", argv[err_param]);
234 | log_print(_log_file, "[ERROR] Only the following commands are supported\n");
235 | for (size_t i = 0; i < sizeof(_command_list) / sizeof(struct command_list); i++) {
236 | log_print(_log_file, "\t%s\t %s\n", _command_list[i].cmd, _command_list[i].desc);
237 | }
238 | result = -3;
239 | goto __exit;
240 | }
241 | else if (res == -4)
242 | {
243 | log_print(_log_file, "\nYou can control the displayed information by entering the following commands\n \n");
244 | for (size_t i = 0; i < sizeof(_command_list) / sizeof(struct command_list); i++) {
245 | log_print(_log_file, "\t%s\t %s\n", _command_list[i].cmd, _command_list[i].desc);
246 | }
247 | result = 0;
248 | goto __exit;
249 | }
250 | }
251 | else {
252 | _log_file = fopen(file_path, "w+");
253 | }
254 |
255 | log_print(_log_file, "\n=================================================== %s %s ==================================================\n ", APP_NAME, APP_VERSION);
256 |
257 | if (_keil_prj_path_list->size > 0) {
258 | log_save(_log_file, "\n[Search keil project] %d item(s)\n", _keil_prj_path_list->size);
259 | }
260 |
261 | for (size_t i = 0; i < _keil_prj_path_list->size; i++) {
262 | log_save(_log_file, "\t%s\n", _keil_prj_path_list->items[i]);
263 | }
264 |
265 | log_save(_log_file, "\n[User input] %s\n", input_param);
266 | log_save(_log_file, "[Current folder] %s\n", _current_dir);
267 | log_save(_log_file, "[Encoding] %d\n", acp);
268 |
269 | /* 4. 确定 keil 工程 */
270 | char *keil_prj_path;
271 | if (input_param[0] != '\0')
272 | {
273 | log_print(_log_file, "\n[Hint] You specify the keil project!\n");
274 | keil_prj_path = input_param;
275 | }
276 | else if (_keil_prj_path_list->size > 0)
277 | {
278 | keil_prj_path = _keil_prj_path_list->items[_keil_prj_path_list->size - 1];
279 |
280 | char *last_slash = strrchr(keil_prj_path, '\\');
281 | if (last_slash)
282 | {
283 | last_slash += 1;
284 | strncpy_s(keil_prj_name, sizeof(keil_prj_name), last_slash, strnlen(last_slash, sizeof(keil_prj_name)));
285 | }
286 | }
287 | else
288 | {
289 | log_print(_log_file, "\n[ERROR] NO keil project found\n");
290 | log_print(_log_file, "[ERROR] Please check: %s\n", input_param);
291 | result = -4;
292 | goto __exit;
293 | }
294 |
295 | log_save(_log_file, "[Keil project path] %s\n", keil_prj_path);
296 | log_save(_log_file, "[Keil project name] %s\n", keil_prj_name);
297 |
298 | bool is_keil4_prj = false;
299 | if (keil_prj_name[strlen(keil_prj_name) - 1] == 'j') {
300 | is_keil4_prj = true;
301 | }
302 | log_save(_log_file, "[Is keil v4] %d\n", is_keil4_prj);
303 |
304 | char keil_prj_full_name[MAX_PRJ_NAME_SIZE] = {0};
305 | memcpy_s(keil_prj_full_name, sizeof(keil_prj_full_name), keil_prj_name, strnlen_s(keil_prj_name, sizeof(keil_prj_full_name)));
306 | char *dot = strrchr(keil_prj_name, '.');
307 | if (dot) {
308 | *dot = '\0';
309 | }
310 |
311 | /* 5. 获取启用的 project target */
312 | /* 打开同名的 .uvoptx 或 .uvopt 文件 */
313 | char target_name[MAX_PRJ_NAME_SIZE] = {0};
314 | snprintf(file_path, file_path_size, "%s\\%s.uvopt", _current_dir, keil_prj_name);
315 | if (is_keil4_prj == false) {
316 | strncat_s(file_path, file_path_size, "x", 1);
317 | }
318 |
319 | /* 不存在 uvoptx 文件时,默认选择第一个 target name */
320 | bool is_has_target = true;
321 | if (uvoptx_file_process(file_path, target_name, sizeof(target_name)) == false)
322 | {
323 | is_has_target = false;
324 | log_print(_log_file, "\n[WARNING] can't open '%s'\n", file_path);
325 | log_print(_log_file, "[WARNING] The first project target is selected by default.\n");
326 | }
327 |
328 | /* 6. 获取 map 和 htm 文件所在的目录及 device 和 output_name 信息 */
329 | /* 打开同名的 .uvprojx 或 .uvproj 文件 */
330 | snprintf(file_path, file_path_size, "%s\\%s.uvproj", _current_dir, keil_prj_name);
331 | if (is_keil4_prj == false) {
332 | strncat_s(file_path, file_path_size, "x", 1);
333 | }
334 |
335 | char target_name_label[MAX_PRJ_NAME_SIZE * 2] = {0};
336 |
337 | if (is_has_target) {
338 | snprintf(target_name_label, sizeof(target_name_label), "%s%s", LABEL_TARGET_NAME, target_name);
339 | } else {
340 | strncpy_s(target_name_label, sizeof(target_name_label), LABEL_TARGET_NAME, strlen(LABEL_TARGET_NAME));
341 | }
342 |
343 | struct uvprojx_info uvprojx_file = {0};
344 | int res = uvprojx_file_process(file_path,
345 | target_name_label,
346 | &uvprojx_file,
347 | !is_has_target);
348 | if (res == -1)
349 | {
350 | log_print(_log_file, "\n[ERROR] can't open .uvproj(x) file\n");
351 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
352 | result = -5;
353 | goto __exit;
354 | }
355 | else if (res == -2)
356 | {
357 | log_print(_log_file, "\n[ERROR] contains unsupported types\n");
358 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
359 | result = -6;
360 | goto __exit;
361 | }
362 | else if (res == -3)
363 | {
364 | log_print(_log_file, "\n[ERROR] generate map file is not checked (Options for Target -> Listing -> Linker Listing)\n");
365 | result = -7;
366 | goto __exit;
367 | }
368 |
369 | log_save(_log_file, "\n[Device] %s\n", uvprojx_file.chip);
370 | log_save(_log_file, "[Target name] %s\n", uvprojx_file.target_name);
371 | log_save(_log_file, "[Output name] %s\n", uvprojx_file.output_name);
372 | log_save(_log_file, "[Output path] %s\n", uvprojx_file.output_path);
373 | log_save(_log_file, "[Listing path] %s\n", uvprojx_file.listing_path);
374 | log_save(_log_file, "[Is has pack] %d\n", uvprojx_file.is_has_pack);
375 | log_save(_log_file, "[Is enbale LTO] %d\n", uvprojx_file.is_enable_lto);
376 | log_save(_log_file, "[Is has user library] %d\n", uvprojx_file.is_has_user_lib);
377 | log_save(_log_file, "[Is custom scatter file] %d\n", uvprojx_file.is_custom_scatter);
378 |
379 | if (uvprojx_file.output_name[0] == '\0')
380 | {
381 | log_print(_log_file, "\n[ERROR] output name is empty\n");
382 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
383 | result = -8;
384 | goto __exit;
385 | }
386 | if (uvprojx_file.listing_path[0] == '\0')
387 | {
388 | log_print(_log_file, "\n[ERROR] listing path is empty\n");
389 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
390 | result = -9;
391 | goto __exit;
392 | }
393 |
394 | char *p_target_name = target_name;
395 | if (is_has_target == false) {
396 | p_target_name = uvprojx_file.target_name;
397 | }
398 | log_print(_log_file, "\n[%s] [%s] [%s]", keil_prj_full_name, p_target_name, uvprojx_file.chip);
399 |
400 | if (uvprojx_file.is_enable_lto) {
401 | log_print(_log_file, " [LTO enable]\n \n");
402 | } else {
403 | log_print(_log_file, " [LTO disable]\n \n");
404 | }
405 |
406 | log_save(_log_file, "[memory info]\n");
407 | for (struct memory_info *memory = _memory_info_head;
408 | memory != NULL;
409 | memory = memory->next)
410 | {
411 | log_save(_log_file, "[name] %s [base addr] 0x%08X [size] 0x%08X [type] %d [off-chip] %d [is pack] %d [ID] %d \n",
412 | memory->name, memory->base_addr, memory->size, memory->type, memory->is_offchip, memory->is_from_pack, memory->id);
413 | }
414 |
415 | /* 7. 从 build_log 文件中获取被改名的文件信息 */
416 | if (uvprojx_file.output_path[0] != '\0')
417 | {
418 | res = combine_path(file_path, file_path_size, keil_prj_path, uvprojx_file.output_path);
419 | snprintf(file_path, file_path_size, "%s%s.build_log.htm", file_path, uvprojx_file.output_name);
420 | if (res == 0)
421 | {
422 | build_log_file_process(file_path);
423 | }
424 | if (res == -1)
425 | {
426 | log_print(_log_file, "\n[WARNING] %s not a absolute path\n", keil_prj_path);
427 | log_print(_log_file, "[WARNING] path: %s\n \n", file_path);
428 | }
429 | else if (res == -2)
430 | {
431 | log_print(_log_file, "\n[WARNING] relative paths go up more levels than absolute paths\n");
432 | log_print(_log_file, "[WARNING] path: %s\n \n", file_path);
433 | }
434 | }
435 | else {
436 | log_print(_log_file, "\n[WARNING] %s is empty, can't read '.build_log.htm' file\n \n", LABEL_OUTPUT_DIRECTORY);
437 | }
438 |
439 | /* 8. 处理剩余的重名文件 */
440 | file_rename_process();
441 |
442 | /* 9. 打开 map 文件,获取 Load Region 和 Execution Region */
443 | res = combine_path(file_path, file_path_size, keil_prj_path, uvprojx_file.listing_path);
444 | if (res == -1)
445 | {
446 | log_print(_log_file, "\n[ERROR] %s not a absolute path\n \n", keil_prj_path);
447 | result = -10;
448 | goto __exit;
449 | }
450 | else if (res == -2)
451 | {
452 | log_print(_log_file, "\n[ERROR] relative paths go up more levels than absolute paths\n \n");
453 | result = -11;
454 | goto __exit;
455 | }
456 |
457 | snprintf(file_path, file_path_size, "%s%s.map", file_path, uvprojx_file.output_name);
458 | log_save(_log_file, "[map file path] %s\n", file_path);
459 |
460 | res = map_file_process(file_path,
461 | &load_region_head,
462 | &object_info_head,
463 | uvprojx_file.is_has_user_lib,
464 | true); /* !uvprojx_file.is_custom_scatter */
465 | if (res == -1)
466 | {
467 | log_print(_log_file, "\n[ERROR] Check if a map file exists (Options for Target -> Listing -> Linker Listing)\n");
468 | log_print(_log_file, "[ERROR] map file path: %s\n", file_path);
469 | result = -12;
470 | goto __exit;
471 | }
472 | else if (res == -2)
473 | {
474 | log_print(_log_file, "\n[ERROR] map file does not contain \"%s\"\n", STR_MEMORY_MAP_OF_THE_IMAGE);
475 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
476 | result = -13;
477 | goto __exit;
478 | }
479 | else if (res == -3)
480 | {
481 | log_print(_log_file, "\n[ERROR] map file does not find object's information\n");
482 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
483 | result = -14;
484 | goto __exit;
485 | }
486 |
487 | log_save(_log_file, "\n[region info]\n");
488 | for (struct load_region *l_region = load_region_head;
489 | l_region != NULL;
490 | l_region = l_region->next)
491 | {
492 | log_save(_log_file, "[load region] %s\n", l_region->name);
493 | for (struct exec_region *e_region = l_region->exec_region;
494 | e_region != NULL;
495 | e_region = e_region->next)
496 | {
497 | log_save(_log_file, "\t[execution region] %s, 0x%08X, 0x%08X, 0x%08X [memory type] %d [memory ID] %d\n",
498 | e_region->name, e_region->base_addr, e_region->size,
499 | e_region->used_size, e_region->memory_type, e_region->memory_id);
500 |
501 | for (struct region_block *block = e_region->zi_block;
502 | block != NULL;
503 | block = block->next)
504 | {
505 | log_save(_log_file, "\t\t[ZI block] addr: 0x%08X, size: 0x%08X (%d)\n",
506 | block->start_addr, block->size, block->size);
507 | }
508 | log_save(_log_file, "\n");
509 | }
510 | }
511 |
512 | /* 10. 打印用户 object 和用户 library 文件的 flash 和 RAM 占用情况 */
513 | /* 10.1 将路径绑定到 object info 对应的 path 成员 */
514 | size_t max_name_len = 0;
515 | size_t max_path_len = 0;
516 | for (struct file_path_list *path_temp = _file_path_list_head;
517 | path_temp != NULL;
518 | path_temp = path_temp->next)
519 | {
520 | for (struct object_info *object_temp = object_info_head;
521 | object_temp != NULL;
522 | object_temp = object_temp->next)
523 | {
524 | if (path_temp->file_type == OBJECT_FILE_TYPE_LIBRARY)
525 | {
526 | if (strcasecmp(object_temp->name, path_temp->old_name) == 0) {
527 | object_temp->path = path_temp->path;
528 | }
529 | }
530 | else
531 | {
532 | if (strcasecmp(object_temp->name, path_temp->new_object_name) == 0) {
533 | object_temp->path = path_temp->path;
534 | }
535 | }
536 | }
537 |
538 | /* 计算出各个文件名称和相对路径的最长长度 */
539 | size_t path_len = strnlen_s(path_temp->path, MAX_PATH);
540 | size_t name_len1 = strnlen_s(path_temp->old_name, MAX_PATH);
541 | size_t name_len2 = strnlen_s(path_temp->new_object_name, MAX_PATH);
542 |
543 | if (name_len1 > max_name_len) {
544 | max_name_len = name_len1;
545 | }
546 | if (name_len2 > max_name_len) {
547 | max_name_len = name_len2;
548 | }
549 | if (path_len > max_path_len) {
550 | max_path_len = path_len;
551 | }
552 | }
553 | log_save(_log_file, "\n[object name max length] %d\n", max_name_len);
554 | log_save(_log_file, "[object path max length] %d\n", max_path_len);
555 |
556 | /* 打印抓取的 object 名称和路径 */
557 | log_save(_log_file, "\n[object in map file]\n");
558 | for (struct object_info *object_temp = object_info_head;
559 | object_temp != NULL;
560 | object_temp = object_temp->next)
561 | {
562 | log_save(_log_file, "[object name] %s%*s [path] %s\n",
563 | object_temp->name, max_name_len + 1 - strlen(object_temp->name), " ", object_temp->path);
564 | }
565 |
566 | /* 打印抓取的 keil 工程中的文件名和路径 */
567 | log_save(_log_file, "\n[file path in keil project]\n");
568 | for (struct file_path_list *path_list = _file_path_list_head;
569 | path_list != NULL;
570 | path_list = path_list->next)
571 | {
572 | log_save(_log_file, "[old name] %s%*s [type] %d [path] %s\n",
573 | path_list->old_name, max_name_len + 1 - strlen(path_list->old_name), " ",
574 | path_list->file_type, path_list->path);
575 |
576 | if (strcmp(path_list->object_name, path_list->new_object_name)) {
577 | log_save(_log_file, "[new name] %s\n", path_list->new_object_name);
578 | }
579 | }
580 |
581 | /* 10.2 打开记录文件,打开失败则新建 */
582 | snprintf(file_path, file_path_size, "%s\\%s-record.txt", _current_dir, APP_NAME);
583 |
584 | bool is_has_record = true;
585 | FILE *p_file = fopen(file_path, "r");
586 | if (p_file == NULL)
587 | {
588 | p_file = fopen(file_path, "w+");
589 | if (p_file == NULL)
590 | {
591 | log_print(_log_file, "\n[ERROR] can't create log file\n");
592 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
593 | result = -15;
594 | goto __exit;
595 | }
596 | is_has_record = false;
597 | }
598 | fclose(p_file);
599 |
600 | /* 10.3 若存在记录文件,则读取各个文件 flash 和 RAM 占用情况 */
601 | bool is_has_object = false;
602 | bool is_has_region = false;
603 | if (is_has_record)
604 | {
605 | record_file_process(file_path,
606 | &record_load_region_head,
607 | &record_object_info_head,
608 | &is_has_object,
609 | &is_has_region,
610 | true); /* !uvprojx_file.is_custom_scatter */
611 | }
612 |
613 | if (is_has_record)
614 | {
615 | /* 将旧的 object 信息绑定到匹配的新的 object 信息上 */
616 | for (struct object_info *new_obj_info = object_info_head;
617 | new_obj_info != NULL;
618 | new_obj_info = new_obj_info->next)
619 | {
620 | for (struct object_info *old_obj_info = record_object_info_head;
621 | old_obj_info != NULL;
622 | old_obj_info = old_obj_info->next)
623 | {
624 | if (strcasecmp(new_obj_info->name, old_obj_info->name) == 0) {
625 | new_obj_info->old_object = old_obj_info;
626 | }
627 | }
628 | }
629 |
630 | log_save(_log_file, "\n[record region info]\n");
631 | /* 将旧的 execution region 绑定到匹配的新的 execution region 上 */
632 | for (struct load_region *old_load_region = record_load_region_head;
633 | old_load_region != NULL;
634 | old_load_region = old_load_region->next)
635 | {
636 | log_save(_log_file, "[load region] %s\n", old_load_region->name);
637 | for (struct exec_region *old_exec_region = old_load_region->exec_region;
638 | old_exec_region != NULL;
639 | old_exec_region = old_exec_region->next)
640 | {
641 | for (struct load_region *new_load_region = load_region_head;
642 | new_load_region != NULL;
643 | new_load_region = new_load_region->next)
644 | {
645 | for (struct exec_region *new_exec_region = new_load_region->exec_region;
646 | new_exec_region != NULL;
647 | new_exec_region = new_exec_region->next)
648 | {
649 | if (strcmp(new_exec_region->name, old_exec_region->name) == 0) {
650 | new_exec_region->old_exec_region = old_exec_region;
651 | }
652 | }
653 | }
654 | log_save(_log_file, "\t[execution region] %s, 0x%08X, 0x%08X, 0x%08X [type] %d [ID] %d\n",
655 | old_exec_region->name, old_exec_region->base_addr, old_exec_region->size,
656 | old_exec_region->used_size, old_exec_region->memory_type, old_exec_region->memory_id);
657 | }
658 | }
659 | }
660 |
661 | /* 10.4 打印并保存本次编译信息至记录文件 */
662 | if (uvprojx_file.is_enable_lto == false)
663 | {
664 | if (_is_display_object)
665 | {
666 | size_t len = 0;
667 | if (_is_display_path)
668 | {
669 | if (max_name_len > max_path_len) {
670 | len = max_name_len;
671 | } else {
672 | len = max_path_len;
673 | }
674 | }
675 | else {
676 | len = max_name_len;
677 | }
678 | object_print_process(object_info_head, len, is_has_object);
679 | }
680 |
681 | /* 保存本次编译信息至记录文件 */
682 | p_file = fopen(file_path, "w+");
683 | if (p_file == NULL)
684 | {
685 | log_print(_log_file, "\n[ERROR] can't create record file\n");
686 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
687 | result = -16;
688 | goto __exit;
689 | }
690 |
691 | fputs(" Code (inc. data) RO Data RW Data ZI Data Debug Object Name\n", p_file);
692 |
693 | for (struct object_info *object_temp = object_info_head;
694 | object_temp != NULL;
695 | object_temp = object_temp->next)
696 | {
697 | snprintf(_line_text, sizeof(_line_text),
698 | "%10d %10d %10d %10d %10d %10d %s\n",
699 | object_temp->code, 0, object_temp->ro_data, object_temp->rw_data, object_temp->zi_data, 0, object_temp->name);
700 | fputs(_line_text, p_file);
701 | }
702 | fputs(STR_OBJECT_TOTALS "\n\n", p_file);
703 | fclose(p_file);
704 | }
705 | // else {
706 | // log_print(_log_file, "[WARNING] Because LTO is enabled, information for each file cannot be displayed\n \n");
707 | // }
708 |
709 | /* 11. 打印总 flash 和 RAM 占用情况,以进度条显示 */
710 | /* 11.1 算出 execution region name 的最大长度 */
711 | size_t max_region_name = 0;
712 | for (struct load_region *l_region = load_region_head;
713 | l_region != NULL;
714 | l_region = l_region->next)
715 | {
716 | for (struct exec_region *e_region = l_region->exec_region;
717 | e_region != NULL;
718 | e_region = e_region->next)
719 | {
720 | size_t len = strnlen_s(e_region->name, 32);
721 | if (len > max_region_name) {
722 | max_region_name = len;
723 | }
724 | }
725 | }
726 |
727 | /* 11.2 判断当前的内存打印模式 */
728 | MEMORY_PRINT_MODE print_mode = MEMORY_PRINT_MODE_0;
729 | if (uvprojx_file.is_has_pack == false)
730 | {
731 | #if defined(ENABLE_REFER_TO_KEIL_DIALOG) && (ENABLE_REFER_TO_KEIL_DIALOG != 0)
732 | if (_memory_info_head == NULL) {
733 | print_mode = MEMORY_PRINT_MODE_2;
734 | } else {
735 | print_mode = MEMORY_PRINT_MODE_1;
736 | }
737 | #else
738 | if (_memory_info_head && uvprojx_file.is_custom_scatter == false) {
739 | print_mode = MEMORY_PRINT_MODE_1;
740 | } else {
741 | print_mode = MEMORY_PRINT_MODE_2;
742 | }
743 | #endif
744 | }
745 | log_save(_log_file, "[memory print mode]: %d\n", print_mode);
746 |
747 | /* 11.3 开始打印 */
748 | memory_numbering(MEMORY_TYPE_RAM);
749 | memory_numbering(MEMORY_TYPE_FLASH);
750 |
751 | if (_is_has_unused_region
752 | && print_mode == MEMORY_PRINT_MODE_0)
753 | {
754 | log_print(_log_file, UNUSED_LOAD_REGION_NAME "\n");
755 |
756 | memory_print_unused(MEMORY_TYPE_RAM, max_region_name);
757 | memory_print_unused(MEMORY_TYPE_FLASH, max_region_name);
758 | log_print(_log_file, " \n");
759 | }
760 |
761 | bool is_print_null = true;
762 | for (struct load_region *l_region = load_region_head;
763 | l_region != NULL;
764 | l_region = l_region->next)
765 | {
766 | log_print(_log_file, "%s\n", l_region->name);
767 | if (print_mode == MEMORY_PRINT_MODE_1)
768 | {
769 | memory_mode1_print(l_region->exec_region, MEMORY_TYPE_RAM, false, max_region_name, is_has_record);
770 | memory_mode1_print(l_region->exec_region, MEMORY_TYPE_RAM, true, max_region_name, is_has_record);
771 | memory_mode1_print(l_region->exec_region, MEMORY_TYPE_FLASH, false, max_region_name, is_has_record);
772 | memory_mode1_print(l_region->exec_region, MEMORY_TYPE_FLASH, true, max_region_name, is_has_record);
773 | memory_mode1_print(l_region->exec_region, MEMORY_TYPE_UNKNOWN, false, max_region_name, is_has_record);
774 | }
775 | else if (print_mode == MEMORY_PRINT_MODE_2)
776 | {
777 | memory_mode2_print(l_region->exec_region, max_region_name, is_has_record);
778 | }
779 | else
780 | {
781 | memory_mode0_print(l_region->exec_region, l_region->name, MEMORY_TYPE_RAM, max_region_name, is_has_record, is_print_null);
782 | memory_mode0_print(l_region->exec_region, l_region->name, MEMORY_TYPE_FLASH, max_region_name, is_has_record, is_print_null);
783 | memory_mode0_print(l_region->exec_region, l_region->name, MEMORY_TYPE_UNKNOWN, max_region_name, is_has_record, is_print_null);
784 | }
785 | is_print_null = false;
786 | }
787 |
788 | /* 12. 打印栈使用情况 */
789 | if (uvprojx_file.output_path[0] != '\0')
790 | {
791 | res = combine_path(file_path, file_path_size, keil_prj_path, uvprojx_file.output_path);
792 | if (res == -1)
793 | {
794 | log_print(_log_file, "\n[ERROR] %s not a absolute path\n \n", keil_prj_path);
795 | result = -17;
796 | goto __exit;
797 | }
798 | else if (res == -2)
799 | {
800 | log_print(_log_file, "\n[ERROR] relative paths go up more levels than absolute paths\n \n");
801 | result = -18;
802 | goto __exit;
803 | }
804 | snprintf(file_path, file_path_size, "%s%s.htm", file_path, uvprojx_file.output_name);
805 | log_save(_log_file, "[htm file path] %s\n", file_path);
806 | stack_print_process(file_path);
807 | }
808 |
809 | /* 13. 保存本次 region 信息至记录文件 */
810 | snprintf(file_path, file_path_size, "%s\\%s-record.txt", _current_dir, APP_NAME);
811 |
812 | if (uvprojx_file.is_enable_lto) {
813 | p_file = fopen(file_path, "w+");
814 | } else {
815 | p_file = fopen(file_path, "a");
816 | }
817 | if (p_file == NULL)
818 | {
819 | log_print(_log_file, "\n[ERROR] can't create record file\n");
820 | log_print(_log_file, "[ERROR] Please check: %s\n", file_path);
821 | result = -19;
822 | goto __exit;
823 | }
824 |
825 | fputs(STR_MEMORY_MAP_OF_THE_IMAGE "\n\n", p_file);
826 |
827 | for (struct load_region *l_region = load_region_head;
828 | l_region != NULL;
829 | l_region = l_region->next)
830 | {
831 | snprintf(_line_text, sizeof(_line_text),
832 | "\t%s %s \n\n",
833 | STR_LOAD_REGION, l_region->name);
834 | fputs(_line_text, p_file);
835 |
836 | for (struct exec_region *e_region = l_region->exec_region;
837 | e_region != NULL;
838 | e_region = e_region->next)
839 | {
840 | snprintf(_line_text, sizeof(_line_text),
841 | "\t\t%s %s (%s0x%08X, %s0x%08X, %s0x%08X, END)\n\n",
842 | STR_EXECUTION_REGION, e_region->name, STR_EXECUTE_BASE_ADDR, e_region->base_addr,
843 | STR_REGION_USED_SIZE, e_region->used_size, STR_REGION_MAX_SIZE, e_region->size);
844 | fputs(_line_text, p_file);
845 | }
846 | }
847 | fputs(STR_IMAGE_COMPONENT_SIZE, p_file);
848 | fclose(p_file);
849 |
850 | __exit:
851 | if (_current_dir) {
852 | free(_current_dir);
853 | }
854 | if (file_path) {
855 | free(file_path);
856 | }
857 | object_info_free(&object_info_head);
858 | object_info_free(&record_object_info_head);
859 | load_region_free(&load_region_head);
860 | load_region_free(&record_load_region_head);
861 |
862 | file_path_free(&_file_path_list_head);
863 | memory_info_free(&_memory_info_head);
864 | prj_path_list_free(_keil_prj_path_list);
865 | log_print(_log_file, "=============================================================================================================================\n\n");
866 | log_save(_log_file, "run time: %.3f s\n", (double)(clock() - run_time) / CLOCKS_PER_SEC);
867 | fclose(_log_file);
868 | return result;
869 | }
870 |
871 |
872 | /**
873 | * @brief 入口参数处理
874 | * @note
875 | * @param param_qty: 参数数量
876 | * @param param[]: 参数列表
877 | * @param prj_name: [out] keil 工程名
878 | * @param name_size: prj_name 的最大 size
879 | * @param prj_path: [out] keil 工程绝对路径
880 | * @param path_size: prj_path 的最大 size
881 | * @param err_param: [out] 发生错误的参数位
882 | * @retval 0: 正常 | -x: 错误
883 | */
884 | int parameter_process(int param_qty,
885 | char *param[],
886 | char *prj_name,
887 | size_t name_size,
888 | char *prj_path,
889 | size_t path_size,
890 | int *err_param)
891 | {
892 | for (size_t i = 1; i < param_qty; i++)
893 | {
894 | log_save(_log_file, "[param %d] %s\n", i, param[i]);
895 |
896 | if (param[i][0] == '-')
897 | {
898 | int seq = 0;
899 | if (strcasecmp(param[i], _command_list[seq++].cmd) == 0) {
900 | _is_save_log = false;
901 | }
902 | else if (strcasecmp(param[i], _command_list[seq++].cmd) == 0) {
903 | _is_display_object = true;
904 | }
905 | else if (strcasecmp(param[i], _command_list[seq++].cmd) == 0) {
906 | _is_display_object = false;
907 | }
908 | else if (strcasecmp(param[i], _command_list[seq++].cmd) == 0) {
909 | _is_display_path = true;
910 | }
911 | else if (strcasecmp(param[i], _command_list[seq++].cmd) == 0) {
912 | _is_display_path = false;
913 | }
914 | else if (strcasecmp(param[i], _command_list[seq++].cmd) == 0) {
915 | _progress_style = PROGRESS_STYLE_0;
916 | }
917 | else if (strcasecmp(param[i], _command_list[seq++].cmd) == 0) {
918 | _progress_style = PROGRESS_STYLE_1;
919 | }
920 | else if (strcasecmp(param[i], _command_list[seq++].cmd) == 0) {
921 | _progress_style = PROGRESS_STYLE_2;
922 | }
923 | else if (strcasecmp(param[i], "-H") == 0
924 | || strcasecmp(param[i], "-HELP") == 0) {
925 | return -4;
926 | }
927 | else
928 | {
929 | *err_param = i;
930 | return -3;
931 | }
932 | }
933 | else
934 | {
935 | char *last_slash = NULL;
936 | size_t param_len = strnlen_s(param[i], MAX_PATH);
937 |
938 | /* 绝对路径 */
939 | if (param[i][1] == ':')
940 | {
941 | DWORD attributes = GetFileAttributes(param[i]);
942 | if (attributes == INVALID_FILE_ATTRIBUTES) {
943 | return -1;
944 | }
945 |
946 | /* 目录 */
947 | if (attributes & FILE_ATTRIBUTE_DIRECTORY)
948 | {
949 | strncpy_s(_current_dir, sizeof(_current_dir), param[i], param_len);
950 |
951 | if (param[i][param_len - 1] == '\\') {
952 | _current_dir[param_len - 1] = '\0';
953 | }
954 | }
955 | /* 文件 */
956 | else
957 | {
958 | /* 不是 keil 工程则报错退出 */
959 | if (is_keil_project(param[i]) == false) {
960 | return -2;
961 | }
962 | strncpy_s(prj_path, path_size, param[i], param_len);
963 |
964 | last_slash = strrchr(prj_path, '\\');
965 | if (last_slash)
966 | {
967 | last_slash += 1;
968 | strncpy_s(prj_name, name_size, last_slash, strnlen(last_slash, name_size));
969 | }
970 | }
971 | }
972 | /* 不支持相对路径 */
973 | else if (param[i][0] == '\\' || param[i][0] == '.') {
974 | return -2;
975 | }
976 | /* 文件名 */
977 | else
978 | {
979 | snprintf(prj_path, path_size, "%s\\%s", _current_dir, param[i]);
980 |
981 | /* 非 keil 工程则检查是否有扩展名 */
982 | if (is_keil_project(param[i]) == false)
983 | {
984 | /* 有扩展名报错退出,无扩展名则从搜索到的列表里进行匹配 */
985 | char *dot = strrchr(param[i], '.');
986 | if (dot) {
987 | return -2;
988 | }
989 |
990 | for (size_t index = 0; index < _keil_prj_path_list->size; index++)
991 | {
992 | if (strstr(_keil_prj_path_list->items[index], param[i]))
993 | {
994 | strncpy_s(prj_path, path_size, _keil_prj_path_list->items[index], strnlen_s(_keil_prj_path_list->items[index], MAX_PATH));
995 | break;
996 | }
997 | }
998 | }
999 |
1000 | last_slash = strrchr(prj_path, '\\');
1001 | if (last_slash)
1002 | {
1003 | last_slash += 1;
1004 | strncpy_s(prj_name, name_size, last_slash, strnlen(last_slash, name_size));
1005 | }
1006 | }
1007 | }
1008 | }
1009 |
1010 | return 0;
1011 | }
1012 |
1013 |
1014 | /**
1015 | * @brief uvoptx 文件处理
1016 | * @note 获取指定的 target name
1017 | * @param file_path: uvoptx 文件路径
1018 | * @param target_name: [out] keil target name
1019 | * @param max_size: target_name 的最大 size
1020 | * @retval true: 成功 | false: 失败
1021 | */
1022 | bool uvoptx_file_process(const char *file_path,
1023 | char *target_name,
1024 | size_t max_size)
1025 | {
1026 | FILE *p_file = fopen(file_path, "r");
1027 | if (p_file == NULL) {
1028 | return false;
1029 | }
1030 |
1031 | uint8_t state = 0;
1032 | while (fgets(_line_text, sizeof(_line_text), p_file))
1033 | {
1034 | char *str;
1035 | switch (state)
1036 | {
1037 | case 0:
1038 | str = strstr(_line_text, LABEL_TARGET_NAME);
1039 | if (str)
1040 | {
1041 | str += strlen(LABEL_TARGET_NAME);
1042 | char *lt = strrchr(_line_text, '<');
1043 | if (lt)
1044 | {
1045 | *lt = '\0';
1046 | strncpy_s(target_name, max_size, str, strnlen_s(str, max_size));
1047 | log_save(_log_file, "[target name] %s\n", target_name);
1048 | state = 1;
1049 | }
1050 | }
1051 | break;
1052 | case 1:
1053 | str = strstr(_line_text, LABEL_IS_CURRENT_TARGET);
1054 | if (str)
1055 | {
1056 | str += strlen(LABEL_IS_CURRENT_TARGET);
1057 | if (*str == '0') {
1058 | state = 0;
1059 | } else {
1060 | state = 2;
1061 | }
1062 | }
1063 | break;
1064 | default: break;
1065 | }
1066 | if (state == 2)
1067 | {
1068 | log_save(_log_file, "[final target name] %s\n", target_name);
1069 | break;
1070 | }
1071 | }
1072 | fclose(p_file);
1073 |
1074 | return true;
1075 | }
1076 |
1077 |
1078 | /**
1079 | * @brief uvprojx 文件处理
1080 | * @note 获取 uvprojx 文件中的信息
1081 | * @param file_path: uvprojx 文件的绝对路径
1082 | * @param target_name: 指定的 target name
1083 | * @param out_info: [out] 解析出的 uvprojx 信息
1084 | * @param is_get_target_name: 是否获取 target name
1085 | * @retval 0: 成功 | -x: 失败
1086 | */
1087 | int uvprojx_file_process(const char *file_path,
1088 | const char *target_name,
1089 | struct uvprojx_info *out_info,
1090 | bool is_get_target_name)
1091 | {
1092 | /* 打开同名的 .uvprojx 或 .uvproj 文件 */
1093 | FILE *p_file = fopen(file_path, "r");
1094 | if (p_file == NULL) {
1095 | return -1;
1096 | }
1097 |
1098 | char *str = NULL;
1099 | char *lt = NULL;
1100 | uint8_t state = 0;
1101 | long mem_pos = 0;
1102 |
1103 | /* 逐行读取 */
1104 | while (fgets(_line_text, sizeof(_line_text), p_file))
1105 | {
1106 | switch (state)
1107 | {
1108 | case 0:
1109 | str = strstr(_line_text, target_name);
1110 | if (str)
1111 | {
1112 | if (is_get_target_name)
1113 | {
1114 | str += strlen(LABEL_TARGET_NAME);
1115 | lt = strrchr(_line_text, '<');
1116 | if (lt)
1117 | {
1118 | *lt = '\0';
1119 | strncpy_s(out_info->target_name, sizeof(out_info->target_name), str, strnlen_s(str, sizeof(out_info->target_name)));
1120 | }
1121 | }
1122 | state = 1;
1123 | }
1124 | break;
1125 | case 1:
1126 | str = strstr(_line_text, LABEL_DEVICE);
1127 | if (str)
1128 | {
1129 | str += strlen(LABEL_DEVICE);
1130 | lt = strrchr(_line_text, '<');
1131 | if (lt)
1132 | {
1133 | *lt = '\0';
1134 | strncpy_s(out_info->chip, sizeof(out_info->chip), str, strnlen_s(str, sizeof(out_info->chip)));
1135 | state = 2;
1136 | }
1137 | }
1138 | break;
1139 | case 2:
1140 | str = strstr(_line_text, LABEL_VENDOR);
1141 | if (str)
1142 | {
1143 | str += strlen(LABEL_VENDOR);
1144 | if (strncasecmp(str, "ARM", 3) == 0)
1145 | {
1146 | out_info->is_has_pack = false;
1147 | state = 4;
1148 | }
1149 | else
1150 | {
1151 | out_info->is_has_pack = true;
1152 | state = 3;
1153 | }
1154 | }
1155 | break;
1156 | case 3:
1157 | bool is_get_first = false;
1158 | char *str_p1 = NULL;
1159 | char *str_p2 = NULL;
1160 | char *end_ptr = NULL;
1161 | char name[MAX_PRJ_NAME_SIZE] = {0};
1162 | uint32_t base_addr = 0;
1163 | uint32_t size = 0;
1164 | size_t mem_id = UNKNOWN_MEMORY_ID;
1165 | MEMORY_TYPE mem_type = MEMORY_TYPE_NONE;
1166 |
1167 | /* 获取 RAM 和 ROM */
1168 | str = strstr(_line_text, LABEL_CPU);
1169 | if (str)
1170 | {
1171 | strtok(_line_text, " ");
1172 | while (1)
1173 | {
1174 | if (is_get_first == false)
1175 | {
1176 | str_p1 = strstr(_line_text, LABEL_CPU);
1177 | str_p1 += strlen(LABEL_CPU);
1178 | is_get_first = true;
1179 | }
1180 | else
1181 | {
1182 | str_p1 = strtok(NULL, " ");
1183 | if (str_p1 == NULL)
1184 | {
1185 | state = 3;
1186 | break;
1187 | }
1188 | }
1189 |
1190 | str_p2 = strstr(str_p1, "(");
1191 | *str_p2 = '\0';
1192 | strncpy_s(name, sizeof(name), str_p1, strnlen_s(str_p1, sizeof(name)));
1193 |
1194 | mem_type = MEMORY_TYPE_UNKNOWN;
1195 | if (strstr(name, "RAM")) {
1196 | mem_type = MEMORY_TYPE_RAM;
1197 | }
1198 | else if (strstr(name, "ROM")) {
1199 | mem_type = MEMORY_TYPE_FLASH;
1200 | }
1201 | else
1202 | {
1203 | state = 4;
1204 | break;
1205 | }
1206 |
1207 | str_p1 = str_p2 + 1;
1208 | str_p2 += 3;
1209 | while ((*str_p2 >= '0') && (*str_p2 <= 'F')) {
1210 | str_p2 += 1;
1211 | }
1212 |
1213 | uint8_t parse_mode = 0;
1214 | if (*str_p2 == ',') {
1215 | parse_mode = 0;
1216 | }
1217 | else if (*str_p2 == '-') {
1218 | parse_mode = 1;
1219 | }
1220 | else {
1221 | return -2;
1222 | }
1223 |
1224 | *str_p2 = '\0';
1225 | base_addr = strtoul(str_p1, &end_ptr, 16);
1226 |
1227 | str_p1 = str_p2 + 1;
1228 | str_p2 = strstr(str_p1, ")");
1229 | *str_p2 = '\0';
1230 | size = strtoul(str_p1, &end_ptr, 16);
1231 |
1232 | if (parse_mode == 1)
1233 | {
1234 | size -= base_addr;
1235 | size += 1;
1236 | }
1237 |
1238 | if (mem_type == MEMORY_TYPE_UNKNOWN) {
1239 | memory_info_add(&_memory_info_head, name, 1, base_addr, size, mem_type, true, true);
1240 | }
1241 | else
1242 | {
1243 | mem_id++;
1244 | memory_info_add(&_memory_info_head, name, mem_id, base_addr, size, mem_type, false, true);
1245 | }
1246 | }
1247 | }
1248 | break;
1249 | case 4:
1250 | str = strstr(_line_text, LABEL_OUTPUT_DIRECTORY);
1251 | if (str)
1252 | {
1253 | str += strlen(LABEL_OUTPUT_DIRECTORY);
1254 | lt = strrchr(_line_text, '<');
1255 | if (lt)
1256 | {
1257 | *lt = '\0';
1258 | strncpy_s(out_info->output_path, sizeof(out_info->output_path), str, strnlen_s(str, sizeof(out_info->output_path)));
1259 | state = 5;
1260 | }
1261 | }
1262 | break;
1263 | case 5:
1264 | str = strstr(_line_text, LABEL_OUTPUT_NAME);
1265 | if (str)
1266 | {
1267 | str += strlen(LABEL_OUTPUT_NAME);
1268 | lt = strrchr(_line_text, '<');
1269 | if (lt)
1270 | {
1271 | *lt = '\0';
1272 | strncpy_s(out_info->output_name, sizeof(out_info->output_name), str, strnlen_s(str, sizeof(out_info->output_name)));
1273 | state = 6;
1274 | }
1275 | }
1276 | break;
1277 | case 6:
1278 | str = strstr(_line_text, LABEL_LISTING_PATH);
1279 | if (str)
1280 | {
1281 | str += strlen(LABEL_LISTING_PATH);
1282 | lt = strrchr(_line_text, '<');
1283 | if (lt)
1284 | {
1285 | *lt = '\0';
1286 | strncpy_s(out_info->listing_path, sizeof(out_info->listing_path), str, strnlen_s(str, sizeof(out_info->listing_path)));
1287 | state = 7;
1288 | }
1289 | }
1290 | break;
1291 | case 7:
1292 | /* 检查是否生成了 map 文件 */
1293 | str = strstr(_line_text, LABEL_IS_CREATE_MAP);
1294 | if (str)
1295 | {
1296 | str += strlen(LABEL_IS_CREATE_MAP);
1297 | if (*str == '0') {
1298 | return -3;
1299 | }
1300 | else
1301 | {
1302 | /* 没有 pack 就读取自定义的 memory area */
1303 | if (out_info->is_has_pack == false || _memory_info_head == NULL) {
1304 | state = 8;
1305 | } else {
1306 | state = 9;
1307 | }
1308 | mem_pos = ftell(p_file);
1309 | }
1310 | }
1311 | break;
1312 | case 8:
1313 | /* 读取自定义 memory area */
1314 | if (memory_area_process(_line_text, false) == false) {
1315 | state = 9;
1316 | }
1317 | break;
1318 | case 9:
1319 | /* 检查是否开启了 LTO */
1320 | if (({str = strstr(_line_text, LABEL_AC6_LTO); str;}))
1321 | {
1322 | str += strlen(LABEL_AC6_LTO);
1323 | if (*str == '0') {
1324 | out_info->is_enable_lto = false;
1325 | } else {
1326 | out_info->is_enable_lto = true;
1327 | }
1328 | state = 10;
1329 | }
1330 | else if (({str = strstr(_line_text, LABEL_END_CADS); str;}))
1331 | {
1332 | out_info->is_enable_lto = false;
1333 | state = 10;
1334 | }
1335 | break;
1336 | case 10:
1337 | /* 读取是否使用了 keil 生成的 scatter file */
1338 | str = strstr(_line_text, LABEL_IS_KEIL_SCATTER);
1339 | if (str)
1340 | {
1341 | str += strlen(LABEL_IS_KEIL_SCATTER);
1342 | if (*str == '0')
1343 | {
1344 | out_info->is_custom_scatter = true;
1345 | fseek(p_file, mem_pos, SEEK_SET);
1346 | state = 11;
1347 | }
1348 | else
1349 | {
1350 | out_info->is_custom_scatter = false;
1351 | state = 12;
1352 | }
1353 | }
1354 | else if (strstr(_line_text, LABEL_END_LDADS)) {
1355 | state = 12;
1356 | }
1357 | break;
1358 | case 11:
1359 | /* 将新的 memory area 加入 memory info 中 */
1360 | if (memory_area_process(_line_text, true) == false) {
1361 | state = 12;
1362 | }
1363 | break;
1364 | case 12:
1365 | /* 获取已加入编译的文件路径,并记录重复的文件名 */
1366 | if (file_path_process(_line_text, &out_info->is_has_user_lib) == false) {
1367 | state = 13;
1368 | }
1369 | break;
1370 | default: break;
1371 | }
1372 | if (state == 13) {
1373 | break;
1374 | }
1375 | }
1376 | fclose(p_file);
1377 |
1378 | return true;
1379 | }
1380 |
1381 |
1382 | /**
1383 | * @brief 读取 build_log 文件,获取文件的改名信息
1384 | * @note
1385 | * @param file_path: build_log 文件所在的路径
1386 | * @retval
1387 | */
1388 | void build_log_file_process(const char *file_path)
1389 | {
1390 | FILE *p_file = fopen(file_path, "r");
1391 | if (p_file == NULL) {
1392 | return;
1393 | }
1394 |
1395 | char *ptr = NULL;
1396 | log_save(_log_file, "\n");
1397 |
1398 | while (fgets(_line_text, sizeof(_line_text), p_file))
1399 | {
1400 | if (({ptr = strstr(_line_text, STR_RENAME_MARK); ptr;}))
1401 | {
1402 | log_save(_log_file, "%s", _line_text);
1403 |
1404 | char *str_p1 = strstr(_line_text, "'");
1405 | str_p1 += 1;
1406 | char *str_p2 = strstr(str_p1, "'");
1407 | *str_p2 = '\0';
1408 |
1409 | for (struct file_path_list *path_temp = _file_path_list_head;
1410 | path_temp != NULL;
1411 | path_temp = path_temp->next)
1412 | {
1413 | if (strcmp(str_p1, path_temp->path) == 0)
1414 | {
1415 | char *str_p3 = strrchr(str_p2 + 1, '\'');
1416 | *str_p3 = '\0';
1417 | str_p1 = strrchr(str_p2 + 1, '\\');
1418 | str_p1 += 1;
1419 | if (path_temp->new_object_name) {
1420 | free(path_temp->new_object_name);
1421 | }
1422 | path_temp->new_object_name = strdup(str_p1);
1423 | path_temp->is_rename = false;
1424 | log_save(_log_file, "'%s' rename to '%s'\n", path_temp->old_name, str_p1);
1425 | }
1426 | }
1427 | }
1428 | else if (({ptr = strstr(_line_text, STR_COMPILING); ptr;})) {
1429 | break;
1430 | }
1431 | }
1432 | log_save(_log_file, "\n");
1433 | fclose(p_file);
1434 | return;
1435 | }
1436 |
1437 |
1438 | /**
1439 | * @brief 文件名重名修改处理
1440 | * @note
1441 | * @retval None
1442 | */
1443 | void file_rename_process(void)
1444 | {
1445 | char str[MAX_PRJ_NAME_SIZE] = {0};
1446 |
1447 | for (struct file_path_list *path_temp1 = _file_path_list_head;
1448 | path_temp1 != NULL;
1449 | path_temp1 = path_temp1->next)
1450 | {
1451 | size_t repeat = 0;
1452 |
1453 | for (struct file_path_list *path_temp2 = path_temp1->next;
1454 | path_temp2 != NULL;
1455 | path_temp2 = path_temp2->next)
1456 | {
1457 | if (path_temp2->is_rename
1458 | && strcmp(path_temp1->object_name, path_temp2->object_name) == 0)
1459 | {
1460 | repeat++;
1461 |
1462 | strncpy_s(str, sizeof(str), path_temp2->old_name, strnlen_s(path_temp2->old_name, sizeof(str)));
1463 | char *dot = strrchr(str, '.');
1464 | if (dot) {
1465 | *dot = '\0';
1466 | }
1467 | snprintf(str, sizeof(str), "%s_%d.o", str, repeat);
1468 | if (path_temp2->new_object_name) {
1469 | free(path_temp2->new_object_name);
1470 | }
1471 | path_temp2->new_object_name = strdup(str);
1472 | path_temp2->is_rename = false;
1473 | log_save(_log_file, "object '%s' rename to '%s'\n", path_temp2->old_name, str);
1474 | }
1475 | }
1476 | }
1477 | }
1478 |
1479 |
1480 | /**
1481 | * @brief 自定义 memory area 读取
1482 | * @note
1483 | * @param str: 读取到的 uvprojx 文件的每一行文本
1484 | * @param is_new: 是否为新增的 memory area
1485 | * @retval true: 继续 | false: 结束
1486 | */
1487 | bool memory_area_process(const char *str, bool is_new)
1488 | {
1489 | static uint8_t area = 0;
1490 | static uint8_t state = 0;
1491 | static uint32_t addr = 0;
1492 | static uint32_t size = 0;
1493 | static size_t mem_id = UNKNOWN_MEMORY_ID;
1494 | static MEMORY_TYPE mem_type = MEMORY_TYPE_NONE;
1495 |
1496 | if (str == NULL || strstr(str, LABEL_END_ONCHIP_MEMORY))
1497 | {
1498 | area = 0;
1499 | state = 0;
1500 | addr = 0;
1501 | size = 0;
1502 | mem_id = UNKNOWN_MEMORY_ID;
1503 | mem_type = MEMORY_TYPE_NONE;
1504 | return false;
1505 | }
1506 |
1507 | char *str_p1 = NULL;
1508 | char *str_p2 = NULL;
1509 | char *end_ptr = NULL;
1510 |
1511 | switch (state)
1512 | {
1513 | case 0:
1514 | if (strstr(str, LABEL_ONCHIP_MEMORY)) {
1515 | state = 1;
1516 | }
1517 | break;
1518 | case 1:
1519 | if (strstr(str, LABEL_MEMORY_AREA)) {
1520 | state = 2;
1521 | }
1522 | break;
1523 | case 2:
1524 | str_p1 = strstr(str, LABEL_MEMORY_TYPE);
1525 | if (str_p1)
1526 | {
1527 | str_p1 += strlen(LABEL_MEMORY_TYPE);
1528 | str_p2 = strrchr(str_p1, '<');
1529 | *str_p2 = '\0';
1530 |
1531 | if (strtoul(str_p1, &end_ptr, 16) == 0) {
1532 | mem_type = MEMORY_TYPE_RAM;
1533 | } else {
1534 | mem_type = MEMORY_TYPE_FLASH;
1535 | }
1536 | area++;
1537 | state = 3;
1538 | }
1539 | break;
1540 | case 3:
1541 | str_p1 = strstr(str, LABEL_MEMORY_ADDRESS);
1542 | if (str_p1)
1543 | {
1544 | str_p1 += strlen(LABEL_MEMORY_ADDRESS);
1545 | str_p2 = strrchr(str_p1, '<');
1546 | *str_p2 = '\0';
1547 | addr = strtoul(str_p1, &end_ptr, 16);
1548 | state = 4;
1549 | }
1550 | break;
1551 | case 4:
1552 | str_p1 = strstr(str, LABEL_MEMORY_SIZE);
1553 | if (str_p1)
1554 | {
1555 | str_p1 += strlen(LABEL_MEMORY_SIZE);
1556 | str_p2 = strrchr(str_p1, '<');
1557 | *str_p2 = '\0';
1558 | size = strtoul(str_p1, &end_ptr, 16);
1559 |
1560 | if (size == 0) {
1561 | state = 2;
1562 | } else {
1563 | state = 5;
1564 | }
1565 |
1566 | if (is_new == false) {
1567 | break;
1568 | }
1569 |
1570 | for (struct memory_info *memory = _memory_info_head;
1571 | memory != NULL;
1572 | memory = memory->next)
1573 | {
1574 | if (addr >= memory->base_addr
1575 | && addr <= (memory->base_addr + memory->size))
1576 | {
1577 | state = 2;
1578 | break;
1579 | }
1580 | }
1581 | }
1582 | break;
1583 | case 5:
1584 | if (strstr(str, LABLE_END_MEMORY_AREA))
1585 | {
1586 | bool is_offchip = true;
1587 | if (area == 4 || area == 5 || area == 9 || area == 10) {
1588 | is_offchip = false;
1589 | }
1590 | mem_id++;
1591 | memory_info_add(&_memory_info_head, NULL, mem_id, addr, size, mem_type, is_offchip, false);
1592 | state = 2;
1593 | }
1594 | break;
1595 | default: break;
1596 | }
1597 | return true;
1598 | }
1599 |
1600 |
1601 | /**
1602 | * @brief 文件路径处理
1603 | * @note 获取 uvprojx 文件中被添加进 keil 工程的文件及其相对路径
1604 | * @param str: 读取到的 uvprojx 文件的每一行文本
1605 | * @param is_has_user_lib: [out] 是否有 user lib
1606 | * @retval true: 继续 | false: 结束
1607 | */
1608 | bool file_path_process(const char *str, bool *is_has_user_lib)
1609 | {
1610 | static uint8_t state = 0;
1611 | static char path[MAX_PATH] = {0};
1612 | static char name[MAX_PRJ_NAME_SIZE] = {0};
1613 | static OBJECT_FILE_TYPE type = OBJECT_FILE_TYPE_USER;
1614 |
1615 | char *str_p1 = NULL;
1616 | char *str_p2 = NULL;
1617 |
1618 | if (strstr(str, LABEL_END_GROUPS))
1619 | {
1620 | state = 0;
1621 | return false;
1622 | }
1623 |
1624 | switch (state)
1625 | {
1626 | case 0:
1627 | if (strstr(str, LABEL_GROUP_NAME)) {
1628 | state = 1;
1629 | }
1630 | break;
1631 | case 1:
1632 | if (({str_p1 = strstr(str, LABEL_FILE_NAME); str_p1;}))
1633 | {
1634 | str_p1 += strlen(LABEL_FILE_NAME);
1635 | str_p2 = strrchr(str_p1, '<');
1636 | *str_p2 = '\0';
1637 | strncpy_s(name, sizeof(name), str_p1, strnlen_s(str_p1, sizeof(name)));
1638 | type = OBJECT_FILE_TYPE_USER;
1639 | state = 2;
1640 | }
1641 | else if (({str_p1 = strstr(str, LABEL_INCLUDE_IN_BUILD); str_p1;}))
1642 | {
1643 | str_p1 += strlen(LABEL_INCLUDE_IN_BUILD);
1644 | if (*str_p1 == '0') {
1645 | state = 0;
1646 | }
1647 | }
1648 | else if (strstr(str, LABEL_END_FILES)) {
1649 | state = 0;
1650 | }
1651 | break;
1652 | case 2:
1653 | str_p1 = strstr(str, LABEL_FILE_TYPE);
1654 | if (str_p1)
1655 | {
1656 | str_p1 += strlen(LABEL_FILE_TYPE);
1657 | /* text document file or custom file */
1658 | if (*str_p1 == '5' || *str_p1 == '6') {
1659 | state = 1;
1660 | }
1661 | else if (*str_p1 == '3') /* object file */
1662 | {
1663 | type = OBJECT_FILE_TYPE_OBJECT;
1664 | state = 3;
1665 | }
1666 | else if (*str_p1 == '4') /* library file */
1667 | {
1668 | *is_has_user_lib = true;
1669 | type = OBJECT_FILE_TYPE_LIBRARY;
1670 | state = 3;
1671 | }
1672 | else {
1673 | state = 3;
1674 | }
1675 | }
1676 | break;
1677 | case 3:
1678 | str_p1 = strstr(str, LABEL_FILE_PATH);
1679 | if (str_p1)
1680 | {
1681 | str_p1 += strlen(LABEL_FILE_PATH);
1682 | str_p2 = strrchr(str_p1, '<');
1683 | *str_p2 = '\0';
1684 | strncpy_s(path, sizeof(path), str_p1, strnlen_s(str_p1, sizeof(path)));
1685 | state = 4;
1686 | }
1687 | break;
1688 | case 4:
1689 | if (strstr(str, LABEL_END_FILE))
1690 | {
1691 | file_path_add(&_file_path_list_head, name, path, type);
1692 | state = 1;
1693 | }
1694 | else if (({str_p1 = strstr(str, LABEL_INCLUDE_IN_BUILD); str_p1;}))
1695 | {
1696 | str_p1 += strlen(LABEL_INCLUDE_IN_BUILD);
1697 | if (*str_p1 != '0') {
1698 | file_path_add(&_file_path_list_head, name, path, type);
1699 | }
1700 | state = 1;
1701 | }
1702 | break;
1703 | default: break;
1704 | }
1705 |
1706 | return true;
1707 | }
1708 |
1709 |
1710 | /**
1711 | * @brief map 文件处理
1712 | * @note 获取 map 文件中每个编译文件的信息
1713 | * @param file_path: map 文件的绝对路径
1714 | * @param region_head: region 链表头
1715 | * @param object_head: object 文件链表头
1716 | * @param is_has_user_lib: 是否获取 user lib 信息
1717 | * @param is_match_memory: 是否要匹配存储器信息
1718 | * @retval 0: 正常 | -x: 错误
1719 | */
1720 | int map_file_process(const char *file_path,
1721 | struct load_region **region_head,
1722 | struct object_info **object_head,
1723 | bool is_get_user_lib,
1724 | bool is_match_memory)
1725 | {
1726 | FILE *p_file = fopen(file_path, "r");
1727 | if (p_file == NULL) {
1728 | return -1;
1729 | }
1730 |
1731 | /* 读取 map 文件 */
1732 | fseek(p_file, 0, SEEK_END);
1733 | long pos_head = ftell(p_file);
1734 | long pos_end = pos_head;
1735 | long memory_map_pos = 0;
1736 |
1737 | /* 从文件末尾开始逆序读取 */
1738 | while (pos_head)
1739 | {
1740 | fseek(p_file, pos_head, SEEK_SET);
1741 | if (fgetc(p_file) == '\n' && (pos_end - pos_head) > 1)
1742 | {
1743 | fseek(p_file, pos_head + 1, SEEK_SET);
1744 | fgets(_line_text, sizeof(_line_text), p_file);
1745 | pos_end = pos_head;
1746 |
1747 | if (strstr(_line_text, STR_MEMORY_MAP_OF_THE_IMAGE))
1748 | {
1749 | /* 记录位置并退出循环 */
1750 | memory_map_pos = ftell(p_file);
1751 | break;
1752 | }
1753 | }
1754 | pos_head--;
1755 | }
1756 |
1757 | if (pos_head == 0)
1758 | {
1759 | fclose(p_file);
1760 | return -2;
1761 | }
1762 |
1763 | /* 获取 map 文件中的 load region 和 execution region 信息 */
1764 | region_info_process(p_file, memory_map_pos, region_head, is_match_memory);
1765 |
1766 | /* 获取每个 .o 文件的 flash 和 RAM 占用情况 */
1767 | return object_info_process(object_head, p_file, NULL, is_get_user_lib, 0);
1768 | }
1769 |
1770 |
1771 | /**
1772 | * @brief 获取 load region 和 execution region 信息
1773 | * @note
1774 | * @param p_file: 文件对象
1775 | * @param read_start_pos: 开始读取的位置
1776 | * @param region_head: region 链表头
1777 | * @param is_match_memory: 是否要将 region 与 memory 绑定
1778 | * @retval 0: 正常 | -5: 获取失败
1779 | */
1780 | int region_info_process(FILE *p_file,
1781 | long read_start_pos,
1782 | struct load_region **region_head,
1783 | bool is_match_memory)
1784 | {
1785 | /* 先从记录的位置开始正序读取 */
1786 | fseek(p_file, read_start_pos, SEEK_SET);
1787 |
1788 | bool is_has_load_region = false;
1789 | uint8_t size_pos = 2;
1790 | struct load_region *l_region = NULL;
1791 | struct exec_region *e_region = NULL;
1792 |
1793 | while (fgets(_line_text, sizeof(_line_text), p_file))
1794 | {
1795 | if (strstr(_line_text, STR_IMAGE_COMPONENT_SIZE)) {
1796 | return 0;
1797 | }
1798 |
1799 | bool is_offchip = false;
1800 | char *str_p1 = NULL;
1801 | char *str_p2 = NULL;
1802 | char *end_ptr = NULL;
1803 | char *load_region_name = NULL;
1804 | char name[MAX_PRJ_NAME_SIZE] = {0};
1805 | uint32_t base_addr = 0;
1806 | uint32_t size = 0;
1807 | uint32_t used_size = 0;
1808 | size_t memory_id = 0;
1809 | MEMORY_TYPE memory_type = MEMORY_TYPE_NONE;
1810 |
1811 | str_p1 = strstr(_line_text, STR_LOAD_REGION);
1812 | if (str_p1)
1813 | {
1814 | str_p1 += strlen(STR_LOAD_REGION) + 1;
1815 | str_p2 = strstr(str_p1, " ");
1816 | *str_p2 = '\0';
1817 | strncpy_s(name, sizeof(name), str_p1, strnlen_s(str_p1, sizeof(name)));
1818 |
1819 | l_region = load_region_create(region_head, name);
1820 | is_has_load_region = true;
1821 | }
1822 | else if (is_has_load_region)
1823 | {
1824 | str_p1 = strstr(_line_text, STR_EXECUTION_REGION);
1825 | if (str_p1)
1826 | {
1827 | if (strstr(_line_text, STR_LOAD_BASE)) {
1828 | size_pos = 3;
1829 | }
1830 |
1831 | str_p1 += strlen(STR_EXECUTION_REGION) + 1;
1832 | str_p2 = strstr(str_p1, " ");
1833 | *str_p2 = '\0';
1834 | strncpy_s(name, sizeof(name), str_p1, strnlen_s(str_p1, sizeof(name)));
1835 |
1836 | str_p1 = strstr(str_p2 + 1, STR_EXECUTE_BASE_ADDR);
1837 | if (str_p1 == NULL)
1838 | {
1839 | str_p1 = strstr(str_p2 + 1, STR_EXECUTE_BASE);
1840 | if (str_p1 == NULL) {
1841 | return -5;
1842 | }
1843 | str_p1 += strlen(STR_EXECUTE_BASE);
1844 | }
1845 | else {
1846 | str_p1 += strlen(STR_EXECUTE_BASE_ADDR);
1847 | }
1848 |
1849 | str_p2 = strstr(str_p1, ",");
1850 | *str_p2 = '\0';
1851 | base_addr = strtoul(str_p1, &end_ptr, 16);
1852 |
1853 | str_p1 = strstr(str_p2 + 1, STR_REGION_USED_SIZE);
1854 | str_p1 += strlen(STR_REGION_USED_SIZE);
1855 | str_p2 = strstr(str_p1, ",");
1856 | *str_p2 = '\0';
1857 | used_size = strtoul(str_p1, &end_ptr, 16);
1858 |
1859 | str_p1 = strstr(str_p2 + 1, STR_REGION_MAX_SIZE);
1860 | str_p1 += strlen(STR_REGION_MAX_SIZE);
1861 | str_p2 = strstr(str_p1, ",");
1862 | *str_p2 = '\0';
1863 | size = strtoul(str_p1, &end_ptr, 16);
1864 |
1865 | is_offchip = false;
1866 | memory_id = UNKNOWN_MEMORY_ID;
1867 | memory_type = MEMORY_TYPE_UNKNOWN;
1868 | load_region_name = NULL;
1869 |
1870 | if (is_match_memory)
1871 | {
1872 | /* 将 execution region 与 对应的 memory 绑定 */
1873 | for (struct memory_info *memory_temp = _memory_info_head;
1874 | memory_temp != NULL;
1875 | memory_temp = memory_temp->next)
1876 | {
1877 | if (base_addr >= memory_temp->base_addr
1878 | && base_addr < (memory_temp->base_addr + memory_temp->size))
1879 | {
1880 | memory_temp->is_used = true;
1881 |
1882 | is_offchip = memory_temp->is_offchip;
1883 | memory_id = memory_temp->id;
1884 | memory_type = memory_temp->type;
1885 | load_region_name = l_region->name;
1886 |
1887 | if (size == UINT32_MAX) {
1888 | size = memory_temp->size;
1889 | }
1890 | break;
1891 | }
1892 | }
1893 | }
1894 |
1895 | region_zi_process(NULL, NULL, 0);
1896 | e_region = load_region_add_exec_region(&l_region,
1897 | name,
1898 | load_region_name,
1899 | memory_id,
1900 | base_addr,
1901 | size,
1902 | used_size,
1903 | memory_type,
1904 | is_offchip);
1905 | }
1906 | else if (e_region
1907 | && e_region->memory_type != MEMORY_TYPE_FLASH
1908 | && strstr(_line_text, "0x"))
1909 | {
1910 | region_zi_process(&e_region, _line_text, size_pos);
1911 | }
1912 | }
1913 | }
1914 |
1915 | return -5;
1916 | }
1917 |
1918 |
1919 | /**
1920 | * @brief 获取 region 中的 zero init 区域块分布
1921 | * @note e_region 参数传值为 NULL 时将复位本函数。
1922 | * 切换至新的 execution region 前,必须复位本函数
1923 | * @param e_region: execution region
1924 | * @param text: 一行文本内容
1925 | * @param size_pos: Size 栏目所在的位置,从 1 算起
1926 | * @retval None
1927 | */
1928 | void region_zi_process(struct exec_region **e_region,
1929 | char *text,
1930 | size_t size_pos)
1931 | {
1932 | static bool is_zi_start = false;
1933 | static uint32_t last_end_addr = 0;
1934 | static struct region_block **zi_block = NULL;
1935 |
1936 | if (e_region == NULL)
1937 | {
1938 | zi_block = NULL;
1939 | is_zi_start = false;
1940 | last_end_addr = 0;
1941 | return;
1942 | }
1943 |
1944 | if (strstr(text, STR_ZERO_INIT)) {
1945 | is_zi_start = true;
1946 | }
1947 | else if (strstr(text, STR_PADDING))
1948 | {
1949 | if (is_zi_start == false) {
1950 | return;
1951 | }
1952 | }
1953 | else
1954 | {
1955 | zi_block = NULL;
1956 | is_zi_start = false;
1957 | last_end_addr = 0;
1958 | return;
1959 | }
1960 |
1961 | char *addr_token = strtok(text, " ");
1962 | for (size_t i = 2; i < size_pos; i++) {
1963 | strtok(NULL, " ");
1964 | }
1965 | char *size_token = strtok(NULL, " ");
1966 |
1967 | char *end_ptr = NULL;
1968 | uint32_t addr = strtoul(addr_token, &end_ptr, 16);
1969 | uint32_t size = strtoul(size_token, &end_ptr, 16);
1970 |
1971 | if (addr > last_end_addr)
1972 | {
1973 | zi_block = &(*e_region)->zi_block;
1974 |
1975 | if (*zi_block)
1976 | {
1977 | struct region_block *block = *zi_block;
1978 |
1979 | while (block->next) {
1980 | block = block->next;
1981 | }
1982 | zi_block = &block->next;
1983 | }
1984 |
1985 | *zi_block = (struct region_block *)malloc(sizeof(struct region_block));
1986 | (*zi_block)->start_addr = addr;
1987 | (*zi_block)->size = size;
1988 | (*zi_block)->next = NULL;
1989 | }
1990 | else if (*zi_block) {
1991 | (*zi_block)->size += size;
1992 | }
1993 |
1994 | last_end_addr = addr + size;
1995 | }
1996 |
1997 |
1998 | /**
1999 | * @brief 获取 object info
2000 | * @note
2001 | * @param object_head: object 文件链表头
2002 | * @param p_file: 文件对象
2003 | * @param end_pos: [out] 最后读取到的文件位置
2004 | * @param is_get_user_lib: 是否获取用户 lib 信息
2005 | * @param parse_mode: 解析模式 0: 按 map 文件解析 | 1: 按 record 文件解析
2006 | * @retval 0: 正常 | -x: 错误
2007 | */
2008 | int object_info_process(struct object_info **object_head,
2009 | FILE *p_file,
2010 | long *end_pos,
2011 | bool is_get_user_lib,
2012 | uint8_t parse_mode)
2013 | {
2014 | int result = 0;
2015 | uint8_t state = 0;
2016 | uint32_t value[16] = {0};
2017 | char name[MAX_PRJ_NAME_SIZE] = {0};
2018 | char *token = NULL;
2019 | char *end_ptr = NULL;
2020 | char *new_line = NULL;
2021 | size_t index = 0;
2022 |
2023 | /* 获取用户文件的 object info */
2024 | while (fgets(_line_text, sizeof(_line_text), p_file))
2025 | {
2026 | switch (state)
2027 | {
2028 | case 0:
2029 | if (parse_mode == 0)
2030 | {
2031 | /* Object Name 全部添加 */
2032 | if (strstr(_line_text, ".o"))
2033 | {
2034 | index = 0;
2035 | /* 切割后转换 */
2036 | token = strtok(_line_text, " ");
2037 | while (token != NULL)
2038 | {
2039 | if (index < OBJECT_INFO_STR_QTY - 1) {
2040 | value[index] = strtoul(token, &end_ptr, 10);
2041 | }
2042 | else /* 最后一个是名称 */
2043 | {
2044 | new_line = strrchr(token, '\n');
2045 | if (new_line) {
2046 | *new_line = '\0';
2047 | }
2048 | strncpy_s(name, sizeof(name), token, strnlen_s(token, sizeof(name)));
2049 | }
2050 | if (++index == OBJECT_INFO_STR_QTY) {
2051 | break;
2052 | }
2053 | token = strtok(NULL, " ");
2054 | }
2055 |
2056 | /* 保存 */
2057 | if (index == OBJECT_INFO_STR_QTY) {
2058 | object_info_add(object_head, name, value[0], value[2], value[3], value[4]);
2059 | }
2060 | else
2061 | {
2062 | result = -3;
2063 | break;
2064 | }
2065 | }
2066 | else if (strstr(_line_text, STR_LIBRARY_MEMBER_NAME))
2067 | {
2068 | if (is_get_user_lib) {
2069 | state = 1;
2070 | } else {
2071 | state = 3;
2072 | }
2073 | }
2074 | }
2075 | else if (parse_mode == 1)
2076 | {
2077 | if (strstr(_line_text, STR_OBJECT_NAME)) {
2078 | state = 2;
2079 | }
2080 | }
2081 | break;
2082 | case 1:
2083 | /* Library Member Name 仅添加匹配的 object */
2084 | if (strstr(_line_text, ".o"))
2085 | {
2086 | index = 0;
2087 | /* 切割后转换 */
2088 | token = strtok(_line_text, " ");
2089 | while (token != NULL)
2090 | {
2091 | if (index < OBJECT_INFO_STR_QTY - 1) {
2092 | value[index] = strtoul(token, &end_ptr, 10);
2093 | }
2094 | else /* 最后一个是名称 */
2095 | {
2096 | new_line = strrchr(token, '\n');
2097 | if (new_line) {
2098 | *new_line = '\0';
2099 | }
2100 | strncpy_s(name, sizeof(name), token, strnlen_s(token, sizeof(name)));
2101 | }
2102 | if (++index == OBJECT_INFO_STR_QTY) {
2103 | break;
2104 | }
2105 | token = strtok(NULL, " ");
2106 | }
2107 |
2108 | /* 保存 */
2109 | if (index == OBJECT_INFO_STR_QTY)
2110 | {
2111 | for (struct file_path_list *path_temp = _file_path_list_head;
2112 | path_temp != NULL;
2113 | path_temp = path_temp->next)
2114 | {
2115 | if (path_temp->file_type == OBJECT_FILE_TYPE_LIBRARY)
2116 | {
2117 | if (strcasecmp(name, path_temp->new_object_name) == 0)
2118 | {
2119 | object_info_add(object_head, name, value[0], value[2], value[3], value[4]);
2120 | break;
2121 | }
2122 | }
2123 | }
2124 | }
2125 | }
2126 | else if (strstr(_line_text, STR_LIBRARY_NAME)) {
2127 | state = 2;
2128 | }
2129 | break;
2130 | case 2:
2131 | /* Library Member Name 仅添加匹配的 object */
2132 | if (strstr(_line_text, STR_OBJECT_TOTALS))
2133 | {
2134 | state = 3;
2135 | break;
2136 | }
2137 | else
2138 | {
2139 | index = 0;
2140 | /* 切割后转换 */
2141 | token = strtok(_line_text, " ");
2142 | while (token != NULL)
2143 | {
2144 | if (index < OBJECT_INFO_STR_QTY - 1) {
2145 | value[index] = strtoul(token, &end_ptr, 10);
2146 | }
2147 | else /* 最后一个是名称 */
2148 | {
2149 | new_line = strrchr(token, '\n');
2150 | if (new_line) {
2151 | *new_line = '\0';
2152 | }
2153 | strncpy_s(name, sizeof(name), token, strnlen_s(token, sizeof(name)));
2154 | }
2155 | if (++index == OBJECT_INFO_STR_QTY) {
2156 | break;
2157 | }
2158 | token = strtok(NULL, " ");
2159 | }
2160 |
2161 | /* 保存 */
2162 | if (index == OBJECT_INFO_STR_QTY)
2163 | {
2164 | if (parse_mode == 1)
2165 | {
2166 | object_info_add(object_head, name, value[0], value[2], value[3], value[4]);
2167 | break;
2168 | }
2169 |
2170 | for (struct file_path_list *path_temp = _file_path_list_head;
2171 | path_temp != NULL;
2172 | path_temp = path_temp->next)
2173 | {
2174 | if (path_temp->file_type == OBJECT_FILE_TYPE_LIBRARY)
2175 | {
2176 | if (strcasecmp(name, path_temp->old_name) == 0)
2177 | {
2178 | object_info_add(object_head, name, value[0], value[2], value[3], value[4]);
2179 | break;
2180 | }
2181 | }
2182 | }
2183 | }
2184 | }
2185 | break;
2186 | default: break;
2187 | }
2188 |
2189 | if (state == 3 || result != 0) {
2190 | break;
2191 | }
2192 | }
2193 |
2194 | if (state == 3)
2195 | {
2196 | if (end_pos) {
2197 | *end_pos = ftell(p_file);
2198 | }
2199 | }
2200 | fclose(p_file);
2201 | return result;
2202 | }
2203 |
2204 |
2205 | /**
2206 | * @brief 记录文件处理
2207 | * @note
2208 | * @param file_path: 文件的绝对路径
2209 | * @param region_head: region 链表头
2210 | * @param object_head: object 文件链表头
2211 | * @param is_has_object: [out] 是否存在 object 信息
2212 | * @param is_has_region: [out] 是否存在 region 信息
2213 | * @param is_match_memory: 是否要将 region 与 memory 绑定
2214 | * @retval 0: 正常 | -x: 错误
2215 | */
2216 | int record_file_process(const char *file_path,
2217 | struct load_region **region_head,
2218 | struct object_info **object_head,
2219 | bool *is_has_object,
2220 | bool *is_has_region,
2221 | bool is_match_memory)
2222 | {
2223 | *is_has_object = false;
2224 | *is_has_region = false;
2225 |
2226 | FILE *p_file = fopen(file_path, "r");
2227 | if (p_file == NULL) {
2228 | return -1;
2229 | }
2230 |
2231 | long end_pos = 0;
2232 | int result = object_info_process(object_head, p_file, &end_pos, false, 1);
2233 | if (result == 0) {
2234 | *is_has_object = true;
2235 | }
2236 |
2237 | p_file = fopen(file_path, "r");
2238 | if (p_file == NULL) {
2239 | return -1;
2240 | }
2241 | result = region_info_process(p_file, end_pos, region_head, is_match_memory);
2242 | if (result == 0) {
2243 | *is_has_region = true;
2244 | }
2245 |
2246 | return result;
2247 | }
2248 |
2249 |
2250 | /**
2251 | * @brief object 信息打印处理
2252 | * @note
2253 | * @param object_head: object 文件链表头
2254 | * @param max_path_len: 最大路径长度
2255 | * @param is_has_record: 是否有记录文件
2256 | * @retval None
2257 | */
2258 | void object_print_process(struct object_info *object_head,
2259 | size_t max_path_len,
2260 | bool is_has_record)
2261 | {
2262 | if ((max_path_len + 2) < strlen(STR_FILE)) {
2263 | max_path_len = strlen(STR_FILE);
2264 | }
2265 |
2266 | size_t len = max_path_len + 2 - strlen(STR_FILE);
2267 | if (_is_display_path) {
2268 | len += strlen("():");
2269 | }
2270 |
2271 | size_t left_space = len / 2;
2272 | size_t right_space = left_space;
2273 | if (len % 2) {
2274 | left_space += 1;
2275 | }
2276 |
2277 | snprintf(_line_text, sizeof(_line_text),
2278 | "%*s%s%*s| RAM (byte) | FLASH (byte) |\n",
2279 | left_space, " ", STR_FILE, right_space, " ");
2280 |
2281 | len = strnlen_s(_line_text, sizeof(_line_text));
2282 | char *line = (char *)malloc(len);
2283 | size_t i = 0;
2284 | for (; i < len - 1; i++) {
2285 | line[i] = '-';
2286 | }
2287 | line[i] = '\0';
2288 | log_print(_log_file, "%s\n", line);
2289 | log_print(_log_file, "%s", _line_text);
2290 | log_print(_log_file, "%s\n", line);
2291 |
2292 | for (struct object_info *obj_info = object_head;
2293 | obj_info != NULL;
2294 | obj_info = obj_info->next)
2295 | {
2296 | if (obj_info->path == NULL) {
2297 | continue;
2298 | }
2299 |
2300 | char *path = obj_info->path;
2301 | char ram_text[MAX_PRJ_NAME_SIZE] = {0};
2302 | char flash_text[MAX_PRJ_NAME_SIZE] = {0};
2303 | size_t path_len = strnlen_s(obj_info->path, MAX_PATH);
2304 | size_t path_space = max_path_len - path_len + 1;
2305 | uint32_t ram = obj_info->rw_data + obj_info->zi_data;
2306 | uint32_t flash = obj_info->code + obj_info->ro_data + obj_info->rw_data;
2307 |
2308 | // if (obj_info->path == NULL || _is_display_path == false)
2309 | if (_is_display_path == false)
2310 | {
2311 | path = obj_info->name;
2312 | if (path == NULL) {
2313 | path = "UNKNOWN";
2314 | }
2315 | path_len = strnlen_s(path, MAX_PATH);
2316 | path_space = max_path_len - path_len + 1;
2317 | }
2318 |
2319 | if (is_has_record)
2320 | {
2321 | if (obj_info->old_object == NULL)
2322 | {
2323 | strncpy_s(ram_text, sizeof(ram_text), "[NEW] ", 10);
2324 | strncpy_s(flash_text, sizeof(flash_text), "[NEW] ", 10);
2325 | }
2326 | else
2327 | {
2328 | char ram_sign;
2329 | char flash_sign;
2330 | uint32_t old_ram = obj_info->old_object->rw_data + obj_info->old_object->zi_data;
2331 | uint32_t old_flash = obj_info->old_object->code + obj_info->old_object->ro_data + obj_info->old_object->rw_data;
2332 | uint32_t ram_increm = 0;
2333 | uint32_t flash_increm = 0;
2334 |
2335 | if (ram < old_ram)
2336 | {
2337 | ram_increm = old_ram - ram;
2338 | ram_sign = '-';
2339 | }
2340 | else
2341 | {
2342 | ram_increm = ram - old_ram;
2343 | ram_sign = '+';
2344 | }
2345 |
2346 | if (flash < old_flash)
2347 | {
2348 | flash_increm = old_flash - flash;
2349 | flash_sign = '-';
2350 | }
2351 | else
2352 | {
2353 | flash_increm = flash - old_flash;
2354 | flash_sign = '+';
2355 | }
2356 |
2357 | char str[MAX_PRJ_NAME_SIZE] = {0};
2358 | size_t str_len = 0;
2359 | size_t space_len = 0;
2360 |
2361 | if (ram_increm)
2362 | {
2363 | snprintf(ram_text, sizeof(ram_text), "[%c%d]", ram_sign, ram_increm);
2364 | str_len = strnlen_s(ram_text, sizeof(ram_text));
2365 | space_len = 10 - str_len;
2366 | if (space_len)
2367 | {
2368 | for (size_t i = 0; i < space_len; i++) {
2369 | str[i] = ' ';
2370 | }
2371 | strncat_s(ram_text, sizeof(ram_text), str, space_len);
2372 | }
2373 | }
2374 | else {
2375 | strncpy_s(ram_text, sizeof(ram_text), " ", 10);
2376 | }
2377 |
2378 | if (flash_increm)
2379 | {
2380 | snprintf(flash_text, sizeof(flash_text), "[%c%d]", flash_sign, flash_increm);
2381 | str_len = strnlen_s(flash_text, sizeof(flash_text));
2382 | space_len = 10 - str_len;
2383 | if (space_len)
2384 | {
2385 | for (size_t i = 0; i < space_len; i++) {
2386 | str[i] = ' ';
2387 | }
2388 | strncat_s(flash_text, sizeof(flash_text), str, space_len);
2389 | }
2390 | }
2391 | else {
2392 | strncpy_s(flash_text, sizeof(flash_text), " ", 10);
2393 | }
2394 | }
2395 | }
2396 | else
2397 | {
2398 | strncpy_s(ram_text, sizeof(ram_text), " ", 10);
2399 | strncpy_s(flash_text, sizeof(flash_text), " ", 10);
2400 | }
2401 |
2402 | if (_is_display_path)
2403 | {
2404 | snprintf(_line_text, sizeof(_line_text),
2405 | "%s():%*s | %10d %s | %10d %s |",
2406 | path, path_space, " ", ram, ram_text, flash, flash_text);
2407 | }
2408 | else
2409 | {
2410 | snprintf(_line_text, sizeof(_line_text),
2411 | "%s%*s | %10d %s | %10d %s |",
2412 | obj_info->name, path_space, " ", ram, ram_text, flash, flash_text);
2413 | }
2414 | log_print(_log_file, "%s\n", _line_text);
2415 | }
2416 | log_print(_log_file, "%s\n", line);
2417 | free(line);
2418 | }
2419 |
2420 |
2421 | /**
2422 | * @brief 为 memory 编号
2423 | * @note
2424 | * @param mem_type: 指定编号的内存类型
2425 | * @retval None
2426 | */
2427 | void memory_numbering(MEMORY_TYPE mem_type)
2428 | {
2429 | size_t id = 1;
2430 |
2431 | for (struct memory_info *memory = _memory_info_head;
2432 | memory != NULL;
2433 | memory = memory->next)
2434 | {
2435 | if (memory->is_used == false) {
2436 | _is_has_unused_region = true;
2437 | }
2438 |
2439 | if (memory->type != mem_type) {
2440 | continue;
2441 | }
2442 |
2443 | memory->mem_id = id++;
2444 | }
2445 | }
2446 |
2447 |
2448 | /**
2449 | * @brief 打印未使用的 memory
2450 | * @note
2451 | * @param mem_type: 指定打印的内存类型
2452 | * @param max_region_name: 最大的 execution region 名称长度
2453 | * @retval None
2454 | */
2455 | void memory_print_unused(MEMORY_TYPE mem_type, size_t max_region_name)
2456 | {
2457 | for (struct memory_info *memory = _memory_info_head;
2458 | memory != NULL;
2459 | memory = memory->next)
2460 | {
2461 | if (memory->is_used) {
2462 | continue;
2463 | }
2464 |
2465 | if (memory->type != mem_type) {
2466 | continue;
2467 | }
2468 |
2469 | if (mem_type == MEMORY_TYPE_RAM) {
2470 | snprintf(_line_text, sizeof(_line_text), " RAM %d ", memory->mem_id);
2471 | }
2472 | else if (mem_type == MEMORY_TYPE_FLASH) {
2473 | snprintf(_line_text, sizeof(_line_text), " FLASH %d ", memory->mem_id);
2474 | }
2475 | else {
2476 | snprintf(_line_text, sizeof(_line_text), " UNKNOWN");
2477 | }
2478 |
2479 | log_print(_log_file, "%s%*s [0x%08X | 0x%08X (%d)]\n",
2480 | _line_text, max_region_name, " ", memory->base_addr, memory->size, memory->size);
2481 | }
2482 | }
2483 |
2484 |
2485 | /**
2486 | * @brief 模式零打印内存占用情况
2487 | * @note
2488 | * @param e_region: execution region
2489 | * @param load_region_name: 指定打印的 load region 名称
2490 | * @param mem_type: 指定打印的 execution region 内存类型
2491 | * @param max_region_name: 最大的 execution region 名称长度
2492 | * @param is_has_record: 是否有记录文件
2493 | * @param is_print_null: 是否打印未使用的存储器
2494 | * @retval None
2495 | */
2496 | void memory_mode0_print(struct exec_region *e_region,
2497 | const char *load_region_name,
2498 | MEMORY_TYPE mem_type,
2499 | size_t max_region_name,
2500 | bool is_has_record,
2501 | bool is_print_null)
2502 | {
2503 | bool is_print_head = false;
2504 | bool is_print_body = false;
2505 | char str[MAX_PRJ_NAME_SIZE] = {0};
2506 |
2507 | if (mem_type == MEMORY_TYPE_UNKNOWN)
2508 | {
2509 | for (struct exec_region *region = e_region;
2510 | region != NULL;
2511 | region = region->next)
2512 | {
2513 | if (region->memory_type == MEMORY_TYPE_UNKNOWN)
2514 | {
2515 | if (is_print_head == false)
2516 | {
2517 | log_print(_log_file, " UNKNOWN\n");
2518 | is_print_head = true;
2519 | }
2520 | progress_print(region, max_region_name, is_has_record);
2521 | }
2522 | }
2523 | if (is_print_head) {
2524 | log_print(_log_file, " \n");
2525 | }
2526 | return;
2527 | }
2528 |
2529 | size_t id = 0;
2530 | bool is_no_region = true;
2531 |
2532 | for (struct memory_info *memory = _memory_info_head;
2533 | memory != NULL;
2534 | memory = memory->next)
2535 | {
2536 | if (memory->type != mem_type) {
2537 | continue;
2538 | }
2539 |
2540 | id++;
2541 | is_no_region = true;
2542 | is_print_head = false;
2543 |
2544 | if (mem_type == MEMORY_TYPE_RAM) {
2545 | snprintf(str, sizeof(str), " RAM %d ", memory->mem_id);
2546 | }
2547 | else if (mem_type == MEMORY_TYPE_FLASH) {
2548 | snprintf(str, sizeof(str), " FLASH %d ", memory->mem_id);
2549 | }
2550 |
2551 | for (struct exec_region *region = e_region;
2552 | region != NULL;
2553 | region = region->next)
2554 | {
2555 | if (region->used_size == 0) {
2556 | continue;
2557 | }
2558 |
2559 | if (strcmp(load_region_name, region->load_region_name)) {
2560 | continue;
2561 | }
2562 |
2563 | if (region->is_printed == false
2564 | && memory->id == region->memory_id
2565 | && memory->type == region->memory_type)
2566 | {
2567 | if (is_print_head == false)
2568 | {
2569 | log_print(_log_file, "%s%*s [0x%08X | 0x%08X (%d)]\n",
2570 | str, max_region_name, " ", memory->base_addr, memory->size, memory->size);
2571 | is_print_head = true;
2572 | is_print_body = true;
2573 | }
2574 |
2575 | progress_print(region, max_region_name, is_has_record);
2576 | is_no_region = false;
2577 | }
2578 | }
2579 |
2580 | // if (is_no_region
2581 | // && is_print_null
2582 | // && memory->is_from_pack)
2583 | // {
2584 | // log_print(_log_file, "%s%*s [0x%.8X | 0x%.8X (%d)]\n",
2585 | // str, max_region_name, " ", memory->base_addr, memory->size, memory->size);
2586 | // log_print(_log_file, " NULL\n");
2587 | // }
2588 | // else {
2589 | // log_print(_log_file, " \n");
2590 | // }
2591 | }
2592 |
2593 | if (is_print_body) {
2594 | log_print(_log_file, " \n");
2595 | }
2596 | }
2597 |
2598 |
2599 | /**
2600 | * @brief 模式一打印内存占用情况
2601 | * @note
2602 | * @param e_region: execution region
2603 | * @param mem_type: 指定打印的 execution region 内存类型
2604 | * @param is_offchip: 是否为片外 memory
2605 | * @param max_region_name: 最大的 execution region 名称长度
2606 | * @param is_has_record: 是否有记录文件
2607 | * @retval None
2608 | */
2609 | void memory_mode1_print(struct exec_region *e_region,
2610 | MEMORY_TYPE mem_type,
2611 | bool is_offchip,
2612 | size_t max_region_name,
2613 | bool is_has_record)
2614 | {
2615 | bool is_print_head = false;
2616 | char str[MAX_PRJ_NAME_SIZE] = {0};
2617 |
2618 | if (mem_type == MEMORY_TYPE_UNKNOWN)
2619 | {
2620 | for (struct exec_region *region = e_region;
2621 | region != NULL;
2622 | region = region->next)
2623 | {
2624 | if (region->memory_type == MEMORY_TYPE_UNKNOWN)
2625 | {
2626 | if (is_print_head == false)
2627 | {
2628 | log_print(_log_file, " UNKNOWN\n");
2629 | is_print_head = true;
2630 | }
2631 | progress_print(region, max_region_name, is_has_record);
2632 | }
2633 | }
2634 | if (is_print_head) {
2635 | log_print(_log_file, " \n");
2636 | }
2637 | return;
2638 | }
2639 |
2640 | if (mem_type == MEMORY_TYPE_RAM) {
2641 | strncpy_s(str, sizeof(str), " RAM", strlen(" RAM"));
2642 | }
2643 | else if (mem_type == MEMORY_TYPE_FLASH) {
2644 | strncpy_s(str, sizeof(str), " FLASH", strlen(" FLASH"));
2645 | }
2646 |
2647 | if (is_offchip == false) {
2648 | strncat_s(str, sizeof(str), " (on-chip)\n", strlen(" (on-chip)\n"));
2649 | } else {
2650 | strncat_s(str, sizeof(str), " (off-chip)\n", strlen(" (off-chip)\n"));
2651 | }
2652 |
2653 | for (struct exec_region *region = e_region;
2654 | region != NULL;
2655 | region = region->next)
2656 | {
2657 | if (region->is_printed == false
2658 | && region->is_offchip == is_offchip
2659 | && region->memory_type == mem_type)
2660 | {
2661 | if (is_print_head == false)
2662 | {
2663 | log_print(_log_file, str);
2664 | is_print_head = true;
2665 | }
2666 | progress_print(region, max_region_name, is_has_record);
2667 | region->is_printed = true;
2668 | }
2669 | }
2670 |
2671 | if (is_print_head) {
2672 | log_print(_log_file, " \n");
2673 | }
2674 | }
2675 |
2676 |
2677 | /**
2678 | * @brief 模式二打印内存占用情况
2679 | * @note
2680 | * @param e_region: execution region
2681 | * @param max_region_name: 最大的 execution region 名称长度
2682 | * @param is_has_record: 是否有记录文件
2683 | * @retval None
2684 | */
2685 | void memory_mode2_print(struct exec_region *e_region,
2686 | size_t max_region_name,
2687 | bool is_has_record)
2688 | {
2689 | for (struct exec_region *region_temp = e_region;
2690 | region_temp != NULL;
2691 | region_temp = region_temp->next)
2692 | {
2693 | if (region_temp->is_printed == false)
2694 | {
2695 | progress_print(region_temp, max_region_name, is_has_record);
2696 | region_temp->is_printed = true;
2697 | }
2698 | }
2699 | log_print(_log_file, " \n");
2700 | }
2701 |
2702 |
2703 | /**
2704 | * @brief 内存占用进度条化打印
2705 | * @note
2706 | * @param region: execution region
2707 | * @param max_region_name: 最大的 execution region 名称长度
2708 | * @param is_has_record: 是否有记录文件
2709 | * @retval None
2710 | */
2711 | void progress_print(struct exec_region *region,
2712 | size_t max_region_name,
2713 | bool is_has_record)
2714 | {
2715 | double size = 0;
2716 | double used_size = 0;
2717 | uint32_t interge = 0;
2718 | char size_str[MAX_PRJ_NAME_SIZE] = {0};
2719 | char used_size_str[MAX_PRJ_NAME_SIZE] = {0};
2720 |
2721 | if (region->used_size < 1024)
2722 | {
2723 | used_size = region->used_size;
2724 | snprintf(used_size_str, sizeof(used_size_str), "%8d", (uint32_t)used_size);
2725 | }
2726 | else if (region->used_size < (1024 * 1024))
2727 | {
2728 | used_size = (double)region->used_size / 1024;
2729 | used_size = floor(used_size * 10) / 10; /* 向下取整一位,防止被四舍五入 */
2730 | snprintf(used_size_str, sizeof(used_size_str), "%5.1f KB", used_size);
2731 | }
2732 | else
2733 | {
2734 | used_size = (double)region->used_size / (1024 * 1024);
2735 | used_size = floor(used_size * 10) / 10;
2736 | snprintf(used_size_str, sizeof(used_size_str), "%5.1f MB", used_size);
2737 | }
2738 |
2739 | if (region->size < 1024)
2740 | {
2741 | size = region->size;
2742 | snprintf(size_str, sizeof(size_str), "%8d", (uint32_t)size);
2743 | }
2744 | else if (region->size < (1024 * 1024))
2745 | {
2746 | size = (double)region->size / 1024;
2747 | size = floor(size * 10) / 10;
2748 | snprintf(size_str, sizeof(size_str), "%5.1f KB", size);
2749 | }
2750 | else
2751 | {
2752 | size = (double)region->size / (1024 * 1024);
2753 | size = floor(size * 10) / 10;
2754 | snprintf(size_str, sizeof(size_str), "%5.1f MB", size);
2755 | }
2756 |
2757 | double percent = (double)region->used_size * 100 / region->size;
2758 | if (percent > 100) {
2759 | percent = 100;
2760 | }
2761 |
2762 | size_t used = 0;
2763 | char progress[256] = {0};
2764 | uint8_t zi_symbol[2] = {0};
2765 | uint8_t used_symbol[2] = {0};
2766 | uint8_t symbol_size = 0;
2767 |
2768 | symbol_size = 1;
2769 | zi_symbol[0] = ZI_SYMBOL_0;
2770 | used_symbol[0] = USED_SYMBOL_0;
2771 |
2772 | if (_progress_style == PROGRESS_STYLE_0)
2773 | {
2774 | if (_encoding_type == ENCODING_TYPE_GBK)
2775 | {
2776 | symbol_size = 2;
2777 | zi_symbol[0] = ZI_SYMBOL_GBK_H;
2778 | zi_symbol[1] = ZI_SYMBOL_GBK_L;
2779 | used_symbol[0] = USED_SYMBOL_GBK_H;
2780 | used_symbol[1] = USED_SYMBOL_GBK_L;
2781 | }
2782 | else if (_encoding_type == ENCODING_TYPE_BIG5)
2783 | {
2784 | symbol_size = 2;
2785 | zi_symbol[0] = ZI_SYMBOL_BIG5_H;
2786 | zi_symbol[1] = ZI_SYMBOL_BIG5_L;
2787 | used_symbol[0] = USED_SYMBOL_BIG5_H;
2788 | used_symbol[1] = USED_SYMBOL_BIG5_L;
2789 | }
2790 | }
2791 | else if (_progress_style == PROGRESS_STYLE_2)
2792 | {
2793 | symbol_size = 1;
2794 | zi_symbol[0] = ZI_SYMBOL_1;
2795 | used_symbol[0] = USED_SYMBOL_1;
2796 | }
2797 |
2798 | /* 先填充实际占用部分 */
2799 | for (; used < ((uint32_t)percent / 2); used++)
2800 | {
2801 | memcpy(&progress[symbol_size * used], used_symbol, symbol_size);
2802 | }
2803 | /* 小于显示比例,避免是空的 */
2804 | if (used == 0 && region->used_size != 0)
2805 | {
2806 | memcpy(&progress[0], used_symbol, symbol_size);
2807 | used = 1;
2808 | }
2809 | /* 将占用部分的 ZI 部分替换 */
2810 | for (struct region_block *block = region->zi_block;
2811 | block != NULL;
2812 | block = block->next)
2813 | {
2814 | size_t zi_start = ((double)block->start_addr - region->base_addr) * 100 / region->size / 2;
2815 | size_t zi_end = ((double)block->start_addr + block->size - region->base_addr) * 100 / region->size / 2;
2816 |
2817 | if (zi_start == 0 && block->start_addr > region->base_addr) {
2818 | zi_start = 1;
2819 | }
2820 | log_save(_log_file, " [zi start] %d [zi end] %d\n", zi_start, zi_end);
2821 |
2822 | for (; zi_start < zi_end && zi_start < used; zi_start++) {
2823 | memcpy(&progress[symbol_size * zi_start], zi_symbol, symbol_size);
2824 | }
2825 |
2826 | if ((block->start_addr + block->size) >= (region->base_addr + region->size)) {
2827 | break;
2828 | }
2829 | }
2830 | /* 剩下未使用部分 */
2831 | for (size_t unused = 0; unused < (50 - used); unused++){
2832 | strncat_s(progress, sizeof(progress), UNUSE_SYMBOL, strlen(UNUSE_SYMBOL));
2833 | }
2834 |
2835 | size_t space_len = max_region_name - strnlen_s(region->name, max_region_name) + 1;
2836 | snprintf(_line_text, sizeof(_line_text),
2837 | " %s%*s [0x%08X]|%s| ( %s / %s ) %5.1f%% ",
2838 | region->name, space_len, " ", region->base_addr, progress, used_size_str, size_str, percent);
2839 |
2840 | if (is_has_record)
2841 | {
2842 | if (region->old_exec_region == NULL) {
2843 | strncat_s(_line_text, sizeof(_line_text), "[NEW]", 5);
2844 | }
2845 | else
2846 | {
2847 | char sign;
2848 | uint32_t data_increm = 0;
2849 | char str_increm[MAX_PRJ_NAME_SIZE] = {0};
2850 |
2851 | if (region->used_size < region->old_exec_region->used_size)
2852 | {
2853 | sign = '-';
2854 | data_increm = region->old_exec_region->used_size - region->used_size;
2855 | }
2856 | else
2857 | {
2858 | sign = '+';
2859 | data_increm = region->used_size - region->old_exec_region->used_size;
2860 | }
2861 |
2862 | if (data_increm)
2863 | {
2864 | snprintf(str_increm, sizeof(str_increm), "[%c%d]", sign, data_increm);
2865 | strncat_s(_line_text, sizeof(_line_text), str_increm, strnlen_s(str_increm, sizeof(str_increm)));
2866 | }
2867 | }
2868 | }
2869 | log_print(_log_file, "%s\n", _line_text);
2870 | }
2871 |
2872 |
2873 | /**
2874 | * @brief 打印栈使用情况
2875 | * @note
2876 | * @param file_path: htm 文件路径
2877 | * @retval None
2878 | */
2879 | void stack_print_process(const char *file_path)
2880 | {
2881 | FILE *p_file = fopen(file_path, "r");
2882 | if (p_file == NULL) {
2883 | return;
2884 | }
2885 |
2886 | char *str_p1 = NULL;
2887 | char *str_p2 = NULL;
2888 | while (fgets(_line_text, sizeof(_line_text), p_file))
2889 | {
2890 | str_p1 = strstr(_line_text, STR_MAX_STACK_USAGE);
2891 | if (str_p1)
2892 | {
2893 | str_p2 = strrchr(_line_text, ')');
2894 | str_p2 += 1;
2895 | *str_p2 = '\0';
2896 | log_print(_log_file, "%s\n \n", str_p1);
2897 | break;
2898 | }
2899 | }
2900 | fclose(p_file);
2901 | return;
2902 | }
2903 |
2904 |
2905 | /**
2906 | * @brief 按扩展名搜索文件
2907 | * @note 本函数不会递归搜索
2908 | * @param dir: 要搜索的目录
2909 | * @param dir_len: 要搜索的目录长度
2910 | * @param extension[]: 扩展名,支持多个
2911 | * @param extension_qty: 扩展名数量
2912 | * @param list: [out] 保存搜索到的文件路径
2913 | * @retval None
2914 | */
2915 | void search_files_by_extension(const char *dir,
2916 | size_t dir_len,
2917 | const char *extension[],
2918 | size_t extension_qty,
2919 | struct prj_path_list *list)
2920 | {
2921 | HANDLE h_find;
2922 | WIN32_FIND_DATA find_data;
2923 |
2924 | /* 加上 '*' 以搜索所有文件和文件夹 */
2925 | char *path = (char *)malloc(dir_len + 3);
2926 | snprintf(path, dir_len + 3, "%s\\*", dir);
2927 |
2928 | /* 开始搜索 */
2929 | h_find = FindFirstFile(path, &find_data);
2930 | if (h_find == INVALID_HANDLE_VALUE)
2931 | {
2932 | free(path);
2933 | return;
2934 | }
2935 |
2936 | do {
2937 | /* 如果找到的是文件,判断其后缀是否为 keil 工程 */
2938 | if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
2939 | {
2940 | char *str = strrchr(find_data.cFileName, '.');
2941 | if (str && is_same_string(str, extension, extension_qty))
2942 | {
2943 | size_t len = dir_len + strnlen_s(find_data.cFileName, MAX_PATH) + 2;
2944 | char *file_path = malloc(len);
2945 | snprintf(file_path, len, "%s\\%s", dir, find_data.cFileName);
2946 | prj_path_list_add(list, file_path);
2947 | }
2948 | }
2949 | } while (FindNextFile(h_find, &find_data));
2950 |
2951 | FindClose(h_find);
2952 | free(path);
2953 | }
2954 |
2955 |
2956 | /**
2957 | * @brief log 记录
2958 | * @note
2959 | * @param p_log: log 文件
2960 | * @param is_print: 是否打印
2961 | * @param fmt: 格式化字符串
2962 | * @param ...: 不定长参数
2963 | * @retval None
2964 | */
2965 | void log_write(FILE *p_log,
2966 | bool is_print,
2967 | const char *fmt,
2968 | ...)
2969 | {
2970 | va_list args;
2971 | uint16_t len;
2972 | static char buff[1024];
2973 |
2974 | va_start(args, fmt);
2975 |
2976 | memset(buff, 0, sizeof(buff));
2977 | len = vsnprintf(buff, sizeof(buff) - 1, fmt, args);
2978 | if (len > sizeof(buff) - 1) {
2979 | len = sizeof(buff) - 1;
2980 | }
2981 |
2982 | if (p_log && _is_save_log) {
2983 | fputs(buff, p_log);
2984 | }
2985 |
2986 | if (is_print) {
2987 | printf("%s", buff);
2988 | }
2989 |
2990 | va_end(args);
2991 | }
2992 |
2993 |
2994 | /**
2995 | * @brief 拼接路径
2996 | * @note
2997 | * @param out_path: [out] 拼接后输出的路径
2998 | * @param out_path_size: 输出的路径的大小
2999 | * @param absolute_path: 输入的绝对路径
3000 | * @param relative_path: 输入的相对路径
3001 | * @retval 0: 正常 | -x: 错误
3002 | */
3003 | int combine_path(char *out_path,
3004 | size_t out_path_size,
3005 | const char *absolute_path,
3006 | const char *relative_path)
3007 | {
3008 | /* 1. 将绝对路径 absolute_path 的文件名和扩展名去除 */
3009 | strncpy_s(out_path, out_path_size, absolute_path, strnlen_s(absolute_path, MAX_PATH));
3010 |
3011 | char *last_slash = strrchr(out_path, '\\');
3012 | if (last_slash == NULL) {
3013 | last_slash = strrchr(out_path, '/');
3014 | }
3015 | if (last_slash != NULL)
3016 | {
3017 | /* 说明不是是盘符根目录 */
3018 | if (*(last_slash - 1) != ':') {
3019 | *last_slash = '\0';
3020 | }
3021 | }
3022 | else {
3023 | return -1;
3024 | }
3025 |
3026 | /* 2. 读取绝对路径的目录层级 并 记录每一层级的相对偏移值 */
3027 | size_t hierarchy_count = 0;
3028 | size_t dir_hierarchy[MAX_DIR_HIERARCHY];
3029 |
3030 | /* 逐字符遍历路径 */
3031 | for (size_t i = 0; i < strnlen_s(out_path, out_path_size); i++)
3032 | {
3033 | if (out_path[i] == '\\' || out_path[i] == '/')
3034 | {
3035 | dir_hierarchy[hierarchy_count++] = i;
3036 |
3037 | if (hierarchy_count >= MAX_DIR_HIERARCHY) {
3038 | break;
3039 | }
3040 | }
3041 | }
3042 |
3043 | /* 3. 读取相对路径 relative_path 的向上层级数 */
3044 | size_t dir_up_count = 0;
3045 | size_t valid_path_offset = 0;
3046 |
3047 | for (size_t i = 0; i < strnlen_s(relative_path, MAX_PATH); )
3048 | {
3049 | if (relative_path[i] == '.'
3050 | && relative_path[i+1] == '.'
3051 | && (relative_path[i+2] == '\\' || relative_path[i+2] == '/'))
3052 | {
3053 | i += 3;
3054 | dir_up_count++;
3055 | valid_path_offset += 3;
3056 | }
3057 | else if (relative_path[i] == '.'
3058 | && (relative_path[i+1] == '\\' || relative_path[i+1] == '/'))
3059 | {
3060 | valid_path_offset = 2;
3061 | break;
3062 | }
3063 | else {
3064 | break;
3065 | }
3066 | }
3067 |
3068 | /* 4. 根据 3 获得的级数,缩减绝对路径的目录层级 */
3069 | if (dir_up_count > 0)
3070 | {
3071 | if ((hierarchy_count - dir_up_count) > 0)
3072 | {
3073 | hierarchy_count -= (dir_up_count - 1);
3074 | size_t offset = dir_hierarchy[hierarchy_count - 1];
3075 | out_path[offset] = '\0';
3076 | }
3077 | else {
3078 | return -2;
3079 | }
3080 | }
3081 |
3082 | /* 5. 根据 2 记录的偏移值和 3 获得的级数,将 absolute_path 和 relative_path 拼接 */
3083 | strncat_s(out_path, out_path_size, "\\", 1);
3084 | strncat_s(out_path, out_path_size, &relative_path[valid_path_offset], strnlen_s(&relative_path[valid_path_offset], MAX_PATH));
3085 |
3086 | for (size_t i = 0; i < strnlen_s(out_path, out_path_size); i++)
3087 | {
3088 | if (out_path[i] == '/') {
3089 | out_path[i] = '\\';
3090 | }
3091 | }
3092 |
3093 | return 0;
3094 | }
3095 |
3096 |
3097 | /**
3098 | * @brief 创建新的文件信息并添加进链表
3099 | * @note
3100 | * @param path_head: 文件路径链表头
3101 | * @param name: 文件名
3102 | * @param path: 文件所在路径
3103 | * @param file_type: 文件类型
3104 | * @retval true: 成功 | false: 失败
3105 | */
3106 | bool file_path_add(struct file_path_list **path_head,
3107 | const char *name,
3108 | const char *path,
3109 | OBJECT_FILE_TYPE file_type)
3110 | {
3111 | bool is_rename = false;
3112 | char str[MAX_PRJ_NAME_SIZE] = {0};
3113 | char old_name[MAX_PRJ_NAME_SIZE] = {0};
3114 | struct file_path_list **path_list = path_head;
3115 |
3116 | memcpy_s(old_name, sizeof(old_name), name, strnlen_s(name, sizeof(old_name)));
3117 |
3118 | /* 可编译的文件和 lib 文件均会被编译为 .o 文件,此处提前进行文件扩展名的替换,便于后续的字符比对和查找 */
3119 | if (file_type == OBJECT_FILE_TYPE_USER || file_type == OBJECT_FILE_TYPE_LIBRARY)
3120 | {
3121 | char *dot = strrchr(name, '.');
3122 | if (dot) {
3123 | *dot = '\0';
3124 | }
3125 | strncpy_s(str, sizeof(str), name, strnlen_s(name, sizeof(str)));
3126 | strncat_s(str, sizeof(str), ".o", strlen(".o"));
3127 | }
3128 | else {
3129 | strncpy_s(str, sizeof(str), name, strnlen_s(name, sizeof(str)));
3130 | }
3131 |
3132 | if (*path_head)
3133 | {
3134 | struct file_path_list *list = *path_head;
3135 | struct file_path_list *last_list = list;
3136 |
3137 | /* 文件名相同的可编译文件会被 keil 改名,此处提前处理,便于后续的字符比对和查找 */
3138 | do {
3139 | if (is_rename == false
3140 | && (file_type == OBJECT_FILE_TYPE_USER || file_type == OBJECT_FILE_TYPE_LIBRARY)
3141 | && strcmp(str, list->object_name) == 0)
3142 | {
3143 | is_rename = true;
3144 | }
3145 | last_list = list;
3146 | list = list->next;
3147 | } while (list);
3148 |
3149 | path_list = &last_list->next;
3150 | }
3151 |
3152 | *path_list = (struct file_path_list *)malloc(sizeof(struct file_path_list));
3153 |
3154 | (*path_list)->old_name = strdup(old_name);
3155 | (*path_list)->object_name = strdup(str);
3156 | (*path_list)->new_object_name = strdup(str);
3157 | (*path_list)->path = strdup(path);
3158 | (*path_list)->file_type = file_type;
3159 | (*path_list)->is_rename = is_rename;
3160 | (*path_list)->next = NULL;
3161 |
3162 | return true;
3163 | }
3164 |
3165 |
3166 | /**
3167 | * @brief 释放文件信息链表占用的内存
3168 | * @note
3169 | * @param path_head: 链表头
3170 | * @retval None
3171 | */
3172 | void file_path_free(struct file_path_list **path_head)
3173 | {
3174 | struct file_path_list *list = *path_head;
3175 | while (list != NULL)
3176 | {
3177 | struct file_path_list *temp = list;
3178 | list = list->next;
3179 | free(temp->old_name);
3180 | free(temp->object_name);
3181 | free(temp->new_object_name);
3182 | free(temp->path);
3183 | free(temp);
3184 | }
3185 | *path_head = NULL;
3186 | }
3187 |
3188 |
3189 | /**
3190 | * @brief 创建新的 memory 并添加进链表
3191 | * @note
3192 | * @param memory_head: memory 链表头
3193 | * @param name: memory 名称
3194 | * @param id: memory ID
3195 | * @param base_addr: memory 基地址
3196 | * @param size: memory 大小,单位 byte
3197 | * @param mem_type: memory 类型
3198 | * @param is_offchip: 是否为片外 memory
3199 | * @param is_from_pack: 信息是否来自 keil 的 pack
3200 | * @retval true: 成功 | false: 失败
3201 | */
3202 | bool memory_info_add(struct memory_info **memory_head,
3203 | const char *name,
3204 | size_t id,
3205 | uint32_t base_addr,
3206 | uint32_t size,
3207 | MEMORY_TYPE mem_type,
3208 | bool is_offchip,
3209 | bool is_from_pack)
3210 | {
3211 | struct memory_info **memory = memory_head;
3212 |
3213 | if (*memory_head)
3214 | {
3215 | struct memory_info *memory_temp = *memory_head;
3216 |
3217 | while (memory_temp->next) {
3218 | memory_temp = memory_temp->next;
3219 | }
3220 | memory = &memory_temp->next;
3221 | }
3222 |
3223 | *memory = (struct memory_info *)malloc(sizeof(struct memory_info));
3224 | if (name) {
3225 | (*memory)->name = strdup(name);
3226 | } else {
3227 | (*memory)->name = NULL;
3228 | }
3229 | (*memory)->id = id;
3230 | (*memory)->base_addr = base_addr;
3231 | (*memory)->size = size;
3232 | (*memory)->type = mem_type;
3233 | (*memory)->is_offchip = is_offchip;
3234 | (*memory)->is_from_pack = is_from_pack;
3235 | (*memory)->is_used = false;
3236 | (*memory)->next = NULL;
3237 |
3238 | return true;
3239 | }
3240 |
3241 |
3242 | /**
3243 | * @brief 释放 memory 链表占用的内存
3244 | * @note
3245 | * @param memory_head: memory 链表头
3246 | * @retval None
3247 | */
3248 | void memory_info_free(struct memory_info **memory_head)
3249 | {
3250 | struct memory_info *memory = *memory_head;
3251 | while (memory != NULL)
3252 | {
3253 | struct memory_info *temp = memory;
3254 | memory = memory->next;
3255 | if (temp->name) {
3256 | free(temp->name);
3257 | }
3258 | free(temp);
3259 | }
3260 | *memory_head = NULL;
3261 | }
3262 |
3263 |
3264 | /**
3265 | * @brief 创建新的 load region
3266 | * @note
3267 | * @param region_head: region 链表头
3268 | * @param name: region 名称
3269 | * @retval NULL | struct load_region *
3270 | */
3271 | struct load_region * load_region_create(struct load_region **region_head, const char *name)
3272 | {
3273 | struct load_region **region = region_head;
3274 |
3275 | if (*region_head)
3276 | {
3277 | struct load_region *region_temp = *region_head;
3278 |
3279 | while (region_temp->next) {
3280 | region_temp = region_temp->next;
3281 | }
3282 | region = ®ion_temp->next;
3283 | }
3284 |
3285 | *region = (struct load_region *)malloc(sizeof(struct load_region));
3286 |
3287 | (*region)->name = strdup(name);
3288 | if ((*region)->name == NULL) {
3289 | return NULL;
3290 | }
3291 | (*region)->exec_region = NULL;
3292 | (*region)->next = NULL;
3293 |
3294 | return (*region);
3295 | }
3296 |
3297 |
3298 | /**
3299 | * @brief 创建新的 execution region 并添加进 load region 链表
3300 | * @note
3301 | * @param l_region: load region 链表头
3302 | * @param name: execution region 名
3303 | * @param load_region_name: 所属的 load region 名
3304 | * @param memory_id: 所在的内存 ID
3305 | * @param base_addr: execution region 基地址
3306 | * @param size: execution region 大小,单位 byte
3307 | * @param used_size: execution region 已使用大小,单位 byte
3308 | * @param mem_type: execution region 内存类型
3309 | * @param is_offchip: 是否为片外 memory
3310 | * @retval NULL | struct exec_region *
3311 | */
3312 | struct exec_region * load_region_add_exec_region(struct load_region **l_region,
3313 | const char *name,
3314 | const char *load_region_name,
3315 | size_t memory_id,
3316 | uint32_t base_addr,
3317 | uint32_t size,
3318 | uint32_t used_size,
3319 | MEMORY_TYPE mem_type,
3320 | bool is_offchip)
3321 | {
3322 | if (*l_region == NULL) {
3323 | return NULL;
3324 | }
3325 |
3326 | struct exec_region **e_region = &((*l_region)->exec_region);
3327 |
3328 | if ((*l_region)->exec_region)
3329 | {
3330 | struct exec_region *region_temp = (*l_region)->exec_region;
3331 |
3332 | while (region_temp->next) {
3333 | region_temp = region_temp->next;
3334 | }
3335 |
3336 | e_region = ®ion_temp->next;
3337 | }
3338 |
3339 | *e_region = (struct exec_region *)malloc(sizeof(struct exec_region));
3340 |
3341 | if (load_region_name) {
3342 | (*e_region)->load_region_name = strdup(load_region_name);
3343 | } else {
3344 | (*e_region)->load_region_name = strdup(UNUSED_LOAD_REGION_NAME);
3345 | }
3346 | (*e_region)->name = strdup(name);
3347 | (*e_region)->memory_id = memory_id;
3348 | (*e_region)->base_addr = base_addr;
3349 | (*e_region)->size = size;
3350 | (*e_region)->used_size = used_size;
3351 | (*e_region)->memory_type = mem_type;
3352 | (*e_region)->is_offchip = is_offchip;
3353 | (*e_region)->is_printed = false;
3354 | (*e_region)->zi_block = NULL;
3355 | (*e_region)->old_exec_region = NULL;
3356 | (*e_region)->next = NULL;
3357 |
3358 | return (*e_region);
3359 | }
3360 |
3361 |
3362 | /**
3363 | * @brief 释放 load region 链表占用的内存
3364 | * @note
3365 | * @param region_head: 链表头
3366 | * @retval None
3367 | */
3368 | void load_region_free(struct load_region **region_head)
3369 | {
3370 | struct load_region *l_region = *region_head;
3371 | while (l_region != NULL)
3372 | {
3373 | struct load_region *l_region_temp = l_region;
3374 |
3375 | struct exec_region *e_region = l_region_temp->exec_region;
3376 | while (e_region != NULL)
3377 | {
3378 | struct exec_region *e_region_temp = e_region;
3379 |
3380 | e_region = e_region->next;
3381 | free(e_region_temp->name);
3382 | free(e_region_temp->load_region_name);
3383 |
3384 | struct region_block *block = e_region_temp->zi_block;
3385 | while (block != NULL)
3386 | {
3387 | struct region_block *block_temp = block;
3388 |
3389 | block = block->next;
3390 | free(block_temp);
3391 | }
3392 |
3393 | free(e_region_temp);
3394 | }
3395 |
3396 | l_region = l_region->next;
3397 | free(l_region_temp->name);
3398 | free(l_region_temp);
3399 | }
3400 | *region_head = NULL;
3401 | }
3402 |
3403 |
3404 | /**
3405 | * @brief 创建新的 object 文件信息并添加进链表
3406 | * @note
3407 | * @param object_head: 链表头
3408 | * @param name: object 文件名
3409 | * @param code: code 大小,单位 byte
3410 | * @param ro_data: read-only data 大小,单位 byte
3411 | * @param rw_data: read-write data 大小,单位 byte
3412 | * @param zi_data: zero-initialize data 大小,单位 byte
3413 | * @retval true: 成功 | false: 失败
3414 | */
3415 | bool object_info_add(struct object_info **object_head,
3416 | const char *name,
3417 | uint32_t code,
3418 | uint32_t ro_data,
3419 | uint32_t rw_data,
3420 | uint32_t zi_data)
3421 | {
3422 | struct object_info **object = object_head;
3423 |
3424 | if (*object_head)
3425 | {
3426 | struct object_info *object_temp = *object_head;
3427 |
3428 | while (object_temp->next) {
3429 | object_temp = object_temp->next;
3430 | }
3431 | object = &object_temp->next;
3432 | }
3433 |
3434 | *object = (struct object_info *)malloc(sizeof(struct object_info));
3435 | (*object)->name = strdup(name);
3436 | if ((*object)->name == NULL) {
3437 | return false;
3438 | }
3439 | (*object)->code = code;
3440 | (*object)->ro_data = ro_data;
3441 | (*object)->rw_data = rw_data;
3442 | (*object)->zi_data = zi_data;
3443 | (*object)->path = NULL;
3444 | (*object)->old_object = NULL;
3445 | (*object)->next = NULL;
3446 |
3447 | return true;
3448 | }
3449 |
3450 |
3451 | /**
3452 | * @brief 释放 object 文件信息占用的内存
3453 | * @note
3454 | * @param object_head: 链表头
3455 | * @retval None
3456 | */
3457 | void object_info_free(struct object_info **object_head)
3458 | {
3459 | struct object_info *object = *object_head;
3460 | while (object != NULL)
3461 | {
3462 | struct object_info *temp = object;
3463 | object = object->next;
3464 | free(temp->name);
3465 | free(temp);
3466 | }
3467 | *object_head = NULL;
3468 | }
3469 |
3470 |
3471 | /**
3472 | * @brief 初始化动态列表
3473 | * @note
3474 | * @param capacity: 列表容量
3475 | * @retval NULL | struct prj_path_list *
3476 | */
3477 | struct prj_path_list * prj_path_list_init(size_t capacity)
3478 | {
3479 | struct prj_path_list *list = malloc(sizeof(struct prj_path_list));
3480 |
3481 | list->items = malloc(capacity * sizeof(char *));
3482 | list->capacity = capacity;
3483 | list->size = 0;
3484 |
3485 | return list;
3486 | }
3487 |
3488 |
3489 | /**
3490 | * @brief 向动态列表添加元素
3491 | * @note
3492 | * @param list: 列表对象
3493 | * @param item: 要新增的项
3494 | * @retval None
3495 | */
3496 | void prj_path_list_add(struct prj_path_list *list, char *item)
3497 | {
3498 | /* 如果数组已满,将其最大容量翻倍 */
3499 | if (list->size == list->capacity)
3500 | {
3501 | list->capacity *= 2;
3502 | list->items = realloc(list->items, list->capacity * sizeof(char *));
3503 | }
3504 | list->items[list->size++] = item;
3505 | }
3506 |
3507 |
3508 | /**
3509 | * @brief 释放动态列表
3510 | * @note
3511 | * @param list: 列表对象
3512 | * @retval None
3513 | */
3514 | void prj_path_list_free(struct prj_path_list *list)
3515 | {
3516 | for (size_t i = 0; i < list->size; i++) {
3517 | free(list->items[i]);
3518 | }
3519 | free(list->items);
3520 | free(list);
3521 | }
3522 |
3523 |
3524 | /**
3525 | * @brief 某路径是否为 keil 工程
3526 | * @note
3527 | * @param path: 路径
3528 | * @retval true: 是 | false: 否
3529 | */
3530 | bool is_keil_project(const char *path)
3531 | {
3532 | for (size_t i = 0; i < sizeof(_keil_prj_extension) / sizeof(_keil_prj_extension[0]); i++)
3533 | {
3534 | char *dot = strrchr(path, '.');
3535 | if (dot == NULL) {
3536 | return false;
3537 | }
3538 |
3539 | if (strncmp(dot, _keil_prj_extension[i], strlen(_keil_prj_extension[i])) == 0) {
3540 | return true;
3541 | }
3542 | }
3543 |
3544 | return false;
3545 | }
3546 |
3547 |
3548 | /**
3549 | * @brief 字符串比对
3550 | * @note
3551 | * @param str1: 字符串 1
3552 | * @param str2[]: 字符串组 2
3553 | * @param str2_qty: 字符串组 2 的数量
3554 | * @retval true: 一致 | false: 不一致
3555 | */
3556 | bool is_same_string(const char *str1,
3557 | const char *str2[],
3558 | size_t str2_qty)
3559 | {
3560 | for (size_t i = 0; i < str2_qty; i++)
3561 | {
3562 | if (strcmp(str1, str2[i]) == 0) {
3563 | return true;
3564 | }
3565 | }
3566 |
3567 | return false;
3568 | }
3569 |
--------------------------------------------------------------------------------
/keil-build-viewer.h:
--------------------------------------------------------------------------------
1 | #ifndef __KEIL_BUILD_VIEWER_H__
2 | #define __KEIL_BUILD_VIEWER_H__
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #define APP_NAME "keil-build-viewer"
12 | #define APP_VERSION "v1.6"
13 |
14 | #define MAX_DIR_HIERARCHY 32 /* 最大目录层级 */
15 | #define MAX_PATH_QTY 32 /* 最大目录数量 */
16 | #define MAX_FILE_QTY 512 /* 最大文件数量 */
17 | #define MAX_PRJ_NAME_SIZE 128 /* 最大工程名称长度 */
18 | #define OBJECT_INFO_STR_QTY 7 /* Code + (inc. data) + RO Data + RW Data + ZI Data + Debug + Object Name */
19 |
20 | #define ENABLE_REFER_TO_KEIL_DIALOG 0 /* 当 chip 没有对应的 keil pack 且使用自定义的 scatter file 时,是否参考 keil 的 memory dialog */
21 |
22 | #define UNKNOWN_MEMORY_ID 1
23 | #define ZI_SYMBOL_0 0x4F /* O */
24 | #define USED_SYMBOL_0 0x23 /* # */
25 | #define ZI_SYMBOL_1 0x4F /* O */
26 | #define USED_SYMBOL_1 0x58 /* X */
27 | #define ZI_SYMBOL_GBK_H 0xA1 /* □ */
28 | #define ZI_SYMBOL_GBK_L 0xF5
29 | #define USED_SYMBOL_GBK_H 0xA1 /* ■ */
30 | #define USED_SYMBOL_GBK_L 0xF6
31 | #define ZI_SYMBOL_BIG5_H 0xA1 /* □ */
32 | #define ZI_SYMBOL_BIG5_L 0xBC
33 | #define USED_SYMBOL_BIG5_H 0xA1 /* ■ */
34 | #define USED_SYMBOL_BIG5_L 0xBD
35 | #define UNUSE_SYMBOL "_"
36 |
37 | #define UNUSED_LOAD_REGION_NAME "Unused Load Region"
38 |
39 | #define STR_ZERO_INIT " Zero "
40 | #define STR_PADDING " PAD"
41 | #define STR_RENAME_MARK " - object file renamed from "
42 | #define STR_COMPILING "compiling "
43 | #define STR_MAX_STACK_USAGE "Maximum Stack Usage "
44 | #define STR_FILE "FILE(s)"
45 | #define STR_LTO_LLVW "lto-llvm-"
46 | #define STR_MEMORY_MAP_OF_THE_IMAGE "Memory Map of the image"
47 | #define STR_LOAD_REGION "Load Region"
48 | #define STR_EXECUTION_REGION "Execution Region"
49 | #define STR_LOAD_BASE "Load base: "
50 | #define STR_REGION_USED_SIZE "Size: "
51 | #define STR_REGION_MAX_SIZE "Max: "
52 | #define STR_EXECUTE_BASE "Base: "
53 | #define STR_EXECUTE_BASE_ADDR "Exec base: "
54 | #define STR_IMAGE_COMPONENT_SIZE "Image component sizes"
55 | #define STR_OBJECT_NAME "Object Name"
56 | #define STR_LIBRARY_MEMBER_NAME "Library Member Name"
57 | #define STR_LIBRARY_NAME "Library Name"
58 | #define STR_OBJECT_TOTALS "Object Totals"
59 | #define STR_LIBRARY_TOTALS "Library Totals"
60 | #define LABEL_TARGET_NAME ""
61 | #define LABEL_IS_CURRENT_TARGET ""
62 | #define LABEL_DEVICE ""
63 | #define LABEL_VENDOR ""
64 | #define LABEL_CPU ""
65 | #define LABEL_OUTPUT_DIRECTORY ""
66 | #define LABEL_OUTPUT_NAME ""
67 | #define LABEL_LISTING_PATH ""
68 | #define LABEL_IS_CREATE_MAP ""
69 | #define LABEL_AC6_LTO ""
70 | #define LABEL_IS_KEIL_SCATTER ""
71 | #define LABEL_END_GROUPS ""
72 | #define LABEL_END_FILE ""
73 | #define LABEL_END_FILES ""
74 | #define LABEL_END_CADS ""
75 | #define LABEL_END_LDADS ""
76 | #define LABEL_GROUP_NAME ""
77 | #define LABEL_FILE_NAME ""
78 | #define LABEL_FILE_TYPE ""
79 | #define LABEL_FILE_PATH ""
80 | #define LABEL_INCLUDE_IN_BUILD ""
81 | #define LABEL_ONCHIP_MEMORY ""
82 | #define LABEL_END_ONCHIP_MEMORY ""
83 | #define LABEL_MEMORY_AREA ""
86 | #define LABEL_MEMORY_ADDRESS ""
87 | #define LABEL_MEMORY_SIZE ""
88 |
89 | #define log_save(log, fmt, ...) log_write(log, false, fmt, ##__VA_ARGS__)
90 | #define log_print(log, fmt, ...) log_write(log, true, fmt, ##__VA_ARGS__)
91 |
92 |
93 | typedef enum
94 | {
95 | ENCODING_TYPE_GBK = 0x00,
96 | ENCODING_TYPE_BIG5,
97 | ENCODING_TYPE_OTHER,
98 |
99 | } ENCODING_TYPE;
100 |
101 | typedef enum
102 | {
103 | PROGRESS_STYLE_0 = 0x00,
104 | PROGRESS_STYLE_1,
105 | PROGRESS_STYLE_2,
106 |
107 | } PROGRESS_STYLE;
108 |
109 | typedef enum
110 | {
111 | MEMORY_PRINT_MODE_0 = 0x00, /* keil pack 有 RAM 和 ROM 信息(多数情况) */
112 | MEMORY_PRINT_MODE_1, /* keil pack 没有 RAM 和 ROM 信息,但 memory 使用了 keil dialog 配置 */
113 | MEMORY_PRINT_MODE_2, /* keil pack 没有 RAM 和 ROM 信息,并且使用自定义的 scatter file */
114 |
115 | } MEMORY_PRINT_MODE;
116 |
117 | typedef enum
118 | {
119 | MEMORY_TYPE_NONE = 0x00,
120 | MEMORY_TYPE_RAM,
121 | MEMORY_TYPE_FLASH,
122 | MEMORY_TYPE_UNKNOWN,
123 |
124 | } MEMORY_TYPE;
125 |
126 | typedef enum
127 | {
128 | OBJECT_FILE_TYPE_UNKNOWN = 0x00,
129 | OBJECT_FILE_TYPE_USER,
130 | OBJECT_FILE_TYPE_OBJECT,
131 | OBJECT_FILE_TYPE_LIBRARY,
132 |
133 | } OBJECT_FILE_TYPE;
134 |
135 |
136 | /* keil 工程路径存储链表 */
137 | struct prj_path_list
138 | {
139 | char **items;
140 | size_t capacity;
141 | size_t size;
142 | };
143 |
144 | struct object_info
145 | {
146 | char *name;
147 | char *path;
148 | uint32_t code;
149 | uint32_t ro_data;
150 | uint32_t rw_data;
151 | uint32_t zi_data;
152 | struct object_info *old_object;
153 | struct object_info *next;
154 | };
155 |
156 | struct region_block
157 | {
158 | uint32_t start_addr;
159 | uint32_t size;
160 | struct region_block *next;
161 | };
162 |
163 | struct exec_region
164 | {
165 | char *name;
166 | char *load_region_name;
167 | size_t memory_id; /* 从 1 开始, 1 固定为 unknown */
168 | uint32_t base_addr;
169 | uint32_t size;
170 | uint32_t used_size;
171 | MEMORY_TYPE memory_type;
172 | bool is_offchip;
173 | bool is_printed;
174 |
175 | struct region_block *zi_block;
176 | struct exec_region *old_exec_region;
177 | struct exec_region *next;
178 | };
179 |
180 | struct load_region
181 | {
182 | char *name;
183 | struct exec_region *exec_region;
184 | struct load_region *next;
185 | };
186 |
187 | struct memory_info
188 | {
189 | char *name;
190 | size_t id;
191 | size_t mem_id;
192 | uint32_t base_addr;
193 | uint32_t size;
194 | MEMORY_TYPE type;
195 | bool is_from_pack;
196 | bool is_offchip;
197 | bool is_used;
198 | struct memory_info *next;
199 | };
200 |
201 | struct file_path_list
202 | {
203 | char *old_name; /* 原名 */
204 | char *object_name; /* 更改为 .o 后缀名的名称 */
205 | char *new_object_name; /* 因重名而改名后的名称,为 .o 后缀 */
206 | char *path;
207 | bool is_rename;
208 | OBJECT_FILE_TYPE file_type;
209 | struct file_path_list *next;
210 | };
211 |
212 | struct command_list
213 | {
214 | const char *cmd;
215 | const char *desc;
216 | };
217 |
218 | struct uvprojx_info
219 | {
220 | bool is_has_pack;
221 | bool is_enable_lto;
222 | bool is_has_user_lib;
223 | bool is_custom_scatter;
224 | char chip[MAX_PRJ_NAME_SIZE];
225 | char target_name[MAX_PRJ_NAME_SIZE];
226 | char output_name[MAX_PRJ_NAME_SIZE];
227 | char output_path[MAX_PATH];
228 | char listing_path[MAX_PATH];
229 | };
230 |
231 |
232 | bool is_keil_project (const char *path);
233 | bool is_same_string (const char *str1,
234 | const char *str2[],
235 | size_t str2_qty);
236 | int combine_path (char *out_path,
237 | size_t out_path_size,
238 | const char *absolute_path,
239 | const char *relative_path);
240 | bool file_path_add (struct file_path_list **path_head,
241 | const char *name,
242 | const char *path,
243 | OBJECT_FILE_TYPE file_type);
244 | void file_path_free (struct file_path_list **path_head);
245 | bool memory_info_add (struct memory_info **memory_head,
246 | const char *name,
247 | size_t id,
248 | uint32_t base_addr,
249 | uint32_t size,
250 | MEMORY_TYPE mem_type,
251 | bool is_offchip,
252 | bool is_from_pack);
253 | void memory_info_free (struct memory_info **memory_head);
254 | bool object_info_add (struct object_info **object_head,
255 | const char *name,
256 | uint32_t code,
257 | uint32_t ro_data,
258 | uint32_t rw_data,
259 | uint32_t zi_data);
260 | void object_info_free (struct object_info **object_head);
261 | struct load_region * load_region_create (struct load_region **region_head, const char *name);
262 | struct exec_region * load_region_add_exec_region (struct load_region **region_head,
263 | const char *name,
264 | const char *load_region_name,
265 | size_t memory_id,
266 | uint32_t base_addr,
267 | uint32_t size,
268 | uint32_t used_size,
269 | MEMORY_TYPE mem_type,
270 | bool is_offchip);
271 | void load_region_free (struct load_region **region_head);
272 | void search_files_by_extension (const char *dir,
273 | size_t dir_len,
274 | const char *extension[],
275 | size_t extension_qty,
276 | struct prj_path_list *list);
277 | struct prj_path_list * prj_path_list_init (size_t capacity);
278 | void prj_path_list_add (struct prj_path_list *list, char *path);
279 | void prj_path_list_free (struct prj_path_list *list);
280 | int parameter_process (int param_qty,
281 | char *param[],
282 | char *prj_name,
283 | size_t name_size,
284 | char *prj_path,
285 | size_t path_size,
286 | int *err_param);
287 | bool uvoptx_file_process (const char *file_path,
288 | char *target_name,
289 | size_t max_size);
290 | int uvprojx_file_process (const char *file_path,
291 | const char *target_name,
292 | struct uvprojx_info *out_info,
293 | bool is_get_target_name);
294 | bool memory_area_process (const char *str, bool is_new);
295 | bool file_path_process (const char *str, bool *is_has_user_lib);
296 | void build_log_file_process (const char *file_path);
297 | void file_rename_process (void);
298 | int map_file_process (const char *file_path,
299 | struct load_region **region_head,
300 | struct object_info **object_head,
301 | bool is_get_user_lib,
302 | bool is_match_memory);
303 | int region_info_process (FILE *p_file,
304 | long read_start_pos,
305 | struct load_region **region_head,
306 | bool is_match_memory);
307 | void region_zi_process (struct exec_region **e_region,
308 | char *text,
309 | size_t size_pos);
310 | int object_info_process (struct object_info **object_head,
311 | FILE *p_file,
312 | long *end_pos,
313 | bool is_get_user_lib,
314 | uint8_t parse_mode);
315 | int record_file_process (const char *file_path,
316 | struct load_region **region_head,
317 | struct object_info **object_head,
318 | bool *is_has_object,
319 | bool *is_has_region,
320 | bool is_match_memory);
321 | void object_print_process (struct object_info *object_head,
322 | size_t max_path_len,
323 | bool is_has_record);
324 | void memory_numbering (MEMORY_TYPE mem_type);
325 | void memory_print_unused (MEMORY_TYPE mem_type, size_t max_region_name);
326 | void memory_mode0_print (struct exec_region *e_region,
327 | const char *load_region_name,
328 | MEMORY_TYPE mem_type,
329 | size_t max_region_name,
330 | bool is_has_record,
331 | bool is_print_null);
332 | void memory_mode1_print (struct exec_region *e_region,
333 | MEMORY_TYPE mem_type,
334 | bool is_offchip,
335 | size_t max_region_name,
336 | bool is_has_record);
337 | void memory_mode2_print (struct exec_region *e_region,
338 | size_t max_region_name,
339 | bool is_has_record);
340 | void progress_print (struct exec_region *region,
341 | size_t max_region_name,
342 | bool is_has_record);
343 | void stack_print_process (const char *file_path);
344 | void log_write (FILE *p_log,
345 | bool is_print,
346 | const char *fmt,
347 | ...);
348 |
349 |
350 | #endif
351 |
--------------------------------------------------------------------------------