home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #26 / NN_1992_26.iso / spool / gnu / gcc / help / 2487 < prev    next >
Encoding:
Text File  |  1992-11-09  |  17.2 KB  |  637 lines

  1. Path: sparky!uunet!stanford.edu!agate!spool.mu.edu!nigel.msen.com!emory!swrinde!cs.utexas.edu!sun-barr!news2me.EBay.Sun.COM!cronkite.Central.Sun.COM!texsun!exucom.exu.ericsson.se!news
  2. From: ebubra@ebu.ericsson.se (Bertil Askelid)
  3. Newsgroups: gnu.gcc.help
  4. Subject: Re: Memory leak detector with gdb?
  5. Message-ID: <1992Nov9.193300.25504@exu.ericsson.se>
  6. Date: 9 Nov 92 19:33:00 GMT
  7. Sender: news@exu.ericsson.se
  8. Reply-To: ebubra@ebu.ericsson.se (Bertil Askelid)
  9. Followup-To: gnu.gdb.bug
  10. Organization: Ericsson Business Communications, Anaheim, California
  11. Lines: 620
  12. Nntp-Posting-Host: proton.ebu.ericsson.se
  13. X-Disclaimer: This article was posted by a user at Ericsson.
  14.               Any opinions expressed are strictly those of the
  15.               user and not necessarily those of Ericsson.
  16.  
  17. <me>  A while ago an article was posted about a memory leak detector
  18. <me>  running with gdb 4.*. I missed it. Is there someone who happens to
  19. <me>  have a copy of it and would like to email it to me?
  20. <me>  
  21. <me>  Thanks!
  22.  
  23.    Thanks to Jan-Erik.Stromquist@uppsala.telesoft.se and
  24.    Patrik.Carlsson@eua.ericsson.se I got hold of the article I asked
  25.    for. As I have received a couple of requests from other persons also
  26.    looking for this gdb memory leak detector patch, I repost it:
  27.  
  28. ================================================================================
  29. Newsgroups: gnu.gdb.bug
  30. From: L.Dachary@cs.ucl.ac.uk
  31. Subject: patch for gdb-4.6 to trace memory leaks
  32. Organization: GNUs Not Usenet
  33. Distribution: gnu
  34. Date: Thu, 15 Oct 1992 17:38:42 GMT
  35.  
  36.    This is a patch for gdb-4.6. It introduces two commands to
  37. trace memory leaks. 
  38.    The leaks_trace command sets a breakpoint in the malloc
  39. function and one in the free function. There is one action associated
  40. with each breakpoint. The action associated with the malloc function
  41. stores the backtrace of function calls using the address of the newly
  42. created object as an index. The action associated with the free
  43. function forgets the backtrace associated with the address of the
  44. object being deleted.
  45.    The leaks_print command prints the table containing backtraces
  46. associated with object addresses.
  47.    If a program has no memory leaks the table should be empty
  48. when the program terminates. If it has memory leaks the backtrace
  49. allows the user to find the place where the non-deleted object has
  50. been created.
  51.  
  52.    In order to use this facility the executable file must contain
  53. a version of malloc compiled with debugging information. Any malloc
  54. package may be used.
  55.  
  56.    Here is a session record that illustrate how this patch can be
  57. used.
  58.  
  59. ---- source b.c -----------
  60. 1 main()
  61. 2 {
  62. 3   char* a = (char*)malloc(1);
  63. 4   free(a);
  64. 5   a = (char*)malloc(1);
  65. 6 }
  66. ---------------------------
  67. $ gcc -g -o b b.c malloc.o
  68. $ gdb b
  69. GDB is free software and you are welcome to distribute copies of it
  70.  under certain conditions; type "show copying" to see the conditions.
  71. There is absolutely no warranty for GDB; type "show warranty" for details.
  72. GDB 4.6, Copyright 1992 Free Software Foundation, Inc...
  73. (gdb) quit
  74. $ gcc -g -o b b.c malloc.o
  75. $ gdb b
  76. GDB is free software and you are welcome to distribute copies of it
  77.  under certain conditions; type "show copying" to see the conditions.
  78. There is absolutely no warranty for GDB; type "show warranty" for details.
  79. GDB 4.6, Copyright 1992 Free Software Foundation, Inc...
  80. (gdb) leaks_trace
  81. Breakpoint for malloc
  82. Breakpoint 1 at 0x298c: file malloc.c, line 481.
  83. Breakpoint for free
  84. Breakpoint 2 at 0x2998: file malloc.c, line 488.
  85. (gdb) run
  86. Starting program: /tmp_mnt/cs/research/coside/fsfb/croissant/coside/gdb-4.6/gdb/b 
  87.  
  88. Program exited with code 01.
  89. (gdb) leaks_print
  90. Backtrace for 0x4c18
  91. #0  0x22c4 in main () at b.c:5
  92. (gdb) quit
  93.  
  94.    I will not maintain this. Please do not send mail concerning
  95. problems you may have with this patch.
  96.  
  97.    Have fun,
  98.  
  99.          Loic
  100.  
  101. BUGS: Only 20 function calls per object.
  102.       Tested only on a sparc.
  103.       Slows down the debugged program dramaticaly.
  104.       Eats a lot of memory.
  105. ---------------------------------------------------------------------------------
  106. *** ../../gdb-4.6-orig/gdb/breakpoint.c   Mon Jul  6 18:10:37 1992
  107. --- breakpoint.c  Tue Oct 13 13:55:49 1992
  108. ***************
  109. *** 157,163 ****
  110.   
  111.   /* Number of last breakpoint made.  */
  112.   
  113. ! static int breakpoint_count;
  114.   
  115.   /* Set breakpoint count to NUM.  */
  116.   static void
  117. --- 157,163 ----
  118.   
  119.   /* Number of last breakpoint made.  */
  120.   
  121. ! int breakpoint_count;
  122.   
  123.   /* Set breakpoint count to NUM.  */
  124.   static void
  125. *** ../../gdb-4.6-orig/gdb/infcmd.c Mon Jul  6 18:10:52 1992
  126. --- infcmd.c   Thu Oct 15 15:51:23 1992
  127. ***************
  128. *** 31,37 ****
  129.   #include "gdbcore.h"
  130.   #include "target.h"
  131.   
  132. ! static void
  133.   continue_command PARAMS ((char *, int));
  134.   
  135.   static void
  136. --- 31,37 ----
  137.   #include "gdbcore.h"
  138.   #include "target.h"
  139.   
  140. ! void
  141.   continue_command PARAMS ((char *, int));
  142.   
  143.   static void
  144. ***************
  145. *** 94,100 ****
  146.   static void
  147.   nexti_command PARAMS ((char *, int));
  148.   
  149. ! static void
  150.   stepi_command PARAMS ((char *, int));
  151.   
  152.   static void
  153. --- 94,100 ----
  154.   static void
  155.   nexti_command PARAMS ((char *, int));
  156.   
  157. ! void
  158.   stepi_command PARAMS ((char *, int));
  159.   
  160.   static void
  161. ***************
  162. *** 244,250 ****
  163.            environ_vector (inferior_environ));
  164.   }
  165.   
  166. ! static void
  167.   continue_command (proc_count_exp, from_tty)
  168.        char *proc_count_exp;
  169.        int from_tty;
  170. --- 244,250 ----
  171.            environ_vector (inferior_environ));
  172.   }
  173.   
  174. ! void
  175.   continue_command (proc_count_exp, from_tty)
  176.        char *proc_count_exp;
  177.        int from_tty;
  178. ***************
  179. *** 308,314 ****
  180.   /* Likewise, but step only one instruction.  */
  181.   
  182.   /* ARGSUSED */
  183. ! static void
  184.   stepi_command (count_string, from_tty)
  185.        char *count_string;
  186.        int from_tty;
  187. --- 308,314 ----
  188.   /* Likewise, but step only one instruction.  */
  189.   
  190.   /* ARGSUSED */
  191. ! void
  192.   stepi_command (count_string, from_tty)
  193.        char *count_string;
  194.        int from_tty;
  195. *** ../../gdb-4.6-orig/gdb/stack.c  Mon Jul  6 18:28:50 1992
  196. --- stack.c Thu Oct 15 15:22:31 1992
  197. ***************
  198. *** 129,134 ****
  199. --- 129,136 ----
  200.     print_frame_info (fi, level, source, 1);
  201.   }
  202.   
  203. + static int malloc_silent = 0;
  204.   void
  205.   print_frame_info (fi, level, source, args)
  206.        struct frame_info *fi;
  207. ***************
  208. *** 211,217 ****
  209.    funname = msymbol -> name;
  210.       }
  211.   
  212. !   if (source >= 0 || !sal.symtab)
  213.       {
  214.         if (level >= 0)
  215.    printf_filtered ("#%-2d ", level);
  216. --- 213,219 ----
  217.    funname = msymbol -> name;
  218.       }
  219.   
  220. !   if (malloc_silent == 0 && (source >= 0 || !sal.symtab))
  221.       {
  222.         if (level >= 0)
  223.    printf_filtered ("#%-2d ", level);
  224. ***************
  225. *** 245,251 ****
  226.         printf_filtered ("\n");
  227.       }
  228.   
  229. !   if ((source != 0) && sal.symtab)
  230.       {
  231.         int done = 0;
  232.         int mid_statement = source < 0 && fi->pc != sal.pc;
  233. --- 247,253 ----
  234.         printf_filtered ("\n");
  235.       }
  236.   
  237. !   if (malloc_silent == 0 && (source != 0) && sal.symtab)
  238.       {
  239.         int done = 0;
  240.         int mid_statement = source < 0 && fi->pc != sal.pc;
  241. ***************
  242. *** 551,556 ****
  243. --- 553,925 ----
  244.   }
  245.   #endif
  246.   
  247. + typedef char bool;
  248. + typedef int leak_pointer;
  249. + #define TRUE ((bool)1)
  250. + #define FALSE ((bool)0)
  251. + #define LEAKS_PC_MAX 20
  252. + typedef struct leak_trace {
  253. +   CORE_ADDR pcs[LEAKS_PC_MAX];
  254. +   bool valid;
  255. +   char index;
  256. +   long address;
  257. +   leak_pointer next;
  258. + } leak_trace;
  259. + #define LEAKS_TABLE_SIZE 1031
  260. + leak_pointer leaks_table[LEAKS_TABLE_SIZE];
  261. + #define LEAKS_POOL_INITIAL 1000
  262. + #define LEAKS_POOL_INCR 5000
  263. + leak_trace* leaks_pool = 0;
  264. + int leaks_pool_first = 0;
  265. + int leaks_pool_max = 0;
  266. + #define LEAKS_FREE_MAX 500
  267. + leak_pointer leaks_free[LEAKS_FREE_MAX];
  268. + int leaks_free_top = 0;
  269. + #define leaks_hash(addr) ((addr >> 1) % LEAKS_TABLE_SIZE)
  270. + leak_pointer leak_alloc()
  271. + {
  272. +   leak_pointer leak;
  273. +   if(leaks_free_top > 0)
  274. +     leak = leaks_free[--leaks_free_top];
  275. +   else {
  276. +     while(leaks_pool_first < leaks_pool_max && leaks_pool[leaks_pool_first].valid == TRUE)
  277. +       leaks_pool_first++;
  278. +     if(leaks_pool_first >= leaks_pool_max) {
  279. +       int i;
  280. +       if(leaks_pool == 0) {
  281. +  leaks_pool = (leak_trace*)malloc(sizeof(leak_trace)*LEAKS_POOL_INITIAL);
  282. +  leaks_pool_max = LEAKS_POOL_INITIAL;
  283. +  for(i = 0; i < leaks_pool_max; i++)
  284. +    leaks_pool[i].valid = FALSE;
  285. +       } else {
  286. +  leaks_pool = (leak_trace*)realloc(leaks_pool, sizeof(leak_trace)*(leaks_pool_max+LEAKS_POOL_INCR));
  287. +  for(i = leaks_pool_max; i < leaks_pool_max+LEAKS_POOL_INCR; i++)
  288. +    leaks_pool[i].valid = FALSE;
  289. +  leaks_pool_max += LEAKS_POOL_INCR;
  290. +       }
  291. +     }
  292. +     leak = leaks_pool_first++;
  293. +   }
  294. +   leaks_pool[leak].valid = TRUE;
  295. +   return leak;
  296. + }
  297. + void leak_free(leak)
  298. +      leak_pointer leak;
  299. + {
  300. +   leaks_pool[leak].valid = FALSE;
  301. +   if(leaks_free_top < LEAKS_FREE_MAX)
  302. +     leaks_free[leaks_free_top++] = leak;
  303. +   else if(leaks_pool_first > leak)
  304. +     leaks_pool_first = leak;
  305. + }
  306. + void leaks_init()
  307. + {
  308. +   int i;
  309. +   for(i = 0; i < LEAKS_TABLE_SIZE; i++)
  310. +     leaks_table[i] = -1;
  311. +   for(i = 0; i < leaks_pool_max; i++)
  312. +     leaks_pool[i].valid = FALSE;
  313. +   leaks_pool_first = 0;
  314. +   leaks_free_top = 0;
  315. + }
  316. + void leaks_hash_store(leak)
  317. +      leak_pointer leak;
  318. + {
  319. +   long key = leaks_hash(leaks_pool[leak].address);
  320. +   leak_pointer chain = leaks_table[key];
  321. +   if(chain < 0) {
  322. +     leaks_table[key] = leak;
  323. +   } else {
  324. +     leak_pointer previous;
  325. +     while(chain >= 0) {
  326. +       previous = chain;
  327. +       chain = leaks_pool[chain].next;
  328. +     }
  329. +     leaks_pool[previous].next = leak;
  330. +   }
  331. +   leaks_pool[leak].next = -1;
  332. + }
  333. + #if 0
  334. + leak_pointer leaks_hash_restore(address)
  335. +      long address;
  336. + {
  337. +   long key = leaks_hash(address);
  338. +   leak_pointer chain = leaks_table[key];
  339. +   while(chaine >= 0)
  340. +     chaine = leaks_pool[chain].next;
  341. +   return chain;
  342. + }
  343. + #endif
  344. + leak_pointer leaks_hash_remove(address)
  345. +      long address;
  346. + {
  347. +   long key = leaks_hash(address);
  348. +   leak_pointer chain = leaks_table[key];
  349. +   if(chain >= 0) {
  350. +     if(leaks_pool[chain].address == address) {
  351. +       leaks_table[key] = -1;
  352. +     } else {
  353. +       leak_pointer previous;
  354. +       while(chain >= 0 && leaks_pool[chain].address != address) {
  355. +  previous = chain;
  356. +  chain = leaks_pool[chain].next;
  357. +       }
  358. +       if(chain >= 0)
  359. +  leaks_pool[previous].next = leaks_pool[chain].next;
  360. +     }
  361. +   }
  362. +   return chain;
  363. + }
  364. + static void
  365. + leaks_trace_command (dumb, from_tty)
  366. +      char *dumb;
  367. +      int from_tty;
  368. + {
  369. +   struct expression* expr;
  370. +   value val;
  371. +   long address;
  372. +   struct block* b;
  373. +   static int bp_set = 0;
  374. +   
  375. +   if(bp_set == 0) {
  376. +     if(expr = parse_expression ("&malloc")) {
  377. +       
  378. +       val = evaluate_expression (expr);
  379. +       address = *(long*)VALUE_CONTENTS_RAW(val);
  380. +       
  381. +       if(b = block_for_pc(address)) {
  382. +  extern struct breakpoint *breakpoint_chain;
  383. +  extern int breakpoint_count;
  384. +  long end = b->endaddr;
  385. +  long lasti = address;
  386. +  FILE* f = fopen("/dev/null", "w");
  387. +  if(f) {
  388. +    long curi = address;
  389. +    char buf[512];
  390. +    while((curi += print_insn(curi, f)) < end)
  391. +      lasti = curi;
  392. +    fclose(f);
  393. +    
  394. +    sprintf(buf, "*%d", lasti);
  395. +    printf_filtered("Breakpoint for malloc\n");
  396. +    break_command(buf, 0);
  397. +    {
  398. +      struct breakpoint* bp;
  399. +      for (bp = breakpoint_chain; bp; bp = bp->next)
  400. +        if(bp->number == breakpoint_count) {
  401. +     struct command_line *first;
  402. +     struct command_line *second;
  403. +     first = (struct command_line*)malloc(sizeof(struct command_line));
  404. +     second = (struct command_line*)malloc(sizeof(struct command_line));
  405. +     first->line = savestring("silent", strlen("silent") + 1);
  406. +     first->next = second;
  407. +     second->line = savestring("leaks_malloc", strlen("leaks_malloc") + 1);
  408. +     second->next = (struct command_line*)0;
  409. +     bp->commands = first;
  410. +        }
  411. +    }
  412. +    printf_filtered("Breakpoint for free\n");
  413. +    break_command("free", 0);
  414. +    {
  415. +      struct breakpoint* bp;
  416. +      for (bp = breakpoint_chain; bp; bp = bp->next)
  417. +        if(bp->number == breakpoint_count) {
  418. +     struct command_line *first;
  419. +     struct command_line *second;
  420. +     first = (struct command_line*)malloc(sizeof(struct command_line));
  421. +     second = (struct command_line*)malloc(sizeof(struct command_line));
  422. +     first->line = savestring("silent", strlen("silent") + 1);
  423. +     first->next = second;
  424. +     second->line = savestring("leaks_free", strlen("leaks_free") + 1);
  425. +     second->next = (struct command_line*)0;
  426. +     bp->commands = first;
  427. +        }
  428. +    }
  429. +  }
  430. +       }
  431. +       bp_set = 1;
  432. +     }
  433. +   }
  434. +   leaks_init();
  435. + }
  436. + static void
  437. +   leaks_malloc_command (allocated, from_tty)
  438. + char *allocated;
  439. + int from_tty;
  440. + {
  441. +   struct frame_info *fi;
  442. +   register int count = LEAKS_PC_MAX;
  443. +   register FRAME frame;
  444. +   register int i;
  445. +   register FRAME trailing;
  446. +   register int trailing_level;
  447. +   value val;
  448. +   long address;
  449. +   char stop_registers[REGISTER_BYTES];
  450. +   struct type value_type;
  451. +   leak_pointer leak;
  452. +   leak_trace* leak_tracep;
  453. +   malloc_silent = 1;
  454. +   stepi_command (0, 0); /* Effectively return from the malloc function */
  455. +   malloc_silent = 0;
  456. +   memset(&value_type, '\0', sizeof(struct type));
  457. +   read_register_bytes (0, stop_registers, REGISTER_BYTES);
  458. +   value_type.code = TYPE_CODE_INT;
  459. +   value_type.length = 4;
  460. +   val = value_being_returned (&value_type, stop_registers, 0);
  461. +   address = *(long*)VALUE_CONTENTS_RAW(val);
  462. + #if 0
  463. +   printf_filtered("malloc 0x%x\n", address);
  464. +   val = access_value_history (0);
  465. +   v = *(long*)VALUE_CONTENTS_RAW(val);
  466. + #endif
  467. +   leak = leak_alloc();
  468. +   leak_tracep = &leaks_pool[leak];
  469. +   leak_tracep->address = address;
  470. +   trailing = get_current_frame ();
  471. +   for (i = 0, frame = trailing;
  472. +        frame && count--;
  473. +        i++, frame = get_prev_frame (frame))
  474. +     {
  475. +       QUIT;
  476. +       leak_tracep->pcs[i] = get_frame_info (frame)->pc;
  477. +     }
  478. +   leak_tracep->index = i;
  479. +   leaks_hash_store(leak);
  480. +   continue_command(0, 0);
  481. + }
  482. + static void
  483. +   leaks_free_command (dumb, from_tty)
  484. + char *dumb;
  485. + int from_tty;
  486. + {
  487. +   long address;
  488. +   value val;
  489. +   int i;
  490. +   struct symbol* func;
  491. +   struct symbol* sym;
  492. +   struct block* b;
  493. +   int nsyms;
  494. +   FRAME fi;
  495. +   
  496. +   fi = get_current_frame ();
  497. +   if(func = find_pc_function (fi->pc)) {
  498. +     int found = 0;
  499. +     b = SYMBOL_BLOCK_VALUE(func);
  500. +     nsyms = BLOCK_NSYMS(b);
  501. +     for (i = 0; !found && i < nsyms; i++)
  502. +       {
  503. +  QUIT;
  504. +  sym = BLOCK_SYM (b, i);
  505. +  switch(SYMBOL_CLASS(sym)) {
  506. +  case LOC_ARG:
  507. +  case LOC_REF_ARG:
  508. +  case LOC_REGPARM:
  509. +  case LOC_LOCAL_ARG:
  510. +    found = 1;
  511. +    break;
  512. +  default:
  513. +    break;
  514. +  }
  515. +       }
  516. +     if(found) {
  517. +       sym = lookup_symbol (SYMBOL_NAME (sym),
  518. +           b, VAR_NAMESPACE, (int *)NULL, (struct symtab **)NULL);
  519. +       val = read_var_value (sym, FRAME_INFO_ID (fi));
  520. +       address = *(long*)VALUE_CONTENTS(val);
  521. +     }
  522. +   }
  523. + #if 0
  524. +   printf_filtered("free 0x%x", address);
  525. + #endif
  526. +   {
  527. +     leak_pointer leak = leaks_hash_remove(address);
  528. +     if(leak >= 0) {
  529. + #if 0
  530. +       printf_filtered(".\n");
  531. + #endif
  532. +       leak_free(leak);
  533. +     } else {
  534. + #if 0
  535. +       printf_filtered("\n");
  536. + #endif
  537. +     }
  538. +   }
  539. +   continue_command(0, 0);
  540. + }
  541. + static void
  542. + leaks_print_command (dumb, from_tty)
  543. +      char *dumb;
  544. +      int from_tty;
  545. + {
  546. +   int i, j;
  547. +   static long global_ctor_lo = 0;
  548. +   static long global_ctor_hi = 0;
  549. +   if(global_ctor_lo == 0) {
  550. +     long global_ctor_address;
  551. +     struct minimal_symbol* msymbol;
  552. +     msymbol = lookup_minimal_symbol("__do_global_ctors", 0);
  553. +     if(msymbol) {
  554. +       struct expression* expr = parse_expression ("&__do_global_ctors");
  555. +       if(expr) {
  556. +  value val = evaluate_expression (expr);
  557. +  struct block* b;
  558. +  global_ctor_address = *(long*)VALUE_CONTENTS_RAW(val);
  559. +  b = block_for_pc(global_ctor_address);
  560. +  if(b) {
  561. +    global_ctor_lo = b->startaddr;
  562. +    global_ctor_hi = b->endaddr;
  563. +  }
  564. +       }
  565. +     }
  566. +   }
  567. +   if(global_ctor_lo != 0) {
  568. +     for(i = 0; i < leaks_pool_max; i++)
  569. +       if(leaks_pool[i].valid == TRUE) {
  570. +  leak_trace* leak_tracep = &leaks_pool[i];
  571. +  for(j = 0; j < leak_tracep->index; j++) {
  572. +    long pc = leak_tracep->pcs[j];
  573. +    if(global_ctor_lo < pc && global_ctor_hi > pc) {
  574. +      leak_tracep->valid = FALSE;
  575. +      break;
  576. +    }
  577. +  }
  578. +       }
  579. +   }
  580. +   {
  581. +     struct frame_info fi;
  582. +     for(i = 0; i < leaks_pool_max; i++)
  583. +       if(leaks_pool[i].valid == TRUE) {
  584. +  leak_trace* leak_tracep = &leaks_pool[i];
  585. +  printf_filtered("Backtrace for 0x%x\n", leak_tracep->address);
  586. +  fi.next_frame = (FRAME_ADDR)0;
  587. +  for(j = 0; j < leak_tracep->index; j++) {
  588. +    fi.next_frame = (FRAME_ADDR)1;
  589. +    fi.pc = leak_tracep->pcs[j];
  590. +    print_frame_info (&fi, j, 0, 0);
  591. +  }
  592. +       }
  593. +   }
  594. + }
  595.   /* Print briefly all stack frames or just the innermost COUNT frames.  */
  596.   
  597.   static void
  598. ***************
  599. *** 1222,1227 ****
  600. --- 1591,1600 ----
  601.     backtrace_limit = 30;
  602.   #endif
  603.   
  604. +   add_com ("leaks_trace", class_stack, leaks_trace_command, "");
  605. +   add_com ("leaks_malloc", class_stack, leaks_malloc_command, "");
  606. +   add_com ("leaks_free", class_stack, leaks_free_command, "");
  607. +   add_com ("leaks_print", class_stack, leaks_print_command, "");
  608.     add_com ("return", class_stack, return_command,
  609.       "Make selected stack frame return to its caller.\n\
  610.   Control remains in the debugger, but when you continue\n\
  611.  
  612. ================================================================================
  613. -- 
  614.             Bertil Askelid
  615.             ebubra@ebu.ericsson.se
  616.