1 // Setup routines for creating fully populated DWFLs. Used in pass 2 and 3.
2 // Copyright (C) 2009 Red Hat, Inc.
4 // This file is part of systemtap, and is free software. You can
5 // redistribute it and/or modify it under the terms of the GNU General
6 // Public License (GPL); either version 2, or (at your option) any
10 #include "setupdwfl.h"
12 #include "dwarf_wrappers.h"
29 #include <sys/times.h>
31 #include <sys/types.h>
35 #include <sys/utsname.h>
38 // XXX: also consider adding $HOME/.debug/ for perf build-id-cache
39 static const char *debuginfo_path_arr
= "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug:build";
40 static const char *debuginfo_env_arr
= getenv("SYSTEMTAP_DEBUGINFO_PATH");
41 static const char *debuginfo_path
= (debuginfo_env_arr
?: debuginfo_path_arr
);
43 // NB: kernel_build_tree doesn't enter into this, as it's for
44 // kernel-side modules only.
45 // XXX: also consider adding $HOME/.debug/ for perf build-id-cache
46 static const char *debuginfo_usr_path_arr
= "+:.debug:/usr/lib/debug:/var/cache/abrt-di/usr/lib/debug";
47 static const char *debuginfo_usr_path
= (debuginfo_env_arr
48 ?: debuginfo_usr_path_arr
);
50 // A pointer to the current systemtap session for use only by a few
51 // dwfl calls. DO NOT rely on this, as it is cleared after use.
53 static systemtap_session
* current_session_for_find_debuginfo
;
55 // The path to the abrt-action-install-debuginfo-to-abrt-cache
57 #define ABRT_PATH "/usr/bin/abrt-action-install-debuginfo-to-abrt-cache"
59 static const Dwfl_Callbacks kernel_callbacks
=
61 dwfl_linux_kernel_find_elf
,
62 internal_find_debuginfo
,
63 dwfl_offline_section_address
,
64 (char **) & debuginfo_path
67 static const Dwfl_Callbacks user_callbacks
=
70 internal_find_debuginfo
,
71 NULL
, /* ET_REL not supported for user space, only ET_EXEC and ET_DYN.
72 dwfl_offline_section_address, */
73 (char **) & debuginfo_usr_path
78 // Store last kernel and user Dwfl for reuse since they are often
79 // re-requested (in phase 2 and then in phase 3).
80 static DwflPtr kernel_dwfl
;
81 static DwflPtr user_dwfl
;
83 // Setup in setup_dwfl_kernel(), for use in setup_dwfl_report_kernel_p().
84 // Either offline_search_modname or offline_search_names is
85 // used. When offline_search_modname is not NULL then
86 // offline_search_names is ignored.
87 static const char *offline_search_modname
;
88 static set
<string
> offline_search_names
;
89 static unsigned offline_modules_found
;
91 // Whether or not we are done reporting kernel modules in
92 // set_dwfl_report_kernel_p().
93 static bool setup_dwfl_done
;
95 // Kept for user_dwfl cache, user modules don't allow wildcards, so
96 // just keep the set of module strings.
97 static set
<string
> user_modset
;
99 // Determines whether or not we will make setup_dwfl_report_kernel_p
100 // report true for all module dependencies. This is necessary for
101 // correctly resolving some dwarf constructs that relocate against
102 // symbols in vmlinux and/or other modules they depend on. See PR10678.
103 static const bool setup_all_deps
= true;
105 // Where to find the kernel (and the Modules.dep file). Setup in
106 // setup_dwfl_kernel(), used by dwfl_linux_kernel_report_offline() and
108 static string elfutils_kernel_path
;
110 static bool is_comma_dash(const char c
) { return (c
== ',' || c
== '-'); }
112 // The module name is the basename (without the extension) of the
113 // module path, with ',' and '-' replaced by '_'.
115 modname_from_path(const string
&path
)
117 size_t dot
= path
.rfind('.');
118 size_t slash
= path
.rfind('/');
119 if (dot
== string::npos
|| slash
== string::npos
|| dot
< slash
)
121 string name
= path
.substr(slash
+ 1, dot
- slash
- 1);
122 replace_if(name
.begin(), name
.end(), is_comma_dash
, '_');
126 // Try to parse modules.dep file,
127 // Simple format: module path (either full or relative), colon,
128 // (possibly empty) space delimited list of module (path)
137 if (elfutils_kernel_path
[0] == '/')
139 modulesdep
= elfutils_kernel_path
;
140 modulesdep
+= "/modules.dep";
144 modulesdep
= "/lib/modules/";
145 modulesdep
+= elfutils_kernel_path
;
146 modulesdep
+= "/modules.dep";
148 in
.open(modulesdep
.c_str());
152 while (getline (in
, l
))
154 size_t off
= l
.find (':');
155 if (off
!= string::npos
)
157 string modpath
, modname
;
158 modpath
= l
.substr (0, off
);
159 modname
= modname_from_path (modpath
);
164 if (offline_search_modname
!= NULL
)
166 if (dwflpp::name_has_wildcard (offline_search_modname
))
168 dep_needed
= !fnmatch (offline_search_modname
,
169 modname
.c_str (), 0);
171 offline_search_names
.insert (modname
);
175 dep_needed
= ! strcmp(modname
.c_str (),
176 offline_search_modname
);
178 offline_search_names
.insert (modname
);
182 dep_needed
= (offline_search_names
.find (modname
)
183 != offline_search_names
.end ());
188 string depstring
= l
.substr (off
+ 1);
189 if (depstring
.size () > 0)
191 stringstream
ss (depstring
);
193 while (ss
>> deppath
)
194 offline_search_names
.insert (modname_from_path(deppath
));
200 // We always want kernel (needed in list so size checks match).
201 // Everything needed now stored in offline_search_names.
202 offline_search_names
.insert ("kernel");
203 offline_search_modname
= NULL
;
206 // Set up our offline search for kernel modules. We don't want the
207 // offline search iteration to do a complete search of the kernel
208 // build tree, since that's wasteful, so create a predicate that
209 // filters and stops reporting as soon as we got everything.
211 setup_dwfl_report_kernel_p(const char* modname
, const char* filename
)
213 if (pending_interrupts
|| setup_dwfl_done
)
216 // elfutils sends us NULL filenames sometimes if it can't find dwarf
217 if (filename
== NULL
)
220 // Check kernel first since it is often the only thing needed,
221 // then we never have to parse and setup the module deps map.
222 // It will be reported as the very first thing.
223 if (setup_all_deps
&& ! strcmp (modname
, "kernel"))
225 if ((offline_search_modname
!= NULL
226 && ! strcmp (offline_search_modname
, "kernel"))
227 || (offline_search_names
.size() == 1
228 && *offline_search_names
.begin() == "kernel"))
229 setup_dwfl_done
= true;
233 offline_modules_found
++;
237 // If offline_search_modname is setup use it (either as regexp or
238 // explicit module/kernel name) and ignore offline_search_names.
239 // Otherwise use offline_search_names exclusively.
240 if (offline_search_modname
!= NULL
)
242 if (dwflpp::name_has_wildcard (offline_search_modname
))
244 int match_p
= !fnmatch(offline_search_modname
, modname
, 0);
245 // In the wildcard case, we don't short-circuit (return -1)
246 // analogously to dwflpp::module_name_final_match().
248 offline_modules_found
++;
252 { /* non-wildcard mode, reject mismatching module names */
253 if (strcmp(modname
, offline_search_modname
))
257 // Done, only one name needed and found it.
258 offline_modules_found
++;
259 setup_dwfl_done
= true;
265 { /* find all in set mode, reject mismatching module names */
266 if (offline_search_names
.find(modname
) == offline_search_names
.end())
270 offline_modules_found
++;
271 if (offline_search_names
.size() == offline_modules_found
)
272 setup_dwfl_done
= true;
279 setup_dwfl_kernel (unsigned *modules_found
, systemtap_session
&s
)
281 Dwfl
*dwfl
= dwfl_begin (&kernel_callbacks
);
282 dwfl_assert ("dwfl_begin", dwfl
);
283 dwfl_report_begin (dwfl
);
285 // We have a problem with -r REVISION vs -r BUILDDIR here. If
286 // we're running against a fedora/rhel style kernel-debuginfo
287 // tree, s.kernel_build_tree is not the place where the unstripped
288 // vmlinux will be installed. Rather, it's over yonder at
289 // /usr/lib/debug/lib/modules/$REVISION/. It seems that there is
290 // no way to set the dwfl_callback.debuginfo_path and always
291 // passs the plain kernel_release here. So instead we have to
292 // hard-code this magic here.
293 if (s
.kernel_build_tree
== string("/lib/modules/"
296 elfutils_kernel_path
= s
.kernel_release
;
298 elfutils_kernel_path
= s
.kernel_build_tree
;
300 offline_modules_found
= 0;
302 // First try to report full path modules.
303 set
<string
>::iterator it
= offline_search_names
.begin();
305 while (it
!= offline_search_names
.end())
309 const char *cname
= (*it
).c_str();
310 Dwfl_Module
*mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
312 offline_modules_found
++;
314 else if ((*it
) == "kernel")
319 // We always need this, even when offline_search_modname is NULL
320 // and offline_search_names is empty because we still might want
321 // the kernel vmlinux reported.
322 setup_dwfl_done
= false;
323 int rc
= dwfl_linux_kernel_report_offline (dwfl
,
324 elfutils_kernel_path
.c_str(),
325 &setup_dwfl_report_kernel_p
);
327 (void) rc
; /* Ignore since the predicate probably returned -1 at some point,
328 And libdwfl interprets that as "whole query failed" rather than
329 "found it already, stop looking". */
331 // NB: the result of an _offline call is the assignment of
332 // virtualized addresses to relocatable objects such as
333 // modules. These have to be converted to real addresses at
334 // run time. See the dwarf_derived_probe ctor and its caller.
336 // If no modules were found, and we are probing the kernel,
337 // attempt to download the kernel debuginfo.
340 // Get the kernel build ID. We still need to call this even if we
341 // already have the kernel debuginfo installed as it adds the
342 // build ID to the script hash.
343 string hex
= get_kernel_build_id(s
);
344 if (offline_modules_found
== 0 && s
.download_dbinfo
!= 0 && !hex
.empty())
346 rc
= download_kernel_debuginfo(s
, hex
);
348 return setup_dwfl_kernel (modules_found
, s
);
352 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
353 *modules_found
= offline_modules_found
;
355 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
356 kernel_dwfl
= DwflPtr(stap_dwfl
);
362 setup_dwfl_kernel(const std::string
&name
,
364 systemtap_session
&s
)
366 current_session_for_find_debuginfo
= &s
;
367 const char *modname
= name
.c_str();
368 set
<string
> names
; // Default to empty
370 /* Support full path kernel modules, these cannot be regular
371 expressions, so just put them in the search set. */
372 if (name
[0] == '/' || ! dwflpp::name_has_wildcard (modname
))
378 if (kernel_dwfl
!= NULL
379 && offline_search_modname
== modname
380 && offline_search_names
== names
)
382 *found
= offline_modules_found
;
386 offline_search_modname
= modname
;
387 offline_search_names
= names
;
389 return setup_dwfl_kernel(found
, s
);
393 setup_dwfl_kernel(const std::set
<std::string
> &names
,
395 systemtap_session
&s
)
397 if (kernel_dwfl
!= NULL
398 && offline_search_modname
== NULL
399 && offline_search_names
== names
)
401 *found
= offline_modules_found
;
405 offline_search_modname
= NULL
;
406 offline_search_names
= names
;
407 return setup_dwfl_kernel(found
, s
);
411 setup_dwfl_user(const std::string
&name
)
413 if (user_dwfl
!= NULL
414 && user_modset
.size() == 1
415 && (*user_modset
.begin()) == name
)
419 user_modset
.insert(name
);
421 Dwfl
*dwfl
= dwfl_begin (&user_callbacks
);
422 dwfl_assert("dwfl_begin", dwfl
);
423 dwfl_report_begin (dwfl
);
425 // XXX: should support buildid-based naming
426 const char *cname
= name
.c_str();
427 Dwfl_Module
*mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
428 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
435 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
436 user_dwfl
= DwflPtr(stap_dwfl
);
442 setup_dwfl_user(std::vector
<std::string
>::const_iterator
&begin
,
443 const std::vector
<std::string
>::const_iterator
&end
,
444 bool all_needed
, systemtap_session
&s
)
446 current_session_for_find_debuginfo
= &s
;
447 // See if we have this dwfl already cached
448 set
<string
> modset(begin
, end
);
449 if (user_dwfl
!= NULL
&& modset
== user_modset
)
452 user_modset
= modset
;
454 Dwfl
*dwfl
= dwfl_begin (&user_callbacks
);
455 dwfl_assert("dwfl_begin", dwfl
);
456 dwfl_report_begin (dwfl
);
457 Dwfl_Module
*mod
= NULL
;
458 // XXX: should support buildid-based naming
459 while (begin
!= end
&& dwfl
!= NULL
)
461 const char *cname
= (*begin
).c_str();
462 mod
= dwfl_report_offline (dwfl
, cname
, cname
, -1);
463 if (! mod
&& all_needed
)
471 /* Extract the build id and add it to the session variable
472 * so it will be added to the script hash */
475 const unsigned char *bits
;
478 clog
<< _("Extracting build ID.") << endl
;
479 int bits_length
= dwfl_module_build_id(mod
, &bits
, &vaddr
);
481 /* Convert the binary bits to a hex string */
482 string hex
= hex_dump(bits
, bits_length
);
484 //Store the build ID in the session
485 s
.build_ids
.push_back(hex
);
489 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl
, NULL
, NULL
));
491 StapDwfl
*stap_dwfl
= new StapDwfl(dwfl
);
492 user_dwfl
= DwflPtr(stap_dwfl
);
498 is_user_module(const std::string
&m
)
500 return m
[0] == '/' && m
.rfind(".ko", m
.length() - 1) != m
.length() - 3;
504 internal_find_debuginfo (Dwfl_Module
*mod
,
505 void **userdata
__attribute__ ((unused
)),
506 const char *modname
__attribute__ ((unused
)),
507 GElf_Addr base
__attribute__ ((unused
)),
508 const char *file_name
,
509 const char *debuglink_file
,
510 GElf_Word debuglink_crc
,
511 char **debuginfo_file_name
)
516 /* To Keep track of whether the abrt successfully installed the debuginfo */
517 static int install_dbinfo_failed
= 0;
519 /* Make sure the current session variable is not null */
520 if(current_session_for_find_debuginfo
== NULL
)
521 goto call_dwfl_standard_find_debuginfo
;
523 /* Check to see if download-debuginfo=0 was set */
524 if(!current_session_for_find_debuginfo
->download_dbinfo
)
525 goto call_dwfl_standard_find_debuginfo
;
527 /* Check that we haven't already run this */
528 if (install_dbinfo_failed
< 0)
530 if(current_session_for_find_debuginfo
->verbose
> 1 && !current_session_for_find_debuginfo
->suppress_warnings
)
531 current_session_for_find_debuginfo
->print_warning( _F("We already tried running '%s'", ABRT_PATH
));
532 goto call_dwfl_standard_find_debuginfo
;
535 /* Extract the build ID */
536 const unsigned char *bits
;
538 if(current_session_for_find_debuginfo
->verbose
> 2)
539 clog
<< _("Extracting build ID.") << endl
;
540 bits_length
= dwfl_module_build_id(mod
, &bits
, &vaddr
);
542 /* Convert the binary bits to a hex string */
543 hex
= hex_dump(bits
, bits_length
);
545 /* Search for the debuginfo with the build ID */
546 if(current_session_for_find_debuginfo
->verbose
> 2)
547 clog
<< _F("Searching for debuginfo with build ID: '%s'.", hex
.c_str()) << endl
;
550 int fd
= dwfl_build_id_find_debuginfo(mod
,
553 debuginfo_file_name
);
558 /* The above failed, so call abrt-action-install-debuginfo-to-abrt-cache
559 to download and install the debuginfo */
560 if(current_session_for_find_debuginfo
->verbose
> 1)
561 clog
<< _F("Downloading and installing debuginfo with build ID: '%s' using %s.", hex
.c_str(), ABRT_PATH
) << endl
;
563 struct tms tms_before
;
564 times (& tms_before
);
565 struct timeval tv_before
;
566 struct tms tms_after
;
567 unsigned _sc_clk_tck
;
568 struct timeval tv_after
;
569 gettimeofday (&tv_before
, NULL
);
571 if(execute_abrt_action_install_debuginfo_to_abrt_cache (hex
) < 0)
573 install_dbinfo_failed
= -1;
574 if (!current_session_for_find_debuginfo
->suppress_warnings
)
575 current_session_for_find_debuginfo
->print_warning(_F("%s failed.", ABRT_PATH
));
576 goto call_dwfl_standard_find_debuginfo
;
579 _sc_clk_tck
= sysconf (_SC_CLK_TCK
);
581 gettimeofday (&tv_after
, NULL
);
582 if(current_session_for_find_debuginfo
->verbose
> 1)
583 clog
<< _("Download completed in ")
584 << ((tms_after
.tms_cutime
+ tms_after
.tms_utime
585 - tms_before
.tms_cutime
- tms_before
.tms_utime
) * 1000 / (_sc_clk_tck
)) << "usr/"
586 << ((tms_after
.tms_cstime
+ tms_after
.tms_stime
587 - tms_before
.tms_cstime
- tms_before
.tms_stime
) * 1000 / (_sc_clk_tck
)) << "sys/"
588 << ((tv_after
.tv_sec
- tv_before
.tv_sec
) * 1000 +
589 ((long)tv_after
.tv_usec
- (long)tv_before
.tv_usec
) / 1000) << "real ms"<< endl
;
591 call_dwfl_standard_find_debuginfo
:
593 /* Call the original dwfl_standard_find_debuginfo */
594 return dwfl_standard_find_debuginfo(mod
, userdata
, modname
, base
,
595 file_name
, debuglink_file
,
596 debuglink_crc
, debuginfo_file_name
);
601 execute_abrt_action_install_debuginfo_to_abrt_cache (string hex
)
603 /* Check that abrt exists */
604 if(access (ABRT_PATH
, X_OK
) < 0)
607 int timeout
= current_session_for_find_debuginfo
->download_dbinfo
;;
609 cmd
.push_back ("/bin/sh");
610 cmd
.push_back ("-c");
612 /* NOTE: abrt does not currently work with asking for confirmation
613 * in version abrt-2.0.3-1.fc15.x86_64, Bugzilla: BZ726192 */
614 if(current_session_for_find_debuginfo
->download_dbinfo
== -1)
616 cmd
.push_back ("echo " + hex
+ " | " + ABRT_PATH
+ " --ids=-");
618 if(!current_session_for_find_debuginfo
->suppress_warnings
)
619 current_session_for_find_debuginfo
->print_warning(_("Due to bug in abrt, it may continue downloading anyway without asking for confirmation."));
622 cmd
.push_back ("echo " + hex
+ " | " + ABRT_PATH
+ " -y --ids=-");
624 /* NOTE: abrt does not allow canceling the download process at the moment
625 * in version abrt-2.0.3-1.fc15.x86_64, Bugzilla: BZ730107 */
626 if(timeout
!= INT_MAX
&& !current_session_for_find_debuginfo
->suppress_warnings
)
627 current_session_for_find_debuginfo
->print_warning(_("Due to a bug in abrt, it may continue downloading after stopping stap if download times out."));
630 if(current_session_for_find_debuginfo
->verbose
> 1 || current_session_for_find_debuginfo
->download_dbinfo
== -1)
631 /* Execute abrt-action-install-debuginfo-to-abrt-cache,
632 * showing output from abrt */
633 pid
= stap_spawn(current_session_for_find_debuginfo
->verbose
, cmd
, NULL
);
636 /* Execute abrt-action-install-debuginfo-to-abrt-cache,
637 * without showing output from abrt */
638 posix_spawn_file_actions_t fa
;
639 if (posix_spawn_file_actions_init(&fa
) != 0)
641 if(posix_spawn_file_actions_addopen(&fa
, 1, "/dev/null", O_WRONLY
, 0) != 0)
643 posix_spawn_file_actions_destroy(&fa
);
646 pid
= stap_spawn(current_session_for_find_debuginfo
->verbose
, cmd
, &fa
);
647 posix_spawn_file_actions_destroy(&fa
);
650 /* Check to see if either the program successfully completed, or if it timed out. */
654 while(timer
< timeout
)
657 rc
= waitpid(pid
, &rstatus
, WNOHANG
);
660 if (rc
> 0 && WIFEXITED(rstatus
))
662 if(pending_interrupts
)
670 if (!current_session_for_find_debuginfo
->suppress_warnings
)
671 current_session_for_find_debuginfo
->print_warning(_("Aborted downloading debuginfo: timed out."));
675 /* Successfully finished downloading! */
676 #if 0 // Should not print this until BZ733690 is fixed as abrt could fail to download
677 // and it would still print success.
678 if(current_session_for_find_debuginfo
->verbose
> 1 || current_session_for_find_debuginfo
->download_dbinfo
== -1)
679 clog
<< _("Download Completed Successfully!") << endl
;
681 if(current_session_for_find_debuginfo
->verbose
> 1 || current_session_for_find_debuginfo
->download_dbinfo
== -1)
682 clog
<< _("ABRT finished attempting to download debuginfo.") << endl
;
687 /* Get the kernel build ID */
689 get_kernel_build_id(systemtap_session
&s
)
693 // Get the kernel information
694 struct utsname kernelinfo
;
695 if( uname(&kernelinfo
) < 0)
698 // Try to find BuildID from vmlinux.id
699 string kernel_buildID_path
= "/lib/modules/"
700 + (string
)kernelinfo
.release
701 + "/build/vmlinux.id";
703 clog
<< _F("Attempting to extract kernel debuginfo build ID from %s", kernel_buildID_path
.c_str()) << endl
;
704 ifstream buildIDfile
;
705 buildIDfile
.open(kernel_buildID_path
.c_str());
706 if(buildIDfile
.is_open())
708 getline(buildIDfile
, hex
);
709 if(buildIDfile
.good())
716 // Try to find BuildID from the notes file if the above didn't work and we are
717 // building a native module
718 if(found
== false && s
.release
== kernelinfo
.release
)
721 clog
<< _("Attempting to extract kernel debuginfo build ID from /sys/kernel/notes") << endl
;
723 const char *notesfile
= "/sys/kernel/notes";
724 int fd
= open64 (notesfile
, O_RDONLY
);
728 assert (sizeof (Elf32_Nhdr
) == sizeof (GElf_Nhdr
));
729 assert (sizeof (Elf64_Nhdr
) == sizeof (GElf_Nhdr
));
734 unsigned char data
[8192];
737 ssize_t n
= read (fd
, buf
.data
, sizeof buf
);
743 unsigned char *p
= buf
.data
;
744 while (p
< &buf
.data
[n
])
746 /* No translation required since we are reading the native kernel. */
747 GElf_Nhdr
*nhdr
= (GElf_Nhdr
*) p
;
749 unsigned char *name
= p
;
750 p
+= (nhdr
->n_namesz
+ 3) & -4U;
751 unsigned char *bits
= p
;
752 p
+= (nhdr
->n_descsz
+ 3) & -4U;
754 if (p
<= &buf
.data
[n
]
755 && nhdr
->n_type
== NT_GNU_BUILD_ID
756 && nhdr
->n_namesz
== sizeof "GNU"
757 && !memcmp (name
, "GNU", sizeof "GNU"))
760 hex
= hex_dump(bits
, nhdr
->n_descsz
);
773 /* Find the kernel build ID and attempt to download the matching debuginfo */
774 int download_kernel_debuginfo (systemtap_session
&s
, string hex
)
776 // NOTE: At some point we want to base the
777 // already_tried_downloading_kernel_debuginfo flag on the build ID rather
778 // than just the stap process.
780 // Don't try this again if we already did.
781 static int already_tried_downloading_kernel_debuginfo
= 0;
782 if(already_tried_downloading_kernel_debuginfo
)
785 // Attempt to download the debuginfo
787 clog
<< _F("Success! Extracted kernel debuginfo build ID: %s", hex
.c_str()) << endl
;
788 int rc
= execute_abrt_action_install_debuginfo_to_abrt_cache(hex
);
789 already_tried_downloading_kernel_debuginfo
= 1;