2011-10-18 10 views
10

Desidero scrivere un passaggio LLVM allo strumento per ogni accesso alla memoria. Ecco cosa sto cercando di fare.Strumentazione del codice C/C++ utilizzando LLVM

Dato un qualsiasi programma C/C++ (come quello indicato di seguito), sto provando ad inserire chiamate ad alcune funzioni, prima e dopo ogni istruzione che legge/scrive dalla/dalla memoria. Ad esempio si consideri il seguente programma C++ (Account.cpp)

#include <stdio.h> 

class Account { 
int balance; 

public: 
Account(int b) 
{ 
    balance = b; 
} 
~Account(){ } 

int read() 
{ 
    int r; 
    r = balance; 
    return r; 
} 

void deposit(int n) 
{ 
    balance = balance + n; 
} 

void withdraw(int n) 
{ 
    int r = read(); 
    balance = r - n; 
} 
}; 

int main() 
{ 
    Account* a = new Account(10); 
    a->deposit(1); 
    a->withdraw(2); 
    delete a; 
} 

Così, dopo la strumentazione mio programma dovrebbe essere simile:

#include <stdio.h> 

class Account 
{ 
    int balance; 

public: 
Account(int b) 
{ 
    balance = b; 
} 
~Account(){ } 

int read() 
{ 
    int r; 
    foo(); 
    r = balance; 
    foo(); 
    return r; 
} 

void deposit(int n) 
{ 
    foo(); 
    balance = balance + n; 
    foo(); 
} 

void withdraw(int n) 
{ 
    foo(); 
    int r = read(); 
    foo(); 
    foo(); 
    balance = r - n; 
    foo(); 
} 
}; 

int main() 
{ 
    Account* a = new Account(10); 
    a->deposit(1); 
    a->withdraw(2); 
    delete a; 
} 

dove foo() può essere qualsiasi funzione come ottenere l'ora di sistema corrente o incrementare un contatore .. così via.

Si prega di darmi esempi (codice sorgente, tutorial ecc.) E passaggi su come eseguirlo. Ho letto il tutorial su come rendere un LLVM Pass dato su http://llvm.org/docs/WritingAnLLVMPass.html, ma non sono riuscito a capire come scrivere un passaggio per il problema precedente.

+0

Bene, è possibile sovraccaricare l'operatore non solo per eseguire le funzioni di addizione, sottrazione, assegnazione, ma anche per chiamare la funzione personalizzata. – vishakvkt

+0

Perché vuoi aggiungere queste funzioni? Se si desidera eseguire il debug del programma, sono disponibili metodi migliori. – tune2fs

+0

Stavo per votare per chiudere questa domanda come duplicato di http: // StackOverflow.it/questions/7526550/instrumenting-c-c-codes-using-llvm (Voglio dire, guarda i titoli) e ho notato che eri l'autore di entrambi. Come ti aspetti che gli utenti di StackOverflow ti diano risposte questa volta che non ti hanno dato l'ultima volta (a meno di farlo per te, cosa che non succederà)? –

risposta

3

provare qualcosa di simile: (è necessario riempire gli spazi vuoti e far funzionare il ciclo iteratore nonostante il fatto che gli elementi vengono inseriti)

class ThePass : public llvm::BasicBlockPass { 
    public: 
    ThePass() : BasicBlockPass() {} 
    virtual bool runOnBasicBlock(llvm::BasicBlock &bb); 
}; 
bool ThePass::runOnBasicBlock(BasicBlock &bb) { 
    bool retval = false; 
    for (BasicBlock::iterator bbit = bb.begin(), bbie = bb.end(); bbit != bbie; 
    ++bbit) { // Make loop work given updates 
    Instruction *i = bbit; 

    CallInst * beforeCall = // INSERT THIS 
    beforeCall->insertBefore(i); 

    if (!i->isTerminator()) { 
     CallInst * afterCall = // INSERT THIS 
     afterCall->insertAfter(i); 
    } 
    } 
    return retval; 
} 

Spero che questo aiuti!

+0

Non dovresti farlo prima e dopo ogni istruzione, ma solo per 'store' e' load' per veri puntatori (non il riduttivo 'alloca's locale). –

+0

Si dovrebbe restituire true dalla funzione runOnBasicBlock per indicare che le istruzioni nel blocco di base sono state modificate. – ConfusedAboutCPP

8

Non ho molta familiarità con LLVM, ma sono un po 'più familiare con GCC (e il suo meccanismo di plugin), poiché sono l'autore principale di GCC MELT (un linguaggio di dominio di alto livello specifico per estendere GCC, che per il modo in cui potresti usare per il tuo problema). Quindi proverò a rispondere in termini generali.

Si deve innanzitutto sapere perché si desidera adattare un compilatore (o un analizzatore statico). È un obiettivo utile, ma presenta degli svantaggi (in particolare, la ridefinizione di alcuni operatori o altri costrutti nel programma C++).

Il punto principale quando si estende un compilatore (sia esso GCC o LLVM o qualcos'altro) è che molto probabilmente dovresti gestire tutta la sua rappresentazione interna (e probabilmente non puoi saltare parti di esso, a meno che tu non abbia un problema definito molto stretto). Per GCC significa gestire più di 100 tipi di Tree-s e quasi 20 tipi di Gimple-s: nel middle end GCC, gli alberi rappresentano gli operandi e le dichiarazioni, mentre i gesti rappresentano le istruzioni. Il vantaggio di questo approccio è che una volta fatto ciò, la tua estensione dovrebbe essere in grado di gestire qualsiasi software accettabile dal compilatore. Lo svantaggio è la complessità delle rappresentazioni interne dei compilatori (che è spiegabile dalla complessità delle definizioni dei linguaggi di origine C++ C & accettati dai compilatori e dalla complessità del codice macchina di destinazione che stanno generando, e dalla distanza crescente tra le lingue di destinazione &).

Così l'hacking un compilatore generale (sia esso GCC o LLVM), o un analizzatore statico (come Frama-C), è piuttosto un grande compito (più di un mese di lavoro, non pochi giorni). Per trattare solo con piccoli programmi C++ come si sta mostrando, non ne vale la pena. Ma vale sicuramente la pena se si intende occuparsi di basi software di grandi dimensioni.

saluti