emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: extremely slow font-lock-mode


From: Martin Pohlack
Subject: Re: extremely slow font-lock-mode
Date: Wed, 12 Dec 2007 01:21:21 +0100
User-agent: Thunderbird 2.0.0.9 (X11/20071115)

martin rudalics wrote:
>> I'm experiencing very slow syntax highlighting with emacs-23.0.60
>> (GTK+ Version 2.12.1)a.
>>
>> Simply scrolling in source files takes incredibly long (~ 1 second for
>> scolling one line backwards).  This is an a dual-core 2 GHZ machine
>> ...
>> I tried to play with all font-lock and jit-lock customization options
>> but only turning font-lock off seemed to make a difference.
>>
>> I tried to use elp to instrument relevant packages (mwheel, font-lock,
>> jit-lock, mouse, lazy-lock, fast-lock).  After some scrolling (~ 20
>> seconds, with pauses, mouse wheel, pg-keys and scrollbar) I get
>> the following results:
> 
> I suppose you did not turn on lazy-lock or fast-lock, hence these two
> are hardly relevant here.

No, I did it just for completeness reasons.

[...]

>> I also noticed, that scrolling forward seems to go considerably faster
>> than scrolling backwards (maybe this has to do with syntax-driven
>> jit-lock?).
> 
> Usually it's slower only if you scroll back into _not yet fontified_
> areas.

That is what I thought too, but I scrolled through the whole file
several times before that.  Maybe caching the font properties is
broken?

>> The results are twofold for me:
>>
>> First, I don't seem to be using elp right or it is the wrong tool.
> 
> You did use it right but it's probably not the right tool for finding
> the cause of the problem.

Ok, I tried some oprofiling, which of course can only catch native
functions.  The result is still interesting:

  $ opreport -l image:/usr/bin/emacs-23-unicode
  
  Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit \
    mask of 0x00 (Unhalted core cycles) count 240000
  samples  %        symbol name
  48798    32.2244  lookup_char_property
  37434    24.7200  next_interval
  24605    16.2482  get_property_and_range
  12083     7.9792  previous_interval
  10370     6.8480  Fcdr
  9619      6.3520  Fcar
  6904      4.5591  textget
  355       0.2344  assq_no_quit
  170       0.1123  find_interval
  ...

>> Ideally, I would like to use a /sampling/ profiler for emacs.
>>  - Is there such a thing that can look into running lisp code?
>>  - How should I be using elp differently for getting meaningful
>>    results?
>>
>> Second, font-lock-mode is currently unusable for me.  I started emacs
>> with -q, so I don't think my local tweak are to blame.  emacs-22 on my
>> local machine is incredibly fast for the same file (also started with
>> -q).
>>  - What might be to blame here?
> 
> First of all we have to find out whether this is mode specific.
> Often a badly constructed regexp can cause such problems.  Did you
> try with different major modes?

Probably not.  I see this in tex, c++, c, diff, asm, and text mode.
Basically everywhere with larger files.  File size seems to matter,
also direction of scrolling and position in the file.  Also cursor
placement using the mouse (no scrolling involved, just clicking
somewhere in the visible buffer area) incurs large delays (~ 1 second).

> Next you should try if it's buffer specific.

I have many source-code files here, which behave similar.  I attached two.

> Can you post the file showing the bug (maybe it's only a small part of
> the file that exhibits the problem).

Seems to be everywhere in the files.

> 
>>  - Do you need more details on any aspect of my machine configuration?
> 
> Please send these details just as if you reported a bug.

I also tried with the current CVS version for the same branch and
built it myself with similar results.

The output below is from calling report-emacs-bug in the buffer of one
such problematic file.


In GNU Emacs 23.0.60.1 (i686-pc-linux-gnu, GTK+ Version 2.12.1)
 of 2007-12-11 on monat
Windowing system distributor `The X.Org Foundation', version 11.0.10300000
configured using `configure  '--prefix=/usr' '--host=i686-pc-linux-gnu' 
'--mandir=/usr/share/man' '--infodir=/usr/share/info' '--datadir=/usr/share' 
'--sysconfdir=/etc' '--localstatedir=/var/lib' 
'--program-suffix=-emacs-23-unicode' 
'--infodir=/usr/share/info/emacs-23-unicode' '--without-carbon' '--with-sound' 
'--with-x' '--with-toolkit-scroll-bars' '--enable-font-backend' 
'--with-freetype' '--with-xft' '--with-jpeg' '--with-tiff' '--with-gif' 
'--with-png' '--with-xpm' '--with-rsvg' '--with-x-toolkit=gtk' 
'--without-hesiod' '--with-kerberos' '--with-kerberos5' '--without-gpm' 
'--build=i686-pc-linux-gnu' 'build_alias=i686-pc-linux-gnu' 
'host_alias=i686-pc-linux-gnu' 'CFLAGS=-march=prescott -pipe -O2''

Important settings:
  value of $LC_ALL: nil
  value of $LC_COLLATE: POSIX
  value of $LC_CTYPE: de_DE.UTF-8
  value of $LC_MESSAGES: en_US.UTF-8
  value of $LC_MONETARY: de_DE.UTF-8
  value of $LC_NUMERIC: nil
  value of $LC_TIME: de_DE.UTF-8
  value of $LANG: en_US.UTF-8
  value of $XMODIFIERS: nil
  locale-coding-system: utf-8-unix
  default-enable-multibyte-characters: t

Major mode: C/l

Minor modes in effect:
  shell-dirtrack-mode: t
  flyspell-mode: t
  desktop-save-mode: t
  pc-selection+-mode: t
  show-paren-mode: t
  pc-selection-mode: t
  mouse-sel-mode: t
  tooltip-mode: t
  mouse-wheel-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  global-auto-composition-mode: t
  auto-composition-mode: t
  auto-compression-mode: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t
  abbrev-mode: t
/*
 * This file contains a 'gate_init' initialization table
 * to initialize the x86 processor trap vectors to default entrypoints.
 * These entrypoints simply push a standard trap_state frame
 * and jump to the 'trap_handler' routine.
 */

#include "config_tcbsize.h"
#include "config_gdt.h"
#include "globalconfig.h"
#include "idt_init.h"
#include "low_level.h"
#include "shortcut.h"
#include "tcboffset.h"

#include "asm.h"

/* We make the trap handler an interrupt gate, because for debugging
   purposes, we don't want any interrupts to occur until they're
   explicitly enabled in the base_trap_handler (usually
   Thread::handle_slow_trap). */

/*
 * No error code.  Clear error code and push trap number.
 */
#define EXCEPTION(n,name)                                       \
        GATE_ENTRY(n,entry_##name,ACC_PL_K | ACC_INTR_GATE)     ;\
        .p2align 3                                              ;\
entry_##name:                                                   ;\
        pushl   $(0)                                            ;\
        pushl   $(n)                                            ;\
        pusha                                                   ;\
        jmp     _slowtraps

/*
 * User-accessible exception.  Otherwise, same as above.
 */
#define EXCEP_USR(n,name)                                       \
        GATE_ENTRY(n,entry_##name,ACC_PL_U | ACC_INTR_GATE)     ;\
        .p2align 3                                              ;\
entry_##name:                                                   ;\
        pushl   $(0)                                            ;\
        pushl   $(n)                                            ;\
        pusha                                                   ;\
        jmp     _slowtraps

/*
 * Error code has been pushed.  Just push trap number.
 */
#define EXCEP_ERR(n,name)                                       \
        GATE_ENTRY(n,entry_##name,ACC_PL_K | ACC_INTR_GATE)     ;\
        .p2align 3                                              ;\
entry_##name:                                                   ;\
        pushl   $(n)                                            ;\
        pusha                                                   ;\
        jmp     _slowtraps

.macro  PRE_ALIEN_IPC
        btrl    $17, KSEG OFS__THREAD__STATE (%ebx) /* Thread_dis_alien */
        jc      1f
        RESTORE_STATE
        sub     $2, 4(%esp)                /* Correct EIP to point to insn */
        popl    %eax
        pushl   $(0x30 << 3 | 2)
        pushl   $(0xd)
        pusha
        jmp     _slowtraps

1:      /* do alien IPC and raise a trap afterwards */  
        RESET_THREAD_CANCEL_AT %ebx
.endm

.macro  POST_ALIEN_IPC
        CHECK_SANITY $3                 /* scratches ecx */
        RESET_USER_SEGMENTS $3,in_cli   /* scratches ecx */
        RESTORE_STATE_AFTER_IPC
        popl    %eax
        pushl   $(0x30 << 3 | 6)
        pushl   $(0xd)
        pusha
        jmp     _slowtraps
.endm

#ifdef CONFIG_KERN_LIB_PAGE
.macro  ROLLBACK_IP addr
        /* scratches flags
         * requires address argument as macro parameter
         */
        cmpl    $VAL__MEM_LAYOUT__KERN_LIB_BASE,\addr
        jl      1f
        cmpl    $VAL__MEM_LAYOUT__KERN_LIB_END,\addr
        jge     1f
        andl    $0xFFFFFFC0,\addr                /* roll back eip (64 bytes) */
#if defined(CONFIG_JDB_ACCOUNTING)
        /* update counter Kern_cnt_lib_page_hit */
        incl    VAL__MEM_LAYOUT__TBUF_STATUS_PAGE + \
        OFS__TBUF_STATUS__KERNCNTS + 44
#endif
1:
.endm
#endif

/* fixme: we could merge the comparisons to speedup the common case */
#ifdef CONFIG_ROLLFORWARD
.macro  ROLLFORWARD_IP addr
        /* scratches flags
         * requires address argument as macro parameter
         */
        // fixme: opcode likely jump
        cmpl    $VAL__MEM_LAYOUT__ROLLFORWARD_BASE,\addr
        jl      1f
        // fixme: opcode likely jump
        cmpl    $VAL__MEM_LAYOUT__ROLLFORWARD_END,\addr
        jge     1f
        orl     $0x1000,\addr  /* add 4k to IP by OR, addr must be 8K aligned */
        //movl  $0x01,$VAL__CONTEXT_RF_ACTIVE  // fixme: real address
1:
.endm

.macro  ROLLFORWARD_PANIC addr reason
        // fixme: opcode likely jump
        cmpl    $VAL__MEM_LAYOUT__ROLLFORWARD_BASE,\addr
        jl      1f
        // fixme: opcode likely jump
        cmpl    $VAL__MEM_LAYOUT__ROLLFORWARD_END,\addr
        jge     1f
        movl    \reason,%eax
        call    rollforward_panic
1:
.endm

#endif

GATE_INITTAB_BEGIN(idt_init_table)

EXCEPTION(0x00,vec00_zero_div)
#ifdef CONFIG_PF_UX
EXCEPTION(0x01,vec01_debug)
#else
/* IA32 has to handle breakpoint exceptions if occured exactly at 
   entry_sys_fast_ipc -- see ia32/entry-ia32.S */
GATE_ENTRY(0x01,entry_vec01_debug,ACC_PL_K | ACC_INTR_GATE)
#endif
/* XXX IA32 has to handle NMI occured exactly at entry_sys_fast_ipc */
EXCEP_USR(0x02,vec02_nmi)
EXCEP_USR(0x03,vec03_breakpoint)
EXCEP_USR(0x04,vec04_into)
EXCEP_USR(0x05,vec05_bounds)
EXCEPTION(0x06,vec06_invop)
/* EXCEPTION(0x07,nofpu) */
#ifdef CONFIG_PF_UX
EXCEP_ERR(0x08,vec08_dbl_fault)
#else
GATE_ENTRY(0x08, GDT_TSS_DBF, ACC_PL_K | ACC_TASK_GATE)
#endif
EXCEPTION(0x09,vec09_fpu_ovfl)
/* EXCEP_ERR(0x0a,vec0a_inv_tss) */
EXCEP_ERR(0x0b,vec0b_segnp)
EXCEP_ERR(0x0c,vec0c_stack_fault)
EXCEP_ERR(0x0d,vec0d_gen_prot)
/* EXCEP_ERR(0x0e,vec0e_page_fault) */
/* EXCEPTION(0x0f,vec0f_trap_0f) */
EXCEPTION(0x10,vec10_fpu_err)
EXCEP_ERR(0x11,vec11_align)
EXCEPTION(0x12,vec12_mcheck)
EXCEPTION(0x13,vec13_simd_err)


        .p2align 4
        .type   slowtraps,@function
        .globl  slowtraps
        /* We have to introduce the label _slowtraps besides the label
           slowtraps to achive that jmps from exception entry points
           are optimized to two-byte jmps. The label slowtraps is visible
           from outside. */
_slowtraps:
slowtraps:
#ifdef CONFIG_PF_UX
# define REG_GS CPU_GS
#else
# define REG_GS %gs
#endif

        pushl   %fs             /* we save the segment regs in the trap   */
        pushl   REG_GS          /* state, but we do not restore them.  We */
        pushl   %ds             /* rather reload them using               */
        pushl   %es             /* RESET_{KERNEL,USER}_SEGMENTS           */

        /* Load the kernel's segment registers.  */
        RESET_KERNEL_SEGMENTS_FORCE_DS_ES /* scratches ecx, edx */
        
#ifdef CONFIG_KERN_LIB_PAGE
        ROLLBACK_IP 56(%esp)    // fixme: address correct? (-> trap_state.cpp)
#endif
#ifdef CONFIG_ROLLFORWARD
        ROLLFORWARD_PANIC 56(%esp), $0
#endif
        /* Note: we do not use RESET_THREAD_CANCEL_* here as that is
           needed only when an I/O-page-fault IPC is sent and when the
           thread is killed. Resetting Thread_cancel here could
           be harmful when using this trap handler in debugging.
           Instead, we clear this flag in Thread::handle_slow_trap()
           just before sending the IPC message or before killing the
           thread.  That's OK, because it is still atomic -- we never
           enable IRQs (sti) before that point. */

        movl    %esp,%eax               /* ARG1: address of trap_state */
#ifndef CONFIG_NO_FRAME_PTR
        pushl   56(%esp)                /* create artificial stack frame */
        pushl   %ebp
# ifndef CONFIG_PROFILE
        leal    (%esp),%ebp
# else
        xorl    %ebp,%ebp
# endif
#endif

        /* Call the C handler function if one has been installed.  */
        movl    BASE_TRAP_HANDLER, %ecx
        orl     %ecx,%ecx
        jz      unexpected_trap_pop
        call    *%ecx
in_slowtrap:

#ifndef CONFIG_NO_FRAME_PTR
        leal    8(%esp),%esp
#endif

        /* If the handler function returned zero (success),
           then resume execution as if the trap never happened.
           Otherwise, just panic.  */
        orl     %eax,%eax
        jnz     unexpected_trap

        CHECK_SANITY 60(%esp)           /* scratches ecx */
        RESET_USER_SEGMENTS 60(%esp),in_cli  /* scratches ecx */

        addl    $4*2,%esp               /* Pop ds, es segment registers */
        popl    REG_GS
        popl    %fs                     /* Restore segment registers */
        popa
        addl    $4*2,%esp               /* Pop trap number and error code */
        iret

unexpected_trap_pop:
#ifndef CONFIG_NO_FRAME_PTR
        leal    8(%esp), %esp
#endif

unexpected_trap:
        movw    %ss,%ax
        movw    %ax,%ds
        movw    %ax,%es
        movl    %esp,%eax
        call    trap_dump_panic

GATE_ENTRY(0x0e,entry_vec0e_page_fault,ACC_PL_K | ACC_INTR_GATE)

/* we must save %cr2 before we can be preempted -- therefore we're an
   interrupt gate (invoked with interrupts turned off).  Also, we
   don't turn them on again here, but only after checking for
   page-ins from the global page directory in thread_page_fault().
   XXX: If you make changes to stack layout here, fix thread_page_fault */

/* XXX slow version - sets up nice stack frame for debugger */

        .p2align 4
        .type   entry_vec0e_page_fault,@function
entry_vec0e_page_fault:
        pushl   %eax                    /* save regs modifiable by C funcs */
        pushl   %ecx
        pushl   %edx
        RESET_KERNEL_SEGMENTS           /* scratches ecx, edx */
        movl    12(%esp),%edx           /* save error code in ARG2 ... */
        movl    PAGE_FAULT_ADDR,%eax    /* save page fault address in ARG1 */

/* We must reset the cancel flag here atomically 
   if we are entering fresh from user mode and an IPC might occur. 
   NOTE: We cannot test the user-mode bit in the error code because
   it will flag "kernel" in case an I/O-bitmap page is not mapped
   during an I/O access. */

        movl    20(%esp),%ecx   /* get CS from stack */
        andb    $3,%cl          /* retrieve current privilege level (CPL) */
        jz      1f              /* CPL == 0 -> kernel, skip resetting state */
        ESP_TO_TCB_AT %ecx
        RESET_THREAD_CANCEL_AT %ecx
1:      movl    %ebp,12(%esp)   /* save frame pointer */
        leal    12(%esp),%ebp   /* load new frame pointer */
#ifdef CONFIG_PROFILE
        call    mcount
#endif
        pushl   %eax            /* save pf address */
        pushl   %edx            /* save error code */
        leal    24(%esp),%ecx   /* ARG5: ptr to Return_frame */
        pushl   %ecx
        pushl   36(%esp)        /* ARG4: eflags */
#ifdef CONFIG_KERN_LIB_PAGE
        ROLLBACK_IP 32(%esp)
#endif
#ifdef CONFIG_ROLLFORWARD
        ROLLFORWARD_PANIC 32(%esp), $1
#endif
        movl    32(%esp),%ecx   /* ARG3: eip */
        call    thread_page_fault
in_page_fault:

#ifdef CONFIG_SMALL_SPACES 
        movl    32(%esp),%ecx                   /* check if page fault was in */
        andl    $0xFFFFFF00,%ecx                /* sysexit trampoline page */
        cmpl    $VAL__MEM_LAYOUT__SMAS_TRAMPOLINE,%ecx
        jz      page_fault_in_trampoline
before_iret_page_fault:
#endif
        orl     %eax,%eax
        jz      bad_page_fault
        lea     16(%esp),%esp
        CHECK_SANITY 20(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 20(%esp),no_cli     /* scratches ecx */
        popl    %edx
        popl    %ecx
        popl    %eax
        popl    %ebp
        iret

/* If code or stack from a small address space are not yet mapped in the
   current page directory we might get a page fault on return from the
   trampoline page. In this case we cannot return to the trampoline page
   after handling the fault because we are already in user mode (with
   segment limits below kernel space) while the trampoline code is located
   in kernel data space. So instead we change ESP and EIP to point to the
   address the trampoline wanted to return to and do the normal IRET. */
#ifdef CONFIG_SMALL_SPACES
page_fault_in_trampoline:
        /* don't interrupt us here */
        cli
        /* make sure that we use the user-level segment for accessing the
           user-level eip */
        movw    $(GDT_DATA_USER|SEL_PL_U), %cx
        movl    %ecx,%ds
        movl    44(%esp),%ecx                   /* user esp */
        movl    (%ecx),%edx
        movl    %edx,32(%esp)
        addl    $16,%ecx
        movl    %ecx,44(%esp)
        orl     %eax,%eax
        jz      bad_page_fault
        leal    16(%esp),%esp
        CHECK_SANITY 20(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 20(%esp),in_cli     /* scratches ecx */
        popl    %edx
        popl    %ecx
        popl    %eax
        popl    %ebp
        iret
#endif

/* recover from a bad page fault by invoking the slow_trap handler */
        .p2align 4
bad_page_fault:
        cli
        addl    $8,%esp         /* pop ARG4 and ARG5 */
        movl    (%ebp),%eax     /* old ebp */
        popl    (%ebp)          /* error code */
        popl    %edx            /* page fault address */
        movl    %eax,%ebp

        /* we have on stack: error code, eax, ecx, edx
           move registers down to make room for trap number */
        subl    $4,%esp
        movl    4(%esp),%eax
        movl    %eax,(%esp)
        movl    8(%esp),%eax
        movl    %eax,4(%esp)
        movl    12(%esp),%eax
        movl    %eax,8(%esp)
        movl    $0x0e,12(%esp)  /* error code */
        pushl   %ebx            /* rest of trap state */
        pushl   %edx            /* page fault address */
        pushl   %ebp
        pushl   %esi
        pushl   %edi
        jmp     slowtraps


/* FPU not available in this context. */
GATE_ENTRY(0x07,entry_vec07_fpu_unavail, ACC_PL_K | ACC_INTR_GATE)

/* do all of this with disabled interrupts */
        .p2align 4
        .type   entry_vec07_fpu_unavail,@function
entry_vec07_fpu_unavail:
        pushl   %eax
        pushl   %ecx
        pushl   %edx
        RESET_KERNEL_SEGMENTS                   /* scratches ecx, edx */
        call    thread_handle_fputrap
in_handle_fputrap:
        CHECK_SANITY 16(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 16(%esp),in_cli     /* scratches ecx */
        test    %eax, %eax
        jz      real_fpu_exception
        popl    %edx
        popl    %ecx
        popl    %eax
        iret 
real_fpu_exception:
        popl    %edx
        popl    %ecx
        popl    %eax
        pushl   $(0)
        pushl   $(7)
        pusha                                                   
        jmp     _slowtraps


/* timer interrupt */
#ifdef CONFIG_SCHED_PIT
GATE_ENTRY(0x20,entry_int_timer,ACC_PL_K | ACC_INTR_GATE)
#endif
#ifdef CONFIG_SCHED_RTC
GATE_ENTRY(0x28,entry_int_timer,ACC_PL_K | ACC_INTR_GATE)
#endif
#ifdef CONFIG_SCHED_APIC
GATE_ENTRY(0x3d,entry_int_timer,ACC_PL_K | ACC_INTR_GATE)
#endif

        .p2align 4
        .globl  entry_int_timer
entry_int_timer:
#ifdef CONFIG_KERN_LIB_PAGE
        ROLLBACK_IP 0(%esp)
#endif
#ifdef CONFIG_ROLLFORWARD
        ROLLFORWARD_IP 0(%esp)
#endif
#ifndef CONFIG_NO_FRAME_PTR
        pushl   %ebp
#ifndef CONFIG_PROFILE
        movl    %esp,%ebp
#else
        xorl    %ebp,%ebp
#endif
#endif
        pushl   %eax
        pushl   %edx
        pushl   %ecx
        RESET_KERNEL_SEGMENTS                   /* scratches ecx, edx */
do_timer_interrupt:
#ifdef CONFIG_NO_FRAME_PTR
        movl    12(%esp), %eax                  /* ARG1: eip for logging */
#else
        movl    16(%esp), %eax                  /* ARG1: eip for logging */
#endif
        call    thread_timer_interrupt          /* enter with disabled irqs */
in_timer_interrupt:
#ifndef CONFIG_NO_FRAME_PTR
        CHECK_SANITY 20(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 20(%esp),in_cli     /* scratches ecx */
#else
        CHECK_SANITY 16(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 16(%esp),in_cli     /* scratches ecx */
#endif
        popl    %ecx
        popl    %edx
        popl    %eax
#ifndef CONFIG_NO_FRAME_PTR
        popl    %ebp
#endif
        iret

        .p2align 4
        .globl  entry_int_timer_slow
entry_int_timer_slow:
#ifdef CONFIG_KERN_LIB_PAGE
        ROLLBACK_IP 0(%esp)
#endif
#ifdef CONFIG_ROLLFORWARD
        ROLLFORWARD_IP 0(%esp)
#endif
#ifndef CONFIG_NO_FRAME_PTR
        pushl   %ebp
#ifndef CONFIG_PROFILE
        movl    %esp,%ebp
#else
        xorl    %ebp,%ebp
#endif
#endif
        pushl   %eax
        pushl   %edx
        pushl   %ecx
        RESET_KERNEL_SEGMENTS                   /* scratches ecx, edx */

        call    thread_timer_interrupt_slow     /* enter with disabled irqs */
in_timer_interrupt_slow:
        jmp     do_timer_interrupt

        
        .p2align 4
        .globl  entry_int_timer_stop
entry_int_timer_stop:
#ifndef CONFIG_NO_FRAME_PTR
        pushl   %ebp
#ifndef CONFIG_PROFILE
        movl    %esp,%ebp
#else
        xorl    %ebp,%ebp
#endif
#endif
        pushl   %eax
        pushl   %edx
        pushl   %ecx
        RESET_KERNEL_SEGMENTS                   /* scratches ecx, edx */
        call    thread_timer_interrupt_stop
#ifndef CONFIG_NO_FRAME_PTR
        CHECK_SANITY 20(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 20(%esp),in_cli     /* scratches ecx */
#else
        CHECK_SANITY 16(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 16(%esp),in_cli     /* scratches ecx */
#endif
        popl    %ecx
        popl    %edx
        popl    %eax
#ifndef CONFIG_NO_FRAME_PTR
        popl    %ebp
#endif
        iret


/* profiling timer interrupt entry point */
#ifdef CONFIG_PROFILE

        .p2align 4
        .globl profile_interrupt_entry
        .type   profile_interrupt_entry,@function
profile_interrupt_entry:
#ifndef CONFIG_NO_FRAME_PTR
        pushl   %ebp
        movl    %esp,%ebp
        pushl   %eax
        movl    8(%esp),%eax                    /* %eax = return address */
#else
        pushl   %eax
        movl    4(%esp),%eax                    /* %eax = return address */
#endif
        pushl   %edx
        pushl   %ecx
        RESET_KERNEL_SEGMENTS                   /* scratches ecx, edx */
        call    profile_interrupt               /* enter with disabled irqs */
#ifndef CONFIG_NO_FRAME_PTR
        CHECK_SANITY 20(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 20(%esp),in_cli     /* scratches ecx */
#else
        CHECK_SANITY 16(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 16(%esp),in_cli     /* scratches ecx */
#endif
        popl    %ecx
        popl    %edx
        popl    %eax
#ifndef CONFIG_NO_FRAME_PTR
        popl    %ebp
#endif
        iret

#endif /* CONFIG_PROFILE */

/* other interrupts */

#define INTERRUPT(int,name)                                     \
        GATE_ENTRY(int,entry_##name,ACC_PL_K | ACC_INTR_GATE)   ;\
        .p2align 3                                              ;\
entry_##name:                                                   ;\
        pushl   %eax                                            ;\
        movl    $ (int - 0x20), %eax    /* ARG1: irqnum */      ;\
        jmp     all_irqs

#ifndef CONFIG_SCHED_PIT
INTERRUPT(0x20,int0)
#endif
INTERRUPT(0x21,int1)
INTERRUPT(0x22,int2)
INTERRUPT(0x23,int3)
INTERRUPT(0x24,int4)
INTERRUPT(0x25,int5)
INTERRUPT(0x26,int6)
INTERRUPT(0x27,int7)
#ifndef CONFIG_SCHED_RTC
INTERRUPT(0x28,int8)
#endif
INTERRUPT(0x29,int9)
INTERRUPT(0x2a,inta)
INTERRUPT(0x2b,intb)
INTERRUPT(0x2c,intc)
INTERRUPT(0x2d,intd)
INTERRUPT(0x2e,inte)
INTERRUPT(0x2f,intf)

        .p2align 4
        .type   all_irqs,@function
all_irqs:
        pushl   %edx
        pushl   %ecx
        RESET_KERNEL_SEGMENTS                   /* scratches ecx, edx */
#ifdef CONFIG_KERN_LIB_PAGE
        ROLLBACK_IP 12(%esp)
#endif
#ifdef CONFIG_ROLLFORWARD
        ROLLFORWARD_IP 12(%esp)
#endif
        movl    12(%esp), %edx                  /* ARG2: eip */
        call    irq_interrupt                   /* enter with disabled irqs */
in_interrupt:
        CHECK_SANITY 16(%esp)                   /* scratches ecx */
        RESET_USER_SEGMENTS 16(%esp),in_cli     /* scratches ecx */
        popl    %ecx
        popl    %edx
        popl    %eax

entry_int_pic_ignore:
        iret

        .global entry_int_pic_ignore
        .global entry_int7
        .global entry_intf

/****************************************************************************/
/* system calls                                                             */
/****************************************************************************/

#ifdef CONFIG_ASSEMBLER_IPC_SHORTCUT
GATE_ENTRY(0x30,entry_sys_ipc,ACC_PL_U | ACC_INTR_GATE);
#else
GATE_ENTRY(0x30,entry_sys_ipc_c,ACC_PL_U | ACC_INTR_GATE);
#endif

#if defined (CONFIG_JDB_LOGGING) || !defined(CONFIG_ASSEMBLER_IPC_SHORTCUT)
        .p2align 4
        .globl  entry_sys_ipc_c
entry_sys_ipc_c:
        pushl   %eax
        SAVE_STATE
        ESP_TO_TCB_AT %ebx
        RESET_KERNEL_SEGMENTS           /* scratches ecx, edx */
        testl   $Thread_alien, KSEG OFS__THREAD__STATE (%ebx)
        jnz     alien_sys_ipc_c
        RESET_THREAD_CANCEL_AT %ebx
        call    ipc_short_cut_wrapper
in_sc_ipc1:
        CHECK_SANITY $3                 /* scratches ecx */
        RESET_USER_SEGMENTS $3,in_cli   /* scratches ecx */
        RESTORE_STATE_AFTER_IPC
        popl    %eax
        iret

        .globl  in_sc_ipc1
#endif

#ifdef CONFIG_JDB
        /* The slow variant of sys_ipc_entry is used when logging IPC */
        .p2align 4
        .globl  entry_sys_ipc_log
entry_sys_ipc_log:
        pushl   %eax
        SAVE_STATE
        ESP_TO_TCB_AT %ebx
        RESET_KERNEL_SEGMENTS           /* scratches ecx, edx */
        testl   $Thread_alien, KSEG OFS__THREAD__STATE (%ebx)
        jnz     alien_sys_ipc_log
        RESET_THREAD_CANCEL_AT %ebx
        call    *syscall_table
in_slow_ipc4:
        CHECK_SANITY $3                 /* scratches ecx */
        RESET_USER_SEGMENTS $3,in_cli   /* scratches ecx */
        RESTORE_STATE_AFTER_IPC
        popl    %eax
        iret

        .globl  in_slow_ipc4
#endif // CONFIG_JDB

        // these labels help show_tcb to guess the thread state
        .globl  in_syscall
        .globl  in_slowtrap
        .globl  in_page_fault
        .globl  in_handle_fputrap
        .globl  in_interrupt  
        .globl  in_timer_interrupt
        .globl  in_timer_interrupt_slow

#define SYSTEM_CALL(int,name)                                   \
        GATE_ENTRY(int,entry_##name,ACC_PL_U | ACC_INTR_GATE)   ;\
        .p2align 3                                              ;\
entry_##name:                                                   ;\
        pushl   %eax                                            ;\
        movl    $(syscall_table+4*(int-0x30)), %eax             ;\
        jmp     all_syscalls

        .p2align 4
        .type   all_syscalls,@function
all_syscalls:
        SAVE_STATE                                              ;\
        ESP_TO_TCB_AT %ebx
        RESET_KERNEL_SEGMENTS           /* scratches ecx, edx */
        testl   $Thread_alien, KSEG OFS__THREAD__STATE (%ebx)
        jnz     alien_sys_call
        RESET_THREAD_CANCEL_AT %ebx
        call    *(%eax)                 /* interrupts enabled in wrappers */
in_syscall:

ret_from_syscall:
        CHECK_SANITY $3                 /* scratches ecx */
        RESET_USER_SEGMENTS $3,no_cli   /* scratches ecx */
        RESTORE_STATE
        popl    %eax
        iret

/* SYSTEM_CALL(0x30,sys_ipc) */
SYSTEM_CALL(0x31,sys_id_nearest)
SYSTEM_CALL(0x32,sys_fpage_unmap)
SYSTEM_CALL(0x33,sys_thread_switch)
SYSTEM_CALL(0x34,sys_thread_schedule)
SYSTEM_CALL(0x35,sys_thread_ex_regs)
SYSTEM_CALL(0x36,sys_task_new)

SYSTEM_CALL(0x39,sys_u_lock)

#ifdef CONFIG_PL0_HACK
SYSTEM_CALL(0x37,sys_priv_control)

GATE_ENTRY(0x38,entry_sys_priv_entry,ACC_PL_U | ACC_INTR_GATE)
        .p2align 3
entry_sys_priv_entry:
        pushl   $0
        pushf
        pushl   %eax
        pushl   %edx
        pushl   %ecx

        lea     16(%esp), %eax
        call    sys_priv_entry_wrapper
        orl     %eax, %eax
        jz      1f

        /* restore registers, diverge to user function */
        popl    %ecx
        popl    %edx
        popl    %eax
        popfl
        addl    $4, %esp

        /* Accessing stack beneath the stack pointer is allowed here since we
         * are executed with interrupts off and we don't expect any exception
         * as well. */
        jmp     *-4(%esp)

1:      /* no privileges so just return to the user */
        addl    $20, %esp
        iret
#endif

#ifndef CONFIG_PF_UX
/* these functions are implemented in entry-ia32.S */
GATE_ENTRY(0x0a,entry_vec0a_invalid_tss,ACC_PL_K | ACC_INTR_GATE)
GATE_ENTRY(0x0f,entry_vec0f_apic_spurious_interrupt_bug,ACC_PL_K | 
ACC_INTR_GATE)
GATE_ENTRY(0x3e,entry_vec3e_apic_error_interrupt,ACC_PL_K | ACC_INTR_GATE)
GATE_ENTRY(0x3f,entry_vec3f_apic_spurious_interrupt,ACC_PL_K | ACC_INTR_GATE)
#endif

GATE_INITTAB_END

        .globl alien_sys_ipc_c /* Also used in shortcut */
alien_sys_ipc_c:
        PRE_ALIEN_IPC
        call    ipc_short_cut_wrapper
        POST_ALIEN_IPC

#if defined (CONFIG_JDB)
alien_sys_ipc_log:
        PRE_ALIEN_IPC
        call    *syscall_table
        POST_ALIEN_IPC
#endif

/*
 * input:  eax: address to syscall function
 * output: eax: error code
 */
.macro SC_ADDR_TO_ERR val
        sub     $syscall_table , %eax   /* eax = byte offset to syscall */
        shr     $2, %eax                /* convert eax to syscall nr */
        add     $0x30, %eax             /* convert eax to syscall int nr */
        shl     $3, %eax                /* construct error code */
        orl     $\val, %eax             /* -"- */
.endm

alien_sys_call:
        btrl    $17, KSEG OFS__THREAD__STATE (%ebx) /* Thread_dis_alien */
        jc      1f
        RESTORE_STATE
        sub     $2, 4(%esp)             /* Correct EIP to point to insn */
        SC_ADDR_TO_ERR 2
2:      pushl   $0xd
        xchgl   4(%esp), %eax
        pusha
        jmp     _slowtraps

1:      /* do alien syscall and trap mafterwards */
        RESET_THREAD_CANCEL_AT %ebx
        pushl   %eax
        call    *(%eax)                 /* call with ENABLED interrupts */
        CHECK_SANITY $3                 /* scratches ecx */
        RESET_USER_SEGMENTS $3,no_cli   /* scratches ecx */
        popl    %eax
        RESTORE_STATE
        SC_ADDR_TO_ERR 6
        jmp     2b


        .p2align
        .globl  leave_by_trigger_exception
leave_by_trigger_exception:
        cli
        subl    $12,%esp                /* clean up stack from previous
                                         * CPL0-CPL0 iret */
        pushl   %eax
        pushl   %ecx
        pushl   %edx
        call    thread_restore_exc_state
        popl    %edx
        popl    %ecx
        popl    %eax
        pushl   $0x00
        pushl   $0xff
        pusha
        jmp     _slowtraps



.section ".text.debug.stack_profiling"

        .global __cyg_profile_func_enter
        .global __cyg_profile_func_exit


__ret_from_cyg:
        ret
__cyg_profile_func_enter:
__cyg_profile_func_exit:
        cmp $0xc0000000, %esp
        jl __ret_from_cyg
        cmp $0xd0000000,%esp
        jg __ret_from_cyg
        mov %esp, %ecx
//      and $0x7ff, %ecx /* 2K TCBs */
        and $0xfff, %ecx /* 4K TCBs */
        cmp $0x200, %ecx
        jg __ret_from_cyg
        int3
        jmp 1f
        .ascii "STACK"
1:      ret






INTERFACE:

#include "l4_types.h"

class Log_patch
{
  friend class Log_event;

private:
  Log_patch             *next;
  Unsigned8             *activator;
};

class Log_event
{
  const char            *event_name;
  Log_patch             *patch;
  int                   event_type;
};

class Jdb_tbuf_events
{
public:
  static Log_event *const log_events[];
};


IMPLEMENTATION:

#include <cstdarg>

#include "boot_info.h"
#include "config.h"
#include "jdb_ktrace.h"

// The following headers are only included here to reflect the module
// dependencies.
// 
// patch_log_sched_invalidate
// patch_log_sched_save
// patch_log_sched_load
// patch_log_context_switch
#include "context.h"
// patch_log_irq
#include "dirq.h"
// patch_log_pf_res
// patch_log_trap
// patch_log_thread_ex_regs
// patch_log_timer_irq
// patch_log_shortcut_failed1
// patch_log_shortcut_failed2
// patch_log_shortcut_succeeded
#include "thread.h"
// patch_show_log_pf_res
#include "jdb_trace.h"
// patch_show_log_context_switch
// patch_show_log_shortcut
#include "jdb_trace_set.h"
// patch_log_send_preemption
#include "preemption.h"


#ifdef CONFIG_JDB_LOGGING

#define DECLARE_PATCH(VAR, NAME)                \
  extern "C" char patch_##NAME;                 \
  static Log_patch VAR(&patch_##NAME)

// logging of context switch not implemented in Assembler shortcut but we
// can switch to C shortcut
DECLARE_PATCH (lp1, log_context_switch);
DECLARE_PATCH (lp2, show_log_context_switch);
static Log_event le1("context switch",
                      Log_event_context_switch, 2, &lp1, &lp2);

// logging of shortcut succeeded/failed not implemented in Assembler shortcut
// but we can switch to C shortcut
DECLARE_PATCH (lp3, log_shortcut_failed_1);
DECLARE_PATCH (lp4, log_shortcut_failed_2);
DECLARE_PATCH (lp5, log_shortcut_succeeded);
DECLARE_PATCH (lp6, show_log_shortcut);
static Log_event le2("ipc shortcut",
                      Log_event_ipc_shortcut, 4, &lp3, &lp4, &lp5, &lp6);

DECLARE_PATCH (lp7, log_irq);
static Log_event le3("irq raised",
                      Log_event_irq_raised, 1, &lp7);

DECLARE_PATCH (lp8, log_timer_irq);
static Log_event le4("timer irq raised",
                      Log_event_timer_irq, 1, &lp8);

DECLARE_PATCH (lp9, log_thread_ex_regs);
#ifdef CONFIG_SYSCALL_ITER
DECLARE_PATCH (lp10, log_thread_ex_regs_failed);
static Log_event le5("thread_ex_regs",
                      Log_event_thread_ex_regs, 2, &lp9, &lp10);
#else
static Log_event le5("thread_ex_regs",
                      Log_event_thread_ex_regs, 1, &lp9);
#endif

#ifdef CONFIG_PF_UX
DECLARE_PATCH (lp11, log_trap);
static Log_event le6("trap raised",
                      Log_event_trap, 1, &lp11);
#else
DECLARE_PATCH (lp11, log_trap);
DECLARE_PATCH (lp12, log_trap_n);
static Log_event le6("trap raised",
                      Log_event_trap, 2, &lp11, &lp12);
#endif

DECLARE_PATCH (lp13, log_pf_res);
DECLARE_PATCH (lp14, show_log_pf_res);
static Log_event le7("pagefault result",
                      Log_event_pf_res, 2, &lp13, &lp14);

DECLARE_PATCH (lp15, log_sched_load);
DECLARE_PATCH (lp16, log_sched_save);
DECLARE_PATCH (lp17, log_sched_invalidate);
static Log_event le8("scheduling event",
                      Log_event_sched, 3, &lp15, &lp16, &lp17);

DECLARE_PATCH (lp18, log_preemption);
static Log_event le9("preemption sent",
                      Log_event_preemption, 1, &lp18);

DECLARE_PATCH (lp20, log_id_nearest);
static Log_event le10("id_nearest",
                      Log_event_id_nearest, 1, &lp20);

DECLARE_PATCH (lp23, log_task_new);
static Log_event le11("task_new",
                      Log_event_task_new, 1, &lp23);

Log_event * const Jdb_tbuf_events::log_events[Log_event_max] =
{ 
  &le1,  // context switch
  &le2,  // ipc shortcut
  &le3,  // irq raised
  &le4,  // timer irq raised
  &le5,  // thread_ex_regs
  &le6,  // trap raised
  &le7,  // pagefault result
  &le8,  // scheduling event
  &le9,  // preemption sent
  &le10, // id nearest
  &le11, // task new
  0,     // terminate list
};

#endif


PUBLIC
explicit
Log_event::Log_event(const char *name, int type,
                     int count, Log_patch *lp, ...)
{
  va_list args;
  Log_patch **p = &patch;

  va_start(args, lp);
  *p = lp;
  for (int i=0; i<count-1; i++)
    {
      p  = &(*p)->next;
      *p = va_arg(args, Log_patch*);
    }
  va_end(args);

  event_name = name;
  event_type = type;
}

// enable all patch entries of log event
PUBLIC
void
Log_event::enable(int enable)
{
  Log_patch *p = patch;

  while (p)
    {
      p->enable(enable);
      p = p->next;
    }
}

// log event enabled if first entry is enabled
PUBLIC inline
int
Log_event::enabled(void) const
{
  return patch->enabled();
}

// get name of log event
PUBLIC inline
const char*
Log_event::get_name(void) const
{
  return event_name;
}

// get type of log event
PUBLIC inline
int
Log_event::get_type(void) const
{
  return event_type;
}

PUBLIC
explicit
Log_patch::Log_patch(void *code)
  : next(0), activator((Unsigned8*)code)
{
}

PUBLIC
void
Log_patch::enable(int enable)
{
  *activator = enable;

  // checksum has changed
  Boot_info::reset_checksum_ro();
}

PUBLIC inline
int
Log_patch::enabled(void)
{
  return *activator;
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]