Найти все операции ввода-вывода файлов в коде C # (возможно, путем статического анализа)

1

Есть ли способ найти весь код, который вызывает операции ввода-вывода, такие как File.WriteAllText, Request.Files["filename"].SaveAs("out") и т.д.?

На данный момент я могу просто grep для всех возможных распространенных способов чтения/записи файлов с чем-то вроде этого:

grep 'SaveAs' -I -r . -l | grep "\.cs"

Это неудовлетворительно, потому что я не могу думать о всех возможных способах чтения и записи файлов. Может быть, это может быть сделано путем отражения каким-то образом или путем анализа системных вызовов в скомпилированных двоичных файлах? Есть идеи?

EDIT: если метод A вызывает метод B и метод B вызывает метод C, а метод C выполняет операцию с файлом, было бы неплохо также идентифицировать этот код. Однако просто для упрощения проблемы было бы достаточно найти прямые вызовы для ввода-вывода.

  • 1
    @ Правильно, удалил мой комментарий.
  • 0
    Если метод A вызывает метод B, а метод B вызывает метод C, а метод C выполняет файловую операцию, нужен ли A в этом списке?
Теги:
code-analysis

3 ответа

1

Это зависит от того, сколько вы хотите сделать в углубленном анализе.
Если вы действительно серьезно относитесь к использованию всех вызовов метода System.IO, я бы предложил использовать NRefactory. Это внешний анализатор С#, который может анализировать код С# и генерируемое дерево синтаксиса и распознаватель кода.
Там хороший учебник об использовании его в проекте кода, который может помочь вам начать работу. Также в учебнике по проекту кода есть вспомогательные классы, которые позволяют загружать все решение и предоставлять вам код-преобразователь.
Если вам нужно больше образцов, вы можете найти их в своем блоге.

PS: Я попытаюсь продлить этот ответ с помощью образца кода за несколько часов.

1

Поэтому по прихоти я решил попробовать это с помощью Mono.Cecil.

Очень просто рекурсивно получить список всех методов, которые вызывает метод:

static IEnumerable<MethodDefinition> GetMethodsCalledInMethod(MethodDefinition md)
{
    if (md.Body == null)
    {
        return Enumerable.Empty<MethodDefinition>();
    }

    return md.Body.Instructions
        .Select(i => i.Operand)
        .OfType<MethodReference>()
        .Select(mr => mr.Resolve())
        .Where(mr => mr != null);
}

Вы можете получить все методы в сборке, которую хотите проверить:

var ad = AssemblyDefinition.ReadAssembly(typeof(ATypeInTheAssembly).Assembly.Location);
var allMethodsInAssembly = ad.Modules
    .SelectMany(m => m.Types)
    .SelectMany(t => t.Methods));

Затем вы можете пройти через это дерево вызовов метода, пока не найдете вызов метода, который выглядит как вызов ввода-вывода.

Func<MethodDefinition, bool> isFileOperation = md =>
    md.DeclaringType.Name == "FileStream";

Достаточно ли этого? Я не знаю. File.WriteAllText использует FileStream. Вы также предоставили пример SaveAs. Но каждый доступ к файлу проходит через FileStream? Я не могу сказать.

Этот подход также имеет проблемы:

  • Вам нужно искать рекурсивные вызовы, потому что иначе ваша рекурсия превратится в бесконечный цикл, и вы взорвите свой стек.
  • Это глупо. Когда я проанализировал простое консольное приложение под названием File.WriteAllLines я получил результат немедленно, но когда я попытался проанализировать сам анализатор, он потерялся в дереве.
  • Если вы столкнулись с интерфейсом или даже с виртуальным методом, вы не можете точно знать, что будет реализовано, поэтому вы не можете знать, будет ли он выполнять операцию ввода-вывода или нет!
  • 0
    Это очень хороший момент в отношении интерфейсов. Я полагаю, что проблема должна быть решена с точки зрения доступной среды выполнения (DLL / реализации).
1

Если вы планируете разрабатывать этот инструмент, вы можете начать здесь.

В статье описаны способы обнаружения зависимостей сборки и метода, поэтому вы можете найти все методы, которые вызывают примитивы ввода-вывода, такие как FileStream.Write.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню