1*5796c8dcSSimon Schubert /* Multi-process/thread control defs for GDB, the GNU debugger. 2*5796c8dcSSimon Schubert Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1997, 1998, 1999, 3*5796c8dcSSimon Schubert 2000, 2007, 2008, 2009 Free Software Foundation, Inc. 4*5796c8dcSSimon Schubert Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA. 5*5796c8dcSSimon Schubert 6*5796c8dcSSimon Schubert 7*5796c8dcSSimon Schubert This file is part of GDB. 8*5796c8dcSSimon Schubert 9*5796c8dcSSimon Schubert This program is free software; you can redistribute it and/or modify 10*5796c8dcSSimon Schubert it under the terms of the GNU General Public License as published by 11*5796c8dcSSimon Schubert the Free Software Foundation; either version 3 of the License, or 12*5796c8dcSSimon Schubert (at your option) any later version. 13*5796c8dcSSimon Schubert 14*5796c8dcSSimon Schubert This program is distributed in the hope that it will be useful, 15*5796c8dcSSimon Schubert but WITHOUT ANY WARRANTY; without even the implied warranty of 16*5796c8dcSSimon Schubert MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17*5796c8dcSSimon Schubert GNU General Public License for more details. 18*5796c8dcSSimon Schubert 19*5796c8dcSSimon Schubert You should have received a copy of the GNU General Public License 20*5796c8dcSSimon Schubert along with this program. If not, see <http://www.gnu.org/licenses/>. */ 21*5796c8dcSSimon Schubert 22*5796c8dcSSimon Schubert #ifndef GDBTHREAD_H 23*5796c8dcSSimon Schubert #define GDBTHREAD_H 24*5796c8dcSSimon Schubert 25*5796c8dcSSimon Schubert struct symtab; 26*5796c8dcSSimon Schubert 27*5796c8dcSSimon Schubert #include "breakpoint.h" 28*5796c8dcSSimon Schubert #include "frame.h" 29*5796c8dcSSimon Schubert #include "ui-out.h" 30*5796c8dcSSimon Schubert #include "inferior.h" 31*5796c8dcSSimon Schubert 32*5796c8dcSSimon Schubert struct thread_info 33*5796c8dcSSimon Schubert { 34*5796c8dcSSimon Schubert struct thread_info *next; 35*5796c8dcSSimon Schubert ptid_t ptid; /* "Actual process id"; 36*5796c8dcSSimon Schubert In fact, this may be overloaded with 37*5796c8dcSSimon Schubert kernel thread id, etc. */ 38*5796c8dcSSimon Schubert int num; /* Convenient handle (GDB thread id) */ 39*5796c8dcSSimon Schubert 40*5796c8dcSSimon Schubert /* Non-zero means the thread is executing. Note: this is different 41*5796c8dcSSimon Schubert from saying that there is an active target and we are stopped at 42*5796c8dcSSimon Schubert a breakpoint, for instance. This is a real indicator whether the 43*5796c8dcSSimon Schubert thread is off and running. */ 44*5796c8dcSSimon Schubert /* This field is internal to thread.c. Never access it directly, 45*5796c8dcSSimon Schubert use is_executing instead. */ 46*5796c8dcSSimon Schubert int executing_; 47*5796c8dcSSimon Schubert 48*5796c8dcSSimon Schubert /* Frontend view of the thread state. Note that the RUNNING/STOPPED 49*5796c8dcSSimon Schubert states are different from EXECUTING. When the thread is stopped 50*5796c8dcSSimon Schubert internally while handling an internal event, like a software 51*5796c8dcSSimon Schubert single-step breakpoint, EXECUTING will be false, but running will 52*5796c8dcSSimon Schubert still be true. As a possible future extension, this could turn 53*5796c8dcSSimon Schubert into enum { stopped, exited, stepping, finishing, until(ling), 54*5796c8dcSSimon Schubert running ... } */ 55*5796c8dcSSimon Schubert /* This field is internal to thread.c. Never access it directly, 56*5796c8dcSSimon Schubert use is_running instead. */ 57*5796c8dcSSimon Schubert int state_; 58*5796c8dcSSimon Schubert 59*5796c8dcSSimon Schubert /* If this is > 0, then it means there's code out there that relies 60*5796c8dcSSimon Schubert on this thread being listed. Don't delete it from the lists even 61*5796c8dcSSimon Schubert if we detect it exiting. */ 62*5796c8dcSSimon Schubert int refcount; 63*5796c8dcSSimon Schubert 64*5796c8dcSSimon Schubert /* User/external stepping state. */ 65*5796c8dcSSimon Schubert 66*5796c8dcSSimon Schubert /* Step-resume or longjmp-resume breakpoint. */ 67*5796c8dcSSimon Schubert struct breakpoint *step_resume_breakpoint; 68*5796c8dcSSimon Schubert 69*5796c8dcSSimon Schubert /* Range to single step within. 70*5796c8dcSSimon Schubert 71*5796c8dcSSimon Schubert If this is nonzero, respond to a single-step signal by continuing 72*5796c8dcSSimon Schubert to step if the pc is in this range. 73*5796c8dcSSimon Schubert 74*5796c8dcSSimon Schubert If step_range_start and step_range_end are both 1, it means to 75*5796c8dcSSimon Schubert step for a single instruction (FIXME: it might clean up 76*5796c8dcSSimon Schubert wait_for_inferior in a minor way if this were changed to the 77*5796c8dcSSimon Schubert address of the instruction and that address plus one. But maybe 78*5796c8dcSSimon Schubert not.). */ 79*5796c8dcSSimon Schubert CORE_ADDR step_range_start; /* Inclusive */ 80*5796c8dcSSimon Schubert CORE_ADDR step_range_end; /* Exclusive */ 81*5796c8dcSSimon Schubert 82*5796c8dcSSimon Schubert /* Stack frame address as of when stepping command was issued. 83*5796c8dcSSimon Schubert This is how we know when we step into a subroutine call, and how 84*5796c8dcSSimon Schubert to set the frame for the breakpoint used to step out. */ 85*5796c8dcSSimon Schubert struct frame_id step_frame_id; 86*5796c8dcSSimon Schubert 87*5796c8dcSSimon Schubert /* Similarly, the frame ID of the underlying stack frame (skipping 88*5796c8dcSSimon Schubert any inlined frames). */ 89*5796c8dcSSimon Schubert struct frame_id step_stack_frame_id; 90*5796c8dcSSimon Schubert 91*5796c8dcSSimon Schubert int current_line; 92*5796c8dcSSimon Schubert struct symtab *current_symtab; 93*5796c8dcSSimon Schubert 94*5796c8dcSSimon Schubert /* Internal stepping state. */ 95*5796c8dcSSimon Schubert 96*5796c8dcSSimon Schubert /* Record the pc of the thread the last time it stopped. This is 97*5796c8dcSSimon Schubert maintained by proceed and keep_going, and used in 98*5796c8dcSSimon Schubert adjust_pc_after_break to distinguish a hardware single-step 99*5796c8dcSSimon Schubert SIGTRAP from a breakpoint SIGTRAP. */ 100*5796c8dcSSimon Schubert CORE_ADDR prev_pc; 101*5796c8dcSSimon Schubert 102*5796c8dcSSimon Schubert /* Nonzero if we are presently stepping over a breakpoint. 103*5796c8dcSSimon Schubert 104*5796c8dcSSimon Schubert If we hit a breakpoint or watchpoint, and then continue, we need 105*5796c8dcSSimon Schubert to single step the current thread with breakpoints disabled, to 106*5796c8dcSSimon Schubert avoid hitting the same breakpoint or watchpoint again. And we 107*5796c8dcSSimon Schubert should step just a single thread and keep other threads stopped, 108*5796c8dcSSimon Schubert so that other threads don't miss breakpoints while they are 109*5796c8dcSSimon Schubert removed. 110*5796c8dcSSimon Schubert 111*5796c8dcSSimon Schubert So, this variable simultaneously means that we need to single 112*5796c8dcSSimon Schubert step the current thread, keep other threads stopped, and that 113*5796c8dcSSimon Schubert breakpoints should be removed while we step. 114*5796c8dcSSimon Schubert 115*5796c8dcSSimon Schubert This variable is set either: 116*5796c8dcSSimon Schubert - in proceed, when we resume inferior on user's explicit request 117*5796c8dcSSimon Schubert - in keep_going, if handle_inferior_event decides we need to 118*5796c8dcSSimon Schubert step over breakpoint. 119*5796c8dcSSimon Schubert 120*5796c8dcSSimon Schubert The variable is cleared in normal_stop. The proceed calls 121*5796c8dcSSimon Schubert wait_for_inferior, which calls handle_inferior_event in a loop, 122*5796c8dcSSimon Schubert and until wait_for_inferior exits, this variable is changed only 123*5796c8dcSSimon Schubert by keep_going. */ 124*5796c8dcSSimon Schubert int trap_expected; 125*5796c8dcSSimon Schubert 126*5796c8dcSSimon Schubert /* Should we step over breakpoint next time keep_going is called? */ 127*5796c8dcSSimon Schubert int stepping_over_breakpoint; 128*5796c8dcSSimon Schubert 129*5796c8dcSSimon Schubert /* Set to TRUE if we should finish single-stepping over a breakpoint 130*5796c8dcSSimon Schubert after hitting the current step-resume breakpoint. */ 131*5796c8dcSSimon Schubert int step_after_step_resume_breakpoint; 132*5796c8dcSSimon Schubert 133*5796c8dcSSimon Schubert /* This is set TRUE when a catchpoint of a shared library event 134*5796c8dcSSimon Schubert triggers. Since we don't wish to leave the inferior in the 135*5796c8dcSSimon Schubert solib hook when we report the event, we step the inferior 136*5796c8dcSSimon Schubert back to user code before stopping and reporting the event. */ 137*5796c8dcSSimon Schubert int stepping_through_solib_after_catch; 138*5796c8dcSSimon Schubert 139*5796c8dcSSimon Schubert /* When stepping_through_solib_after_catch is TRUE, this is a 140*5796c8dcSSimon Schubert list of the catchpoints that should be reported as triggering 141*5796c8dcSSimon Schubert when we finally do stop stepping. */ 142*5796c8dcSSimon Schubert bpstat stepping_through_solib_catchpoints; 143*5796c8dcSSimon Schubert 144*5796c8dcSSimon Schubert /* Per-thread command support. */ 145*5796c8dcSSimon Schubert 146*5796c8dcSSimon Schubert /* Pointer to what is left to do for an execution command after the 147*5796c8dcSSimon Schubert target stops. Used only in asynchronous mode, by targets that 148*5796c8dcSSimon Schubert support async execution. Several execution commands use it. */ 149*5796c8dcSSimon Schubert struct continuation *continuations; 150*5796c8dcSSimon Schubert 151*5796c8dcSSimon Schubert /* Similar to the above, but used when a single execution command 152*5796c8dcSSimon Schubert requires several resume/stop iterations. Used by the step 153*5796c8dcSSimon Schubert command. */ 154*5796c8dcSSimon Schubert struct continuation *intermediate_continuations; 155*5796c8dcSSimon Schubert 156*5796c8dcSSimon Schubert /* Nonzero if the thread is being proceeded for a "finish" command 157*5796c8dcSSimon Schubert or a similar situation when stop_registers should be saved. */ 158*5796c8dcSSimon Schubert int proceed_to_finish; 159*5796c8dcSSimon Schubert 160*5796c8dcSSimon Schubert /* Nonzero if the thread is being proceeded for an inferior function 161*5796c8dcSSimon Schubert call. */ 162*5796c8dcSSimon Schubert int in_infcall; 163*5796c8dcSSimon Schubert 164*5796c8dcSSimon Schubert enum step_over_calls_kind step_over_calls; 165*5796c8dcSSimon Schubert 166*5796c8dcSSimon Schubert /* Nonzero if stopped due to a step command. */ 167*5796c8dcSSimon Schubert int stop_step; 168*5796c8dcSSimon Schubert 169*5796c8dcSSimon Schubert /* If stepping, nonzero means step count is > 1 so don't print frame 170*5796c8dcSSimon Schubert next time inferior stops if it stops due to stepping. */ 171*5796c8dcSSimon Schubert int step_multi; 172*5796c8dcSSimon Schubert 173*5796c8dcSSimon Schubert /* This is used to remember when a fork or vfork event was caught by 174*5796c8dcSSimon Schubert a catchpoint, and thus the event is to be followed at the next 175*5796c8dcSSimon Schubert resume of the thread, and not immediately. */ 176*5796c8dcSSimon Schubert struct target_waitstatus pending_follow; 177*5796c8dcSSimon Schubert 178*5796c8dcSSimon Schubert /* Last signal that the inferior received (why it stopped). */ 179*5796c8dcSSimon Schubert enum target_signal stop_signal; 180*5796c8dcSSimon Schubert 181*5796c8dcSSimon Schubert /* Chain containing status of breakpoint(s) the thread stopped 182*5796c8dcSSimon Schubert at. */ 183*5796c8dcSSimon Schubert bpstat stop_bpstat; 184*5796c8dcSSimon Schubert 185*5796c8dcSSimon Schubert /* True if this thread has been explicitly requested to stop. */ 186*5796c8dcSSimon Schubert int stop_requested; 187*5796c8dcSSimon Schubert 188*5796c8dcSSimon Schubert /* Private data used by the target vector implementation. */ 189*5796c8dcSSimon Schubert struct private_thread_info *private; 190*5796c8dcSSimon Schubert }; 191*5796c8dcSSimon Schubert 192*5796c8dcSSimon Schubert /* Create an empty thread list, or empty the existing one. */ 193*5796c8dcSSimon Schubert extern void init_thread_list (void); 194*5796c8dcSSimon Schubert 195*5796c8dcSSimon Schubert /* Add a thread to the thread list, print a message 196*5796c8dcSSimon Schubert that a new thread is found, and return the pointer to 197*5796c8dcSSimon Schubert the new thread. Caller my use this pointer to 198*5796c8dcSSimon Schubert initialize the private thread data. */ 199*5796c8dcSSimon Schubert extern struct thread_info *add_thread (ptid_t ptid); 200*5796c8dcSSimon Schubert 201*5796c8dcSSimon Schubert /* Same as add_thread, but does not print a message 202*5796c8dcSSimon Schubert about new thread. */ 203*5796c8dcSSimon Schubert extern struct thread_info *add_thread_silent (ptid_t ptid); 204*5796c8dcSSimon Schubert 205*5796c8dcSSimon Schubert /* Same as add_thread, and sets the private info. */ 206*5796c8dcSSimon Schubert extern struct thread_info *add_thread_with_info (ptid_t ptid, 207*5796c8dcSSimon Schubert struct private_thread_info *); 208*5796c8dcSSimon Schubert 209*5796c8dcSSimon Schubert /* Delete an existing thread list entry. */ 210*5796c8dcSSimon Schubert extern void delete_thread (ptid_t); 211*5796c8dcSSimon Schubert 212*5796c8dcSSimon Schubert /* Delete an existing thread list entry, and be quiet about it. Used 213*5796c8dcSSimon Schubert after the process this thread having belonged to having already 214*5796c8dcSSimon Schubert exited, for example. */ 215*5796c8dcSSimon Schubert extern void delete_thread_silent (ptid_t); 216*5796c8dcSSimon Schubert 217*5796c8dcSSimon Schubert /* Delete a step_resume_breakpoint from the thread database. */ 218*5796c8dcSSimon Schubert extern void delete_step_resume_breakpoint (struct thread_info *); 219*5796c8dcSSimon Schubert 220*5796c8dcSSimon Schubert /* Translate the integer thread id (GDB's homegrown id, not the system's) 221*5796c8dcSSimon Schubert into a "pid" (which may be overloaded with extra thread information). */ 222*5796c8dcSSimon Schubert extern ptid_t thread_id_to_pid (int); 223*5796c8dcSSimon Schubert 224*5796c8dcSSimon Schubert /* Translate a 'pid' (which may be overloaded with extra thread information) 225*5796c8dcSSimon Schubert into the integer thread id (GDB's homegrown id, not the system's). */ 226*5796c8dcSSimon Schubert extern int pid_to_thread_id (ptid_t ptid); 227*5796c8dcSSimon Schubert 228*5796c8dcSSimon Schubert /* Boolean test for an already-known pid (which may be overloaded with 229*5796c8dcSSimon Schubert extra thread information). */ 230*5796c8dcSSimon Schubert extern int in_thread_list (ptid_t ptid); 231*5796c8dcSSimon Schubert 232*5796c8dcSSimon Schubert /* Boolean test for an already-known thread id (GDB's homegrown id, 233*5796c8dcSSimon Schubert not the system's). */ 234*5796c8dcSSimon Schubert extern int valid_thread_id (int thread); 235*5796c8dcSSimon Schubert 236*5796c8dcSSimon Schubert /* Search function to lookup a thread by 'pid'. */ 237*5796c8dcSSimon Schubert extern struct thread_info *find_thread_ptid (ptid_t ptid); 238*5796c8dcSSimon Schubert 239*5796c8dcSSimon Schubert /* Find thread by GDB user-visible thread number. */ 240*5796c8dcSSimon Schubert struct thread_info *find_thread_id (int num); 241*5796c8dcSSimon Schubert 242*5796c8dcSSimon Schubert /* Finds the first thread of the inferior given by PID. If PID is -1, 243*5796c8dcSSimon Schubert returns the first thread in the list. */ 244*5796c8dcSSimon Schubert struct thread_info *first_thread_of_process (int pid); 245*5796c8dcSSimon Schubert 246*5796c8dcSSimon Schubert /* Returns any thread of process PID. */ 247*5796c8dcSSimon Schubert extern struct thread_info *any_thread_of_process (int pid); 248*5796c8dcSSimon Schubert 249*5796c8dcSSimon Schubert /* Change the ptid of thread OLD_PTID to NEW_PTID. */ 250*5796c8dcSSimon Schubert void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid); 251*5796c8dcSSimon Schubert 252*5796c8dcSSimon Schubert /* Iterator function to call a user-provided callback function 253*5796c8dcSSimon Schubert once for each known thread. */ 254*5796c8dcSSimon Schubert typedef int (*thread_callback_func) (struct thread_info *, void *); 255*5796c8dcSSimon Schubert extern struct thread_info *iterate_over_threads (thread_callback_func, void *); 256*5796c8dcSSimon Schubert 257*5796c8dcSSimon Schubert extern int thread_count (void); 258*5796c8dcSSimon Schubert 259*5796c8dcSSimon Schubert /* Switch from one thread to another. */ 260*5796c8dcSSimon Schubert extern void switch_to_thread (ptid_t ptid); 261*5796c8dcSSimon Schubert 262*5796c8dcSSimon Schubert /* Marks thread PTID is running, or stopped. 263*5796c8dcSSimon Schubert If PIDGET (PTID) is -1, marks all threads. */ 264*5796c8dcSSimon Schubert extern void set_running (ptid_t ptid, int running); 265*5796c8dcSSimon Schubert 266*5796c8dcSSimon Schubert /* Marks or clears thread(s) PTID as having been requested to stop. 267*5796c8dcSSimon Schubert If PTID is MINUS_ONE_PTID, applies to all threads. If 268*5796c8dcSSimon Schubert ptid_is_pid(PTID) is true, applies to all threads of the process 269*5796c8dcSSimon Schubert pointed at by PTID. If STOP, then the THREAD_STOP_REQUESTED 270*5796c8dcSSimon Schubert observer is called with PTID as argument. */ 271*5796c8dcSSimon Schubert extern void set_stop_requested (ptid_t ptid, int stop); 272*5796c8dcSSimon Schubert 273*5796c8dcSSimon Schubert /* NOTE: Since the thread state is not a boolean, most times, you do 274*5796c8dcSSimon Schubert not want to check it with negation. If you really want to check if 275*5796c8dcSSimon Schubert the thread is stopped, 276*5796c8dcSSimon Schubert 277*5796c8dcSSimon Schubert use (good): 278*5796c8dcSSimon Schubert 279*5796c8dcSSimon Schubert if (is_stopped (ptid)) 280*5796c8dcSSimon Schubert 281*5796c8dcSSimon Schubert instead of (bad): 282*5796c8dcSSimon Schubert 283*5796c8dcSSimon Schubert if (!is_running (ptid)) 284*5796c8dcSSimon Schubert 285*5796c8dcSSimon Schubert The latter also returns true on exited threads, most likelly not 286*5796c8dcSSimon Schubert what you want. */ 287*5796c8dcSSimon Schubert 288*5796c8dcSSimon Schubert /* Reports if in the frontend's perpective, thread PTID is running. */ 289*5796c8dcSSimon Schubert extern int is_running (ptid_t ptid); 290*5796c8dcSSimon Schubert 291*5796c8dcSSimon Schubert /* Is this thread listed, but known to have exited? We keep it listed 292*5796c8dcSSimon Schubert (but not visible) until it's safe to delete. */ 293*5796c8dcSSimon Schubert extern int is_exited (ptid_t ptid); 294*5796c8dcSSimon Schubert 295*5796c8dcSSimon Schubert /* In the frontend's perpective, is this thread stopped? */ 296*5796c8dcSSimon Schubert extern int is_stopped (ptid_t ptid); 297*5796c8dcSSimon Schubert 298*5796c8dcSSimon Schubert /* In the frontend's perpective is there any thread running? */ 299*5796c8dcSSimon Schubert extern int any_running (void); 300*5796c8dcSSimon Schubert 301*5796c8dcSSimon Schubert /* Marks thread PTID as executing, or not. If PIDGET (PTID) is -1, 302*5796c8dcSSimon Schubert marks all threads. 303*5796c8dcSSimon Schubert 304*5796c8dcSSimon Schubert Note that this is different from the running state. See the 305*5796c8dcSSimon Schubert description of state_ and executing_ fields of struct 306*5796c8dcSSimon Schubert thread_info. */ 307*5796c8dcSSimon Schubert extern void set_executing (ptid_t ptid, int executing); 308*5796c8dcSSimon Schubert 309*5796c8dcSSimon Schubert /* Reports if thread PTID is executing. */ 310*5796c8dcSSimon Schubert extern int is_executing (ptid_t ptid); 311*5796c8dcSSimon Schubert 312*5796c8dcSSimon Schubert /* Merge the executing property of thread PTID over to its thread 313*5796c8dcSSimon Schubert state property (frontend running/stopped view). 314*5796c8dcSSimon Schubert 315*5796c8dcSSimon Schubert "not executing" -> "stopped" 316*5796c8dcSSimon Schubert "executing" -> "running" 317*5796c8dcSSimon Schubert "exited" -> "exited" 318*5796c8dcSSimon Schubert 319*5796c8dcSSimon Schubert If PIDGET (PTID) is -1, go over all threads. 320*5796c8dcSSimon Schubert 321*5796c8dcSSimon Schubert Notifications are only emitted if the thread state did change. */ 322*5796c8dcSSimon Schubert extern void finish_thread_state (ptid_t ptid); 323*5796c8dcSSimon Schubert 324*5796c8dcSSimon Schubert /* Same as FINISH_THREAD_STATE, but with an interface suitable to be 325*5796c8dcSSimon Schubert registered as a cleanup. PTID_P points to the ptid_t that is 326*5796c8dcSSimon Schubert passed to FINISH_THREAD_STATE. */ 327*5796c8dcSSimon Schubert extern void finish_thread_state_cleanup (void *ptid_p); 328*5796c8dcSSimon Schubert 329*5796c8dcSSimon Schubert /* Commands with a prefix of `thread'. */ 330*5796c8dcSSimon Schubert extern struct cmd_list_element *thread_cmd_list; 331*5796c8dcSSimon Schubert 332*5796c8dcSSimon Schubert /* Print notices on thread events (attach, detach, etc.), set with 333*5796c8dcSSimon Schubert `set print thread-events'. */ 334*5796c8dcSSimon Schubert extern int print_thread_events; 335*5796c8dcSSimon Schubert 336*5796c8dcSSimon Schubert extern void print_thread_info (struct ui_out *uiout, int thread, 337*5796c8dcSSimon Schubert int pid); 338*5796c8dcSSimon Schubert 339*5796c8dcSSimon Schubert extern struct cleanup *make_cleanup_restore_current_thread (void); 340*5796c8dcSSimon Schubert 341*5796c8dcSSimon Schubert /* Returns a pointer into the thread_info corresponding to 342*5796c8dcSSimon Schubert INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */ 343*5796c8dcSSimon Schubert extern struct thread_info* inferior_thread (void); 344*5796c8dcSSimon Schubert 345*5796c8dcSSimon Schubert #endif /* GDBTHREAD_H */ 346