]> granicus.if.org Git - php/commitdiff
extended conditional breakpoints
authorkrakjoe <joe.watkins@live.co.uk>
Tue, 3 Dec 2013 00:15:33 +0000 (00:15 +0000)
committerkrakjoe <joe.watkins@live.co.uk>
Tue, 3 Dec 2013 00:15:33 +0000 (00:15 +0000)
Changelog.md
phpdbg_bp.c
phpdbg_bp.h
phpdbg_break.c
phpdbg_break.h
phpdbg_cmd.c
phpdbg_cmd.h
phpdbg_help.c

index aed299cab148b626b3cccf11a129978118427f2d..be151c819b68e4b02261c72d5dbbb3cd2c95f113 100644 (file)
@@ -6,6 +6,7 @@ Version 0.3.0 2013-00-00
 
 1. Added ability to disable an enable a single breakpoint
 2. Added ability to override SAPI name
+3. Added extended conditional breakpoint support "break at"
 
 Version 0.2.0 2013-11-31
 ------------------------
index caf3c8ac0a289f72b2c3096bf53caf9655e5e306..5ea78293f31fe665e356d8c3333f504aa3d52140 100644 (file)
@@ -33,7 +33,7 @@ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_symbol(zend_function* T
 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_method(zend_op_array* TSRMLS_DC);
 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t TSRMLS_DC);
 static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opcode(zend_uchar TSRMLS_DC);
-static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(TSRMLS_D); /* }}} */
+static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execute_data *execute_data TSRMLS_DC); /* }}} */
 
 /*
 * Note:
@@ -328,54 +328,122 @@ PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRML
        }
 } /* }}} */
 
-PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_len TSRMLS_DC) /* {{{ */
+static inline void phpdbg_create_conditional_break(phpdbg_breakcond_t *brake, const phpdbg_param_t *param, const char *expr, size_t expr_len, zend_ulong hash TSRMLS_DC) /* {{{ */
 {
-       zend_ulong hash = zend_inline_hash_func(expr, expr_len);
-
-       if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash)) {
-               phpdbg_breakcond_t new_break;
-               zend_uint cops = CG(compiler_options);
-               zval pv;
-
-               PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_COND);
-               new_break.hash = hash;
-
-               cops = CG(compiler_options);
+       phpdbg_breakcond_t new_break;
+       zend_uint cops = CG(compiler_options);
+       zval pv;
 
-               CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL;
+       PHPDBG_BREAK_INIT(new_break, PHPDBG_BREAK_COND);
+       new_break.hash = hash;
+       
+       if (param) {
+               new_break.paramed = 1;
+               phpdbg_copy_param(
+                       param, &new_break.param TSRMLS_CC);
+       } else {
+               new_break.paramed = 0;
+       }
+       
+       cops = CG(compiler_options);
 
-               new_break.code_len = Z_STRLEN(pv) = expr_len + sizeof("return ;") - 1;
-               new_break.code = Z_STRVAL(pv) = emalloc(Z_STRLEN(pv) + 1);
-               memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1);
-               memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, expr, expr_len);
-               Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';';
-               Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0';
-               Z_TYPE(pv) = IS_STRING;
+       CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL;
 
-               new_break.ops = zend_compile_string(
-                       &pv, "Conditional Breakpoint Code" TSRMLS_CC);
+       new_break.code = estrndup(expr, expr_len);
+       new_break.code_len = expr_len;
+       
+       Z_STRLEN(pv) = expr_len + sizeof("return ;") - 1;
+       Z_STRVAL(pv) = emalloc(Z_STRLEN(pv) + 1);
+       memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1);
+       memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, expr, expr_len);
+       Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';';
+       Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0';
+       Z_TYPE(pv) = IS_STRING;
+
+       new_break.ops = zend_compile_string(
+               &pv, "Conditional Breakpoint Code" TSRMLS_CC);
+
+       zval_dtor(&pv); 
+               
+       if (new_break.ops) {
+               zend_hash_index_update(
+                       &PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash, &new_break,
+                       sizeof(phpdbg_breakcond_t), (void**)&brake);
 
-               if (new_break.ops) {
-                       phpdbg_breakcond_t *brake;
+               phpdbg_notice("Conditional breakpoint #%d added %s/%p",
+                       brake->id, brake->code, brake->ops);
 
-                       zend_hash_index_update(
-                               &PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash, &new_break,
-                               sizeof(phpdbg_breakcond_t), (void**)&brake);
+               PHPDBG_G(flags) |= PHPDBG_HAS_COND_BP;
+               PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+       } else {
+                phpdbg_error(
+                       "Failed to compile code for expression %s", expr);
+                efree((char*)new_break.code);
+                PHPDBG_G(bp_count)--;
+       }
+       CG(compiler_options) = cops;
+} /* }}} */
 
-                       phpdbg_notice("Conditional breakpoint #%d added %s/%p",
-                               brake->id, brake->code, brake->ops);
+PHPDBG_API void phpdbg_set_breakpoint_expression(const char *expr, size_t expr_len TSRMLS_DC) /* {{{ */
+{
+       zend_ulong expr_hash = zend_inline_hash_func(expr, expr_len);
+       phpdbg_breakcond_t new_break;
+       
+       if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
+               phpdbg_create_conditional_break(
+                       &new_break, NULL, expr, expr_len, expr_hash TSRMLS_CC);
+       } else {
+               phpdbg_notice("Conditional break %s exists", expr);
+       }
+} /* }}} */
 
-                       PHPDBG_G(flags) |= PHPDBG_HAS_COND_BP;
-                       PHPDBG_BREAK_MAPPING(new_break.id, &PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC) /* {{{ */
+{
+       if (input->argc > 3 && phpdbg_argv_is(2, "if")) {
+               phpdbg_breakcond_t new_break;
+               phpdbg_param_t new_param;
+               
+               zend_ulong expr_hash = 0L;
+               size_t expr_len;
+               const char *join = strstr(input->string, "if");
+               const char *expr = (join) + sizeof("if");
+               
+               expr_len = strlen(expr);
+               expr = phpdbg_trim(expr, expr_len, &expr_len);
+               expr_hash = zend_inline_hash_func(expr, expr_len);
+               
+               {
+                       /* get a clean parameter from input string */
+                       size_t sparam_len = 0L;
+                       char *sparam = input->string;
+                       
+                       sparam[
+                               strstr(input->string, " ") - input->string] = 0;
+                       sparam_len = strlen(sparam);
+                       
+                       switch (phpdbg_parse_param(sparam, sparam_len, &new_param TSRMLS_CC)) {
+                               case EMPTY_PARAM:
+                               case NUMERIC_PARAM:
+                                       phpdbg_clear_param(
+                                               &new_param TSRMLS_CC);
+                                       goto usage;
+                       }
+                       
+                       expr_hash += phpdbg_hash_param(&new_param TSRMLS_CC);
+               }
+               
+               if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], expr_hash)) {
+                       phpdbg_create_conditional_break(
+                               &new_break, &new_param, expr, expr_len, expr_hash TSRMLS_CC);
                } else {
-                        phpdbg_error(
-                               "Failed to compile code for expression %s", expr);
-                        efree((char*)new_break.code);
-                        PHPDBG_G(bp_count)--;
+                       phpdbg_notice(
+                               "Conditional break %s exists at the specified location", expr);
                }
-               CG(compiler_options) = cops;
+               
+               phpdbg_clear_param(&new_param TSRMLS_CC);
        } else {
-               phpdbg_notice("Conditional break %s exists", expr);
+usage:
+               phpdbg_error("usage: break at <func|method|file:line|address> if <expression>");
        }
 } /* }}} */
 
@@ -479,7 +547,82 @@ static inline phpdbg_breakbase_t *phpdbg_find_breakpoint_opcode(zend_uchar opcod
        return NULL;
 } /* }}} */
 
-static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(TSRMLS_D) /* {{{ */
+static inline zend_bool phpdbg_find_breakpoint_param(phpdbg_param_t *param, zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
+{
+       zend_function *function = (zend_function*) execute_data->function_state.function;
+        
+       switch (param->type) {
+               case STR_PARAM: {
+                       /* function breakpoint */
+                       
+                       if (function->type != ZEND_USER_FUNCTION) {
+                               return 0;
+                       }
+                       
+                       {
+                               const char *str = NULL;
+                               size_t len = 0L;
+                               zend_op_array *ops  = (zend_op_array*)function;
+                               str = ops->function_name ? ops->function_name : "main";
+                               len = strlen(str);
+                               
+                               if (len == param->len) {
+                                       return (memcmp(param->str, str, len) == SUCCESS);
+                               }
+                       }
+               } break;
+               
+               case FILE_PARAM: {
+                       if ((param->file.line == zend_get_executed_lineno(TSRMLS_C))) {
+                               const char *str = zend_get_executed_filename(TSRMLS_C);
+                               size_t lengths[2] = {strlen(param->file.name), strlen(str)};
+                               
+                               if (lengths[0] == lengths[1]) {
+                                       return (memcmp(
+                                               param->file.name, str, lengths[0]) == SUCCESS);
+                               }
+                       }
+               } break;
+               
+               case METHOD_PARAM: {
+                       if (function->type != ZEND_USER_FUNCTION) {
+                               return 0;
+                       }
+                       
+                       {
+                               zend_op_array *ops = (zend_op_array*) function;
+                               
+                               if (ops->scope) {
+                                       size_t lengths[2] = {
+                                               strlen(param->method.class), ops->scope->name_length};
+                                       if (lengths[0] == lengths[1]) {
+                                               if (memcmp(param->method.class, 
+                                                       ops->scope->name, lengths[0]) == SUCCESS) {
+                                                       lengths[0] = strlen(param->method.name);
+                                                       lengths[1] = strlen(ops->function_name);
+                                                       
+                                                       if (lengths[0] == lengths[1]) {
+                                                               return (memcmp(param->method.name, 
+                                                                       ops->function_name, lengths[0]) == SUCCESS);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               } break;
+               
+               case ADDR_PARAM: {
+                       return ((phpdbg_opline_ptr_t)execute_data->opline == param->addr);
+               } break;
+               
+               case NUMERIC_PARAM:
+               case EMPTY_PARAM: { 
+                       /* do nothing */ } break;
+       }
+       return 0;
+} /* }}} */
+
+static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(zend_execute_data *execute_data TSRMLS_DC) /* {{{ */
 {
        phpdbg_breakcond_t *bp;
        HashPosition position;
@@ -493,6 +636,16 @@ static inline phpdbg_breakbase_t *phpdbg_find_conditional_breakpoint(TSRMLS_D) /
                zval **orig_retval = EG(return_value_ptr_ptr);
                zend_op_array *orig_ops = EG(active_op_array);
                zend_op **orig_opline = EG(opline_ptr);
+               
+               if (((phpdbg_breakbase_t*)bp)->disabled) {
+                       continue;
+               }
+               
+               if (bp->paramed) {
+                       if (!phpdbg_find_breakpoint_param(&bp->param, execute_data TSRMLS_CC)) {
+                               continue;
+                       }
+               }
 
                ALLOC_INIT_ZVAL(retval);
 
@@ -549,7 +702,7 @@ PHPDBG_API phpdbg_breakbase_t *phpdbg_find_breakpoint(zend_execute_data* execute
        /* conditions cannot be executed by eval()'d code */
        if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL) &&
                (PHPDBG_G(flags) & PHPDBG_HAS_COND_BP) &&
-               (base = phpdbg_find_conditional_breakpoint(TSRMLS_C))) {
+               (base = phpdbg_find_conditional_breakpoint(execute_data TSRMLS_CC))) {
                goto result;
        }
 
index d9ed37dbaa7ae75d316027575c7ee9a4aa1e31cd..3838a68d18b38e44383dd914c09c410f0d507726 100644 (file)
@@ -83,6 +83,8 @@ typedef struct _phpdbg_breakop_t {
 typedef struct _phpdbg_breakcond_t {
        phpdbg_breakbase(code);
        size_t                  code_len;
+       zend_bool       paramed;
+       phpdbg_param_t  param;
        zend_ulong      hash;
        zend_op_array  *ops;
 } phpdbg_breakcond_t;
@@ -94,7 +96,8 @@ PHPDBG_API void phpdbg_set_breakpoint_method(const char* class_name, const char*
 PHPDBG_API void phpdbg_set_breakpoint_opcode(const char* opname, size_t opname_len TSRMLS_DC);
 PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC);
 PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC);
-PHPDBG_API void phpdbg_set_breakpoint_expression(const char* expression, size_t expression_len TSRMLS_DC); /* }}} */
+PHPDBG_API void phpdbg_set_breakpoint_expression(const char* expression, size_t expression_len TSRMLS_DC);
+PHPDBG_API void phpdbg_set_breakpoint_at(const phpdbg_param_t *param, const phpdbg_input_t *input TSRMLS_DC); /* }}} */
 
 /* {{{ Breakpoint Detection API */
 PHPDBG_API phpdbg_breakbase_t* phpdbg_find_breakpoint(zend_execute_data* TSRMLS_DC); /* }}} */
index a8a5677e8077c8336998c3546d2d0eb6cac40864..916211233ac707280d461b90a55c3dbd608e09b7 100644 (file)
@@ -78,6 +78,13 @@ PHPDBG_BREAK(on) /* {{{ */
        return SUCCESS;
 } /* }}} */
 
+PHPDBG_BREAK(at) /* {{{ */
+{
+       phpdbg_set_breakpoint_at(param, input TSRMLS_CC);
+
+       return SUCCESS;
+} /* }}} */
+
 PHPDBG_BREAK(lineno) /* {{{ */
 {
        switch (param->type) {
index 1d6f594bc8f220ceba3c6b5305cb85c9cf7dbbf0..cad801ced6862e5029099f02af082f9cf68327da 100644 (file)
  * Printer Forward Declarations
  */
 PHPDBG_BREAK(file);
+PHPDBG_BREAK(func);
 PHPDBG_BREAK(method);
 PHPDBG_BREAK(address);
+PHPDBG_BREAK(at);
 PHPDBG_BREAK(op);
 PHPDBG_BREAK(on);
 PHPDBG_BREAK(lineno);
-PHPDBG_BREAK(func);
 PHPDBG_BREAK(del);
 
 /**
@@ -42,12 +43,13 @@ PHPDBG_BREAK(del);
  */
 static const phpdbg_command_t phpdbg_break_commands[] = {
        PHPDBG_COMMAND_D_EX(file,        "specify breakpoint by file:line",                        'F', break_file,    NULL, 1),
+       PHPDBG_COMMAND_D_EX(func,        "specify breakpoint by global function name",             'f', break_func,    NULL, 1),
        PHPDBG_COMMAND_D_EX(method,      "specify breakpoint by class::method",                    'm', break_method,  NULL, 1),
        PHPDBG_COMMAND_D_EX(address,     "specify breakpoint by address",                          'a', break_address, NULL, 1),
        PHPDBG_COMMAND_D_EX(op,          "specify breakpoint by opcode",                           'O', break_op,      NULL, 1),
-       PHPDBG_COMMAND_D_EX(on,          "specify breakpoint by expression",                       'o', break_on,      NULL, 1),
+       PHPDBG_COMMAND_D_EX(on,          "specify breakpoint by condition",                        'o', break_on,      NULL, 1),
+       PHPDBG_COMMAND_D_EX(at,          "specify breakpoint by location and condition",           'A', break_at,      NULL, 1),
        PHPDBG_COMMAND_D_EX(lineno,      "specify breakpoint by line of currently executing file", 'l', break_lineno,  NULL, 1),
-       PHPDBG_COMMAND_D_EX(func,        "specify breakpoint by global function name",             'f', break_func,    NULL, 1),
        PHPDBG_COMMAND_D_EX(del,         "delete breakpoint by identifier number",                 'd', break_del,     NULL, 1),
        PHPDBG_END_COMMAND
 };
index 42a84e3b9f6df638d546e2a12a0f12affe6c5c9d..d5db5f0a2f963ebdfa396f4f25f34ea8c45aa241 100644 (file)
@@ -119,6 +119,120 @@ PHPDBG_API void phpdbg_clear_param(phpdbg_param_t *param TSRMLS_DC) /* {{{ */
 
 } /* }}} */
 
+PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t* src, phpdbg_param_t* dest TSRMLS_DC) /* {{{ */
+{
+       switch ((dest->type = src->type)) {
+               case STR_PARAM:
+                       dest->str = estrndup(src->str, src->len);
+                       dest->len = src->len;
+               break;
+               
+               case ADDR_PARAM:
+                       dest->addr = src->addr;
+               break;
+               
+               case NUMERIC_PARAM:
+                       dest->num = src->num;
+               break;
+               
+               case METHOD_PARAM:
+                       dest->method.class = estrdup(src->method.class);
+                       dest->method.name = estrdup(src->method.name);
+               break;
+               
+               case FILE_PARAM:
+                       dest->file.name = estrdup(src->file.name);
+                       dest->file.line = src->file.line;
+               break;
+               
+               case EMPTY_PARAM: { /* do nothing */ } break;
+       }
+} /* }}} */
+
+PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t *param TSRMLS_DC) /* {{{ */
+{
+       zend_ulong hash = param->type;
+       
+       switch (param->type) {
+               case STR_PARAM:
+                       hash += zend_inline_hash_func(param->str, param->len);
+               break;
+               
+               case METHOD_PARAM:
+                       hash += zend_inline_hash_func(param->method.class, strlen(param->method.class));
+                       hash += zend_inline_hash_func(param->method.name, strlen(param->method.name));
+               break;
+               
+               case FILE_PARAM:
+                       hash += zend_inline_hash_func(param->file.name, strlen(param->file.name));
+                       hash += param->file.line;
+               break;
+               
+               case ADDR_PARAM:
+                       hash += param->addr;
+               break;
+               
+               case NUMERIC_PARAM:
+                       hash += param->num;
+               break;
+               
+               case EMPTY_PARAM: { /* do nothing */ } break;
+       }
+       
+       return hash;
+} /* }}} */
+
+PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *l, const phpdbg_param_t *r TSRMLS_DC) /* {{{ */
+{
+       if (l && r) {
+               if (l->type == r->type) {
+                       switch (l->type) {
+                               case STR_PARAM:
+                                       return (l->len == r->len) && 
+                                                       (memcmp(l->str, r->str, l->len) == SUCCESS);
+                                                       
+                               case NUMERIC_PARAM:
+                                       return (l->num == r->num);
+                                       
+                               case ADDR_PARAM:
+                                       return (l->addr == r->addr);
+                                       
+                               case FILE_PARAM: {
+                                       if (l->file.line == r->file.line) {
+                                               size_t lengths[2] = {
+                                                       strlen(l->file.name), strlen(r->file.name)};
+                                               
+                                               if (lengths[0] == lengths[1]) {
+                                                       return (memcmp(
+                                                               l->file.name, r->file.name, lengths[0]) == SUCCESS);
+                                               }
+                                       }
+                               } break;        
+                               
+                               case METHOD_PARAM: {
+                                       size_t lengths[2] = {
+                                               strlen(l->method.class), strlen(r->method.class)};
+                                       if (lengths[0] == lengths[1]) {
+                                               if (memcmp(l->method.class, r->method.class, lengths[0]) == SUCCESS) {
+                                                       lengths[0] = strlen(l->method.name);
+                                                       lengths[1] = strlen(r->method.name);
+                                                       
+                                                       if (lengths[0] == lengths[1]) {
+                                                               return (memcmp(
+                                                                       l->method.name, r->method.name, lengths[0]) == SUCCESS);
+                                                       }
+                                               }
+                                       }
+                               } break;
+                               
+                               case EMPTY_PARAM:
+                                       return 1;
+                       }
+               }
+       }
+       return 0;
+} /* }}} */
+
 PHPDBG_API phpdbg_input_t **phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC) /* {{{ */
 {
        char *p;
index fcb96879c53b458071ccfa95ecb5a0f868a2a203..f3d38d58eaaec0bfea5888d6b41f4647136dd9ca 100644 (file)
@@ -120,13 +120,16 @@ PHPDBG_API void phpdbg_destroy_input(phpdbg_input_t** TSRMLS_DC);
 PHPDBG_API phpdbg_input_t** phpdbg_read_argv(char *buffer, int *argc TSRMLS_DC);
 PHPDBG_API void phpdbg_destroy_argv(phpdbg_input_t **argv, int argc TSRMLS_DC);
 #define phpdbg_argv_is(n, s) \
-       (memcmp(input->argv[n]->string, s, input->argv[n]->length-1) == SUCCESS)
+       (memcmp(input->argv[n]->string, s, input->argv[n]->length) == SUCCESS)
 
 /*
 * Parameter Management
 */
 PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char*, size_t, phpdbg_param_t* TSRMLS_DC);
 PHPDBG_API void phpdbg_clear_param(phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API void phpdbg_copy_param(const phpdbg_param_t*, phpdbg_param_t* TSRMLS_DC);
+PHPDBG_API zend_bool phpdbg_match_param(const phpdbg_param_t *, const phpdbg_param_t * TSRMLS_DC);
+PHPDBG_API zend_ulong phpdbg_hash_param(const phpdbg_param_t * TSRMLS_DC);
 PHPDBG_API const char* phpdbg_get_param_type(const phpdbg_param_t* TSRMLS_DC);
 
 /*
index 964f7624fac9f014f526fd23c2e2d3bcb57500fd..8d9915e59b8d03c514bd235e922f39f8e7967711 100644 (file)
@@ -246,6 +246,13 @@ PHPDBG_HELP(break) /* {{{ */
        phpdbg_writeln("\t%sb on ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
        phpdbg_writeln("\tWill break when the condition evaluates to true");
        phpdbg_writeln(EMPTY);
+       phpdbg_writeln("\t%sbreak at phpdbg::isGreat if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
+       phpdbg_writeln("\tWill break at every opcode in phpdbg::isGreat when the condition evaluates to true");
+       phpdbg_writeln("\t%sbreak at test.php:20 if ($expression == true)", phpdbg_get_prompt(TSRMLS_C));
+       phpdbg_writeln("\tWill break at every opcode on line 20 of test.php when the condition evaluates to true");
+       phpdbg_write("\t");
+       phpdbg_notice("The location can be anything accepted by file, func, method, or address break commands");
+       phpdbg_writeln(EMPTY);
        phpdbg_writeln("\t%sbreak op ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
        phpdbg_writeln("\t%sb O ZEND_ADD", phpdbg_get_prompt(TSRMLS_C));
        phpdbg_writeln("\tWill break on every occurence of the opcode provided");