|
From: <sv...@va...> - 2005-06-19 19:25:47
|
Author: njn
Date: 2005-06-19 20:25:44 +0100 (Sun, 19 Jun 2005)
New Revision: 3957
Log:
Moved VG_(start_debugger) out of m_main.c into its own module,
m_debugger. This removes the dependence of m_signals.c and m_errormgr.c
on m_main.c. It required also moving VG_(clexecfd) out of m_main.c; I p=
ut
it in m_libcproc.c which seemed like an ok-but-not-great choice.
Added:
trunk/coregrind/m_debugger.c
trunk/coregrind/pub_core_debugger.h
Modified:
trunk/coregrind/Makefile.am
trunk/coregrind/m_errormgr.c
trunk/coregrind/m_libcproc.c
trunk/coregrind/m_main.c
trunk/coregrind/m_signals.c
trunk/coregrind/pub_core_libcproc.h
trunk/coregrind/pub_core_main.h
Modified: trunk/coregrind/Makefile.am
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/Makefile.am 2005-06-19 18:58:03 UTC (rev 3956)
+++ trunk/coregrind/Makefile.am 2005-06-19 19:25:44 UTC (rev 3957)
@@ -36,6 +36,7 @@
pub_core_basics.h \
pub_core_cpuid.h \
pub_core_debuginfo.h \
+ pub_core_debugger.h \
pub_core_debuglog.h \
pub_core_demangle.h \
pub_core_dispatch.h \
@@ -95,6 +96,7 @@
=20
stage2_SOURCES =3D \
m_cpuid.S \
+ m_debugger.c \
m_debuglog.c \
m_errormgr.c \
m_execontext.c \
Added: trunk/coregrind/m_debugger.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/m_debugger.c 2005-06-19 18:58:03 UTC (rev 3956)
+++ trunk/coregrind/m_debugger.c 2005-06-19 19:25:44 UTC (rev 3957)
@@ -0,0 +1,158 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Attaching a debugger. m_debugger.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2005 Julian Seward=20
+ js...@ac...
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_debugger.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_libcproc.h"
+#include "pub_core_libcsignal.h"
+#include "pub_core_options.h"
+
+// We can remove these easily by implementing our own VG_(ptrace)() and
+// VG_(fork)().
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static Int ptrace_setregs(Int pid, VexGuestArchState* vex)
+{
+ struct vki_user_regs_struct regs;
+#if defined(VGA_x86)
+ regs.cs =3D vex->guest_CS;
+ regs.ss =3D vex->guest_SS;
+ regs.ds =3D vex->guest_DS;
+ regs.es =3D vex->guest_ES;
+ regs.fs =3D vex->guest_FS;
+ regs.gs =3D vex->guest_GS;
+ regs.eax =3D vex->guest_EAX;
+ regs.ebx =3D vex->guest_EBX;
+ regs.ecx =3D vex->guest_ECX;
+ regs.edx =3D vex->guest_EDX;
+ regs.esi =3D vex->guest_ESI;
+ regs.edi =3D vex->guest_EDI;
+ regs.ebp =3D vex->guest_EBP;
+ regs.esp =3D vex->guest_ESP;
+ regs.eflags =3D LibVEX_GuestX86_get_eflags(vex);
+ regs.eip =3D vex->guest_EIP;
+
+ return ptrace(PTRACE_SETREGS, pid, NULL, ®s);
+#elif defined(VGA_amd64)
+ I_die_here;
+#else
+# error Unknown arch
+#endif
+}
+
+/* Start debugger and get it to attach to this process. Called if the
+ user requests this service after an error has been shown, so she can
+ poke around and look at parameters, memory, etc. You can't
+ meaningfully get the debugger to continue the program, though; to
+ continue, quit the debugger. */
+void VG_(start_debugger) ( ThreadId tid )
+{
+ Int pid;
+
+ if ((pid =3D fork()) =3D=3D 0) {
+ ptrace(PTRACE_TRACEME, 0, NULL, NULL);
+ VG_(kill)(VG_(getpid)(), VKI_SIGSTOP);
+
+ } else if (pid > 0) {
+ Int status;
+ Int res;
+
+ if ((res =3D VG_(waitpid)(pid, &status, 0)) =3D=3D pid &&
+ WIFSTOPPED(status) && WSTOPSIG(status) =3D=3D SIGSTOP &&
+ ptrace_setregs(pid, &(VG_(threads)[tid].arch.vex)) =3D=3D 0 &&
+ VG_(kill)(pid, SIGSTOP) =3D=3D 0 &&
+ ptrace(PTRACE_DETACH, pid, NULL, 0) =3D=3D 0)
+ {
+ Char pidbuf[15];
+ Char file[30];
+ Char buf[100];
+ Char *bufptr;
+ Char *cmdptr;
+ =20
+ VG_(sprintf)(pidbuf, "%d", pid);
+ VG_(sprintf)(file, "/proc/%d/fd/%d", pid, VG_(clexecfd));
+=20
+ bufptr =3D buf;
+ cmdptr =3D VG_(clo_db_command);
+ =20
+ while (*cmdptr) {
+ switch (*cmdptr) {
+ case '%':
+ switch (*++cmdptr) {
+ case 'f':
+ VG_(memcpy)(bufptr, file, VG_(strlen)(file));
+ bufptr +=3D VG_(strlen)(file);
+ cmdptr++;
+ break;
+ case 'p':
+ VG_(memcpy)(bufptr, pidbuf, VG_(strlen)(pidbuf));
+ bufptr +=3D VG_(strlen)(pidbuf);
+ cmdptr++;
+ break;
+ default:
+ *bufptr++ =3D *cmdptr++;
+ break;
+ }
+ break;
+ default:
+ *bufptr++ =3D *cmdptr++;
+ break;
+ }
+ }
+ =20
+ *bufptr++ =3D '\0';
+ =20
+ VG_(message)(Vg_UserMsg, "starting debugger with cmd: %s", buf)=
;
+ res =3D VG_(system)(buf);
+ if (res =3D=3D 0) { =20
+ VG_(message)(Vg_UserMsg, "");
+ VG_(message)(Vg_UserMsg,=20
+ "Debugger has detached. Valgrind regains contr=
ol. We continue.");
+ } else {
+ VG_(message)(Vg_UserMsg, "Apparently failed!");
+ VG_(message)(Vg_UserMsg, "");
+ }
+ }
+
+ VG_(kill)(pid, VKI_SIGKILL);
+ VG_(waitpid)(pid, &status, 0);
+ }
+}
+
+
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
Modified: trunk/coregrind/m_errormgr.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/m_errormgr.c 2005-06-19 18:58:03 UTC (rev 3956)
+++ trunk/coregrind/m_errormgr.c 2005-06-19 19:25:44 UTC (rev 3957)
@@ -29,7 +29,8 @@
*/
=20
#include "pub_core_basics.h"
-#include "pub_core_threadstate.h" // needed for pub_core_main.h
+#include "pub_core_threadstate.h"
+#include "pub_core_debugger.h"
#include "pub_core_debuginfo.h"
#include "pub_core_errormgr.h"
#include "pub_core_execontext.h"
@@ -38,7 +39,6 @@
#include "pub_core_libcfile.h"
#include "pub_core_libcprint.h"
#include "pub_core_libcproc.h"
-#include "pub_core_main.h" // for VG_(start_debugger)()
#include "pub_core_mallocfree.h"
#include "pub_core_options.h"
#include "pub_core_stacktrace.h"
Modified: trunk/coregrind/m_libcproc.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/m_libcproc.c 2005-06-19 18:58:03 UTC (rev 3956)
+++ trunk/coregrind/m_libcproc.c 2005-06-19 19:25:44 UTC (rev 3957)
@@ -47,6 +47,9 @@
Char** VG_(client_argv);
Char** VG_(client_envp);
=20
+/* client executable file descriptor */
+Int VG_(clexecfd);
+
/* Path to library directory */
const Char *VG_(libdir) =3D VG_LIBDIR;
=20
Modified: trunk/coregrind/m_main.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/m_main.c 2005-06-19 18:58:03 UTC (rev 3956)
+++ trunk/coregrind/m_main.c 2005-06-19 19:25:44 UTC (rev 3957)
@@ -63,8 +63,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/wait.h>
#include <unistd.h>
=20
#include "memcheck/memcheck.h"
@@ -148,119 +146,6 @@
=20
=20
/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
-/*=3D=3D=3D Miscellaneous global functions =
=3D=3D=3D*/
-/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
-
-static Int ptrace_setregs(Int pid, VexGuestArchState* vex)
-{
- struct vki_user_regs_struct regs;
-#if defined(VGA_x86)
- regs.cs =3D vex->guest_CS;
- regs.ss =3D vex->guest_SS;
- regs.ds =3D vex->guest_DS;
- regs.es =3D vex->guest_ES;
- regs.fs =3D vex->guest_FS;
- regs.gs =3D vex->guest_GS;
- regs.eax =3D vex->guest_EAX;
- regs.ebx =3D vex->guest_EBX;
- regs.ecx =3D vex->guest_ECX;
- regs.edx =3D vex->guest_EDX;
- regs.esi =3D vex->guest_ESI;
- regs.edi =3D vex->guest_EDI;
- regs.ebp =3D vex->guest_EBP;
- regs.esp =3D vex->guest_ESP;
- regs.eflags =3D LibVEX_GuestX86_get_eflags(vex);
- regs.eip =3D vex->guest_EIP;
-
- return ptrace(PTRACE_SETREGS, pid, NULL, ®s);
-#elif defined(VGA_amd64)
- I_die_here;
-#else
-# error Unknown arch
-#endif
-}
-
-/* Start debugger and get it to attach to this process. Called if the
- user requests this service after an error has been shown, so she can
- poke around and look at parameters, memory, etc. You can't
- meaningfully get the debugger to continue the program, though; to
- continue, quit the debugger. */
-void VG_(start_debugger) ( ThreadId tid )
-{
- Int pid;
-
- if ((pid =3D fork()) =3D=3D 0) {
- ptrace(PTRACE_TRACEME, 0, NULL, NULL);
- VG_(kill)(VG_(getpid)(), VKI_SIGSTOP);
-
- } else if (pid > 0) {
- Int status;
- Int res;
-
- if ((res =3D VG_(waitpid)(pid, &status, 0)) =3D=3D pid &&
- WIFSTOPPED(status) && WSTOPSIG(status) =3D=3D SIGSTOP &&
- ptrace_setregs(pid, &(VG_(threads)[tid].arch.vex)) =3D=3D 0 &&
- kill(pid, SIGSTOP) =3D=3D 0 &&
- ptrace(PTRACE_DETACH, pid, NULL, 0) =3D=3D 0)
- {
- Char pidbuf[15];
- Char file[30];
- Char buf[100];
- Char *bufptr;
- Char *cmdptr;
- =20
- VG_(sprintf)(pidbuf, "%d", pid);
- VG_(sprintf)(file, "/proc/%d/fd/%d", pid, VG_(clexecfd));
-=20
- bufptr =3D buf;
- cmdptr =3D VG_(clo_db_command);
- =20
- while (*cmdptr) {
- switch (*cmdptr) {
- case '%':
- switch (*++cmdptr) {
- case 'f':
- VG_(memcpy)(bufptr, file, VG_(strlen)(file));
- bufptr +=3D VG_(strlen)(file);
- cmdptr++;
- break;
- case 'p':
- VG_(memcpy)(bufptr, pidbuf, VG_(strlen)(pidbuf));
- bufptr +=3D VG_(strlen)(pidbuf);
- cmdptr++;
- break;
- default:
- *bufptr++ =3D *cmdptr++;
- break;
- }
- break;
- default:
- *bufptr++ =3D *cmdptr++;
- break;
- }
- }
- =20
- *bufptr++ =3D '\0';
- =20
- VG_(message)(Vg_UserMsg, "starting debugger with cmd: %s", buf)=
;
- res =3D VG_(system)(buf);
- if (res =3D=3D 0) { =20
- VG_(message)(Vg_UserMsg, "");
- VG_(message)(Vg_UserMsg,=20
- "Debugger has detached. Valgrind regains contr=
ol. We continue.");
- } else {
- VG_(message)(Vg_UserMsg, "Apparently failed!");
- VG_(message)(Vg_UserMsg, "");
- }
- }
-
- VG_(kill)(pid, VKI_SIGKILL);
- VG_(waitpid)(pid, &status, 0);
- }
-}
-
-
-/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
/*=3D=3D=3D Check we were launched by stage 1 =
=3D=3D=3D*/
/*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D*/
=20
Modified: trunk/coregrind/m_signals.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/m_signals.c 2005-06-19 18:58:03 UTC (rev 3956)
+++ trunk/coregrind/m_signals.c 2005-06-19 19:25:44 UTC (rev 3957)
@@ -82,6 +82,7 @@
#include "pub_core_basics.h"
#include "pub_core_threadstate.h"
#include "pub_core_aspacemgr.h"
+#include "pub_core_debugger.h"
#include "pub_core_errormgr.h"
#include "pub_core_libcbase.h"
#include "pub_core_libcassert.h"
@@ -90,7 +91,6 @@
#include "pub_core_libcproc.h"
#include "pub_core_libcsignal.h"
#include "pub_core_machine.h"
-#include "pub_core_main.h"
#include "pub_core_mallocfree.h"
#include "pub_core_options.h"
#include "pub_core_scheduler.h"
Added: trunk/coregrind/pub_core_debugger.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/pub_core_debugger.h 2005-06-19 18:58:03 UTC (rev 3956=
)
+++ trunk/coregrind/pub_core_debugger.h 2005-06-19 19:25:44 UTC (rev 3957=
)
@@ -0,0 +1,45 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Attaching a debugger. pub_core_debugger.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2005 Julian Seward
+ js...@ac...
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307, USA.
+
+ The GNU General Public License is contained in the file COPYING.
+*/
+
+#ifndef __PUB_CORE_DEBUGGER_H
+#define __PUB_CORE_DEBUGGER_H
+
+//--------------------------------------------------------------------
+// PURPOSE: This simple module just deals with attaching a debugger to t=
he
+// running program.
+//--------------------------------------------------------------------
+
+extern void VG_(start_debugger) ( ThreadId tid );
+
+#endif // __PUB_CORE_DEBUGGER_H
+
+/*--------------------------------------------------------------------*/
+/*--- end ---*/
+/*--------------------------------------------------------------------*/
Modified: trunk/coregrind/pub_core_libcproc.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/pub_core_libcproc.h 2005-06-19 18:58:03 UTC (rev 3956=
)
+++ trunk/coregrind/pub_core_libcproc.h 2005-06-19 19:25:44 UTC (rev 3957=
)
@@ -58,6 +58,9 @@
client environment. */
#define VALGRINDCLO "_VALGRIND_CLO"
=20
+// Client's executable file descriptor.
+extern Int VG_(clexecfd);
+
// Client's original rlimit data and rlimit stack
extern struct vki_rlimit VG_(client_rlimit_data);
extern struct vki_rlimit VG_(client_rlimit_stack);
Modified: trunk/coregrind/pub_core_main.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/pub_core_main.h 2005-06-19 18:58:03 UTC (rev 3956)
+++ trunk/coregrind/pub_core_main.h 2005-06-19 19:25:44 UTC (rev 3957)
@@ -38,16 +38,10 @@
// things.
//--------------------------------------------------------------------
=20
-/* client executable file descriptor */
-extern Int VG_(clexecfd);
-
// Help set up the child used when doing execve() with --trace-children=3D=
yes
Char* VG_(build_child_VALGRINDCLO) ( Char* exename );
Char* VG_(build_child_exename) ( void );
=20
-/* Something of a function looking for a home ... start up debugger. */
-extern void VG_(start_debugger) ( ThreadId tid );
-
// Do everything which needs doing before the process finally ends,
// like printing reports, etc
extern void VG_(shutdown_actions_NORETURN) (
|