Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Script to insert logging into every function in a project?

I have inherited a fairly large codebase, 90% C++, and I need to get up to speed on it quickly. There are hundreds of .cc files in a wide directory tree structure.

It's fairly complex, and has no logging. In order to figure out how some major subsystems work, I want to insert a function call into every function.

E.g., given a .cc file full of stuff like this:

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

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

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

I'd like to get this:

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.";
    // ...
}

This can be done via python script, CMD script, power shell script, etc. If there is a way to make VS do it, great. Whatever works. Doesn't have to be pretty, I'm not checking any of this in.

Also, it doesn't necessarily need to get everything. E.g. nested classes, implementations in header files, etc.

like image 586
i_am_jorf Avatar asked Sep 18 '09 03:09

i_am_jorf


3 Answers

Had something similar for adding profiling code using Macros in VS, here's the code (this also groups everything under a single "undo" command and lists all of the changes in its own output window)

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

P.S Remember to change the "Const ToInsert As String = ..." to the code you actually want to be inserted

like image 102
Grant Peters Avatar answered Nov 01 '22 01:11

Grant Peters


Since you're using Visual C++, and it seems you only need the name of the function called, it might be possible to automate this further using the following command-line switches to cl.exe:

  • /Gh: Enable _penter function call
  • /GH: Enable _pexit function call

Basically, providing these switches means that the compiler will automatically inject calls to functions named _penter() and _pexit() whenever any function begins or ends. You can then provide a separately-compiled module that implements these two functions, which either (a) calls some helper library such as DbgHelp to determine the name of the called function, or (b) just grabs the return address from the stack and prints it verbatim -- afterwards, write a script to transform these addresses into function names by looking at e.g. the linker map file produced if you pass /link /MAP:mymapfile.txt to cl.exe.

Of course, you'll need to put your _penter() and _pexit() in a separate module with /Gh and /GH turned off to avoid infinite recursion! :)

like image 20
j_random_hacker Avatar answered Nov 01 '22 03:11

j_random_hacker


I did it some years ago in VS.
Regex will help you.
BTW, it not nesasary to insert different string. You can add the same string like:


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

EDIT

something like this regexp (valid for VS only):


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

You should extend the regexp depending of your needs.

like image 34
Dmitriy Avatar answered Nov 01 '22 03:11

Dmitriy