Hacking and more...
HaCkinG CulT
Lista Forumurilor Pe Tematici
Hacking and more... | Reguli | Inregistrare | Login

POZE HACKING AND MORE...

Nu sunteti logat.
Nou pe simpatie:
Briji pe Simpatie.ro
Femeie
24 ani
Bihor
cauta Barbat
24 - 48 ani
Hacking and more... / n00bs / Un tutorial pentru Morphine :) Moderat de Shocker
Autor
Mesaj Pagini: 1
Yakushiji
Grand Master

Inregistrat: acum 17 ani
Postari: 389
A little Morphine Tutorial


Morphine is a trace analysis system of Mercury programs. The goal of this tutorial is to give you an rough idea about what Morphine is. If you want a more exhaustive presentation, please refer to the Getting started and A debugging session with Morphine sections of the Morphine User Manual.

This tutorial consists in a Morphine trace analysis session of a Mercury version of the  program that solves the famous N-queens problem (queens.m).


   1. Running Morphine
   2. Morphine = Prolog +  current/1  +  next/0
   3. Toward an efficient extensible debugger: fget/1
   4. Toward efficient extensible monitors: collect/2 (also called foldt/2)
   5. If you want to know more about Morphine...

Running Morphine

    * First of all, the Mercury program (queens.m) has to be compiled with debugging options:

        $ mmc --trace deep queens.m

    This creates the executable program `queens'.
  #   Now, let's run Morphine!

          $ morphine

              ECLiPSe Constraint Logic Programming System [kernel]
              Version 4.1.0, Copyright IC-Parc and ICL, Sun Feb 21 18:37 1999

          [morphine]:

      Morphine is nothing more than an Eclipse interpreter  in which Morphine modules have been loaded.
    *   The first thing to do is to run the Mercury program under the control of Morphine. To do that, we use the command run/1.

          [morphine]: run(queens).

              Start debugging queens program.
              1: 1 [1] call main(state('<c_pointer>'), -)

          [morphine]:

      The two process, Morphine and the debugged program, are now running in coroutining. The execution stops at the first event and gives the control to Morphine which prints a trace line and waits for user queries. The trace line here simply says that the current event is the first one, it is the first goal called, the execution depth is 1, it is a call event and the current predicate is main/2. state('<c_pointer>') means that the first argument of the current predicate is a I/O state (C pointer); the '-' stands for non instantiated arguments.

Morphine = Prolog +  current/1  +  next/0
Only two primitives plus Prolog are enough to build a powerful trace analysis system. In this section, we are not concerned with efficiency; we will deal with efficiency issues in the next two sections. Let me first quickly show what those two primitives `current/1' and `next/0' are.

    *   The predicate `current/1' lets users retrieve any information relative to the current event. Each elementary piece of information is called an event attribute. Examples of event attributes are the event number, the execution number, the port, the predicate name, the list of its argument, etc. The whole list of event attributes (and their aliases) can be obtained via the command `list_attribute_aliases/0' (or `laa/0', for short).

       
       

      For example, in order to retrieve the current port, the Morphine query...
       
            [morphine]: current(port = Port).

                Port = call          More? (

      ... unifies the logical variable Port with the port attribute of the current event.
       
    *   The second primitive we need is the one to make the execution move one event forward. This primitive is called `next/0'; `next/0' makes the execution proceeds until the next execution event  is reached and then prints a trace line (by calling the `print_event/0' predicate).

       
            [morphine]: next.

                2: 2 [2] call data(-)

    * For example, in order to dump the whole execution trace, we can take advantage of the repeat-fail mechanism of Prolog and type the following query:

       
            [morphine]: next, fail.

                3: 2 [2] exit data([1, 2, 3, 4, 5])
                4: 3 [2] call queen([1, 2, 3, 4, 5], -)
                5: 4 [3] call qperm([1, 2, 3, 4, 5], -)
                6: 4 [3] switch qperm([1, 2, 3, 4, 5], -)
                7: 5 [4] call qdelete(-, [1, 2, 3, 4, 5], -)
                8: 5 [4] disj qdelete(-, [1, 2, 3, 4, 5], -)
                9: 5 [4] exit qdelete(1, [1, 2, 3, 4, 5], [2, 3, 4, 5])
                10: 6 [4] call qperm([2, 3, 4, 5], -)
                11: 6 [4] switch qperm([2, 3, 4, 5], -)
                ...

    * Now, we can combine those commands in order to move to given points of the execution trace. But before, we run again the lastly run program (rerun/0):

       
            [morphine]: rerun.

                Start debugging queens program.
                1: 1 [1] call main(state('<<c_pointer>>'), -)

            [morphine]: next_np, current(name = qperm and port = exit), print_event.

                32: 14 [8] exit qperm([], [])

      That query makes the execution move until the first success of predicate `qperm', and then prints a trace line. The predicate `next_np/0' is the same one as `next/0', except it does not print a trace line (i.e., it does not call `print_event/0'; "_np" stands for "no print".
    *   With those two primitives, we can also write (trace analysis) programs. For example, here is how to implement a simple monitor that count the number of goal calls.

       
            [morphine]: [user].
               count_call(N)  :-
                    ( next_np ->
                            ( current(port = call) -> N1 is N + 1 ; N1 is N),
                            count_call(N1)
                    ;
                            % the end of the execution is reached
                            printf("The number of goals called  is %w.n", N)
                    ).

                    ^D
             
      Then, we can use that new command to count the number of calls in the queens program:
             
            [morphine]: rerun, count_call(1).

                Start debugging queens program.
                  1: 1 [1] call main(state('<<c_pointer>>'), -)
                [1, 3, 5, 2, 4]
                last event is reached
                End of connection with the traced program
                The number of goals called is 146.

Toward an efficient extensible debugger: fget/1
The problem with the previously described trace analysis system is the efficiency. Each time you need to move one event forward in the execution, you send a message to the debuggee process, receive a response from it, and then interpret it. This message exchange together with the Operating System level context switches it induces can cost a lot, especially to process a million of execution events, which is quite common.

In order to avoid those inter process communications, we introduced a new primitive, called `fget/1', that filters out execution events in the debuggee process. Actually, fget(ListOf Attributes) does exactly the same job as the sequence of goals next_np, current(ListOfAttributes) except it requires only one communication between Morphine and the debuggee (instead of 32, as it required in the previous example).

    * Hence, we can move to the first success of `qperm' like this:

            [morphine]: rerun, fget(name = qperm and port = exit).

                Start debugging queens program.
                 1: 1 [1] call main(state('<<c_pointer>>'), -)
                 32:        14 [8] exit qperm([], [])

    * Using fget/1 and current/1, you can implement all classical debugging commands as efficiently as their hard coded  counterparts (with an handful of communications between the two processes).

    *   The count_call/1 monitor can now be implemented using fget like this:
             
            [morphine]: [user].
            count_call(N) :-
                    ( fget_np(port = call) ->
                            N1 is N + 1,
                            count_call(N1)
                    ;
                            % the end of the execution is reached
                            printf("The number of goal call is %w.n", N)
                    ).

                    ^D
             
    * The purpose of fget /1 is really the efficiency, but note that it also makes Morphine queries and programs more compact.


Toward efficient extensible monitors: collect/2 (also called foldt/2)
The primitives fget/1 and current/1 are enough to efficiently implement most debugging commands. But when it comes to write monitors (a monitor is a program that analyses and extracts run time information about a whole program execution, like the count_call/1 program we have just given) then it turns out that it is not satisfactory. For example, the fget/1 version of the monitor that counts the number of calls may require  about 1/5 of the communications the next/0 version needs; but if we want to monitor a 5 million events execution, this means that it still requires about one million message exchanges. The idea is that for monitoring a whole program execution, it is not necessary to interact with the program as in debugging, and thus you do not want to pay the price induced by the coroutining. With collect/2, we will be able to do it in one pass.

    *   The command collect/2 is a kind of foldl/4 meta-predicate that operates on a list of events and which is directly plugged into the Mercury trace system. As with foldl/4, before calling collect/2 you need to initialize an accumulator (initialize/1) and to define a filter (filter/4) that will be applied to each element of the list of events and to the accumulator; collect/2 outputs the final value of the accumulator in its second argument. The initialization and the filtering predicates are defined in Mercury.

    *   For example, to implement the monitor that counts the number of goals called using collect/2, you just need to write the following code in a file named, let's say, count_call:
            "
            :- import_module int.
            :- type collected_type == int.

            initialize(0).

            filter(Event, Cpt0, Cpt, continue) :-
                  ( if port(Event) = call then Cpt = Cpt0+1 else Cpt = Cpt0 ).
            "
             
             
      Then, you just need to call collect/2 with the name of that file as its first argument; the result of the monitoring activity will be bounded with its second argument.
       

          [morphine]: rerun, collect(count_call, Result).

              Start debugging queens program.
              1: 1 [1] call main(state('<<c_pointer>>'), -)

              [1, 3, 5, 2, 4]
              End of connection with the traced program

              Result = 146

      Actually, the count_call file is used to generated a Mercury module that is compiled, dynamically linked with the current Mercury program execution. Then the resulting program is executed. When the end of the execution is reached, the result is send to Morphine.

    * Of course, you can write more useful monitors; see for example the control_flow Scenario which illustrates the use of collect/2 to perform a control flow graph (which produces for queens.m the following postscript file: queens.cfg.ps), a dynamic call graph (queens.cdg.ps), or a proof tree (queens.pt.ps)  with only a few lines of Mercury code. It also illustrates the advantage of having a full programming language (namely Prolog) to post process the result of the monitoring activity.

    * For more details about collect/2, you might want to have a look at the TPLP 2002 paper which full ref is given on my web page.


If you want to know more about Morphine...

  # You can have a look at the HTML version the Morphine User and Reference manual. You can also play with the on-line help features, i.e., using the `man/1' and the `apropos/1' commands.

        [morphine]: man apropos.
         

            apropos(Name) {a}
            Command which displays all the commands, primitives, procedures, parameters, or
            types for which Name is a substring of.
            Example:

                [morphine]: apropos man.

                    man
                    manual
                    latex_manual
                    window_command
                    opium_command_in_module
                    print_man

            Name : atom
            type of command : opium

 


_______________________________________


pus acum 17 ani
   
Pagini: 1  

Mergi la