Скрипт для вставки входа в каждую функцию в проекте? - PullRequest
5 голосов
/ 18 сентября 2009

Я унаследовал довольно большую кодовую базу, 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, сценария Power Shell и т. Д. Если есть способ заставить VS делать это, отлично. Что бы ни работало. Не должен быть красивым, я не проверяю ничего из этого.

Кроме того, не обязательно получать все. Например. вложенные классы, реализации в заголовочных файлах и т. д.

Ответы [ 5 ]

4 голосов
/ 18 сентября 2009

Было что-то похожее для добавления кода профилирования с использованием макросов в 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

P.S Не забудьте заменить «Const ToInsert As String = ...» на код, который вы действительно хотите вставить

4 голосов
/ 18 сентября 2009

Поскольку вы используете Visual C ++ и вам кажется, что вам нужно только имя вызываемой функции, возможно, это можно еще автоматизировать, используя следующие переключатели командной строки для cl.exe:

  • /Gh: включить _penter вызов функции
  • /GH: включить _pexit вызов функции

По сути, предоставление этих ключей означает, что компилятор будет автоматически вводить вызовы функций с именами _penter() и _pexit() всякий раз, когда любая функция начинается или заканчивается. Затем вы можете предоставить отдельно скомпилированный модуль, который реализует эти две функции, который либо (а) вызывает некоторую вспомогательную библиотеку, такую ​​как DbgHelp , чтобы определить имя вызываемой функции, либо (б) просто получает возврат адрес из стека и печатает его дословно - потом напишите скрипт для преобразования этих адресов в имена функций, посмотрев, например, файл карты компоновщика, созданный, если вы передадите /link /MAP:mymapfile.txt в cl.exe.

Конечно, вам нужно поместить _penter() и _pexit() в отдельный модуль с отключенными /Gh и /GH, чтобы избежать бесконечной рекурсии! :)

2 голосов
/ 18 сентября 2009

Я сделал это несколько лет назад в VS.
Regex поможет вам.
Кстати, это не обязательно вставлять разные строки. Вы можете добавить ту же строку, например:


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

EDIT

что-то вроде этого регулярного выражения (действительно только для VS):


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

Вы должны расширить регулярное выражение в зависимости от ваших потребностей.

1 голос
/ 18 сентября 2009

Рассматривали ли вы запуск кода в отладчике и просто пошаговое выполнение всего приложения (или иным образом установка точки останова для кода, который вас интересует, и просто пошаговое выполнение этого)? Я считаю, что иногда это полезный метод, когда приходится сталкиваться с большой базой устаревшего кода, которую я не писал.

В качестве альтернативы, если вы компилируете в мире VS, попробуйте взглянуть на /Gh и /GH переключается на cl.exe. Похоже, они позволяют вам перехватывать функции входа / выхода и вызывать некоторые другие процедуры. Я никогда не использовал их раньше, но они, кажется, напрямую отвечают вашим потребностям.

1 голос
/ 18 сентября 2009

Профилировщик времени выполнения даст вам такую ​​информацию: он скажет, какие подпрограммы вызывались из каждой подпрограммы и сколько раз (но не в какой последовательности).

...