打印完整的递归调用栈

之前在写0-1背包问题的递归解法时,想要弄出完整的递归栈。尝试了使用debug工具手工追踪并画出调用栈,发现太麻烦了,又试了一下使用visual studio的code map功能,发现对于递归,它只会显示递归函数不断调用自己,并不会自动展开成为树的形式。所以我就使用了最简陋的办法,就是自己写了一个类,依赖C++的constructor和destructor来自动将栈输入到一个vector中,并且在main函数结束的地方添加一个语句将其内容输出到文件中。

这里使用了一些C++11的特性,我使用mingw和msvc2015编译通过了,其它的编译器就不知道了。

下面是这个类的代码,写的很不完善:

//stacktrace.h

  1. #ifndef STACKTRACE_H
  2. #define STACKTRACE_H
  3. #include <iostream>
  4. #include <string>
  5. #include <vector>
  6. /**
  7. * 这个类还比较简单粗暴,没有实现字符串格式化参数的软编码(如果需要可以使用boost::format()来像sprintf()一样格式化),
  8. * 也没有作为模板类实现以完成可变形参构造函数(注意不要使用C语言的va_arg)。
  9. * 使用的时候需要根据需求进行修改。
  10. *
  11. * 使用方法:
  12. * 在main函数中setSpaceLength(unsigned length)来控制格式,如果不做这一步,则默认spaceLength = 4
  13. * 在需要进行stack trace的函数所在的文件中include "stacktrace.h"
  14. * 在需要进行stack trace的函数开头加上一句 StackTrace(capacity, itemNum); capacity和itemNum是需要打印到stack trace的数据
  15. * 在main函数的结尾加上一句writeToFile(std::ostream &os, StackTrace::outputType type = reformat);
  16. *
  17. * 如果需要修改这个类的显示效果,只需要修改 StackTrace(double capacity, int itemNum);
  18. * 注意:每一行的结束需要写入一个换行符
  19. *
  20. */
  21. class StackTrace
  22. {
  23. private: //private data field
  24. static int depth;
  25. static std::vector<std::string> content;
  26. static unsigned spaceLength;
  27. static std::vector<unsigned> firstNotSpace;
  28. public: //public data field
  29. enum outputType{raw,reformat};
  30. public: //constructor and copy control
  31. StackTrace(double capacity, int itemNum);
  32. virtual ~StackTrace()
  33. {
  34. --depth;
  35. }
  36. public: //public member function
  37. static void setSpaceLength(unsigned length);
  38. static void writeToFile(std::ostream &os, outputType type = reformat);
  39. private: //private member function
  40. static void clear();
  41. static void outputResult(std::ostream &os);
  42. static void generateFirstNotSpace();
  43. static void textReplace();
  44. };
  45. #endif // RECURSIONSTACKTRACE_H

//stacktrace.cpp

  1. #include "stacktrace.h"
  2. #include <sstream>
  3. #include <iostream>
  4. #include <vector>
  5. #include <string>
  6. #include <fstream>
  7. #include <cstdlib>
  8. using namespace std;
  9. int StackTrace::depth = 0;
  10. std::vector<std::string> StackTrace::content;
  11. unsigned StackTrace::spaceLength = 4;
  12. std::vector<unsigned> StackTrace::firstNotSpace;
  13. void StackTrace::clear()
  14. {
  15. content.clear();
  16. firstNotSpace.clear();
  17. }
  18. void StackTrace::setSpaceLength(unsigned length)
  19. {
  20. spaceLength = length;
  21. }
  22. StackTrace::StackTrace(double capacity, int itemNum)
  23. {
  24. ++depth;
  25. ostringstream temp;
  26. int i;
  27. for(i = 0;i<depth-1;++i)
  28. {
  29. for(unsigned j= 0;j<spaceLength;++j)
  30. temp << " ";
  31. //cout << " ";
  32. }
  33. if(i<depth)
  34. {
  35. if(spaceLength==0)
  36. ; //null statement
  37. else
  38. {
  39. temp << "└";
  40. for(unsigned j=0; j<spaceLength-1; ++j)
  41. temp << "-";
  42. }
  43. }
  44. temp << "(" << itemNum << ", " << capacity << ")" << endl;
  45. content.push_back(temp.str());
  46. }
  47. void StackTrace::writeToFile(std::ostream &os, outputType type)
  48. {
  49. if(type == reformat)
  50. {
  51. generateFirstNotSpace();
  52. textReplace();
  53. }
  54. outputResult(os);
  55. clear();
  56. }
  57. void StackTrace::outputResult(ostream &os)
  58. {
  59. for(const string &temp:content)
  60. os << temp;
  61. os << flush;
  62. }
  63. void StackTrace::generateFirstNotSpace()
  64. {
  65. for(size_t i= 0; i<content.size();++i)
  66. firstNotSpace.push_back(content[i].find_first_not_of(‘ ‘));
  67. }
  68. void StackTrace::textReplace()
  69. {
  70. for(size_t i=1; i<content.size();++i)
  71. {
  72. size_t pos = 0;
  73. while((pos = content[i].find("└",pos)) != string::npos)
  74. {
  75. for(int j = i-1;j>=0;--j)
  76. {
  77. if(firstNotSpace[j]==string::npos)
  78. continue;
  79. if(firstNotSpace[j] <= pos )
  80. goto END2LOOP;
  81. else
  82. {
  83. content[j][pos] = ‘|‘;
  84. continue;
  85. }
  86. }
  87. }
  88. END2LOOP: ;
  89. }
  90. }

以下是结果范例:

  1. └---(5, 3)
  2. └---(4, 1)
  3. | └---(3, 0)
  4. | └---(3, 1)
  5. | └---(2, 1)
  6. | └---(1, 0)
  7. | └---(1, 1)
  8. | └---(0, 0.9)
  9. | | └---(-1, 0.6)
  10. | | └---(-1, 0.9)
  11. | └---(0, 1)
  12. | └---(-1, 0.7)
  13. | └---(-1, 1)
  14. └---(4, 3)
  15. └---(3, 2)
  16. | └---(2, 2)
  17. | └---(1, 1)
  18. | | └---(0, 0.9)
  19. | | | └---(-1, 0.6)
  20. | | | └---(-1, 0.9)
  21. | | └---(0, 1)
  22. | | └---(-1, 0.7)
  23. | | └---(-1, 1)
  24. | └---(1, 2)
  25. | └---(0, 1.9)
  26. | | └---(-1, 1.6)
  27. | | └---(-1, 1.9)
  28. | └---(0, 2)
  29. | └---(-1, 1.7)
  30. | └---(-1, 2)
  31. └---(3, 3)
  32. └---(2, 3)
  33. └---(1, 2)
  34. | └---(0, 1.9)
  35. | | └---(-1, 1.6)
  36. | | └---(-1, 1.9)
  37. | └---(0, 2)
  38. | └---(-1, 1.7)
  39. | └---(-1, 2)
  40. └---(1, 3)
  41. └---(0, 2.9)
  42. | └---(-1, 2.6)
  43. | └---(-1, 2.9)
  44. └---(0, 3)
  45. └---(-1, 2.7)
  46. └---(-1, 3)

这里有别人写的类似的类:

A Simple C++ Function Call Stack Trace Utility

来自为知笔记(Wiz)

时间: 2024-10-29 04:19:19

打印完整的递归调用栈的相关文章

golang 打印所有的runtime 调用栈

依赖包: import "runtime" 代码: buf := make([]byte, 1 << 20)  runtime.Stack(buf, true) fmt.Printf("\n%s", buf)

在c或c+程序里打印调用栈。转

在C/C++程序里打印调用栈信息 我们知道,GDB的backtrace命令可以查看堆栈信息.但很多时候,GDB根本用不上.比如说,在线上环境中可能没有GDB,即使有,也不太可能让我们直接在上面调试.如果能让程序自己输出调用栈,那是最好不过了.本文介绍和调用椎栈相关的几个函数. NAME       backtrace, backtrace_symbols, backtrace_symbols_fd - support for application self-debugging SYNOPSIS

android native HAL程序 java程序 linux kernel打印调用栈的方法

android native HAL程序 java程序 linux kernel打印调用栈的方法 关于android java打出调用栈的方法 1)方法一:refs:frameworks/base/services/java/com/android/server/ActivityManagerService.javastartProcessLocked(){Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "amProcessStart&quo

perf打印调用栈的过程

perf_prepare_sample-->perf_callchain-->get_perf_callchain 上面的调用栈会使用 perf_event_output--> 0xffffffff811837f0 : perf_event_output+0x0/0x80 [kernel] 0xffffffff81183a1f : __perf_event_overflow+0x1af/0x1d0 [kernel] 0xffffffff81183c1a : perf_swevent_ov

二叉树遍历,递归,栈,Morris

一篇质量非常高的关于二叉树遍历的帖子,转帖自http://noalgo.info/832.html 二叉树遍历(递归.非递归.Morris遍历) 2015年01月06日 |  分类:数据结构 |  标签:二叉树遍历 |  评论:8条评论 |  浏览:6,603次 二叉树遍历是二叉树中最基本的问题,其实现的方法非常多,有简单粗暴但容易爆栈的递归算法,还有稍微高级的使用栈模拟递归的非递归算法,另外还有不用栈而且只需要常数空间和线性时间的神奇Morris遍历算法,本文将对这些算法进行讲解和实现. 递归

获取任意线程调用栈的那些事

BSBacktraceLogger 是一个轻量级的框架,可以获取任意线程的调用栈,开源在我的 GitHub,建议下载下来结合本文阅读. 我们知道 NSThread 有一个类方法 callstackSymbols 可以获取调用栈,但是它输出的是当前线程的调用栈.在利用 Runloop 检测卡顿时,子线程检测到了主线程发生卡顿,需要通过主线程的调用栈来分析具体是哪个方法导致了阻塞,这时系统提供的方法就无能为力了. 最简单.自然的想法就是利用 dispatch_async 或 performSelec

函数的递归调用与二分法

一,什么是递归? 递归的作用可以完全取代循环,很多函数编程语言中习惯用递归来实现循环 1,递归算法: (1),'重复' ,凡是通过循环语句可以实现的,都可以用递归来实现 (2),'将问题分解成同类的子问题', 如持续循环的运算操作,持续循环的判断操作,他们的每次循环都是同样的一个'动作',这个动作就是一个子问题 2,函数的递归调用: 在调用一个函数的过程又直接或者间接的调用该函数本身,称之为递归调用. 递归必须满足两个条件: 1, 每进入下一次递归调用,问题的规模都应该有所减少 2, 递归必须有

Python旅途——函数的递归和栈的使用

Python--函数之递归.栈的使用 今天主要和大家分享函数的递归,同时引入一个新的概念--栈 1.递归 1.定义 函数的递归指的就是函数自己调用自己,什么是函数自己调用自己呢?我们来看一个栗子: 这里给大家一个数学中的一个数列:斐波那契数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,它指的是前两项的和等于第三项,那么我们对于这

浏览器中的JavaScript执行机制:08 | 调用栈:为什么JavaScript代码会出现栈溢出?

前言:该篇说明:请见 说明 —— 浏览器工作原理与实践 目录 在上篇文章中,我们讲到了,当一段代码被执行时,JavaScript 引擎先会对其进行编译,并创建执行上下文.但是并没有明确说明到底什么样的代码才算符合规范. 那么接下来我们就来明确下,哪些情况下代码才算是“一段”代码,才会在执行之前就进行编译并创建执行上下文.一般说来,有这么三种情况: 1.当 JavaScript 执行全局代码的时候,会编译全局代码并创建全局执行上下文,而且在整个页面的生存周期内,全局执行上下文只有一份. 2.当调用