xref: /dragonfly/contrib/gdb-7/gdb/probe.c (revision 3851e4b8)
1 /* Generic static probe support for GDB.
2 
3    Copyright (C) 2012-2013 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include "defs.h"
21 #include "probe.h"
22 #include "command.h"
23 #include "cli/cli-cmds.h"
24 #include "cli/cli-utils.h"
25 #include "objfiles.h"
26 #include "symtab.h"
27 #include "progspace.h"
28 #include "filenames.h"
29 #include "exceptions.h"
30 #include "linespec.h"
31 #include "gdb_regex.h"
32 #include "frame.h"
33 #include "arch-utils.h"
34 #include <ctype.h>
35 
36 
37 
38 /* See definition in probe.h.  */
39 
40 struct symtabs_and_lines
41 parse_probes (char **argptr, struct linespec_result *canonical)
42 {
43   char *arg_start, *arg_end, *arg;
44   char *objfile_name = NULL, *provider = NULL, *name, *p;
45   struct cleanup *cleanup;
46   struct symtabs_and_lines result;
47   struct objfile *objfile;
48   struct program_space *pspace;
49   const struct probe_ops *probe_ops;
50   const char *cs;
51 
52   result.sals = NULL;
53   result.nelts = 0;
54 
55   arg_start = *argptr;
56 
57   cs = *argptr;
58   probe_ops = probe_linespec_to_ops (&cs);
59   gdb_assert (probe_ops != NULL);
60 
61   arg = (char *) cs;
62   arg = skip_spaces (arg);
63   if (!*arg)
64     error (_("argument to `%s' missing"), arg_start);
65 
66   arg_end = skip_to_space (arg);
67 
68   /* We make a copy here so we can write over parts with impunity.  */
69   arg = savestring (arg, arg_end - arg);
70   cleanup = make_cleanup (xfree, arg);
71 
72   /* Extract each word from the argument, separated by ":"s.  */
73   p = strchr (arg, ':');
74   if (p == NULL)
75     {
76       /* This is `-p name'.  */
77       name = arg;
78     }
79   else
80     {
81       char *hold = p + 1;
82 
83       *p = '\0';
84       p = strchr (hold, ':');
85       if (p == NULL)
86 	{
87 	  /* This is `-p provider:name'.  */
88 	  provider = arg;
89 	  name = hold;
90 	}
91       else
92 	{
93 	  /* This is `-p objfile:provider:name'.  */
94 	  *p = '\0';
95 	  objfile_name = arg;
96 	  provider = hold;
97 	  name = p + 1;
98 	}
99     }
100 
101   if (*name == '\0')
102     error (_("no probe name specified"));
103   if (provider && *provider == '\0')
104     error (_("invalid provider name"));
105   if (objfile_name && *objfile_name == '\0')
106     error (_("invalid objfile name"));
107 
108   ALL_PSPACES (pspace)
109     ALL_PSPACE_OBJFILES (pspace, objfile)
110       {
111 	VEC (probe_p) *probes;
112 	struct probe *probe;
113 	int ix;
114 
115 	if (!objfile->sf || !objfile->sf->sym_probe_fns)
116 	  continue;
117 
118 	if (objfile_name
119 	    && FILENAME_CMP (objfile->name, objfile_name) != 0
120 	    && FILENAME_CMP (lbasename (objfile->name), objfile_name) != 0)
121 	  continue;
122 
123 	probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile);
124 
125 	for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
126 	  {
127 	    struct symtab_and_line *sal;
128 
129 	    if (probe_ops != &probe_ops_any && probe->pops != probe_ops)
130 	      continue;
131 
132 	    if (provider && strcmp (probe->provider, provider) != 0)
133 	      continue;
134 
135 	    if (strcmp (probe->name, name) != 0)
136 	      continue;
137 
138 	    ++result.nelts;
139 	    result.sals = xrealloc (result.sals,
140 				    result.nelts
141 				    * sizeof (struct symtab_and_line));
142 	    sal = &result.sals[result.nelts - 1];
143 
144 	    init_sal (sal);
145 
146 	    sal->pc = probe->address;
147 	    sal->explicit_pc = 1;
148 	    sal->section = find_pc_overlay (sal->pc);
149 	    sal->pspace = pspace;
150 	    sal->probe = probe;
151 	  }
152       }
153 
154   if (result.nelts == 0)
155     {
156       throw_error (NOT_FOUND_ERROR,
157 		   _("No probe matching objfile=`%s', provider=`%s', name=`%s'"),
158 		   objfile_name ? objfile_name : _("<any>"),
159 		   provider ? provider : _("<any>"),
160 		   name);
161     }
162 
163   if (canonical)
164     {
165       canonical->special_display = 1;
166       canonical->pre_expanded = 1;
167       canonical->addr_string = savestring (*argptr, arg_end - *argptr);
168     }
169 
170   *argptr = arg_end;
171   do_cleanups (cleanup);
172 
173   return result;
174 }
175 
176 /* See definition in probe.h.  */
177 
178 VEC (probe_p) *
179 find_probes_in_objfile (struct objfile *objfile, const char *provider,
180 			const char *name)
181 {
182   VEC (probe_p) *probes, *result = NULL;
183   int ix;
184   struct probe *probe;
185 
186   if (!objfile->sf || !objfile->sf->sym_probe_fns)
187     return NULL;
188 
189   probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile);
190   for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
191     {
192       if (strcmp (probe->provider, provider) != 0)
193 	continue;
194 
195       if (strcmp (probe->name, name) != 0)
196 	continue;
197 
198       VEC_safe_push (probe_p, result, probe);
199     }
200 
201   return result;
202 }
203 
204 /* See definition in probe.h.  */
205 
206 struct probe *
207 find_probe_by_pc (CORE_ADDR pc)
208 {
209   struct objfile *objfile;
210 
211   ALL_OBJFILES (objfile)
212   {
213     VEC (probe_p) *probes;
214     int ix;
215     struct probe *probe;
216 
217     if (!objfile->sf || !objfile->sf->sym_probe_fns)
218       continue;
219 
220     /* If this proves too inefficient, we can replace with a hash.  */
221     probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile);
222     for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
223       if (probe->address == pc)
224 	return probe;
225   }
226 
227   return NULL;
228 }
229 
230 
231 
232 /* A helper function for collect_probes that compiles a regexp and
233    throws an exception on error.  This installs a cleanup to free the
234    resulting pattern on success.  If RX is NULL, this does nothing.  */
235 
236 static void
237 compile_rx_or_error (regex_t *pattern, const char *rx, const char *message)
238 {
239   int code;
240 
241   if (!rx)
242     return;
243 
244   code = regcomp (pattern, rx, REG_NOSUB);
245   if (code == 0)
246     make_regfree_cleanup (pattern);
247   else
248     {
249       char *err = get_regcomp_error (code, pattern);
250 
251       make_cleanup (xfree, err);
252       error (("%s: %s"), message, err);
253     }
254 }
255 
256 /* Make a vector of probes matching OBJNAME, PROVIDER, and PROBE_NAME.
257    If POPS is not NULL, only probes of this certain probe_ops will match.
258    Each argument is a regexp, or NULL, which matches anything.  */
259 
260 static VEC (probe_p) *
261 collect_probes (char *objname, char *provider, char *probe_name,
262 		const struct probe_ops *pops)
263 {
264   struct objfile *objfile;
265   VEC (probe_p) *result = NULL;
266   struct cleanup *cleanup, *cleanup_temps;
267   regex_t obj_pat, prov_pat, probe_pat;
268 
269   cleanup = make_cleanup (VEC_cleanup (probe_p), &result);
270 
271   cleanup_temps = make_cleanup (null_cleanup, NULL);
272   compile_rx_or_error (&prov_pat, provider, _("Invalid provider regexp"));
273   compile_rx_or_error (&probe_pat, probe_name, _("Invalid probe regexp"));
274   compile_rx_or_error (&obj_pat, objname, _("Invalid object file regexp"));
275 
276   ALL_OBJFILES (objfile)
277     {
278       VEC (probe_p) *probes;
279       struct probe *probe;
280       int ix;
281 
282       if (! objfile->sf || ! objfile->sf->sym_probe_fns)
283 	continue;
284 
285       if (objname)
286 	{
287 	  if (regexec (&obj_pat, objfile->name, 0, NULL, 0) != 0)
288 	    continue;
289 	}
290 
291       probes = objfile->sf->sym_probe_fns->sym_get_probes (objfile);
292 
293       for (ix = 0; VEC_iterate (probe_p, probes, ix, probe); ix++)
294 	{
295 	  if (pops != NULL && probe->pops != pops)
296 	    continue;
297 
298 	  if (provider
299 	      && regexec (&prov_pat, probe->provider, 0, NULL, 0) != 0)
300 	    continue;
301 
302 	  if (probe_name
303 	      && regexec (&probe_pat, probe->name, 0, NULL, 0) != 0)
304 	    continue;
305 
306 	  VEC_safe_push (probe_p, result, probe);
307 	}
308     }
309 
310   do_cleanups (cleanup_temps);
311   discard_cleanups (cleanup);
312   return result;
313 }
314 
315 /* A qsort comparison function for probe_p objects.  */
316 
317 static int
318 compare_probes (const void *a, const void *b)
319 {
320   const struct probe *pa = *((const struct probe **) a);
321   const struct probe *pb = *((const struct probe **) b);
322   int v;
323 
324   v = strcmp (pa->provider, pb->provider);
325   if (v)
326     return v;
327 
328   v = strcmp (pa->name, pb->name);
329   if (v)
330     return v;
331 
332   if (pa->address < pb->address)
333     return -1;
334   if (pa->address > pb->address)
335     return 1;
336 
337   return strcmp (pa->objfile->name, pb->objfile->name);
338 }
339 
340 /* Helper function that generate entries in the ui_out table being
341    crafted by `info_probes_for_ops'.  */
342 
343 static void
344 gen_ui_out_table_header_info (VEC (probe_p) *probes,
345 			      const struct probe_ops *p)
346 {
347   /* `headings' refers to the names of the columns when printing `info
348      probes'.  */
349   VEC (info_probe_column_s) *headings = NULL;
350   struct cleanup *c;
351   info_probe_column_s *column;
352   size_t headings_size;
353   int ix;
354 
355   gdb_assert (p != NULL);
356 
357   if (p->gen_info_probes_table_header == NULL
358       && p->gen_info_probes_table_values == NULL)
359     return;
360 
361   gdb_assert (p->gen_info_probes_table_header != NULL
362 	      && p->gen_info_probes_table_values != NULL);
363 
364   c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings);
365   p->gen_info_probes_table_header (&headings);
366 
367   headings_size = VEC_length (info_probe_column_s, headings);
368 
369   for (ix = 0;
370        VEC_iterate (info_probe_column_s, headings, ix, column);
371        ++ix)
372     {
373       struct probe *probe;
374       int jx;
375       size_t size_max = strlen (column->print_name);
376 
377       for (jx = 0; VEC_iterate (probe_p, probes, jx, probe); ++jx)
378 	{
379 	  /* `probe_fields' refers to the values of each new field that this
380 	     probe will display.  */
381 	  VEC (const_char_ptr) *probe_fields = NULL;
382 	  struct cleanup *c2;
383 	  const char *val;
384 	  int kx;
385 
386 	  if (probe->pops != p)
387 	    continue;
388 
389 	  c2 = make_cleanup (VEC_cleanup (const_char_ptr), &probe_fields);
390 	  p->gen_info_probes_table_values (probe, &probe_fields);
391 
392 	  gdb_assert (VEC_length (const_char_ptr, probe_fields)
393 		      == headings_size);
394 
395 	  for (kx = 0; VEC_iterate (const_char_ptr, probe_fields, kx, val);
396 	       ++kx)
397 	    {
398 	      /* It is valid to have a NULL value here, which means that the
399 		 backend does not have something to write and this particular
400 		 field should be skipped.  */
401 	      if (val == NULL)
402 		continue;
403 
404 	      size_max = max (strlen (val), size_max);
405 	    }
406 	  do_cleanups (c2);
407 	}
408 
409       ui_out_table_header (current_uiout, size_max, ui_left,
410 			   column->field_name, column->print_name);
411     }
412 
413   do_cleanups (c);
414 }
415 
416 /* Helper function to print extra information about a probe and an objfile
417    represented by PROBE.  */
418 
419 static void
420 print_ui_out_info (struct probe *probe)
421 {
422   int ix;
423   int j = 0;
424   /* `values' refers to the actual values of each new field in the output
425      of `info probe'.  `headings' refers to the names of each new field.  */
426   VEC (const_char_ptr) *values = NULL;
427   VEC (info_probe_column_s) *headings = NULL;
428   info_probe_column_s *column;
429   struct cleanup *c;
430 
431   gdb_assert (probe != NULL);
432   gdb_assert (probe->pops != NULL);
433 
434   if (probe->pops->gen_info_probes_table_header == NULL
435       && probe->pops->gen_info_probes_table_values == NULL)
436     return;
437 
438   gdb_assert (probe->pops->gen_info_probes_table_header != NULL
439 	      && probe->pops->gen_info_probes_table_values != NULL);
440 
441   c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings);
442   make_cleanup (VEC_cleanup (const_char_ptr), &values);
443 
444   probe->pops->gen_info_probes_table_header (&headings);
445   probe->pops->gen_info_probes_table_values (probe, &values);
446 
447   gdb_assert (VEC_length (info_probe_column_s, headings)
448 	      == VEC_length (const_char_ptr, values));
449 
450   for (ix = 0;
451        VEC_iterate (info_probe_column_s, headings, ix, column);
452        ++ix)
453     {
454       const char *val = VEC_index (const_char_ptr, values, j++);
455 
456       if (val == NULL)
457 	ui_out_field_skip (current_uiout, column->field_name);
458       else
459 	ui_out_field_string (current_uiout, column->field_name, val);
460     }
461 
462   do_cleanups (c);
463 }
464 
465 /* Helper function that returns the number of extra fields which POPS will
466    need.  */
467 
468 static int
469 get_number_extra_fields (const struct probe_ops *pops)
470 {
471   VEC (info_probe_column_s) *headings = NULL;
472   struct cleanup *c;
473   int n;
474 
475   if (pops->gen_info_probes_table_header == NULL)
476     return 0;
477 
478   c = make_cleanup (VEC_cleanup (info_probe_column_s), &headings);
479   pops->gen_info_probes_table_header (&headings);
480 
481   n = VEC_length (info_probe_column_s, headings);
482 
483   do_cleanups (c);
484 
485   return n;
486 }
487 
488 /* See comment in probe.h.  */
489 
490 void
491 info_probes_for_ops (char *arg, int from_tty, const struct probe_ops *pops)
492 {
493   char *provider, *probe_name = NULL, *objname = NULL;
494   struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
495   VEC (probe_p) *probes;
496   int i, any_found;
497   int ui_out_extra_fields = 0;
498   size_t size_addr;
499   size_t size_name = strlen ("Name");
500   size_t size_objname = strlen ("Object");
501   size_t size_provider = strlen ("Provider");
502   struct probe *probe;
503   struct gdbarch *gdbarch = get_current_arch ();
504 
505   /* Do we have a `provider:probe:objfile' style of linespec?  */
506   provider = extract_arg (&arg);
507   if (provider)
508     {
509       make_cleanup (xfree, provider);
510 
511       probe_name = extract_arg (&arg);
512       if (probe_name)
513 	{
514 	  make_cleanup (xfree, probe_name);
515 
516 	  objname = extract_arg (&arg);
517 	  if (objname)
518 	    make_cleanup (xfree, objname);
519 	}
520     }
521 
522   if (pops == NULL)
523     {
524       const struct probe_ops *po;
525       int ix;
526 
527       /* If the probe_ops is NULL, it means the user has requested a "simple"
528 	 `info probes', i.e., she wants to print all information about all
529 	 probes.  For that, we have to identify how many extra fields we will
530 	 need to add in the ui_out table.
531 
532 	 To do that, we iterate over all probe_ops, querying each one about
533 	 its extra fields, and incrementing `ui_out_extra_fields' to reflect
534 	 that number.  */
535 
536       for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po); ++ix)
537 	ui_out_extra_fields += get_number_extra_fields (po);
538     }
539   else
540     ui_out_extra_fields = get_number_extra_fields (pops);
541 
542   probes = collect_probes (objname, provider, probe_name, pops);
543   make_cleanup (VEC_cleanup (probe_p), &probes);
544   make_cleanup_ui_out_table_begin_end (current_uiout,
545 				       4 + ui_out_extra_fields,
546 				       VEC_length (probe_p, probes),
547 				       "StaticProbes");
548 
549   if (!VEC_empty (probe_p, probes))
550     qsort (VEC_address (probe_p, probes), VEC_length (probe_p, probes),
551 	   sizeof (probe_p), compare_probes);
552 
553   /* What's the size of an address in our architecture?  */
554   size_addr = gdbarch_addr_bit (gdbarch) == 64 ? 18 : 10;
555 
556   /* Determining the maximum size of each field (`provider', `name' and
557      `objname').  */
558   for (i = 0; VEC_iterate (probe_p, probes, i, probe); ++i)
559     {
560       size_name = max (strlen (probe->name), size_name);
561       size_provider = max (strlen (probe->provider), size_provider);
562       size_objname = max (strlen (probe->objfile->name), size_objname);
563     }
564 
565   ui_out_table_header (current_uiout, size_provider, ui_left, "provider",
566 		       _("Provider"));
567   ui_out_table_header (current_uiout, size_name, ui_left, "name", _("Name"));
568   ui_out_table_header (current_uiout, size_addr, ui_left, "addr", _("Where"));
569 
570   if (pops == NULL)
571     {
572       const struct probe_ops *po;
573       int ix;
574 
575       /* We have to generate the table header for each new probe type that we
576 	 will print.  */
577       for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po); ++ix)
578 	gen_ui_out_table_header_info (probes, po);
579     }
580   else
581     gen_ui_out_table_header_info (probes, pops);
582 
583   ui_out_table_header (current_uiout, size_objname, ui_left, "object",
584 		       _("Object"));
585   ui_out_table_body (current_uiout);
586 
587   for (i = 0; VEC_iterate (probe_p, probes, i, probe); ++i)
588     {
589       struct cleanup *inner;
590 
591       inner = make_cleanup_ui_out_tuple_begin_end (current_uiout, "probe");
592 
593       ui_out_field_string (current_uiout, "provider", probe->provider);
594       ui_out_field_string (current_uiout, "name", probe->name);
595       ui_out_field_core_addr (current_uiout, "addr",
596 			      get_objfile_arch (probe->objfile),
597 			      probe->address);
598 
599       if (pops == NULL)
600 	{
601 	  const struct probe_ops *po;
602 	  int ix;
603 
604 	  for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, po);
605 	       ++ix)
606 	    if (probe->pops == po)
607 	      print_ui_out_info (probe);
608 	}
609       else
610 	print_ui_out_info (probe);
611 
612       ui_out_field_string (current_uiout, "object", probe->objfile->name);
613       ui_out_text (current_uiout, "\n");
614 
615       do_cleanups (inner);
616     }
617 
618   any_found = !VEC_empty (probe_p, probes);
619   do_cleanups (cleanup);
620 
621   if (!any_found)
622     ui_out_message (current_uiout, 0, _("No probes matched.\n"));
623 }
624 
625 /* Implementation of the `info probes' command.  */
626 
627 static void
628 info_probes_command (char *arg, int from_tty)
629 {
630   info_probes_for_ops (arg, from_tty, NULL);
631 }
632 
633 /* See comments in probe.h.  */
634 
635 struct value *
636 probe_safe_evaluate_at_pc (struct frame_info *frame, unsigned n)
637 {
638   struct probe *probe;
639   const struct sym_probe_fns *probe_fns;
640   unsigned n_args;
641 
642   probe = find_probe_by_pc (get_frame_pc (frame));
643   if (!probe)
644     return NULL;
645 
646   gdb_assert (probe->objfile != NULL);
647   gdb_assert (probe->objfile->sf != NULL);
648   gdb_assert (probe->objfile->sf->sym_probe_fns != NULL);
649 
650   probe_fns = probe->objfile->sf->sym_probe_fns;
651   n_args = probe_fns->sym_get_probe_argument_count (probe);
652 
653   if (n >= n_args)
654     return NULL;
655 
656   return probe_fns->sym_evaluate_probe_argument (probe, n);
657 }
658 
659 /* See comment in probe.h.  */
660 
661 const struct probe_ops *
662 probe_linespec_to_ops (const char **linespecp)
663 {
664   int ix;
665   const struct probe_ops *probe_ops;
666 
667   for (ix = 0; VEC_iterate (probe_ops_cp, all_probe_ops, ix, probe_ops); ix++)
668     if (probe_ops->is_linespec (linespecp))
669       return probe_ops;
670 
671   return NULL;
672 }
673 
674 /* See comment in probe.h.  */
675 
676 int
677 probe_is_linespec_by_keyword (const char **linespecp, const char *const *keywords)
678 {
679   const char *s = *linespecp;
680   const char *const *csp;
681 
682   for (csp = keywords; *csp; csp++)
683     {
684       const char *keyword = *csp;
685       size_t len = strlen (keyword);
686 
687       if (strncmp (s, keyword, len) == 0 && isspace (s[len]))
688 	{
689 	  *linespecp += len + 1;
690 	  return 1;
691 	}
692     }
693 
694   return 0;
695 }
696 
697 /* Implementation of `is_linespec' method for `struct probe_ops'.  */
698 
699 static int
700 probe_any_is_linespec (const char **linespecp)
701 {
702   static const char *const keywords[] = { "-p", "-probe", NULL };
703 
704   return probe_is_linespec_by_keyword (linespecp, keywords);
705 }
706 
707 /* Dummy method used for `probe_ops_any'.  */
708 
709 static void
710 probe_any_get_probes (VEC (probe_p) **probesp, struct objfile *objfile)
711 {
712   /* No probes can be provided by this dummy backend.  */
713 }
714 
715 /* Operations associated with a generic probe.  */
716 
717 const struct probe_ops probe_ops_any =
718 {
719   probe_any_is_linespec,
720   probe_any_get_probes,
721 };
722 
723 /* See comments in probe.h.  */
724 
725 struct cmd_list_element **
726 info_probes_cmdlist_get (void)
727 {
728   static struct cmd_list_element *info_probes_cmdlist;
729 
730   if (info_probes_cmdlist == NULL)
731     add_prefix_cmd ("probes", class_info, info_probes_command,
732 		    _("\
733 Show available static probes.\n\
734 Usage: info probes [all|TYPE [ARGS]]\n\
735 TYPE specifies the type of the probe, and can be one of the following:\n\
736   - stap\n\
737 If you specify TYPE, there may be additional arguments needed by the\n\
738 subcommand.\n\
739 If you do not specify any argument, or specify `all', then the command\n\
740 will show information about all types of probes."),
741 		    &info_probes_cmdlist, "info probes ",
742 		    0/*allow-unknown*/, &infolist);
743 
744   return &info_probes_cmdlist;
745 }
746 
747 VEC (probe_ops_cp) *all_probe_ops;
748 
749 void _initialize_probe (void);
750 
751 void
752 _initialize_probe (void)
753 {
754   VEC_safe_push (probe_ops_cp, all_probe_ops, &probe_ops_any);
755 
756   add_cmd ("all", class_info, info_probes_command,
757 	   _("\
758 Show information about all type of probes."),
759 	   info_probes_cmdlist_get ());
760 }
761