[I.2]个人作业:软件案例分析

[I.2]个人作业:软件案例分析

——coder0xe
项目 内容
这个作业属于哪个课程 2025春季软件工程(罗杰、任健)
这个作业的要求在哪里 [I.2]个人作业:软件案例分析
我在这个课程的目标是 在PSP中精进个人代码技术,在TSP中提高团队合作凝聚力
这个作业在哪个具体方面帮助我实现目标 分析市场上的软件案例,全面地了解软件工程的原理在好/坏软件上的作用

0. 选题介绍

​ 本次作业选择开源项目进行分析,我选择面向C/C++/C#/Swift的一款调试工具LLDB进行分析。LLDB全称Lowel-Level Debugger, 是苹果公司开发的一款开源、高性能的源代码级调试器,作为Clang和LLVM项目的一部分,LLDB为提供了丰富的功能和灵活的接口,使得开发者能够有效地定位和修复代码中的bug。与之类似的调试工具还有gdb等经典的调试工具,我选择gdb进行比较分析。

参考材料:

1. 调研、评测

1.1 软件评测

1.1.1 软件使用

​ LLDB是一款较为新兴的源代码调试工具,由于已经具有GDB调试C/C++代码的经验,我选择直接使用lldb工具并使用help命令来查看具体有哪些调试指令,lldb具有的调试命令如下输出:

​ 下面针对几个常见的指令进行使用:

  • breakpoint:设置断点
  • step:步进(进入语句中调用的函数)
  • stepi:汇编指令步进
  • next:步过(跳过语句中调用的函数)
  • nexti:汇编指令步过
  • run:运行调试目标
  • print:打印变量的值
  • file:指定调试文件
  • quit/exit:退出LLDB调试器
  • gui:居然提供了GUI调试页面!

​ 我编写了一个简单的但又不失复杂性的CPP程序来实践调试流程,内容是首先创建一棵7个节点的满二叉树,而后进行层序遍历,输出每一层的结果,其中使用了new等内存分配关键字,可以作为调试过程中查看内存分配的目标:

#include <iostream>
#include <queue>
#include <vector>
using namespace std;

typedef struct Tnode {
  int data;
  struct Tnode *left, *right;
} Tnode, *Tree;

void InitTree(Tree &T) {
  T = new Tnode{1, nullptr, nullptr};
  Tnode *left = new Tnode{2, nullptr, nullptr};
  Tnode *right = new Tnode{3, nullptr, nullptr};
  T->left = left;
  T->right = right;
  left = new Tnode{4, nullptr, nullptr};
  right = new Tnode{5, nullptr, nullptr};
  T->left->left = left;
  T->left->right = right;
  left = new Tnode{6, nullptr, nullptr};
  right = new Tnode{7, nullptr, nullptr};
  T->right->left = left;
  T->right->right = right;
}

vector<vector<int>> LevelOrder(const Tree T) {
  if (!T)
    return vector<vector<int>>{};
  vector<vector<int>> res;
  queue<Tnode *> q;
  q.push(T);
  while (!q.empty()) {
    int size = q.size();
    vector<int> row;
    while (size--) {
      Tnode *node = q.front();
      q.pop();
      row.push_back(node->data);
      if (node->left)
        q.push(node->left);
      if (node->right)
        q.push(node->right);
    }
    res.push_back(row);
  }
  return res;
}

int main() {
  Tree T = nullptr;
  InitTree(T);
  vector<vector<int>> res = LevelOrder(T);
  for (int i = 0; i < res.size(); i++) {
    cout << "Row " << i + 1 << ": ";
    for (int j = 0; j < res[i].size(); j++) {
      cout << res[i][j] << " ,";
    }
    cout << endl;
  }
  return 0;
}
  • 编译指令:

    clang++ -g test.cpp -o test.out
    
  • 调试过程:

    1. lldb: 进入调试

    2. file test.out:选择调试文件

    3. 首先在InitTree函数尾部打断点,之后运行run,停在函数尾部,使用命令查看内存分配情况

      breakpoint set --file test.cpp --line 24
      
    4. 查看内存分配情况:memory read <address>

    5. 使用continue指令继续运行代码,在层序遍历处打断点,使用逐步调试和输出的方式进行测试。

      • 打印当前队列头节点的值

      • 使用n/s逐步调试,空格默认重复上一次的指令

    6. 体验一下GUI页面

1.1.2 软件分析

​ 通过写一个具体的CPP程序来体验LLDB调试工具,我的主观感受上从GDB到LLDB的技术跨度不大,可以说会使用GDB就可以无伤上手LLDB,毕竟还有help嘛!LLDB丰富的调试指令可以为用户提供细致精确的调试体验,可以满足我当前的技术水平和工程规模的调试需求。

​ 作为一个调试工具,与传统的调试工具GDB相比,我认为LLDB是在一定程度上超过了GDB的使用体验的,主要在以下几方面:

  • 在用户界面上:
    • 相比GDB,LLDB在断点处提供了更加丰富的上下文代码信息,便于用户查看
    • LLDB提供了GUI页面,而GDB只提供了TUI页面,LLDB相对更加用户友好
  • 在调试功能上:
    • LLDB兼容了GDB的基本指令模式,技术跨度小
    • LLDB的指令比GDB更加丰富,help指令说明详细
  • 在情怀上(误):
    • 谁会拒绝用LLVM全家桶呢?

1.1.3 改进意见

LLDB的官方文档中介绍了GDB到LLDB的指令映射,这是让我惊喜的一点,这体现了软件工程中对于用户体验的考量,让使用GDB的老家伙们乐于使用LLDB作为调试工具而不用从头摸索。作为建议的一点是:建议加入专门介绍调试指令的文档,目前的文档只详细介绍了breakpoint(断点指令)的详细使用,对于其他指令则缺乏介绍。同时,建议美化GUI页面,我喜欢花花绿绿的页面!

1.1.4 用户调研

  • 采访对象的背景:选择王德庆老师软件工程班的沈锎同学进行采访,沈锎同学经常编写Rust/C/C++代码,具有丰富的代码编写和调试经验,我选择将问题以调查问卷的方式发送给沈锎同学,方便进行系统作答。

  • 下面是沈锎同学的答案:

    • 请问你对调试工具的需求是什么?

      ​ 基本功能:设置并管理断点,并支持多样的断点类型。要能够支持单步调试,包括逐行、逐过程、逐指令等情形。可以实时查看变量的值,可以对变量或寄存器进行监控。能够查看函数调用栈信息,并能动态切换栈帧、查看栈变量。允许在调试过程中计算任意表达式的值。

      ​ 性能需求:能够快速启动、加载、索引,在进行单步操作时减少卡顿。

      ​ 用户体验:提供简洁美观的用户界面,常用操作可以进行提示,并且拥有详实准确的文档。

    • 请问你目前实际使用的调试工具是什么?

      ​ 调试 C 语言程序会使用 GDB,调试 Rust 程序会使用 LLDB.

    • 请问你在使用LLDB时,会遇到哪些问题或者亮点?

      ​ 亮点:相比 GDB 而言,LLDB 的 TUI 界面比较美观,对 Rust 的支持比较好,能够正确解析 Rust 的栈帧。同时,LLDB 能够支持通过 Python 脚本进行交互,非常现代,并且有效。

      ​ 问题:LLDB 在进行调试的时候,有时会遇到卡顿,甚至在一些极端情况下,比如目标程序异常退出时,可能出现一些假死,着给实际使用带来了一些困扰。此外,LLDB 的用户群体相对较少,在使用过程中遇到问题时能够找到的资料较少。

    • 从用户体验的角度来说,你认为LLDB还有哪些地方需要改进?

      ​ 命令的智能化提示:能够对用户输入的指令进行补全,甚至可以根据上下文进行只能提示。

      ​ 调试信息的优化:进一步优化调试信息的展示方式,对于输出内容较长时,可以简化输出内容,或者对重点内容进行突出渲染。

      ​ 丰富的社区支持:增加更多对于”常见问题“的支持与帮助,这个也许需要软件开发者进行社区的建设与维护。

    • 你认为与LLDB相比GDB在数据量/界面/准确度/用户体验上具有哪些优势或劣势?

      ​ (界面)LLDB 在界面上有很大的优势,显示更加现代,通过 ASCII 形成一些图案,更加美观且指示清晰。GDB 的界面则更加紧凑,却又略显简陋。

      ​ (准确度)LLDB 在诸如 Rust 这样的新兴语言的调试中有优势,对于栈帧等内容解析的准确度上更有优势。但据说在某些复杂 C/C++ 程序或特殊场景的处理上,LLDB 的表现距离 GDB 还有一定的距离。

      ​ (用户体验)对于我而言 GDB 有使用基础,因此对于其基础指令有深刻的印象,使用相对而言还是要简单一些。LLDB 的指令更加新颖,有一些新鲜的体验,但需要时间去适应。同时,如前面所述,GDB 的用户群体广泛,拥有大量的教程和资源,在这方面,LLDB 会有一些劣势。

    • 请你为LLDB评价:
      a)非常不推荐
      b)不推荐
      c)一般
      => d)好,不错 <=
      e)非常推荐

1.1.5 评测结论

  • a) 非常不推荐
  • b) 不推荐
  • c) 一般
  • d) 好,不错
  • e) 非常推荐

​ 我的结论是:d)好,不错

2.Bug分析和提交

​ 作为一个调试工具,我在使用LLDB进行代码调试过程中发现了一些bug. 我的系统环境:

  • Archlinux内核版本:6.13.6
  • LLVM/Clang/LLDB版本:19.1.7

2.1 Bug1

2.1.1 bug复现
  • 可复现性:必然触发

  • 调试的代码:

    int main() {
      int var = 0;
      return 0;
    }
    
  • 编译指令: clang test.c -O1 -g -static

  • 调试过程如下:

  • 可以发现,在成功修改var变量的值后,再次打印var值却显示原来的值

2.1.2 bug分析

​ 通过查看DWARF区域的内存情况,我们可以观察到var变量的属性

 <2><32>: Abbrev Number: 3 (DW_TAG_variable)
    <33>   DW_AT_const_value : 0
    <34>   DW_AT_name        : (indexed string: 0x5): var
    <35>   DW_AT_decl_file   : 0
    <36>   DW_AT_decl_line   : 2
    <37>   DW_AT_type        : <0x3c>

​ 这个变量有一个DW_AT_const_value属性,这就是造成它不可被修改的原因。

可能成因:回顾大三上学期完成的SysY Compiler的项目经验, 从编译系统的角度分析这个问题,我认为这个bug的可能成因是在函数分配栈内存时发生了错误,错误地为变量分配了const的属性。

bug的严重性:在程序员调试代码过程中,修改变量值的场景不在少数,修改变量值的错误会很大程度的影响程序员的调试体验,但是在一定程度上可以被程序员及时发现,不会造成很大的损失。另外在此处我们针对调试工具给出发生bug的严重等级评价:

  • 五颗星:这些bug会导致调试工具完全无法使用,或者导致调试过程中出现严重的数据丢失或安全问题。例如,调试工具在启动时崩溃,或者调试过程中导致被调试程序的数据损坏。
  • 四颗星:这些bug会严重影响调试工具的主要功能,可能导致调试过程无法继续。例如,频繁的崩溃,或者关键功能(如设置断点、查看变量)无法正常工作。
  • 三颗星:这些bug会显著影响调试工具的使用体验,但通常有解决方法或替代方案。例如,调试过程中偶尔出现的崩溃,或者某些复杂表达式无法正确求值。
  • 二颗星:这些bug可能会影响某些特定功能的使用,但不会导致调试工具完全无法使用。例如,某些特定情况下的表达式求值错误,或者某些特定平台上的兼容性问题。
  • 一颗星:这些bug通常不会影响调试工具的主要功能,只是一些小的瑕疵或不便。例如,某些命令的输出格式不正确,或者某些功能的文档不够详细。

​ 这个bug我对它的评价是:三颗星

软件团队为何未能修复? LLDB隶属于LLVM开源社区,LLVM project是一个庞大的编译系统开源项目,对于庞大的编译系统来说,在编译某些程序时产生bug是可能的,或者说是可以忍受的、可以理解的。我认为这次bug的发生可能是因为开发人员对于边缘情况没有考虑到位,同时测试人员没有进行周到的测试。

2.1.3 bug改进建议

​ 在断点处p var = 10应当可以正常的修改变量的值。

2.1.4 bug反馈

​ 我选择在Github上的LLVM代码库中向开发者提出issue,链接🔗

2.2 Bug2

2.2.1 bug复现
  • 可复现性:必然触发

  • 调试的代码:

    class Test {
      static const int a[];
    };
    const int Test::a[] = {1};
    
    int a(int val) { return val; }
    
    int main(int argc, char const *argv[]) {
      return a(2); // break here
    }
    
  • 编译指令: clang++ test2.cpp -stdlib=libc++ -g -o test2.out

  • 调试过程如下:

  • 解释:我们在Test命名空间(namespace)中创建了变量a,这个变量我们在main函数中应当是不可见的,即我们在main函数中调用a(1),LLDB应当认为我们在调用函数a,而通过LLDB的报错信息我们可以发现LLDB认为我们是对a这个Test命名空间中的变量进行了()重载,这明显是错误的。

2.2.2 bug分析

可能成因:LLDB中对于命名空间的解析以及变量作用域的范围解析出现问题,以至于我们要调用函数a,LLDB却认为我们是在对Test命名空间中的a变量进行了()重载。

bug的严重性:三星。

软件团队为何未修复? 我认为是因为开发人员的疏忽和测试人员测试不全面造成的。

2.2.3 bug改进建议

​ 这里应当调用函数a, 传参1。

2.2.4 bug反馈

​ 我选择在Github上的LLVM代码库中向开发者提出issue,链接🔗

2.分析

2.1 工作量分析

​ 下图为LLDB开源代码的文件结构:

​ 将代码clone到本地并使用代码统计工具cloc来统计代码行数,总代码行数为3w+行数。对于一个6人左右的计算机专业的大学毕业生,在开发层面上我们估计每个人每天大概能开发500行左右代码,总体开发时间在10天左右,加上5天测试等时间,共需要15天时间。

2.2 软件质量分析

​ 目前市场上面向C/C++的调试工具主要为GDB和LLDB。与GDB相比,LLDB具有以下优势:

  • 通常在性能和内存使用方面表现更好,特别是在处理大型项目时。
  • 使用更现代的代码基础和优化技术,提供更快的调试体验。
  • 提供与 GDB 类似的调试功能,支持断点、观察点、单步执行、堆栈跟踪、变量查看和修改等。
  • 在多线程调试和远程调试方面表现更好,提供更稳定的体验。
  • 提供命令行界面(CLI),支持多种图形用户界面(GUI)前端,如 Xcode、Visual Studio Code 等。
  • 命令行界面设计更现代,命令更直观,易于学习和使用。

​ 综合来看我愿意把LLDB排在第一名的位置。

对LLDB开发团队的建议:改进稳定性、错误处理和恢复机制。

3.建议和规划

3.1 市场现状

3.1.1 市场概况

​ 调试工具市场主要面向软件开发人员,包括专业开发者、软件工程师、系统管理员、计算机科学学生、业余编程爱好者等。知名技术平台Stack Overflow有超过400万活跃的开发者用户,全球的开发者数量要远超这个数字,用户群体庞大。

3.1.2 竞争产品

​ LLDB的主要竞争产品包括GDB、Visual Studio Debugger、WinDbg等。

3.1.2 产品定位

  • LLDB:由LLVM开发团队开发的一款开源的新兴的调试工具,主要应用在MacOS平台。优势是开源代码,功能强大;劣势在于作为一款新兴的工具,还没得到广泛的推广。
  • GDB:优势在于历史悠久,成熟稳定,社区支持广泛,广泛应用于Linux平台;劣势在于性能和易用性上略逊于LLDB。
  • Visual Studio Debugger:集成在Visual Studio IDE中,主要应用于Windows平台。优势在于与IDE紧密集成,用户无需配置开箱即用,体验好;劣势在于跨平台方面没有支持。
  • WinDbg:主要用于Windows平台的内核调试。优势在于强大的内核调试功能;劣势在于学习曲线陡峭,用户界面不友好,难以推广。

3.2 市场与产品生态

3.2.1 核心用户群

​ 核心用户群是软件开发人员:

  • 年龄18~45岁之间
  • 学历或专业上具有计算机科学与技术或软件工程相关专业背景
  • 收入中等偏上
  • 爱好编程和技术,追求代码的艺术
  • 表面需求:使用代码调试工具调试代码
  • 潜在需求:提高开发效率和代码质量

3.2.2 用户群体关系

​ 开发人员之间通过在线技术论坛,例如Stack OverflowCSDN等或其他技术社区以及社交媒体进行技术交流,可以为LLDB建立开放的在线技术交流社区,通过社区支持和用户反馈不断提高产品体验,从而与已有的LLVM系列构成完整的生态链。

3.2.3 产品生态

​ LLDB已有较为良好的产品生态,LLDB与Clang编译器、LLVM工具链紧密集成;可以利用这些关系构建产品生态,通过提供完整的开发工具链,向linux平台抢占更多的用户。

3.3 产品规划

3.3.1 新功能设计

智能断点功能:智能断点功能是一个集成在LLDB调试器中的高级功能,可以自动化、智能化地管理断点,从而帮助用户更快地定位问题,提高用户调试效率和用户体验。

​ 智能断点管理系统可以显著提高调试效率和用户体验,特别是在处理大型项目和复杂调试场景时。相比其他功能,智能断点管理系统能够直接解决用户在调试过程中遇到的痛点,如手动管理断点繁琐、条件断点设置复杂、断点管理缺乏灵活性等问题。通过自动化和智能化的断点管理,用户可以更高效地定位和解决问题,从而提高开发效率。

NABCD分析:

  • N(Need):现代调试工具用户在面对大型项目和复杂调试场景时,不断需要调试工具,还对调试器提出了智能化需求。
  • A(Approach):通过设计一个智能断点功能,提供自动设置断点、条件断点、断点分组、断点历史记录等功能,帮助用户更高效地管理断点。
  • B(Benefit):提高用户的调试效率和体验,减少手动定位bug并打断点的时间,帮助程序员更快地解决bug。
  • C(Competitors):相比GDB和Visual Studio Debugger等工具,我们的智能断点功能在减少用户调试时间、提高用户调试体验上具有巨大优势。
  • D(Delivery):依托目前广泛的LLVM系列编译器的广大用户群体,通过LLVM技术社区进行推广和用户反馈,不断改进。

3.3.2 角色配置

  • 项目经理:1人
  • 开发人员:3名
  • 测试人员:1名

3.3.3 团队计划

  • 第1周:开需求分析会,明确智能断点功能的需求,确定智能断点功能的技术路线、实现方案
  • 第2~6周:开发人员实现核心功能自动设置断点,同时测试人员编写测试用例进行全面测试
  • 第7~10周:开发人员完成辅助功能条件断点、断点分组、断点历史记录等,测试人员跟进测试
  • 第11~12周:测试人员继续进行功能测试,开发人员编写技术文档以及用户使用手册
  • 第13~14周:在LLVM技术社区发布alpha版本并进行推广,开发人员进行性能优化,测试人员跟进测试
  • 第15周:发布beta版本,接收社区反馈对产品进行不断改进。
  • 第16周:正式发布具有智能断点功能的LLDB调试器,收集用户反馈,修改bug,构想新功能,准备下一个版本。
posted @ 2025-03-12 21:26  coder0xe  阅读(70)  评论(0)    收藏  举报