将日志插入项目中的每个函数的脚本?

将日志插入项目中的每个函数的脚本?

问题描述:

我已经继承了一个相当大的代码库,90%的C++,我需要赶紧加快速度。在宽目录树结构中有数百个.cc文件。将日志插入项目中的每个函数的脚本?

它相当复杂,并没有日志。为了弄清楚一些主要的子系统是如何工作的,我想在每个函数中插入一个函数调用。

例如,给定一个.cc文件充满了这样的东西:

void A::foo(int a, int b) { 
    // ... 
} 

void A::bar() { 
    // ... 
} 

void B::bleh(const string& in) { 
    // ... 
} 

我希望得到这样的:

void A::foo(int a, int b) { 
    LOG(debug) << "A::foo() called."; 
    // ... 
} 

void A::bar() { 
    LOG(debug) << "A::bar() called."; 
    // ... 
} 

void B::bleh(const string& in) { 
    LOG(debug) << "B::bleh() called."; 
    // ... 
} 

这可以通过Python脚本来完成,CMD脚本,电源外壳脚本等。如果有办法让VS做到这一点,那就太好了。无论什么作品。不一定要漂亮,我不检查任何这个。

此外,它不一定需要得到一切。例如。嵌套类,头文件中的实现等。

已经为增加使用VS宏分析代码类似的东西,这里的代码

Imports System 
Imports EnvDTE 
Imports EnvDTE80 
Imports System.Diagnostics 

Public Module Module1 

    Function GetOutputWindowPane(ByVal Name As String, Optional ByVal show As Boolean = True) As OutputWindowPane 
     Dim window As Window 
     Dim outputWindow As OutputWindow 
     Dim outputWindowPane As OutputWindowPane 

     window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput) 
     If show Then window.Visible = True 
     outputWindow = window.Object 
     Try 
      outputWindowPane = outputWindow.OutputWindowPanes.Item(Name) 
     Catch e As System.Exception 
      outputWindowPane = outputWindow.OutputWindowPanes.Add(Name) 
     End Try 
     outputWindowPane.Activate() 
     Return outputWindowPane 
    End Function 

    Const ToInsert As String = "/* Inserted text :D */" 

    Sub AddProfilingToFunction(ByVal func As CodeFunction2) 
     Dim editPoint As EditPoint2 = func.StartPoint.CreateEditPoint() 
     While editPoint.GetText(1) <> "{" 
      editPoint.CharRight() 
     End While 

     editPoint.CharRight() 
     editPoint.InsertNewLine(1) 

     Dim insertStartLine As Integer = editPoint.Line 
     Dim insertStartChar As Integer = editPoint.LineCharOffset 
     editPoint.Insert(ToInsert) 

     GetOutputWindowPane("Macro Inserted Code").OutputString(_ 
      editPoint.Parent.Parent.FullName & _ 
      "(" & insertStartLine & "," & insertStartChar & _ 
      ") : Inserted Code """ & ToInsert & """" & vbCrLf) 
    End Sub 

    Sub AddProfilingToProject(ByVal proj As Project) 
     If Not proj.CodeModel() Is Nothing Then 
      Dim EventTitle As String = "Add Profiling to project '" & proj.Name & "'" 
      GetOutputWindowPane("Macro Inserted Code").OutputString("Add Profiling to project '" & proj.Name & "'" & vbCrLf) 
      DTE.UndoContext.Open(EventTitle) 
      Try 
       Dim allNames As String = "" 
       For i As Integer = 1 To proj.CodeModel().CodeElements.Count() 
        If proj.CodeModel().CodeElements.Item(i).Kind = vsCMElement.vsCMElementFunction Then 
         AddProfilingToFunction(proj.CodeModel().CodeElements.Item(i)) 
        End If 
       Next 
      Finally 
       DTE.UndoContext.Close() 
      End Try 
      GetOutputWindowPane("Macro Inserted Code").OutputString(vbCrLf) 
     End If 
    End Sub 

    Sub AddProfilingToSolution() 
     GetOutputWindowPane("Macro Inserted Code").Clear() 
     If Not DTE.Solution Is Nothing And DTE.Solution.IsOpen() Then 
      For i As Integer = 1 To DTE.Solution.Projects.Count() 
       AddProfilingToProject(DTE.Solution.Projects.Item(i)) 
      Next 
     End If 
    End Sub 

End Module 
(这也是集团在一个单一的“撤消”命令,并列出了所有在其自己的输出窗口中改变一切)

PS 记住更改“常量ToInsert的String = ...”你确实要插入

+0

这是一个有趣的方法! +1。我敢打赌,VS IDE比一个手工构建的正则表达式更好地发现函数定义的开始。但假设“CodeModel”与IntelliSense使用的是相同的东西,在我的经验中,仍然会有一些情况依然窒息。 – 2009-09-18 06:02:56

+0

这也很漂亮。我会给它一个镜头。 – 2009-09-18 16:19:49

+0

有没有办法排除头文件(.h和.inl)?另外,也许排除模板? – 2009-09-18 17:34:13

运行时分析器会给你提供这些信息:它会告诉你每个例程调用了哪些子例程,以及多少次(但不是按什么顺序) 。

+0

是啊,我已经尝试了很多不同(免费)廓线仪,包括从一个代码微软。要么他们都吮吸,要么我太愚蠢,不知道如何使用它们。如果你有一个体面的工具的链接,以及一些关于如何获得上述信息的指示,我会非常乐意放弃。在这一点上,插入一些日志似乎更容易。 – 2009-09-18 03:26:01

+1

我在年龄段没有使用过。我使用的(来自Numega)已经出售给Compuware,然后转售现在归“Micro Focus”所有:http://www.microfocus.com/products/DevPartner/StudioProfessionalEditionCapabilities.asp#6他们的网站说你可以下载它的试用版。 – ChrisW 2009-09-18 03:36:20

几年前我在VS做过。
正则表达式将帮助你。
顺便说一句,不要插入不同的字符串。您可以添加相同的字符串,如:


LOG(debug) << __FUNCTION__ << " called."; 

编辑

这样的正则表达式(有效期仅为VS):


(void|char|int):b+:i\:\::i\([^(]*\):b*\{ 

您应该根据扩展您的需求正则表达式。

+0

啊是的,__FUNCTION __...好点。你不会碰巧有一个体面的正则表达式,你会吗? – 2009-09-18 03:39:38

+0

@jeffamaphone:我将正则表达式的示例添加到我的回答中 – Dmitriy 2009-09-18 03:46:32

+0

如果所有的函数声明遵循一个简单的模式,但它不可能捕获大型复杂代码库中的所有函数,因为函数声明可能采用非常不寻常的形式(其中有些很难与其他类型的声明区分开来)。 – 2009-09-18 04:24:24

您是否考虑过在调试器中运行代码,并简单地遍历整个应用程序(或者在您感兴趣的代码上设置断点,然后逐步完成)?我发现当面对一个我没有写的大型遗留代码库时,有时候这是一种有用的技术。

另外,如果你在VS世界编译,考虑采取看看/Gh/GH开关cl.exe。他们似乎允许你钩住函数进入/退出并调用其他例程。我以前从未使用过它们,但它们似乎直接满足了您的需求。

+0

噢,我整天都这么做。只有很多异步,多线程的东西正在进行,我正在寻找更高层次的关于这个事情的工作原理。 – 2009-09-18 03:42:58

由于您使用的Visual C++,似乎你只需要调用的函数的名称,有可能进一步使用以下命令行来自动完成这个切换到cl.exe

  • /Gh :启用_penter函数调用
  • /GH:启用_pexit函数调用

基本上,提供这些开关意味着,编译器将自动只要任何函数开始或结束,就会向名为_penter()_pexit()的函数注入调用。然后,您可以提供一个单独编译的模块来实现这两个函数,它们(a)调用一些辅助函数库(例如DbgHelp)来确定被调用函数的名称,或者(b)仅从栈中获取返回地址并打印它逐字地 - 之后,通过查看例如,写一个脚本来将这些地址转换成函数名称如果您将/link /MAP:mymapfile.txt传递给cl.exe,则生成链接器映射文件。

当然,你需要把你的_penter()_pexit()放在一个单独的模块中,/Gh/GH关闭以避免无限递归! :)

+0

嘿,那可能很有趣。谢谢。 – 2009-09-18 04:55:33

+1

您不需要用于prolog/epilog函数的单独模块。 '_penter()'和'_pexit()'定义如下:'extern“C”void __declspec(naked)_cdecl _penter(void)'。裸体功能没有检测到,所以没有递归机会。您不必担心子函数的递归,但可以使用线程局部变量检查来解决,以便在调用任何子项之前尽早在_penter()和_pexit()中解决。 – Adisak 2010-10-19 20:43:05