/*******************************************************************************
 * Copyright (c) 2007, 2015 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 * You may elect to redistribute this code under either of these licenses.
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/

/*
 * This module implements Breakpoints service.
 * The service maintains a list of breakpoints.
 * Each breakpoint consists of one or more conditions that determine
 * when a program's execution should be interrupted.
 */

#ifndef D_breakpoints
#define D_breakpoints

#include <tcf/framework/context.h>
#include <tcf/framework/protocol.h>

typedef struct BreakpointInfo BreakpointInfo;
typedef struct BreakpointAttribute BreakpointAttribute;

struct BreakpointAttribute {
    BreakpointAttribute * next;
    char * name;        /* Attribute name */
    char * value;       /* Attribute value as JSON string */
};

#if SERVICE_Breakpoints

/*
 * Breakpoint attribute names.
 * Clients may define additional attributes.
 */
#define BREAKPOINT_ID               "ID"
#define BREAKPOINT_ENABLED          "Enabled"
#define BREAKPOINT_TYPE             "BreakpointType"
#define BREAKPOINT_CONTEXTNAMES     "ContextNames"
#define BREAKPOINT_CONTEXTIDS       "ContextIds"
#define BREAKPOINT_EXECUTABLEPATHS  "ExecPaths"
#define BREAKPOINT_LOCATION         "Location"
#define BREAKPOINT_SIZE             "Size"
#define BREAKPOINT_ACCESSMODE       "AccessMode"
#define BREAKPOINT_FILE             "File"
#define BREAKPOINT_LINE             "Line"
#define BREAKPOINT_COLUMN           "Column"
#define BREAKPOINT_PATTERN          "MaskValue"
#define BREAKPOINT_MASK             "Mask"
#define BREAKPOINT_STOP_GROUP       "StopGroup"
#define BREAKPOINT_IGNORECOUNT      "IgnoreCount"
#define BREAKPOINT_TIME             "Time"
#define BREAKPOINT_SCALE            "TimeScale"
#define BREAKPOINT_UNITS            "TimeUnits"
#define BREAKPOINT_CONDITION        "Condition"
#define BREAKPOINT_TEMPORARY        "Temporary"
#define BREAKPOINT_EVENT_TYPE       "EventType"
#define BREAKPOINT_EVENT_ARGS       "EventArgs"
#define BREAKPOINT_CLIENT_DATA      "ClientData"
#define BREAKPOINT_CONTEXT_QUERY    "ContextQuery"
#define BREAKPOINT_SERVICE          "Service"
#define BREAKPOINT_SKIP_PROLOGUE    "SkipPrologue"
#define BREAKPOINT_LINE_OFFSET      "LineOffset"


/* Breakpoints event listener */
typedef struct BreakpointsEventListener {
    void (*breakpoint_created)(BreakpointInfo *, void *);
    void (*breakpoint_changed)(BreakpointInfo *, void *);
    void (*breakpoint_deleted)(BreakpointInfo *, void *);
    void (*breakpoint_status_changed)(BreakpointInfo *, void *);
} BreakpointsEventListener;

/*
 * Add a listener for Breakpoints service events.
 */
extern void add_breakpoint_event_listener(BreakpointsEventListener * listener, void * args);

/*
 * Remove a listener of Breakpoints service events.
 */
extern void rem_breakpoint_event_listener(BreakpointsEventListener * listener);

/*
 * Iterate all breakpoints known to the Breakpoints service,
 * including breakpoints that are created by other (remote) clients.
 */
typedef void IterateBreakpointsCallBack(BreakpointInfo *, void *);
extern void iterate_breakpoints(IterateBreakpointsCallBack * callback, void * args);

/*
 * Get breakpoint attributes.
 */
extern BreakpointAttribute * get_breakpoint_attributes(BreakpointInfo * bp);

/*
 * Create new breakpoint with given attributes.
 * Attributes must include, at least, BREAKPOINT_ID.
 * If a breakpoint with such ID already exists, it will be modified to match
 * new attributes instead of creating a new one.
 * Caller should allocate attributes using myalloc.h functions.
 * Breakpoints service will free attributes memory using loc_free().
 */
extern BreakpointInfo * create_breakpoint(BreakpointAttribute * attrs);

/*
 * Change breakpoint attributes to given attributes.
 * Caller should allocate attributes using myalloc.h functions.
 * Breakpoints service will free attributes memory using loc_free().
 * The function compares existing attributes with new ones,
 * and calls listeners only if attributes are different.
 */
extern void change_breakpoint_attributes(BreakpointInfo * bp, BreakpointAttribute * attrs);

/*
 * Return breakpoint status information as a JSON string.
 * Clients should dispose the string using loc_free().
 */
extern char * get_breakpoint_status(BreakpointInfo * bp);

/*
 * Delete a breakpoint.
 * If other (remote) client also created a breakpoint with same ID,
 * the breakpoint will be deleted when both clients have requested it to be deleted.
 */
extern void delete_breakpoint(BreakpointInfo * bp);

/*
 * Iterate all breakpoints that are linked to context breakpoint 'cb' in the breakpoint address space
 * associated with executable context 'ctx'. Breakpoint address space is the context returned by
 * context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT).
 * Single 'cb' can be linked to multiple breakpoints if those breakpoint locations are evaluated
 * to same address in same address space. Single breakpoint can be linked to multiple CBs if the
 * breakpoint scope spawns multiple address spaces.
 */
typedef void IterateCBLinksCallBack(BreakpointInfo *, void *);
extern void iterate_context_breakpoint_links(Context * ctx, ContextBreakpoint * cb, IterateCBLinksCallBack * callback, void * args);

/*
 * The function is called by Run Control service every time a context is stopped by a breakpoint.
 * The function evaluates breakpoint condition and calls suspend_debug_context() if the condition is true.
 */
extern void evaluate_breakpoint(Context * ctx);

/*
 * When a context is stopped by breakpoint, it is necessary to disable
 * the breakpoint temporarily before the context can be resumed.
 * This function function removes break instruction, then does single step
 * over breakpoint location, then restores break instruction.
 * Return 0 if it is OK to resume context from current state,
 * return 1 if context needs to step over a breakpoint.
 */
extern int skip_breakpoint(Context * ctx, int single_step);

/*
 * Return 1 if the context is stepping over a software breakpoint,
 * Return 0 otherwise.
 */
extern int is_skipping_breakpoint(Context * ctx);

/* Return 1 if break instruction is planted at given address in the context memory */
extern int is_breakpoint_address(Context * ctx, ContextAddress address);

/*
 * Notify fork(): clone planted software breakpoints when a process forks.
 * The process clone inherits memory contents of the original process,
 * so software breakpoints are also cloned and the Breakpoints service needs to update the list of planted breakpoints.
 */
extern void clone_breakpoints_on_process_fork(Context * parent, Context * child);

/*
 * Notify exec(): invalidate planted software breakpoints when a process calls exec().
 * exec() replaces memory contents of the original process with something else,
 * so software breakpoints are destroyed and the Breakpoints service needs to remove them from the list of planted breakpoints.
 */
extern void invalidate_breakpoints_on_process_exec(Context * prs);

/*
 * Unplant all breakpoints in a process (e.g. before detaching).
 * Return -1 and set errno if cannot remove some of the breakpoints.
 * Return 0 on success.
 */
extern int unplant_breakpoints(Context * ctx);

/*
 * Check if memory data buffer contains planted break instructions and remove them.
 * Return -1 and set errno if the check cannot be done.
 */
extern int check_breakpoints_on_memory_read(Context * ctx, ContextAddress address, void * buf, size_t size);

/*
 * Check if data is about to be written over planted break instructions and adjust the data and breakpoint backing storage
 * Return -1 and set errno if the check cannot be done.
 * Return 0 on success.
 */
extern int check_breakpoints_on_memory_write(Context * ctx, ContextAddress address, void * buf, size_t size);

/* Evenpoint callback. It is called when context is suspended by eventpoint, right before "context_stopped" event */
typedef void EventPointCallBack(Context *, void *);

/* Create, plant and return eventpoint. Eventpoints are breakpoints that are created by agent to control execution of debugee.
 * Eventpoint are not exposed through "Breakpoints" TCF service, they are handled by the agent itself.
 * 'attrs' are disposed by the Breakpoints service using loc_free().
 */
extern BreakpointInfo * create_eventpoint(const char * location, Context * ctx, EventPointCallBack * callback, void * callback_args);
extern BreakpointInfo * create_eventpoint_ext(BreakpointAttribute * attrs, Context * ctx, EventPointCallBack * callback, void * callback_args);

/* Unplant and destroy eventpoint */
extern void destroy_eventpoint(BreakpointInfo * eventpoint);

extern void ini_breakpoints_service(Protocol *, TCFBroadcastGroup *);

#else /* SERVICE_Breakpoints */

#define skip_breakpoint(ctx, single_step) 0
#define is_breakpoint_address(ctx, address) 0
#define clone_breakpoints_on_process_fork(parent, child) 0
#define unplant_breakpoints(ctx) 0
#define check_breakpoints_on_memory_read(ctx, address, buf, size) 0
#define check_breakpoints_on_memory_write(ctx, address, buf, size) 0
#define create_eventpoint(location, ctx, callback, callback_args) 0

#endif /* SERVICE_Breakpoints */

#endif /* D_breakpoints */
