Changes in / [5203efb1:2fa10f6] in mainline


Ignore:
Location:
kernel/arch/mips32
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • kernel/arch/mips32/include/context_offset.h

    r5203efb1 r2fa10f6  
    6161#endif /* KERNEL */
    6262
     63/* istate_t */
     64#define EOFFSET_AT     0x0
     65#define EOFFSET_V0     0x4
     66#define EOFFSET_V1     0x8
     67#define EOFFSET_A0     0xc
     68#define EOFFSET_A1     0x10
     69#define EOFFSET_A2     0x14
     70#define EOFFSET_A3     0x18
     71#define EOFFSET_T0     0x1c
     72#define EOFFSET_T1     0x20
     73#define EOFFSET_T2     0x24
     74#define EOFFSET_T3     0x28
     75#define EOFFSET_T4     0x2c
     76#define EOFFSET_T5     0x30
     77#define EOFFSET_T6     0x34
     78#define EOFFSET_T7     0x38
     79#define EOFFSET_T8     0x3c
     80#define EOFFSET_T9     0x40
     81#define EOFFSET_GP     0x44
     82#define EOFFSET_SP     0x48
     83#define EOFFSET_RA     0x4c
     84#define EOFFSET_LO     0x50
     85#define EOFFSET_HI     0x54
     86#define EOFFSET_STATUS 0x58
     87#define EOFFSET_EPC    0x5c
     88#define EOFFSET_K1     0x60
     89#define REGISTER_SPACE 104      /* respect stack alignment */
     90
    6391#ifdef __ASM__
    6492
  • kernel/arch/mips32/include/exception.h

    r5203efb1 r2fa10f6  
    6060
    6161typedef struct istate {
    62         /*
    63          * The first seven registers are arranged so that the istate structure
    64          * can be used both for exception handlers and for the syscall handler.
    65          */
    66         uint32_t a0;    /* arg1 */
    67         uint32_t a1;    /* arg2 */
    68         uint32_t a2;    /* arg3 */
    69         uint32_t a3;    /* arg4 */
    70         uint32_t t0;    /* arg5 */
    71         uint32_t t1;    /* arg6 */
    72         uint32_t v0;    /* arg7 */
     62        uint32_t at;
     63        uint32_t v0;
    7364        uint32_t v1;
    74         uint32_t at;
     65        uint32_t a0;
     66        uint32_t a1;
     67        uint32_t a2;
     68        uint32_t a3;
     69        uint32_t t0;
     70        uint32_t t1;
    7571        uint32_t t2;
    7672        uint32_t t3;
     
    7975        uint32_t t6;
    8076        uint32_t t7;
    81         uint32_t s0;
    82         uint32_t s1;
    83         uint32_t s2;
    84         uint32_t s3;
    85         uint32_t s4;
    86         uint32_t s5;
    87         uint32_t s6;
    88         uint32_t s7;
    8977        uint32_t t8;
    9078        uint32_t t9;
    91         uint32_t kt0;
    92         uint32_t kt1;   /* We use it as thread-local pointer */
    9379        uint32_t gp;
    9480        uint32_t sp;
    95         uint32_t s8;
    9681        uint32_t ra;
    9782       
     
    9984        uint32_t hi;
    10085       
    101         uint32_t status;        /* cp0_status */
    102         uint32_t epc;           /* cp0_epc */
    103 
    104         uint32_t alignment;     /* to make sizeof(istate_t) a multiple of 8 */
     86        uint32_t status;  /* cp0_status */
     87        uint32_t epc;     /* cp0_epc */
     88        uint32_t k1;      /* We use it as thread-local pointer */
    10589} istate_t;
    10690
     
    124108NO_TRACE static inline unative_t istate_get_fp(istate_t *istate)
    125109{
    126         return istate->sp;
     110        /* FIXME */
     111       
     112        return 0;
    127113}
    128114
  • kernel/arch/mips32/src/debug/stacktrace.c

    r5203efb1 r2fa10f6  
    3333 */
    3434
    35 /*
    36  * This stack tracing code is based on the suggested algorithm described on page
    37  * 3-27 and 3-28 of:
    38  *
    39  * SYSTEM V
    40  * APPLICATION BINARY INTERFACE
    41  *
    42  * MIPS RISC Processor
    43  * Supplement
    44  * 3rd Edition
    45  *
    46  * Unfortunately, GCC generates code which is not entirely compliant with this
    47  * method. For example, it places the "jr ra" instruction quite arbitrarily in
    48  * the middle of the function which makes the original algorithm unapplicable.
    49  *
    50  * We deal with this problem by simply not using those parts of the algorithm
    51  * that rely on the "jr ra" instruction occurring in the last basic block of a
    52  * function, which gives us still usable, but less reliable stack tracer. The
    53  * unreliability stems from the fact that under some circumstances it can become
    54  * confused and produce incorrect or incomplete stack trace. We apply extra
    55  * sanity checks so that the algorithm is still safe and should not crash the
    56  * system.
    57  *
    58  * Even though not perfect, our solution is pretty lightweight, especially when
    59  * compared with a prospective alternative solution based on additional
    60  * debugging information stored directly in the kernel image.
    61  */
    62 
    6335#include <stacktrace.h>
    6436#include <syscall/copy.h>
     
    12496extern char ktext_end;
    12597
    126 static bool bounds_check(uintptr_t pc)
    127 {
    128         return (pc >= (uintptr_t) &ktext_start) &&
    129             (pc < (uintptr_t) &ktext_end);
    130 }
    131 
    13298static bool
    13399scan(stack_trace_context_t *ctx, uintptr_t *prev_fp, uintptr_t *prev_ra)
     
    140106        do {
    141107                inst--;
    142                 if (!bounds_check((uintptr_t) inst))
    143                         return false;
    144108#if 0
    145109                /*
     
    216180                                        return false;
    217181                                /* too big offsets are suspicious */
    218                                 if ((size_t) offset > sizeof(istate_t))
     182                                if (offset > 32 * 4)
    219183                                        return false;
    220184
     
    243207{
    244208        return !((ctx->fp == 0) || ((ctx->fp % 8) != 0) ||
    245             (ctx->pc % 4 != 0) || !bounds_check(ctx->pc));
     209            (ctx->pc % 4 != 0) || (ctx->pc < (uintptr_t) &ktext_start) ||
     210            (ctx->pc >= (uintptr_t) &ktext_end));
    246211}
    247212
  • kernel/arch/mips32/src/exception.c

    r5203efb1 r2fa10f6  
    7474void istate_decode(istate_t *istate)
    7575{
    76         printf("epc=%p\tsta=%p\tlo =%p\thi =%p\n",
    77             istate->epc, istate->status, istate->lo, istate->hi);
    78         printf("a0 =%p\ta1 =%p\ta2 =%p\ta3 =%p\n",
    79             istate->a0, istate->a1, istate->a2, istate->a3);
    80         printf("t0 =%p\tt1 =%p\tt2 =%p\tt3 =%p\n",
    81             istate->t0, istate->t1, istate->t2, istate->t3);
    82         printf("t4 =%p\tt5 =%p\tt6 =%p\tt7 =%p\n",
    83             istate->t4, istate->t5, istate->t6, istate->t7);
    84         printf("t8 =%p\tt9 =%p\tv0 =%p\tv1 =%p\n",
    85             istate->t8, istate->t9, istate->v0, istate->v1);
    86         printf("s0 =%p\ts1 =%p\ts2 =%p\ts3 =%p\n",
    87             istate->s0, istate->s1, istate->s2, istate->s3);
    88         printf("s4 =%p\ts5 =%p\ts6 =%p\ts7 =%p\n",
    89             istate->s4, istate->s5, istate->s6, istate->s7);
    90         printf("s8 =%p\tat =%p\tkt0=%p\tkt1=%p\n",
    91             istate->s8, istate->at, istate->kt0, istate->kt1);
    92         printf("sp =%p\tra =%p\tgp =%p\n",
    93             istate->sp, istate->ra, istate->gp);
     76        printf("at=%p\tv0=%p\tv1=%p\n", istate->at, istate->v0, istate->v1);
     77        printf("a0=%p\ta1=%p\ta2=%p\n", istate->a0, istate->a1, istate->a2);
     78        printf("a3=%p\tt0=%p\tt1=%p\n", istate->a3, istate->t0, istate->t1);
     79        printf("t2=%p\tt3=%p\tt4=%p\n", istate->t2, istate->t3, istate->t4);
     80        printf("t5=%p\tt6=%p\tt7=%p\n", istate->t5, istate->t6, istate->t7);
     81        printf("t8=%p\tt9=%p\tgp=%p\n", istate->t8, istate->t9, istate->gp);
     82        printf("sp=%p\tra=%p\t\n", istate->sp, istate->ra);
     83        printf("lo=%p\thi=%p\t\n", istate->lo, istate->hi);
     84        printf("cp0_status=%p\tcp0_epc=%p\tk1=%p\n",
     85            istate->status, istate->epc, istate->k1);
    9486}
    9587
     
    10597                ASSERT(THREAD);
    10698                istate->epc += 4;
    107                 istate->v1 = istate->kt1;
     99                istate->v1 = istate->k1;
    108100        } else
    109101                unhandled_exception(n, istate);
  • kernel/arch/mips32/src/start.S

    r5203efb1 r2fa10f6  
    4646
    4747/*
    48  * Which status bits are thread-local:
     48 * Which status bits should are thread-local:
    4949 * KSU(UM), EXL, ERL, IE
    5050 */
    5151#define REG_SAVE_MASK 0x1f
    52 
    53 #define ISTATE_OFFSET_A0        0
    54 #define ISTATE_OFFSET_A1        4
    55 #define ISTATE_OFFSET_A2        8
    56 #define ISTATE_OFFSET_A3        12
    57 #define ISTATE_OFFSET_T0        16
    58 #define ISTATE_OFFSET_T1        20
    59 #define ISTATE_OFFSET_V0        24
    60 #define ISTATE_OFFSET_V1        28
    61 #define ISTATE_OFFSET_AT        32
    62 #define ISTATE_OFFSET_T2        36
    63 #define ISTATE_OFFSET_T3        40
    64 #define ISTATE_OFFSET_T4        44
    65 #define ISTATE_OFFSET_T5        48
    66 #define ISTATE_OFFSET_T6        52
    67 #define ISTATE_OFFSET_T7        56
    68 #define ISTATE_OFFSET_S0        60
    69 #define ISTATE_OFFSET_S1        64
    70 #define ISTATE_OFFSET_S2        68
    71 #define ISTATE_OFFSET_S3        72
    72 #define ISTATE_OFFSET_S4        76
    73 #define ISTATE_OFFSET_S5        80
    74 #define ISTATE_OFFSET_S6        84
    75 #define ISTATE_OFFSET_S7        88
    76 #define ISTATE_OFFSET_T8        92
    77 #define ISTATE_OFFSET_T9        96
    78 #define ISTATE_OFFSET_KT0       100
    79 #define ISTATE_OFFSET_KT1       104
    80 #define ISTATE_OFFSET_GP        108
    81 #define ISTATE_OFFSET_SP        112
    82 #define ISTATE_OFFSET_S8        116
    83 #define ISTATE_OFFSET_RA        120
    84 #define ISTATE_OFFSET_LO        124
    85 #define ISTATE_OFFSET_HI        128
    86 #define ISTATE_OFFSET_STATUS    132
    87 #define ISTATE_OFFSET_EPC       136
    88 #define ISTATE_OFFSET_ALIGNMENT 140
    89 
    90 #define ISTATE_SOFT_SIZE        144
    91 
    92 /*
    93  * The fake ABI prologue is never executed and may not be part of the
    94  * procedure's body. Instead, it should be immediately preceding the procedure's
    95  * body. Its only purpose is to trick the stack trace walker into thinking that
    96  * the exception is more or less just a normal function call.
    97  */
    98 .macro FAKE_ABI_PROLOGUE
    99         sub $sp, ISTATE_SOFT_SIZE
    100         sw $ra, ISTATE_OFFSET_EPC($sp)
    101 .endm
    10252
    10353/*
     
    10858 */
    10959.macro REGISTERS_STORE_AND_EXC_RESET r
    110         sw $at, ISTATE_OFFSET_AT(\r)
    111         sw $v0, ISTATE_OFFSET_V0(\r)
    112         sw $v1, ISTATE_OFFSET_V1(\r)
    113         sw $a0, ISTATE_OFFSET_A0(\r)
    114         sw $a1, ISTATE_OFFSET_A1(\r)
    115         sw $a2, ISTATE_OFFSET_A2(\r)
    116         sw $a3, ISTATE_OFFSET_A3(\r)
    117         sw $t0, ISTATE_OFFSET_T0(\r)
    118         sw $t1, ISTATE_OFFSET_T1(\r)
    119         sw $t2, ISTATE_OFFSET_T2(\r)
    120         sw $t3, ISTATE_OFFSET_T3(\r)
    121         sw $t4, ISTATE_OFFSET_T4(\r)
    122         sw $t5, ISTATE_OFFSET_T5(\r)
    123         sw $t6, ISTATE_OFFSET_T6(\r)
    124         sw $t7, ISTATE_OFFSET_T7(\r)
    125         sw $t8, ISTATE_OFFSET_T8(\r)
    126         sw $t9, ISTATE_OFFSET_T9(\r)
    127         sw $s0, ISTATE_OFFSET_S0(\r)
    128         sw $s1, ISTATE_OFFSET_S1(\r)
    129         sw $s2, ISTATE_OFFSET_S2(\r)
    130         sw $s3, ISTATE_OFFSET_S3(\r)
    131         sw $s4, ISTATE_OFFSET_S4(\r)
    132         sw $s5, ISTATE_OFFSET_S5(\r)
    133         sw $s6, ISTATE_OFFSET_S6(\r)
    134         sw $s7, ISTATE_OFFSET_S7(\r)
    135         sw $s8, ISTATE_OFFSET_S8(\r)
     60        sw $at, EOFFSET_AT(\r)
     61        sw $v0, EOFFSET_V0(\r)
     62        sw $v1, EOFFSET_V1(\r)
     63        sw $a0, EOFFSET_A0(\r)
     64        sw $a1, EOFFSET_A1(\r)
     65        sw $a2, EOFFSET_A2(\r)
     66        sw $a3, EOFFSET_A3(\r)
     67        sw $t0, EOFFSET_T0(\r)
     68        sw $t1, EOFFSET_T1(\r)
     69        sw $t2, EOFFSET_T2(\r)
     70        sw $t3, EOFFSET_T3(\r)
     71        sw $t4, EOFFSET_T4(\r)
     72        sw $t5, EOFFSET_T5(\r)
     73        sw $t6, EOFFSET_T6(\r)
     74        sw $t7, EOFFSET_T7(\r)
     75        sw $t8, EOFFSET_T8(\r)
     76        sw $t9, EOFFSET_T9(\r)
    13677       
    13778        mflo $at
    138         sw $at, ISTATE_OFFSET_LO(\r)
     79        sw $at, EOFFSET_LO(\r)
    13980        mfhi $at
    140         sw $at, ISTATE_OFFSET_HI(\r)
    141        
    142         sw $gp, ISTATE_OFFSET_GP(\r)
    143         sw $ra, ISTATE_OFFSET_RA(\r)
    144         sw $k0, ISTATE_OFFSET_KT0(\r)
    145         sw $k1, ISTATE_OFFSET_KT1(\r)
     81        sw $at, EOFFSET_HI(\r)
     82       
     83        sw $gp, EOFFSET_GP(\r)
     84        sw $ra, EOFFSET_RA(\r)
     85        sw $k1, EOFFSET_K1(\r)
    14686       
    14787        mfc0 $t0, $status
     
    15595        and $t0, $t0, $t3
    15696       
    157         sw $t2, ISTATE_OFFSET_STATUS(\r)
    158         sw $t1, ISTATE_OFFSET_EPC(\r)
     97        sw $t2, EOFFSET_STATUS(\r)
     98        sw $t1, EOFFSET_EPC(\r)
    15999        mtc0 $t0, $status
    160100.endm
     
    166106         */
    167107        mfc0 $t0, $status
    168         lw $t1, ISTATE_OFFSET_STATUS(\r)
     108        lw $t1,EOFFSET_STATUS(\r)
    169109       
    170110        /* mask UM, EXL, ERL, IE */
     
    176116        mtc0 $t0, $status
    177117       
    178         lw $v0, ISTATE_OFFSET_V0(\r)
    179         lw $v1, ISTATE_OFFSET_V1(\r)
    180         lw $a0, ISTATE_OFFSET_A0(\r)
    181         lw $a1, ISTATE_OFFSET_A1(\r)
    182         lw $a2, ISTATE_OFFSET_A2(\r)
    183         lw $a3, ISTATE_OFFSET_A3(\r)
    184         lw $t0, ISTATE_OFFSET_T0(\r)
    185         lw $t1, ISTATE_OFFSET_T1(\r)
    186         lw $t2, ISTATE_OFFSET_T2(\r)
    187         lw $t3, ISTATE_OFFSET_T3(\r)
    188         lw $t4, ISTATE_OFFSET_T4(\r)
    189         lw $t5, ISTATE_OFFSET_T5(\r)
    190         lw $t6, ISTATE_OFFSET_T6(\r)
    191         lw $t7, ISTATE_OFFSET_T7(\r)
    192         lw $t8, ISTATE_OFFSET_T8(\r)
    193         lw $t9, ISTATE_OFFSET_T9(\r)
    194        
    195         lw $gp, ISTATE_OFFSET_GP(\r)
    196         lw $ra, ISTATE_OFFSET_RA(\r)
    197         lw $k1, ISTATE_OFFSET_KT1(\r)
    198        
    199         lw $at, ISTATE_OFFSET_LO(\r)
     118        lw $v0, EOFFSET_V0(\r)
     119        lw $v1, EOFFSET_V1(\r)
     120        lw $a0, EOFFSET_A0(\r)
     121        lw $a1, EOFFSET_A1(\r)
     122        lw $a2, EOFFSET_A2(\r)
     123        lw $a3, EOFFSET_A3(\r)
     124        lw $t0, EOFFSET_T0(\r)
     125        lw $t1, EOFFSET_T1(\r)
     126        lw $t2, EOFFSET_T2(\r)
     127        lw $t3, EOFFSET_T3(\r)
     128        lw $t4, EOFFSET_T4(\r)
     129        lw $t5, EOFFSET_T5(\r)
     130        lw $t6, EOFFSET_T6(\r)
     131        lw $t7, EOFFSET_T7(\r)
     132        lw $t8, EOFFSET_T8(\r)
     133        lw $t9, EOFFSET_T9(\r)
     134       
     135        lw $gp, EOFFSET_GP(\r)
     136        lw $ra, EOFFSET_RA(\r)
     137        lw $k1, EOFFSET_K1(\r)
     138       
     139        lw $at, EOFFSET_LO(\r)
    200140        mtlo $at
    201         lw $at, ISTATE_OFFSET_HI(\r)
     141        lw $at, EOFFSET_HI(\r)
    202142        mthi $at
    203143       
    204         lw $at, ISTATE_OFFSET_EPC(\r)
     144        lw $at, EOFFSET_EPC(\r)
    205145        mtc0 $at, $epc
    206146       
    207         lw $at, ISTATE_OFFSET_AT(\r)
    208         lw $sp, ISTATE_OFFSET_SP(\r)
     147        lw $at, EOFFSET_AT(\r)
     148        lw $sp, EOFFSET_SP(\r)
    209149.endm
    210150
     
    219159       
    220160        beq $k0, $0, 1f
    221         move $k0, $sp
     161        add $k0, $sp, 0
    222162       
    223163        /* move $k0 pointer to kernel stack */
     
    226166       
    227167        /* move $k0 (supervisor_sp) */
    228         lw $k0, ($k0)
     168        lw $k0, 0($k0)
    229169       
    230170        1:
     
    262202        nop
    263203
    264         FAKE_ABI_PROLOGUE
    265204exception_handler:
    266205        KERNEL_STACK_TO_K0
    267206       
    268         sub $k0, ISTATE_SOFT_SIZE
    269         sw $sp, ISTATE_OFFSET_SP($k0)
     207        sub $k0, REGISTER_SPACE
     208        sw $sp, EOFFSET_SP($k0)
    270209        move $sp, $k0
    271210       
     
    288227        /* the $sp is automatically restored to former value */
    289228        eret
     229
     230#define SS_SP      EOFFSET_SP
     231#define SS_STATUS  EOFFSET_STATUS
     232#define SS_EPC     EOFFSET_EPC
     233#define SS_K1      EOFFSET_K1
    290234
    291235/** Syscall entry
     
    305249 */
    306250syscall_shortcut:
     251        /* we have a lot of space on the stack, with free use */
    307252        mfc0 $t3, $epc
    308253        mfc0 $t2, $status
    309         sw $t3, ISTATE_OFFSET_EPC($sp)  /* save EPC */
    310         sw $k1, ISTATE_OFFSET_KT1($sp)  /* save $k1 not saved on context switch */
     254        sw $t3, SS_EPC($sp)  /* save EPC */
     255        sw $k1, SS_K1($sp)   /* save $k1 not saved on context switch */
    311256       
    312257        and $t4, $t2, REG_SAVE_MASK  /* save only KSU, EXL, ERL, IE */
     
    315260        ori $t2, $t2, 0x1  /* set IE */
    316261       
    317         sw $t4, ISTATE_OFFSET_STATUS($sp)
     262        sw $t4, SS_STATUS($sp)
    318263        mtc0 $t2, $status
    319264       
    320265        /*
    321266         * Call the higher level system call handler.
     267         * We are going to reuse part of the unused exception stack frame.
    322268         *
    323269         */
    324         sw $t0, ISTATE_OFFSET_T0($sp)  /* save the 5th argument on the stack */
    325         sw $t1, ISTATE_OFFSET_T1($sp)  /* save the 6th argument on the stack */
     270        sw $t0, STACK_ARG4($sp)  /* save the 5th argument on the stack */
     271        sw $t1, STACK_ARG5($sp)  /* save the 6th argument on the stack */
    326272        jal syscall_handler
    327         sw $v0, ISTATE_OFFSET_V0($sp)  /* save the syscall number on the stack */
     273        sw $v0, STACK_ARG6($sp)  /* save the syscall number on the stack */
    328274       
    329275        /* restore status */
    330276        mfc0 $t2, $status
    331         lw $t3, ISTATE_OFFSET_STATUS($sp)
     277        lw $t3, SS_STATUS($sp)
    332278       
    333279        /*
     
    342288       
    343289        /* restore epc + 4 */
    344         lw $t2, ISTATE_OFFSET_EPC($sp)
    345         lw $k1, ISTATE_OFFSET_KT1($sp)
     290        lw $t2, SS_EPC($sp)
     291        lw $k1, SS_K1($sp)
    346292        addi $t2, $t2, 4
    347293        mtc0 $t2, $epc
    348294       
    349         lw $sp, ISTATE_OFFSET_SP($sp)  /* restore $sp */
    350         eret
    351 
    352         FAKE_ABI_PROLOGUE
     295        lw $sp, SS_SP($sp)  /* restore $sp */
     296        eret
     297
    353298tlb_refill_handler:
    354299        KERNEL_STACK_TO_K0
    355         sub $k0, ISTATE_SOFT_SIZE
     300        sub $k0, REGISTER_SPACE
    356301        REGISTERS_STORE_AND_EXC_RESET $k0
    357         sw $sp, ISTATE_OFFSET_SP($k0)
    358         move $sp, $k0
     302        sw $sp,EOFFSET_SP($k0)
     303        add $sp, $k0, 0
    359304       
    360305        jal tlb_refill
    361         move $a0, $sp
     306        add $a0, $sp, 0
    362307       
    363308        REGISTERS_LOAD $sp
    364309        eret
    365310
    366         FAKE_ABI_PROLOGUE
    367311cache_error_handler:
    368312        KERNEL_STACK_TO_K0
    369         sub $k0, ISTATE_SOFT_SIZE
     313        sub $k0, REGISTER_SPACE
    370314        REGISTERS_STORE_AND_EXC_RESET $k0
    371         sw $sp, ISTATE_OFFSET_SP($k0)
    372         move $sp, $k0
     315        sw $sp,EOFFSET_SP($k0)
     316        add $sp, $k0, 0
    373317       
    374318        jal cache_error
    375         move $a0, $sp
     319        add $a0, $sp, 0
    376320       
    377321        REGISTERS_LOAD $sp
     
    379323
    380324userspace_asm:
    381         move $sp, $a0
    382         move $v0, $a1
    383         move $t9, $a2      /* set up correct entry into PIC code */
     325        add $sp, $a0, 0
     326        add $v0, $a1, 0
     327        add $t9, $a2, 0    /* set up correct entry into PIC code */
    384328        xor $a0, $a0, $a0  /* $a0 is defined to hold pcb_ptr */
    385329                           /* set it to 0 */
Note: See TracChangeset for help on using the changeset viewer.