2009-07-30 10 views
10

J'écris un outil qui appelle DTrace pour tracer le programme spécifié par l'utilisateur. Si mon outil utilise dtrace -c pour exécuter le programme en tant que sous-processus de DTrace, non seulement je ne peux transmettre aucun argument au programme, mais le programme fonctionne avec tous les privilèges de DTrace, c'est-à-dire en tant que root (Je suis sur Mac OS X). Cela rend certaines choses qui devraient fonctionner et, évidemment, fait beaucoup de choses qui ne devraient pas fonctionner.Comment tracer un programme depuis ses débuts sans l'exécuter en tant que root

L'autre solution que je connaisse est de commencer moi-même le programme, mettre en pause en l'envoyant SIGSTOP, passez son PID à dtrace -p, continuer ensuite en l'envoyant SIGCONT. Le problème est que soit le programme s'exécute pendant quelques secondes sans être suivi pendant que DTrace rassemble les informations de symbole ou, si je dors pendant quelques secondes avant de continuer le processus, DTrace se plaint que objc<pid>:<class>:<method>:entry ne correspond à aucune sonde.

Existe-t-il un moyen de lancer le programme sous le compte de l'utilisateur, pas en tant que root, mais DTrace peut encore le suivre depuis le début?

Répondre

2

Créez un programme de lancement qui attendra un signal de quelque sorte (pas nécessairement un signal littéral, juste une indication qu'il est prêt), puis exec() votre cible. Maintenant dtrace -p le programme de lancement, et une fois que dtrace est en place, laissez le lanceur aller.

+1

Encore mieux: roulez tout en un. Créez un tuyau, une fourchette, demandez à l'enfant d'attendre sur le tuyau, dtrace -p CHILD_PID, écrivez dans le tuyau, l'enfant se réveille et appelle exec. – bstpierre

+0

Cela semble prometteur. Je ne sais pas si cela fonctionnera, cependant: Mon outil est écrit en Python, et il est conçu pour tracer des programmes Cocoa. Sans aucun Cocoa dans mon outil, je pense que j'obtiendrai toujours l'erreur que le fournisseur d'objc ne correspond à aucune sonde. Mais je vais essayer demain. –

3

Si l'autre réponse ne fonctionne pas pour vous, pouvez-vous exécuter le programme dans gdb, casser dans le main (ou même plus tôt), obtenir le pid, et démarrer le script? J'ai essayé cela dans le passé et cela a semblé fonctionner.

5

Quelque chose comme sudo dtruss -f sudo -u <original username> <command> a fonctionné pour moi, mais je me suis senti mal à ce sujet par la suite.

J'ai déposé un bug Radar à ce sujet et l'ai fait fermé en tant que copie de # 5108629.

3

Eh bien, cela est un peu vieux, mais pourquoi ne pas :-) ..

Je ne pense pas qu'il y ait un moyen de faire simplement de la ligne de commande, mais comme l'a suggéré, une simple application de lancement, comme le suivant, le feraient. La fixation manuelle pourrait bien entendu être également remplacée par quelques appels à libdtrace.

int main(int argc, char *argv[]) { 
    pid_t pid = fork(); 
    if(pid == 0) { 
     setuid(123); 
     seteuid(123); 
     ptrace(PT_TRACE_ME, 0, NULL, 0); 
     execl("/bin/ls", "/bin/ls", NULL); 
    } else if(pid > 0) { 
     int status; 
     wait(&status); 

     printf("Process %d started. Attach now, and click enter.\n", pid); 
     getchar(); 

     ptrace(PT_CONTINUE, pid, (caddr_t) 1, 0); 
    } 

    return 0; 
} 
3

Ce script prend le nom de l'exécutable (pour une application c'est CFBundleExecutable du info.plist) que vous souhaitez surveiller à DTrace en tant que paramètre (vous pouvez alors lancer l'application cible après ce script est en cours d'exécution) :

string gTarget;  /* the name of the target executable */ 

dtrace:::BEGIN 
{ 
    gTarget = $$1; /* get the target execname from 1st DTrace parameter */ 

    /* 
    * Note: DTrace's execname is limited to 15 characters so if $$1 has more 
    * than 15 characters the simple string comparison "($$1 == execname)" 
    * will fail. We work around this by copying the parameter passed in $$1 
    * to gTarget and truncating that to 15 characters. 
    */ 

    gTarget[15] = 0;  /* truncate to 15 bytes */ 
    gTargetPID = -1;  /* invalidate target pid */ 
} 

/* 
* capture target launch (success) 
*/ 
proc:::exec-success 
/
    gTarget == execname 
/
{ 
    gTargetPID = pid; 
} 

/* 
* detect when our target exits 
*/ 
syscall::*exit:entry 
/
    pid == gTargetPID 
/
{ 
    gTargetPID = -1;  /* invalidate target pid */ 
} 

/* 
* capture open arguments 
*/ 
syscall::open*:entry 
/
    ((pid == gTargetPID) || progenyof(gTargetPID)) 
/
{ 
    self->arg0 = arg0; 
    self->arg1 = arg1; 
} 

/* 
* track opens 
*/ 
syscall::open*:return 
/
    ((pid == gTargetPID) || progenyof(gTargetPID)) 
/
{ 
    this->op_kind = ((self->arg1 & O_ACCMODE) == O_RDONLY) ? "READ" : "WRITE"; 
    this->path0 = self->arg0 ? copyinstr(self->arg0) : "<nil>"; 

    printf("open for %s: <%s> #%d", 
     this->op_kind, 
     this->path0, 
     arg0); 
} 
+0

C'est très bien, sauf que, ce faisant, dtrace ne trouve aucun point de trace du fournisseur pid. Par exemple, j'obtiens une erreur: "description de sonde pid * :: confstr: return ne correspond à aucune sonde" – Droopycom

1

dtruss a l'option -n où vous pouvez spécifier le nom du processus que vous voulez tracer, sans le démarrer (crédit à cette dernière partie de la réponse de @ kenorb à https://stackoverflow.com/a/11706251/970301). Donc, quelque chose comme ce qui suit devrait le faire:

sudo dtruss -n "$program" 
$program 
0

Voir my answer sur question connexes « Comment obtenir DTrace pour exécuter la commande tracée avec non-root priviledges? » [sic] Essentiellement, vous pouvez démarrer un processus d'arrière-plan (non root) qui attend 1sec que DTrace démarre (désolé pour les conditions de concurrence) et insère le PID de ce processus.

sudo true && \ 
(sleep 1; cat /etc/hosts) &; \ 
sudo dtrace -n 'syscall:::entry /pid == $1/ {@[probefunc] = count();}' $! \ 
&& kill $! 

Explication complète dans une réponse liée.

Questions connexes