text: /*
   * %CopyrightBegin%
   *
   * Copyright Ericsson AB 1996-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%
   */
  /* This File contains functions which are called if a user hits ^C */
  
  #ifdef HAVE_CONFIG_H
  #  include "config.h"
  #endif
  
  #include "sys.h"
  #include "erl_vm.h"
  #include "global.h"
  #include "erl_process.h"
  #include "version.h"
  #include "error.h"
  #include "version.h"
  #include "erl_db.h"
  #include "bif.h"
  #include "erl_version.h"
  #include "hash.h"
  #include "atom.h"
  #include "beam_load.h"
  #include "erl_instrument.h"
  #include "erl_hl_timer.h"
  #include "erl_thr_progress.h"
  
  /* Forward declarations -- should really appear somewhere else */
  static void process_killer(void);
  void do_break(void);
  void erl_crash_dump_v(char *file, int line, char* fmt, va_list args);
  
  #ifdef DEBUG
  static void bin_check(void);
  #endif
  
  static void print_garb_info(fmtfn_t to, void *to_arg, Process* p);
  #ifdef OPPROF
  static void dump_frequencies(void);
  #endif
  
  static void dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size);
  
  extern char* erts_system_version[];
  
  static void
  port_info(fmtfn_t to, void *to_arg)
  {
      int i, max = erts_ptab_max(&erts_port);
      for (i = 0; i < max; i++) {
  	Port *p = erts_pix2port(i);
  	if (p)
  	    print_port_info(p, to, to_arg);
      }
  }
  
  void
  process_info(fmtfn_t to, void *to_arg)
  {
      int i, max = erts_ptab_max(&erts_proc);
      for (i = 0; i < max; i++) {
  	Process *p = erts_pix2proc(i);
  	if (p && p->i != ENULL) {
  	    /* Do not include processes with no heap,
  	     * they are most likely just created and has invalid data
  	     */
  	    if (!ERTS_PROC_IS_EXITING(p) && p->heap != NULL)
  		print_process_info(to, to_arg, p);
  	}
      }
  
      port_info(to, to_arg);
  }
  
  static void
  process_killer(void)
  {
      int i, j, max = erts_ptab_max(&erts_proc);
      Process* rp;
  
      erts_printf("\n\nProcess Information\n\n");
      erts_printf("--------------------------------------------------\n");
      for (i = max-1; i >= 0; i--) {
  	rp = erts_pix2proc(i);
  	if (rp && rp->i != ENULL) {
  	    int br;
  	    print_process_info(ERTS_PRINT_STDOUT, NULL, rp);
  	    erts_printf("(k)ill (n)ext (r)eturn:\n");
  	    while(1) {
  		if ((j = sys_get_key(0)) <= 0)
  		    erts_exit(0, "");
  		switch(j) {
  		case 'k': {
  		    ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND;
  		    erts_aint32_t state;
  		    erts_proc_inc_refc(rp);
  		    erts_proc_lock(rp, rp_locks);
  		    state = erts_atomic32_read_acqb(&rp->state);
  		    if (state & (ERTS_PSFLG_FREE
  				 | ERTS_PSFLG_EXITING
  				 | ERTS_PSFLG_ACTIVE
  				 | ERTS_PSFLG_ACTIVE_SYS
  				 | ERTS_PSFLG_IN_RUNQ
  				 | ERTS_PSFLG_RUNNING
  				 | ERTS_PSFLG_RUNNING_SYS
  				 | ERTS_PSFLG_DIRTY_RUNNING
  				 | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
  			erts_printf("Can only kill WAITING processes this way\n");
  		    }
  		    else {
  			(void) erts_send_exit_signal(NULL,
  						     NIL,
  						     rp,
  						     &rp_locks,
  						     am_kill,
  						     NIL,
  						     NULL,
  						     0);
  		    }
  		    erts_proc_unlock(rp, rp_locks);
  		    erts_proc_dec_refc(rp);
  		}
  		case 'n': br = 1; break;
  		case 'r': return;
  		default: return;
  		}
  		if (br == 1) break;
  	    }
  	}
      }
  }
  
  typedef struct {
      int is_first;
      fmtfn_t to;
      void *to_arg;
  } PrintMonitorContext;
  
  static void doit_print_link(ErtsLink *lnk, void *vpcontext)
  {
      PrintMonitorContext *pcontext = vpcontext;
      fmtfn_t to = pcontext->to;
      void *to_arg = pcontext->to_arg;
  
      if (pcontext->is_first) {
  	pcontext->is_first = 0;
  	erts_print(to, to_arg, "%T", lnk->pid);
      } else {
  	erts_print(to, to_arg, ", %T", lnk->pid);
      }
  }
      
  
  static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext)
  {
      PrintMonitorContext *pcontext = vpcontext;
      fmtfn_t to = pcontext->to;
      void *to_arg = pcontext->to_arg;
      char *prefix = ", ";
   
      if (pcontext->is_first) {
  	pcontext->is_first = 0;
  	prefix = "";
      }
  
      switch (mon->type) {
      case MON_ORIGIN:
  	if (is_atom(mon->u.pid)) { /* dist by name */
  	    ASSERT(is_node_name_atom(mon->u.pid));
  	    erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name,
  		       mon->u.pid, mon->ref);
  	} else if (is_atom(mon->name)){ /* local by name */
  	    erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name,
  		       erts_this_dist_entry->sysname, mon->ref);
  	} else { /* local and distributed by pid */
  	    erts_print(to, to_arg, "%s{to,%T,%T}", prefix, mon->u.pid, mon->ref);
  	}
  	break;
      case MON_TARGET:
  	erts_print(to, to_arg, "%s{from,%T,%T}", prefix, mon->u.pid, mon->ref);
  	break;
      case MON_NIF_TARGET: {
          ErtsResource* rsrc = mon->u.resource;
          erts_print(to, to_arg, "%s{from,{%T,%T},%T}", prefix, rsrc->type->module,
                     rsrc->type->name, mon->ref);
  	break;
      }
      }
  }
  			       
  /* Display info about an individual Erlang process */
  void
  print_process_info(fmtfn_t to, void *to_arg, Process *p)
  {
      time_t approx_started;
      int garbing = 0;
      int running = 0;
      struct saved_calls *scb;
      erts_aint32_t state;
  
      /* display the PID */
      erts_print(to, to_arg, "=proc:%T\n", p->common.id);
  
      /* Display the state */
      erts_print(to, to_arg, "State: ");
  
      state = erts_atomic32_read_acqb(&p->state);
      erts_dump_process_state(to, to_arg, state);
      if (state & ERTS_PSFLG_GC) {
          garbing = 1;
          running = 1;
      } else if (state & (ERTS_PSFLG_RUNNING
  			| ERTS_PSFLG_DIRTY_RUNNING))
          running = 1;
  
      /*
       * If the process is registered as a global process, display the
       * registered name
       */
      if (p->common.u.alive.reg)
  	erts_print(to, to_arg, "Name: %T\n", p->common.u.alive.reg->name);
  
      /*
       * Display the initial function name
       */
      erts_print(to, to_arg, "Spawned as: %T:%T/%bpu\n",
  	       p->u.initial.module,
  	       p->u.initial.function,
  	       p->u.initial.arity);
      
      if (p->current != NULL) {
  	if (running) {
  	    erts_print(to, to_arg, "Last scheduled in for: ");
  	} else {
  	    erts_print(to, to_arg, "Current call: ");
  	}
  	erts_print(to, to_arg, "%T:%T/%bpu\n",
  		   p->current->module,
  		   p->current->function,
  		   p->current->arity);
      }
  
      erts_print(to, to_arg, "Spawned by: %T\n", p->parent);
      approx_started = (time_t) p->approx_started;
      erts_print(to, to_arg, "Started: %s", ctime(&approx_started));
      ERTS_MSGQ_MV_INQ2PRIVQ(p);
      erts_print(to, to_arg, "Message queue length: %d\n", p->msg.len);
  
      /* display the message queue only if there is anything in it */
      if (!ERTS_IS_CRASH_DUMPING && p->msg.first != NULL && !garbing) {
  	ErtsMessage* mp;
  	erts_print(to, to_arg, "Message queue: [");
  	for (mp = p->msg.first; mp; mp = mp->next)
  	    erts_print(to, to_arg, mp->next ? "%T," : "%T", ERL_MESSAGE_TERM(mp));
  	erts_print(to, to_arg, "]\n");
      }
  
      {
         int frags = 0;
         ErlHeapFragment *m = p->mbuf;
         while (m != NULL) {
  	   frags++;
  	   m = m->next;
         }
         erts_print(to, to_arg, "Number of heap fragments: %d\n", frags);
      }
      erts_print(to, to_arg, "Heap fragment data: %beu\n", MBUF_SIZE(p));
  
      scb = ERTS_PROC_GET_SAVED_CALLS_BUF(p);
      if (scb) {
         int i, j;
  
         erts_print(to, to_arg, "Last calls:");
         for (i = 0; i < scb->n; i++) {
  	     erts_print(to, to_arg, " ");
  	     j = scb->cur - i - 1;
  	     if (j < 0)
  		j += scb->len;
  	     if (scb->ct[j] == &exp_send)
  		erts_print(to, to_arg, "send");
  	     else if (scb->ct[j] == &exp_receive)
  		erts_print(to, to_arg, "'receive'");
  	     else if (scb->ct[j] == &exp_timeout)
  		   erts_print(to, to_arg, "timeout");
  	     else
  		 erts_print(to, to_arg, "%T:%T/%bpu\n",
  			    scb->ct[j]->info.mfa.module,
  			    scb->ct[j]->info.mfa.function,
  			    scb->ct[j]->info.mfa.arity);
         }
         erts_print(to, to_arg, "\n");
      }
  
      /* display the links only if there are any*/
      if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p)) {
  	PrintMonitorContext context = {1, to, to_arg};
  	erts_print(to, to_arg,"Link list: [");
  	erts_doforall_links(ERTS_P_LINKS(p), &doit_print_link, &context);	
  	erts_doforall_monitors(ERTS_P_MONITORS(p), &doit_print_monitor, &context);
  	erts_print(to, to_arg,"]\n");
      }
  
      if (!ERTS_IS_CRASH_DUMPING) {
  
  	/* and the dictionary */
  	if (p->dictionary != NULL && !garbing) {
  	    erts_print(to, to_arg, "Dictionary: ");
  	    erts_dictionary_dump(to, to_arg, p->dictionary);
  	    erts_print(to, to_arg, "\n");
  	}
      }
      
      /* print the number of reductions etc */
      erts_print(to, to_arg, "Reductions: %beu\n", p->reds);
  
      erts_print(to, to_arg, "Stack+heap: %beu\n", p->heap_sz);
      erts_print(to, to_arg, "OldHeap: %bpu\n",
                 (OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HEAP(p)) );
      erts_print(to, to_arg, "Heap unused: %bpu\n", (p->hend - p->htop));
      erts_print(to, to_arg, "OldHeap unused: %bpu\n",
  	       (OLD_HEAP(p) == NULL) ? 0 : (OLD_HEND(p) - OLD_HTOP(p)) );
      erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0));
  
      if (garbing) {
  	print_garb_info(to, to_arg, p);
      }
      
      if (ERTS_IS_CRASH_DUMPING) {
  	erts_program_counter_info(to, to_arg, p);
      } else {
  	erts_print(to, to_arg, "Stack dump:\n");
  	if (!garbing)
  	    erts_stack_dump(to, to_arg, p);
      }
  
      /* Display all states */
      erts_print(to, to_arg, "Internal State: ");
      erts_dump_extended_process_state(to, to_arg, state);
  }
  
  static void
  print_garb_info(fmtfn_t to, void *to_arg, Process* p)
  {
      /* ERTS_SMP: A scheduler is probably concurrently doing gc... */
      if (!ERTS_IS_CRASH_DUMPING)
        return;
      erts_print(to, to_arg, "New heap start: %bpX\n", p->heap);
      erts_print(to, to_arg, "New heap top: %bpX\n", p->htop);
      erts_print(to, to_arg, "Stack top: %bpX\n", p->stop);
      erts_print(to, to_arg, "Stack end: %bpX\n", p->hend);
      erts_print(to, to_arg, "Old heap start: %bpX\n", OLD_HEAP(p));
      erts_print(to, to_arg, "Old heap top: %bpX\n", OLD_HTOP(p));
      erts_print(to, to_arg, "Old heap end: %bpX\n", OLD_HEND(p));
  }
  
  void
  info(fmtfn_t to, void *to_arg)
  {
      erts_memory(&to, to_arg, NULL, THE_NON_VALUE);
      atom_info(to, to_arg);
      module_info(to, to_arg);
      export_info(to, to_arg);
      register_info(to, to_arg);
      erts_fun_info(to, to_arg);
      erts_node_table_info(to, to_arg);
      erts_dist_table_info(to, to_arg);
      erts_allocated_areas(&to, to_arg, NULL);
      erts_allocator_info(to, to_arg);
  
  }
  
  static int code_size(struct erl_module_instance* modi)
  {
      int size = modi->code_length;
  
      if (modi->code_hdr) {
          ErtsLiteralArea* lit = modi->code_hdr->literal_area;
          if (lit)
              size += (lit->end - lit->start) * sizeof(Eterm);
      }
      return size;
  }
  
  void
  loaded(fmtfn_t to, void *to_arg)
  {
      int i;
      int old = 0;
      int cur = 0;
      BeamCodeHeader* code;
      Module* modp;
      ErtsCodeIndex code_ix;
  
      code_ix = erts_active_code_ix();
      erts_rlock_old_code(code_ix);
  
      /*
       * Calculate and print totals.
       */
      for (i = 0; i < module_code_size(code_ix); i++) {
  	if ((modp = module_code(i, code_ix)) != NULL) {
  	    cur += code_size(&modp->curr);
              old += code_size(&modp->old);
  	}
      }
      erts_print(to, to_arg, "Current code: %d\n", cur);
      erts_print(to, to_arg, "Old code: %d\n", old);
      
      /*
       * Print one line per module.
       */
  
      for (i = 0; i < module_code_size(code_ix); i++) {
  	modp = module_code(i, code_ix);
  	if (!ERTS_IS_CRASH_DUMPING) {
  	    /*
  	     * Interactive dump; keep it brief.
  	     */
  	    if (modp != NULL && ((modp->curr.code_length != 0) ||
                                   (modp->old.code_length != 0))) {
  		erts_print(to, to_arg, "%T %d", make_atom(modp->module),
                             code_size(&modp->curr));
  		if (modp->old.code_length != 0)
  		    erts_print(to, to_arg, " (%d old)", code_size(&modp->old));
  		erts_print(to, to_arg, "\n");
  	    }
  	} else {
  	    /*
  	     * To crash dump; make it parseable.
  	     */
  	    if (modp != NULL && ((modp->curr.code_length != 0) ||
                                   (modp->old.code_length != 0))) {
  		erts_print(to, to_arg, "=mod:");
  		erts_print(to, to_arg, "%T", make_atom(modp->module));
  		erts_print(to, to_arg, "\n");
  		erts_print(to, to_arg, "Current size: %d\n",
  			   code_size(&modp->curr));
  		code = modp->curr.code_hdr;
  		if (code != NULL && code->attr_ptr) {
  		    erts_print(to, to_arg, "Current attributes: ");
  		    dump_attributes(to, to_arg, code->attr_ptr,
  				    code->attr_size);
  		}
  		if (code != NULL && code->compile_ptr) {
  		    erts_print(to, to_arg, "Current compilation info: ");
  		    dump_attributes(to, to_arg, code->compile_ptr,
  				    code->compile_size);
  		}
  
  		if (modp->old.code_length != 0) {
  		    erts_print(to, to_arg, "Old size: %d\n", code_size(&modp->old));
  		    code = modp->old.code_hdr;
  		    if (code->attr_ptr) {
  			erts_print(to, to_arg, "Old attributes: ");
  			dump_attributes(to, to_arg, code->attr_ptr,
  					code->attr_size);
  		    }
  		    if (code->compile_ptr) {
  			erts_print(to, to_arg, "Old compilation info: ");
  			dump_attributes(to, to_arg, code->compile_ptr,
  					code->compile_size);
  		    }
  		}
  	    }
  	}
      }
      erts_runlock_old_code(code_ix);
  }
  
  
  static void
  dump_attributes(fmtfn_t to, void *to_arg, byte* ptr, int size)
  {
      while (size-- > 0) {
  	erts_print(to, to_arg, "%02X", *ptr++);
      }
      erts_print(to, to_arg, "\n");
  }
  
  
  void
  do_break(void)
  {
      int i;
  #ifdef __WIN32__
      char *mode; /* enough for storing "window" */
  
      /* check if we're in console mode and, if so,
         halt immediately if break is called */
      mode = erts_read_env("ERL_CONSOLE_MODE");
      if (mode && strcmp(mode, "window") != 0)
  	erts_exit(0, "");
      erts_free_read_env(mode);
  #endif /* __WIN32__ */
  
      ASSERT(erts_thr_progress_is_blocking());
  
      erts_printf("\n"
  		"BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded\n"
  		"       (v)ersion (k)ill (D)b-tables (d)istribution\n");
  
      while (1) {
  	if ((i = sys_get_key(0)) <= 0)
  	    erts_exit(0, "");
  	switch (i) {
  	case 'q':
  	case 'a': 
  	case '*': /* 
  		   * The asterisk is an read error on windows, 
  		   * where sys_get_key isn't that great in console mode.
  		   * The usual reason for a read error is Ctrl-C. Treat this as
  		   * 'a' to avoid infinite loop.
  		   */
  	    erts_exit(0, "");
  	case 'A':		/* Halt generating crash dump */
  	    erts_exit(ERTS_ERROR_EXIT, "Crash dump requested by user");
  	case 'c':
  	    return;
  	case 'p':
  	    process_info(ERTS_PRINT_STDOUT, NULL);
  	    return;
  	case 'm':
  	    return;
  	case 'o':
  	    port_info(ERTS_PRINT_STDOUT, NULL);
  	    return;
  	case 'i':
  	    info(ERTS_PRINT_STDOUT, NULL);
  	    return;
  	case 'l':
  	    loaded(ERTS_PRINT_STDOUT, NULL);
  	    return;
  	case 'v':
  	    erts_printf("Erlang (%s) emulator version "
  		       ERLANG_VERSION "\n",
  		       EMULATOR);
  #if ERTS_SAVED_COMPILE_TIME
  	    erts_printf("Compiled on " ERLANG_COMPILE_DATE "\n");
  #endif
  	    return;
  	case 'd':
  	    distribution_info(ERTS_PRINT_STDOUT, NULL);
  	    return;
  	case 'D':
  	    db_info(ERTS_PRINT_STDOUT, NULL, 1);
  	    return; 
  	case 'k':
  	    process_killer();
  	    return;
  #ifdef OPPROF
  	case 'X':
  	    dump_frequencies();
  	    return;
  	case 'x':
  	    {
  		int i;
  		for (i = 0; i <= HIGHEST_OP; i++) {
  		    if (opc[i].name != NULL) {
  			erts_printf("%-16s %8d\n", opc[i].name, opc[i].count);
  		    }
  		}
  	    }
  	    return;
  	case 'z':
  	    {
  		int i;
  		for (i = 0; i <= HIGHEST_OP; i++)
  		    opc[i].count = 0;
  	    }
  	    return;
  #endif
  #ifdef DEBUG
  	case 't':
  	    /* erts_p_slpq(); */
  	    return;
  	case 'b':
  	    bin_check();
  	    return;
  	case 'C':
  	    abort();
  #endif
  	case '\n':
  	    continue;
  	default:
  	    erts_printf("Eh?\n\n");
  	}
      }
  
  }
  
  
  #ifdef OPPROF
  static void
  dump_frequencies(void)
  {
      int i;
      FILE* fp;
      time_t now;
      static char name[] = "op_freq.dump";
  
      fp = fopen(name, "w");
      if (fp == NULL) {
  	fprintf(stderr, "Failed to open %s for writing\n", name);
  	return;
      }
  
      time(&now);
      fprintf(fp, "# Generated %s\n", ctime(&now));
  
      for (i = 0; i <= HIGHEST_OP; i++) {
  	if (opc[i].name != NULL) {
  	    fprintf(fp, "%s %d\n", opc[i].name, opc[i].count);
  	}
      }
      fclose(fp);
      erts_printf("Frequencies dumped to %s\n", name);
  }
  #endif
  
  
  #ifdef DEBUG
  
  static void 
  bin_check(void)
  {
      Process  *rp;
      struct erl_off_heap_header* hdr;
      int i, printed = 0, max = erts_ptab_max(&erts_proc);
  
      for (i=0; i < max; i++) {
  	rp = erts_pix2proc(i);
  	if (!rp)
  	    continue;
  	for (hdr = rp->off_heap.first; hdr; hdr = hdr->next) {
  	    if (hdr->thing_word == HEADER_PROC_BIN) {
  		ProcBin *bp = (ProcBin*) hdr;
  		if (!printed) {
  		    erts_printf("Process %T holding binary data \n", rp->common.id);
  		    printed = 1;
  		}
  		erts_printf("%p orig_size: %bpd, norefs = %bpd\n",
  			    bp->val, 
  			    bp->val->orig_size, 
  			    erts_refc_read(&bp->val->intern.refc, 1));
  	    }
  	}
  	if (printed) {
  	    erts_printf("--------------------------------------\n");
  	    printed = 0;
  	}
      }
      /* db_bin_check() has to be rewritten for the AVL trees... */
      /*db_bin_check();*/ 
  }
  
  #endif
  
  static Sint64 crash_dump_limit = ERTS_SINT64_MAX;
  static Sint64 crash_dump_written = 0;
  
  static int crash_dump_limited_writer(void* vfdp, char* buf, size_t len)
  {
      const char stop_msg[] = "\n=abort:CRASH DUMP SIZE LIMIT REACHED\n";
  
      crash_dump_written += len;
      if (crash_dump_written <= crash_dump_limit) {
          return erts_write_fd(vfdp, buf, len);
      }
  
      len -= (crash_dump_written - crash_dump_limit);
      erts_write_fd(vfdp, buf, len);
      erts_write_fd(vfdp, (char*)stop_msg, sizeof(stop_msg)-1);
  
      /* We assume that crash dump was called from erts_exit_vv() */
      erts_exit_epilogue();
  }
  
  /* XXX THIS SHOULD BE IN SYSTEM !!!! */
  void
  erl_crash_dump_v(char *file, int line, char* fmt, va_list args)
  {
      ErtsThrPrgrData tpd_buf; /* in case we aren't a managed thread... */
      int fd;
      size_t envsz;
      time_t now;
      char env[21]; /* enough to hold any 64-bit integer */
      size_t dumpnamebufsize = MAXPATHLEN;
      char dumpnamebuf[MAXPATHLEN];
      char* dumpname;
      int secs;
      int env_erl_crash_dump_seconds_set = 1;
      int i;
      fmtfn_t to = &erts_write_fd;
      void*   to_arg;
  
      if (ERTS_SOMEONE_IS_CRASH_DUMPING)
  	return;
  
      /* Order all managed threads to block, this has to be done
         first to guarantee that this is the only thread to generate
         crash dump. */
      erts_thr_progress_fatal_error_block(&tpd_buf);
  
  #ifdef ERTS_SYS_SUSPEND_SIGNAL
      /*
       * We suspend all scheduler threads so that we can dump some
       * data about the currently running processes and scheduler data.
       * We have to be very very careful when doing this as the schedulers
       * could be anywhere.
       */
      sys_init_suspend_handler();
  
      for (i = 0; i < erts_no_schedulers; i++) {
          erts_tid_t tid = ERTS_SCHEDULER_IX(i)->tid;
          if (!erts_equal_tids(tid,erts_thr_self()))
              sys_thr_suspend(tid);
      }
  
  #endif
  
      /* Allow us to pass certain places without locking... */
      erts_atomic32_set_mb(&erts_writing_erl_crash_dump, 1);
      erts_tsd_set(erts_is_crash_dumping_key, (void *) 1);
  
  
      envsz = sizeof(env);
      /* ERL_CRASH_DUMP_SECONDS not set
       * if we have a heart port, break immediately
       * otherwise dump crash indefinitely (until crash is complete)
       * same as ERL_CRASH_DUMP_SECONDS = 0
       * - do not write dump
       * - do not set an alarm
       * - break immediately
       *
       * ERL_CRASH_DUMP_SECONDS = 0
       * - do not write dump
       * - do not set an alarm
       * - break immediately
       *
       * ERL_CRASH_DUMP_SECONDS < 0
       * - do not set alarm
       * - write dump until done
       *
       * ERL_CRASH_DUMP_SECONDS = S (and S positive)
       * - Don't dump file forever
       * - set alarm (set in sys)
       * - write dump until alarm or file is written completely
       */
  	
      if (erts_sys_getenv__("ERL_CRASH_DUMP_SECONDS", env, &envsz) != 0) {
  	env_erl_crash_dump_seconds_set = 0;
  	secs = -1;
      } else {
  	env_erl_crash_dump_seconds_set = 1;
  	secs = atoi(env);
      }
  
      if (secs == 0) {
  	return;
      }
  
      /* erts_sys_prepare_crash_dump returns 1 if heart port is found, otherwise 0
       * If we don't find heart (0) and we don't have ERL_CRASH_DUMP_SECONDS set
       * we should continue writing a dump
       *
       * beware: secs -1 means no alarm
       */
  
      if (erts_sys_prepare_crash_dump(secs) && !env_erl_crash_dump_seconds_set ) {
  	return;
      }
  
      crash_dump_limit = ERTS_SINT64_MAX;
      envsz = sizeof(env);
      if (erts_sys_getenv__("ERL_CRASH_DUMP_BYTES", env, &envsz) == 0) {
          Sint64 limit;
          char* endptr;
          errno = 0;
          limit = ErtsStrToSint64(env, &endptr, 10);
          if (errno == 0 && limit >= 0 && endptr != env && *endptr == 0) {
              if (limit == 0)
                  return;
              crash_dump_limit = limit;
              to = &crash_dump_limited_writer;
          }
      }
  
      if (erts_sys_getenv__("ERL_CRASH_DUMP",&dumpnamebuf[0],&dumpnamebufsize) != 0)
  	dumpname = "erl_crash.dump";
      else
  	dumpname = &dumpnamebuf[0];
      
      erts_fprintf(stderr,"\nCrash dump is being written to: %s...", dumpname);
  
      fd = open(dumpname,O_WRONLY | O_CREAT | O_TRUNC,0640);
      if (fd < 0)
  	return; /* Can't create the crash dump, skip it */
      to_arg = (void*)&fd;
      time(&now);
      erts_cbprintf(to, to_arg, "=erl_crash_dump:0.3\n%s", ctime(&now));
  
      if (file != NULL)
         erts_cbprintf(to, to_arg, "The error occurred in file %s, line %d\n", file, line);
  
      if (fmt != NULL && *fmt != '\0') {
  	erts_cbprintf(to, to_arg, "Slogan: ");
  	erts_vcbprintf(to, to_arg, fmt, args);
      }
      erts_cbprintf(to, to_arg, "System version: ");
      erts_print_system_version(to, to_arg, NULL);
  #if ERTS_SAVED_COMPILE_TIME
      erts_cbprintf(to, to_arg, "%s\n", "Compiled: " ERLANG_COMPILE_DATE);
  #endif
  
      erts_cbprintf(to, to_arg, "Taints: ");
      erts_print_nif_taints(to, to_arg);
      erts_cbprintf(to, to_arg, "Atoms: %d\n", atom_table_size());
  
      /* We want to note which thread it was that called erts_exit */
      if (erts_get_scheduler_data()) {
          erts_cbprintf(to, to_arg, "Calling Thread: scheduler:%d\n",
                        erts_get_scheduler_data()->no);
      } else {
          if (!erts_thr_getname(erts_thr_self(), dumpnamebuf, MAXPATHLEN))
              erts_cbprintf(to, to_arg, "Calling Thread: %s\n", dumpnamebuf);
          else
              erts_cbprintf(to, to_arg, "Calling Thread: %p\n", erts_thr_self());
      }
  
  #if defined(ERTS_HAVE_TRY_CATCH)
  
      /*
       * erts_print_scheduler_info is not guaranteed to be safe to call
       * here for all schedulers as we may have suspended a scheduler
       * in the middle of updating the STACK_TOP and STACK_START
       * variables and thus when scanning the stack we could get
       * segmentation faults. We protect against this very unlikely
       * scenario by using the ERTS_SYS_TRY_CATCH.
       */
      for (i = 0; i < erts_no_schedulers; i++) {
          ERTS_SYS_TRY_CATCH(
              erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i)),
              erts_cbprintf(to, to_arg, "** crashed **\n"));
      }
  #endif
  
  
  #ifdef ERTS_SYS_SUSPEND_SIGNAL
  
      /* We resume all schedulers so that we are in a known safe state
         when we write the rest of the crash dump */
      for (i = 0; i < erts_no_schedulers; i++) {
          erts_tid_t tid = ERTS_SCHEDULER_IX(i)->tid;
          if (!erts_equal_tids(tid,erts_thr_self()))
  	    sys_thr_resume(tid);
      }
  #endif
  
      /*
       * Wait for all managed threads to block. If all threads haven't blocked
       * after a minute, we go anyway and hope for the best...
       *
       * We do not release system again. We expect an exit() or abort() after
       * dump has been written.
       */
      erts_thr_progress_fatal_error_wait(60000);
      /* Either worked or not... */
  
  #ifndef ERTS_HAVE_TRY_CATCH
      /* This is safe to call here, as all schedulers are blocked */
      for (i = 0; i < erts_no_schedulers; i++) {
          erts_print_scheduler_info(to, to_arg, ERTS_SCHEDULER_IX(i));
      }
  #endif
      
      info(to, to_arg); /* General system info */
      if (erts_ptab_initialized(&erts_proc))
  	process_info(to, to_arg); /* Info about each process and port */
      db_info(to, to_arg, 0);
      erts_print_bif_timer_info(to, to_arg);
      distribution_info(to, to_arg);
      erts_cbprintf(to, to_arg, "=loaded_modules\n");
      loaded(to, to_arg);
      erts_dump_fun_entries(to, to_arg);
      erts_deep_process_dump(to, to_arg);
      erts_cbprintf(to, to_arg, "=atoms\n");
      dump_atoms(to, to_arg);
  
      /* Keep the instrumentation data at the end of the dump */
      if (erts_instr_memory_map || erts_instr_stat) {
  	erts_cbprintf(to, to_arg, "=instr_data\n");
  
  	if (erts_instr_stat) {
  	    erts_cbprintf(to, to_arg, "=memory_status\n");
  	    erts_instr_dump_stat_to(to, to_arg, 0);
  	}
  	if (erts_instr_memory_map) {
  	    erts_cbprintf(to, to_arg, "=memory_map\n");
  	    erts_instr_dump_memory_map_to(to, to_arg);
  	}
      }
  
      erts_cbprintf(to, to_arg, "=end\n");
      close(fd);
      erts_fprintf(stderr,"done\n");
  }
  
  