IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Delphi Discussion :

repérer la procédure qui s'est branchée sur la function [FAQ]


Sujet :

Delphi

  1. #21
    Membre Expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 55
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Par défaut
    Il y a un moyen de savoir quelle fonction a appelé une procédure ou une fonction, mais je vous préviens : "c'est chaud" et à mon humble avis plus un exercice de style qu'utile

    D'abord, un prérequis que voous devez savoir :
    Si j'ai défini ceci dans mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var UnPointeur:Pointer;
    procedure MaProcedure;
    begin
      ...
    end;
    Si j'écrit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ...
    UnPointeur:=@MaProcedure;
    ...
    Mon pointeur contient l'adresse du point d'entrée de ma procedure.

    Ensuite, lorsqu'une procédure (ou une fonction) est appelée, si l'on active la génération de ce que l'on appelle les "Stack Frames" ou cadre de pile (ce n'est pas toujours fait par Delphi, mais on peu le forcer avec une directive de compilation), le registre EBP du microprocesseur contient l'adresse d'une zone de mémoire que l'on appelle le cadre de pile.

    A l'adresse EBP+4, on obtient l'adresse d'où à été appelé la procédure.
    A partir de cette dernière adresse, on peut déterminer quelle procedure a pu appeler notre procedure (vous suivez ? )

    Il s'uffit d'avoir la liste des adresses que sont les points d'entrée des procédures suceptibles d'avoir appelé notre procedure et de détermine laquelle de nos candidates possède une adresse immédiatement inférieure au point d'entrée de notre procédure.

    Comme une démonstration vaux mieux que du code, et bien voilà (une fiche, deux boutons) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
     
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
    //renvoie l'adresse de la procedure la plus proche de 'UneAdresse"
    //parmis celle proposées en paramètre tableau
    function TrouverProcedureLaPlusProche(UnPointeur:Pointer;T:array of Pointer):Pointer;
    var i,NumElem:Integer;
    begin
      Result:=0;
      NumElem:=Length(T);
      if NumElem=0 then Exit;
      i:=0;
      //On va renvoyer la plus grande adresse, inférieure à l'adresse recherchée
      for i:=Low(T) to High(T) do
      if ((Cardinal(Result)<Cardinal(T[i])) and (Cardinal(T[i])<Cardinal(UnPointeur)))
      then Result:=T[i];
    end;
     
     
     
    procedure Bouton1; forward;
    procedure Bouton2; forward;
     
     
     {$W+} //<--- obligatoire et à mettre avant notre procedure !
           //Force la génération des cadres de piles
    procedure Affiche;
    var UnPointeur,UneAdresseDeProcedure: Pointer;
    begin
      //renvoie l'adresse pointée par EBP+4 qui est
      //l'adresse d'où à été appelée la procédure
      asm // Address := %ReturnAddress%;
        mov edx, [ebp+4];    // EDX := (EBP+4)^;
        mov UnPointeur, edx;    // Address := EDX;
      end;
      UneAdresseDeProcedure:=TrouverProcedureLaPlusProche(UnPointeur,[@bouton1,@bouton2]);
     
      if UneAdresseDeProcedure=@bouton1 then ShowMessage('Bouton1')
      else
      if UneAdresseDeProcedure=@bouton2 then ShowMessage('Bouton2')
      else
      ShowMessage('Pas trouvé qui m''a appelé');
    end;
     
    procedure Bouton1;
    begin
      affiche;
    end;
     
    procedure Bouton2;
    begin
      affiche;
    end;
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Bouton1;
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      Bouton2;
    end;
     
    initialization
     
    end.
    Si vous n'avez pas tout compris, sachez que je patauge encore un peu moi même dedans, donc...

  2. #22
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 496
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 496
    Par défaut
    salut

    c'est bien compliquer ton truc ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    procedure SimulatedMethod(Parent : TForm; Sender: TObject);
    begin
      Self.Caption := 'C'est cool de voir ça ';
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    var
      Event: TNotifyEvent;
    begin
      TMethod(Event).Code := @SimulatedMethod;
      TMethod(Event).Data := Self;
      Button1.OnClick := Event;
    end;
    ensuite y'a plus qu'a recuperer le sender

    @+ Phil

  3. #23
    Membre Expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 55
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Par défaut
    Citation Envoyé par anapurna
    salut

    c'est bien compliquer ton truc ?
    Oui, je suis d'accord avec ta solution, mais elle ne répond pas à la question, à savoir :

    repérer la procédure qui s'est branchée sur la function, et je précise si tu n'as pas tout lu sans passer quoique ce soit en paramètres.

  4. #24
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 496
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 496
    Par défaut
    salut

    j'ai relu tout le thread c'est marqué null par qu'il ne veut pas
    de paramettre ?

    @+ Phil

  5. #25
    Membre Expert
    Avatar de LadyWasky
    Femme Profil pro
    Inscrit en
    Juin 2004
    Messages
    2 932
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 55
    Localisation : France, Hauts de Seine (Île de France)

    Informations forums :
    Inscription : Juin 2004
    Messages : 2 932
    Par défaut
    Citation Envoyé par anapurna
    salut

    j'ai relu tout le thread c'est marqué null par qu'il ne veut pas
    de paramettre ?

    @+ Phil
    Disons que même la solution d'edam qui, convient le, est de loin la plus simple ne lui a pas convenu.
    Si tu as bien lu cette contrainte qui consiste à ne pas vouloir faire passer une quelconque information sur la procedure appelante en paramètre est implicite et bien là non ? ou alors j'ai rien compris, ni personne d'autre ici d'ailleurs...

    C'est pour ça que Laurent se demandait comment on pouvait faire avec la pile d'appel :
    Donc de connaitre l'appelant sans donner d'autres informations à l'appelé, cela ne vas pas être facile !
    Au moins je fourni une réponse qui utilise ce principe, voilà.
    Je ne vois pas, comment avec cette contrainte, j'aurais pu donner une réponse plus simple... (d'ailleurs j'ai prévenu que c'était ardu dans mon post)

  6. #26
    Membre éclairé
    Avatar de OutOfRange
    Profil pro
    Inscrit en
    Avril 2005
    Messages
    533
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2005
    Messages : 533
    Par défaut
    Merci à tous
    Bon finalement je vais utiliser le bon vieux paramètre passé à la function...
    Moi qui pensais avoir posé une question limite intéressante...
    Allez je coche résolu

  7. #27
    Rédacteur


    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    7 171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 7 171
    Billets dans le blog
    1
    Par défaut
    Trés bonne démonstration Waskol.
    Juste une remarque sur point qui me gratte un peu,
    Citation Envoyé par waskol
    Il suffit d'avoir la liste des adresses
    Je méfie de ce type de formulation en programmation (je précise c'est pas un défi ).

    Voir ce début de réponse (excellent site mais payant) dans un contexte différent :
    http://www.experts-exchange.com/Prog..._21201209.html

    La JCL/JVCL propose une solution( à vérifier si c'est la même problématique) en association avec un fichier .map (.exe + .map)
    Un article sur le sujet mais en Russe, bon ça permet de recenser les méthodes au sein de la JCL :
    http://www.rsdn.ru/article/Delphi/DelphiJCL.xml

    J'ai tjr eu ce code à analyser dans ma todo list mais je n'ai jamais eu le temps

    [edit]
    http://www.gnegg.ch/archives/jcldebug.html
    Une ancienne version du code source JClDebug qui permet d'analysert le fichier .map
    http://www.koders.com/delphi/fid2727...1CAB33527.aspx

  8. #28
    Membre Expert

    Homme Profil pro
    Inscrit en
    Mars 2004
    Messages
    897
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2004
    Messages : 897
    Par défaut
    Sur http://www.experts-exchange.com/Prog..._21201209.html

    Les réponses retenues à cette question sont les suivantes :

    Citation Envoyé par reponse1 andrewjb
    A few things...

    Project/Options/Linker says your application should be loaded at offset $00400000 (Image base). So you need to add $00400000 to the .code addresses in the map file.

    For reasons I've never understood, it also add an extra $00001000 offset... ?? Don't know why.

    The problem is that the access violation doesn't actally occur within your code.. It will (probably) occur in the system function that does string assignment, for example. Let's see.. The address I get for the AV is 004042b0. Subtract $00400000 to get 000042b0. Subtract the magic $00001000 to get 32b0. Look that up in the .code segment in the map file to get @LStrAsg which starts at 3288.... Which sounds like it's probably a string assignment function to me :-) (Phew!)

    So you need to unravel the stack when you get an AV. (Which is what Madshi does!) This means you need to compile with stack frames enabled, and you need to poke around within your application when it fails. When you get the AV you can open the CPU window (Delphi Pro) and look at the stack. (Typically at address 0012xxxx or so in the bottom right window).

    Register ESP is the stack pointer. Register EBP is typically the stack frame pointer. The address just below that on the stack is probably the return address i.e. what's called this function.. Then you can unravel.. but it's not trivial...
    Citation Envoyé par reponse2 Madshi
    As Andrew said, you need to substract the image base, which is most probably $400000. Furthermore you need to substract the "base of code" address, which is stored in the image nt headers of each module (exe/dll). It's usually $1000. You can check which value it has by using the freeware tool "PEProwse Pro". It's the field "Base Of Code" in the "Details" of the "Optional Header". You'll also find the image base address there.

    In your specific situation "Access Violation at address 004042A4" this seems to be the address "32a4" in the map file. As you've noticed yourself, this address is not inside of your own unit. The reason for this simply is, that your code "PTest.sBuffer := 'string'" calls a Delphi system function, which tries to assign the string. This Delphi system function then crashes.

    So, as you can see, having just the crashing address doesn't help much. Even if you find the name of the crashing function (which would be "System.@LStrAsg") you still have no clue what really happended.

    The only way to get really helping information is to do a full stack trace and get the unit/function name of each stack item. This way the 2nd item in that stack trace should be the line of code where you did the crashing string assignment.

  9. #29
    Rédacteur


    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    7 171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 7 171
    Billets dans le blog
    1
    Par défaut
    Allez un dernier lien pour la route :
    Advanced exception handler to find the exception source file name, call stack and all other related information :
    http://clevercomponents.com/articles.../exwatcher.asp

    Voir aussi le lien cité dans l'article :
    map2dbg, by Lucian Wischik

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Quelle ressource réseau est branchée sur tel port d'un commutateur
    Par darkwall_37 dans le forum Développement
    Réponses: 6
    Dernier message: 04/05/2013, 00h43
  2. Comment savoir si une clé USB est branchée sur le PC
    Par marwal dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 24/03/2010, 20h20
  3. Réponses: 0
    Dernier message: 15/07/2009, 16h11
  4. Réponses: 18
    Dernier message: 12/09/2007, 20h18
  5. Acceder a une base SQL qui n'est pas sur le meme serveur
    Par skyo dans le forum MS SQL Server
    Réponses: 4
    Dernier message: 20/01/2006, 11h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo