2011-08-04 25 views
6

Piszę concolic engine dla Python za pomocą funkcji sys.settrace().Python śledzenie i warunkowe skoki

Głównym zadaniem podczas tego rodzaju wykonywania jest rejestrowanie ograniczeń zmiennych wejściowych. Ograniczenia są niczym innym jak warunkami instrukcji if, które tworzą dwie gałęzie (gałąź "wtedy" i "inna").

Po zakończeniu wykonywania, silnik wybiera ograniczenie i znajduje odpowiednie wartości dla danych wejściowych, tak że wykonanie będzie przebiegać w dół wzdłuż drugiego oddziału (przy wykonaniu x przechodzi do gałęzi 'wtedy', w wykonaniu x + 1 idzie wzdłuż gałęzi "else").

to jest mieć trochę kontekście, dlaczego robię to, co usiłuję zrobić ...

Łącząc settrace() i moduł dis, mogę zobaczyć kodu bajtowego każdej linii źródłowej, tuż zanim zostanie wykonany. W ten sposób mogę łatwo zapisać warunki, które pojawiają się podczas wykonywania.

Ale wtedy mam duży problem. Muszę wiedzieć, w którą stronę pójdzie, którą gałąź wykonała egzekucja. Więc jeśli mój kod jest coś takiego:

if x > a: 
    print x 
else: 
    print a 

w pewnym momencie moje śledzenia rzeczą będzie zobaczyć:

t: if x > 0: 

następnie interpreter Pythona wykona wtedy i skok (lub nie) gdzieś. I widzę:

t + 1: print x 

Więc jest instrukcja t + 1 w branży „następnie” lub w „inny” jeden? Należy pamiętać, że funkcja śledzenia widzi tylko jakiś kod bajtowy w bieżącym bloku.

Wiem dwa sposoby, aby to zrobić. Jednym z nich jest ocena warunku, aby zobaczyć dokładnie, czy jest to prawda czy fałsz. Działa to tylko wtedy, gdy nie ma żadnych skutków ubocznych.

Innym sposobem jest spróbuj spojrzeć i wskaźnik instrukcji pod numerem t + 1 i spróbuj zrozumieć, gdzie jesteśmy w kodzie. Tak właśnie używam, ale to bardzo delikatne, ponieważ na t + 1 mogłem znaleźć się gdzieś zupełnie innym (innym modułem, wbudowaną funkcją, itp.).

Na koniec pytanie, które mam, brzmi: czy istnieje sposób na uzyskanie samego Pythona lub z modułu C/rozszerzenie/cokolwiek, wynik ostatniego warunkowego skoku?

Alternatywnie, czy są dostępne bardziej szczegółowe opcje śledzenia? Coś jak wykonywanie kodu bajtowego po jednym kodzie naraz. Dzięki funkcji settrace() maksymalna rozdzielczość, jaką otrzymuję, to całe linie kodu źródłowego.

W najgorszym przypadku, myślę, że mogę zmodyfikować interpreter Pythona, aby ujawnić takie informacje, ale zostawiłbym to w ostateczności, z oczywistych powodów.

Odpowiedz

4

W końcu to właśnie zrobiłem. Zaimplementowałem oprzyrządowanie AST i działa całkiem nieźle.

Grając z AST, trzeba przenieść wszystkie wywołania funkcji (atrybuty również i subskrypcje, ze względu na getattr() i przyjaciół, z if warunki poprzez tworzenie zmiennych tymczasowych. Ponadto trzeba podzielić operatorów and i or.

następnie dodać wywołanie do własnej funkcji na początku każdej gałęzi, z logiczną parametru True dla następnie gałęzi i False dla inny gałęzi.

potem wrot e konwerter AST na źródło (jest gdzieś w sieci, ale nie działa w aktualnych wersjach Pythona).

Praca z AST jest bardzo łatwa i bardzo prosta. W efekcie wykonałem trzy transformacje, dodając również niektóre instrukcje import.

To jest pierwsze przejście, jako przykład. Dzieli jeśli warunki jeśli zawierają or lub and operatory:

class SplitBoolOpPass1(ast.NodeTransformer): 
    def visit_If(self, node): 
     while isinstance(node.test, ast.BoolOp): 
     new_node = ast.If(test=node.test.values.pop(), body=node.body, orelse=node.orelse) 
     if isinstance(node.test.op, ast.And): 
      if len(node.test.values) == 1: 
      node.test = node.test.values[0] 
      node.body = [new_node] 
     else: 
      if len(node.test.values) == 1: 
      node.test = node.test.values[0] 
      node.orelse = [new_node] 
     node = self.generic_visit(node) # recusion 
     return node 

Prawdopodobnie to nie jest bardzo przydatny do zastosowań pokrycie kodu, ponieważ bałagan z kodem sporo.

5

Brak informacji w obiekcie śledzenia o ostatniej branej wziętej.

To, co zrobiłem, aby zaimplementować pomiar zasięgu gałęzi w module coverage.py, ma na celu zachowanie rekordu dla każdej ramki stosu ostatniego wykonanego wiersza, a po następnym wywołaniu funkcji śledzenia, mogę zapisać parę numerów linii, które tworzą od-do łuku egzekucji.

O dokładniejszym śledzeniu: można oszukać interpreter Pythona i podać informacje o kodzie bajtowym. Mój eksperyment jest opisany tutaj: Wicked hack: Python bytecode tracing

Byłbym bardzo zainteresowany, aby zobaczyć, jak postępuje ta praca!

+0

Widziałem również, że istnieje możliwość zbudowania abstrakcyjnego drzewa składni z kodu, modyfikowania go, kompilowania i uruchamiania. Powinno być możliwe wykorzystanie tego i dodanie bardzo prostej oprzyrządowania (jak wywołanie funkcji na początku każdej gałęzi, która mówi, w którą stronę wypadł). –

+0

Tak, to także możliwość, nie taka, z którą eksperymentowałem. –