53、软件追踪系统中的 QS 与 QSPY 应用详解

软件追踪系统中的 QS 与 QSPY 应用详解

在软件开发过程中,软件追踪系统对于调试和性能分析至关重要。本文将详细介绍 QS(Quantum Spy)软件追踪系统的相关接口、字典记录、应用特定记录、移植配置,以及 QSPY 主机应用的相关内容。

1. 面向块的接口:QS_getBlock()

QS 提供了一种面向块的接口 QS_getBlock() ,用于一次性获取连续的数据块,这对于 DMA 类型的传输非常有用。

  • 函数行为

    • 若调用时存在可用字节,函数返回 QS 跟踪缓冲区中数据块起始位置的指针,并将连续字节数写入 pNbytes 所指向的位置。 *pNbytes 的值也作为输入,限制调用者可接受的数据块最大大小,且字节不会从跟踪缓冲区复制。
    • 若调用时 QS 缓冲区中无可用字节,函数返回 NULL 指针,并将 pNbytes 指向的值设为 0。
    • QS_getBlock() 返回的字节数少于 *pNbytes 的初始值时,不能假定 QS 跟踪缓冲区为空,可能数据块接近跟踪缓冲区末尾,需再次调用 QS_getBlock() 获取剩余数据。
    • 调用者在 QS_getBlock() 返回内存块后,必须传输完返回块中的所有字节,才能再次调用该函数。
  • 注意事项 QS_getBlock() 函数内部不锁定中断,且不可重入,应在锁定中断的情况下调用。

以下是使用 QS_getBlock() 将数据输出到 16550 兼容 UART 的示例代码:

void QF_onIdle(void) {
    /* called with interrupts LOCKED */
    QF_INT_UNLOCK(dummy);
    /* always unlock interrupts */
    #ifdef Q_SPY
    if ((inportb(l_uart_base + 5) & (1 << 5)) != 0) {
        /* THR Empty? */
        uint16_t fifo = UART_16550_TXFIFO_DEPTH;
        /* 16550 Tx FIFO depth */
        uint8_t const *block;
        QF_INT_LOCK(dummy);
        block = QS_getBlock(&fifo);
        /* try to get next block to transmit */
        QF_INT_UNLOCK(dummy);
        while (fifo-- != 0) {
            /* any bytes in the block? */
            outportb(l_uart_base + 0, *block++);
        }
    }
    #endif
}

操作步骤如下:
1. 空闲处理是实现跟踪数据输出的理想方式,使用 QF_onIdle() 空闲回调。
2. QF_onIdle() 回调在锁定中断的情况下调用,需先解锁中断。
3. 仅当 Q_SPY 宏定义时,才执行 QS 跟踪缓冲区输出。
4. 检查 16550 UART 的发送保持寄存器(THR)是否为空。
5. 获取 16550 UART 的 Tx FIFO 深度。
6. 定义临时指针 block 用于保存 QS_getBlock() 的返回值。
7. 锁定中断后调用 QS_getBlock() 获取要传输的连续跟踪数据块。
8. 解锁中断。
9. 当 Tx FIFO 有空间时,将跟踪字节写入发送保持寄存器。

2. 字典跟踪记录

在编译和加载应用程序映像到目标设备时,对象名、函数名和信号名等符号信息会从代码中剥离。为了让 QSPY 主机组件获取这些符号信息,QS 提供了特殊的跟踪记录。

  • 作用 :字典跟踪记录并非生成跟踪所必需,但符号信息的存在能显著提高软件跟踪或调试器的使用效率。
  • 类型 :QS 支持三种类型的字典跟踪记录,分别是对象字典、函数字典和信号字典。

    字典类型 生成宏 作用
    对象字典 QS_OBJ_DICTIONARY() 将对象在内存中的地址与其符号名关联
    函数字典 QS_FUN_DICTIONARY() 将函数在内存中的地址与其符号名关联
    信号字典 QS_SIG_DICTIONARY() 将事件信号的数值和状态机对象与信号的符号名关联
  • 对象字典

    • 使用 QS_OBJ_DICTIONARY() 宏生成,该宏仅接受一个参数,即对象的地址,并使用“字符串化”预处理器运算符将参数转换为 C 字符串。应使用有意义的持久对象地址调用该宏,如 &l_table &l_philo[3] ,而非通用指针。
    • QS 通过内存池管理的内存缓冲区识别内存池,通过事件队列管理的环形缓冲区识别事件队列。
    • 对象字典记录与 QS 本地过滤器密切相关,二者使用相同约定。例如,可通过 QS_FILTER_MP_OBJ() 宏选择特定内存池的本地过滤器,通过 QS_FILTER_EQ_OBJ() 宏选择特定事件队列的本地过滤器。

以下是对象字典所需的预定义 QS 记录示例:
| 对象类型 | 示例 | QS 记录 |
| ---- | ---- | ---- |
| 状态机 | QS_OBJ_DICTIONARY(&l_table) | QS_QEP_STATE_EMPTY , QS_QEP_STATE_ENTRY , QS_QEP_STATE_EXIT , QS_QEP_STATE_INIT , QS_QEP_INIT_TRAN , QS_QEP_INTERN_TRAN , QS_QEP_TRAN , QS_QEP_IGNORED |
| 活动对象 | QS_OBJ_DICTIONARY(&l_philo[0]) | QS_QF_ACTIVE_ADD , QS_QF_ACTIVE_REMOVE , QS_QF_ACTIVE_SUBSCRIBE , QS_QF_ACTIVE_UNSUBSCRIBE , QS_QF_ACTIVE_POST_FIFO , QS_QF_ACTIVE_POST_LIFO , QS_QF_ACTIVE_GET , QS_QF_ACTIVE_GET_LAST |
| 内存池 | QS_OBJ_DICTIONARY(l_smlPoolSto) | QS_QF_MPOOL_INIT , QS_QF_MPOOL_GET , QS_QF_MPOOL_PUT |
| 事件队列 | QS_OBJ_DICTIONARY(l_philQueueSto[0]) | QS_QF_EQUEUE_INIT , QS_QF_EQUEUE_POST_FIFO , QS_QF_EQUEUE_POST_LIFO , QS_QF_EQUEUE_GET , QS_QF_EQUEUE_GET_LAST |
| 时间事件 | QS_OBJ_DICTIONARY(&l_philo[0].timeEvt) | QS_QF_TICK , QS_QF_TIMEEVT_ARM , QS_QF_TIMEEVT_AUTO_DISARM , QS_QF_TIMEEVT_DISARM_ATTEMPT , QS_QF_TIMEEVT_DISARM , QS_QF_TIMEEVT_REARM , QS_QF_TIMEEVT_POST , QS_QF_TIMEEVT_PUBLISH |

  • 函数字典 :使用 QS_FUN_DICTIONARY() 宏生成,该宏仅接受一个参数,即函数的地址,并使用“字符串化”预处理器运算符将参数转换为 C 字符串,主要为状态处理函数提供符号名。

  • 信号字典 :使用 QS_SIG_DICTIONARY() 宏生成,该宏接受两个参数:事件信号的数值和状态机对象的地址。使用信号值和状态机对象是因为仅信号值不足以唯一标识符号信号,全局发布的信号需系统唯一,局部使用的信号在不同状态机中可能有不同含义。

以下是使用 QS_SIG_DICTIONARY() 宏的示例:

// 全局发布信号示例
QS_SIG_DICTIONARY(GLOBAL_SIG_1, NULL);
QS_SIG_DICTIONARY(GLOBAL_SIG_2, NULL);
// 局部信号示例
QS_SIG_DICTIONARY(TIMEOUT_SIG, &Philosopher);
3. 应用特定的 QS 跟踪记录

应用特定的 QS 记录允许从应用程序级代码生成跟踪信息,可将其视为 printf() 的等效替代,但开销更小。

  • 记录结构
    • 多数情况下,应用特定记录使用 QS_BEGIN() / QS_END() 宏对封装,这对宏在记录开始时锁定中断,结束时解锁中断。
    • 若要在已建立的关键部分或 ISR 中生成跟踪数据,可使用 QS_BEGIN_NOLOCK() / QS_END_NOLOCK() 宏避免关键部分嵌套。
    • QS_BEGIN() 宏接受两个参数:第一个参数是枚举记录类型,用于全局开关过滤器,且是每个记录头的一部分,应用特定记录类型必须从 QS_USER 值开始,以避免与 QP 组件中已有的预定义 QS 记录重叠;第二个参数用于本地过滤器,可选择性记录特定应用级对象。若不想使用本地过滤器,可将 NULL 作为第二个参数。

以下是一个应用特定 QS 记录的示例:

QS_BEGIN(MY_QS_RECORD, myObjectPointer)
/* trace record begin */
QS_STR("Hello");
/* string data element */
QS_U8(3, n);
/* uint8_t data, 3-decimal digits format */
...
/* QS data */
QS_MEM(buf, sizeof(buf));
/* memory block of a given size */
QS_END()
/* trace record end */
  • 数据元素 :支持的数据元素包括 8 位、16 位和 32 位的有符号和无符号整数、32 位和 64 位的浮点数、以零结尾的字符串和可变大小的内存块。还提供了特殊宏用于插入平台相关元素,如事件信号、对象指针和函数指针,QS 仅记录给定平台所需的最小字节数。

  • 编码结构 :应用特定跟踪记录与所有 QS 记录一样,以序列号和记录 ID 开头,紧随记录 ID 的是时间戳,时间戳使用的字节数可通过 QS_TIME_SIZE 宏配置。每个数据元素以格式字节开头,包含数据类型信息(低半字节)和显示元素的格式宽度(高半字节)。

以下是应用特定跟踪记录的编码结构示意图:

graph LR
    A[Seq No.] --> B[Rec ID]
    B --> C[Chk sum]
    C --> D[Flag 0x7E]
    D --> E[QS_TIME_SIZE]
    E --> F[Time stamp]
    F --> G[fmt 'H']
    G --> H['e']
    H --> I['l']
    I --> J['l']
    J --> K['o']
    K --> L['\0']
    L --> M[fmt n]
    M --> N[...]
    N --> O[fmt size]
    O --> P[buf[0]]
    P --> Q[buf[1]]
    Q --> R[buf[2]]
    R --> S[...]

操作步骤如下:
1. 使用 QS_BEGIN() 宏开始记录,指定记录类型和可选的本地过滤器对象。
2. 使用各种数据元素宏插入所需数据。
3. 使用 QS_END() 宏结束记录。

4. 移植和配置 QS

使用 QS 时,需将其适配到所选的 CPU、编译器和内核/RTOS,这称为移植。

  • 移植代码组织 :QS 平台特定代码仅包含 qs_port.h 和通常在应用程序的板级支持包( bsp.c )中定义的 QS 回调函数。

以下是 80x86、QK/DOS、大内存模型的 qs_port.h 头文件示例:

#ifndef qs_port_h
#define qs_port_h
#define QS_OBJ_PTR_SIZE 4
#define QS_FUN_PTR_SIZE 4
#define QS_TIME_SIZE 4
#include "qf_port.h"
/* use QS with QF */
#include "qs.h"
/* QS platform-independent public interface */
#endif
/* qs_port_h */
  • 宏定义说明

    • QS_OBJ_PTR_SIZE :指定特定平台上对象指针的大小(字节)。
    • QS_FUN_PTR_SIZE :指定特定平台上函数指针的大小(字节)。
    • QS_TIME_SIZE :配置 QS 时间戳 QSTimeCtr 的大小(字节)。
  • 注意事项

    • 若 QS 与 QF 实时框架一起使用,需包含 qf_port.h 头文件,此时 QS 关键部分与 qf_port.h 中定义的相同。
    • 若 QS 仅与 QEP 组件一起使用或完全独立使用,需通过定义 QS_INT_KEY_TYPE QS_INT_LOCK() QS_INT_UNLOCK() 宏提供自己的独立关键部分机制。
    • qs.h 平台无关头文件必须包含在 qf_port.h 头文件中。

操作步骤如下:
1. 创建 qs_port.h 头文件。
2. 根据平台需求定义 QS_OBJ_PTR_SIZE QS_FUN_PTR_SIZE QS_TIME_SIZE 宏。
3. 若与 QF 一起使用,包含 qf_port.h 头文件。
4. 包含 qs.h 平台无关头文件。

5. QSPY 主机应用

QSPY 是 Quantum Spy 软件追踪系统的主机端组件,是一个简单的控制台应用程序,无复杂 GUI,旨在提供 QS 数据的解析、存储和导出功能,可将数据导出到 MATLAB 等强大工具。

  • 特点

    • 平台中立,使用可移植的 C++ 编写,支持 Linux 和 Windows 等多种平台。
    • 易于适配各种目标 - 主机通信链路,开箱支持串行(RS232)、TCP/IP 和文件通信链路,可通过通用硬件抽象层(HAL)轻松添加其他通信链路。
    • 接受多个命令行参数,可配置数据链路和所有目标依赖项,如指针大小、信号大小等,可处理来自任何嵌入式目标的数据,已在多种 8 位、16 位或 32 位 CPU 上测试。
  • 功能

    • 提供简单的、统一的、人类可读的文本输出到屏幕。
    • 若 QS 跟踪数据包含字典跟踪记录,QSPY 应用符号信息输出对象、信号和状态的标识符;否则,输出各种指针和信号的十六进制值。
    • 可将跟踪数据以矩阵格式导出到 MATLAB,提供了将 QSPY 跟踪数据导入 MATLAB 的特殊脚本。
  • 安装 :QSPY 主机应用程序包含在特定目录中,QP/C 和 QP/C++ 版本除包含的 qs.h 头文件版本不同外,其他相同。

以下是 QSPY 主机应用程序的源代码组织:

<qp>\qpc\
- QP/C root directory (<qp>\qpcpp for QP/C++)
|
+-doxygen\
- QP/C documentation generated with Doxygen
| +-html\
- "QP/C Reference Manual" in HTML format
| | +-index.html
- The starting HTML page for the "QP/C Reference Manual"
| | |
(contains the "QSPY Reference Manual")
| | +- . . .
| +-qpc.chm
- "QP/C Reference Manual" in CHM Help format
|
(contains the "QSPY Reference Manual")
|
+-include/
- QP platform-independent header files
| +-qs.h
- QS platform-independent header file (used by QSPY)
|
+-tools\
- Tools directory
| +-qspy\
- QSPY host application
| | +-include\
- platform-independent include
| | | +-dict.h
- dictionary class header file
| | | +-getopt.h
- command-line option parser
| | | +-hal.h
- Hardware Abstraction Layer header file
| | | +-qspy.h
- QSPY parser header file
| | +-source\
- platform-independent sources (C++)
| | | +-dict.cpp
- dictionary class implementation
| | | +-getopt.c
- command-line option parser
| | | +-main.cpp
- main() entry point
| | | +-qspy.cpp
- QSpy parser
| | |
| | +-linux\
- Linux version of QSPY
| | | +-gnu\
- GNU compiler
| | | | +-dbg\
- debug build directory
| | | | +-rel\
- release build directory
| | | | +-com.cpp
- serial port HAL for Linux
| | | | +-tcp.cpp
- TCP/IP port HAL for Linux
| | | | +-Makefile - make file to build QSPY for Linux
| | |
| | +-win32\
- Win32 (Windows) version of QSPY
| | | +-mingw\
- MinGW compiler (GNU)
| | | | +-dbg\
- debug build directory
| | | | +-rel\
- release build directory
| | | | | +-qspy.exe – QSPY executable
| | | | +-com.cpp
- serial port HAL for Win32
| | | | +-tcp.cpp
- TCP/IP port HAL for Win32
| | | | +-make.bat – Simple batch script to build QSPY
| | |
| | | +-vc2005\
- Visual C++ 2005 toolset
| | | | +-Debug\
- debug build directory
| | | | +-Release\ - release build directory
| | | | | +-qspy.exe – QSPY executable
| | | | +-com.cpp
- serial port HAL for Win32
| | | | +-tcp.cpp
- TCP/IP port HAL for Win32
| | | | +-qspy.sln - Visual C++ Solution to build QSPY for Win32
| | |
| | +-matlab\
- MATLAB scripts
| | | +-qspy.m
- MATLAB script to import the QS data into MATLAB
| | | +-dpp.spy
- Example of a QS binary file from DPP application
| | | +-philo_timing.m - example MATLAB script to generate timing diagrams
| | |
for the DPP example

操作步骤如下:
1. 从指定目录获取 QSPY 主机应用程序。
2. 根据需要选择合适的版本(QP/C 或 QP/C++)。
3. 配置命令行参数,包括数据链路和目标依赖项。
4. 运行 QSPY 应用程序,解析和处理 QS 跟踪数据。
5. 若需要,将数据导出到 MATLAB 进行进一步分析。

综上所述,QS 和 QSPY 为软件开发中的调试和性能分析提供了强大的工具,通过合理使用这些功能,可以提高开发效率和软件质量。

软件追踪系统中的 QS 与 QSPY 应用详解

6. QSPY 主机应用的使用与配置

QSPY 主机应用的使用涉及多个方面,包括安装、命令行参数配置等,以下将详细介绍其操作步骤和注意事项。

  • 安装步骤

    1. 找到 QSPY 主机应用程序所在目录,对于 QP/C 是 <qp>\qpc\tools\qspy\ ,对于 QP/C++ 是 <qp>\qpcpp\tools\qspy\
    2. 确认所需的依赖文件,如 qs.h 头文件,根据使用的是 QP/C 还是 QP/C++ 版本选择对应的头文件。
  • 命令行参数配置 :QSPY 接受多个命令行参数来配置数据链路和目标依赖项,以下是一些常见参数的说明:
    | 参数类型 | 说明 | 示例 |
    | ---- | ---- | ---- |
    | 数据链路参数 | 用于指定与目标设备的通信方式,如串口、TCP/IP 或文件 | --serial /dev/ttyS0 --tcp 192.168.1.100:1234 |
    | 目标依赖参数 | 配置指针大小、信号大小等目标相关信息 | --ptr-size 4 --sig-size 2 |

以下是一个使用命令行启动 QSPY 的示例:

qspy --serial /dev/ttyS0 --ptr-size 4 --sig-size 2
  • 数据处理与输出
    1. 运行 QSPY 应用程序后,它会开始解析从目标设备接收到的 QS 跟踪数据。
    2. 若跟踪数据包含字典跟踪记录,QSPY 会应用符号信息输出对象、信号和状态的标识符;否则,输出十六进制值。
    3. QSPY 提供简单的、统一的、人类可读的文本输出到屏幕,方便开发者查看。
    4. 若需要将数据导出到 MATLAB 进行进一步分析,可使用提供的 qspy.m 脚本。操作步骤如下:
      1. 确保 MATLAB 环境已正确配置。
      2. 在 MATLAB 中运行 qspy.m 脚本,并指定 QSPY 输出的数据文件。
      3. 数据将以矩阵格式导入 MATLAB,可使用 MATLAB 的强大功能进行数据处理和可视化。
7. QS 与 QSPY 的应用场景与优势

QS 和 QSPY 在软件开发中具有广泛的应用场景,以下是一些常见的应用场景及它们带来的优势。

  • 调试与故障排查

    • 在开发过程中,当软件出现故障时,QS 可以记录详细的运行信息,如对象状态、函数调用、信号传递等。
    • QSPY 可以解析这些记录,将其转换为人类可读的形式,帮助开发者快速定位问题所在。例如,通过查看对象字典和函数字典记录,开发者可以清晰地了解各个对象和函数的状态。
    • 优势:提高调试效率,减少故障排查时间。
  • 性能分析

    • QS 可以记录软件的运行时间、资源使用等性能数据。
    • QSPY 可以对这些数据进行分析和可视化,帮助开发者找出性能瓶颈。例如,通过分析时间戳和函数调用记录,开发者可以了解哪些函数执行时间过长。
    • 优势:优化软件性能,提高系统响应速度。
  • 系统监控

    • 在软件运行过程中,QS 可以持续记录系统的运行状态。
    • QSPY 可以实时显示这些信息,帮助开发者监控系统的健康状况。例如,通过查看事件队列的使用情况,开发者可以了解系统的负载情况。
    • 优势:及时发现系统异常,保障系统稳定运行。
8. 总结与建议

通过以上对 QS 和 QSPY 的详细介绍,我们可以看到它们在软件开发中的重要作用。以下是一些总结和建议,帮助开发者更好地使用这些工具。

  • 总结

    • QS 提供了面向块的接口、字典跟踪记录、应用特定记录等功能,方便开发者记录软件运行信息。
    • QSPY 作为主机应用,负责解析和处理 QS 记录,提供人类可读的输出,并支持数据导出到 MATLAB 进行进一步分析。
  • 建议

    • 在系统初始化阶段,合理生成字典记录,避免在实时运行时生成,以减少对系统性能的影响。
    • 在使用 QS 记录时,注意选择合适的记录类型和数据元素,避免生成过大的记录,保持关键部分代码的简洁。
    • 在配置 QSPY 时,仔细设置命令行参数,确保正确处理 QS 跟踪数据。
    • 结合 MATLAB 等工具进行数据处理和可视化,充分发挥 QS 和 QSPY 的优势。

以下是使用 QS 和 QSPY 的整体流程图:

graph LR
    A[软件开发] --> B[QS 记录生成]
    B --> C[数据传输]
    C --> D[QSPY 解析处理]
    D --> E[数据输出]
    E --> F[查看分析]
    F --> G{是否需要进一步分析}
    G -- 是 --> H[导出到 MATLAB]
    G -- 否 --> I[结束]
    H --> J[MATLAB 处理可视化]
    J --> I

总之,QS 和 QSPY 为软件开发提供了一套完整的跟踪和分析解决方案,开发者可以根据实际需求灵活运用这些工具,提高开发效率和软件质量。在实际应用中,不断探索和总结经验,充分发挥它们的潜力,为软件开发带来更多的便利和价值。

基于51单片机,实现对直流电机的调速、测速以及正反转控制。项目包含完整的仿真文件、源程序、原理图和PCB设计文件,适合学习和实践51单片机在电机控制方面的应用。 功能特点 调速控制:通过按键调整PWM占空比,实现电机的速度调节。 测速功能:采用霍尔传感器非接触式测速,实时显示电机转速。 正反转控制:通过按键切换电机的正转和反转状态。 LCD显示:使用LCD1602液晶显示屏,显示当前的转速和PWM占空比。 硬件组成 主控制器:STC89C51/52单片机(AT89S51/52、AT89C51/52通用)。 测速传感器:霍尔传感器,用于非接触式测速。 显示模块:LCD1602液晶显示屏,显示转速和占空比。 电机驱动:采用双H桥电路,控制电机的正反转和调速。 软件设计 编程语言:C语言。 开发环境:Keil uVision。 仿真工具:Proteus。 使用说明 液晶屏显示: 第一行显示电机转速(单位:转/分)。 第二行显示PWM占空比(0~100%)。 按键功能: 1键:加速键,短按占空比加1,长按连续加。 2键:减速键,短按占空比减1,长按连续减。 3键:反转切换键,按下后电机反转。 4键:正转切换键,按下后电机正转。 5键:开始暂停键,按一下开始,再按一下暂停。 注意事项 磁铁和霍尔元件的距离应保持在2mm左右,过近可能会在电机转动时碰到霍尔元件,过远则可能导致霍尔元件无法检测到磁铁。 资源文件 仿真文件:Proteus仿真文件,用于模拟电机控制系统的运行。 源程序:Keil uVision项目文件,包含完整的C语言源代码。 原理图:电路设计原理图,详细展示了各模块的连接方式。 PCB设计:PCB布局文件,可用于实际电路板的制作。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值