aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/beam/beam_bp.c
blob: 0832b3f3741398517bcc6f8f3933376291246f1b (plain) (tree)
1
2
3
4
5
6
7
8
9

                   
  
                                                        
  


                                                                   
  






                                                                           
  















                        
                     
                            












                                                                            
                                   
                                      

                                                                   
                                        
                                                                    
     

                                      

      






                                       
                                       
 
                                       
 



                                                                             
 

                                      
                                
 






                                    
                                                              

 




                                                        
                                        
                                             

                                           





                                 
                                          
                                               



 






                                                                            
                                                                             
                                                                          
                                                                           
                                                                      
                                                


                                                           
                                                  

                                                          
                                                                    
 

                                                                      
 




                                                                           
 
             

                                                     
                                                     
                                                     






                                                                                                       
 



                                                                            

                    

                                                      
                                                                        
                                                                          

 

    
                                                                        













                                                             

                                                            






                                                                     
                                                                  
                         
                                                                   


                            
                                                                    





                                                
 
                                         
                                                              
                                              

                         
                                                               

                         
















                                                      



                   

 
    
                                                                     










                                                                    
 















                                                       
                      
                                          


                                                           
             
                                                               
                                                                                           


                     

                                                                            






                    

                                              



                                 


    
                                                   




                                 
                                                     

                             
                                                        







                                  
                                                     

                                    
                                             



           
                                                              
 
                                
















                                                           
                                                               


                                      
                                        














                                                             
                   




                                                
                                                    
                                                       

                                                             
         
                            
                







                                                                
                                                               



                                      
                                            
                                                  




                                    
                                            


                                      
                                           









                                               

                                                          






                                        
                                                           

                             
                                             
                                    



                                                                 












                                                                     






                                                                      
               

                                               











                                          
                                                



           
                                      
 
                                              
                                                     
                                    















                                                                      
                                                                        

 
    
                                                                            
 
                                                             
 
 
    
                                                                        


                                                                      
                                                                  


    
                                                                            
 
                                                                       
 
 
    
                                                                      
 
                                
                                                                      
                                                  

 
    

                                                                             

 

                                      
                                                           

 


                                                                 
                                                         
                                         

 


                                                                
                                                                   
                                         
 
 


                                      
                                         

 
    
                                                      
 
                                



                                                                          
                                            




         

                                       
                                        


    
                                       
 
                                                  

 


                                      
                                                    
                                   

 


                                      
                                                         

 


                                     
                                                                   

 


                                     
                                 



                                       
                             

           
 
                                                    
                 

                                   

                 
                                              
                             
                                                  
                                        
                     
                                               




                             
                                                  
                                        
                     

                                         
                                     

             

 
    
                                                       
 
                                                    
 
                                           
                            

                                               
                                 

 













                                                                     
                                           
                    
                                                       

                             
                                                         






                                        
                                                   
                         
                                                               

                                     
                                                                 









                                             
         
                                                                     



                      
                                         
 
                                                        
 
                       
                      

                                            


                                                  

                                            
                                            





                                                  
 
                                          
                                                        
                                                                                
                                                  
                                                                                
     
 
                                         

                                          
                                                                    
 

                                                                               
                                                           
                                                       






                                               
         


                                           
                                                

     
                                                
                
                                                  
                                 


                                                         


                                                     
                                                                          






                                                     
                                                        






                                                                      
                                                     




                             












                                                                    
                                             
                                                                    

                                                          

                          
                             
                      
                            
 
                                         
 
                          










                                                                       

                                                   



                                                                  
                                                                 
                                                        

                                         
                              
 
                                                                     
                                 
                                                                     




                                                            
                                                       






                                               


                                                
                                         
                                                     








                                                             























                                                                































                                                                     







                                                             

                                                      










                                                             
                                                                    
                                                   

                                                    
                                                                    
                                                      




















                                                                         
                                                                 
                                                             
                                                         
                                                               



                                              
                                                                      


                                                             
                                                                         
         

                                                 









                                                                     
                                         


                  
                 
                                                           
                                                       
 
                            
                                 



                         

                                                 
                                   
                                                                
                                 


                                          





                                                                  
                                        



                                                 
                                                                         







                                                                
                                       
     

                                   
               
                      
                                                 
                                                           
                                        
                                
                                        

                                                                            


                                                             
                                                       
                                                   
                                                         


                      


    
                                                                       
 
                           



                                            
                                          

                
                                                                     
                                                                                    


                                            
    
                                       
                          









                                                            
                                                 
            
                        
                                       
                                      
                                   


                                 
                                       


                                                     
                                   













                                              
                                   
                      
                      

                         
                
                          
 







                                      
     
 
                   
                     

                             

 
    
                                                    
 
                           



                                            
                                        

              
                                                                   
                                                                                  
 



                                            
                        









                                              
 


                                         
                        
 
                                      
                                   


                                 
                                       


                                                               

                                   









                                              
 

         
                     
                         
 
     

                             
 
 
    
                                                                         
 
                                                                      
                                               

             
                             
                                           
         
                 



             
   
                                                               
                                            
 
                                                             
    
             
                             
                                          
         
                         
                                                                         
         
                 



             
    
                                                      
 
                                                        
    
             
                        
                                                                         
         
                 



             
                                                                     




                                     
                                         

              



                                                
                                          













                                                         
             
                                                                                 
 






                                                



                                                                  

                                                        

                                                            





                                                                
         
                 
     
 



             

                                        
                 
                             
                     

             
                                                                             
                    
                                                 
                    
                                            
                             
                                    
                                                          



                                                                        




                






                                                        




















                                                          

                                      









                                                               
                                                     






















                                                                                                        
              










                                                  

                                       









                                    
                                












                                                  
                                                          
                                          

                                            
                            
                                        




                                     
              


                                                 
                  





                                                 
                                           
                       
                                                      
                                           
                                
 
                                       
 

                                
 





                                                  






                                                   
                                     





                                       
               

                             

 




                                                                            

                                                               
                                                         
 

           
 

                             

                                                   
                                             
     

 
           
                                                                          
                                                                  



                      
                                          
 
                                                     
                     
                 
              
                                                                             
                                                
                   
         
                                     
                                                   


                                              
                         
     
                      





                                                                    
                                                                


                                              
                                       
                                         
                                           
                                                
                
                                               
                                                       
         
                                                 
               


                                              
 
                                           
                                                     
                




                                                    
         
                                                 
               
     
 


                                          
 
                                                                     


                                                   

                                                 

                                 
                                          
                                      
                                                                   
                                                                     
                              




                                                  

                                              






                                                       
                                      
                                        
                                                           
                                      
                                              
         
                       
     
 
                             
                                             

 

                                             
 
           



                             
                                                             
     

 
          
                                                        
 


                      
                                          
 
                                                     
 
                                     

                 
 
                      


                                             
                                                                



                                       
                                       








                                                              
 
                                             

             
 
           
                                
 

                                                            

                                



           

                            
                                                


                  
 


                              
                                                


















                                                                 
                                                                     

                                          
                             
                                                                       


                         
             
                                            
         


                        

 
                  
                                
 
                                                             
                             

 
                     
                                               
 
                                
 
                                                      
                                      
                 
     
            
                                                          


                                                 
         
     
             
 
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2000-2017. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */
 
#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif
 
#include "sys.h"
#include "erl_vm.h"
#include "global.h"
#include "erl_process.h"
#include "beam_load.h"
#include "bif.h"
#include "error.h"
#include "erl_binary.h"
#include "beam_bp.h"
#include "erl_term.h"
#include "erl_nfunc_sched.h"

/* *************************************************************************
** Macros
*/

/*
** Memory allocation macros
*/
/* Breakpoint data */
#define Alloc(SZ)		erts_alloc(ERTS_ALC_T_BPD, (SZ))
#define ReAlloc(P, SIZ)		erts_realloc(ERTS_ALC_T_BPD, (P), (SZ))
#define Free(P)			erts_free(ERTS_ALC_T_BPD, (P))

#if defined(ERTS_ENABLE_LOCK_CHECK)
#  define ERTS_REQ_PROC_MAIN_LOCK(P) \
      if ((P)) erts_proc_lc_require_lock((P), ERTS_PROC_LOCK_MAIN,\
					 __FILE__, __LINE__)
#  define ERTS_UNREQ_PROC_MAIN_LOCK(P) \
      if ((P)) erts_proc_lc_unrequire_lock((P), ERTS_PROC_LOCK_MAIN)
#else
#  define ERTS_REQ_PROC_MAIN_LOCK(P)
#  define ERTS_UNREQ_PROC_MAIN_LOCK(P)
#endif

#define ERTS_BPF_LOCAL_TRACE       0x01
#define ERTS_BPF_META_TRACE        0x02
#define ERTS_BPF_COUNT             0x04
#define ERTS_BPF_COUNT_ACTIVE      0x08
#define ERTS_BPF_DEBUG             0x10
#define ERTS_BPF_TIME_TRACE        0x20
#define ERTS_BPF_TIME_TRACE_ACTIVE 0x40
#define ERTS_BPF_GLOBAL_TRACE      0x80

#define ERTS_BPF_ALL               0xFF

extern BeamInstr beam_return_to_trace[1];   /* OpCode(i_return_to_trace) */
extern BeamInstr beam_return_trace[1];      /* OpCode(i_return_trace) */
extern BeamInstr beam_exception_trace[1];   /* OpCode(i_exception_trace) */
extern BeamInstr beam_return_time_trace[1]; /* OpCode(i_return_time_trace) */

erts_atomic32_t erts_active_bp_index;
erts_atomic32_t erts_staging_bp_index;
erts_mtx_t erts_dirty_bp_ix_mtx;

/*
 * Inlined helpers
 */

static ERTS_INLINE ErtsMonotonicTime
get_mtime(Process *c_p)
{
    return erts_get_monotonic_time(erts_proc_sched_data(c_p));
}

static ERTS_INLINE Uint32
acquire_bp_sched_ix(Process *c_p)
{
    ErtsSchedulerData *esdp = erts_proc_sched_data(c_p);
    ASSERT(esdp);
    if (ERTS_SCHEDULER_IS_DIRTY(esdp)) {
	erts_mtx_lock(&erts_dirty_bp_ix_mtx);
        return (Uint32) erts_no_schedulers;
    }
    return (Uint32) esdp->no - 1;
}

static ERTS_INLINE void
release_bp_sched_ix(Uint32 ix)
{
    if (ix == (Uint32) erts_no_schedulers)
        erts_mtx_unlock(&erts_dirty_bp_ix_mtx);
}



/* *************************************************************************
** Local prototypes
*/

/*
** Helpers
*/
static ErtsTracer do_call_trace(Process* c_p, ErtsCodeInfo *info, Eterm* reg,
                                int local, Binary* ms, ErtsTracer tracer);
static void set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
		      enum erts_break_op count_op, ErtsTracer tracer);
static void set_function_break(ErtsCodeInfo *ci,
			       Binary *match_spec,
			       Uint break_flags,
			       enum erts_break_op count_op,
			       ErtsTracer tracer);

static void clear_break(BpFunctions* f, Uint break_flags);
static int clear_function_break(ErtsCodeInfo *ci, Uint break_flags);

static BpDataTime* get_time_break(ErtsCodeInfo *ci);
static GenericBpData* check_break(ErtsCodeInfo *ci, Uint break_flags);

static void bp_meta_unref(BpMetaTracer *bmt);
static void bp_count_unref(BpCount *bcp);
static void bp_time_unref(BpDataTime *bdt);
static void consolidate_bp_data(Module *modp, ErtsCodeInfo *ci, int local);
static void uninstall_breakpoint(ErtsCodeInfo *ci);

/* bp_hash */
#define BP_TIME_ADD(pi0, pi1)                       \
    do {                                            \
	(pi0)->count   += (pi1)->count;             \
	(pi0)->time    += (pi1)->time;              \
    } while(0)

static void bp_hash_init(bp_time_hash_t *hash, Uint n);
static void bp_hash_rehash(bp_time_hash_t *hash, Uint n);
static ERTS_INLINE bp_data_time_item_t * bp_hash_get(bp_time_hash_t *hash, bp_data_time_item_t *sitem);
static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_data_time_item_t *sitem);
static void bp_hash_delete(bp_time_hash_t *hash);

/* *************************************************************************
** External interfaces
*/

void 
erts_bp_init(void) {
    erts_atomic32_init_nob(&erts_active_bp_index, 0);
    erts_atomic32_init_nob(&erts_staging_bp_index, 1);
    erts_mtx_init(&erts_dirty_bp_ix_mtx, "dirty_break_point_index", NIL,
        ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DEBUG);
}


void
erts_bp_match_functions(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
{
    ErtsCodeIndex code_ix = erts_active_code_ix();
    Uint max_funcs = 0;
    int current;
    int max_modules = module_code_size(code_ix);
    int num_modules = 0;
    Module* modp;
    Module** module;
    Uint i;

    module = (Module **) Alloc(max_modules*sizeof(Module *));
    num_modules = 0;
    for (current = 0; current < max_modules; current++) {
	modp = module_code(current, code_ix);
	if (modp->curr.code_hdr) {
	    max_funcs += modp->curr.code_hdr->num_functions;
	    module[num_modules++] = modp;
	}
    }

    f->matching = (BpFunction *) Alloc(max_funcs*sizeof(BpFunction));
    i = 0;
    for (current = 0; current < num_modules; current++) {
	BeamCodeHeader* code_hdr = module[current]->curr.code_hdr;
	ErtsCodeInfo* ci;
	Uint num_functions = (Uint)(UWord) code_hdr->num_functions;
	Uint fi;

	if (specified > 0) {
	    if (mfa->module != make_atom(module[current]->module)) {
		/* Wrong module name */
		continue;
	    }
	}

	for (fi = 0; fi < num_functions; fi++) {

	    ci = code_hdr->functions[fi];
	    ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
	    if (erts_is_function_native(ci)) {
		continue;
	    }
	    if (is_nil(ci->mfa.module)) { /* Ignore BIF stub */
		continue;
	    }
            switch (specified) {
            case 3:
                if (ci->mfa.arity != mfa->arity)
                    continue;
            case 2:
                if (ci->mfa.function != mfa->function)
                    continue;
            case 1:
                if (ci->mfa.module != mfa->module)
                    continue;
            case 0:
                break;
            }
            /* Store match */
            f->matching[i].ci = ci;
            f->matching[i].mod = module[current];
            i++;
	}
    }
    f->matched = i;
    Free(module);
}

void
erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
{
    ErtsCodeIndex code_ix = erts_active_code_ix();
    int i;
    int num_exps = export_list_size(code_ix);
    int ne;

    f->matching = (BpFunction *) Alloc(num_exps*sizeof(BpFunction));
    ne = 0;
    for (i = 0; i < num_exps; i++) {
	Export* ep = export_list(i, code_ix);
	BeamInstr* pc;

        switch (specified) {
        case 3:
            if (mfa->arity != ep->info.mfa.arity)
                continue;
        case 2:
            if (mfa->function != ep->info.mfa.function)
                continue;
        case 1:
            if (mfa->module != ep->info.mfa.module)
                continue;
        case 0:
            break;
        default:
            ASSERT(0);
        }

	pc = ep->beam;
	if (ep->addressv[code_ix] == pc) {
	    if (BeamIsOpCode(*pc, op_apply_bif) ||
                BeamIsOpCode(*pc, op_call_error_handler)) {
                continue;
	    }
	    ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint));
	} else if (erts_is_function_native(erts_code_to_codeinfo(ep->addressv[code_ix]))) {
	    continue;
	}

	f->matching[ne].ci = &ep->info;
	f->matching[ne].mod = erts_get_module(ep->info.mfa.module, code_ix);
	ne++;

    }
    f->matched = ne;
}

void
erts_bp_free_matched_functions(BpFunctions* f)
{
    if (f->matching) {
	Free(f->matching);
    }
    else ASSERT(f->matched == 0);
}

void
erts_consolidate_bp_data(BpFunctions* f, int local)
{
    BpFunction* fs = f->matching;
    Uint i;
    Uint n = f->matched;

    ERTS_LC_ASSERT(erts_has_code_write_permission());

    for (i = 0; i < n; i++) {
	consolidate_bp_data(fs[i].mod, fs[i].ci, local);
    }
}

void
erts_consolidate_bif_bp_data(void)
{
    int i;

    ERTS_LC_ASSERT(erts_has_code_write_permission());
    for (i = 0; i < BIF_SIZE; i++) {
	Export *ep = bif_export[i];
	consolidate_bp_data(0, &ep->info, 0);
    }
}

static void
consolidate_bp_data(Module* modp, ErtsCodeInfo *ci, int local)
{
    GenericBp* g = ci->u.gen_bp;
    GenericBpData* src;
    GenericBpData* dst;
    Uint flags;

    if (g == 0) {
	return;
    }

    src = &g->data[erts_active_bp_ix()];
    dst = &g->data[erts_staging_bp_ix()];

    /*
     * The contents of the staging area may be out of date.
     * Decrement all reference pointers.
     */

    flags = dst->flags;
    if (flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
	MatchSetUnref(dst->local_ms);
    }
    if (flags & ERTS_BPF_META_TRACE) {
	bp_meta_unref(dst->meta_tracer);
	MatchSetUnref(dst->meta_ms);
    }
    if (flags & ERTS_BPF_COUNT) {
	bp_count_unref(dst->count);
    }
    if (flags & ERTS_BPF_TIME_TRACE) {
	bp_time_unref(dst->time);
    }

    /*
     * If all flags are zero, deallocate all breakpoint data.
     */

    flags = dst->flags = src->flags;
    if (flags == 0) {
	if (modp) {
	    if (local) {
		modp->curr.num_breakpoints--;
	    } else {
		modp->curr.num_traced_exports--;
	    }
	    ASSERT(modp->curr.num_breakpoints >= 0);
	    ASSERT(modp->curr.num_traced_exports >= 0);
	    ASSERT(! BeamIsOpCode(*erts_codeinfo_to_code(ci),
                                  op_i_generic_breakpoint));
	}
	ci->u.gen_bp = NULL;
	Free(g);
	return;
    }

    /*
     * Copy the active data to the staging area (making it ready
     * for the next time it will be used).
     */

    if (flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
	dst->local_ms = src->local_ms;
	MatchSetRef(dst->local_ms);
    }
    if (flags & ERTS_BPF_META_TRACE) {
	dst->meta_tracer = src->meta_tracer;
	erts_refc_inc(&dst->meta_tracer->refc, 1);
	dst->meta_ms = src->meta_ms;
	MatchSetRef(dst->meta_ms);
    }
    if (flags & ERTS_BPF_COUNT) {
	dst->count = src->count;
	erts_refc_inc(&dst->count->refc, 1);
    }
    if (flags & ERTS_BPF_TIME_TRACE) {
	dst->time = src->time;
	erts_refc_inc(&dst->time->refc, 1);
	ASSERT(dst->time->hash);
    }
}

void
erts_commit_staged_bp(void)
{
    ErtsBpIndex staging = erts_staging_bp_ix();
    ErtsBpIndex active = erts_active_bp_ix();

    erts_atomic32_set_nob(&erts_active_bp_index, staging);
    erts_atomic32_set_nob(&erts_staging_bp_index, active);
}

void
erts_install_breakpoints(BpFunctions* f)
{
    Uint i;
    Uint n = f->matched;
    BeamInstr br = BeamOpCodeAddr(op_i_generic_breakpoint);

    for (i = 0; i < n; i++) {
	ErtsCodeInfo* ci = f->matching[i].ci;
	GenericBp* g = ci->u.gen_bp;
        BeamInstr volatile *pc = erts_codeinfo_to_code(ci);
        BeamInstr instr = *pc;

	if (!BeamIsOpCode(instr, op_i_generic_breakpoint) && g) {
	    Module* modp = f->matching[i].mod;

	    /*
	     * The breakpoint must be disabled in the active data
	     * (it will enabled later by switching bp indices),
	     * and enabled in the staging data.
	     */
	    ASSERT(g->data[erts_active_bp_ix()].flags == 0);
	    ASSERT(g->data[erts_staging_bp_ix()].flags != 0);

	    /*
	     * The following write is not protected by any lock. We
	     * assume that the hardware guarantees that a write of an
	     * aligned word-size writes is atomic (i.e. that other
	     * processes executing this code will not see a half
	     * pointer).
             *
             * The contents of *pc is marked 'volatile' to ensure that
             * the compiler will do a single full-word write, and not
             * try any fancy optimizations to write a half word.
	     */
            instr = BeamSetCodeAddr(instr, br);
            *pc = instr;
	    modp->curr.num_breakpoints++;
	}
    }
}

void
erts_uninstall_breakpoints(BpFunctions* f)
{
    Uint i;
    Uint n = f->matched;

    for (i = 0; i < n; i++) {
	uninstall_breakpoint(f->matching[i].ci);
    }
}

static void
uninstall_breakpoint(ErtsCodeInfo *ci)
{
    BeamInstr *pc = erts_codeinfo_to_code(ci);
    if (BeamIsOpCode(*pc, op_i_generic_breakpoint)) {
	GenericBp* g = ci->u.gen_bp;
	if (g->data[erts_active_bp_ix()].flags == 0) {
	    /*
	     * The following write is not protected by any lock. We
	     * assume that the hardware guarantees that a write of an
	     * aligned word-size (or half-word) writes is atomic
	     * (i.e. that other processes executing this code will not
	     * see a half pointer).
	     */
	    *pc = g->orig_instr;
	}
    }
}

void
erts_set_trace_break(BpFunctions* f, Binary *match_spec)
{
    set_break(f, match_spec, ERTS_BPF_LOCAL_TRACE, 0, erts_tracer_true);
}

void
erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, ErtsTracer tracer)
{
    set_break(f, match_spec, ERTS_BPF_META_TRACE, 0, tracer);
}

void
erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local)
{
    Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;

    set_function_break(ci, match_spec, flags, 0, erts_tracer_nil);
}

void
erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec, ErtsTracer tracer)
{
    set_function_break(ci, match_spec, ERTS_BPF_META_TRACE, 0, tracer);
}

void
erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op count_op)
{
    set_function_break(ci, NULL,
		       ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
		       count_op, erts_tracer_nil);
}

void
erts_clear_time_trace_bif(ErtsCodeInfo *ci) {
    clear_function_break(ci, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
}

void
erts_set_debug_break(BpFunctions* f) {
    set_break(f, NULL, ERTS_BPF_DEBUG, 0, erts_tracer_nil);
}

void
erts_set_count_break(BpFunctions* f, enum erts_break_op count_op)
{
    set_break(f, 0, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE,
	      count_op, erts_tracer_nil);
}

void
erts_set_time_break(BpFunctions* f, enum erts_break_op count_op)
{
    set_break(f, 0, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
	      count_op, erts_tracer_nil);
}

void
erts_clear_trace_break(BpFunctions* f)
{
    clear_break(f, ERTS_BPF_LOCAL_TRACE);
}

void
erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local)
{
    GenericBp* g = ci->u.gen_bp;

    if (g) {
	Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
	if (g->data[erts_staging_bp_ix()].flags & flags) {
	    clear_function_break(ci, flags);
	}
    }
}

void
erts_clear_mtrace_break(BpFunctions* f)
{
    clear_break(f, ERTS_BPF_META_TRACE);
}

void
erts_clear_mtrace_bif(ErtsCodeInfo *ci)
{
    clear_function_break(ci, ERTS_BPF_META_TRACE);
}

void
erts_clear_debug_break(BpFunctions* f)
{
    ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
    clear_break(f, ERTS_BPF_DEBUG);
}

void
erts_clear_count_break(BpFunctions* f)
{
    clear_break(f, ERTS_BPF_COUNT|ERTS_BPF_COUNT_ACTIVE);
}

void
erts_clear_time_break(BpFunctions* f)
{
    clear_break(f, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
}

void
erts_clear_all_breaks(BpFunctions* f)
{
    clear_break(f, ERTS_BPF_ALL);
}

int 
erts_clear_module_break(Module *modp) {
    BeamCodeHeader* code_hdr;
    Uint n;
    Uint i;

    ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
    ASSERT(modp);
    code_hdr = modp->curr.code_hdr;
    if (!code_hdr) {
	return 0;
    }
    n = (Uint)(UWord) code_hdr->num_functions;
    for (i = 0; i < n; ++i) {
	ErtsCodeInfo *ci = code_hdr->functions[i];
	if (erts_is_function_native(ci))
	    continue;
	clear_function_break(ci, ERTS_BPF_ALL);
    }

    erts_commit_staged_bp();

    for (i = 0; i < n; ++i) {
	ErtsCodeInfo *ci = code_hdr->functions[i];
	if (erts_is_function_native(ci))
	    continue;
	uninstall_breakpoint(ci);
	consolidate_bp_data(modp, ci, 1);
	ASSERT(ci->u.gen_bp == NULL);
    }
    return n;
}

void
erts_clear_export_break(Module* modp, ErtsCodeInfo *ci)
{
    ERTS_LC_ASSERT(erts_thr_progress_is_blocking());

    clear_function_break(ci, ERTS_BPF_ALL);
    erts_commit_staged_bp();
    *erts_codeinfo_to_code(ci) = (BeamInstr) 0;
    consolidate_bp_data(modp, ci, 0);
    ASSERT(ci->u.gen_bp == NULL);
}

/*
 * If c_p->cp is a trace return instruction, we set cp
 * to be the place where we again start to execute code.
 *
 * cp is used by match spec {caller} to get the calling
 * function, and if we don't do this fixup it will be
 * 'undefined'. This has the odd side effect of {caller}
 * not really being which function is the caller, but
 * rather which function we are about to return to.
 */
static void fixup_cp_before_trace(Process *c_p, int *return_to_trace)
{
    Eterm *cpp, *E = c_p->stop;
    BeamInstr w = *c_p->cp;
    if (BeamIsOpCode(w, op_return_trace)) {
        cpp = &E[2];
    } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
        *return_to_trace = 1;
        cpp = &E[0];
    } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
        cpp = &E[0];
    } else {
        cpp = NULL;
    }
    if (cpp) {
        for (;;) {
            BeamInstr w = *cp_val(*cpp);
            if (BeamIsOpCode(w, op_return_trace)) {
                cpp += 3;
            } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
                *return_to_trace = 1;
                cpp += 1;
            } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
                cpp += 2;
            } else {
                break;
            }
        }
        c_p->cp = (BeamInstr *) cp_val(*cpp);
        ASSERT(is_CP(*cpp));
    }
}

BeamInstr
erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
{
    GenericBp* g;
    GenericBpData* bp;
    Uint bp_flags;
    ErtsBpIndex ix = erts_active_bp_ix();

    ASSERT(BeamIsOpCode(info->op, op_i_func_info_IaaI));

    g = info->u.gen_bp;
    bp = &g->data[ix];
    bp_flags = bp->flags;
    ASSERT((bp_flags & ~ERTS_BPF_ALL) == 0);
    if (bp_flags & (ERTS_BPF_LOCAL_TRACE|
		    ERTS_BPF_GLOBAL_TRACE|
		    ERTS_BPF_TIME_TRACE_ACTIVE) &&
	!IS_TRACED_FL(c_p, F_TRACE_CALLS)) {
	bp_flags &= ~(ERTS_BPF_LOCAL_TRACE|
		      ERTS_BPF_GLOBAL_TRACE|
		      ERTS_BPF_TIME_TRACE|
		      ERTS_BPF_TIME_TRACE_ACTIVE);
	if (bp_flags == 0) {	/* Quick exit */
	    return g->orig_instr;
	}
    }

    if (bp_flags & ERTS_BPF_LOCAL_TRACE) {
	ASSERT((bp_flags & ERTS_BPF_GLOBAL_TRACE) == 0);
	(void) do_call_trace(c_p, info, reg, 1, bp->local_ms, erts_tracer_true);
    } else if (bp_flags & ERTS_BPF_GLOBAL_TRACE) {
	(void) do_call_trace(c_p, info, reg, 0, bp->local_ms, erts_tracer_true);
    }

    if (bp_flags & ERTS_BPF_META_TRACE) {
	ErtsTracer old_tracer, new_tracer;

	old_tracer = erts_atomic_read_nob(&bp->meta_tracer->tracer);

	new_tracer = do_call_trace(c_p, info, reg, 1, bp->meta_ms, old_tracer);

	if (!ERTS_TRACER_COMPARE(new_tracer, old_tracer)) {
            if (old_tracer == erts_atomic_cmpxchg_acqb(
                    &bp->meta_tracer->tracer,
                    (erts_aint_t)new_tracer,
                    (erts_aint_t)old_tracer)) {
                ERTS_TRACER_CLEAR(&old_tracer);
            } else {
                ERTS_TRACER_CLEAR(&new_tracer);
            }
	}
    }

    if (bp_flags & ERTS_BPF_COUNT_ACTIVE) {
	erts_atomic_inc_nob(&bp->count->acount);
    }

    if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) {
	Eterm w;
	erts_trace_time_call(c_p, info, bp->time);
	w = (BeamInstr) *c_p->cp;
	if (! (BeamIsOpCode(w, op_i_return_time_trace) ||
	       BeamIsOpCode(w, op_return_trace) ||
               BeamIsOpCode(w, op_i_return_to_trace)) ) {
	    Eterm* E = c_p->stop;
	    ASSERT(c_p->htop <= E && E <= c_p->hend);
	    if (E - 2 < c_p->htop) {
		(void) erts_garbage_collect(c_p, 2, reg, info->mfa.arity);
		ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
	    }
	    E = c_p->stop;

	    ASSERT(c_p->htop <= E && E <= c_p->hend);

	    E -= 2;
	    E[0] = make_cp(erts_codeinfo_to_code(info));
	    E[1] = make_cp(c_p->cp);     /* original return address */
	    c_p->cp = beam_return_time_trace;
	    c_p->stop = E;
	}
    }

    if (bp_flags & ERTS_BPF_DEBUG) {
	return BeamOpCodeAddr(op_i_debug_breakpoint);
    } else {
	return g->orig_instr;
    }
}

/*
 * Entry point called by the trace wrap functions in erl_bif_wrap.c
 *
 * The trace wrap functions are themselves called through the export
 * entries instead of the original BIF functions.
 */
Eterm
erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
{
    Eterm result;
    Eterm (*func)(Process*, Eterm*, BeamInstr*);
    Export* ep = bif_export[bif_index];
    Uint32 flags = 0, flags_meta = 0;
    ErtsTracer meta_tracer = erts_tracer_nil;
    int applying = (I == ep->beam); /* Yup, the apply code for a bif
                                      * is actually in the
                                      * export entry */
    BeamInstr *cp = p->cp;
    GenericBp* g;
    GenericBpData* bp = NULL;
    Uint bp_flags = 0;
    int return_to_trace = 0;

    ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);

    g = ep->info.u.gen_bp;
    if (g) {
	bp = &g->data[erts_active_bp_ix()];
	bp_flags = bp->flags;
    }

    /*
     * Make continuation pointer OK, it is not during direct BIF calls,
     * but it is correct during apply of bif.
     */
    if (!applying) {
	p->cp = I;
    } else {
        fixup_cp_before_trace(p, &return_to_trace);
    }
    if (bp_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE) &&
	IS_TRACED_FL(p, F_TRACE_CALLS)) {
	int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE);
	flags = erts_call_trace(p, &ep->info, bp->local_ms, args,
				local, &ERTS_TRACER(p));
    }
    if (bp_flags & ERTS_BPF_META_TRACE) {
	ErtsTracer old_tracer;

        meta_tracer = erts_atomic_read_nob(&bp->meta_tracer->tracer);
        old_tracer = meta_tracer;
	flags_meta = erts_call_trace(p, &ep->info, bp->meta_ms, args,
				     0, &meta_tracer);

	if (!ERTS_TRACER_COMPARE(old_tracer, meta_tracer)) {
            ErtsTracer new_tracer = erts_tracer_nil;
            erts_tracer_update(&new_tracer, meta_tracer);
	    if (old_tracer == erts_atomic_cmpxchg_acqb(
                    &bp->meta_tracer->tracer,
                    (erts_aint_t)new_tracer,
                    (erts_aint_t)old_tracer)) {
                ERTS_TRACER_CLEAR(&old_tracer);
            } else {
                ERTS_TRACER_CLEAR(&new_tracer);
            }
	}
    }
    if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE &&
	IS_TRACED_FL(p, F_TRACE_CALLS)) {
	erts_trace_time_call(p, &ep->info, bp->time);
    }

    /* Restore original continuation pointer (if changed). */
    p->cp = cp;

    func = bif_table[bif_index].f;

    result = func(p, args, I);

    if (erts_nif_export_check_save_trace(p, result,
					 applying, ep,
					 cp, flags,
					 flags_meta, I,
					 meta_tracer)) {
	/*
	 * erts_bif_trace_epilogue() will be called
	 * later when appropriate via the NIF export
	 * scheduling functionality...
	 */
	return result;
    }

    return erts_bif_trace_epilogue(p, result, applying, ep, cp,
				   flags, flags_meta, I,
				   meta_tracer);
}

Eterm
erts_bif_trace_epilogue(Process *p, Eterm result, int applying,
			Export* ep, BeamInstr *cp, Uint32 flags,
			Uint32 flags_meta, BeamInstr* I,
			ErtsTracer meta_tracer)
{
    if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) {
	BeamInstr i_return_trace      = beam_return_trace[0];
	BeamInstr i_return_to_trace   = beam_return_to_trace[0];
	BeamInstr i_return_time_trace = beam_return_time_trace[0];
	Eterm *cpp;
	/* Maybe advance cp to skip trace stack frames */
	for (cpp = p->stop;  ;  cp = cp_val(*cpp++)) {
	    if (*cp == i_return_trace) {
		/* Skip stack frame variables */
		while (is_not_CP(*cpp)) cpp++;
		cpp += 2; /* Skip return_trace parameters */
	    } else if (*cp == i_return_time_trace) {
		/* Skip stack frame variables */
		while (is_not_CP(*cpp)) cpp++;
		cpp += 1; /* Skip return_time_trace parameters */
	    } else if (*cp == i_return_to_trace) {
		/* A return_to trace message is going to be generated
		 * by normal means, so we do not have to.
		 */
		cp = NULL;
		break;
	    } else break;
	}
    }

    /* Try to get these in the order
     * they usually appear in normal code... */
    if (is_non_value(result)) {
	Uint reason = p->freason;
	if (reason != TRAP) {
	    Eterm class;
	    Eterm value = p->fvalue;
	    /* Expand error value like in handle_error() */
	    if (reason & EXF_ARGLIST) {
		Eterm *tp;
		ASSERT(is_tuple(value));
		tp = tuple_val(value);
		value = tp[1];
	    }
	    if ((reason & EXF_THROWN) && (p->catches <= 0)) {
                Eterm *hp = HAlloc(p, 3);
		value = TUPLE2(hp, am_nocatch, value);
		reason = EXC_ERROR;
	    }
	    /* Note: expand_error_value() could theoretically
	     * allocate on the heap, but not for any error
	     * returned by a BIF, and it would do no harm,
	     * just be annoying.
	     */
	    value = expand_error_value(p, reason, value);
	    class = exception_tag[GET_EXC_CLASS(reason)];

	    if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
		erts_trace_exception(p, &ep->info.mfa, class, value,
				     &meta_tracer);
	    }
	    if (flags & MATCH_SET_EXCEPTION_TRACE) {
		erts_trace_exception(p, &ep->info.mfa, class, value,
				     &ERTS_TRACER(p));
	    }
	    if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
		/* can only happen if(local)*/
		Eterm *ptr = p->stop;
		ASSERT(is_CP(*ptr));
		ASSERT(ptr <= STACK_START(p));
		/* Search the nearest stack frame for a catch */
		while (++ptr < STACK_START(p)) {
		    if (is_CP(*ptr)) break;
		    if (is_catch(*ptr)) {
			if (applying) {
			    /* Apply of BIF, cp is in calling function */
			    if (cp) erts_trace_return_to(p, cp);
			} else {
			    /* Direct bif call, I points into
			     * calling function */
			    erts_trace_return_to(p, I);
			}
		    }
		}
	    }
	    if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
		erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
		ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE;
		erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
	    }
	}
    } else {
	if (flags_meta & MATCH_SET_RX_TRACE) {
	    erts_trace_return(p, &ep->info.mfa, result, &meta_tracer);
	}
	/* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
	if (flags & MATCH_SET_RX_TRACE) {
	    erts_trace_return(p, &ep->info.mfa, result, &ERTS_TRACER(p));
	}
	if (flags & MATCH_SET_RETURN_TO_TRACE &&
            IS_TRACED_FL(p, F_TRACE_RETURN_TO)) {
	    /* can only happen if(local)*/
	    if (applying) {
		/* Apply of BIF, cp is in calling function */
		if (cp) erts_trace_return_to(p, cp);
	    } else {
		/* Direct bif call, I points into calling function */
		erts_trace_return_to(p, I);
	    }
	}
    }
    ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
    return result;
}

static ErtsTracer
do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg,
	      int local, Binary* ms, ErtsTracer tracer)
{
    int return_to_trace = 0;
    BeamInstr *cp_save = c_p->cp;
    Uint32 flags;
    Uint need = 0;
    Eterm* E = c_p->stop;

    fixup_cp_before_trace(c_p, &return_to_trace);

    ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
    flags = erts_call_trace(c_p, info, ms, reg, local, &tracer);
    ERTS_REQ_PROC_MAIN_LOCK(c_p);

    /* restore cp after potential fixup */
    c_p->cp = cp_save;

    ASSERT(!ERTS_PROC_IS_EXITING(c_p));
    if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
	need += 1;
    }
    if (flags & MATCH_SET_RX_TRACE) {
	need += 3 + size_object(tracer);
    }
    if (need) {
	ASSERT(c_p->htop <= E && E <= c_p->hend);
	if (E - need < c_p->htop) {
	    (void) erts_garbage_collect(c_p, need, reg, info->mfa.arity);
	    ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
	    E = c_p->stop;
	}
    }
    if (flags & MATCH_SET_RETURN_TO_TRACE && !return_to_trace) {
	E -= 1;
	ASSERT(c_p->htop <= E && E <= c_p->hend);
	E[0] = make_cp(c_p->cp);
	c_p->cp = beam_return_to_trace;
    }
    if (flags & MATCH_SET_RX_TRACE)
    {
	E -= 3;
        c_p->stop = E;
	ASSERT(c_p->htop <= E && E <= c_p->hend);
	ASSERT(is_CP((Eterm) (UWord) (&info->mfa.module)));
	ASSERT(IS_TRACER_VALID(tracer));
	E[2] = make_cp(c_p->cp);
        E[1] = copy_object(tracer, c_p);
	E[0] = make_cp(&info->mfa.module);
                               /* We ARE at the beginning of an instruction,
				  the funcinfo is above i. */
	c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE) ?
	    beam_exception_trace : beam_return_trace;
	erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
	ERTS_TRACE_FLAGS(c_p) |= F_EXCEPTION_TRACE;
	erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
    } else
        c_p->stop = E;
    return tracer;
}

void
erts_trace_time_call(Process* c_p, ErtsCodeInfo *info, BpDataTime* bdt)
{
    ErtsMonotonicTime time;
    process_breakpoint_time_t *pbt = NULL;
    bp_data_time_item_t sitem, *item = NULL;
    bp_time_hash_t *h = NULL;
    BpDataTime *pbdt = NULL;
    Uint32 six = acquire_bp_sched_ix(c_p);

    ASSERT(c_p);
    ASSERT(erts_atomic32_read_acqb(&c_p->state) & (ERTS_PSFLG_RUNNING
						       | ERTS_PSFLG_DIRTY_RUNNING));

    /* get previous timestamp and breakpoint
     * from the process psd  */
    
    pbt = ERTS_PROC_GET_CALL_TIME(c_p);
    time = get_mtime(c_p);

    /* get pbt
     * timestamp = t0
     * lookup bdt from code
     * set ts0 to pbt
     * add call count here?
     */
    if (pbt == 0) {
	/* First call of process to instrumented function */
	pbt = Alloc(sizeof(process_breakpoint_time_t));
	(void) ERTS_PROC_SET_CALL_TIME(c_p, pbt);
    } else {
	ASSERT(pbt->ci);
	/* add time to previous code */
	sitem.time = time - pbt->time;
	sitem.pid = c_p->common.id;
	sitem.count = 0;

	/* previous breakpoint */
	pbdt = get_time_break(pbt->ci);

	/* if null then the breakpoint was removed */
	if (pbdt) {
	    h = &(pbdt->hash[six]);

	    ASSERT(h);
	    ASSERT(h->item);

	    item = bp_hash_get(h, &sitem);
	    if (!item) {
		item = bp_hash_put(h, &sitem);
	    } else {
		BP_TIME_ADD(item, &sitem);
	    }
	}
    }

    /* Add count to this code */
    sitem.pid     = c_p->common.id;
    sitem.count   = 1;
    sitem.time    = 0;

    /* this breakpoint */
    ASSERT(bdt);
    h = &(bdt->hash[six]);

    ASSERT(h);
    ASSERT(h->item);

    item = bp_hash_get(h, &sitem);
    if (!item) {
	item = bp_hash_put(h, &sitem);
    } else {
	BP_TIME_ADD(item, &sitem);
    }

    pbt->ci = info;
    pbt->time = time;

    release_bp_sched_ix(six);
}

void
erts_trace_time_return(Process *p, ErtsCodeInfo *ci)
{
    ErtsMonotonicTime time;
    process_breakpoint_time_t *pbt = NULL;
    bp_data_time_item_t sitem, *item = NULL;
    bp_time_hash_t *h = NULL;
    BpDataTime *pbdt = NULL;
    Uint32 six = acquire_bp_sched_ix(p);

    ASSERT(p);
    ASSERT(erts_atomic32_read_acqb(&p->state) & (ERTS_PSFLG_RUNNING
						     | ERTS_PSFLG_DIRTY_RUNNING));

    /* get previous timestamp and breakpoint
     * from the process psd  */

    pbt = ERTS_PROC_GET_CALL_TIME(p);
    time = get_mtime(p);

    /* get pbt
     * lookup bdt from code
     * timestamp = t1
     * get ts0 from pbt
     * get item from bdt->hash[bp_hash(p->id)]
     * ack diff (t1, t0) to item
     */

    if (pbt) {

	/* might have been removed due to
	 * trace_pattern(false)
	 */
	ASSERT(pbt->ci);

	sitem.time = time - pbt->time;
	sitem.pid   = p->common.id;
	sitem.count = 0;

	/* previous breakpoint */
	pbdt = get_time_break(pbt->ci);

	/* beware, the trace_pattern might have been removed */
	if (pbdt) {

	    h = &(pbdt->hash[six]);

	    ASSERT(h);
	    ASSERT(h->item);

	    item = bp_hash_get(h, &sitem);
	    if (!item) {
		item = bp_hash_put(h, &sitem);
	    } else {
		BP_TIME_ADD(item, &sitem);
	    }

	}

	pbt->ci = ci;
	pbt->time = time;

    }

    release_bp_sched_ix(six);
}

int 
erts_is_trace_break(ErtsCodeInfo *ci, Binary **match_spec_ret, int local)
{
    Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
    GenericBpData* bp = check_break(ci, flags);

    if (bp) {
	if (match_spec_ret) {
	    *match_spec_ret = bp->local_ms;
	}
	return 1;
    }
    return 0;
}

int
erts_is_mtrace_break(ErtsCodeInfo *ci, Binary **match_spec_ret,
		     ErtsTracer *tracer_ret)
{
    GenericBpData* bp = check_break(ci, ERTS_BPF_META_TRACE);
    
    if (bp) {
	if (match_spec_ret) {
	    *match_spec_ret = bp->meta_ms;
	}
	if (tracer_ret) {
            *tracer_ret = erts_atomic_read_nob(&bp->meta_tracer->tracer);
	}
	return 1;
    }
    return 0;
}

int 
erts_is_count_break(ErtsCodeInfo *ci, Uint *count_ret)
{
    GenericBpData* bp = check_break(ci, ERTS_BPF_COUNT);
    
    if (bp) {
	if (count_ret) {
	    *count_ret = (Uint) erts_atomic_read_nob(&bp->count->acount);
	}
	return 1;
    }
    return 0;
}

int erts_is_time_break(Process *p, ErtsCodeInfo *ci, Eterm *retval) {
    Uint i, ix;
    bp_time_hash_t hash;
    Uint size;
    Eterm *hp, t;
    bp_data_time_item_t *item = NULL;
    BpDataTime *bdt = get_time_break(ci);

    if (bdt) {
	if (retval) {
	    /* collect all hashes to one hash */
	    bp_hash_init(&hash, 64);
	    /* foreach threadspecific hash */
	    for (i = 0; i < bdt->n; i++) {
		bp_data_time_item_t *sitem;

	        /* foreach hash bucket not NIL*/
		for(ix = 0; ix < bdt->hash[i].n; ix++) {
		    item = &(bdt->hash[i].item[ix]);
		    if (item->pid != NIL) {
			sitem = bp_hash_get(&hash, item);
			if (sitem) {
			    BP_TIME_ADD(sitem, item);
			} else {
			    bp_hash_put(&hash, item);
			}
		    }
		}
	    }
	    /* *retval should be NIL or term from previous bif in export entry */

	    if (hash.used > 0) {
		size = (5 + 2)*hash.used;
		hp   = HAlloc(p, size);

		for(ix = 0; ix < hash.n; ix++) {
		    item = &(hash.item[ix]);
		    if (item->pid != NIL) {
			ErtsMonotonicTime sec, usec;
			usec = ERTS_MONOTONIC_TO_USEC(item->time);
			sec = usec / 1000000;
			usec = usec - sec*1000000;
			t = TUPLE4(hp, item->pid,
				make_small(item->count),
				   make_small((Uint) sec),
				   make_small((Uint) usec));
			hp += 5;
			*retval = CONS(hp, t, *retval); hp += 2;
		    }
		}
	    }
	    bp_hash_delete(&hash);
	}
	return 1;
    }

    return 0;
}


ErtsCodeInfo *
erts_find_local_func(ErtsCodeMFA *mfa) {
    Module *modp;
    BeamCodeHeader* code_hdr;
    ErtsCodeInfo* ci;
    Uint i,n;

    if ((modp = erts_get_module(mfa->module, erts_active_code_ix())) == NULL)
	return NULL;
    if ((code_hdr = modp->curr.code_hdr) == NULL)
	return NULL;
    n = (BeamInstr) code_hdr->num_functions;
    for (i = 0; i < n; ++i) {
	ci = code_hdr->functions[i];
	ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
	ASSERT(mfa->module == ci->mfa.module || is_nil(ci->mfa.module));
	if (mfa->function == ci->mfa.function &&
	    mfa->arity == ci->mfa.arity) {
	    return ci;
	}
    }
    return NULL;
}

static void bp_hash_init(bp_time_hash_t *hash, Uint n) {
    Uint size = sizeof(bp_data_time_item_t)*n;
    Uint i;

    hash->n    = n;
    hash->used = 0;

    hash->item = (bp_data_time_item_t *)Alloc(size);
    sys_memzero(hash->item, size);

    for(i = 0; i < n; ++i) {
	hash->item[i].pid = NIL;
    }
}

static void bp_hash_rehash(bp_time_hash_t *hash, Uint n) {
    bp_data_time_item_t *item = NULL;
    Uint size = sizeof(bp_data_time_item_t)*n;
    Uint ix;
    Uint hval;

    item = (bp_data_time_item_t *)Alloc(size);
    sys_memzero(item, size);

    for( ix = 0; ix < n; ++ix) {
	item[ix].pid = NIL;
    }

    /* rehash, old hash -> new hash */

    for( ix = 0; ix < hash->n; ix++) {
	if (hash->item[ix].pid != NIL) {

	    hval = ((hash->item[ix].pid) >> 4) % n; /* new n */

	    while (item[hval].pid != NIL) {
		hval = (hval + 1) % n;
	    }
	    item[hval].pid     = hash->item[ix].pid;
	    item[hval].count   = hash->item[ix].count;
	    item[hval].time    = hash->item[ix].time;
	}
    }

    Free(hash->item);
    hash->n = n;
    hash->item = item;
}
static ERTS_INLINE bp_data_time_item_t * bp_hash_get(bp_time_hash_t *hash, bp_data_time_item_t *sitem) {
    Eterm pid = sitem->pid;
    Uint hval = (pid >> 4) % hash->n;
    bp_data_time_item_t *item = NULL;

    item = hash->item;

    while (item[hval].pid != pid) {
	if (item[hval].pid == NIL) return NULL;
	hval = (hval + 1) % hash->n;
    }

    return &(item[hval]);
}

static ERTS_INLINE bp_data_time_item_t * bp_hash_put(bp_time_hash_t *hash, bp_data_time_item_t* sitem) {
    Uint hval;
    float r = 0.0;
    bp_data_time_item_t *item;

    /* make sure that the hash is not saturated */
    /* if saturated, rehash it */

    r = hash->used / (float) hash->n;

    if (r > 0.7f) {
	bp_hash_rehash(hash, hash->n * 2);
    }
    /* Do hval after rehash */
    hval = (sitem->pid >> 4) % hash->n;

    /* find free slot */
    item = hash->item;

    while (item[hval].pid != NIL) {
	hval = (hval + 1) % hash->n;
    }
    item = &(hash->item[hval]);

    item->pid     = sitem->pid;
    item->time    = sitem->time;
    item->count   = sitem->count;
    hash->used++;

    return item;
}

static void bp_hash_delete(bp_time_hash_t *hash) {
    hash->n = 0;
    hash->used = 0;
    Free(hash->item);
    hash->item = NULL;
}

void erts_schedule_time_break(Process *p, Uint schedule) {
    process_breakpoint_time_t *pbt = NULL;
    bp_data_time_item_t sitem, *item = NULL;
    bp_time_hash_t *h = NULL;
    BpDataTime *pbdt = NULL;
    Uint32 six = acquire_bp_sched_ix(p);

    ASSERT(p);

    pbt = ERTS_PROC_GET_CALL_TIME(p);

    if (pbt) {

	switch(schedule) {
	case ERTS_BP_CALL_TIME_SCHEDULE_EXITING :
	    break;
	case ERTS_BP_CALL_TIME_SCHEDULE_OUT :
	    /* When a process is scheduled _out_,
	     * timestamp it and add its delta to
	     * the previous breakpoint.
	     */

	    pbdt = get_time_break(pbt->ci);
	    if (pbdt) {
		sitem.time = get_mtime(p) - pbt->time;
		sitem.pid   = p->common.id;
		sitem.count = 0;

		h = &(pbdt->hash[six]);

		ASSERT(h);
		ASSERT(h->item);

		item = bp_hash_get(h, &sitem);
		if (!item) {
		    item = bp_hash_put(h, &sitem);
		} else {
		    BP_TIME_ADD(item, &sitem);
		}
	    }
	    break;
	case ERTS_BP_CALL_TIME_SCHEDULE_IN :
	    /* When a process is scheduled _in_,
	     * timestamp it and remove the previous
	     * timestamp in the psd.
	     */
	    pbt->time = get_mtime(p);
	    break;
	default :
	    ASSERT(0);
		/* will never happen */
	    break;
	}
    } /* pbt */

    release_bp_sched_ix(six);
}

/* *************************************************************************
** Local helpers
*/


static void
set_break(BpFunctions* f, Binary *match_spec, Uint break_flags,
	  enum erts_break_op count_op, ErtsTracer tracer)
{
    Uint i;
    Uint n;

    n = f->matched;
    for (i = 0; i < n; i++) {
	set_function_break(f->matching[i].ci,
                           match_spec, break_flags,
			   count_op, tracer);
    }
}

static void
set_function_break(ErtsCodeInfo *ci, Binary *match_spec, Uint break_flags,
		   enum erts_break_op count_op, ErtsTracer tracer)
{
    GenericBp* g;
    GenericBpData* bp;
    Uint common;
    ErtsBpIndex ix = erts_staging_bp_ix();

    ERTS_LC_ASSERT(erts_has_code_write_permission());
    g = ci->u.gen_bp;
    if (g == 0) {
	int i;
	if (count_op == ERTS_BREAK_RESTART || count_op == ERTS_BREAK_PAUSE) {
	    /* Do not insert a new breakpoint */
	    return;
	}
	g = Alloc(sizeof(GenericBp));
	g->orig_instr = *erts_codeinfo_to_code(ci);
	for (i = 0; i < ERTS_NUM_BP_IX; i++) {
	    g->data[i].flags = 0;
	}
	ci->u.gen_bp = g;
    }
    bp = &g->data[ix];

    /*
     * If we are changing an existing breakpoint, clean up old data.
     */

    common = break_flags & bp->flags;
    if (common & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
	MatchSetUnref(bp->local_ms);
    } else if (common & ERTS_BPF_META_TRACE) {
	MatchSetUnref(bp->meta_ms);
	bp_meta_unref(bp->meta_tracer);
    } else if (common & ERTS_BPF_COUNT) {
	if (count_op == ERTS_BREAK_PAUSE) {
	    bp->flags &= ~ERTS_BPF_COUNT_ACTIVE;
	} else {
	    bp->flags |= ERTS_BPF_COUNT_ACTIVE;
	    erts_atomic_set_nob(&bp->count->acount, 0);
	}
	ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
	return;
    } else if (common & ERTS_BPF_TIME_TRACE) {
	BpDataTime* bdt = bp->time;
	Uint i = 0;

	if (count_op == ERTS_BREAK_PAUSE) {
	    bp->flags &= ~ERTS_BPF_TIME_TRACE_ACTIVE;
	} else {
	    bp->flags |= ERTS_BPF_TIME_TRACE_ACTIVE;
	    for (i = 0; i < bdt->n; i++) {
		bp_hash_delete(&(bdt->hash[i]));
		bp_hash_init(&(bdt->hash[i]), 32);
	    }
	}
	ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
	return;
    }

    /*
     * Initialize the new breakpoint data.
     */

    if (break_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
	MatchSetRef(match_spec);
	bp->local_ms = match_spec;
    } else if (break_flags & ERTS_BPF_META_TRACE) {
	BpMetaTracer* bmt;
        ErtsTracer meta_tracer = erts_tracer_nil;
	MatchSetRef(match_spec);
	bp->meta_ms = match_spec;
	bmt = Alloc(sizeof(BpMetaTracer));
	erts_refc_init(&bmt->refc, 1);
        erts_tracer_update(&meta_tracer, tracer); /* copy tracer */
	erts_atomic_init_nob(&bmt->tracer, (erts_aint_t)meta_tracer);
	bp->meta_tracer = bmt;
    } else if (break_flags & ERTS_BPF_COUNT) {
	BpCount* bcp;

	ASSERT((bp->flags & ERTS_BPF_COUNT) == 0);
	bcp = Alloc(sizeof(BpCount));
	erts_refc_init(&bcp->refc, 1);
	erts_atomic_init_nob(&bcp->acount, 0);
	bp->count = bcp;
    } else if (break_flags & ERTS_BPF_TIME_TRACE) {
	BpDataTime* bdt;
	int i;

	ASSERT((bp->flags & ERTS_BPF_TIME_TRACE) == 0);
	bdt = Alloc(sizeof(BpDataTime));
	erts_refc_init(&bdt->refc, 1);
	bdt->n = erts_no_schedulers + 1;
	bdt->hash = Alloc(sizeof(bp_time_hash_t)*(bdt->n));
	for (i = 0; i < bdt->n; i++) {
	    bp_hash_init(&(bdt->hash[i]), 32);
	}
	bp->time = bdt;
    }

    bp->flags |= break_flags;
    ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
}

static void
clear_break(BpFunctions* f, Uint break_flags)
{
    Uint i;
    Uint n;

    n = f->matched;
    for (i = 0; i < n; i++) {
	clear_function_break(f->matching[i].ci, break_flags);
    }
}

static int
clear_function_break(ErtsCodeInfo *ci, Uint break_flags)
{
    GenericBp* g;
    GenericBpData* bp;
    Uint common;
    ErtsBpIndex ix = erts_staging_bp_ix();

    ERTS_LC_ASSERT(erts_has_code_write_permission());

    if ((g = ci->u.gen_bp) == NULL) {
	return 1;
    }

    bp = &g->data[ix];
    ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
    common = bp->flags & break_flags;
    bp->flags &= ~break_flags;
    if (common & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE)) {
	MatchSetUnref(bp->local_ms);
    }
    if (common & ERTS_BPF_META_TRACE) {
	MatchSetUnref(bp->meta_ms);
	bp_meta_unref(bp->meta_tracer);
    }
    if (common & ERTS_BPF_COUNT) {
	ASSERT((bp->flags & ERTS_BPF_COUNT_ACTIVE) == 0);
	bp_count_unref(bp->count);
    }
    if (common & ERTS_BPF_TIME_TRACE) {
	ASSERT((bp->flags & ERTS_BPF_TIME_TRACE_ACTIVE) == 0);
	bp_time_unref(bp->time);
    }

    ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
    return 1;
}

static void
bp_meta_unref(BpMetaTracer* bmt)
{
    if (erts_refc_dectest(&bmt->refc, 0) <= 0) {
        ErtsTracer trc = erts_atomic_read_nob(&bmt->tracer);
        ERTS_TRACER_CLEAR(&trc);
	Free(bmt);
    }
}

static void
bp_count_unref(BpCount* bcp)
{
    if (erts_refc_dectest(&bcp->refc, 0) <= 0) {
	Free(bcp);
    }
}

static void
bp_time_unref(BpDataTime* bdt)
{
    if (erts_refc_dectest(&bdt->refc, 0) <= 0) {
	Uint i = 0;
	Uint j = 0;
	Process *h_p = NULL;
	bp_data_time_item_t* item = NULL;
	process_breakpoint_time_t* pbt = NULL;

	/* remove all psd associated with the hash
	 * and then delete the hash.
	 * ... sigh ...
	 */

	for (i = 0; i < bdt->n; ++i) {
	    if (bdt->hash[i].used) {
		for (j = 0; j < bdt->hash[i].n; ++j) {
		    item = &(bdt->hash[i].item[j]);
		    if (item->pid != NIL) {
			h_p = erts_pid2proc(NULL, 0, item->pid,
					    ERTS_PROC_LOCK_MAIN);
			if (h_p) {
			    pbt = ERTS_PROC_SET_CALL_TIME(h_p, NULL);
			    if (pbt) {
				Free(pbt);
			    }
			    erts_proc_unlock(h_p, ERTS_PROC_LOCK_MAIN);
			}
		    }
		}
	    }
	    bp_hash_delete(&(bdt->hash[i]));
	}
	Free(bdt->hash);
	Free(bdt);
    }
}

static BpDataTime*
get_time_break(ErtsCodeInfo *ci)
{
    GenericBpData* bp = check_break(ci, ERTS_BPF_TIME_TRACE);
    return bp ? bp->time : 0;
}

static GenericBpData*
check_break(ErtsCodeInfo *ci, Uint break_flags)
{
    GenericBp* g = ci->u.gen_bp;

    ASSERT(BeamIsOpCode(ci->op, op_i_func_info_IaaI));
    if (erts_is_function_native(ci)) {
	return 0;
    }
    if (g) {
	GenericBpData* bp = &g->data[erts_active_bp_ix()];
	ASSERT((bp->flags & ~ERTS_BPF_ALL) == 0);
	if (bp->flags & break_flags) {
	    return bp;
	}
    }
    return 0;
}